@poping/yome 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -7
- package/README.zh-CN.md +5 -7
- package/bin/yome-calwatch +0 -0
- package/dist/context.js +6 -3
- package/dist/context.js.map +1 -1
- package/dist/daemon/calendarPermission.d.ts +10 -0
- package/dist/daemon/calendarPermission.js +68 -0
- package/dist/daemon/calendarPermission.js.map +1 -0
- package/dist/daemon/cronCli.d.ts +19 -0
- package/dist/daemon/cronCli.js +403 -0
- package/dist/daemon/cronCli.js.map +1 -0
- package/dist/daemon/envHint.d.ts +5 -0
- package/dist/daemon/envHint.js +139 -0
- package/dist/daemon/envHint.js.map +1 -0
- package/dist/daemon/humanCron.d.ts +1 -0
- package/dist/daemon/humanCron.js +72 -0
- package/dist/daemon/humanCron.js.map +1 -0
- package/dist/daemon/humanOnce.d.ts +1 -0
- package/dist/daemon/humanOnce.js +54 -0
- package/dist/daemon/humanOnce.js.map +1 -0
- package/dist/daemon/index.d.ts +5 -0
- package/dist/daemon/index.js +168 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/launchd.d.ts +7 -0
- package/dist/daemon/launchd.js +93 -0
- package/dist/daemon/launchd.js.map +1 -0
- package/dist/daemon/log.d.ts +17 -0
- package/dist/daemon/log.js +57 -0
- package/dist/daemon/log.js.map +1 -0
- package/dist/daemon/paths.d.ts +11 -0
- package/dist/daemon/paths.js +27 -0
- package/dist/daemon/paths.js.map +1 -0
- package/dist/daemon/runTaskEntry.d.ts +1 -0
- package/dist/daemon/runTaskEntry.js +67 -0
- package/dist/daemon/runTaskEntry.js.map +1 -0
- package/dist/daemon/runner.d.ts +21 -0
- package/dist/daemon/runner.js +175 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/scheduler.d.ts +5 -0
- package/dist/daemon/scheduler.js +162 -0
- package/dist/daemon/scheduler.js.map +1 -0
- package/dist/daemon/taskStore.d.ts +62 -0
- package/dist/daemon/taskStore.js +88 -0
- package/dist/daemon/taskStore.js.map +1 -0
- package/dist/daemon/triggers/calendar.d.ts +8 -0
- package/dist/daemon/triggers/calendar.js +248 -0
- package/dist/daemon/triggers/calendar.js.map +1 -0
- package/dist/daemon/triggers/childRunner.d.ts +14 -0
- package/dist/daemon/triggers/childRunner.js +111 -0
- package/dist/daemon/triggers/childRunner.js.map +1 -0
- package/dist/daemon/triggers/cron.d.ts +14 -0
- package/dist/daemon/triggers/cron.js +91 -0
- package/dist/daemon/triggers/cron.js.map +1 -0
- package/dist/daemon/triggers/file.d.ts +7 -0
- package/dist/daemon/triggers/file.js +123 -0
- package/dist/daemon/triggers/file.js.map +1 -0
- package/dist/daemon/triggers/once.d.ts +10 -0
- package/dist/daemon/triggers/once.js +80 -0
- package/dist/daemon/triggers/once.js.map +1 -0
- package/dist/index.js +96 -9
- package/dist/index.js.map +1 -1
- package/dist/skills/runner/dispatcher.d.ts +74 -3
- package/dist/skills/runner/dispatcher.js +258 -48
- package/dist/skills/runner/dispatcher.js.map +1 -1
- package/dist/skills/runner/dispatcher.test.d.ts +1 -0
- package/dist/skills/runner/dispatcher.test.js +141 -0
- package/dist/skills/runner/dispatcher.test.js.map +1 -0
- package/dist/skills/runner/kernel.js +0 -1
- package/dist/skills/runner/kernel.js.map +1 -1
- package/dist/skills/runner/nodeBackend.d.ts +32 -0
- package/dist/skills/runner/nodeBackend.js +147 -0
- package/dist/skills/runner/nodeBackend.js.map +1 -0
- package/dist/state/todos.d.ts +12 -0
- package/dist/state/todos.js +32 -0
- package/dist/state/todos.js.map +1 -0
- package/dist/tools/askUser.d.ts +20 -0
- package/dist/tools/askUser.js +126 -0
- package/dist/tools/askUser.js.map +1 -0
- package/dist/tools/bash.js +13 -19
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +18 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/todoWrite.d.ts +2 -0
- package/dist/tools/todoWrite.js +141 -0
- package/dist/tools/todoWrite.js.map +1 -0
- package/dist/tools/yome.d.ts +2 -0
- package/dist/tools/yome.js +87 -0
- package/dist/tools/yome.js.map +1 -0
- package/dist/ui/App.js +52 -3
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/AskUserPrompt.d.ts +7 -0
- package/dist/ui/AskUserPrompt.js +78 -0
- package/dist/ui/AskUserPrompt.js.map +1 -0
- package/dist/ui/InputBar.js +20 -34
- package/dist/ui/InputBar.js.map +1 -1
- package/dist/ui/MessageList.d.ts +8 -0
- package/dist/ui/MessageList.js +2 -2
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/MultilineTextInput.d.ts +31 -0
- package/dist/ui/MultilineTextInput.js +393 -0
- package/dist/ui/MultilineTextInput.js.map +1 -0
- package/dist/ui/MultilineTextInput.test.d.ts +1 -0
- package/dist/ui/MultilineTextInput.test.js +30 -0
- package/dist/ui/MultilineTextInput.test.js.map +1 -0
- package/dist/ui/TodoPanel.d.ts +7 -0
- package/dist/ui/TodoPanel.js +36 -0
- package/dist/ui/TodoPanel.js.map +1 -0
- package/dist/yomeSkills/cli.d.ts +8 -0
- package/dist/yomeSkills/cli.js +155 -8
- package/dist/yomeSkills/cli.js.map +1 -1
- package/dist/yomeSkills/deprecate.d.ts +29 -0
- package/dist/yomeSkills/deprecate.js +99 -0
- package/dist/yomeSkills/deprecate.js.map +1 -0
- package/dist/yomeSkills/installFromHubTarball.d.ts +26 -0
- package/dist/yomeSkills/installFromHubTarball.js +161 -0
- package/dist/yomeSkills/installFromHubTarball.js.map +1 -0
- package/dist/yomeSkills/installGithub.d.ts +7 -0
- package/dist/yomeSkills/installGithub.js +42 -21
- package/dist/yomeSkills/installGithub.js.map +1 -1
- package/dist/yomeSkills/invoke.js +50 -18
- package/dist/yomeSkills/invoke.js.map +1 -1
- package/dist/yomeSkills/publish.d.ts +2 -0
- package/dist/yomeSkills/publish.js +6 -1
- package/dist/yomeSkills/publish.js.map +1 -1
- package/package.json +10 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Persistent task registry for `yome cron`.
|
|
2
|
+
//
|
|
3
|
+
// File: ~/.yome/cron/tasks.json
|
|
4
|
+
// Format: { version: 1, tasks: TaskRecord[] }
|
|
5
|
+
//
|
|
6
|
+
// We keep the schema flat-ish and forward-compatible: extra fields are
|
|
7
|
+
// preserved on round-trip, so future PRs can add (e.g.) calendar / file
|
|
8
|
+
// triggers without breaking older daemon binaries.
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync, renameSync } from 'fs';
|
|
10
|
+
import { randomUUID } from 'crypto';
|
|
11
|
+
import { TASKS_FILE, ensureDirs } from './paths.js';
|
|
12
|
+
const HISTORY_LIMIT = 20;
|
|
13
|
+
function readRaw() {
|
|
14
|
+
if (!existsSync(TASKS_FILE))
|
|
15
|
+
return { version: 1, tasks: [] };
|
|
16
|
+
try {
|
|
17
|
+
const raw = readFileSync(TASKS_FILE, 'utf-8').trim();
|
|
18
|
+
if (!raw)
|
|
19
|
+
return { version: 1, tasks: [] };
|
|
20
|
+
const parsed = JSON.parse(raw);
|
|
21
|
+
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.tasks)) {
|
|
22
|
+
return { version: 1, tasks: [] };
|
|
23
|
+
}
|
|
24
|
+
return { version: 1, tasks: parsed.tasks };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return { version: 1, tasks: [] };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function writeRaw(store) {
|
|
31
|
+
ensureDirs();
|
|
32
|
+
// Atomic-ish write via rename to avoid corrupting the file if the
|
|
33
|
+
// daemon dies mid-write (OS will only see the old or the new bytes).
|
|
34
|
+
const tmp = TASKS_FILE + '.tmp';
|
|
35
|
+
writeFileSync(tmp, JSON.stringify(store, null, 2), 'utf-8');
|
|
36
|
+
renameSync(tmp, TASKS_FILE);
|
|
37
|
+
}
|
|
38
|
+
export function listTasks() {
|
|
39
|
+
return readRaw().tasks;
|
|
40
|
+
}
|
|
41
|
+
export function getTask(id) {
|
|
42
|
+
return readRaw().tasks.find((t) => t.id === id);
|
|
43
|
+
}
|
|
44
|
+
export function addTask(input) {
|
|
45
|
+
const store = readRaw();
|
|
46
|
+
const t = {
|
|
47
|
+
id: shortId(),
|
|
48
|
+
createdAt: Date.now(),
|
|
49
|
+
enabled: input.enabled ?? true,
|
|
50
|
+
...input,
|
|
51
|
+
};
|
|
52
|
+
store.tasks.push(t);
|
|
53
|
+
writeRaw(store);
|
|
54
|
+
return t;
|
|
55
|
+
}
|
|
56
|
+
export function removeTask(id) {
|
|
57
|
+
const store = readRaw();
|
|
58
|
+
const before = store.tasks.length;
|
|
59
|
+
store.tasks = store.tasks.filter((t) => t.id !== id);
|
|
60
|
+
if (store.tasks.length === before)
|
|
61
|
+
return false;
|
|
62
|
+
writeRaw(store);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
export function setEnabled(id, enabled) {
|
|
66
|
+
const store = readRaw();
|
|
67
|
+
const t = store.tasks.find((x) => x.id === id);
|
|
68
|
+
if (!t)
|
|
69
|
+
return false;
|
|
70
|
+
t.enabled = enabled;
|
|
71
|
+
writeRaw(store);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
export function recordRun(id, summary) {
|
|
75
|
+
const store = readRaw();
|
|
76
|
+
const t = store.tasks.find((x) => x.id === id);
|
|
77
|
+
if (!t)
|
|
78
|
+
return;
|
|
79
|
+
t.lastRun = summary;
|
|
80
|
+
t.history = [summary, ...(t.history ?? [])].slice(0, HISTORY_LIMIT);
|
|
81
|
+
writeRaw(store);
|
|
82
|
+
}
|
|
83
|
+
/** Short, sortable-ish task id: tsk_<8 hex chars>. */
|
|
84
|
+
function shortId() {
|
|
85
|
+
const u = randomUUID().replace(/-/g, '');
|
|
86
|
+
return `tsk_${u.slice(0, 10)}`;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=taskStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskStore.js","sourceRoot":"","sources":["../../src/daemon/taskStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TaskRecord } from '../taskStore.js';
|
|
2
|
+
interface TriggerCtx {
|
|
3
|
+
yomeBinPath: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function registerCalendarTask(task: TaskRecord, ctx: TriggerCtx): void;
|
|
6
|
+
export declare function unregisterCalendarTask(taskId: string): void;
|
|
7
|
+
export declare function listActiveCalendar(): string[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// cli/src/daemon/triggers/calendar.ts
|
|
2
|
+
//
|
|
3
|
+
// Calendar trigger — bridges between Yome tasks and the Swift
|
|
4
|
+
// `yome-calwatch` helper.
|
|
5
|
+
//
|
|
6
|
+
// Lifecycle (per task):
|
|
7
|
+
// 1. registerCalendarTask(task) writes a tiny JSON spec describing
|
|
8
|
+
// the filter (events[], titleRegex, calendar, leadMs) into
|
|
9
|
+
// ~/.yome/cron/calendar/<taskId>/spec.json.
|
|
10
|
+
// 2. We spawn `yome-calwatch --config <spec.json>` as a long-lived
|
|
11
|
+
// child. Each task gets its own helper process so a single
|
|
12
|
+
// crash/permission-revoke only takes down one trigger and the
|
|
13
|
+
// filter args stay completely local to that helper.
|
|
14
|
+
// 3. The helper streams JSONL lines on stdout. We parse them and:
|
|
15
|
+
// - "ready" → emit a [daemon] log line, nothing else.
|
|
16
|
+
// - "event" → call runChildTask(...) with extraEnv carrying
|
|
17
|
+
// a [calendar event ...] context block to the
|
|
18
|
+
// Agent, and a triggerMeta entry for the audit log.
|
|
19
|
+
// - "heartbeat" → ignored (used to detect stalled helpers).
|
|
20
|
+
// - "shutdown" → ignored (clean exit).
|
|
21
|
+
// - "error" → emit, do NOT respawn if it's a permission error;
|
|
22
|
+
// else respawn with backoff.
|
|
23
|
+
// 4. unregisterCalendarTask(id) sends SIGTERM to the helper.
|
|
24
|
+
//
|
|
25
|
+
// Crash recovery:
|
|
26
|
+
// If the helper exits with a non-zero code (and we did NOT request
|
|
27
|
+
// shutdown), we respawn after a backoff that grows from 1s → 30s.
|
|
28
|
+
// On clean exit (signal=SIGTERM after our request) we do nothing.
|
|
29
|
+
//
|
|
30
|
+
// State:
|
|
31
|
+
// ~/.yome/cron/calendar/<taskId>/spec.json filter spec
|
|
32
|
+
// ~/.yome/cron/calendar/<taskId>/fired.json helper-managed dedupe set
|
|
33
|
+
import { spawn } from 'child_process';
|
|
34
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
35
|
+
import { dirname, resolve, join } from 'path';
|
|
36
|
+
import { fileURLToPath } from 'url';
|
|
37
|
+
import { runChildTask } from './childRunner.js';
|
|
38
|
+
import { CRON_ROOT } from '../paths.js';
|
|
39
|
+
function hashSpec(s) {
|
|
40
|
+
// Small inputs; JSON.stringify with sorted keys is fine. We don't
|
|
41
|
+
// need cryptographic strength — only "did anything change".
|
|
42
|
+
const norm = JSON.stringify(s, Object.keys(s).sort());
|
|
43
|
+
let h = 0;
|
|
44
|
+
for (let i = 0; i < norm.length; i++) {
|
|
45
|
+
h = (h * 31 + norm.charCodeAt(i)) | 0;
|
|
46
|
+
}
|
|
47
|
+
return String(h);
|
|
48
|
+
}
|
|
49
|
+
const active = new Map();
|
|
50
|
+
const SPEC_ROOT = join(CRON_ROOT, 'calendar');
|
|
51
|
+
function specPath(taskId) {
|
|
52
|
+
return join(SPEC_ROOT, taskId, 'spec.json');
|
|
53
|
+
}
|
|
54
|
+
function stateDir(taskId) {
|
|
55
|
+
return join(SPEC_ROOT, taskId);
|
|
56
|
+
}
|
|
57
|
+
function resolveCalwatchPath() {
|
|
58
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
59
|
+
// dist/daemon/triggers/calendar.js → ../../../bin/yome-calwatch
|
|
60
|
+
// src/daemon/triggers/calendar.ts → ../../../bin/yome-calwatch
|
|
61
|
+
return resolve(here, '..', '..', '..', 'bin', 'yome-calwatch');
|
|
62
|
+
}
|
|
63
|
+
export function registerCalendarTask(task, ctx) {
|
|
64
|
+
if (task.trigger.kind !== 'calendar')
|
|
65
|
+
return;
|
|
66
|
+
const t = task.trigger;
|
|
67
|
+
const dir = stateDir(task.id);
|
|
68
|
+
if (!existsSync(dir))
|
|
69
|
+
mkdirSync(dir, { recursive: true });
|
|
70
|
+
const spec = {
|
|
71
|
+
taskId: task.id,
|
|
72
|
+
events: t.events,
|
|
73
|
+
leadMs: t.leadMs ?? 0,
|
|
74
|
+
titleRegex: t.titleRegex ?? '',
|
|
75
|
+
calendar: t.calendar ?? '',
|
|
76
|
+
stateDir: dir,
|
|
77
|
+
};
|
|
78
|
+
const newHash = hashSpec(spec);
|
|
79
|
+
// Idempotent re-register: tasks.json gets rewritten on every
|
|
80
|
+
// recordRun() → fs.watch fires → reloadAllTasks() walks every task.
|
|
81
|
+
// Without this guard we'd kill the long-lived calwatch helper and
|
|
82
|
+
// respawn it (losing the in-memory poll cycle, briefly orphaning
|
|
83
|
+
// any in-flight EventKit query, and spamming the log) every time
|
|
84
|
+
// ANY other task records a run. Skip when the spec is byte-identical.
|
|
85
|
+
const existing = active.get(task.id);
|
|
86
|
+
if (existing && existing.specHash === newHash && existing.child) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Spec changed (or no helper running) → tear down and respawn.
|
|
90
|
+
unregisterCalendarTask(task.id);
|
|
91
|
+
writeFileSync(specPath(task.id), JSON.stringify(spec, null, 2), 'utf-8');
|
|
92
|
+
const at = {
|
|
93
|
+
taskId: task.id,
|
|
94
|
+
child: null,
|
|
95
|
+
buffer: '',
|
|
96
|
+
shuttingDown: false,
|
|
97
|
+
respawnTimer: null,
|
|
98
|
+
respawnDelayMs: 1_000,
|
|
99
|
+
permissionFailed: false,
|
|
100
|
+
specHash: newHash,
|
|
101
|
+
};
|
|
102
|
+
active.set(task.id, at);
|
|
103
|
+
spawnHelper(at, ctx);
|
|
104
|
+
}
|
|
105
|
+
export function unregisterCalendarTask(taskId) {
|
|
106
|
+
const at = active.get(taskId);
|
|
107
|
+
if (!at)
|
|
108
|
+
return;
|
|
109
|
+
at.shuttingDown = true;
|
|
110
|
+
if (at.respawnTimer) {
|
|
111
|
+
clearTimeout(at.respawnTimer);
|
|
112
|
+
at.respawnTimer = null;
|
|
113
|
+
}
|
|
114
|
+
if (at.child && !at.child.killed) {
|
|
115
|
+
try {
|
|
116
|
+
at.child.kill('SIGTERM');
|
|
117
|
+
}
|
|
118
|
+
catch { /* noop */ }
|
|
119
|
+
}
|
|
120
|
+
active.delete(taskId);
|
|
121
|
+
}
|
|
122
|
+
export function listActiveCalendar() {
|
|
123
|
+
return [...active.keys()];
|
|
124
|
+
}
|
|
125
|
+
function spawnHelper(at, ctx) {
|
|
126
|
+
const bin = resolveCalwatchPath();
|
|
127
|
+
if (!existsSync(bin)) {
|
|
128
|
+
// eslint-disable-next-line no-console
|
|
129
|
+
console.error(`[calendar:${at.taskId}] helper binary missing at ${bin}. ` +
|
|
130
|
+
`Build with: cd cli && npm run build:native`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const child = spawn(bin, ['--config', specPath(at.taskId)], {
|
|
134
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
135
|
+
detached: false,
|
|
136
|
+
});
|
|
137
|
+
at.child = child;
|
|
138
|
+
// eslint-disable-next-line no-console
|
|
139
|
+
console.log(`[calendar:${at.taskId}] helper started pid=${child.pid}`);
|
|
140
|
+
child.stdout?.on('data', (chunk) => {
|
|
141
|
+
at.buffer += chunk.toString('utf-8');
|
|
142
|
+
let nl;
|
|
143
|
+
// Process complete lines; keep the trailing partial in the buffer.
|
|
144
|
+
while ((nl = at.buffer.indexOf('\n')) >= 0) {
|
|
145
|
+
const line = at.buffer.slice(0, nl).trim();
|
|
146
|
+
at.buffer = at.buffer.slice(nl + 1);
|
|
147
|
+
if (line)
|
|
148
|
+
handleHelperLine(at, line, ctx);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
child.stderr?.on('data', (d) => {
|
|
152
|
+
process.stderr.write(`[calendar:${at.taskId}] (helper stderr) ${d.toString('utf-8')}`);
|
|
153
|
+
});
|
|
154
|
+
child.on('exit', (code, signal) => {
|
|
155
|
+
at.child = null;
|
|
156
|
+
// eslint-disable-next-line no-console
|
|
157
|
+
console.log(`[calendar:${at.taskId}] helper exited code=${code} signal=${signal}`);
|
|
158
|
+
if (at.shuttingDown)
|
|
159
|
+
return;
|
|
160
|
+
if (at.permissionFailed) {
|
|
161
|
+
console.error(`[calendar:${at.taskId}] permission failure is sticky; not respawning.`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// Backoff respawn.
|
|
165
|
+
const delay = at.respawnDelayMs;
|
|
166
|
+
at.respawnDelayMs = Math.min(at.respawnDelayMs * 2, 30_000);
|
|
167
|
+
at.respawnTimer = setTimeout(() => {
|
|
168
|
+
if (!at.shuttingDown)
|
|
169
|
+
spawnHelper(at, ctx);
|
|
170
|
+
}, delay);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function handleHelperLine(at, line, ctx) {
|
|
174
|
+
let msg;
|
|
175
|
+
try {
|
|
176
|
+
msg = JSON.parse(line);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
process.stderr.write(`[calendar:${at.taskId}] non-JSON helper line: ${line}\n`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
switch (msg?.type) {
|
|
183
|
+
case 'ready':
|
|
184
|
+
// eslint-disable-next-line no-console
|
|
185
|
+
console.log(`[calendar:${at.taskId}] ready (events=${(msg.events ?? []).join(',')} ` +
|
|
186
|
+
`titleRegex=${JSON.stringify(msg.titleRegex ?? '')} ` +
|
|
187
|
+
`calendar=${JSON.stringify(msg.calendar ?? '')})`);
|
|
188
|
+
// First successful ready resets backoff so a transient EventKit
|
|
189
|
+
// hiccup doesn't leave us stuck at 30s respawns.
|
|
190
|
+
at.respawnDelayMs = 1_000;
|
|
191
|
+
return;
|
|
192
|
+
case 'event':
|
|
193
|
+
handleEventFire(at, msg, ctx);
|
|
194
|
+
return;
|
|
195
|
+
case 'heartbeat':
|
|
196
|
+
return;
|
|
197
|
+
case 'shutdown':
|
|
198
|
+
return;
|
|
199
|
+
case 'error': {
|
|
200
|
+
const code = String(msg.code ?? '');
|
|
201
|
+
// eslint-disable-next-line no-console
|
|
202
|
+
console.error(`[calendar:${at.taskId}] helper error: ${msg.message} (fix: ${msg.fix ?? '-'})`);
|
|
203
|
+
if (code === 'no_permission')
|
|
204
|
+
at.permissionFailed = true;
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
default:
|
|
208
|
+
// eslint-disable-next-line no-console
|
|
209
|
+
console.warn(`[calendar:${at.taskId}] unknown helper message type: ${msg?.type}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function handleEventFire(at, msg, ctx) {
|
|
213
|
+
// Look up the live task record so we have prompt + permissions.
|
|
214
|
+
// Lazy-import to avoid circular module deps with scheduler.
|
|
215
|
+
import('../taskStore.js').then((m) => {
|
|
216
|
+
const task = m.getTask(at.taskId);
|
|
217
|
+
if (!task || !task.enabled)
|
|
218
|
+
return;
|
|
219
|
+
const startIso = new Date(msg.startMs).toISOString();
|
|
220
|
+
const endIso = msg.endMs ? new Date(msg.endMs).toISOString() : '';
|
|
221
|
+
const ctxLines = [
|
|
222
|
+
`[calendar trigger fired]`,
|
|
223
|
+
`kind: ${msg.kind}`,
|
|
224
|
+
`eventId: ${msg.eventId}`,
|
|
225
|
+
`title: ${msg.title}`,
|
|
226
|
+
`calendar: ${msg.calendar}`,
|
|
227
|
+
`start: ${startIso}`,
|
|
228
|
+
...(endIso ? [`end: ${endIso}`] : []),
|
|
229
|
+
...(msg.location ? [`location: ${msg.location}`] : []),
|
|
230
|
+
...(msg.notes ? [`notes:\n${msg.notes}`] : []),
|
|
231
|
+
];
|
|
232
|
+
const extraContext = ctxLines.join('\n');
|
|
233
|
+
runChildTask({
|
|
234
|
+
taskId: at.taskId,
|
|
235
|
+
yomeBinPath: ctx.yomeBinPath,
|
|
236
|
+
task,
|
|
237
|
+
triggerMeta: {
|
|
238
|
+
triggerKind: 'calendar',
|
|
239
|
+
calendarKind: msg.kind,
|
|
240
|
+
eventId: msg.eventId,
|
|
241
|
+
title: msg.title,
|
|
242
|
+
startMs: msg.startMs,
|
|
243
|
+
},
|
|
244
|
+
extraEnv: { YOME_TASK_EXTRA_CONTEXT: extraContext },
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=calendar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar.js","sourceRoot":"","sources":["../../../src/daemon/triggers/calendar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TaskRecord, RunSummary } from '../taskStore.js';
|
|
2
|
+
export interface ChildRunOptions {
|
|
3
|
+
taskId: string;
|
|
4
|
+
yomeBinPath: string;
|
|
5
|
+
task: TaskRecord;
|
|
6
|
+
/** Free-form metadata appended to run_start log entry (e.g. trigger source). */
|
|
7
|
+
triggerMeta?: Record<string, unknown>;
|
|
8
|
+
/** Extra env vars merged into the child process. Used by triggers to inject
|
|
9
|
+
* runtime context (e.g. YOME_TASK_EXTRA_CONTEXT="[calendar event]…"). */
|
|
10
|
+
extraEnv?: Record<string, string>;
|
|
11
|
+
/** Called after the child exits and stats have been recorded. */
|
|
12
|
+
onComplete?: (summary: RunSummary) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function runChildTask(opts: ChildRunOptions): void;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Spawn a child `yome __run-task <id>` process and wire it up:
|
|
2
|
+
// - capture stdout/stderr
|
|
3
|
+
// - enforce wall-time budget (SIGTERM → SIGKILL)
|
|
4
|
+
// - on exit, parse the last JSON line of stdout for real stats and
|
|
5
|
+
// forward them to recordRun()
|
|
6
|
+
//
|
|
7
|
+
// Used by every trigger (cron / once / file / future calendar) so the
|
|
8
|
+
// behaviour is identical no matter what fired the task.
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
import { recordRun } from '../taskStore.js';
|
|
11
|
+
import { appendLog, openTaskLog } from '../log.js';
|
|
12
|
+
const KILL_GRACE_MS = 3_000;
|
|
13
|
+
export function runChildTask(opts) {
|
|
14
|
+
const { taskId, yomeBinPath, task, triggerMeta, extraEnv, onComplete } = opts;
|
|
15
|
+
const runTs = Date.now();
|
|
16
|
+
const logFile = openTaskLog(taskId, runTs);
|
|
17
|
+
appendLog(logFile, {
|
|
18
|
+
type: 'run_start',
|
|
19
|
+
taskId,
|
|
20
|
+
prompt: task.prompt,
|
|
21
|
+
scheduledFire: true,
|
|
22
|
+
...(triggerMeta ?? {}),
|
|
23
|
+
});
|
|
24
|
+
const maxMs = task.maxDurationMs ?? 5 * 60_000;
|
|
25
|
+
const child = spawn(process.execPath, [yomeBinPath, '__run-task', taskId], {
|
|
26
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
27
|
+
// Env merge order (later wins):
|
|
28
|
+
// 1. parent process.env (daemon's own env)
|
|
29
|
+
// 2. task.env (per-task settings stored in tasks.json,
|
|
30
|
+
// e.g. YOME_WEB_HEADLESS=0)
|
|
31
|
+
// 3. caller-provided extraEnv (trigger-injected runtime context,
|
|
32
|
+
// e.g. YOME_TASK_EXTRA_CONTEXT)
|
|
33
|
+
// 4. YOME_DAEMON_RUN_TS (always last; sourced from this run)
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
...(task.env ?? {}),
|
|
37
|
+
...(extraEnv ?? {}),
|
|
38
|
+
YOME_DAEMON_RUN_TS: String(runTs),
|
|
39
|
+
},
|
|
40
|
+
detached: false,
|
|
41
|
+
});
|
|
42
|
+
let stdout = '';
|
|
43
|
+
let stderr = '';
|
|
44
|
+
child.stdout?.on('data', (d) => { stdout += d.toString('utf-8'); });
|
|
45
|
+
child.stderr?.on('data', (d) => { stderr += d.toString('utf-8'); });
|
|
46
|
+
let timedOut = false;
|
|
47
|
+
const killer = setTimeout(() => {
|
|
48
|
+
timedOut = true;
|
|
49
|
+
appendLog(logFile, { type: 'timeout', maxMs });
|
|
50
|
+
try {
|
|
51
|
+
child.kill('SIGTERM');
|
|
52
|
+
}
|
|
53
|
+
catch { /* noop */ }
|
|
54
|
+
setTimeout(() => { try {
|
|
55
|
+
child.kill('SIGKILL');
|
|
56
|
+
}
|
|
57
|
+
catch { /* noop */ } }, KILL_GRACE_MS);
|
|
58
|
+
}, maxMs);
|
|
59
|
+
child.on('exit', (code, signal) => {
|
|
60
|
+
clearTimeout(killer);
|
|
61
|
+
// The child writes a single JSON status line to stdout on success.
|
|
62
|
+
// Parse the LAST non-empty line so any incidental console.log earlier
|
|
63
|
+
// in the run doesn't break stats — we still get the summary.
|
|
64
|
+
const stats = parseStatsFromStdout(stdout);
|
|
65
|
+
const ok = code === 0 && !timedOut;
|
|
66
|
+
const summary = {
|
|
67
|
+
ts: runTs,
|
|
68
|
+
ok,
|
|
69
|
+
durationMs: Date.now() - runTs,
|
|
70
|
+
toolCalls: stats?.toolCalls ?? 0,
|
|
71
|
+
inputTokens: stats?.inputTokens ?? 0,
|
|
72
|
+
outputTokens: stats?.outputTokens ?? 0,
|
|
73
|
+
error: ok
|
|
74
|
+
? undefined
|
|
75
|
+
: timedOut
|
|
76
|
+
? `timeout after ${maxMs}ms`
|
|
77
|
+
: (stats?.error ?? `exit=${code} signal=${signal} stderr=${stderr.slice(0, 500)}`),
|
|
78
|
+
};
|
|
79
|
+
appendLog(logFile, {
|
|
80
|
+
type: 'run_end',
|
|
81
|
+
ok,
|
|
82
|
+
timedOut,
|
|
83
|
+
exitCode: code,
|
|
84
|
+
signal,
|
|
85
|
+
toolCalls: summary.toolCalls,
|
|
86
|
+
inputTokens: summary.inputTokens,
|
|
87
|
+
outputTokens: summary.outputTokens,
|
|
88
|
+
stdoutTail: stdout.slice(-500),
|
|
89
|
+
stderrTail: stderr.slice(-500),
|
|
90
|
+
});
|
|
91
|
+
recordRun(taskId, summary);
|
|
92
|
+
onComplete?.(summary);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function parseStatsFromStdout(stdout) {
|
|
96
|
+
const lines = stdout.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
97
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
98
|
+
const line = lines[i];
|
|
99
|
+
if (!line.startsWith('{'))
|
|
100
|
+
continue;
|
|
101
|
+
try {
|
|
102
|
+
const j = JSON.parse(line);
|
|
103
|
+
if (typeof j === 'object' && j && 'taskId' in j && 'ok' in j) {
|
|
104
|
+
return j;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch { /* not JSON, keep scanning back */ }
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=childRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"childRunner.js","sourceRoot":"","sources":["../../../src/daemon/triggers/childRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TaskRecord } from '../taskStore.js';
|
|
2
|
+
export declare function registerCronTask(task: TaskRecord, opts: {
|
|
3
|
+
yomeBinPath: string;
|
|
4
|
+
}): void;
|
|
5
|
+
export declare function unregisterCronTask(id: string): void;
|
|
6
|
+
export declare function listActive(): string[];
|
|
7
|
+
export declare function unregisterAll(): void;
|
|
8
|
+
/** Manually fire a task by id (used by `yome cron run <id>` and the scheduler). */
|
|
9
|
+
export declare function fireTask(taskId: string, yomeBinPath: string, task: TaskRecord): void;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the absolute path to bin/yome.js from this module's location.
|
|
12
|
+
* Works whether the daemon is launched from source (tsx) or built dist/.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveYomeBinPath(): string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Time-based trigger backed by node-cron.
|
|
2
|
+
//
|
|
3
|
+
// One node-cron task per registered TaskRecord with kind='cron'. When the
|
|
4
|
+
// schedule fires we spawn the runner OUT-OF-PROCESS (a child node process
|
|
5
|
+
// running `cli/bin/yome.js __run-task <id>`) so that:
|
|
6
|
+
// - a crashing task can't take down the daemon
|
|
7
|
+
// - we can enforce a wall-time budget by killing the child
|
|
8
|
+
// - the LLM long-poll doesn't block the scheduler from firing other
|
|
9
|
+
// tasks in parallel
|
|
10
|
+
import cron from 'node-cron';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { dirname, resolve } from 'path';
|
|
13
|
+
import { runChildTask } from './childRunner.js';
|
|
14
|
+
const active = new Map();
|
|
15
|
+
export function registerCronTask(task, opts) {
|
|
16
|
+
if (task.trigger.kind !== 'cron')
|
|
17
|
+
return;
|
|
18
|
+
if (!task.enabled)
|
|
19
|
+
return;
|
|
20
|
+
const schedule = task.trigger.schedule;
|
|
21
|
+
const tz = task.trigger.tz;
|
|
22
|
+
if (!cron.validate(schedule)) {
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
24
|
+
console.error(`[cron] task ${task.id}: invalid schedule '${schedule}', skipping`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Idempotent re-register: tasks.json gets rewritten by recordRun()
|
|
28
|
+
// every time ANY task fires, which triggers fs.watch →
|
|
29
|
+
// reloadAllTasks() → registerCronTask for every cron task. Without
|
|
30
|
+
// this guard each cron reload would tear down the node-cron
|
|
31
|
+
// ScheduledTask (which has its own internal next-fire timer) and
|
|
32
|
+
// build a fresh one, slightly drifting the firing wall-clock and
|
|
33
|
+
// wasting CPU. When schedule + tz are unchanged, leave it alone —
|
|
34
|
+
// node-cron's job already keeps a reference to the latest TaskRecord
|
|
35
|
+
// via the closure, but the prompt/env can change so we update the
|
|
36
|
+
// task pointer without touching the timer.
|
|
37
|
+
const existing = active.get(task.id);
|
|
38
|
+
if (existing && existing.schedule === schedule && existing.tz === tz) {
|
|
39
|
+
existing.task = task; // refresh prompt/env/allow rules
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (existing) {
|
|
43
|
+
unregisterCronTask(task.id);
|
|
44
|
+
}
|
|
45
|
+
const job = cron.schedule(schedule, () => {
|
|
46
|
+
// Always re-read the latest TaskRecord at fire time so any
|
|
47
|
+
// mid-flight prompt/allow edits land in the spawned child.
|
|
48
|
+
const latest = active.get(task.id)?.task ?? task;
|
|
49
|
+
fireTask(task.id, opts.yomeBinPath, latest);
|
|
50
|
+
}, tz ? { timezone: tz } : undefined);
|
|
51
|
+
active.set(task.id, { task, job, schedule, tz });
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.log(`[cron] registered ${task.id} (schedule="${schedule}", tz=${tz ?? 'local'})`);
|
|
54
|
+
}
|
|
55
|
+
export function unregisterCronTask(id) {
|
|
56
|
+
const entry = active.get(id);
|
|
57
|
+
if (!entry)
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
entry.job.stop();
|
|
61
|
+
}
|
|
62
|
+
catch { /* noop */ }
|
|
63
|
+
active.delete(id);
|
|
64
|
+
}
|
|
65
|
+
export function listActive() {
|
|
66
|
+
return [...active.keys()];
|
|
67
|
+
}
|
|
68
|
+
export function unregisterAll() {
|
|
69
|
+
for (const id of [...active.keys()])
|
|
70
|
+
unregisterCronTask(id);
|
|
71
|
+
}
|
|
72
|
+
/** Manually fire a task by id (used by `yome cron run <id>` and the scheduler). */
|
|
73
|
+
export function fireTask(taskId, yomeBinPath, task) {
|
|
74
|
+
runChildTask({
|
|
75
|
+
taskId,
|
|
76
|
+
yomeBinPath,
|
|
77
|
+
task,
|
|
78
|
+
triggerMeta: { triggerKind: 'cron', schedule: task.trigger.schedule },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the absolute path to bin/yome.js from this module's location.
|
|
83
|
+
* Works whether the daemon is launched from source (tsx) or built dist/.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveYomeBinPath() {
|
|
86
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
87
|
+
// dist layout: dist/daemon/triggers/cron.js → ../../../bin/yome.js
|
|
88
|
+
// src layout: src/daemon/triggers/cron.ts → ../../../bin/yome.js
|
|
89
|
+
return resolve(here, '..', '..', '..', 'bin', 'yome.js');
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=cron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../../src/daemon/triggers/cron.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TaskRecord } from '../taskStore.js';
|
|
2
|
+
export declare function registerFileTask(task: TaskRecord, opts: {
|
|
3
|
+
yomeBinPath: string;
|
|
4
|
+
}): void;
|
|
5
|
+
export declare function unregisterFileTask(id: string): void;
|
|
6
|
+
export declare function listActiveFile(): string[];
|
|
7
|
+
export declare function unregisterAllFile(): void;
|