文件内容
scripts/push-toggle.js
#!/usr/bin/env node
/**
* calendar-extractor — cron push toggle
* Generated by JavisSkills/skill-creator. Saves push prefs and prints the exact
* `openclaw cron add` command to register the schedule.
*
* Cron flags follow the real openclaw CLI (src/cli/cron-cli/register.cron-add.ts):
* --cron <expr> 5/6-field crontab (NOT --schedule)
* --message <text> isolated agentTurn payload (NOT --command)
* --session isolated
* --tz <IANA>
* --channel/--to only for non-iOS channels (iOS delivery is the push call itself)
*/
'use strict';
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const { resolveUserId, safeUserPath, readJson, writeJson } = require('./data');
const SLUG = 'calendar-extractor';
const cmd = process.argv[2];
const userId = resolveUserId(process.argv[3]);
const args = process.argv.slice(4);
function getFlag(name, defaultVal) {
const i = args.indexOf(`--${name}`);
return i >= 0 && i + 1 < args.length ? args[i + 1] : defaultVal;
}
function crontabFromTime(t) {
const m = /^(\d{1,2}):(\d{2})$/.exec(t || '');
if (!m) return null;
return `${parseInt(m[2], 10)} ${parseInt(m[1], 10)} * * *`;
}
const prefsPath = safeUserPath(userId).replace(/\.json$/, '.prefs.json');
function loadPrefs() {
return fs.existsSync(prefsPath) ? readJson(prefsPath) : {};
}
function savePrefs(p) {
writeJson(prefsPath, p);
}
const cronName = `${SLUG}-${userId}`;
if (cmd === 'on') {
const time = getFlag('time', '08:00');
const channel = getFlag('channel', 'iOS');
const tz = getFlag('tz', 'America/Los_Angeles');
const crontab = crontabFromTime(time) || '0 8 * * *';
savePrefs({ time, channel, tz, enabledAt: new Date().toISOString() });
const agentCmd =
`Run /${SLUG}. Step 1: node scripts/${SLUG}.js ${userId} fetch (recent transcripts as JSON, ` +
`with a top-level reference_time + tz anchor). ` +
`Step 2: extract calendar events as a JSON array (title, start_at, end_at ISO 8601, location, attendees). ` +
`Resolve all relative dates/times against reference_time in its tz (fallback: session started_at), ` +
`infer AM/PM from context, and use null when unresolvable. ` +
`Step 3: pipe that array into node scripts/${SLUG}.js ${userId} push — it dedups and delivers a markdown digest to iOS.`;
const channelFlags = channel && channel !== 'iOS'
? ` --channel ${channel} --to "<channel-target-id>"`
: '';
console.log(`✅ ${SLUG} push enabled: ${time} ${tz} via ${channel}`);
console.log('Register the cron with:');
console.log(
` openclaw cron add --name "${cronName}" --cron "${crontab}" --tz "${tz}"` +
` --session isolated${channelFlags} --message ${JSON.stringify(agentCmd)}`
);
} else if (cmd === 'off') {
if (fs.existsSync(prefsPath)) fs.unlinkSync(prefsPath);
try { execSync(`openclaw cron remove --name "${cronName}"`); } catch (_) { /* best-effort */ }
console.log(`✅ ${SLUG} push disabled`);
} else if (cmd === 'status') {
const prefs = loadPrefs();
if (Object.keys(prefs).length === 0) {
console.log(`❌ ${SLUG} push not enabled for ${userId}`);
} else {
console.log(`✅ ${SLUG} push: ${prefs.time} ${prefs.tz || ''} via ${prefs.channel} (since ${prefs.enabledAt})`);
}
} else {
console.error(`Usage: node push-toggle.js [on|off|status] <userId> [--time HH:MM] [--tz IANA] [--channel iOS|Telegram|Discord|Slack]`);
process.exit(1);
}