210 lines
6.5 KiB
JavaScript
210 lines
6.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Create validation crons for self-evaluating cron jobs
|
|
*
|
|
* Each validation cron runs 2 minutes after the original cron
|
|
* and validates the self-evaluation output
|
|
*/
|
|
|
|
const { execSync } = require('child_process');
|
|
|
|
// Cron configurations with their schedules
|
|
const SELF_EVAL_CRONS = [
|
|
{ id: 'b81588fa-5111-41fb-871f-d767dc1f783b', name: 'Daily Stock Briefing', schedule: '0 6 * * 1-5' },
|
|
{ id: 'b9662f08-36ee-4e6d-ab9d-fd2d48f21737', name: '모닝 브리핑', schedule: '15 6 * * 1-5' },
|
|
{ id: 'bd8e8994-3646-4f7a-b994-4f3ae9f1890a', name: 'Daily Wrap-up', schedule: '0 17 * * 1-5' },
|
|
{ id: 'cc9ddcf5-734c-4e8e-b0e0-51884f8a5196', name: 'Trend Hunter', schedule: '30 12,20 * * *' },
|
|
{ id: '41e625c8-59a5-4df5-bd97-2dbc5282eda7', name: 'IT/AI 뉴스 브리핑', schedule: '0 12 * * *' },
|
|
{ id: '6b2da787-7df8-49e8-b506-9139f66f86ca', name: '조식 알림', schedule: '55 7 * * 1-5' },
|
|
{ id: '422b96a7-8931-43ba-84ce-a55b1b9a6477', name: '취침 알림', schedule: '0 0 * * *' },
|
|
{ id: 'e16e5163-9caf-444b-b74d-0cbebaed013b', name: '부부 약 먹기 알림', schedule: '0 22 * * *' },
|
|
{ id: 'dfa2bf81-fa94-45b2-a154-b7e4a78fc173', name: '관훈 미확정 저녁', schedule: '0 19 * * *' },
|
|
{ id: '270a5dc7-f19e-402f-ae3a-79c628a3cde8', name: 'Monthly DCA', schedule: '0 20 25 * *' },
|
|
{ id: '22c071ae-598f-48da-b002-4d1fd395bf0a', name: '실적 발표 캘린더', schedule: '0 8 * * 1' },
|
|
{ id: '41e5363d-6b32-48c2-9bf6-738d950c6d6c', name: '주간 요약 리포트', schedule: '0 20 * * 0' },
|
|
{ id: 'ddef1a57-21e8-4614-991c-a3f29177e8ee', name: '월간 비용 추적', schedule: '0 9 * * 1' },
|
|
{ id: 'a98f06f7-a084-4993-b352-358d00ed340f', name: 'TQQQ 15분 모니터링', schedule: '*/15 * * * *' }
|
|
];
|
|
|
|
/**
|
|
* Add 2 minutes to a cron expression
|
|
*/
|
|
function addTwoMinutes(cronExpr) {
|
|
const parts = cronExpr.split(' ');
|
|
|
|
// Handle */N format (e.g., */15)
|
|
if (parts[0].includes('*/')) {
|
|
// For */15, validation would run at 2,17,32,47 (offset by 2)
|
|
const interval = parseInt(parts[0].split('/')[1]);
|
|
parts[0] = `2-59/${interval}`;
|
|
return parts.join(' ');
|
|
}
|
|
|
|
// Handle range format (e.g., 1-59/15)
|
|
if (parts[0].includes('-') && parts[0].includes('/')) {
|
|
// Already has offset, just adjust
|
|
return parts.join(' ');
|
|
}
|
|
|
|
// Handle fixed minute
|
|
const minute = parseInt(parts[0]);
|
|
const newMinute = (minute + 2) % 60;
|
|
parts[0] = newMinute.toString();
|
|
|
|
// If minute wrapped around (58,59 → 0,1), increment hour
|
|
if (newMinute < 2 && minute >= 58) {
|
|
if (parts[1] !== '*' && !parts[1].includes(',') && !parts[1].includes('-')) {
|
|
const hour = parseInt(parts[1]);
|
|
parts[1] = ((hour + 1) % 24).toString();
|
|
}
|
|
}
|
|
|
|
return parts.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Create validation cron job
|
|
*/
|
|
function createValidationCron(target) {
|
|
const validationSchedule = addTwoMinutes(target.schedule);
|
|
|
|
// Determine channel based on target
|
|
// Main user-facing crons go to #jarvis, others to #debug
|
|
const mainCrons = [
|
|
'Daily Stock Briefing',
|
|
'모닝 브리핑',
|
|
'Daily Wrap-up',
|
|
'Trend Hunter',
|
|
'IT/AI 뉴스 브리핑',
|
|
'조식 알림',
|
|
'취침 알림',
|
|
'부부 약 먹기 알림',
|
|
'관훈 미확정 저녁',
|
|
'Monthly DCA',
|
|
'실적 발표 캘린더',
|
|
'주간 요약 리포트',
|
|
'월간 비용 추적',
|
|
'TQQQ 15분 모니터링'
|
|
];
|
|
const targetChannel = mainCrons.includes(target.name) ? '1468386844621144065' : '1469190688083280065';
|
|
|
|
const job = {
|
|
name: `🔍 Validation: ${target.name}`,
|
|
enabled: true,
|
|
schedule: {
|
|
kind: 'cron',
|
|
expr: validationSchedule,
|
|
tz: 'Asia/Seoul'
|
|
},
|
|
sessionTarget: 'isolated',
|
|
wakeMode: 'next-heartbeat',
|
|
payload: {
|
|
kind: 'agentTurn',
|
|
model: 'anthropic/claude-haiku-4-5-20251001',
|
|
thinking: 'off',
|
|
channel: 'discord',
|
|
to: `channel:${targetChannel}`,
|
|
deliver: true,
|
|
message: `
|
|
🔍 **자기평가 검증: ${target.name}**
|
|
|
|
**Task:**
|
|
1. Discord 채널에서 최근 5분 내 메시지 검색
|
|
2. "${target.name}" 크론의 자기평가 섹션 찾기
|
|
3. 검증 스크립트 실행
|
|
|
|
**Steps:**
|
|
|
|
\`\`\`bash
|
|
# 1. Search recent messages (last 5 minutes)
|
|
# Use message tool to search Discord channel
|
|
|
|
# 2. Extract the message with "자기평가 (V2.5)" section
|
|
|
|
# 3. Save to temp file
|
|
echo "[MESSAGE_CONTENT]" > /tmp/cron-validation-input.txt
|
|
|
|
# 4. Run validation script
|
|
node ~/openclaw/scripts/validate-self-review.js \\
|
|
"${target.id}" \\
|
|
"${target.name}" \\
|
|
/tmp/cron-validation-input.txt \\
|
|
0 \\
|
|
0 \\
|
|
0
|
|
\`\`\`
|
|
|
|
**메시지 검색 (message tool 사용):**
|
|
- action: search
|
|
- channel: discord
|
|
- channelId: ${targetChannel}
|
|
- query: "자기평가"
|
|
- limit: 3
|
|
|
|
메시지를 찾아서 validate-self-review.js로 검증하세요.
|
|
|
|
**출력:**
|
|
- PASS: NO_REPLY (조용히)
|
|
- WARN/INFO: 플래그 요약 (간단히)
|
|
- FAIL: 상세 보고 (무엇이 잘못됐는지)
|
|
|
|
**중요:** 메트릭 수집이 불완전하므로 포맷/일관성 검증에만 집중.
|
|
`.trim()
|
|
}
|
|
};
|
|
|
|
return job;
|
|
}
|
|
|
|
/**
|
|
* Main function
|
|
*/
|
|
async function main() {
|
|
console.log('🔍 Creating validation crons for self-evaluating jobs...\n');
|
|
|
|
let created = 0;
|
|
let failed = 0;
|
|
let skipped = 0;
|
|
|
|
for (const target of SELF_EVAL_CRONS) {
|
|
try {
|
|
const job = createValidationCron(target);
|
|
const jobJson = JSON.stringify(job).replace(/'/g, "\\'");
|
|
const cmd = `openclaw cron add '${jobJson}'`;
|
|
|
|
const result = execSync(cmd, { encoding: 'utf8' });
|
|
console.log(`✅ Created validation cron for: ${target.name}`);
|
|
console.log(` Schedule: ${job.schedule.expr}`);
|
|
created++;
|
|
} catch (e) {
|
|
if (e.message.includes('already exists')) {
|
|
console.log(`⏭️ Skipped (already exists): ${target.name}`);
|
|
skipped++;
|
|
} else {
|
|
console.log(`❌ Failed to create validation cron for ${target.name}:`);
|
|
console.log(` Error: ${e.message}`);
|
|
failed++;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`\n📊 Summary:`);
|
|
console.log(` ✅ Created: ${created}`);
|
|
console.log(` ⏭️ Skipped: ${skipped}`);
|
|
console.log(` ❌ Failed: ${failed}`);
|
|
console.log(` 📝 Total: ${SELF_EVAL_CRONS.length}`);
|
|
|
|
if (created > 0) {
|
|
console.log(`\n✨ Next steps:`);
|
|
console.log(` 1. Wait for next cron execution`);
|
|
console.log(` 2. Check Discord for validation results`);
|
|
console.log(` 3. Review validation-YYYY-MM-DD.jsonl files`);
|
|
}
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
module.exports = { createValidationCron, addTwoMinutes };
|