@poping/yome 0.0.2 → 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/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +306 -27
- package/README.zh-CN.md +333 -0
- package/bin/yome-calwatch +0 -0
- package/dist/agent.d.ts +24 -2
- package/dist/agent.js +34 -2
- package/dist/agent.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js +121 -13
- 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 +240 -15
- package/dist/index.js.map +1 -1
- package/dist/llm.js +45 -2
- package/dist/llm.js.map +1 -1
- package/dist/loops/chain.js +8 -0
- package/dist/loops/chain.js.map +1 -1
- package/dist/loops/evaluator.js +8 -0
- package/dist/loops/evaluator.js.map +1 -1
- package/dist/loops/orchestrator.js +8 -0
- package/dist/loops/orchestrator.js.map +1 -1
- package/dist/loops/parallel.js.map +1 -1
- package/dist/loops/route.js +8 -0
- package/dist/loops/route.js.map +1 -1
- package/dist/loops/simple.js +15 -0
- package/dist/loops/simple.js.map +1 -1
- package/dist/permissions/index.d.ts +1 -1
- package/dist/permissions/index.js +1 -1
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/loader.d.ts +20 -1
- package/dist/permissions/loader.js +51 -0
- package/dist/permissions/loader.js.map +1 -1
- package/dist/redact.d.ts +56 -0
- package/dist/redact.js +191 -0
- package/dist/redact.js.map +1 -0
- package/dist/skills/runner/applescript.d.ts +49 -0
- package/dist/skills/runner/applescript.js +100 -0
- package/dist/skills/runner/applescript.js.map +1 -0
- package/dist/skills/runner/dispatcher.d.ts +128 -0
- package/dist/skills/runner/dispatcher.js +617 -0
- package/dist/skills/runner/dispatcher.js.map +1 -0
- 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.d.ts +8 -0
- package/dist/skills/runner/kernel.js +731 -0
- package/dist/skills/runner/kernel.js.map +1 -0
- 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/skills/runner/tokenizer.d.ts +36 -0
- package/dist/skills/runner/tokenizer.js +177 -0
- package/dist/skills/runner/tokenizer.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/threadCli.d.ts +11 -0
- package/dist/threadCli.js +177 -0
- package/dist/threadCli.js.map +1 -0
- package/dist/threadShare.d.ts +21 -0
- package/dist/threadShare.js +121 -0
- package/dist/threadShare.js.map +1 -0
- package/dist/threadSubmit.d.ts +32 -0
- package/dist/threadSubmit.js +199 -0
- package/dist/threadSubmit.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 +63 -14
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.d.ts +24 -2
- package/dist/tools/index.js +54 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/skillCall.d.ts +2 -0
- package/dist/tools/skillCall.js +77 -0
- package/dist/tools/skillCall.js.map +1 -0
- 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/AgentPicker.js +3 -3
- package/dist/ui/AgentPicker.js.map +1 -1
- package/dist/ui/App.js +263 -61
- 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/Banner.d.ts +2 -1
- package/dist/ui/Banner.js +23 -4
- package/dist/ui/Banner.js.map +1 -1
- package/dist/ui/InputBar.js +25 -36
- package/dist/ui/InputBar.js.map +1 -1
- package/dist/ui/Markdown.d.ts +2 -2
- package/dist/ui/Markdown.js +22 -7
- package/dist/ui/Markdown.js.map +1 -1
- package/dist/ui/MarketplacePicker.d.ts +7 -0
- package/dist/ui/MarketplacePicker.js +122 -0
- package/dist/ui/MarketplacePicker.js.map +1 -0
- package/dist/ui/MessageList.d.ts +12 -1
- package/dist/ui/MessageList.js +72 -7
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/ModelPicker.js +4 -4
- package/dist/ui/ModelPicker.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/PermissionPrompt.d.ts +16 -4
- package/dist/ui/PermissionPrompt.js +60 -15
- package/dist/ui/PermissionPrompt.js.map +1 -1
- package/dist/ui/SessionPicker.js +2 -2
- package/dist/ui/SessionPicker.js.map +1 -1
- package/dist/ui/ShimmerText.d.ts +8 -0
- package/dist/ui/ShimmerText.js +40 -0
- package/dist/ui/ShimmerText.js.map +1 -0
- package/dist/ui/Spinner.js +3 -9
- package/dist/ui/Spinner.js.map +1 -1
- 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/ui/TogglePicker.js +4 -4
- package/dist/ui/TogglePicker.js.map +1 -1
- package/dist/ui/ToolResult.js +6 -0
- package/dist/ui/ToolResult.js.map +1 -1
- package/dist/ui/UnifiedSkillsPicker.d.ts +10 -0
- package/dist/ui/UnifiedSkillsPicker.js +63 -0
- package/dist/ui/UnifiedSkillsPicker.js.map +1 -0
- package/dist/ui/animation.d.ts +3 -0
- package/dist/ui/animation.js +48 -0
- package/dist/ui/animation.js.map +1 -0
- package/dist/ui/useThrottledStream.d.ts +7 -0
- package/dist/ui/useThrottledStream.js +63 -0
- package/dist/ui/useThrottledStream.js.map +1 -0
- package/dist/yomeSkills/auth.d.ts +20 -0
- package/dist/yomeSkills/auth.js +70 -0
- package/dist/yomeSkills/auth.js.map +1 -0
- package/dist/yomeSkills/blacklist.d.ts +33 -0
- package/dist/yomeSkills/blacklist.js +101 -0
- package/dist/yomeSkills/blacklist.js.map +1 -0
- package/dist/yomeSkills/capabilities.d.ts +54 -0
- package/dist/yomeSkills/capabilities.js +175 -0
- package/dist/yomeSkills/capabilities.js.map +1 -0
- package/dist/yomeSkills/capabilityGuard.d.ts +31 -0
- package/dist/yomeSkills/capabilityGuard.js +113 -0
- package/dist/yomeSkills/capabilityGuard.js.map +1 -0
- package/dist/yomeSkills/cli.d.ts +25 -0
- package/dist/yomeSkills/cli.js +624 -0
- package/dist/yomeSkills/cli.js.map +1 -0
- 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/devLink.d.ts +17 -0
- package/dist/yomeSkills/devLink.js +91 -0
- package/dist/yomeSkills/devLink.js.map +1 -0
- package/dist/yomeSkills/doctor.d.ts +13 -0
- package/dist/yomeSkills/doctor.js +152 -0
- package/dist/yomeSkills/doctor.js.map +1 -0
- package/dist/yomeSkills/enable.d.ts +8 -0
- package/dist/yomeSkills/enable.js +67 -0
- package/dist/yomeSkills/enable.js.map +1 -0
- package/dist/yomeSkills/hubPing.d.ts +1 -0
- package/dist/yomeSkills/hubPing.js +41 -0
- package/dist/yomeSkills/hubPing.js.map +1 -0
- package/dist/yomeSkills/install.d.ts +18 -0
- package/dist/yomeSkills/install.js +143 -0
- package/dist/yomeSkills/install.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 +33 -0
- package/dist/yomeSkills/installGithub.js +213 -0
- package/dist/yomeSkills/installGithub.js.map +1 -0
- package/dist/yomeSkills/installMeta.d.ts +8 -0
- package/dist/yomeSkills/installMeta.js +76 -0
- package/dist/yomeSkills/installMeta.js.map +1 -0
- package/dist/yomeSkills/integrity.d.ts +26 -0
- package/dist/yomeSkills/integrity.js +107 -0
- package/dist/yomeSkills/integrity.js.map +1 -0
- package/dist/yomeSkills/invoke.d.ts +29 -0
- package/dist/yomeSkills/invoke.js +135 -0
- package/dist/yomeSkills/invoke.js.map +1 -0
- package/dist/yomeSkills/list.d.ts +11 -0
- package/dist/yomeSkills/list.js +55 -0
- package/dist/yomeSkills/list.js.map +1 -0
- package/dist/yomeSkills/login.d.ts +41 -0
- package/dist/yomeSkills/login.js +221 -0
- package/dist/yomeSkills/login.js.map +1 -0
- package/dist/yomeSkills/manifest.d.ts +60 -0
- package/dist/yomeSkills/manifest.js +47 -0
- package/dist/yomeSkills/manifest.js.map +1 -0
- package/dist/yomeSkills/paths.d.ts +13 -0
- package/dist/yomeSkills/paths.js +33 -0
- package/dist/yomeSkills/paths.js.map +1 -0
- package/dist/yomeSkills/publish.d.ts +18 -0
- package/dist/yomeSkills/publish.js +114 -0
- package/dist/yomeSkills/publish.js.map +1 -0
- package/dist/yomeSkills/rollback.d.ts +10 -0
- package/dist/yomeSkills/rollback.js +83 -0
- package/dist/yomeSkills/rollback.js.map +1 -0
- package/dist/yomeSkills/search.d.ts +21 -0
- package/dist/yomeSkills/search.js +31 -0
- package/dist/yomeSkills/search.js.map +1 -0
- package/dist/yomeSkills/skillsIndex.d.ts +36 -0
- package/dist/yomeSkills/skillsIndex.js +111 -0
- package/dist/yomeSkills/skillsIndex.js.map +1 -0
- package/dist/yomeSkills/unified.d.ts +53 -0
- package/dist/yomeSkills/unified.js +187 -0
- package/dist/yomeSkills/unified.js.map +1 -0
- package/dist/yomeSkills/uninstall.d.ts +7 -0
- package/dist/yomeSkills/uninstall.js +22 -0
- package/dist/yomeSkills/uninstall.js.map +1 -0
- package/dist/yomeSkills/update.d.ts +18 -0
- package/dist/yomeSkills/update.js +75 -0
- package/dist/yomeSkills/update.js.map +1 -0
- package/dist/yomeSkills/validate.d.ts +11 -0
- package/dist/yomeSkills/validate.js +99 -0
- package/dist/yomeSkills/validate.js.map +1 -0
- package/package.json +23 -6
|
@@ -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;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// File-system trigger backed by chokidar.
|
|
2
|
+
//
|
|
3
|
+
// Storage shape:
|
|
4
|
+
// { kind: 'file', path: '<glob>', events?: ['change'|'add'|'unlink'][] }
|
|
5
|
+
//
|
|
6
|
+
// Path can be a single file, a directory (recursive), or a glob
|
|
7
|
+
// ('~/Desktop/test/**/*.xlsx'). chokidar handles tilde-expansion poorly,
|
|
8
|
+
// so we expand it ourselves before handing the pattern over.
|
|
9
|
+
//
|
|
10
|
+
// Debouncing: chokidar fires multiple events for a single save on some
|
|
11
|
+
// editors (e.g. Excel writes a temp file then renames). We coalesce
|
|
12
|
+
// per-task into a 750ms window so the agent gets called once per logical
|
|
13
|
+
// change burst, not once per low-level fs syscall.
|
|
14
|
+
import chokidar from 'chokidar';
|
|
15
|
+
import { homedir } from 'os';
|
|
16
|
+
import { runChildTask } from './childRunner.js';
|
|
17
|
+
function sameEvents(a, b) {
|
|
18
|
+
if (a.length !== b.length)
|
|
19
|
+
return false;
|
|
20
|
+
const sa = [...a].sort();
|
|
21
|
+
const sb = [...b].sort();
|
|
22
|
+
return sa.every((v, i) => v === sb[i]);
|
|
23
|
+
}
|
|
24
|
+
const active = new Map();
|
|
25
|
+
const debouncers = new Map();
|
|
26
|
+
const DEBOUNCE_MS = 750;
|
|
27
|
+
const DEFAULT_EVENTS = ['add', 'change'];
|
|
28
|
+
export function registerFileTask(task, opts) {
|
|
29
|
+
if (task.trigger.kind !== 'file')
|
|
30
|
+
return;
|
|
31
|
+
if (!task.enabled)
|
|
32
|
+
return;
|
|
33
|
+
const rawPath = task.trigger.path;
|
|
34
|
+
if (!rawPath || typeof rawPath !== 'string') {
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.error(`[file] task ${task.id}: missing path, skipping`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const expanded = expandTilde(rawPath);
|
|
40
|
+
const events = (task.trigger.events ?? DEFAULT_EVENTS);
|
|
41
|
+
// Idempotent re-registration: every recordRun() rewrites tasks.json
|
|
42
|
+
// which fires fs.watch → reloadAllTasks(). If we always tore down the
|
|
43
|
+
// chokidar watcher and rebuilt it, we'd waste ~tens of ms per reload
|
|
44
|
+
// AND briefly drop events that arrive between close() and the new
|
|
45
|
+
// watcher's "ready". When the path + events haven't changed, leave
|
|
46
|
+
// the existing watcher alone.
|
|
47
|
+
const existing = active.get(task.id);
|
|
48
|
+
if (existing && existing.path === expanded && sameEvents(existing.events, events)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (existing) {
|
|
52
|
+
unregisterFileTask(task.id);
|
|
53
|
+
}
|
|
54
|
+
const watcher = chokidar.watch(expanded, {
|
|
55
|
+
persistent: true,
|
|
56
|
+
ignoreInitial: true, // don't fire for files that already exist on startup
|
|
57
|
+
awaitWriteFinish: {
|
|
58
|
+
stabilityThreshold: 250,
|
|
59
|
+
pollInterval: 100,
|
|
60
|
+
},
|
|
61
|
+
// Use polling on macOS for network mounts / weird filesystems if
|
|
62
|
+
// we hit issues; default native fsevents is faster and we'll prefer
|
|
63
|
+
// it. The user can override via env if needed.
|
|
64
|
+
usePolling: process.env.YOME_FILE_WATCH_POLL === '1',
|
|
65
|
+
});
|
|
66
|
+
const onEvent = (kind, file) => {
|
|
67
|
+
if (!events.includes(kind))
|
|
68
|
+
return;
|
|
69
|
+
if (debouncers.has(task.id))
|
|
70
|
+
clearTimeout(debouncers.get(task.id));
|
|
71
|
+
const t = setTimeout(() => {
|
|
72
|
+
debouncers.delete(task.id);
|
|
73
|
+
runChildTask({
|
|
74
|
+
taskId: task.id,
|
|
75
|
+
yomeBinPath: opts.yomeBinPath,
|
|
76
|
+
task,
|
|
77
|
+
triggerMeta: { triggerKind: 'file', event: kind, file },
|
|
78
|
+
});
|
|
79
|
+
}, DEBOUNCE_MS);
|
|
80
|
+
t.unref?.();
|
|
81
|
+
debouncers.set(task.id, t);
|
|
82
|
+
};
|
|
83
|
+
watcher.on('add', (f) => onEvent('add', f));
|
|
84
|
+
watcher.on('change', (f) => onEvent('change', f));
|
|
85
|
+
watcher.on('unlink', (f) => onEvent('unlink', f));
|
|
86
|
+
watcher.on('error', (e) => {
|
|
87
|
+
// eslint-disable-next-line no-console
|
|
88
|
+
console.error(`[file] task ${task.id}: watcher error:`, e);
|
|
89
|
+
});
|
|
90
|
+
active.set(task.id, { taskId: task.id, watcher, path: expanded, events });
|
|
91
|
+
// eslint-disable-next-line no-console
|
|
92
|
+
console.log(`[file] registered ${task.id} (path="${expanded}", events=[${events.join(',')}])`);
|
|
93
|
+
}
|
|
94
|
+
export function unregisterFileTask(id) {
|
|
95
|
+
const entry = active.get(id);
|
|
96
|
+
if (!entry)
|
|
97
|
+
return;
|
|
98
|
+
try {
|
|
99
|
+
entry.watcher.close();
|
|
100
|
+
}
|
|
101
|
+
catch { /* noop */ }
|
|
102
|
+
active.delete(id);
|
|
103
|
+
const d = debouncers.get(id);
|
|
104
|
+
if (d) {
|
|
105
|
+
clearTimeout(d);
|
|
106
|
+
debouncers.delete(id);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function listActiveFile() {
|
|
110
|
+
return [...active.keys()];
|
|
111
|
+
}
|
|
112
|
+
export function unregisterAllFile() {
|
|
113
|
+
for (const id of [...active.keys()])
|
|
114
|
+
unregisterFileTask(id);
|
|
115
|
+
}
|
|
116
|
+
function expandTilde(p) {
|
|
117
|
+
if (p === '~')
|
|
118
|
+
return homedir();
|
|
119
|
+
if (p.startsWith('~/'))
|
|
120
|
+
return homedir() + p.slice(1);
|
|
121
|
+
return p;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../src/daemon/triggers/file.ts"],"names":[],"mappings":"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"}
|