@poping/yome 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -7
- package/README.zh-CN.md +5 -7
- package/bin/yome-calwatch +0 -0
- package/dist/context.js +6 -3
- package/dist/context.js.map +1 -1
- package/dist/daemon/calendarPermission.d.ts +10 -0
- package/dist/daemon/calendarPermission.js +68 -0
- package/dist/daemon/calendarPermission.js.map +1 -0
- package/dist/daemon/cronCli.d.ts +19 -0
- package/dist/daemon/cronCli.js +403 -0
- package/dist/daemon/cronCli.js.map +1 -0
- package/dist/daemon/envHint.d.ts +5 -0
- package/dist/daemon/envHint.js +139 -0
- package/dist/daemon/envHint.js.map +1 -0
- package/dist/daemon/humanCron.d.ts +1 -0
- package/dist/daemon/humanCron.js +72 -0
- package/dist/daemon/humanCron.js.map +1 -0
- package/dist/daemon/humanOnce.d.ts +1 -0
- package/dist/daemon/humanOnce.js +54 -0
- package/dist/daemon/humanOnce.js.map +1 -0
- package/dist/daemon/index.d.ts +5 -0
- package/dist/daemon/index.js +168 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/launchd.d.ts +7 -0
- package/dist/daemon/launchd.js +93 -0
- package/dist/daemon/launchd.js.map +1 -0
- package/dist/daemon/log.d.ts +17 -0
- package/dist/daemon/log.js +57 -0
- package/dist/daemon/log.js.map +1 -0
- package/dist/daemon/paths.d.ts +11 -0
- package/dist/daemon/paths.js +27 -0
- package/dist/daemon/paths.js.map +1 -0
- package/dist/daemon/runTaskEntry.d.ts +1 -0
- package/dist/daemon/runTaskEntry.js +67 -0
- package/dist/daemon/runTaskEntry.js.map +1 -0
- package/dist/daemon/runner.d.ts +21 -0
- package/dist/daemon/runner.js +175 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/scheduler.d.ts +5 -0
- package/dist/daemon/scheduler.js +162 -0
- package/dist/daemon/scheduler.js.map +1 -0
- package/dist/daemon/taskStore.d.ts +62 -0
- package/dist/daemon/taskStore.js +88 -0
- package/dist/daemon/taskStore.js.map +1 -0
- package/dist/daemon/triggers/calendar.d.ts +8 -0
- package/dist/daemon/triggers/calendar.js +248 -0
- package/dist/daemon/triggers/calendar.js.map +1 -0
- package/dist/daemon/triggers/childRunner.d.ts +14 -0
- package/dist/daemon/triggers/childRunner.js +111 -0
- package/dist/daemon/triggers/childRunner.js.map +1 -0
- package/dist/daemon/triggers/cron.d.ts +14 -0
- package/dist/daemon/triggers/cron.js +91 -0
- package/dist/daemon/triggers/cron.js.map +1 -0
- package/dist/daemon/triggers/file.d.ts +7 -0
- package/dist/daemon/triggers/file.js +123 -0
- package/dist/daemon/triggers/file.js.map +1 -0
- package/dist/daemon/triggers/once.d.ts +10 -0
- package/dist/daemon/triggers/once.js +80 -0
- package/dist/daemon/triggers/once.js.map +1 -0
- package/dist/index.js +96 -9
- package/dist/index.js.map +1 -1
- package/dist/skills/runner/dispatcher.d.ts +74 -3
- package/dist/skills/runner/dispatcher.js +258 -48
- package/dist/skills/runner/dispatcher.js.map +1 -1
- package/dist/skills/runner/dispatcher.test.d.ts +1 -0
- package/dist/skills/runner/dispatcher.test.js +141 -0
- package/dist/skills/runner/dispatcher.test.js.map +1 -0
- package/dist/skills/runner/kernel.js +0 -1
- package/dist/skills/runner/kernel.js.map +1 -1
- package/dist/skills/runner/nodeBackend.d.ts +32 -0
- package/dist/skills/runner/nodeBackend.js +147 -0
- package/dist/skills/runner/nodeBackend.js.map +1 -0
- package/dist/state/todos.d.ts +12 -0
- package/dist/state/todos.js +32 -0
- package/dist/state/todos.js.map +1 -0
- package/dist/tools/askUser.d.ts +20 -0
- package/dist/tools/askUser.js +126 -0
- package/dist/tools/askUser.js.map +1 -0
- package/dist/tools/bash.js +13 -19
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +18 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/todoWrite.d.ts +2 -0
- package/dist/tools/todoWrite.js +141 -0
- package/dist/tools/todoWrite.js.map +1 -0
- package/dist/tools/yome.d.ts +2 -0
- package/dist/tools/yome.js +87 -0
- package/dist/tools/yome.js.map +1 -0
- package/dist/ui/App.js +52 -3
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/AskUserPrompt.d.ts +7 -0
- package/dist/ui/AskUserPrompt.js +78 -0
- package/dist/ui/AskUserPrompt.js.map +1 -0
- package/dist/ui/InputBar.js +20 -34
- package/dist/ui/InputBar.js.map +1 -1
- package/dist/ui/MessageList.d.ts +8 -0
- package/dist/ui/MessageList.js +2 -2
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/MultilineTextInput.d.ts +31 -0
- package/dist/ui/MultilineTextInput.js +393 -0
- package/dist/ui/MultilineTextInput.js.map +1 -0
- package/dist/ui/MultilineTextInput.test.d.ts +1 -0
- package/dist/ui/MultilineTextInput.test.js +30 -0
- package/dist/ui/MultilineTextInput.test.js.map +1 -0
- package/dist/ui/TodoPanel.d.ts +7 -0
- package/dist/ui/TodoPanel.js +36 -0
- package/dist/ui/TodoPanel.js.map +1 -0
- package/dist/yomeSkills/cli.d.ts +8 -0
- package/dist/yomeSkills/cli.js +155 -8
- package/dist/yomeSkills/cli.js.map +1 -1
- package/dist/yomeSkills/deprecate.d.ts +29 -0
- package/dist/yomeSkills/deprecate.js +99 -0
- package/dist/yomeSkills/deprecate.js.map +1 -0
- package/dist/yomeSkills/installFromHubTarball.d.ts +26 -0
- package/dist/yomeSkills/installFromHubTarball.js +161 -0
- package/dist/yomeSkills/installFromHubTarball.js.map +1 -0
- package/dist/yomeSkills/installGithub.d.ts +7 -0
- package/dist/yomeSkills/installGithub.js +42 -21
- package/dist/yomeSkills/installGithub.js.map +1 -1
- package/dist/yomeSkills/invoke.js +50 -18
- package/dist/yomeSkills/invoke.js.map +1 -1
- package/dist/yomeSkills/publish.d.ts +2 -0
- package/dist/yomeSkills/publish.js +6 -1
- package/dist/yomeSkills/publish.js.map +1 -1
- package/package.json +10 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type LogEntryType = 'run_start' | 'run_end' | 'tool_use' | 'tool_result' | 'text_delta' | 'permission_denied_auto' | 'timeout' | 'error';
|
|
2
|
+
export interface LogEntry {
|
|
3
|
+
ts: number;
|
|
4
|
+
type: LogEntryType;
|
|
5
|
+
[k: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function openTaskLog(taskId: string, runTs: number): string;
|
|
8
|
+
export declare function appendLog(file: string, entry: Omit<LogEntry, 'ts'> & {
|
|
9
|
+
ts?: number;
|
|
10
|
+
}): void;
|
|
11
|
+
/** List run-log files for a task, newest first. */
|
|
12
|
+
export declare function listRunsForTask(taskId: string): {
|
|
13
|
+
runTs: number;
|
|
14
|
+
file: string;
|
|
15
|
+
sizeBytes: number;
|
|
16
|
+
}[];
|
|
17
|
+
export declare function readRunLog(file: string): LogEntry[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// JSONL audit log for daemon-run tasks.
|
|
2
|
+
// One file per (taskId, runStartTs). Append-only, line-delimited JSON so
|
|
3
|
+
// `yome cron logs <id>` can tail / parse it without touching live writers.
|
|
4
|
+
import { appendFileSync, readFileSync, readdirSync, existsSync, statSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { logFileForTask, logDirForTask } from './paths.js';
|
|
7
|
+
export function openTaskLog(taskId, runTs) {
|
|
8
|
+
return logFileForTask(taskId, runTs);
|
|
9
|
+
}
|
|
10
|
+
export function appendLog(file, entry) {
|
|
11
|
+
const full = { ts: entry.ts ?? Date.now(), ...entry };
|
|
12
|
+
try {
|
|
13
|
+
appendFileSync(file, JSON.stringify(full) + '\n', 'utf-8');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// Best-effort: if the log dir vanished mid-run we don't want to crash
|
|
17
|
+
// the agent. The runner already keeps an in-memory error counter.
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** List run-log files for a task, newest first. */
|
|
21
|
+
export function listRunsForTask(taskId) {
|
|
22
|
+
const dir = logDirForTask(taskId);
|
|
23
|
+
if (!existsSync(dir))
|
|
24
|
+
return [];
|
|
25
|
+
const files = readdirSync(dir).filter((f) => f.endsWith('.jsonl'));
|
|
26
|
+
const out = files.map((f) => {
|
|
27
|
+
const full = join(dir, f);
|
|
28
|
+
const runTs = Number.parseInt(f.replace('.jsonl', ''), 10) || 0;
|
|
29
|
+
let sizeBytes = 0;
|
|
30
|
+
try {
|
|
31
|
+
sizeBytes = statSync(full).size;
|
|
32
|
+
}
|
|
33
|
+
catch { /* race */ }
|
|
34
|
+
return { runTs, file: full, sizeBytes };
|
|
35
|
+
});
|
|
36
|
+
out.sort((a, b) => b.runTs - a.runTs);
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
export function readRunLog(file) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = readFileSync(file, 'utf-8').trim();
|
|
42
|
+
if (!raw)
|
|
43
|
+
return [];
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const line of raw.split('\n')) {
|
|
46
|
+
try {
|
|
47
|
+
out.push(JSON.parse(line));
|
|
48
|
+
}
|
|
49
|
+
catch { /* skip malformed */ }
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/daemon/log.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,yEAAyE;AACzE,2EAA2E;AAE3E,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACrF,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAkB3D,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,KAAa;IACvD,OAAO,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAA6C;IACnF,MAAM,IAAI,GAAa,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAc,CAAC;IAC5E,IAAI,CAAC;QACH,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,kEAAkE;IACpE,CAAC;AACH,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC;YAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QAC7D,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,MAAM,GAAG,GAAe,EAAE,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC;gBAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const DAEMON_ROOT: string;
|
|
2
|
+
export declare const CRON_ROOT: string;
|
|
3
|
+
export declare const TASKS_FILE: string;
|
|
4
|
+
export declare const LOGS_ROOT: string;
|
|
5
|
+
export declare const PID_FILE: string;
|
|
6
|
+
export declare const STDOUT_LOG: string;
|
|
7
|
+
export declare const STDERR_LOG: string;
|
|
8
|
+
export declare const PLIST_LABEL = "work.yome.daemon";
|
|
9
|
+
export declare function ensureDirs(): void;
|
|
10
|
+
export declare function logFileForTask(taskId: string, runTs: number): string;
|
|
11
|
+
export declare function logDirForTask(taskId: string): string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Daemon-wide path constants. Centralised so the runner, scheduler, log
|
|
2
|
+
// reader and launchd plist generator all agree on where things live.
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { mkdirSync } from 'fs';
|
|
6
|
+
export const DAEMON_ROOT = join(homedir(), '.yome', 'daemon');
|
|
7
|
+
export const CRON_ROOT = join(homedir(), '.yome', 'cron');
|
|
8
|
+
export const TASKS_FILE = join(CRON_ROOT, 'tasks.json');
|
|
9
|
+
export const LOGS_ROOT = join(CRON_ROOT, 'logs');
|
|
10
|
+
export const PID_FILE = join(DAEMON_ROOT, 'daemon.pid');
|
|
11
|
+
export const STDOUT_LOG = join(DAEMON_ROOT, 'stdout.log');
|
|
12
|
+
export const STDERR_LOG = join(DAEMON_ROOT, 'stderr.log');
|
|
13
|
+
export const PLIST_LABEL = 'work.yome.daemon';
|
|
14
|
+
export function ensureDirs() {
|
|
15
|
+
mkdirSync(DAEMON_ROOT, { recursive: true });
|
|
16
|
+
mkdirSync(CRON_ROOT, { recursive: true });
|
|
17
|
+
mkdirSync(LOGS_ROOT, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
export function logFileForTask(taskId, runTs) {
|
|
20
|
+
const dir = join(LOGS_ROOT, taskId);
|
|
21
|
+
mkdirSync(dir, { recursive: true });
|
|
22
|
+
return join(dir, `${runTs}.jsonl`);
|
|
23
|
+
}
|
|
24
|
+
export function logDirForTask(taskId) {
|
|
25
|
+
return join(LOGS_ROOT, taskId);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/daemon/paths.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,qEAAqE;AAErE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAE/B,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACjD,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AACxD,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AAC1D,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAE9C,MAAM,UAAU,UAAU;IACxB,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,KAAa;IAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACpC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runTaskById(taskId: string): Promise<number>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Entry point for `yome __run-task <id>`.
|
|
2
|
+
//
|
|
3
|
+
// The cron trigger spawns a child process per fire, with this command.
|
|
4
|
+
// We load the task from tasks.json, build a config (same as interactive
|
|
5
|
+
// `yome`), call runDaemonTask, then exit with 0/1 based on success.
|
|
6
|
+
//
|
|
7
|
+
// This file is intentionally tiny — all business logic lives in
|
|
8
|
+
// runner.ts. Keeping the entry small means the cold-start cost per fire
|
|
9
|
+
// is just "load Node + parse tasks.json + bootstrap Agent".
|
|
10
|
+
import { resolveConfig, loadModelEntries, modelEntryToConfig } from '../config.js';
|
|
11
|
+
import { getTask } from './taskStore.js';
|
|
12
|
+
import { runDaemonTask } from './runner.js';
|
|
13
|
+
import { buildEnvHint } from './envHint.js';
|
|
14
|
+
export async function runTaskById(taskId) {
|
|
15
|
+
const t = getTask(taskId);
|
|
16
|
+
if (!t) {
|
|
17
|
+
console.error(`__run-task: no such task: ${taskId}`);
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
// Resolve the LLM config. Same precedence as interactive `yome`:
|
|
21
|
+
// stored config → first customModels entry → bail.
|
|
22
|
+
let config = resolveConfig({});
|
|
23
|
+
if (!config.apiKey) {
|
|
24
|
+
const entries = loadModelEntries();
|
|
25
|
+
if (entries.length > 0)
|
|
26
|
+
config = modelEntryToConfig(entries[0]);
|
|
27
|
+
}
|
|
28
|
+
if (!config.apiKey) {
|
|
29
|
+
console.error('__run-task: no API key — set one via `yome --key sk-...` first');
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
// Compose the final user prompt by stacking three layers, in order:
|
|
33
|
+
// 1. envHint — auto-detected local tools + per-OS gotchas, only in daemon mode.
|
|
34
|
+
// 2. extra — runtime context injected by file/calendar triggers via env var.
|
|
35
|
+
// 3. t.prompt — what the user originally wrote in `yome cron add "..."`.
|
|
36
|
+
// Each layer is independent: if the env hint detects nothing, it returns
|
|
37
|
+
// '' and is filtered out so we don't waste tokens on an empty header.
|
|
38
|
+
const envHint = buildEnvHint();
|
|
39
|
+
const extra = process.env.YOME_TASK_EXTRA_CONTEXT;
|
|
40
|
+
const promptWithContext = [envHint, extra?.trim(), t.prompt]
|
|
41
|
+
.filter((s) => !!s && s.length > 0)
|
|
42
|
+
.join('\n\n');
|
|
43
|
+
const result = await runDaemonTask(config, {
|
|
44
|
+
taskId: t.id,
|
|
45
|
+
prompt: promptWithContext,
|
|
46
|
+
cwd: t.cwd,
|
|
47
|
+
autoAllow: t.autoAllow,
|
|
48
|
+
autoDeny: t.autoDeny,
|
|
49
|
+
});
|
|
50
|
+
// Brief stdout summary — the audit jsonl in ~/.yome/cron/logs/<id>/
|
|
51
|
+
// has the full transcript. The parent process (scheduler) captures
|
|
52
|
+
// stdout and stuffs the tail into the run_end log entry, so this
|
|
53
|
+
// doubles as a coarse status line.
|
|
54
|
+
console.log(JSON.stringify({
|
|
55
|
+
taskId: t.id,
|
|
56
|
+
ok: result.ok,
|
|
57
|
+
toolCalls: result.toolCalls,
|
|
58
|
+
durationMs: result.durationMs,
|
|
59
|
+
inputTokens: result.inputTokens,
|
|
60
|
+
outputTokens: result.outputTokens,
|
|
61
|
+
error: result.error,
|
|
62
|
+
finalTextHead: result.finalText.slice(0, 200),
|
|
63
|
+
logFile: result.logFile,
|
|
64
|
+
}));
|
|
65
|
+
return result.ok ? 0 : 1;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=runTaskEntry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runTaskEntry.js","sourceRoot":"","sources":["../../src/daemon/runTaskEntry.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,oEAAoE;AACpE,EAAE;AACF,gEAAgE;AAChE,wEAAwE;AACxE,4DAA4D;AAE5D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc;IAC9C,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,iEAAiE;IACjE,qDAAqD;IACrD,IAAI,MAAM,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;QAChF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,oEAAoE;IACpE,sFAAsF;IACtF,qFAAqF;IACrF,8EAA8E;IAC9E,yEAAyE;IACzE,sEAAsE;IACtE,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;IAClD,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;SACzD,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC/C,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE;QACzC,MAAM,EAAE,CAAC,CAAC,EAAE;QACZ,MAAM,EAAE,iBAAiB;QACzB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACrB,CAAC,CAAC;IAEH,oEAAoE;IACpE,mEAAmE;IACnE,iEAAiE;IACjE,mCAAmC;IACnC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;QACzB,MAAM,EAAE,CAAC,CAAC,EAAE;QACZ,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,aAAa,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC7C,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC,CAAC;IAEJ,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { YomeConfig } from '../config.js';
|
|
2
|
+
export interface DaemonTaskSpec {
|
|
3
|
+
taskId: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
/** Permission allowlist, e.g. ['Read', 'Write', 'Yome(@yome/fs:*)']. */
|
|
7
|
+
autoAllow?: string[];
|
|
8
|
+
/** Permission denylist (overrides allowlist), e.g. ['Bash(rm:*)']. */
|
|
9
|
+
autoDeny?: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface DaemonTaskResult {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
finalText: string;
|
|
14
|
+
toolCalls: number;
|
|
15
|
+
inputTokens: number;
|
|
16
|
+
outputTokens: number;
|
|
17
|
+
durationMs: number;
|
|
18
|
+
logFile: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function runDaemonTask(config: YomeConfig, spec: DaemonTaskSpec): Promise<DaemonTaskResult>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// Daemon-side Agent runner — "non-interactive" mode.
|
|
2
|
+
//
|
|
3
|
+
// Reuses cli/src/agent.ts wholesale. The trick is in WHICH callbacks we
|
|
4
|
+
// register:
|
|
5
|
+
// - onAskPermission → auto-deny (with feedback to the LLM) instead of
|
|
6
|
+
// prompting a human
|
|
7
|
+
// - setAskUserHandler is NOT called → AskUser tool short-circuits per
|
|
8
|
+
// its own headless contract (see tools/askUser.ts)
|
|
9
|
+
// - autoAllow / autoDeny rules layered into the permission context
|
|
10
|
+
// - all tool_use / tool_result / text_delta written to a per-run jsonl
|
|
11
|
+
// audit log
|
|
12
|
+
//
|
|
13
|
+
// The agent's own MAX_ITERATIONS=30 (loops/simple.ts) is the iteration
|
|
14
|
+
// safety net. Wall-time budget is enforced by the parent process spawning
|
|
15
|
+
// the runner with a kill timer (see triggers/cron.ts).
|
|
16
|
+
import { Agent } from '../agent.js';
|
|
17
|
+
import { appendLog, openTaskLog } from './log.js';
|
|
18
|
+
/**
|
|
19
|
+
* Default safety denylist applied to every daemon task on top of the
|
|
20
|
+
* user's autoDeny.
|
|
21
|
+
*
|
|
22
|
+
* We deliberately keep this list TINY. Daemon tasks already require an
|
|
23
|
+
* explicit `--allow Bash(*)` to use Bash at all, so a second layer of
|
|
24
|
+
* "no rm, no chmod" was over-cautious and broke common scripted
|
|
25
|
+
* workflows (Agent writes a shell script then can't `chmod +x` it,
|
|
26
|
+
* Agent does `rm /tmp/somefile` and gets blocked, etc.).
|
|
27
|
+
*
|
|
28
|
+
* We only block the patterns that are catastrophic and cannot be
|
|
29
|
+
* undone: `sudo` (privilege escalation), and `rm -rf` against root or
|
|
30
|
+
* the user's home. Garden-variety `rm somefile` is allowed; if you don't
|
|
31
|
+
* want the agent deleting files at all, use `--deny "Bash(rm:*)"` per
|
|
32
|
+
* task.
|
|
33
|
+
*/
|
|
34
|
+
const DEFAULT_DAEMON_DENY = [
|
|
35
|
+
'Bash(sudo:*)',
|
|
36
|
+
'Bash(rm -rf /:*)',
|
|
37
|
+
'Bash(rm -rf /*:*)',
|
|
38
|
+
'Bash(rm -rf ~:*)',
|
|
39
|
+
'Bash(rm -rf ~/:*)',
|
|
40
|
+
'Bash(rm -rf $HOME:*)',
|
|
41
|
+
'Bash(rm -rf $HOME/:*)',
|
|
42
|
+
];
|
|
43
|
+
export async function runDaemonTask(config, spec) {
|
|
44
|
+
const runTs = Date.now();
|
|
45
|
+
const logFile = openTaskLog(spec.taskId, runTs);
|
|
46
|
+
appendLog(logFile, {
|
|
47
|
+
type: 'run_start',
|
|
48
|
+
taskId: spec.taskId,
|
|
49
|
+
prompt: spec.prompt,
|
|
50
|
+
autoAllow: spec.autoAllow ?? [],
|
|
51
|
+
autoDeny: spec.autoDeny ?? [],
|
|
52
|
+
cwd: spec.cwd ?? process.cwd(),
|
|
53
|
+
model: config.model,
|
|
54
|
+
});
|
|
55
|
+
// Optional cwd switch — many tasks expect to read/write under a specific
|
|
56
|
+
// directory, but we don't want to pollute the daemon's own cwd.
|
|
57
|
+
const originalCwd = process.cwd();
|
|
58
|
+
if (spec.cwd) {
|
|
59
|
+
try {
|
|
60
|
+
process.chdir(spec.cwd);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
appendLog(logFile, { type: 'error', stage: 'chdir', message: String(e) });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const agent = new Agent(config);
|
|
67
|
+
for (const rule of spec.autoAllow ?? []) {
|
|
68
|
+
agent.addPermissionRule(rule, 'allow', 'session');
|
|
69
|
+
}
|
|
70
|
+
for (const rule of [...DEFAULT_DAEMON_DENY, ...(spec.autoDeny ?? [])]) {
|
|
71
|
+
agent.addPermissionRule(rule, 'deny', 'session');
|
|
72
|
+
}
|
|
73
|
+
const onAskPermission = async (toolName, message, input) => {
|
|
74
|
+
appendLog(logFile, {
|
|
75
|
+
type: 'permission_denied_auto',
|
|
76
|
+
toolName,
|
|
77
|
+
message,
|
|
78
|
+
input: redactForLog(input),
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
decision: 'deny',
|
|
82
|
+
feedback: `Daemon mode: '${toolName}' is not in this task's autoAllow list. ` +
|
|
83
|
+
`Either complete the task with a different tool or stop and explain why.`,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
let finalText = '';
|
|
87
|
+
let toolCalls = 0;
|
|
88
|
+
let inputTokens = 0;
|
|
89
|
+
let outputTokens = 0;
|
|
90
|
+
let error;
|
|
91
|
+
try {
|
|
92
|
+
await agent.run(spec.prompt, {
|
|
93
|
+
onTextDelta: (text) => {
|
|
94
|
+
finalText += text;
|
|
95
|
+
// We don't log every delta (too chatty); the run_end entry has the
|
|
96
|
+
// full assembled text.
|
|
97
|
+
},
|
|
98
|
+
onToolUse: (name, input) => {
|
|
99
|
+
toolCalls++;
|
|
100
|
+
appendLog(logFile, { type: 'tool_use', name, input: redactForLog(input) });
|
|
101
|
+
},
|
|
102
|
+
onToolResult: (name, result) => {
|
|
103
|
+
appendLog(logFile, {
|
|
104
|
+
type: 'tool_result',
|
|
105
|
+
name,
|
|
106
|
+
result: result.length > 4_000 ? result.slice(0, 4_000) + '…[truncated]' : result,
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
onDone: (usage) => {
|
|
110
|
+
inputTokens = usage.inputTokens;
|
|
111
|
+
outputTokens = usage.outputTokens;
|
|
112
|
+
},
|
|
113
|
+
onError: (err) => {
|
|
114
|
+
error = err?.message ?? String(err);
|
|
115
|
+
appendLog(logFile, { type: 'error', stage: 'agent_loop', message: error });
|
|
116
|
+
},
|
|
117
|
+
onAskPermission,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (e) {
|
|
121
|
+
error = e?.message ?? String(e);
|
|
122
|
+
appendLog(logFile, { type: 'error', stage: 'unhandled', message: error });
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
if (spec.cwd) {
|
|
126
|
+
try {
|
|
127
|
+
process.chdir(originalCwd);
|
|
128
|
+
}
|
|
129
|
+
catch { /* noop */ }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const durationMs = Date.now() - runTs;
|
|
133
|
+
const ok = !error;
|
|
134
|
+
appendLog(logFile, {
|
|
135
|
+
type: 'run_end',
|
|
136
|
+
ok,
|
|
137
|
+
durationMs,
|
|
138
|
+
toolCalls,
|
|
139
|
+
inputTokens,
|
|
140
|
+
outputTokens,
|
|
141
|
+
finalText: finalText.length > 4_000 ? finalText.slice(0, 4_000) + '…[truncated]' : finalText,
|
|
142
|
+
error,
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
ok,
|
|
146
|
+
finalText: finalText.trim(),
|
|
147
|
+
toolCalls,
|
|
148
|
+
inputTokens,
|
|
149
|
+
outputTokens,
|
|
150
|
+
durationMs,
|
|
151
|
+
logFile,
|
|
152
|
+
error,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Strip obvious secrets from tool input before writing to disk. The audit
|
|
157
|
+
* log lives in plaintext so we should not leak API keys / tokens that the
|
|
158
|
+
* agent might pass through. Best-effort — not a security boundary.
|
|
159
|
+
*/
|
|
160
|
+
function redactForLog(input) {
|
|
161
|
+
const out = {};
|
|
162
|
+
for (const [k, v] of Object.entries(input)) {
|
|
163
|
+
if (/key|token|secret|password|authorization/i.test(k)) {
|
|
164
|
+
out[k] = '[redacted]';
|
|
165
|
+
}
|
|
166
|
+
else if (typeof v === 'string' && v.length > 2_000) {
|
|
167
|
+
out[k] = v.slice(0, 2_000) + '…[truncated]';
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
out[k] = v;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/daemon/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Daemon main loop.
|
|
2
|
+
//
|
|
3
|
+
// Responsibilities:
|
|
4
|
+
// 1. Read tasks.json on startup, register a node-cron job per
|
|
5
|
+
// enabled cron task.
|
|
6
|
+
// 2. Watch tasks.json for changes (re-register on edit so that
|
|
7
|
+
// `yome cron add/rm/...` takes effect without restarting daemon).
|
|
8
|
+
// 3. Handle SIGTERM/SIGINT cleanly (unregister jobs, remove pid file).
|
|
9
|
+
//
|
|
10
|
+
// File-trigger and calendar-trigger registration is left as TODO hooks
|
|
11
|
+
// (PR2 / PR4). The shape mirrors registerCronTask so adding a new
|
|
12
|
+
// trigger kind is "implement registerXxxTask + branch on kind here".
|
|
13
|
+
import { watch } from 'fs';
|
|
14
|
+
import { writeFileSync, unlinkSync, existsSync, readFileSync } from 'fs';
|
|
15
|
+
import { listTasks } from './taskStore.js';
|
|
16
|
+
import { registerCronTask, unregisterCronTask, listActive as listActiveCron, resolveYomeBinPath } from './triggers/cron.js';
|
|
17
|
+
import { registerOnceTask, unregisterOnceTask, listActiveOnce } from './triggers/once.js';
|
|
18
|
+
import { registerFileTask, unregisterFileTask, listActiveFile } from './triggers/file.js';
|
|
19
|
+
import { registerCalendarTask, unregisterCalendarTask, listActiveCalendar } from './triggers/calendar.js';
|
|
20
|
+
import { ensureDirs, PID_FILE, TASKS_FILE } from './paths.js';
|
|
21
|
+
let started = false;
|
|
22
|
+
let watcher = null;
|
|
23
|
+
let reloadDebounce = null;
|
|
24
|
+
export function startDaemon() {
|
|
25
|
+
if (started)
|
|
26
|
+
return;
|
|
27
|
+
started = true;
|
|
28
|
+
ensureDirs();
|
|
29
|
+
writePidFile();
|
|
30
|
+
installSignalHandlers();
|
|
31
|
+
reloadAllTasks();
|
|
32
|
+
watchTasksFile();
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.log(`[daemon] started (pid=${process.pid}); ` +
|
|
35
|
+
`cron=${listActiveCron().length} once=${listActiveOnce().length} ` +
|
|
36
|
+
`file=${listActiveFile().length} calendar=${listActiveCalendar().length}`);
|
|
37
|
+
}
|
|
38
|
+
function reloadAllTasks() {
|
|
39
|
+
const yomeBinPath = resolveYomeBinPath();
|
|
40
|
+
const tasks = listTasks();
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
for (const t of tasks) {
|
|
43
|
+
seen.add(t.id);
|
|
44
|
+
if (!t.enabled) {
|
|
45
|
+
unregisterAllForId(t.id);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
switch (t.trigger.kind) {
|
|
49
|
+
case 'cron':
|
|
50
|
+
registerCronTask(t, { yomeBinPath });
|
|
51
|
+
break;
|
|
52
|
+
case 'once':
|
|
53
|
+
registerOnceTask(t, { yomeBinPath });
|
|
54
|
+
break;
|
|
55
|
+
case 'file':
|
|
56
|
+
registerFileTask(t, { yomeBinPath });
|
|
57
|
+
break;
|
|
58
|
+
case 'calendar':
|
|
59
|
+
registerCalendarTask(t, { yomeBinPath });
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Drop any active registrations that no longer exist in tasks.json.
|
|
64
|
+
for (const activeId of [
|
|
65
|
+
...listActiveCron(),
|
|
66
|
+
...listActiveOnce().map((o) => o.taskId),
|
|
67
|
+
...listActiveFile(),
|
|
68
|
+
...listActiveCalendar(),
|
|
69
|
+
]) {
|
|
70
|
+
if (!seen.has(activeId))
|
|
71
|
+
unregisterAllForId(activeId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Idempotently unregister an id from every trigger subsystem. */
|
|
75
|
+
function unregisterAllForId(id) {
|
|
76
|
+
unregisterCronTask(id);
|
|
77
|
+
unregisterOnceTask(id);
|
|
78
|
+
unregisterFileTask(id);
|
|
79
|
+
unregisterCalendarTask(id);
|
|
80
|
+
}
|
|
81
|
+
function watchTasksFile() {
|
|
82
|
+
// tasks.json is written via tmp+rename (atomic). fs.watch on the file
|
|
83
|
+
// itself loses its handle after the first rename — the kernel inode
|
|
84
|
+
// changes out from under it. Watching the PARENT directory and
|
|
85
|
+
// filtering for tasks.json events survives renames forever and works
|
|
86
|
+
// on macOS, Linux, and Windows.
|
|
87
|
+
const dir = TASKS_FILE.replace(/\/[^/]+$/, '');
|
|
88
|
+
const fileBase = TASKS_FILE.split('/').pop();
|
|
89
|
+
try {
|
|
90
|
+
watcher = watch(dir, (_eventType, filename) => {
|
|
91
|
+
if (filename !== fileBase && filename !== `${fileBase}.tmp`)
|
|
92
|
+
return;
|
|
93
|
+
if (reloadDebounce)
|
|
94
|
+
clearTimeout(reloadDebounce);
|
|
95
|
+
reloadDebounce = setTimeout(() => {
|
|
96
|
+
if (!existsSync(TASKS_FILE))
|
|
97
|
+
return; // mid-rename window
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.log('[daemon] tasks.json changed, reloading…');
|
|
100
|
+
try {
|
|
101
|
+
reloadAllTasks();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
console.error('[daemon] reload failed:', e);
|
|
105
|
+
}
|
|
106
|
+
}, 250);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
console.error('[daemon] failed to watch tasks dir:', e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function writePidFile() {
|
|
114
|
+
try {
|
|
115
|
+
writeFileSync(PID_FILE, String(process.pid), 'utf-8');
|
|
116
|
+
}
|
|
117
|
+
catch { /* noop */ }
|
|
118
|
+
}
|
|
119
|
+
function removePidFile() {
|
|
120
|
+
try {
|
|
121
|
+
unlinkSync(PID_FILE);
|
|
122
|
+
}
|
|
123
|
+
catch { /* noop */ }
|
|
124
|
+
}
|
|
125
|
+
function installSignalHandlers() {
|
|
126
|
+
const shutdown = (sig) => {
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
128
|
+
console.log(`[daemon] received ${sig}, shutting down…`);
|
|
129
|
+
if (watcher)
|
|
130
|
+
try {
|
|
131
|
+
watcher.close();
|
|
132
|
+
}
|
|
133
|
+
catch { /* noop */ }
|
|
134
|
+
removePidFile();
|
|
135
|
+
process.exit(0);
|
|
136
|
+
};
|
|
137
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
138
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
139
|
+
}
|
|
140
|
+
/** Used by `yome daemon status` (called from a separate process — peeks pid file). */
|
|
141
|
+
export function readPidIfRunning() {
|
|
142
|
+
try {
|
|
143
|
+
if (!existsSync(PID_FILE))
|
|
144
|
+
return null;
|
|
145
|
+
const raw = readFileSync(PID_FILE, 'utf-8').trim();
|
|
146
|
+
const pid = Number.parseInt(raw, 10);
|
|
147
|
+
if (!pid || Number.isNaN(pid))
|
|
148
|
+
return null;
|
|
149
|
+
// process.kill(pid, 0) throws if the process doesn't exist.
|
|
150
|
+
try {
|
|
151
|
+
process.kill(pid, 0);
|
|
152
|
+
return pid;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/daemon/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type CalendarEventKind = 'event-start' | 'event-end' | 'event-added';
|
|
2
|
+
export type TriggerSpec = {
|
|
3
|
+
kind: 'cron';
|
|
4
|
+
schedule: string;
|
|
5
|
+
tz?: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'once';
|
|
8
|
+
atMs: number;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'file';
|
|
11
|
+
path: string;
|
|
12
|
+
events?: ('change' | 'add' | 'unlink')[];
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'calendar';
|
|
15
|
+
events: CalendarEventKind[];
|
|
16
|
+
/** For event-start: fire this many ms BEFORE the event begins. Default 0. */
|
|
17
|
+
leadMs?: number;
|
|
18
|
+
/** Optional case-insensitive regex on event title. */
|
|
19
|
+
titleRegex?: string;
|
|
20
|
+
/** Optional case-insensitive substring of calendar display name. */
|
|
21
|
+
calendar?: string;
|
|
22
|
+
};
|
|
23
|
+
export interface RunSummary {
|
|
24
|
+
ts: number;
|
|
25
|
+
ok: boolean;
|
|
26
|
+
durationMs: number;
|
|
27
|
+
toolCalls: number;
|
|
28
|
+
inputTokens: number;
|
|
29
|
+
outputTokens: number;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface TaskRecord {
|
|
33
|
+
id: string;
|
|
34
|
+
createdAt: number;
|
|
35
|
+
trigger: TriggerSpec;
|
|
36
|
+
prompt: string;
|
|
37
|
+
cwd?: string;
|
|
38
|
+
/** Permission allow rules layered onto the agent for this task. */
|
|
39
|
+
autoAllow?: string[];
|
|
40
|
+
/** Permission deny rules — override allow. */
|
|
41
|
+
autoDeny?: string[];
|
|
42
|
+
/** Hard wall-time budget. Default 5 min in runner.ts. */
|
|
43
|
+
maxDurationMs?: number;
|
|
44
|
+
/** Extra environment variables passed to the spawned __run-task child.
|
|
45
|
+
* Useful for skill-level switches (e.g. YOME_WEB_HEADLESS=0 to open a
|
|
46
|
+
* visible browser window) without polluting the daemon's own env. */
|
|
47
|
+
env?: Record<string, string>;
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
lastRun?: RunSummary;
|
|
50
|
+
/** Last N run summaries (newest first). Bounded to avoid unbounded growth. */
|
|
51
|
+
history?: RunSummary[];
|
|
52
|
+
/** Free-form description shown in `yome cron list`. */
|
|
53
|
+
note?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function listTasks(): TaskRecord[];
|
|
56
|
+
export declare function getTask(id: string): TaskRecord | undefined;
|
|
57
|
+
export declare function addTask(input: Omit<TaskRecord, 'id' | 'createdAt' | 'enabled'> & {
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
}): TaskRecord;
|
|
60
|
+
export declare function removeTask(id: string): boolean;
|
|
61
|
+
export declare function setEnabled(id: string, enabled: boolean): boolean;
|
|
62
|
+
export declare function recordRun(id: string, summary: RunSummary): void;
|