@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
package/README.md
CHANGED
|
@@ -63,10 +63,9 @@ The agent loop must not live only in the 30 seconds after you press Enter. It sh
|
|
|
63
63
|
|
|
64
64
|
| Capability | Meaning |
|
|
65
65
|
|---|---|
|
|
66
|
-
| **Daemon** |
|
|
66
|
+
| **Daemon** | Yome runs as an OS-level daemon, not a chat window. It watches your filesystem, calendar, IM signals, and long-running jobs in the background — and only surfaces when something matters. The agent loop never has to be re-spawned from scratch. |
|
|
67
67
|
| **Live compaction** | Long sessions auto-compress history; tokens never overflow, memory never lost |
|
|
68
|
-
| **
|
|
69
|
-
| **Async agent** | Long jobs run in the background and push notifications when done, instead of blocking your prompt |
|
|
68
|
+
| **Oncall** | The agent is event-driven, not prompt-driven. WeChat / Feishu / Slack / iMessage messages, calendar fires, build finishes, GPU spikes, *@you* mentions — Yome reacts to the world and pages you with a one-line summary + a draft response, instead of waiting for you to ask. |
|
|
70
69
|
|
|
71
70
|
**Available today:**
|
|
72
71
|
|
|
@@ -76,7 +75,7 @@ yome thread share <session-id> --skill=<slug> # build redacted case bundle
|
|
|
76
75
|
yome thread submit <bundle-dir> --skill=<slug> # publish as PR (needs gh CLI)
|
|
77
76
|
```
|
|
78
77
|
|
|
79
|
-
Sessions, history compaction and case bundles ship today. Daemon
|
|
78
|
+
Sessions, history compaction and case bundles ship today. Daemon and Oncall are next-up on the `next` branch.
|
|
80
79
|
|
|
81
80
|
---
|
|
82
81
|
|
|
@@ -307,9 +306,8 @@ We do not replace your tools. We **empower** them without interrupting the way y
|
|
|
307
306
|
| Capability model (sandbox grants) | **stable** |
|
|
308
307
|
| Thread history + case bundles | **stable** |
|
|
309
308
|
| Live history compaction | beta |
|
|
310
|
-
| Daemon (always-on
|
|
311
|
-
|
|
|
312
|
-
| Async agent (background long-running) | next-up |
|
|
309
|
+
| Daemon (OS-level always-on loop) | experimental, on `next` branch |
|
|
310
|
+
| Oncall (event-driven, auto-paging) | next-up |
|
|
313
311
|
|
|
314
312
|
**Daemon roadmap** *(scoped, on `next`)*
|
|
315
313
|
|
package/README.zh-CN.md
CHANGED
|
@@ -63,10 +63,9 @@ Agent loop 不能只活在用户敲下 Enter 的那 30 秒;它应当 always-on,
|
|
|
63
63
|
|
|
64
64
|
| 能力 | 含义 |
|
|
65
65
|
|---|---|
|
|
66
|
-
| **Daemon · 守护进程** | Agent
|
|
66
|
+
| **Daemon · 守护进程** | Yome 是一个跑在系统层的 daemon,而不是一个聊天窗口。它在后台一直盯着你的文件系统、日历、IM 信号、长任务进程,只有真正值得打断你的时候才出现。Agent loop 永远不需要从零冷启动。 |
|
|
67
67
|
| **Live compaction · 即时压缩** | 长会话自动压缩历史,token 永远不爆,记忆永远不丢 |
|
|
68
|
-
| **
|
|
69
|
-
| **Async agent · 异步 agent** | 后台跑长任务,完成后主动 push 通知,而不是阻塞你的 prompt |
|
|
68
|
+
| **Oncall · 主动响应** | Agent 由事件驱动,而不是 prompt 驱动。微信 / 飞书 / Slack / iMessage 来消息,日历到点,build 跑完,GPU 飙高,有人 @你 —— Yome 监听这些信号,主动把"这件事 + 一个候选回复"推给你,而不是等你想起来再问它。 |
|
|
70
69
|
|
|
71
70
|
**当前可用:**
|
|
72
71
|
|
|
@@ -76,7 +75,7 @@ yome thread share <session-id> --skill=<slug> # 构建脱敏的 case bundle
|
|
|
76
75
|
yome thread submit <bundle-dir> --skill=<slug> # 作为 PR 发布 (需要 gh CLI)
|
|
77
76
|
```
|
|
78
77
|
|
|
79
|
-
会话 / 历史压缩 / case bundle 这三件已落地。Daemon
|
|
78
|
+
会话 / 历史压缩 / case bundle 这三件已落地。Daemon 与 Oncall 在 `next` 分支推进。
|
|
80
79
|
|
|
81
80
|
---
|
|
82
81
|
|
|
@@ -307,9 +306,8 @@ yome --key sk-... --base-url https://… --model …
|
|
|
307
306
|
| Capability model (sandbox grants) | **stable** |
|
|
308
307
|
| Thread 历史 + case bundles | **stable** |
|
|
309
308
|
| Live history compaction | beta |
|
|
310
|
-
| Daemon (
|
|
311
|
-
|
|
|
312
|
-
| Async agent (后台长任务) | next-up |
|
|
309
|
+
| Daemon (系统级常驻 loop) | experimental, 在 `next` 分支 |
|
|
310
|
+
| Oncall (事件驱动 / 主动 paging) | next-up |
|
|
313
311
|
|
|
314
312
|
**Daemon roadmap** *(scoped, 在 `next` 分支)*
|
|
315
313
|
|
|
Binary file
|
package/dist/context.js
CHANGED
|
@@ -93,11 +93,14 @@ You help with software engineering tasks: reading code, writing code, debugging,
|
|
|
93
93
|
- Never expose secrets or API keys.
|
|
94
94
|
|
|
95
95
|
## Available Tools
|
|
96
|
-
You have these tools: Read, Edit, Write, Bash, Glob, Grep, LS.
|
|
96
|
+
You have these tools: Read, Edit, Write, Bash, Yome, AskUser, TodoWrite, Glob, Grep, LS.
|
|
97
97
|
- Use Read to view file contents.
|
|
98
98
|
- Use Edit to modify existing files (find & replace with old_string/new_string).
|
|
99
99
|
- Use Write to create new files.
|
|
100
|
-
- Use Bash
|
|
100
|
+
- Use Bash for /bin/sh commands ONLY (ls, cat, git, mkdir, build/test runners, pipes…). It does NOT route to hub skills.
|
|
101
|
+
- Use Yome to invoke installed hub-skill commands (xl, ppt, cal, fs, rem, …) — pass the command line exactly as a human would type it (e.g. \`xl books\`, \`ppt new ~/Desktop/x.pptx\`).
|
|
102
|
+
- Use AskUser when requirements are ambiguous or you need the user to choose between trade-offs BEFORE doing irreversible work. Send 1–4 multiple-choice questions; do NOT include "Other" (the UI provides a custom-answer entry). Blocks until the user responds.
|
|
103
|
+
- Use TodoWrite proactively whenever you start a task with 3+ meaningful steps. Always send the FULL list. Keep exactly ONE item in_progress at a time. Flip status the moment you start / finish a step — don't batch updates. Each item needs \`content\` (imperative) + \`activeForm\` (present continuous) + \`status\`.
|
|
101
104
|
- Use Glob to find files by pattern.
|
|
102
105
|
- Use Grep to search file contents.
|
|
103
106
|
- Use LS to list directory contents.
|
|
@@ -162,7 +165,7 @@ function buildSkillsSection(skills) {
|
|
|
162
165
|
* the trigger / effects / entry triad.
|
|
163
166
|
*/
|
|
164
167
|
function buildHubSkillsSection(entries) {
|
|
165
|
-
let section = `\n## Installed Hub Skills\nUse the
|
|
168
|
+
let section = `\n## Installed Hub Skills\nUse the **Yome** tool: \`<domain> <action> [args]\` (no \`yome\` prefix, no SkillCall, NOT the Bash tool).\nThe Bash tool is for /bin/sh ops only and will NOT route to skills.\n\n`;
|
|
166
169
|
for (const e of entries) {
|
|
167
170
|
const manifest = readManifest(e.installedAt);
|
|
168
171
|
section += renderL1Block(e.domain, manifest, e.description) + '\n';
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CalendarAccessResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
status: 'granted' | 'denied' | 'restricted' | 'not-determined' | 'unknown' | 'no-helper';
|
|
4
|
+
message: string;
|
|
5
|
+
/** Hint surfaced from the Swift helper (e.g. "open System Settings → ..."). */
|
|
6
|
+
fix?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Best-effort path to bin/yome-calwatch. */
|
|
9
|
+
export declare function resolveCalwatchPath(): string;
|
|
10
|
+
export declare function checkCalendarAccess(): CalendarAccessResult;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Pre-flight EventKit permission probe for `yome cron add --on calendar:*`.
|
|
2
|
+
//
|
|
3
|
+
// We invoke the same Swift helper the daemon uses (`yome-calwatch
|
|
4
|
+
// --check-access`) — it's the only reliable way to learn the current
|
|
5
|
+
// state because TCC is per-binary, not per-user.
|
|
6
|
+
//
|
|
7
|
+
// Exit codes (mirrors the helper):
|
|
8
|
+
// 0 granted
|
|
9
|
+
// 2 denied / restricted (with a JSON line describing the fix)
|
|
10
|
+
// 3 unknown
|
|
11
|
+
// non-existent binary → returns { ok: false, message: '...' }
|
|
12
|
+
import { spawnSync } from 'child_process';
|
|
13
|
+
import { existsSync } from 'fs';
|
|
14
|
+
import { dirname, resolve } from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
/** Best-effort path to bin/yome-calwatch. */
|
|
17
|
+
export function resolveCalwatchPath() {
|
|
18
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
// dist/daemon/calendarPermission.js → ../../bin/yome-calwatch
|
|
20
|
+
// src/daemon/calendarPermission.ts → ../../bin/yome-calwatch
|
|
21
|
+
return resolve(here, '..', '..', 'bin', 'yome-calwatch');
|
|
22
|
+
}
|
|
23
|
+
export function checkCalendarAccess() {
|
|
24
|
+
const bin = resolveCalwatchPath();
|
|
25
|
+
if (!existsSync(bin)) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
status: 'no-helper',
|
|
29
|
+
message: 'native helper yome-calwatch is missing. ' +
|
|
30
|
+
'On macOS: cd cli && npm run build:native. ' +
|
|
31
|
+
'On Linux/Windows: calendar triggers are macOS-only.',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const r = spawnSync(bin, ['--check-access'], { encoding: 'utf-8', timeout: 30_000 });
|
|
35
|
+
// Helper writes one JSON line to stdout regardless of exit code.
|
|
36
|
+
const last = (r.stdout ?? '').split('\n').reverse().find((l) => l.trim().startsWith('{'));
|
|
37
|
+
let parsed = {};
|
|
38
|
+
if (last) {
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(last);
|
|
41
|
+
}
|
|
42
|
+
catch { /* noop */ }
|
|
43
|
+
}
|
|
44
|
+
const status = String(parsed.status ?? '');
|
|
45
|
+
const fix = typeof parsed.fix === 'string' ? parsed.fix : undefined;
|
|
46
|
+
if (r.status === 0 && status === 'granted') {
|
|
47
|
+
return { ok: true, status: 'granted', message: 'calendar access granted' };
|
|
48
|
+
}
|
|
49
|
+
if (status === 'denied' || status === 'restricted') {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
status: status,
|
|
53
|
+
message: `calendar access ${status}`,
|
|
54
|
+
fix,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (status === 'not-determined') {
|
|
58
|
+
return { ok: false, status: 'not-determined', message: 'calendar access not yet determined', fix };
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
status: 'unknown',
|
|
63
|
+
message: `unable to determine calendar access (helper exit=${r.status}). ` +
|
|
64
|
+
`stdout=${(r.stdout ?? '').slice(0, 200)} stderr=${(r.stderr ?? '').slice(0, 200)}`,
|
|
65
|
+
fix,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=calendarPermission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendarPermission.js","sourceRoot":"","sources":["../../src/daemon/calendarPermission.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CronCliFlags {
|
|
2
|
+
at?: string;
|
|
3
|
+
human?: string;
|
|
4
|
+
once?: string;
|
|
5
|
+
on?: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
within?: string;
|
|
8
|
+
titleRegex?: string;
|
|
9
|
+
calendar?: string;
|
|
10
|
+
tz?: string;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
allow?: string | string[];
|
|
13
|
+
deny?: string | string[];
|
|
14
|
+
maxMs?: string;
|
|
15
|
+
env?: string | string[];
|
|
16
|
+
follow?: boolean;
|
|
17
|
+
json?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function runCronSubcommand(args: string[], flags: CronCliFlags): Promise<number>;
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
// `yome cron <subcommand>` — task management UI on top of taskStore.
|
|
2
|
+
//
|
|
3
|
+
// add create a new task
|
|
4
|
+
// list list tasks
|
|
5
|
+
// rm delete a task
|
|
6
|
+
// enable / disable
|
|
7
|
+
// run manually fire a task right now (out-of-process, like the scheduler does)
|
|
8
|
+
// logs show audit logs for a task
|
|
9
|
+
//
|
|
10
|
+
// All of these are pure CLI side-effects. The actual scheduling lives in
|
|
11
|
+
// the daemon process; tasks.json is the shared state and the daemon
|
|
12
|
+
// watches it for changes (see scheduler.ts).
|
|
13
|
+
import cron from 'node-cron';
|
|
14
|
+
import { addTask, listTasks, removeTask, getTask, setEnabled } from './taskStore.js';
|
|
15
|
+
import { humanToCron } from './humanCron.js';
|
|
16
|
+
import { humanToOnceMs } from './humanOnce.js';
|
|
17
|
+
import { fireTask, resolveYomeBinPath } from './triggers/cron.js';
|
|
18
|
+
import { listRunsForTask, readRunLog } from './log.js';
|
|
19
|
+
import { readPidIfRunning } from './scheduler.js';
|
|
20
|
+
import { execSync } from 'child_process';
|
|
21
|
+
import { checkCalendarAccess } from './calendarPermission.js';
|
|
22
|
+
export async function runCronSubcommand(args, flags) {
|
|
23
|
+
const sub = args[0];
|
|
24
|
+
switch (sub) {
|
|
25
|
+
case 'add': return doAdd(args.slice(1), flags);
|
|
26
|
+
case 'list':
|
|
27
|
+
case 'ls': return doList(flags);
|
|
28
|
+
case 'rm':
|
|
29
|
+
case 'remove': return doRm(args.slice(1));
|
|
30
|
+
case 'enable': return doToggle(args.slice(1), true);
|
|
31
|
+
case 'disable': return doToggle(args.slice(1), false);
|
|
32
|
+
case 'run': return doRun(args.slice(1));
|
|
33
|
+
case 'logs': return doLogs(args.slice(1), flags);
|
|
34
|
+
case undefined:
|
|
35
|
+
case 'help':
|
|
36
|
+
case '--help':
|
|
37
|
+
printHelp();
|
|
38
|
+
return 0;
|
|
39
|
+
default:
|
|
40
|
+
console.error(`Unknown subcommand: yome cron ${sub}`);
|
|
41
|
+
printHelp();
|
|
42
|
+
return 2;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function printHelp() {
|
|
46
|
+
console.log(`Usage: yome cron <subcommand>
|
|
47
|
+
|
|
48
|
+
add "<prompt>" Add a task. Pick exactly one trigger:
|
|
49
|
+
--at "<crontab>" cron schedule, e.g. "0 18 * * *"
|
|
50
|
+
--human "<phrase>" friendly: "9:00", "every 5 min", "mon 9:00"
|
|
51
|
+
--once "<time>" one-shot: "in 5 minutes",
|
|
52
|
+
"tomorrow 9:00", "2026-04-30 18:00", ISO 8601
|
|
53
|
+
--on file:<event> --path "<glob>"
|
|
54
|
+
fire on file events. <event> ∈
|
|
55
|
+
change (add+change, default),
|
|
56
|
+
any (add+change+unlink),
|
|
57
|
+
add, unlink
|
|
58
|
+
Glob: "~/Desktop/test/**/*.xlsx"
|
|
59
|
+
--on calendar:<event> fire on macOS Calendar events. <event> ∈
|
|
60
|
+
event-start (default), event-end, event-added
|
|
61
|
+
Filters (calendar only):
|
|
62
|
+
--within "10m" lead time before start
|
|
63
|
+
--title-regex "^Standup" case-insensitive title match
|
|
64
|
+
--calendar "Work" calendar display name (substring)
|
|
65
|
+
Optional:
|
|
66
|
+
--tz <iana> timezone (default: system local; cron only)
|
|
67
|
+
--cwd <dir> working dir for the task
|
|
68
|
+
--allow <rules> comma-separated allowlist, e.g. "Read,Write,Yome(@yome/fs:*)"
|
|
69
|
+
--deny <rules> comma-separated denylist
|
|
70
|
+
--max-ms <ms> wall-time cap (default 300000)
|
|
71
|
+
list / ls List tasks
|
|
72
|
+
rm <id> Remove a task
|
|
73
|
+
enable <id> Re-enable a paused task
|
|
74
|
+
disable <id> Pause a task without deleting it
|
|
75
|
+
run <id> Fire a task immediately (out-of-process)
|
|
76
|
+
logs <id> [-f] Show audit logs for a task
|
|
77
|
+
-f / --follow follow the latest run
|
|
78
|
+
|
|
79
|
+
Tip: edits to tasks.json take effect immediately — the daemon watches
|
|
80
|
+
the file. You don't need to restart the daemon after add/rm/enable.`);
|
|
81
|
+
}
|
|
82
|
+
function doAdd(args, flags) {
|
|
83
|
+
const prompt = args[0];
|
|
84
|
+
if (!prompt) {
|
|
85
|
+
console.error('Usage: yome cron add "<prompt>" <--at|--human|--once|--on ...>');
|
|
86
|
+
return 2;
|
|
87
|
+
}
|
|
88
|
+
// Exactly one trigger type must be specified.
|
|
89
|
+
const triggerSpecified = [flags.at, flags.human, flags.once, flags.on].filter(Boolean).length;
|
|
90
|
+
if (triggerSpecified === 0) {
|
|
91
|
+
console.error('Must specify a trigger: --at, --human, --once, or --on');
|
|
92
|
+
return 2;
|
|
93
|
+
}
|
|
94
|
+
if (triggerSpecified > 1) {
|
|
95
|
+
console.error('Specify exactly ONE trigger (--at / --human / --once / --on)');
|
|
96
|
+
return 2;
|
|
97
|
+
}
|
|
98
|
+
let trigger;
|
|
99
|
+
let summary;
|
|
100
|
+
try {
|
|
101
|
+
if (flags.at || flags.human) {
|
|
102
|
+
let schedule;
|
|
103
|
+
if (flags.at) {
|
|
104
|
+
if (!cron.validate(flags.at))
|
|
105
|
+
throw new Error(`Invalid crontab: '${flags.at}'`);
|
|
106
|
+
schedule = flags.at;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
schedule = humanToCron(flags.human);
|
|
110
|
+
}
|
|
111
|
+
trigger = { kind: 'cron', schedule, ...(flags.tz ? { tz: flags.tz } : {}) };
|
|
112
|
+
summary = `cron "${schedule}"${flags.tz ? ` tz=${flags.tz}` : ''}`;
|
|
113
|
+
}
|
|
114
|
+
else if (flags.once) {
|
|
115
|
+
const atMs = humanToOnceMs(flags.once);
|
|
116
|
+
trigger = { kind: 'once', atMs };
|
|
117
|
+
summary = `once @ ${new Date(atMs).toISOString()}`;
|
|
118
|
+
}
|
|
119
|
+
else if (flags.on) {
|
|
120
|
+
// file:change → add+change (any mutation that creates/updates content)
|
|
121
|
+
// file:any → add+change+unlink
|
|
122
|
+
// file:add → just creates
|
|
123
|
+
// file:unlink → just deletions
|
|
124
|
+
// calendar:event-start | event-end | event-added (each via Swift helper)
|
|
125
|
+
const [domain, event] = flags.on.split(':');
|
|
126
|
+
if (domain === 'file') {
|
|
127
|
+
if (!flags.path)
|
|
128
|
+
throw new Error('--on file:* requires --path "<glob>"');
|
|
129
|
+
const events = mapFileEvent(event ?? 'change');
|
|
130
|
+
trigger = { kind: 'file', path: flags.path, events };
|
|
131
|
+
summary = `file ${event ?? 'change'} on ${flags.path}`;
|
|
132
|
+
}
|
|
133
|
+
else if (domain === 'calendar') {
|
|
134
|
+
const ev = (event ?? 'event-start');
|
|
135
|
+
if (ev !== 'event-start' && ev !== 'event-end' && ev !== 'event-added') {
|
|
136
|
+
throw new Error(`unknown calendar event: ${ev} (use event-start | event-end | event-added)`);
|
|
137
|
+
}
|
|
138
|
+
// Pre-flight permission probe so the user gets immediate feedback
|
|
139
|
+
// instead of "task added but never fires". Mirrors option C in the
|
|
140
|
+
// PR4 plan (helper also re-checks at watcher startup).
|
|
141
|
+
const access = checkCalendarAccess();
|
|
142
|
+
if (!access.ok) {
|
|
143
|
+
let msg = `cannot add calendar trigger: ${access.message}`;
|
|
144
|
+
if (access.fix)
|
|
145
|
+
msg += `\n → ${access.fix}`;
|
|
146
|
+
throw new Error(msg);
|
|
147
|
+
}
|
|
148
|
+
const leadMs = flags.within ? parseDurationMs(flags.within) : 0;
|
|
149
|
+
trigger = {
|
|
150
|
+
kind: 'calendar',
|
|
151
|
+
events: [ev],
|
|
152
|
+
...(leadMs ? { leadMs } : {}),
|
|
153
|
+
...(flags.titleRegex ? { titleRegex: flags.titleRegex } : {}),
|
|
154
|
+
...(flags.calendar ? { calendar: flags.calendar } : {}),
|
|
155
|
+
};
|
|
156
|
+
const filterDesc = [
|
|
157
|
+
flags.titleRegex ? `title=/${flags.titleRegex}/i` : '',
|
|
158
|
+
flags.calendar ? `cal="${flags.calendar}"` : '',
|
|
159
|
+
leadMs ? `lead=${flags.within}` : '',
|
|
160
|
+
].filter(Boolean).join(' ');
|
|
161
|
+
summary = `calendar ${ev}${filterDesc ? ` ${filterDesc}` : ''}`;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
throw new Error(`unknown --on event: ${flags.on}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw new Error('unreachable');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
console.error(`✗ ${e?.message ?? e}`);
|
|
173
|
+
return 2;
|
|
174
|
+
}
|
|
175
|
+
const t = addTask({
|
|
176
|
+
trigger,
|
|
177
|
+
prompt,
|
|
178
|
+
cwd: flags.cwd,
|
|
179
|
+
autoAllow: parseRuleList(flags.allow),
|
|
180
|
+
autoDeny: parseRuleList(flags.deny),
|
|
181
|
+
maxDurationMs: flags.maxMs ? Number.parseInt(flags.maxMs, 10) : undefined,
|
|
182
|
+
env: parseEnvList(flags.env),
|
|
183
|
+
});
|
|
184
|
+
console.log(`✓ added ${t.id}`);
|
|
185
|
+
console.log(` trigger: ${summary}`);
|
|
186
|
+
console.log(` prompt: ${prompt.length > 80 ? prompt.slice(0, 80) + '…' : prompt}`);
|
|
187
|
+
if (t.autoAllow?.length)
|
|
188
|
+
console.log(` autoAllow: ${t.autoAllow.join(', ')}`);
|
|
189
|
+
if (t.autoDeny?.length)
|
|
190
|
+
console.log(` autoDeny: ${t.autoDeny.join(', ')}`);
|
|
191
|
+
if (!readPidIfRunning()) {
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log('⚠️ Daemon is not running. Start it with: yome daemon start');
|
|
194
|
+
}
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
function parseRuleList(s) {
|
|
198
|
+
if (!s)
|
|
199
|
+
return undefined;
|
|
200
|
+
// meow with isMultiple gives us an array; the manual case (single
|
|
201
|
+
// --allow "a,b,c") gives us a string. Normalise both into a flat
|
|
202
|
+
// array of comma-trimmed rule names.
|
|
203
|
+
const parts = Array.isArray(s) ? s : [s];
|
|
204
|
+
const out = parts.flatMap((p) => p.split(',').map((x) => x.trim()).filter(Boolean));
|
|
205
|
+
return out.length === 0 ? undefined : out;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Parse repeated --env KEY=VAL flags (or a single comma-separated string)
|
|
209
|
+
* into a flat { KEY: VAL } record. Quoted values aren't unescaped — pass
|
|
210
|
+
* them via shell quoting at the call site.
|
|
211
|
+
*/
|
|
212
|
+
function parseEnvList(s) {
|
|
213
|
+
if (!s)
|
|
214
|
+
return undefined;
|
|
215
|
+
const parts = Array.isArray(s) ? s : [s];
|
|
216
|
+
const out = {};
|
|
217
|
+
for (const p of parts) {
|
|
218
|
+
for (const kv of p.split(',')) {
|
|
219
|
+
const eq = kv.indexOf('=');
|
|
220
|
+
if (eq <= 0)
|
|
221
|
+
continue;
|
|
222
|
+
const k = kv.slice(0, eq).trim();
|
|
223
|
+
const v = kv.slice(eq + 1).trim();
|
|
224
|
+
if (k)
|
|
225
|
+
out[k] = v;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return Object.keys(out).length === 0 ? undefined : out;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Parse a friendly duration string into milliseconds.
|
|
232
|
+
* "30s" → 30000 "10m" → 600000 "2h" → 7200000 "500ms" → 500
|
|
233
|
+
* bare number → milliseconds.
|
|
234
|
+
*/
|
|
235
|
+
function parseDurationMs(s) {
|
|
236
|
+
const m = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/i.exec(s.trim());
|
|
237
|
+
if (!m)
|
|
238
|
+
throw new Error(`invalid duration: ${JSON.stringify(s)} (use e.g. "30s", "10m", "2h")`);
|
|
239
|
+
const n = Number(m[1]);
|
|
240
|
+
const unit = (m[2] ?? 'ms').toLowerCase();
|
|
241
|
+
switch (unit) {
|
|
242
|
+
case 'ms': return Math.round(n);
|
|
243
|
+
case 's': return Math.round(n * 1000);
|
|
244
|
+
case 'm': return Math.round(n * 60_000);
|
|
245
|
+
case 'h': return Math.round(n * 3_600_000);
|
|
246
|
+
default: return Math.round(n);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Map a user-friendly file event token into chokidar event names.
|
|
251
|
+
*
|
|
252
|
+
* Most users typing `--on file:change` mean "any time these files are
|
|
253
|
+
* created, edited, or deleted" rather than chokidar's strict "change =
|
|
254
|
+
* existing file content modified, distinct from add/unlink". We pick a
|
|
255
|
+
* sensible default and let power users be explicit.
|
|
256
|
+
*/
|
|
257
|
+
function mapFileEvent(token) {
|
|
258
|
+
switch (token.toLowerCase()) {
|
|
259
|
+
case 'change': return ['add', 'change'];
|
|
260
|
+
case 'any':
|
|
261
|
+
case '*': return ['add', 'change', 'unlink'];
|
|
262
|
+
case 'add': return ['add'];
|
|
263
|
+
case 'unlink':
|
|
264
|
+
case 'delete': return ['unlink'];
|
|
265
|
+
default:
|
|
266
|
+
throw new Error(`unknown file event: '${token}' (try change | add | unlink | any)`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function doList(flags) {
|
|
270
|
+
const tasks = listTasks();
|
|
271
|
+
if (flags.json) {
|
|
272
|
+
process.stdout.write(JSON.stringify(tasks, null, 2) + '\n');
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
275
|
+
if (tasks.length === 0) {
|
|
276
|
+
console.log('(no tasks defined — use `yome cron add ...`)');
|
|
277
|
+
return 0;
|
|
278
|
+
}
|
|
279
|
+
const idW = Math.max(2, ...tasks.map((t) => t.id.length));
|
|
280
|
+
const schedW = Math.max(8, ...tasks.map((t) => trigSummary(t.trigger).length));
|
|
281
|
+
const pad = (s, w) => s + ' '.repeat(Math.max(0, w - s.length));
|
|
282
|
+
console.log(` ${pad('ID', idW)} ${pad('SCHEDULE', schedW)} STATE LAST-RUN PROMPT`);
|
|
283
|
+
for (const t of tasks) {
|
|
284
|
+
const state = t.enabled ? 'on ' : 'off';
|
|
285
|
+
const last = t.lastRun
|
|
286
|
+
? new Date(t.lastRun.ts).toISOString().replace('T', ' ').slice(0, 19) + (t.lastRun.ok ? ' ok' : ' err')
|
|
287
|
+
: '—';
|
|
288
|
+
const prompt = t.prompt.length > 60 ? t.prompt.slice(0, 60) + '…' : t.prompt;
|
|
289
|
+
console.log(` ${pad(t.id, idW)} ${pad(trigSummary(t.trigger), schedW)} ${state} ${pad(last, 22)} ${prompt}`);
|
|
290
|
+
}
|
|
291
|
+
if (!readPidIfRunning() && tasks.some((t) => t.enabled)) {
|
|
292
|
+
console.log('');
|
|
293
|
+
console.log('⚠️ Daemon is not running — enabled tasks will not fire. Start with: yome daemon start');
|
|
294
|
+
}
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
function trigSummary(trig) {
|
|
298
|
+
if (trig?.kind === 'cron')
|
|
299
|
+
return trig.schedule;
|
|
300
|
+
if (trig?.kind === 'once')
|
|
301
|
+
return `once @ ${new Date(trig.atMs).toISOString().slice(0, 16)}`;
|
|
302
|
+
if (trig?.kind === 'file')
|
|
303
|
+
return `file:${trig.path}`;
|
|
304
|
+
if (trig?.kind === 'calendar') {
|
|
305
|
+
const ev = (trig.events ?? ['event-start']).join(',');
|
|
306
|
+
const bits = [`cal:${ev}`];
|
|
307
|
+
if (trig.leadMs)
|
|
308
|
+
bits.push(`-${Math.round(trig.leadMs / 1000)}s`);
|
|
309
|
+
if (trig.titleRegex)
|
|
310
|
+
bits.push(`/${trig.titleRegex}/`);
|
|
311
|
+
if (trig.calendar)
|
|
312
|
+
bits.push(`@${trig.calendar}`);
|
|
313
|
+
return bits.join(' ');
|
|
314
|
+
}
|
|
315
|
+
return '?';
|
|
316
|
+
}
|
|
317
|
+
function doRm(args) {
|
|
318
|
+
const id = args[0];
|
|
319
|
+
if (!id) {
|
|
320
|
+
console.error('Usage: yome cron rm <id>');
|
|
321
|
+
return 2;
|
|
322
|
+
}
|
|
323
|
+
if (!removeTask(id)) {
|
|
324
|
+
console.error(`✗ no such task: ${id}`);
|
|
325
|
+
return 1;
|
|
326
|
+
}
|
|
327
|
+
console.log(`✓ removed ${id}`);
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
function doToggle(args, enabled) {
|
|
331
|
+
const id = args[0];
|
|
332
|
+
if (!id) {
|
|
333
|
+
console.error(`Usage: yome cron ${enabled ? 'enable' : 'disable'} <id>`);
|
|
334
|
+
return 2;
|
|
335
|
+
}
|
|
336
|
+
if (!setEnabled(id, enabled)) {
|
|
337
|
+
console.error(`✗ no such task: ${id}`);
|
|
338
|
+
return 1;
|
|
339
|
+
}
|
|
340
|
+
console.log(`✓ ${enabled ? 'enabled' : 'disabled'} ${id}`);
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
function doRun(args) {
|
|
344
|
+
const id = args[0];
|
|
345
|
+
if (!id) {
|
|
346
|
+
console.error('Usage: yome cron run <id>');
|
|
347
|
+
return 2;
|
|
348
|
+
}
|
|
349
|
+
const t = getTask(id);
|
|
350
|
+
if (!t) {
|
|
351
|
+
console.error(`✗ no such task: ${id}`);
|
|
352
|
+
return 1;
|
|
353
|
+
}
|
|
354
|
+
// Spawn out-of-process via the same path the scheduler uses, so behaviour
|
|
355
|
+
// (stdout capture, kill timer, jsonl logging) matches a scheduled fire.
|
|
356
|
+
console.log(`→ firing ${id} now (out-of-process)…`);
|
|
357
|
+
fireTask(id, resolveYomeBinPath(), t);
|
|
358
|
+
console.log(`✓ task spawned. Watch logs with: yome cron logs ${id} -f`);
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
function doLogs(args, flags) {
|
|
362
|
+
const id = args[0];
|
|
363
|
+
if (!id) {
|
|
364
|
+
console.error('Usage: yome cron logs <id> [-f]');
|
|
365
|
+
return 2;
|
|
366
|
+
}
|
|
367
|
+
const runs = listRunsForTask(id);
|
|
368
|
+
if (runs.length === 0) {
|
|
369
|
+
console.log(`(no runs recorded for ${id} yet)`);
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
const latest = runs[0];
|
|
373
|
+
if (flags.follow) {
|
|
374
|
+
try {
|
|
375
|
+
execSync(`tail -F "${latest.file}"`, { stdio: 'inherit' });
|
|
376
|
+
}
|
|
377
|
+
catch { /* user terminated tail */ }
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
console.log(`── latest run: ${new Date(latest.runTs).toISOString()} (${latest.file}) ──`);
|
|
381
|
+
for (const e of readRunLog(latest.file)) {
|
|
382
|
+
const t = new Date(e.ts).toISOString().slice(11, 19);
|
|
383
|
+
if (e.type === 'tool_use') {
|
|
384
|
+
console.log(`[${t}] tool ${e.name} ${JSON.stringify(e.input).slice(0, 120)}`);
|
|
385
|
+
}
|
|
386
|
+
else if (e.type === 'tool_result') {
|
|
387
|
+
const r = String(e.result ?? '').replace(/\n/g, ' ').slice(0, 120);
|
|
388
|
+
console.log(`[${t}] └─ ${r}`);
|
|
389
|
+
}
|
|
390
|
+
else if (e.type === 'run_start' || e.type === 'run_end') {
|
|
391
|
+
console.log(`[${t}] ${e.type} ${JSON.stringify({ ok: e.ok, durationMs: e.durationMs, error: e.error }).slice(0, 200)}`);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
console.log(`[${t}] ${e.type} ${JSON.stringify(e).slice(0, 200)}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (runs.length > 1) {
|
|
398
|
+
console.log('');
|
|
399
|
+
console.log(`(${runs.length - 1} earlier run(s) in ${latest.file.replace(/\/[^/]+$/, '')})`);
|
|
400
|
+
}
|
|
401
|
+
return 0;
|
|
402
|
+
}
|
|
403
|
+
//# sourceMappingURL=cronCli.js.map
|