@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.
Files changed (276) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +11 -0
  3. package/README.md +306 -27
  4. package/README.zh-CN.md +333 -0
  5. package/bin/yome-calwatch +0 -0
  6. package/dist/agent.d.ts +24 -2
  7. package/dist/agent.js +34 -2
  8. package/dist/agent.js.map +1 -1
  9. package/dist/context.d.ts +2 -0
  10. package/dist/context.js +121 -13
  11. package/dist/context.js.map +1 -1
  12. package/dist/daemon/calendarPermission.d.ts +10 -0
  13. package/dist/daemon/calendarPermission.js +68 -0
  14. package/dist/daemon/calendarPermission.js.map +1 -0
  15. package/dist/daemon/cronCli.d.ts +19 -0
  16. package/dist/daemon/cronCli.js +403 -0
  17. package/dist/daemon/cronCli.js.map +1 -0
  18. package/dist/daemon/envHint.d.ts +5 -0
  19. package/dist/daemon/envHint.js +139 -0
  20. package/dist/daemon/envHint.js.map +1 -0
  21. package/dist/daemon/humanCron.d.ts +1 -0
  22. package/dist/daemon/humanCron.js +72 -0
  23. package/dist/daemon/humanCron.js.map +1 -0
  24. package/dist/daemon/humanOnce.d.ts +1 -0
  25. package/dist/daemon/humanOnce.js +54 -0
  26. package/dist/daemon/humanOnce.js.map +1 -0
  27. package/dist/daemon/index.d.ts +5 -0
  28. package/dist/daemon/index.js +168 -0
  29. package/dist/daemon/index.js.map +1 -0
  30. package/dist/daemon/launchd.d.ts +7 -0
  31. package/dist/daemon/launchd.js +93 -0
  32. package/dist/daemon/launchd.js.map +1 -0
  33. package/dist/daemon/log.d.ts +17 -0
  34. package/dist/daemon/log.js +57 -0
  35. package/dist/daemon/log.js.map +1 -0
  36. package/dist/daemon/paths.d.ts +11 -0
  37. package/dist/daemon/paths.js +27 -0
  38. package/dist/daemon/paths.js.map +1 -0
  39. package/dist/daemon/runTaskEntry.d.ts +1 -0
  40. package/dist/daemon/runTaskEntry.js +67 -0
  41. package/dist/daemon/runTaskEntry.js.map +1 -0
  42. package/dist/daemon/runner.d.ts +21 -0
  43. package/dist/daemon/runner.js +175 -0
  44. package/dist/daemon/runner.js.map +1 -0
  45. package/dist/daemon/scheduler.d.ts +5 -0
  46. package/dist/daemon/scheduler.js +162 -0
  47. package/dist/daemon/scheduler.js.map +1 -0
  48. package/dist/daemon/taskStore.d.ts +62 -0
  49. package/dist/daemon/taskStore.js +88 -0
  50. package/dist/daemon/taskStore.js.map +1 -0
  51. package/dist/daemon/triggers/calendar.d.ts +8 -0
  52. package/dist/daemon/triggers/calendar.js +248 -0
  53. package/dist/daemon/triggers/calendar.js.map +1 -0
  54. package/dist/daemon/triggers/childRunner.d.ts +14 -0
  55. package/dist/daemon/triggers/childRunner.js +111 -0
  56. package/dist/daemon/triggers/childRunner.js.map +1 -0
  57. package/dist/daemon/triggers/cron.d.ts +14 -0
  58. package/dist/daemon/triggers/cron.js +91 -0
  59. package/dist/daemon/triggers/cron.js.map +1 -0
  60. package/dist/daemon/triggers/file.d.ts +7 -0
  61. package/dist/daemon/triggers/file.js +123 -0
  62. package/dist/daemon/triggers/file.js.map +1 -0
  63. package/dist/daemon/triggers/once.d.ts +10 -0
  64. package/dist/daemon/triggers/once.js +80 -0
  65. package/dist/daemon/triggers/once.js.map +1 -0
  66. package/dist/index.js +240 -15
  67. package/dist/index.js.map +1 -1
  68. package/dist/llm.js +45 -2
  69. package/dist/llm.js.map +1 -1
  70. package/dist/loops/chain.js +8 -0
  71. package/dist/loops/chain.js.map +1 -1
  72. package/dist/loops/evaluator.js +8 -0
  73. package/dist/loops/evaluator.js.map +1 -1
  74. package/dist/loops/orchestrator.js +8 -0
  75. package/dist/loops/orchestrator.js.map +1 -1
  76. package/dist/loops/parallel.js.map +1 -1
  77. package/dist/loops/route.js +8 -0
  78. package/dist/loops/route.js.map +1 -1
  79. package/dist/loops/simple.js +15 -0
  80. package/dist/loops/simple.js.map +1 -1
  81. package/dist/permissions/index.d.ts +1 -1
  82. package/dist/permissions/index.js +1 -1
  83. package/dist/permissions/index.js.map +1 -1
  84. package/dist/permissions/loader.d.ts +20 -1
  85. package/dist/permissions/loader.js +51 -0
  86. package/dist/permissions/loader.js.map +1 -1
  87. package/dist/redact.d.ts +56 -0
  88. package/dist/redact.js +191 -0
  89. package/dist/redact.js.map +1 -0
  90. package/dist/skills/runner/applescript.d.ts +49 -0
  91. package/dist/skills/runner/applescript.js +100 -0
  92. package/dist/skills/runner/applescript.js.map +1 -0
  93. package/dist/skills/runner/dispatcher.d.ts +128 -0
  94. package/dist/skills/runner/dispatcher.js +617 -0
  95. package/dist/skills/runner/dispatcher.js.map +1 -0
  96. package/dist/skills/runner/dispatcher.test.d.ts +1 -0
  97. package/dist/skills/runner/dispatcher.test.js +141 -0
  98. package/dist/skills/runner/dispatcher.test.js.map +1 -0
  99. package/dist/skills/runner/kernel.d.ts +8 -0
  100. package/dist/skills/runner/kernel.js +731 -0
  101. package/dist/skills/runner/kernel.js.map +1 -0
  102. package/dist/skills/runner/nodeBackend.d.ts +32 -0
  103. package/dist/skills/runner/nodeBackend.js +147 -0
  104. package/dist/skills/runner/nodeBackend.js.map +1 -0
  105. package/dist/skills/runner/tokenizer.d.ts +36 -0
  106. package/dist/skills/runner/tokenizer.js +177 -0
  107. package/dist/skills/runner/tokenizer.js.map +1 -0
  108. package/dist/state/todos.d.ts +12 -0
  109. package/dist/state/todos.js +32 -0
  110. package/dist/state/todos.js.map +1 -0
  111. package/dist/threadCli.d.ts +11 -0
  112. package/dist/threadCli.js +177 -0
  113. package/dist/threadCli.js.map +1 -0
  114. package/dist/threadShare.d.ts +21 -0
  115. package/dist/threadShare.js +121 -0
  116. package/dist/threadShare.js.map +1 -0
  117. package/dist/threadSubmit.d.ts +32 -0
  118. package/dist/threadSubmit.js +199 -0
  119. package/dist/threadSubmit.js.map +1 -0
  120. package/dist/tools/askUser.d.ts +20 -0
  121. package/dist/tools/askUser.js +126 -0
  122. package/dist/tools/askUser.js.map +1 -0
  123. package/dist/tools/bash.js +63 -14
  124. package/dist/tools/bash.js.map +1 -1
  125. package/dist/tools/index.d.ts +24 -2
  126. package/dist/tools/index.js +54 -5
  127. package/dist/tools/index.js.map +1 -1
  128. package/dist/tools/skillCall.d.ts +2 -0
  129. package/dist/tools/skillCall.js +77 -0
  130. package/dist/tools/skillCall.js.map +1 -0
  131. package/dist/tools/todoWrite.d.ts +2 -0
  132. package/dist/tools/todoWrite.js +141 -0
  133. package/dist/tools/todoWrite.js.map +1 -0
  134. package/dist/tools/yome.d.ts +2 -0
  135. package/dist/tools/yome.js +87 -0
  136. package/dist/tools/yome.js.map +1 -0
  137. package/dist/ui/AgentPicker.js +3 -3
  138. package/dist/ui/AgentPicker.js.map +1 -1
  139. package/dist/ui/App.js +263 -61
  140. package/dist/ui/App.js.map +1 -1
  141. package/dist/ui/AskUserPrompt.d.ts +7 -0
  142. package/dist/ui/AskUserPrompt.js +78 -0
  143. package/dist/ui/AskUserPrompt.js.map +1 -0
  144. package/dist/ui/Banner.d.ts +2 -1
  145. package/dist/ui/Banner.js +23 -4
  146. package/dist/ui/Banner.js.map +1 -1
  147. package/dist/ui/InputBar.js +25 -36
  148. package/dist/ui/InputBar.js.map +1 -1
  149. package/dist/ui/Markdown.d.ts +2 -2
  150. package/dist/ui/Markdown.js +22 -7
  151. package/dist/ui/Markdown.js.map +1 -1
  152. package/dist/ui/MarketplacePicker.d.ts +7 -0
  153. package/dist/ui/MarketplacePicker.js +122 -0
  154. package/dist/ui/MarketplacePicker.js.map +1 -0
  155. package/dist/ui/MessageList.d.ts +12 -1
  156. package/dist/ui/MessageList.js +72 -7
  157. package/dist/ui/MessageList.js.map +1 -1
  158. package/dist/ui/ModelPicker.js +4 -4
  159. package/dist/ui/ModelPicker.js.map +1 -1
  160. package/dist/ui/MultilineTextInput.d.ts +31 -0
  161. package/dist/ui/MultilineTextInput.js +393 -0
  162. package/dist/ui/MultilineTextInput.js.map +1 -0
  163. package/dist/ui/MultilineTextInput.test.d.ts +1 -0
  164. package/dist/ui/MultilineTextInput.test.js +30 -0
  165. package/dist/ui/MultilineTextInput.test.js.map +1 -0
  166. package/dist/ui/PermissionPrompt.d.ts +16 -4
  167. package/dist/ui/PermissionPrompt.js +60 -15
  168. package/dist/ui/PermissionPrompt.js.map +1 -1
  169. package/dist/ui/SessionPicker.js +2 -2
  170. package/dist/ui/SessionPicker.js.map +1 -1
  171. package/dist/ui/ShimmerText.d.ts +8 -0
  172. package/dist/ui/ShimmerText.js +40 -0
  173. package/dist/ui/ShimmerText.js.map +1 -0
  174. package/dist/ui/Spinner.js +3 -9
  175. package/dist/ui/Spinner.js.map +1 -1
  176. package/dist/ui/TodoPanel.d.ts +7 -0
  177. package/dist/ui/TodoPanel.js +36 -0
  178. package/dist/ui/TodoPanel.js.map +1 -0
  179. package/dist/ui/TogglePicker.js +4 -4
  180. package/dist/ui/TogglePicker.js.map +1 -1
  181. package/dist/ui/ToolResult.js +6 -0
  182. package/dist/ui/ToolResult.js.map +1 -1
  183. package/dist/ui/UnifiedSkillsPicker.d.ts +10 -0
  184. package/dist/ui/UnifiedSkillsPicker.js +63 -0
  185. package/dist/ui/UnifiedSkillsPicker.js.map +1 -0
  186. package/dist/ui/animation.d.ts +3 -0
  187. package/dist/ui/animation.js +48 -0
  188. package/dist/ui/animation.js.map +1 -0
  189. package/dist/ui/useThrottledStream.d.ts +7 -0
  190. package/dist/ui/useThrottledStream.js +63 -0
  191. package/dist/ui/useThrottledStream.js.map +1 -0
  192. package/dist/yomeSkills/auth.d.ts +20 -0
  193. package/dist/yomeSkills/auth.js +70 -0
  194. package/dist/yomeSkills/auth.js.map +1 -0
  195. package/dist/yomeSkills/blacklist.d.ts +33 -0
  196. package/dist/yomeSkills/blacklist.js +101 -0
  197. package/dist/yomeSkills/blacklist.js.map +1 -0
  198. package/dist/yomeSkills/capabilities.d.ts +54 -0
  199. package/dist/yomeSkills/capabilities.js +175 -0
  200. package/dist/yomeSkills/capabilities.js.map +1 -0
  201. package/dist/yomeSkills/capabilityGuard.d.ts +31 -0
  202. package/dist/yomeSkills/capabilityGuard.js +113 -0
  203. package/dist/yomeSkills/capabilityGuard.js.map +1 -0
  204. package/dist/yomeSkills/cli.d.ts +25 -0
  205. package/dist/yomeSkills/cli.js +624 -0
  206. package/dist/yomeSkills/cli.js.map +1 -0
  207. package/dist/yomeSkills/deprecate.d.ts +29 -0
  208. package/dist/yomeSkills/deprecate.js +99 -0
  209. package/dist/yomeSkills/deprecate.js.map +1 -0
  210. package/dist/yomeSkills/devLink.d.ts +17 -0
  211. package/dist/yomeSkills/devLink.js +91 -0
  212. package/dist/yomeSkills/devLink.js.map +1 -0
  213. package/dist/yomeSkills/doctor.d.ts +13 -0
  214. package/dist/yomeSkills/doctor.js +152 -0
  215. package/dist/yomeSkills/doctor.js.map +1 -0
  216. package/dist/yomeSkills/enable.d.ts +8 -0
  217. package/dist/yomeSkills/enable.js +67 -0
  218. package/dist/yomeSkills/enable.js.map +1 -0
  219. package/dist/yomeSkills/hubPing.d.ts +1 -0
  220. package/dist/yomeSkills/hubPing.js +41 -0
  221. package/dist/yomeSkills/hubPing.js.map +1 -0
  222. package/dist/yomeSkills/install.d.ts +18 -0
  223. package/dist/yomeSkills/install.js +143 -0
  224. package/dist/yomeSkills/install.js.map +1 -0
  225. package/dist/yomeSkills/installFromHubTarball.d.ts +26 -0
  226. package/dist/yomeSkills/installFromHubTarball.js +161 -0
  227. package/dist/yomeSkills/installFromHubTarball.js.map +1 -0
  228. package/dist/yomeSkills/installGithub.d.ts +33 -0
  229. package/dist/yomeSkills/installGithub.js +213 -0
  230. package/dist/yomeSkills/installGithub.js.map +1 -0
  231. package/dist/yomeSkills/installMeta.d.ts +8 -0
  232. package/dist/yomeSkills/installMeta.js +76 -0
  233. package/dist/yomeSkills/installMeta.js.map +1 -0
  234. package/dist/yomeSkills/integrity.d.ts +26 -0
  235. package/dist/yomeSkills/integrity.js +107 -0
  236. package/dist/yomeSkills/integrity.js.map +1 -0
  237. package/dist/yomeSkills/invoke.d.ts +29 -0
  238. package/dist/yomeSkills/invoke.js +135 -0
  239. package/dist/yomeSkills/invoke.js.map +1 -0
  240. package/dist/yomeSkills/list.d.ts +11 -0
  241. package/dist/yomeSkills/list.js +55 -0
  242. package/dist/yomeSkills/list.js.map +1 -0
  243. package/dist/yomeSkills/login.d.ts +41 -0
  244. package/dist/yomeSkills/login.js +221 -0
  245. package/dist/yomeSkills/login.js.map +1 -0
  246. package/dist/yomeSkills/manifest.d.ts +60 -0
  247. package/dist/yomeSkills/manifest.js +47 -0
  248. package/dist/yomeSkills/manifest.js.map +1 -0
  249. package/dist/yomeSkills/paths.d.ts +13 -0
  250. package/dist/yomeSkills/paths.js +33 -0
  251. package/dist/yomeSkills/paths.js.map +1 -0
  252. package/dist/yomeSkills/publish.d.ts +18 -0
  253. package/dist/yomeSkills/publish.js +114 -0
  254. package/dist/yomeSkills/publish.js.map +1 -0
  255. package/dist/yomeSkills/rollback.d.ts +10 -0
  256. package/dist/yomeSkills/rollback.js +83 -0
  257. package/dist/yomeSkills/rollback.js.map +1 -0
  258. package/dist/yomeSkills/search.d.ts +21 -0
  259. package/dist/yomeSkills/search.js +31 -0
  260. package/dist/yomeSkills/search.js.map +1 -0
  261. package/dist/yomeSkills/skillsIndex.d.ts +36 -0
  262. package/dist/yomeSkills/skillsIndex.js +111 -0
  263. package/dist/yomeSkills/skillsIndex.js.map +1 -0
  264. package/dist/yomeSkills/unified.d.ts +53 -0
  265. package/dist/yomeSkills/unified.js +187 -0
  266. package/dist/yomeSkills/unified.js.map +1 -0
  267. package/dist/yomeSkills/uninstall.d.ts +7 -0
  268. package/dist/yomeSkills/uninstall.js +22 -0
  269. package/dist/yomeSkills/uninstall.js.map +1 -0
  270. package/dist/yomeSkills/update.d.ts +18 -0
  271. package/dist/yomeSkills/update.js +75 -0
  272. package/dist/yomeSkills/update.js.map +1 -0
  273. package/dist/yomeSkills/validate.d.ts +11 -0
  274. package/dist/yomeSkills/validate.js +99 -0
  275. package/dist/yomeSkills/validate.js.map +1 -0
  276. 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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"}