Files
openclaw-backups/skills/openclaw-self-healing/scripts/create-validation-crons.js

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 };