@the-open-engine/zeroshot 5.4.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.
Files changed (295) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/cli/commands/inspect-render.js +135 -0
  5. package/cli/commands/inspect.js +294 -0
  6. package/cli/commands/providers.js +149 -0
  7. package/cli/index.js +5431 -0
  8. package/cli/lib/first-run.js +211 -0
  9. package/cli/lib/update-checker.js +281 -0
  10. package/cli/message-formatter-utils.js +75 -0
  11. package/cli/message-formatters-normal.js +275 -0
  12. package/cli/message-formatters-watch.js +185 -0
  13. package/cluster-templates/base-templates/debug-workflow.json +422 -0
  14. package/cluster-templates/base-templates/full-workflow.json +727 -0
  15. package/cluster-templates/base-templates/heavy-validation.json +272 -0
  16. package/cluster-templates/base-templates/quick-validation.json +285 -0
  17. package/cluster-templates/base-templates/single-worker.json +71 -0
  18. package/cluster-templates/base-templates/worker-validator.json +230 -0
  19. package/cluster-templates/conductor-bootstrap.json +130 -0
  20. package/docker/zeroshot-cluster/Dockerfile +153 -0
  21. package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
  22. package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
  23. package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
  24. package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
  25. package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
  26. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
  27. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
  28. package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
  29. package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
  30. package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
  31. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
  32. package/lib/agent-cli-provider/adapters/claude.js +181 -0
  33. package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
  34. package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
  35. package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
  36. package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
  37. package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
  38. package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
  39. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
  40. package/lib/agent-cli-provider/adapters/codex.js +151 -0
  41. package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
  42. package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
  43. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
  44. package/lib/agent-cli-provider/adapters/common.js +71 -0
  45. package/lib/agent-cli-provider/adapters/common.js.map +1 -0
  46. package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
  47. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
  48. package/lib/agent-cli-provider/adapters/gemini.js +176 -0
  49. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
  50. package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
  51. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
  52. package/lib/agent-cli-provider/adapters/index.js +95 -0
  53. package/lib/agent-cli-provider/adapters/index.js.map +1 -0
  54. package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
  55. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
  56. package/lib/agent-cli-provider/adapters/opencode.js +228 -0
  57. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
  58. package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
  59. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
  60. package/lib/agent-cli-provider/contract-actions.js +140 -0
  61. package/lib/agent-cli-provider/contract-actions.js.map +1 -0
  62. package/lib/agent-cli-provider/contract-env.d.ts +11 -0
  63. package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/contract-env.js +113 -0
  65. package/lib/agent-cli-provider/contract-env.js.map +1 -0
  66. package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
  67. package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/contract-envelope.js +52 -0
  69. package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
  70. package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
  71. package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/contract-errors.js +71 -0
  73. package/lib/agent-cli-provider/contract-errors.js.map +1 -0
  74. package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
  75. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
  76. package/lib/agent-cli-provider/contract-fallback.js +102 -0
  77. package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
  78. package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
  79. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
  80. package/lib/agent-cli-provider/contract-invoke.js +89 -0
  81. package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
  82. package/lib/agent-cli-provider/contract-options.d.ts +3 -0
  83. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/contract-options.js +140 -0
  85. package/lib/agent-cli-provider/contract-options.js.map +1 -0
  86. package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
  87. package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
  88. package/lib/agent-cli-provider/contract-parse.js +81 -0
  89. package/lib/agent-cli-provider/contract-parse.js.map +1 -0
  90. package/lib/agent-cli-provider/contract-support.d.ts +18 -0
  91. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
  92. package/lib/agent-cli-provider/contract-support.js +154 -0
  93. package/lib/agent-cli-provider/contract-support.js.map +1 -0
  94. package/lib/agent-cli-provider/contract.d.ts +13 -0
  95. package/lib/agent-cli-provider/contract.d.ts.map +1 -0
  96. package/lib/agent-cli-provider/contract.js +30 -0
  97. package/lib/agent-cli-provider/contract.js.map +1 -0
  98. package/lib/agent-cli-provider/env-safety.d.ts +11 -0
  99. package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
  100. package/lib/agent-cli-provider/env-safety.js +83 -0
  101. package/lib/agent-cli-provider/env-safety.js.map +1 -0
  102. package/lib/agent-cli-provider/errors.d.ts +5 -0
  103. package/lib/agent-cli-provider/errors.d.ts.map +1 -0
  104. package/lib/agent-cli-provider/errors.js +115 -0
  105. package/lib/agent-cli-provider/errors.js.map +1 -0
  106. package/lib/agent-cli-provider/executable.d.ts +3 -0
  107. package/lib/agent-cli-provider/executable.d.ts.map +1 -0
  108. package/lib/agent-cli-provider/executable.js +24 -0
  109. package/lib/agent-cli-provider/executable.js.map +1 -0
  110. package/lib/agent-cli-provider/index.d.ts +8 -0
  111. package/lib/agent-cli-provider/index.d.ts.map +1 -0
  112. package/lib/agent-cli-provider/index.js +31 -0
  113. package/lib/agent-cli-provider/index.js.map +1 -0
  114. package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
  115. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
  116. package/lib/agent-cli-provider/invoke-evidence.js +17 -0
  117. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
  118. package/lib/agent-cli-provider/json.d.ts +16 -0
  119. package/lib/agent-cli-provider/json.d.ts.map +1 -0
  120. package/lib/agent-cli-provider/json.js +110 -0
  121. package/lib/agent-cli-provider/json.js.map +1 -0
  122. package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
  123. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
  124. package/lib/agent-cli-provider/log-prefix.js +22 -0
  125. package/lib/agent-cli-provider/log-prefix.js.map +1 -0
  126. package/lib/agent-cli-provider/process-runner.d.ts +17 -0
  127. package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
  128. package/lib/agent-cli-provider/process-runner.js +89 -0
  129. package/lib/agent-cli-provider/process-runner.js.map +1 -0
  130. package/lib/agent-cli-provider/redaction.d.ts +9 -0
  131. package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
  132. package/lib/agent-cli-provider/redaction.js +227 -0
  133. package/lib/agent-cli-provider/redaction.js.map +1 -0
  134. package/lib/agent-cli-provider/schema.d.ts +5 -0
  135. package/lib/agent-cli-provider/schema.d.ts.map +1 -0
  136. package/lib/agent-cli-provider/schema.js +136 -0
  137. package/lib/agent-cli-provider/schema.js.map +1 -0
  138. package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
  139. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
  140. package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
  141. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
  142. package/lib/agent-cli-provider/types.d.ts +194 -0
  143. package/lib/agent-cli-provider/types.d.ts.map +1 -0
  144. package/lib/agent-cli-provider/types.js +12 -0
  145. package/lib/agent-cli-provider/types.js.map +1 -0
  146. package/lib/completion.js +174 -0
  147. package/lib/detached-startup.js +220 -0
  148. package/lib/docker-config.js +220 -0
  149. package/lib/git-remote-utils.js +165 -0
  150. package/lib/id-detector.js +55 -0
  151. package/lib/provider-defaults.js +62 -0
  152. package/lib/provider-detection.js +59 -0
  153. package/lib/provider-names.js +57 -0
  154. package/lib/repo-settings.js +69 -0
  155. package/lib/settings/claude-auth.js +78 -0
  156. package/lib/settings.js +542 -0
  157. package/lib/start-cluster.js +321 -0
  158. package/lib/stream-json-parser.js +67 -0
  159. package/package.json +162 -0
  160. package/scripts/fix-node-pty-permissions.js +75 -0
  161. package/scripts/record-demo.sh +279 -0
  162. package/scripts/setup-merge-queue.sh +170 -0
  163. package/scripts/test-install.sh +40 -0
  164. package/scripts/validate-templates.js +107 -0
  165. package/src/agent/agent-config.js +266 -0
  166. package/src/agent/agent-context-builder.js +189 -0
  167. package/src/agent/agent-context-sections.js +338 -0
  168. package/src/agent/agent-context-sources.js +147 -0
  169. package/src/agent/agent-hook-executor.js +721 -0
  170. package/src/agent/agent-input-injector.js +141 -0
  171. package/src/agent/agent-lifecycle.js +982 -0
  172. package/src/agent/agent-quality-gate-schema.js +93 -0
  173. package/src/agent/agent-quality-gates-context.js +51 -0
  174. package/src/agent/agent-stuck-detector.js +256 -0
  175. package/src/agent/agent-task-executor.js +2028 -0
  176. package/src/agent/agent-trigger-evaluator.js +67 -0
  177. package/src/agent/context-metrics.js +160 -0
  178. package/src/agent/context-pack-builder.js +367 -0
  179. package/src/agent/context-replay-policy.js +51 -0
  180. package/src/agent/guidance-queue.js +77 -0
  181. package/src/agent/output-extraction.js +367 -0
  182. package/src/agent/output-reformatter.js +175 -0
  183. package/src/agent/pr-verification.js +653 -0
  184. package/src/agent/rate-limit-backoff.js +82 -0
  185. package/src/agent/schema-utils.js +146 -0
  186. package/src/agent/validation-platform.js +35 -0
  187. package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
  188. package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
  189. package/src/agent-cli-provider/adapters/claude.ts +247 -0
  190. package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
  191. package/src/agent-cli-provider/adapters/codex.ts +217 -0
  192. package/src/agent-cli-provider/adapters/common.ts +124 -0
  193. package/src/agent-cli-provider/adapters/gemini.ts +243 -0
  194. package/src/agent-cli-provider/adapters/index.ts +126 -0
  195. package/src/agent-cli-provider/adapters/opencode.ts +286 -0
  196. package/src/agent-cli-provider/contract-actions.ts +150 -0
  197. package/src/agent-cli-provider/contract-env.ts +111 -0
  198. package/src/agent-cli-provider/contract-envelope.ts +110 -0
  199. package/src/agent-cli-provider/contract-errors.ts +66 -0
  200. package/src/agent-cli-provider/contract-fallback.ts +121 -0
  201. package/src/agent-cli-provider/contract-invoke.ts +104 -0
  202. package/src/agent-cli-provider/contract-options.ts +173 -0
  203. package/src/agent-cli-provider/contract-parse.ts +94 -0
  204. package/src/agent-cli-provider/contract-support.ts +167 -0
  205. package/src/agent-cli-provider/contract.ts +56 -0
  206. package/src/agent-cli-provider/env-safety.ts +82 -0
  207. package/src/agent-cli-provider/errors.ts +122 -0
  208. package/src/agent-cli-provider/executable.ts +24 -0
  209. package/src/agent-cli-provider/index.ts +83 -0
  210. package/src/agent-cli-provider/invoke-evidence.ts +18 -0
  211. package/src/agent-cli-provider/json.ts +114 -0
  212. package/src/agent-cli-provider/log-prefix.ts +20 -0
  213. package/src/agent-cli-provider/process-runner.ts +145 -0
  214. package/src/agent-cli-provider/redaction.ts +282 -0
  215. package/src/agent-cli-provider/schema.ts +115 -0
  216. package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
  217. package/src/agent-cli-provider/types.ts +237 -0
  218. package/src/agent-wrapper.js +615 -0
  219. package/src/agents/git-pusher-template.js +705 -0
  220. package/src/attach/attach-client.js +438 -0
  221. package/src/attach/attach-server.js +543 -0
  222. package/src/attach/index.js +37 -0
  223. package/src/attach/protocol.js +220 -0
  224. package/src/attach/ring-buffer.js +121 -0
  225. package/src/attach/send-input.js +88 -0
  226. package/src/attach/socket-discovery.js +267 -0
  227. package/src/claude-task-runner.js +661 -0
  228. package/src/config-router.js +89 -0
  229. package/src/config-validator.js +2202 -0
  230. package/src/copy-worker.js +43 -0
  231. package/src/guidance-topics.js +10 -0
  232. package/src/input-helpers.js +65 -0
  233. package/src/isolation-manager.js +1734 -0
  234. package/src/issue-providers/README.md +305 -0
  235. package/src/issue-providers/azure-devops-provider.js +307 -0
  236. package/src/issue-providers/base-provider.js +232 -0
  237. package/src/issue-providers/github-provider.js +210 -0
  238. package/src/issue-providers/gitlab-provider.js +262 -0
  239. package/src/issue-providers/index.js +196 -0
  240. package/src/issue-providers/jira-provider.js +260 -0
  241. package/src/ledger.js +692 -0
  242. package/src/lib/gc.js +232 -0
  243. package/src/lib/safe-exec.js +88 -0
  244. package/src/logic-engine.js +201 -0
  245. package/src/message-buffer.js +81 -0
  246. package/src/message-bus-bridge.js +144 -0
  247. package/src/message-bus.js +256 -0
  248. package/src/name-generator.js +232 -0
  249. package/src/orchestrator.js +3924 -0
  250. package/src/preflight.js +712 -0
  251. package/src/process-metrics.js +608 -0
  252. package/src/providers/anthropic/index.js +3 -0
  253. package/src/providers/base-provider.js +355 -0
  254. package/src/providers/capabilities.js +60 -0
  255. package/src/providers/google/index.js +3 -0
  256. package/src/providers/index.js +293 -0
  257. package/src/providers/openai/index.js +3 -0
  258. package/src/providers/opencode/index.js +3 -0
  259. package/src/quality-gates.js +143 -0
  260. package/src/schemas/sub-cluster.js +208 -0
  261. package/src/state-snapshot.js +398 -0
  262. package/src/state-snapshotter.js +142 -0
  263. package/src/status-footer.js +1026 -0
  264. package/src/sub-cluster-wrapper.js +693 -0
  265. package/src/task-runner.js +30 -0
  266. package/src/template-resolver.js +425 -0
  267. package/src/template-validation/index.js +338 -0
  268. package/src/template-validation/simulate-consensus-gates.js +324 -0
  269. package/src/template-validation/simulate-random-topology.js +541 -0
  270. package/src/template-validation/simulate-two-stage-validation.js +270 -0
  271. package/src/worktree-claude-config.js +135 -0
  272. package/src/worktree-tooling-env.js +150 -0
  273. package/task-lib/attachable-watcher.js +381 -0
  274. package/task-lib/commands/clean.js +50 -0
  275. package/task-lib/commands/episodes.js +105 -0
  276. package/task-lib/commands/get-log-path.js +23 -0
  277. package/task-lib/commands/kill.js +32 -0
  278. package/task-lib/commands/list.js +105 -0
  279. package/task-lib/commands/logs.js +439 -0
  280. package/task-lib/commands/resume.js +42 -0
  281. package/task-lib/commands/run.js +57 -0
  282. package/task-lib/commands/schedule.js +105 -0
  283. package/task-lib/commands/scheduler-cmd.js +96 -0
  284. package/task-lib/commands/schedules.js +148 -0
  285. package/task-lib/commands/status.js +44 -0
  286. package/task-lib/commands/unschedule.js +16 -0
  287. package/task-lib/completion.js +9 -0
  288. package/task-lib/config.js +11 -0
  289. package/task-lib/name-generator.js +230 -0
  290. package/task-lib/package.json +3 -0
  291. package/task-lib/provider-helper-runtime.js +29 -0
  292. package/task-lib/runner.js +190 -0
  293. package/task-lib/scheduler.js +252 -0
  294. package/task-lib/store.js +529 -0
  295. package/task-lib/watcher.js +305 -0
@@ -0,0 +1,190 @@
1
+ import { fork } from 'child_process';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { LOGS_DIR } from './config.js';
5
+ import { addTask, generateId, ensureDirs } from './store.js';
6
+ import { createRequire } from 'module';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const { prepareSingleAgentProviderCommand } = require('./provider-helper-runtime.js');
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+
13
+ export function spawnTask(prompt, options = {}) {
14
+ ensureDirs();
15
+
16
+ const id = generateId();
17
+ const logFile = join(LOGS_DIR, `${id}.log`);
18
+ const cwd = options.cwd || process.cwd();
19
+
20
+ const outputFormat = resolveOutputFormat(options);
21
+ const jsonSchema = resolveJsonSchema(options, outputFormat);
22
+ const prepared = prepareSingleAgentProviderCommand({
23
+ provider: options.provider || null,
24
+ context: prompt,
25
+ options: buildProviderOptions(options, outputFormat, jsonSchema, cwd),
26
+ });
27
+ const providerName = prepared.adapter.id;
28
+ const modelSpec = prepared.options.modelSpec;
29
+ const commandSpec = prepared.commandSpec;
30
+
31
+ const task = buildTaskRecord({
32
+ id,
33
+ prompt,
34
+ cwd,
35
+ options,
36
+ logFile,
37
+ providerName,
38
+ modelSpec,
39
+ });
40
+
41
+ addTask(task);
42
+
43
+ const watcherConfig = buildWatcherConfig(
44
+ outputFormat,
45
+ jsonSchema,
46
+ options,
47
+ providerName,
48
+ commandSpec
49
+ );
50
+ const watcherScript = resolveWatcherScript(options);
51
+ spawnWatcher({
52
+ watcherScript,
53
+ id,
54
+ cwd,
55
+ logFile,
56
+ finalArgs: commandSpec.args,
57
+ watcherConfig,
58
+ });
59
+
60
+ return task;
61
+ }
62
+
63
+ function resolveOutputFormat(options) {
64
+ return options.outputFormat || 'stream-json';
65
+ }
66
+
67
+ function resolveJsonSchema(options, outputFormat) {
68
+ let jsonSchema = options.jsonSchema || null;
69
+ if (jsonSchema && outputFormat !== 'json') {
70
+ console.warn('Warning: --json-schema requires --output-format json, ignoring schema');
71
+ jsonSchema = null;
72
+ }
73
+ return jsonSchema;
74
+ }
75
+
76
+ function buildProviderOptions(options, outputFormat, jsonSchema, cwd) {
77
+ return {
78
+ outputFormat,
79
+ jsonSchema,
80
+ cwd,
81
+ autoApprove: true,
82
+ ...modelSpecOption(options),
83
+ ...(options.resume ? { resumeSessionId: options.resume } : {}),
84
+ ...(options.continue ? { continueSession: true } : {}),
85
+ };
86
+ }
87
+
88
+ function modelSpecOption(options) {
89
+ const modelSpec = resolveRequestedModelSpec(options);
90
+ return modelSpec === undefined ? {} : { modelSpec };
91
+ }
92
+
93
+ function resolveRequestedModelSpec(options) {
94
+ if (options.model) {
95
+ return {
96
+ model: options.model,
97
+ ...(options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}),
98
+ };
99
+ }
100
+
101
+ if (options.reasoningEffort) {
102
+ return {
103
+ ...(options.modelLevel ? { level: options.modelLevel } : {}),
104
+ reasoningEffort: options.reasoningEffort,
105
+ };
106
+ }
107
+ if (options.modelLevel) return { level: options.modelLevel };
108
+ return undefined;
109
+ }
110
+
111
+ function buildTaskRecord({ id, prompt, cwd, options, logFile, providerName, modelSpec }) {
112
+ return {
113
+ id,
114
+ prompt: prompt.slice(0, 200) + (prompt.length > 200 ? '...' : ''),
115
+ fullPrompt: prompt,
116
+ cwd,
117
+ status: 'running',
118
+ pid: null,
119
+ sessionId: options.resume || options.sessionId || null,
120
+ logFile,
121
+ createdAt: new Date().toISOString(),
122
+ updatedAt: new Date().toISOString(),
123
+ exitCode: null,
124
+ error: null,
125
+ provider: providerName,
126
+ model: modelSpec?.model || null,
127
+ // Schedule reference (if spawned by scheduler)
128
+ scheduleId: options.scheduleId || null,
129
+ // Attach support
130
+ socketPath: null,
131
+ attachable: false,
132
+ };
133
+ }
134
+
135
+ function buildWatcherConfig(outputFormat, jsonSchema, options, providerName, commandSpec) {
136
+ return {
137
+ outputFormat,
138
+ jsonSchema,
139
+ silentJsonOutput: options.silentJsonOutput || false,
140
+ provider: providerName,
141
+ command: commandSpec.binary,
142
+ env: commandSpec.env || {},
143
+ commandSpec: buildWatcherCommandSpec(commandSpec),
144
+ };
145
+ }
146
+
147
+ function buildWatcherCommandSpec(commandSpec) {
148
+ const watcherCommandSpec = { ...commandSpec };
149
+ delete watcherCommandSpec.args;
150
+ return watcherCommandSpec;
151
+ }
152
+
153
+ function resolveWatcherScript(options) {
154
+ const useAttachable = options.attachable !== false && !options.jsonSchema;
155
+ return useAttachable ? join(__dirname, 'attachable-watcher.js') : join(__dirname, 'watcher.js');
156
+ }
157
+
158
+ function spawnWatcher({ watcherScript, id, cwd, logFile, finalArgs, watcherConfig }) {
159
+ const watcher = fork(
160
+ watcherScript,
161
+ [id, cwd, logFile, JSON.stringify(finalArgs), JSON.stringify(watcherConfig)],
162
+ {
163
+ detached: true,
164
+ stdio: 'ignore',
165
+ }
166
+ );
167
+
168
+ watcher.unref();
169
+ watcher.disconnect(); // Close IPC channel so parent can exit
170
+ }
171
+
172
+ export function isProcessRunning(pid) {
173
+ if (!pid) return false;
174
+ try {
175
+ process.kill(pid, 0);
176
+ return true;
177
+ } catch {
178
+ return false;
179
+ }
180
+ }
181
+
182
+ export function killTask(pid) {
183
+ if (!pid) return false;
184
+ try {
185
+ process.kill(pid, 'SIGTERM');
186
+ return true;
187
+ } catch {
188
+ return false;
189
+ }
190
+ }
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Scheduler daemon - runs as background process
5
+ * Checks for due scheduled tasks and spawns them
6
+ */
7
+
8
+ import { readFileSync, writeFileSync, existsSync, appendFileSync, unlinkSync } from 'fs';
9
+ import { loadSchedules, updateSchedule } from './store.js';
10
+ import { spawnTask } from './runner.js';
11
+ import { SCHEDULER_PID_FILE, SCHEDULER_LOG } from './config.js';
12
+
13
+ const CHECK_INTERVAL = 60000; // 60 seconds
14
+
15
+ /**
16
+ * Parse human-readable interval to milliseconds
17
+ * Supports: 30s, 5m, 2h, 1d, 1w
18
+ */
19
+ export function parseInterval(str) {
20
+ const match = str.match(/^(\d+)([smhdw])$/i);
21
+ if (!match) return null;
22
+
23
+ const value = parseInt(match[1], 10);
24
+ const unit = match[2].toLowerCase();
25
+
26
+ const multipliers = {
27
+ s: 1000,
28
+ m: 60 * 1000,
29
+ h: 60 * 60 * 1000,
30
+ d: 24 * 60 * 60 * 1000,
31
+ w: 7 * 24 * 60 * 60 * 1000,
32
+ };
33
+
34
+ return value * multipliers[unit];
35
+ }
36
+
37
+ /**
38
+ * Parse cron expression and get next run time
39
+ * Simple cron parser supporting: minute hour day month weekday
40
+ */
41
+ export function getNextCronTime(cronExpr, fromDate = new Date()) {
42
+ const parts = cronExpr.trim().split(/\s+/);
43
+ if (parts.length !== 5) return null;
44
+
45
+ const [minute, hour] = parts;
46
+
47
+ // Simple implementation - just handle basic cases
48
+ // For full cron support, use cron-parser package
49
+ const next = new Date(fromDate);
50
+ next.setSeconds(0);
51
+ next.setMilliseconds(0);
52
+
53
+ // Handle simple cases
54
+ if (minute !== '*') {
55
+ const mins = minute.split(',').map((m) => parseInt(m, 10));
56
+ const currentMin = next.getMinutes();
57
+ const nextMin = mins.find((m) => m > currentMin) ?? mins[0];
58
+ if (nextMin <= currentMin) {
59
+ next.setHours(next.getHours() + 1);
60
+ }
61
+ next.setMinutes(nextMin);
62
+ }
63
+
64
+ if (hour !== '*') {
65
+ const hours = hour.split(',').map((h) => parseInt(h, 10));
66
+ const currentHour = next.getHours();
67
+ const nextHour = hours.find((h) => h > currentHour) ?? hours[0];
68
+ if (nextHour <= currentHour && minute === '*') {
69
+ next.setDate(next.getDate() + 1);
70
+ }
71
+ if (nextHour !== currentHour) {
72
+ next.setMinutes(minute === '*' ? 0 : parseInt(minute, 10));
73
+ }
74
+ next.setHours(nextHour);
75
+ }
76
+
77
+ // For complex cron expressions, fall back to 1 hour from now
78
+ // Full implementation would need cron-parser
79
+ if (next <= fromDate) {
80
+ next.setTime(fromDate.getTime() + 60 * 60 * 1000);
81
+ }
82
+
83
+ return next;
84
+ }
85
+
86
+ /**
87
+ * Calculate next run time for a schedule
88
+ */
89
+ export function calculateNextRun(schedule) {
90
+ if (schedule.interval) {
91
+ return new Date(Date.now() + schedule.interval);
92
+ }
93
+ if (schedule.cron) {
94
+ return getNextCronTime(schedule.cron);
95
+ }
96
+ return null;
97
+ }
98
+
99
+ /**
100
+ * Log to scheduler log file
101
+ */
102
+ function log(msg) {
103
+ const timestamp = new Date().toISOString();
104
+ const line = `[${timestamp}] ${msg}\n`;
105
+ appendFileSync(SCHEDULER_LOG, line);
106
+ }
107
+
108
+ /**
109
+ * Check and run due schedules
110
+ */
111
+ async function checkSchedules() {
112
+ const schedules = loadSchedules();
113
+ const now = new Date();
114
+
115
+ for (const schedule of Object.values(schedules)) {
116
+ if (!schedule.enabled) continue;
117
+
118
+ const nextRun = new Date(schedule.nextRunAt);
119
+ if (nextRun > now) continue;
120
+
121
+ // Schedule is due - spawn task
122
+ log(`Running scheduled task: ${schedule.id} - "${schedule.prompt.slice(0, 50)}..."`);
123
+
124
+ try {
125
+ const task = await spawnTask(schedule.prompt, {
126
+ cwd: schedule.cwd,
127
+ scheduleId: schedule.id,
128
+ });
129
+
130
+ // Update schedule with next run time
131
+ const nextRunAt = calculateNextRun(schedule);
132
+ updateSchedule(schedule.id, {
133
+ lastRunAt: now.toISOString(),
134
+ lastTaskId: task.id,
135
+ nextRunAt: nextRunAt ? nextRunAt.toISOString() : null,
136
+ });
137
+
138
+ log(
139
+ `Spawned task ${task.id} for schedule ${schedule.id}, next run: ${nextRunAt?.toISOString() || 'none'}`
140
+ );
141
+ } catch (err) {
142
+ log(`Error spawning task for schedule ${schedule.id}: ${err.message}`);
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Start the scheduler daemon
149
+ */
150
+ export function startDaemon() {
151
+ // Check if already running
152
+ if (existsSync(SCHEDULER_PID_FILE)) {
153
+ const existingPid = parseInt(readFileSync(SCHEDULER_PID_FILE, 'utf-8').trim(), 10);
154
+ try {
155
+ process.kill(existingPid, 0);
156
+ console.log(`Scheduler already running (PID: ${existingPid})`);
157
+ return false;
158
+ } catch {
159
+ // Process not running, clean up stale PID file
160
+ unlinkSync(SCHEDULER_PID_FILE);
161
+ }
162
+ }
163
+
164
+ // Write PID file
165
+ writeFileSync(SCHEDULER_PID_FILE, String(process.pid));
166
+
167
+ log(`Scheduler daemon started (PID: ${process.pid})`);
168
+ console.log(`Scheduler daemon started (PID: ${process.pid})`);
169
+
170
+ // Run check loop
171
+ const runLoop = async () => {
172
+ while (true) {
173
+ try {
174
+ await checkSchedules();
175
+ } catch (err) {
176
+ // Log error with full stack trace - scheduler errors are critical bugs
177
+ const errorMsg = `SCHEDULER ERROR: ${err.message}\nStack: ${err.stack}`;
178
+ log(errorMsg);
179
+ console.error(errorMsg);
180
+ }
181
+ await new Promise((r) => setTimeout(r, CHECK_INTERVAL));
182
+ }
183
+ };
184
+
185
+ // Handle shutdown
186
+ process.on('SIGTERM', () => {
187
+ log('Scheduler daemon stopping (SIGTERM)');
188
+ if (existsSync(SCHEDULER_PID_FILE)) {
189
+ unlinkSync(SCHEDULER_PID_FILE);
190
+ }
191
+ process.exit(0);
192
+ });
193
+
194
+ process.on('SIGINT', () => {
195
+ log('Scheduler daemon stopping (SIGINT)');
196
+ if (existsSync(SCHEDULER_PID_FILE)) {
197
+ unlinkSync(SCHEDULER_PID_FILE);
198
+ }
199
+ process.exit(0);
200
+ });
201
+
202
+ runLoop();
203
+ return true;
204
+ }
205
+
206
+ /**
207
+ * Stop the scheduler daemon
208
+ */
209
+ export function stopDaemon() {
210
+ if (!existsSync(SCHEDULER_PID_FILE)) {
211
+ console.log('Scheduler is not running');
212
+ return false;
213
+ }
214
+
215
+ const pid = parseInt(readFileSync(SCHEDULER_PID_FILE, 'utf-8').trim(), 10);
216
+
217
+ try {
218
+ process.kill(pid, 'SIGTERM');
219
+ unlinkSync(SCHEDULER_PID_FILE);
220
+ console.log(`Scheduler stopped (PID: ${pid})`);
221
+ log(`Scheduler daemon stopped by user (PID: ${pid})`);
222
+ return true;
223
+ } catch {
224
+ // Process not running, clean up
225
+ unlinkSync(SCHEDULER_PID_FILE);
226
+ console.log('Scheduler was not running (cleaned up stale PID file)');
227
+ return false;
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Get daemon status
233
+ */
234
+ export function getDaemonStatus() {
235
+ if (!existsSync(SCHEDULER_PID_FILE)) {
236
+ return { running: false, pid: null };
237
+ }
238
+
239
+ const pid = parseInt(readFileSync(SCHEDULER_PID_FILE, 'utf-8').trim(), 10);
240
+
241
+ try {
242
+ process.kill(pid, 0);
243
+ return { running: true, pid };
244
+ } catch {
245
+ return { running: false, pid: null, stale: true };
246
+ }
247
+ }
248
+
249
+ // If run directly, start daemon
250
+ if (process.argv[1]?.endsWith('scheduler.js')) {
251
+ startDaemon();
252
+ }