@namzu/cli 29.0.1 → 29.1.0
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/CHANGELOG.md +115 -0
- package/README.md +20 -0
- package/dist/browser/commands.d.ts +29 -0
- package/dist/browser/commands.d.ts.map +1 -0
- package/dist/browser/commands.js +361 -0
- package/dist/browser/commands.js.map +1 -0
- package/dist/browser/control.d.ts +79 -0
- package/dist/browser/control.d.ts.map +1 -0
- package/dist/browser/control.js +140 -0
- package/dist/browser/control.js.map +1 -0
- package/dist/browser/describe.d.ts +12 -0
- package/dist/browser/describe.d.ts.map +1 -0
- package/dist/browser/describe.js +27 -0
- package/dist/browser/describe.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +1 -0
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/browser.d.ts +7 -0
- package/dist/commands/browser.d.ts.map +1 -0
- package/dist/commands/browser.js +16 -0
- package/dist/commands/browser.js.map +1 -0
- package/dist/commands/drain.d.ts +2 -1
- package/dist/commands/drain.d.ts.map +1 -1
- package/dist/commands/drain.js +6 -3
- package/dist/commands/drain.js.map +1 -1
- package/dist/commands/exec-flags.d.ts +2 -1
- package/dist/commands/exec-flags.d.ts.map +1 -1
- package/dist/commands/exec-flags.js +6 -3
- package/dist/commands/exec-flags.js.map +1 -1
- package/dist/commands/exec-json.d.ts.map +1 -1
- package/dist/commands/exec-json.js +2 -1
- package/dist/commands/exec-json.js.map +1 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +2 -1
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/host-queries.d.ts.map +1 -1
- package/dist/commands/host-queries.js +10 -3
- package/dist/commands/host-queries.js.map +1 -1
- package/dist/commands/schedule.d.ts +9 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/schedule.js +126 -0
- package/dist/commands/schedule.js.map +1 -0
- package/dist/commands/serve.d.ts +3 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +5 -3
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/skills.d.ts +10 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +23 -7
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +11 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/config/load.d.ts +39 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +232 -1
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +78 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/user-config.d.ts +23 -0
- package/dist/config/user-config.d.ts.map +1 -0
- package/dist/config/user-config.js +48 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/context/capabilities.d.ts +1 -1
- package/dist/context/capabilities.d.ts.map +1 -1
- package/dist/context/capabilities.js +1 -0
- package/dist/context/capabilities.js.map +1 -1
- package/dist/doctor/checks/browser.d.ts +15 -0
- package/dist/doctor/checks/browser.d.ts.map +1 -0
- package/dist/doctor/checks/browser.js +63 -0
- package/dist/doctor/checks/browser.js.map +1 -0
- package/dist/doctor/checks/index.d.ts +4 -1
- package/dist/doctor/checks/index.d.ts.map +1 -1
- package/dist/doctor/checks/index.js +8 -1
- package/dist/doctor/checks/index.js.map +1 -1
- package/dist/doctor/checks/scheduler.d.ts +10 -0
- package/dist/doctor/checks/scheduler.d.ts.map +1 -0
- package/dist/doctor/checks/scheduler.js +74 -0
- package/dist/doctor/checks/scheduler.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/notifications/desktop/freedesktop.d.ts +10 -0
- package/dist/integrations/notifications/desktop/freedesktop.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/freedesktop.js +33 -0
- package/dist/integrations/notifications/desktop/freedesktop.js.map +1 -0
- package/dist/integrations/notifications/desktop/macos.d.ts +6 -0
- package/dist/integrations/notifications/desktop/macos.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/macos.js +17 -0
- package/dist/integrations/notifications/desktop/macos.js.map +1 -0
- package/dist/integrations/notifications/desktop/sanitize.d.ts +16 -0
- package/dist/integrations/notifications/desktop/sanitize.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/sanitize.js +49 -0
- package/dist/integrations/notifications/desktop/sanitize.js.map +1 -0
- package/dist/integrations/notifications/desktop/throttle.d.ts +10 -0
- package/dist/integrations/notifications/desktop/throttle.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/throttle.js +39 -0
- package/dist/integrations/notifications/desktop/throttle.js.map +1 -0
- package/dist/integrations/notifications/desktop/windows-toast.d.ts +23 -0
- package/dist/integrations/notifications/desktop/windows-toast.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop/windows-toast.js +56 -0
- package/dist/integrations/notifications/desktop/windows-toast.js.map +1 -0
- package/dist/integrations/notifications/desktop.d.ts +82 -0
- package/dist/integrations/notifications/desktop.d.ts.map +1 -0
- package/dist/integrations/notifications/desktop.js +198 -0
- package/dist/integrations/notifications/desktop.js.map +1 -0
- package/dist/integrations/resident/session-step.d.ts.map +1 -1
- package/dist/integrations/resident/session-step.js +1 -0
- package/dist/integrations/resident/session-step.js.map +1 -1
- package/dist/integrations/subagents/runtime.d.ts +6 -0
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +14 -5
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/permissions/browser-sites.d.ts +95 -0
- package/dist/permissions/browser-sites.d.ts.map +1 -0
- package/dist/permissions/browser-sites.js +270 -0
- package/dist/permissions/browser-sites.js.map +1 -0
- package/dist/schedule/build.d.ts +79 -0
- package/dist/schedule/build.d.ts.map +1 -0
- package/dist/schedule/build.js +246 -0
- package/dist/schedule/build.js.map +1 -0
- package/dist/schedule/changes.d.ts +22 -0
- package/dist/schedule/changes.d.ts.map +1 -0
- package/dist/schedule/changes.js +55 -0
- package/dist/schedule/changes.js.map +1 -0
- package/dist/schedule/commands/add.d.ts +24 -0
- package/dist/schedule/commands/add.d.ts.map +1 -0
- package/dist/schedule/commands/add.js +459 -0
- package/dist/schedule/commands/add.js.map +1 -0
- package/dist/schedule/commands/args.d.ts +22 -0
- package/dist/schedule/commands/args.d.ts.map +1 -0
- package/dist/schedule/commands/args.js +80 -0
- package/dist/schedule/commands/args.js.map +1 -0
- package/dist/schedule/commands/confirm-prompt.d.ts +6 -0
- package/dist/schedule/commands/confirm-prompt.d.ts.map +1 -0
- package/dist/schedule/commands/confirm-prompt.js +21 -0
- package/dist/schedule/commands/confirm-prompt.js.map +1 -0
- package/dist/schedule/commands/lifecycle.d.ts +39 -0
- package/dist/schedule/commands/lifecycle.d.ts.map +1 -0
- package/dist/schedule/commands/lifecycle.js +585 -0
- package/dist/schedule/commands/lifecycle.js.map +1 -0
- package/dist/schedule/commands/list.d.ts +33 -0
- package/dist/schedule/commands/list.d.ts.map +1 -0
- package/dist/schedule/commands/list.js +202 -0
- package/dist/schedule/commands/list.js.map +1 -0
- package/dist/schedule/commands/logs.d.ts +7 -0
- package/dist/schedule/commands/logs.d.ts.map +1 -0
- package/dist/schedule/commands/logs.js +106 -0
- package/dist/schedule/commands/logs.js.map +1 -0
- package/dist/schedule/commands/service.d.ts +16 -0
- package/dist/schedule/commands/service.d.ts.map +1 -0
- package/dist/schedule/commands/service.js +458 -0
- package/dist/schedule/commands/service.js.map +1 -0
- package/dist/schedule/daemon/daemon.d.ts +156 -0
- package/dist/schedule/daemon/daemon.d.ts.map +1 -0
- package/dist/schedule/daemon/daemon.js +1248 -0
- package/dist/schedule/daemon/daemon.js.map +1 -0
- package/dist/schedule/daemon/endpoint.d.ts +38 -0
- package/dist/schedule/daemon/endpoint.d.ts.map +1 -0
- package/dist/schedule/daemon/endpoint.js +158 -0
- package/dist/schedule/daemon/endpoint.js.map +1 -0
- package/dist/schedule/daemon/exit.d.ts +8 -0
- package/dist/schedule/daemon/exit.d.ts.map +1 -0
- package/dist/schedule/daemon/exit.js +8 -0
- package/dist/schedule/daemon/exit.js.map +1 -0
- package/dist/schedule/daemon/log.d.ts +12 -0
- package/dist/schedule/daemon/log.d.ts.map +1 -0
- package/dist/schedule/daemon/log.js +53 -0
- package/dist/schedule/daemon/log.js.map +1 -0
- package/dist/schedule/daemon/notify.d.ts +31 -0
- package/dist/schedule/daemon/notify.d.ts.map +1 -0
- package/dist/schedule/daemon/notify.js +89 -0
- package/dist/schedule/daemon/notify.js.map +1 -0
- package/dist/schedule/daemon/retention.d.ts +16 -0
- package/dist/schedule/daemon/retention.d.ts.map +1 -0
- package/dist/schedule/daemon/retention.js +46 -0
- package/dist/schedule/daemon/retention.js.map +1 -0
- package/dist/schedule/daemon/sessions.d.ts +23 -0
- package/dist/schedule/daemon/sessions.d.ts.map +1 -0
- package/dist/schedule/daemon/sessions.js +52 -0
- package/dist/schedule/daemon/sessions.js.map +1 -0
- package/dist/schedule/env.d.ts +28 -0
- package/dist/schedule/env.d.ts.map +1 -0
- package/dist/schedule/env.js +99 -0
- package/dist/schedule/env.js.map +1 -0
- package/dist/schedule/fire/browser-preflight.d.ts +41 -0
- package/dist/schedule/fire/browser-preflight.d.ts.map +1 -0
- package/dist/schedule/fire/browser-preflight.js +73 -0
- package/dist/schedule/fire/browser-preflight.js.map +1 -0
- package/dist/schedule/fire/fire.d.ts +78 -0
- package/dist/schedule/fire/fire.d.ts.map +1 -0
- package/dist/schedule/fire/fire.js +482 -0
- package/dist/schedule/fire/fire.js.map +1 -0
- package/dist/schedule/fire/result.d.ts +13 -0
- package/dist/schedule/fire/result.d.ts.map +1 -0
- package/dist/schedule/fire/result.js +18 -0
- package/dist/schedule/fire/result.js.map +1 -0
- package/dist/schedule/fire/unattended-note.d.ts +22 -0
- package/dist/schedule/fire/unattended-note.d.ts.map +1 -0
- package/dist/schedule/fire/unattended-note.js +46 -0
- package/dist/schedule/fire/unattended-note.js.map +1 -0
- package/dist/schedule/floor.d.ts +116 -0
- package/dist/schedule/floor.d.ts.map +1 -0
- package/dist/schedule/floor.js +1051 -0
- package/dist/schedule/floor.js.map +1 -0
- package/dist/schedule/folder.d.ts +34 -0
- package/dist/schedule/folder.d.ts.map +1 -0
- package/dist/schedule/folder.js +84 -0
- package/dist/schedule/folder.js.map +1 -0
- package/dist/schedule/paths.d.ts +49 -0
- package/dist/schedule/paths.d.ts.map +1 -0
- package/dist/schedule/paths.js +53 -0
- package/dist/schedule/paths.js.map +1 -0
- package/dist/schedule/policy.d.ts +141 -0
- package/dist/schedule/policy.d.ts.map +1 -0
- package/dist/schedule/policy.js +378 -0
- package/dist/schedule/policy.js.map +1 -0
- package/dist/schedule/resume-command.d.ts +36 -0
- package/dist/schedule/resume-command.d.ts.map +1 -0
- package/dist/schedule/resume-command.js +72 -0
- package/dist/schedule/resume-command.js.map +1 -0
- package/dist/schedule/service/detect.d.ts +22 -0
- package/dist/schedule/service/detect.d.ts.map +1 -0
- package/dist/schedule/service/detect.js +29 -0
- package/dist/schedule/service/detect.js.map +1 -0
- package/dist/schedule/service/index.d.ts +48 -0
- package/dist/schedule/service/index.d.ts.map +1 -0
- package/dist/schedule/service/index.js +352 -0
- package/dist/schedule/service/index.js.map +1 -0
- package/dist/schedule/service/launchd.d.ts +24 -0
- package/dist/schedule/service/launchd.d.ts.map +1 -0
- package/dist/schedule/service/launchd.js +84 -0
- package/dist/schedule/service/launchd.js.map +1 -0
- package/dist/schedule/service/manifest.d.ts +44 -0
- package/dist/schedule/service/manifest.d.ts.map +1 -0
- package/dist/schedule/service/manifest.js +16 -0
- package/dist/schedule/service/manifest.js.map +1 -0
- package/dist/schedule/service/names.d.ts +9 -0
- package/dist/schedule/service/names.d.ts.map +1 -0
- package/dist/schedule/service/names.js +22 -0
- package/dist/schedule/service/names.js.map +1 -0
- package/dist/schedule/service/quote.d.ts +24 -0
- package/dist/schedule/service/quote.d.ts.map +1 -0
- package/dist/schedule/service/quote.js +64 -0
- package/dist/schedule/service/quote.js.map +1 -0
- package/dist/schedule/service/runner.d.ts +17 -0
- package/dist/schedule/service/runner.d.ts.map +1 -0
- package/dist/schedule/service/runner.js +31 -0
- package/dist/schedule/service/runner.js.map +1 -0
- package/dist/schedule/service/systemd.d.ts +37 -0
- package/dist/schedule/service/systemd.d.ts.map +1 -0
- package/dist/schedule/service/systemd.js +96 -0
- package/dist/schedule/service/systemd.js.map +1 -0
- package/dist/schedule/service/windows-task.d.ts +51 -0
- package/dist/schedule/service/windows-task.d.ts.map +1 -0
- package/dist/schedule/service/windows-task.js +134 -0
- package/dist/schedule/service/windows-task.js.map +1 -0
- package/dist/schedule/service/wsl.d.ts +57 -0
- package/dist/schedule/service/wsl.d.ts.map +1 -0
- package/dist/schedule/service/wsl.js +111 -0
- package/dist/schedule/service/wsl.js.map +1 -0
- package/dist/schedule/store/atomic.d.ts +43 -0
- package/dist/schedule/store/atomic.d.ts.map +1 -0
- package/dist/schedule/store/atomic.js +135 -0
- package/dist/schedule/store/atomic.js.map +1 -0
- package/dist/schedule/store/claims.d.ts +26 -0
- package/dist/schedule/store/claims.d.ts.map +1 -0
- package/dist/schedule/store/claims.js +57 -0
- package/dist/schedule/store/claims.js.map +1 -0
- package/dist/schedule/store/digest.d.ts +18 -0
- package/dist/schedule/store/digest.d.ts.map +1 -0
- package/dist/schedule/store/digest.js +139 -0
- package/dist/schedule/store/digest.js.map +1 -0
- package/dist/schedule/store/history.d.ts +21 -0
- package/dist/schedule/store/history.d.ts.map +1 -0
- package/dist/schedule/store/history.js +114 -0
- package/dist/schedule/store/history.js.map +1 -0
- package/dist/schedule/store/jobs.d.ts +61 -0
- package/dist/schedule/store/jobs.d.ts.map +1 -0
- package/dist/schedule/store/jobs.js +210 -0
- package/dist/schedule/store/jobs.js.map +1 -0
- package/dist/schedule/store/state.d.ts +19 -0
- package/dist/schedule/store/state.d.ts.map +1 -0
- package/dist/schedule/store/state.js +38 -0
- package/dist/schedule/store/state.js.map +1 -0
- package/dist/schedule/types.d.ts +256 -0
- package/dist/schedule/types.d.ts.map +1 -0
- package/dist/schedule/types.js +9 -0
- package/dist/schedule/types.js.map +1 -0
- package/dist/skills/catalog.d.ts +96 -0
- package/dist/skills/catalog.d.ts.map +1 -0
- package/dist/skills/catalog.js +226 -0
- package/dist/skills/catalog.js.map +1 -0
- package/dist/skills/save.d.ts +148 -0
- package/dist/skills/save.d.ts.map +1 -0
- package/dist/skills/save.js +344 -0
- package/dist/skills/save.js.map +1 -0
- package/dist/skills/store.d.ts +103 -16
- package/dist/skills/store.d.ts.map +1 -1
- package/dist/skills/store.js +227 -30
- package/dist/skills/store.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +591 -72
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/LiveActivity.d.ts +8 -1
- package/dist/tui/LiveActivity.d.ts.map +1 -1
- package/dist/tui/LiveActivity.js +20 -4
- package/dist/tui/LiveActivity.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +24 -4
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +113 -15
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/SaveSkillOverlay.d.ts +33 -0
- package/dist/tui/SaveSkillOverlay.d.ts.map +1 -0
- package/dist/tui/SaveSkillOverlay.js +118 -0
- package/dist/tui/SaveSkillOverlay.js.map +1 -0
- package/dist/tui/agent.d.ts +185 -5
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +325 -54
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/browser-notices.d.ts +33 -0
- package/dist/tui/browser-notices.d.ts.map +1 -0
- package/dist/tui/browser-notices.js +102 -0
- package/dist/tui/browser-notices.js.map +1 -0
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/permission-review.js +53 -4
- package/dist/tui/permission-review.js.map +1 -1
- package/dist/tui/schedule/host-commands.d.ts +34 -0
- package/dist/tui/schedule/host-commands.d.ts.map +1 -0
- package/dist/tui/schedule/host-commands.js +309 -0
- package/dist/tui/schedule/host-commands.js.map +1 -0
- package/dist/tui/schedule/integration.d.ts +62 -0
- package/dist/tui/schedule/integration.d.ts.map +1 -0
- package/dist/tui/schedule/integration.js +145 -0
- package/dist/tui/schedule/integration.js.map +1 -0
- package/dist/tui/schedule/loop-host.d.ts +42 -0
- package/dist/tui/schedule/loop-host.d.ts.map +1 -0
- package/dist/tui/schedule/loop-host.js +153 -0
- package/dist/tui/schedule/loop-host.js.map +1 -0
- package/dist/tui/schedule/resume.d.ts +122 -0
- package/dist/tui/schedule/resume.d.ts.map +1 -0
- package/dist/tui/schedule/resume.js +324 -0
- package/dist/tui/schedule/resume.js.map +1 -0
- package/dist/tui/schedule/startup.d.ts +11 -0
- package/dist/tui/schedule/startup.d.ts.map +1 -0
- package/dist/tui/schedule/startup.js +121 -0
- package/dist/tui/schedule/startup.js.map +1 -0
- package/dist/tui/schedule/tool-host.d.ts +44 -0
- package/dist/tui/schedule/tool-host.d.ts.map +1 -0
- package/dist/tui/schedule/tool-host.js +294 -0
- package/dist/tui/schedule/tool-host.js.map +1 -0
- package/dist/tui/skills/learning.d.ts +74 -0
- package/dist/tui/skills/learning.d.ts.map +1 -0
- package/dist/tui/skills/learning.js +131 -0
- package/dist/tui/skills/learning.js.map +1 -0
- package/dist/tui/skills/suggest-setting.d.ts +22 -0
- package/dist/tui/skills/suggest-setting.d.ts.map +1 -0
- package/dist/tui/skills/suggest-setting.js +55 -0
- package/dist/tui/skills/suggest-setting.js.map +1 -0
- package/dist/tui/skills/suggestion-ledger.d.ts +35 -0
- package/dist/tui/skills/suggestion-ledger.d.ts.map +1 -0
- package/dist/tui/skills/suggestion-ledger.js +52 -0
- package/dist/tui/skills/suggestion-ledger.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +20 -0
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +76 -2
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/turn-interruption.d.ts +20 -1
- package/dist/tui/turn-interruption.d.ts.map +1 -1
- package/dist/tui/turn-interruption.js +43 -0
- package/dist/tui/turn-interruption.js.map +1 -1
- package/dist/tui/types.d.ts +9 -1
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +6 -4
- package/skills/README.md +15 -0
- package/skills/browser-automation/SKILL.md +84 -0
- package/skills/schedule-task/SKILL.md +140 -0
- package/skills/skill-creator/SKILL.md +101 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `schedule/history/<job-id>.jsonl`: one JSON record per line, appended with
|
|
3
|
+
* a single `write` on an `O_APPEND` descriptor. Lines stay under 4 KiB, so an
|
|
4
|
+
* append is atomic on a local POSIX file system and two writers never
|
|
5
|
+
* interleave inside a line. A run's later status is a NEW record with the
|
|
6
|
+
* same `runId`; the last one wins, and nothing is rewritten in place except by
|
|
7
|
+
* compaction.
|
|
8
|
+
*/
|
|
9
|
+
import { closeSync, openSync, readFileSync, renameSync, statSync, writeSync } from 'node:fs';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
import { PRIVATE_FILE_MODE, ensureDir } from './atomic.js';
|
|
12
|
+
const MAX_LINE = 4_000;
|
|
13
|
+
const KEEP_RECORDS = 500;
|
|
14
|
+
const KEEP_MS = 180 * 24 * 60 * 60 * 1000;
|
|
15
|
+
/** A model-produced string trimmed so a record fits one atomic line. */
|
|
16
|
+
function fit(record) {
|
|
17
|
+
let line = JSON.stringify(record);
|
|
18
|
+
if (line.length <= MAX_LINE)
|
|
19
|
+
return line;
|
|
20
|
+
if (record.kind === 'run') {
|
|
21
|
+
const trimmed = {
|
|
22
|
+
...record,
|
|
23
|
+
...(record.summary ? { summary: record.summary.slice(0, 200) } : {}),
|
|
24
|
+
...(record.reason ? { reason: record.reason.slice(0, 1_000) } : {}),
|
|
25
|
+
...(record.warnings
|
|
26
|
+
? { warnings: record.warnings.slice(0, 3).map((w) => w.slice(0, 300)) }
|
|
27
|
+
: {}),
|
|
28
|
+
};
|
|
29
|
+
line = JSON.stringify(trimmed);
|
|
30
|
+
}
|
|
31
|
+
return line.length <= MAX_LINE
|
|
32
|
+
? line
|
|
33
|
+
: JSON.stringify({ ...record, detail: undefined, reason: 'record too long' });
|
|
34
|
+
}
|
|
35
|
+
export function appendHistory(paths, jobId, record) {
|
|
36
|
+
const path = paths.historyOf(jobId);
|
|
37
|
+
ensureDir(dirname(path));
|
|
38
|
+
const fd = openSync(path, 'a', PRIVATE_FILE_MODE);
|
|
39
|
+
try {
|
|
40
|
+
writeSync(fd, `${fit(record)}\n`);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
closeSync(fd);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Every readable record, oldest first. A torn or foreign line is skipped. */
|
|
47
|
+
export function readHistory(paths, jobId) {
|
|
48
|
+
let text;
|
|
49
|
+
try {
|
|
50
|
+
text = readFileSync(paths.historyOf(jobId), 'utf8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const line of text.split('\n')) {
|
|
57
|
+
if (!line.trim())
|
|
58
|
+
continue;
|
|
59
|
+
try {
|
|
60
|
+
const record = JSON.parse(line);
|
|
61
|
+
if (record && record.v === 1 && typeof record.kind === 'string')
|
|
62
|
+
out.push(record);
|
|
63
|
+
}
|
|
64
|
+
catch { }
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Runs folded by `runId` (the last record of each wins), plus the other
|
|
70
|
+
* records, newest first.
|
|
71
|
+
*/
|
|
72
|
+
export function foldHistory(records) {
|
|
73
|
+
const runs = new Map();
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const record of records) {
|
|
76
|
+
if (record.kind === 'run') {
|
|
77
|
+
const at = runs.get(record.runId);
|
|
78
|
+
if (at !== undefined) {
|
|
79
|
+
out[at] = record;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
runs.set(record.runId, out.length);
|
|
83
|
+
}
|
|
84
|
+
out.push(record);
|
|
85
|
+
}
|
|
86
|
+
return out.reverse();
|
|
87
|
+
}
|
|
88
|
+
/** Keep the last 500 records or 180 days, whichever is more. Returns how many were dropped. */
|
|
89
|
+
export function compactHistory(paths, jobId, now = Date.now()) {
|
|
90
|
+
const path = paths.historyOf(jobId);
|
|
91
|
+
try {
|
|
92
|
+
if (statSync(path).size < 256 * 1024)
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
const records = readHistory(paths, jobId);
|
|
99
|
+
const keepFrom = Math.max(0, records.length - KEEP_RECORDS);
|
|
100
|
+
const kept = records.filter((r, i) => i >= keepFrom || now - Date.parse(r.at) <= KEEP_MS);
|
|
101
|
+
if (kept.length === records.length)
|
|
102
|
+
return 0;
|
|
103
|
+
const temporary = `${path}.${process.pid}.compact`;
|
|
104
|
+
const fd = openSync(temporary, 'w', PRIVATE_FILE_MODE);
|
|
105
|
+
try {
|
|
106
|
+
writeSync(fd, kept.map((r) => `${JSON.stringify(r)}\n`).join(''));
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
closeSync(fd);
|
|
110
|
+
}
|
|
111
|
+
renameSync(temporary, path);
|
|
112
|
+
return records.length - kept.length;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../../src/schedule/store/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job definitions: `schedule/jobs/<job-id>.json`.
|
|
3
|
+
*
|
|
4
|
+
* A job changes only by an operator action — the CLI, the TUI, a confirmed
|
|
5
|
+
* tool call — or by the daemon's own lifecycle transitions (completed,
|
|
6
|
+
* expired, paused after failures, held after an edit it did not make). Every
|
|
7
|
+
* write is a compare-and-set on `revision`: the writer first publishes a
|
|
8
|
+
* revision marker `jobs/.revisions/<job-id>/<n>` with `link`, and only the one
|
|
9
|
+
* writer that published marker `n` writes revision `n`. Two writers that read
|
|
10
|
+
* the same revision cannot both win; the loser gets
|
|
11
|
+
* {@link ScheduleConflictError} and re-reads.
|
|
12
|
+
*
|
|
13
|
+
* A hand edit of the JSON skips all of this. That is not prevented — the file
|
|
14
|
+
* is the operator's — but it is caught: the job's confirmation digest no
|
|
15
|
+
* longer matches, and the daemon holds the job until someone confirms it.
|
|
16
|
+
*/
|
|
17
|
+
import type { SchedulePaths } from '../paths.js';
|
|
18
|
+
import type { ScheduleJob } from '../types.js';
|
|
19
|
+
export declare const JOB_NAME: RegExp;
|
|
20
|
+
export declare class ScheduleConflictError extends Error {
|
|
21
|
+
readonly jobId: string;
|
|
22
|
+
readonly name = "ScheduleConflictError";
|
|
23
|
+
constructor(jobId: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class ScheduleJobNotFoundError extends Error {
|
|
26
|
+
readonly ref: string;
|
|
27
|
+
readonly name = "ScheduleJobNotFoundError";
|
|
28
|
+
constructor(ref: string);
|
|
29
|
+
}
|
|
30
|
+
/** JSON with object keys sorted, so a digest does not depend on write order. */
|
|
31
|
+
export declare function stableStringify(value: unknown): string;
|
|
32
|
+
/**
|
|
33
|
+
* The digest of what a confirmation vouches for: the prompt, the folder and
|
|
34
|
+
* its trust, the permissions, the schedule, the model, the budget, the
|
|
35
|
+
* approval window and the pinned project config. Not a secret — a tripwire.
|
|
36
|
+
*/
|
|
37
|
+
export declare function jobSecurityDigest(job: ScheduleJob): string;
|
|
38
|
+
/** Whether a job's confirmation still vouches for what the file says. */
|
|
39
|
+
export declare function confirmationHolds(job: ScheduleJob): boolean;
|
|
40
|
+
export declare function readJob(paths: SchedulePaths, id: string): ScheduleJob | undefined;
|
|
41
|
+
export interface JobListing {
|
|
42
|
+
readonly jobs: ScheduleJob[];
|
|
43
|
+
/** Files that could not be read, with why. Never silently dropped. */
|
|
44
|
+
readonly errors: {
|
|
45
|
+
readonly path: string;
|
|
46
|
+
readonly message: string;
|
|
47
|
+
}[];
|
|
48
|
+
}
|
|
49
|
+
export declare function listJobs(paths: SchedulePaths): JobListing;
|
|
50
|
+
/** A job by exact name or by an unambiguous id prefix (four characters at least). */
|
|
51
|
+
export declare function findJob(paths: SchedulePaths, ref: string): ScheduleJob;
|
|
52
|
+
/** Write a new job. Refuses a name another job has. */
|
|
53
|
+
export declare function createJob(paths: SchedulePaths, job: ScheduleJob): ScheduleJob;
|
|
54
|
+
/**
|
|
55
|
+
* Change a job, compare-and-set on the revision read. `mutate` returns the
|
|
56
|
+
* new body; revision and `updatedAt` are set here.
|
|
57
|
+
*/
|
|
58
|
+
export declare function updateJob(paths: SchedulePaths, id: string, expectedRevision: number, mutate: (job: ScheduleJob) => ScheduleJob, now?: Date): ScheduleJob;
|
|
59
|
+
/** Remove a job's definition, markers, state and claims. History is kept. */
|
|
60
|
+
export declare function deleteJob(paths: SchedulePaths, id: string): void;
|
|
61
|
+
//# sourceMappingURL=jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../src/schedule/store/jobs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAS9C,eAAO,MAAM,QAAQ,QAA8B,CAAA;AAMnD,qBAAa,qBAAsB,SAAQ,KAAK;IAEnC,QAAQ,CAAC,KAAK,EAAE,MAAM;IADlC,SAAkB,IAAI,2BAA0B;gBAC3B,KAAK,EAAE,MAAM;CAGlC;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IAEtC,QAAQ,CAAC,GAAG,EAAE,MAAM;IADhC,SAAkB,IAAI,8BAA6B;gBAC9B,GAAG,EAAE,MAAM;CAGhC;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAStD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAgB1D;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAE3D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEjF;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,CAAA;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACtE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAuBzD;AAED,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CActE;AA4CD,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,WAAW,GAAG,WAAW,CAe7E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACxB,KAAK,EAAE,aAAa,EACpB,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,WAAW,EACzC,GAAG,GAAE,IAAiB,GACpB,WAAW,CAgBb;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAKhE"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job definitions: `schedule/jobs/<job-id>.json`.
|
|
3
|
+
*
|
|
4
|
+
* A job changes only by an operator action — the CLI, the TUI, a confirmed
|
|
5
|
+
* tool call — or by the daemon's own lifecycle transitions (completed,
|
|
6
|
+
* expired, paused after failures, held after an edit it did not make). Every
|
|
7
|
+
* write is a compare-and-set on `revision`: the writer first publishes a
|
|
8
|
+
* revision marker `jobs/.revisions/<job-id>/<n>` with `link`, and only the one
|
|
9
|
+
* writer that published marker `n` writes revision `n`. Two writers that read
|
|
10
|
+
* the same revision cannot both win; the loser gets
|
|
11
|
+
* {@link ScheduleConflictError} and re-reads.
|
|
12
|
+
*
|
|
13
|
+
* A hand edit of the JSON skips all of this. That is not prevented — the file
|
|
14
|
+
* is the operator's — but it is caught: the job's confirmation digest no
|
|
15
|
+
* longer matches, and the daemon holds the job until someone confirms it.
|
|
16
|
+
*/
|
|
17
|
+
import { createHash } from 'node:crypto';
|
|
18
|
+
import { readdirSync, rmSync, statSync, unlinkSync } from 'node:fs';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { ScheduleFormatError, ensureDir, publishExclusive, readVersioned, writeJsonAtomic, } from './atomic.js';
|
|
21
|
+
export const JOB_NAME = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
22
|
+
/** A revision marker older than this whose revision never landed was abandoned by a crashed writer. */
|
|
23
|
+
const ABANDONED_MARKER_MS = 60_000;
|
|
24
|
+
const KEEP_MARKERS = 8;
|
|
25
|
+
export class ScheduleConflictError extends Error {
|
|
26
|
+
jobId;
|
|
27
|
+
name = 'ScheduleConflictError';
|
|
28
|
+
constructor(jobId) {
|
|
29
|
+
super(`Job ${jobId} changed while it was being edited; read it again and retry.`);
|
|
30
|
+
this.jobId = jobId;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class ScheduleJobNotFoundError extends Error {
|
|
34
|
+
ref;
|
|
35
|
+
name = 'ScheduleJobNotFoundError';
|
|
36
|
+
constructor(ref) {
|
|
37
|
+
super(`No scheduled job is named or starts with "${ref}".`);
|
|
38
|
+
this.ref = ref;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** JSON with object keys sorted, so a digest does not depend on write order. */
|
|
42
|
+
export function stableStringify(value) {
|
|
43
|
+
if (Array.isArray(value))
|
|
44
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
45
|
+
if (value && typeof value === 'object') {
|
|
46
|
+
const entries = Object.entries(value)
|
|
47
|
+
.filter(([, v]) => v !== undefined)
|
|
48
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
49
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(',')}}`;
|
|
50
|
+
}
|
|
51
|
+
return JSON.stringify(value);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The digest of what a confirmation vouches for: the prompt, the folder and
|
|
55
|
+
* its trust, the permissions, the schedule, the model, the budget, the
|
|
56
|
+
* approval window and the pinned project config. Not a secret — a tripwire.
|
|
57
|
+
*/
|
|
58
|
+
export function jobSecurityDigest(job) {
|
|
59
|
+
return createHash('sha256')
|
|
60
|
+
.update(stableStringify({
|
|
61
|
+
prompt: job.prompt,
|
|
62
|
+
folder: job.folder.canonical,
|
|
63
|
+
trust: job.trust?.canonical ?? null,
|
|
64
|
+
permissions: job.permissions,
|
|
65
|
+
schedule: job.schedule,
|
|
66
|
+
model: job.model,
|
|
67
|
+
budget: job.budget,
|
|
68
|
+
approvalTtlMs: job.approvalTtlMs,
|
|
69
|
+
projectDigest: job.projectDigest,
|
|
70
|
+
}))
|
|
71
|
+
.digest('hex');
|
|
72
|
+
}
|
|
73
|
+
/** Whether a job's confirmation still vouches for what the file says. */
|
|
74
|
+
export function confirmationHolds(job) {
|
|
75
|
+
return job.confirmation !== null && job.confirmation.digest === jobSecurityDigest(job);
|
|
76
|
+
}
|
|
77
|
+
export function readJob(paths, id) {
|
|
78
|
+
return readVersioned(paths.job(id), 'schedule-job');
|
|
79
|
+
}
|
|
80
|
+
export function listJobs(paths) {
|
|
81
|
+
let names;
|
|
82
|
+
try {
|
|
83
|
+
names = readdirSync(paths.jobs);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { jobs: [], errors: [] };
|
|
87
|
+
}
|
|
88
|
+
const jobs = [];
|
|
89
|
+
const errors = [];
|
|
90
|
+
for (const name of names.sort()) {
|
|
91
|
+
if (!name.endsWith('.json') || name.startsWith('.'))
|
|
92
|
+
continue;
|
|
93
|
+
const path = join(paths.jobs, name);
|
|
94
|
+
try {
|
|
95
|
+
const job = readVersioned(path, 'schedule-job');
|
|
96
|
+
if (job)
|
|
97
|
+
jobs.push(job);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
errors.push({
|
|
101
|
+
path,
|
|
102
|
+
message: error instanceof ScheduleFormatError ? error.message : String(error),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { jobs: jobs.sort((a, b) => a.name.localeCompare(b.name)), errors };
|
|
107
|
+
}
|
|
108
|
+
/** A job by exact name or by an unambiguous id prefix (four characters at least). */
|
|
109
|
+
export function findJob(paths, ref) {
|
|
110
|
+
const { jobs } = listJobs(paths);
|
|
111
|
+
const byName = jobs.find((j) => j.name === ref);
|
|
112
|
+
if (byName)
|
|
113
|
+
return byName;
|
|
114
|
+
if (ref.length >= 4) {
|
|
115
|
+
const matches = jobs.filter((j) => j.id.startsWith(ref.toLowerCase()));
|
|
116
|
+
if (matches.length === 1)
|
|
117
|
+
return matches[0];
|
|
118
|
+
if (matches.length > 1) {
|
|
119
|
+
throw new Error(`"${ref}" matches ${matches.length} jobs (${matches.map((j) => j.name).join(', ')}); use the name.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
throw new ScheduleJobNotFoundError(ref);
|
|
123
|
+
}
|
|
124
|
+
function markerDir(paths, id) {
|
|
125
|
+
return join(paths.jobs, '.revisions', id);
|
|
126
|
+
}
|
|
127
|
+
/** Win revision `n` of a job, or learn that someone else did. */
|
|
128
|
+
function claimRevision(paths, id, current) {
|
|
129
|
+
const dir = markerDir(paths, id);
|
|
130
|
+
ensureDir(dir);
|
|
131
|
+
let target = current + 1;
|
|
132
|
+
for (let attempt = 0; attempt < 16; attempt++) {
|
|
133
|
+
if (publishExclusive(join(dir, String(target)), {
|
|
134
|
+
at: new Date().toISOString(),
|
|
135
|
+
pid: process.pid,
|
|
136
|
+
}))
|
|
137
|
+
return target;
|
|
138
|
+
// Somebody holds `target`. If it never landed and is old, its writer
|
|
139
|
+
// crashed between the marker and the document; step over it.
|
|
140
|
+
let age = 0;
|
|
141
|
+
try {
|
|
142
|
+
age = Date.now() - statSync(join(dir, String(target))).mtimeMs;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
age = 0;
|
|
146
|
+
}
|
|
147
|
+
const landed = (readJob(paths, id)?.revision ?? 0) >= target;
|
|
148
|
+
if (landed || age < ABANDONED_MARKER_MS)
|
|
149
|
+
throw new ScheduleConflictError(id);
|
|
150
|
+
target += 1;
|
|
151
|
+
}
|
|
152
|
+
throw new ScheduleConflictError(id);
|
|
153
|
+
}
|
|
154
|
+
function pruneMarkers(paths, id, latest) {
|
|
155
|
+
const dir = markerDir(paths, id);
|
|
156
|
+
try {
|
|
157
|
+
for (const name of readdirSync(dir)) {
|
|
158
|
+
const n = Number(name);
|
|
159
|
+
if (Number.isSafeInteger(n) && n < latest - KEEP_MARKERS)
|
|
160
|
+
unlinkSync(join(dir, name));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch { }
|
|
164
|
+
}
|
|
165
|
+
/** Write a new job. Refuses a name another job has. */
|
|
166
|
+
export function createJob(paths, job) {
|
|
167
|
+
if (!JOB_NAME.test(job.name)) {
|
|
168
|
+
throw new Error(`"${job.name}" is not a job name: lowercase letters, digits and dashes, starting with a letter or digit.`);
|
|
169
|
+
}
|
|
170
|
+
const { jobs } = listJobs(paths);
|
|
171
|
+
if (jobs.some((j) => j.name === job.name)) {
|
|
172
|
+
throw new Error(`A job named "${job.name}" already exists.`);
|
|
173
|
+
}
|
|
174
|
+
ensureDir(paths.jobs);
|
|
175
|
+
claimRevision(paths, job.id, 0);
|
|
176
|
+
const created = { ...job, revision: 1 };
|
|
177
|
+
writeJsonAtomic(paths.job(job.id), created);
|
|
178
|
+
return created;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Change a job, compare-and-set on the revision read. `mutate` returns the
|
|
182
|
+
* new body; revision and `updatedAt` are set here.
|
|
183
|
+
*/
|
|
184
|
+
export function updateJob(paths, id, expectedRevision, mutate, now = new Date()) {
|
|
185
|
+
const current = readJob(paths, id);
|
|
186
|
+
if (!current)
|
|
187
|
+
throw new ScheduleJobNotFoundError(id);
|
|
188
|
+
if (current.revision !== expectedRevision)
|
|
189
|
+
throw new ScheduleConflictError(id);
|
|
190
|
+
const revision = claimRevision(paths, id, current.revision);
|
|
191
|
+
const next = {
|
|
192
|
+
...mutate(current),
|
|
193
|
+
id: current.id,
|
|
194
|
+
v: 1,
|
|
195
|
+
kind: 'schedule-job',
|
|
196
|
+
revision,
|
|
197
|
+
updatedAt: now.toISOString(),
|
|
198
|
+
};
|
|
199
|
+
writeJsonAtomic(paths.job(id), next);
|
|
200
|
+
pruneMarkers(paths, id, revision);
|
|
201
|
+
return next;
|
|
202
|
+
}
|
|
203
|
+
/** Remove a job's definition, markers, state and claims. History is kept. */
|
|
204
|
+
export function deleteJob(paths, id) {
|
|
205
|
+
rmSync(paths.job(id), { force: true });
|
|
206
|
+
rmSync(markerDir(paths, id), { recursive: true, force: true });
|
|
207
|
+
rmSync(paths.stateOf(id), { force: true });
|
|
208
|
+
rmSync(paths.claimsOf(id), { recursive: true, force: true });
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../../src/schedule/store/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `schedule/state/<job-id>.json`: what the daemon remembers about a job
|
|
3
|
+
* between evaluations. The daemon is its only writer (one daemon owns a home
|
|
4
|
+
* at a time, by lease), so a plain replace is enough.
|
|
5
|
+
*/
|
|
6
|
+
import type { SchedulePaths } from '../paths.js';
|
|
7
|
+
import type { ScheduleJob, ScheduleJobState } from '../types.js';
|
|
8
|
+
export declare function emptyState(jobId: string): ScheduleJobState;
|
|
9
|
+
export declare function readState(paths: SchedulePaths, jobId: string): ScheduleJobState;
|
|
10
|
+
export declare function writeState(paths: SchedulePaths, state: ScheduleJobState): void;
|
|
11
|
+
/**
|
|
12
|
+
* When an active job fires next: what the scheduler last computed, or — before
|
|
13
|
+
* it has looked at the job — the spec's own next time. Undefined for a job
|
|
14
|
+
* that is not active.
|
|
15
|
+
*/
|
|
16
|
+
export declare function nextFireOf(job: Pick<ScheduleJob, 'state' | 'schedule'>, state: Pick<ScheduleJobState, 'nextFireAt'>, now?: Date): string | undefined;
|
|
17
|
+
/** Drop optional keys whose value is undefined, so a spread can remove a field. */
|
|
18
|
+
export declare function withoutUndefined<T extends object>(value: T): T;
|
|
19
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/schedule/store/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGhE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAO1D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAI/E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,UAAU,CAAC,EAC5C,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,EAC3C,GAAG,OAAa,GACd,MAAM,GAAG,SAAS,CAIpB;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAE9D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `schedule/state/<job-id>.json`: what the daemon remembers about a job
|
|
3
|
+
* between evaluations. The daemon is its only writer (one daemon owns a home
|
|
4
|
+
* at a time, by lease), so a plain replace is enough.
|
|
5
|
+
*/
|
|
6
|
+
import { nextFireTime } from '@namzu/sdk';
|
|
7
|
+
import { readVersioned, writeJsonAtomic } from './atomic.js';
|
|
8
|
+
export function emptyState(jobId) {
|
|
9
|
+
return {
|
|
10
|
+
v: 1,
|
|
11
|
+
kind: 'schedule-state',
|
|
12
|
+
jobId,
|
|
13
|
+
counters: { runs: 0, failures: 0, failureStreak: 0 },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function readState(paths, jobId) {
|
|
17
|
+
return (readVersioned(paths.stateOf(jobId), 'schedule-state') ?? emptyState(jobId));
|
|
18
|
+
}
|
|
19
|
+
export function writeState(paths, state) {
|
|
20
|
+
writeJsonAtomic(paths.stateOf(state.jobId), state);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* When an active job fires next: what the scheduler last computed, or — before
|
|
24
|
+
* it has looked at the job — the spec's own next time. Undefined for a job
|
|
25
|
+
* that is not active.
|
|
26
|
+
*/
|
|
27
|
+
export function nextFireOf(job, state, now = new Date()) {
|
|
28
|
+
if (job.state !== 'active')
|
|
29
|
+
return undefined;
|
|
30
|
+
if (state.nextFireAt && Date.parse(state.nextFireAt) > now.getTime())
|
|
31
|
+
return state.nextFireAt;
|
|
32
|
+
return nextFireTime(job.schedule, now)?.toISOString();
|
|
33
|
+
}
|
|
34
|
+
/** Drop optional keys whose value is undefined, so a spread can remove a field. */
|
|
35
|
+
export function withoutUndefined(value) {
|
|
36
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== undefined));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/schedule/store/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGzC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE5D,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,OAAO;QACN,CAAC,EAAE,CAAC;QACJ,IAAI,EAAE,gBAAgB;QACtB,KAAK;QACL,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE;KACpD,CAAA;AACF,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAoB,EAAE,KAAa;IAC5D,OAAO,CACN,aAAa,CAAmB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAC5F,CAAA;AACF,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAoB,EAAE,KAAuB;IACvE,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACzB,GAA4C,EAC5C,KAA2C,EAC3C,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC5C,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE;QAAE,OAAO,KAAK,CAAC,UAAU,CAAA;IAC7F,OAAO,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,CAAA;AACtD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,gBAAgB,CAAmB,KAAQ;IAC1D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAM,CAAA;AACzF,CAAC"}
|