@neotx/cli 0.1.0-alpha.2 → 0.1.0-alpha.20

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 (60) hide show
  1. package/README.md +383 -0
  2. package/dist/activity-LWUVGQVN.js +86 -0
  3. package/dist/activity-LWUVGQVN.js.map +1 -0
  4. package/dist/{agents-Y6LREFXP.js → agents-PH3P7G7E.js} +2 -2
  5. package/dist/{chunk-CP54H7WA.js → chunk-3ZP3BQXB.js} +6 -11
  6. package/dist/chunk-3ZP3BQXB.js.map +1 -0
  7. package/dist/{chunk-TNJOG54I.js → chunk-F622JUDY.js} +6 -2
  8. package/dist/{chunk-TNJOG54I.js.map → chunk-F622JUDY.js.map} +1 -1
  9. package/dist/config-NYF6AJXU.js +282 -0
  10. package/dist/config-NYF6AJXU.js.map +1 -0
  11. package/dist/{cost-DNGKT4UC.js → cost-OQGFNBBG.js} +3 -8
  12. package/dist/cost-OQGFNBBG.js.map +1 -0
  13. package/dist/daemon/supervisor-worker.js +7 -1
  14. package/dist/daemon/supervisor-worker.js.map +1 -1
  15. package/dist/daemon/worker.js +27 -10
  16. package/dist/daemon/worker.js.map +1 -1
  17. package/dist/decision-PNZ2S2TU.js +362 -0
  18. package/dist/decision-PNZ2S2TU.js.map +1 -0
  19. package/dist/doctor-ZBO73UID.js +337 -0
  20. package/dist/doctor-ZBO73UID.js.map +1 -0
  21. package/dist/guide-UQRNA3FC.js +23 -0
  22. package/dist/guide-UQRNA3FC.js.map +1 -0
  23. package/dist/index.js +17 -9
  24. package/dist/index.js.map +1 -1
  25. package/dist/{init-YNSPTCA3.js → init-UYS6KS5U.js} +4 -20
  26. package/dist/init-UYS6KS5U.js.map +1 -0
  27. package/dist/log-PTHLI7ZN.js +141 -0
  28. package/dist/log-PTHLI7ZN.js.map +1 -0
  29. package/dist/{mcp-GH6CCW7A.js → mcp-XHZND5A4.js} +6 -1
  30. package/dist/mcp-XHZND5A4.js.map +1 -0
  31. package/dist/memory-6R22DFS7.js +292 -0
  32. package/dist/memory-6R22DFS7.js.map +1 -0
  33. package/dist/{run-KIU2ZE72.js → run-OF53USMD.js} +46 -20
  34. package/dist/run-OF53USMD.js.map +1 -0
  35. package/dist/{runs-CHA2JM5K.js → runs-TAASM3YF.js} +16 -12
  36. package/dist/runs-TAASM3YF.js.map +1 -0
  37. package/dist/status-LQOFOJJI.js +90 -0
  38. package/dist/status-LQOFOJJI.js.map +1 -0
  39. package/dist/{supervise-KIB2EYY4.js → supervise-FI6MYULH.js} +33 -28
  40. package/dist/supervise-FI6MYULH.js.map +1 -0
  41. package/dist/supervisor-3RUX5SPH.js +16 -0
  42. package/dist/supervisor-3RUX5SPH.js.map +1 -0
  43. package/dist/{tui-QS3RPHKH.js → tui-JJ6CD2YW.js} +377 -43
  44. package/dist/tui-JJ6CD2YW.js.map +1 -0
  45. package/dist/version-XVOAMGDD.js +26 -0
  46. package/dist/version-XVOAMGDD.js.map +1 -0
  47. package/dist/webhooks-PUKAHFHE.js +151 -0
  48. package/dist/webhooks-PUKAHFHE.js.map +1 -0
  49. package/package.json +22 -4
  50. package/dist/chunk-CP54H7WA.js.map +0 -1
  51. package/dist/cost-DNGKT4UC.js.map +0 -1
  52. package/dist/doctor-GC4NH7H6.js +0 -173
  53. package/dist/doctor-GC4NH7H6.js.map +0 -1
  54. package/dist/init-YNSPTCA3.js.map +0 -1
  55. package/dist/mcp-GH6CCW7A.js.map +0 -1
  56. package/dist/run-KIU2ZE72.js.map +0 -1
  57. package/dist/runs-CHA2JM5K.js.map +0 -1
  58. package/dist/supervise-KIB2EYY4.js.map +0 -1
  59. package/dist/tui-QS3RPHKH.js.map +0 -1
  60. /package/dist/{agents-Y6LREFXP.js.map → agents-PH3P7G7E.js.map} +0 -0
@@ -0,0 +1,282 @@
1
+ import {
2
+ printError,
3
+ printJson,
4
+ printSuccess
5
+ } from "./chunk-YQIWMDXL.js";
6
+
7
+ // src/commands/config.ts
8
+ import { existsSync } from "fs";
9
+ import { mkdir, readFile, writeFile } from "fs/promises";
10
+ import { join } from "path";
11
+ import { ConfigStore, getDataDir, neoConfigSchema, repoOverrideConfigSchema } from "@neotx/core";
12
+ import { defineCommand } from "citty";
13
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
14
+ function getGlobalConfigPath() {
15
+ return join(getDataDir(), "config.yml");
16
+ }
17
+ function getRepoConfigPath(repoPath) {
18
+ return join(repoPath, ".neo", "config.yml");
19
+ }
20
+ function findRepoRoot() {
21
+ const cwd = process.cwd();
22
+ if (existsSync(join(cwd, ".neo", "config.yml"))) {
23
+ return cwd;
24
+ }
25
+ if (existsSync(join(cwd, ".git"))) {
26
+ return cwd;
27
+ }
28
+ return void 0;
29
+ }
30
+ function getByPath(obj, path) {
31
+ if (path === "") return obj;
32
+ const keys = path.split(".");
33
+ let current = obj;
34
+ for (const key of keys) {
35
+ if (current === null || current === void 0) return void 0;
36
+ if (typeof current !== "object") return void 0;
37
+ current = current[key];
38
+ }
39
+ return current;
40
+ }
41
+ function setByPath(obj, path, value) {
42
+ if (path === "") return value;
43
+ const keys = path.split(".");
44
+ const result = { ...obj };
45
+ let current = result;
46
+ for (let i = 0; i < keys.length - 1; i++) {
47
+ const key = keys[i];
48
+ if (key === void 0) continue;
49
+ if (current[key] === void 0 || current[key] === null || typeof current[key] !== "object") {
50
+ current[key] = {};
51
+ } else {
52
+ current[key] = { ...current[key] };
53
+ }
54
+ current = current[key];
55
+ }
56
+ const lastKey = keys[keys.length - 1];
57
+ if (lastKey !== void 0) {
58
+ current[lastKey] = value;
59
+ }
60
+ return result;
61
+ }
62
+ function unsetByPath(obj, path) {
63
+ if (path === "") return {};
64
+ const keys = path.split(".");
65
+ const result = { ...obj };
66
+ if (keys.length === 1) {
67
+ const firstKey = keys[0];
68
+ if (firstKey !== void 0) {
69
+ delete result[firstKey];
70
+ }
71
+ return result;
72
+ }
73
+ let current = result;
74
+ for (let i = 0; i < keys.length - 1; i++) {
75
+ const key = keys[i];
76
+ if (key === void 0) continue;
77
+ if (current[key] === void 0 || typeof current[key] !== "object") {
78
+ return result;
79
+ }
80
+ current[key] = { ...current[key] };
81
+ current = current[key];
82
+ }
83
+ const lastKey = keys[keys.length - 1];
84
+ if (lastKey !== void 0) {
85
+ delete current[lastKey];
86
+ }
87
+ return result;
88
+ }
89
+ async function loadConfigFile(filePath) {
90
+ if (!existsSync(filePath)) return null;
91
+ try {
92
+ const content = await readFile(filePath, "utf-8");
93
+ const parsed = parseYaml(content);
94
+ if (parsed === null || typeof parsed !== "object") return null;
95
+ return parsed;
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+ async function saveConfigFile(filePath, config) {
101
+ const dir = join(filePath, "..");
102
+ await mkdir(dir, { recursive: true });
103
+ await writeFile(filePath, stringifyYaml(config), "utf-8");
104
+ }
105
+ async function handleGet(key) {
106
+ const repoPath = findRepoRoot();
107
+ const store = new ConfigStore(repoPath);
108
+ await store.load();
109
+ try {
110
+ const value = store.get(key);
111
+ if (value === void 0) {
112
+ printError(`Key not found: ${key}`);
113
+ process.exitCode = 1;
114
+ return;
115
+ }
116
+ if (typeof value === "object" && value !== null) {
117
+ printJson(value);
118
+ } else {
119
+ console.log(String(value));
120
+ }
121
+ } catch {
122
+ printError(`Key not found: ${key}`);
123
+ process.exitCode = 1;
124
+ }
125
+ }
126
+ async function handleList(format) {
127
+ const repoPath = findRepoRoot();
128
+ const store = new ConfigStore(repoPath);
129
+ await store.load();
130
+ const config = store.getAll();
131
+ if (format === "json") {
132
+ printJson(config);
133
+ } else {
134
+ console.log(stringifyYaml(config));
135
+ }
136
+ }
137
+ async function handleSet(key, value, global) {
138
+ const configPath = global ? getGlobalConfigPath() : getRepoConfigPath(process.cwd());
139
+ if (!global && !findRepoRoot()) {
140
+ printError("Not in a repository. Use --global to set global config.");
141
+ process.exitCode = 1;
142
+ return;
143
+ }
144
+ let config = await loadConfigFile(configPath) ?? {};
145
+ let parsedValue;
146
+ try {
147
+ parsedValue = JSON.parse(value);
148
+ } catch {
149
+ if (value === "true") {
150
+ parsedValue = true;
151
+ } else if (value === "false") {
152
+ parsedValue = false;
153
+ } else if (!Number.isNaN(Number(value)) && value.trim() !== "") {
154
+ parsedValue = Number(value);
155
+ } else {
156
+ parsedValue = value;
157
+ }
158
+ }
159
+ config = setByPath(config, key, parsedValue);
160
+ const schema = global ? neoConfigSchema : repoOverrideConfigSchema;
161
+ const validation = schema.safeParse(config);
162
+ if (!validation.success) {
163
+ const issues = validation.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
164
+ printError(`Invalid config value:
165
+ ${issues}`);
166
+ process.exitCode = 1;
167
+ return;
168
+ }
169
+ await saveConfigFile(configPath, config);
170
+ printSuccess(`Set ${key} = ${JSON.stringify(parsedValue)}`);
171
+ }
172
+ async function handleUnset(key, global) {
173
+ const configPath = global ? getGlobalConfigPath() : getRepoConfigPath(process.cwd());
174
+ if (!global && !findRepoRoot()) {
175
+ printError("Not in a repository. Use --global to unset global config.");
176
+ process.exitCode = 1;
177
+ return;
178
+ }
179
+ const config = await loadConfigFile(configPath);
180
+ if (!config) {
181
+ printError(`Config file not found: ${configPath}`);
182
+ process.exitCode = 1;
183
+ return;
184
+ }
185
+ if (getByPath(config, key) === void 0) {
186
+ printError(`Key not found: ${key}`);
187
+ process.exitCode = 1;
188
+ return;
189
+ }
190
+ const updatedConfig = unsetByPath(config, key);
191
+ await saveConfigFile(configPath, updatedConfig);
192
+ printSuccess(`Unset ${key}`);
193
+ }
194
+ function handlePath() {
195
+ const globalPath = getGlobalConfigPath();
196
+ const globalExists = existsSync(globalPath);
197
+ console.log(`Global: ${globalPath}${globalExists ? "" : " (not found)"}`);
198
+ const repoRoot = findRepoRoot();
199
+ if (repoRoot) {
200
+ const repoPath = getRepoConfigPath(repoRoot);
201
+ const repoExists = existsSync(repoPath);
202
+ console.log(`Repo: ${repoPath}${repoExists ? "" : " (not found)"}`);
203
+ } else {
204
+ console.log("Repo: (not in a repository)");
205
+ }
206
+ }
207
+ var config_default = defineCommand({
208
+ meta: {
209
+ name: "config",
210
+ description: "Manage neo configuration"
211
+ },
212
+ args: {
213
+ action: {
214
+ type: "positional",
215
+ description: "Action: get, list, set, unset, path",
216
+ required: false
217
+ },
218
+ key: {
219
+ type: "positional",
220
+ description: "Config key (dot notation, e.g., budget.dailyCapUsd)",
221
+ required: false
222
+ },
223
+ value: {
224
+ type: "positional",
225
+ description: "Value to set",
226
+ required: false
227
+ },
228
+ global: {
229
+ type: "boolean",
230
+ description: "Use global config (~/.neo/config.yml)",
231
+ default: false,
232
+ alias: "g"
233
+ },
234
+ format: {
235
+ type: "string",
236
+ description: "Output format: yaml, json (for list)",
237
+ default: "yaml",
238
+ alias: "f"
239
+ }
240
+ },
241
+ async run({ args }) {
242
+ const action = args.action ?? "list";
243
+ const key = args.key;
244
+ const value = args.value;
245
+ const global = args.global;
246
+ const format = args.format;
247
+ switch (action) {
248
+ case "get":
249
+ if (!key) {
250
+ printError("Usage: neo config get <key>");
251
+ process.exitCode = 1;
252
+ return;
253
+ }
254
+ return handleGet(key);
255
+ case "list":
256
+ return handleList(format);
257
+ case "set":
258
+ if (!key || value === void 0) {
259
+ printError("Usage: neo config set <key> <value> [--global]");
260
+ process.exitCode = 1;
261
+ return;
262
+ }
263
+ return handleSet(key, value, global);
264
+ case "unset":
265
+ if (!key) {
266
+ printError("Usage: neo config unset <key> [--global]");
267
+ process.exitCode = 1;
268
+ return;
269
+ }
270
+ return handleUnset(key, global);
271
+ case "path":
272
+ return handlePath();
273
+ default:
274
+ printError(`Unknown action: ${action}. Use: get, list, set, unset, path`);
275
+ process.exitCode = 1;
276
+ }
277
+ }
278
+ });
279
+ export {
280
+ config_default as default
281
+ };
282
+ //# sourceMappingURL=config-NYF6AJXU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/config.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { ConfigStore, getDataDir, neoConfigSchema, repoOverrideConfigSchema } from \"@neotx/core\";\nimport { defineCommand } from \"citty\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { printError, printJson, printSuccess } from \"../output.js\";\n\n// ─── Path helpers ───────────────────────────────────────────\n\nfunction getGlobalConfigPath(): string {\n return join(getDataDir(), \"config.yml\");\n}\n\nfunction getRepoConfigPath(repoPath: string): string {\n return join(repoPath, \".neo\", \"config.yml\");\n}\n\nfunction findRepoRoot(): string | undefined {\n // Look for .neo/config.yml or .git in current directory\n const cwd = process.cwd();\n if (existsSync(join(cwd, \".neo\", \"config.yml\"))) {\n return cwd;\n }\n if (existsSync(join(cwd, \".git\"))) {\n return cwd;\n }\n return undefined;\n}\n\n// ─── Dot-notation helpers ───────────────────────────────────\n\nfunction getByPath(obj: unknown, path: string): unknown {\n if (path === \"\") return obj;\n\n const keys = path.split(\".\");\n let current = obj;\n\n for (const key of keys) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[key];\n }\n\n return current;\n}\n\nfunction setByPath(\n obj: Record<string, unknown>,\n path: string,\n value: unknown,\n): Record<string, unknown> {\n if (path === \"\") return value as Record<string, unknown>;\n\n const keys = path.split(\".\");\n const result = { ...obj };\n let current: Record<string, unknown> = result;\n\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (key === undefined) continue;\n if (current[key] === undefined || current[key] === null || typeof current[key] !== \"object\") {\n current[key] = {};\n } else {\n current[key] = { ...(current[key] as Record<string, unknown>) };\n }\n current = current[key] as Record<string, unknown>;\n }\n\n const lastKey = keys[keys.length - 1];\n if (lastKey !== undefined) {\n current[lastKey] = value;\n }\n\n return result;\n}\n\nfunction unsetByPath(obj: Record<string, unknown>, path: string): Record<string, unknown> {\n if (path === \"\") return {};\n\n const keys = path.split(\".\");\n const result = { ...obj };\n\n if (keys.length === 1) {\n const firstKey = keys[0];\n if (firstKey !== undefined) {\n delete result[firstKey];\n }\n return result;\n }\n\n // Navigate to parent and delete the last key\n let current: Record<string, unknown> = result;\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (key === undefined) continue;\n if (current[key] === undefined || typeof current[key] !== \"object\") {\n return result; // Path doesn't exist\n }\n current[key] = { ...(current[key] as Record<string, unknown>) };\n current = current[key] as Record<string, unknown>;\n }\n\n const lastKey = keys[keys.length - 1];\n if (lastKey !== undefined) {\n delete current[lastKey];\n }\n return result;\n}\n\n// ─── Config file I/O ────────────────────────────────────────\n\nasync function loadConfigFile(filePath: string): Promise<Record<string, unknown> | null> {\n if (!existsSync(filePath)) return null;\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = parseYaml(content);\n if (parsed === null || typeof parsed !== \"object\") return null;\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nasync function saveConfigFile(filePath: string, config: Record<string, unknown>): Promise<void> {\n const dir = join(filePath, \"..\");\n await mkdir(dir, { recursive: true });\n await writeFile(filePath, stringifyYaml(config), \"utf-8\");\n}\n\n// ─── Handlers ───────────────────────────────────────────────\n\nasync function handleGet(key: string): Promise<void> {\n const repoPath = findRepoRoot();\n const store = new ConfigStore(repoPath);\n await store.load();\n\n try {\n const value = store.get(key);\n\n if (value === undefined) {\n printError(`Key not found: ${key}`);\n process.exitCode = 1;\n return;\n }\n\n // Output: JSON for objects/arrays, plain for primitives\n if (typeof value === \"object\" && value !== null) {\n printJson(value);\n } else {\n console.log(String(value));\n }\n } catch {\n printError(`Key not found: ${key}`);\n process.exitCode = 1;\n }\n}\n\nasync function handleList(format: string): Promise<void> {\n const repoPath = findRepoRoot();\n const store = new ConfigStore(repoPath);\n await store.load();\n\n const config = store.getAll();\n\n if (format === \"json\") {\n printJson(config);\n } else {\n // Default to YAML\n console.log(stringifyYaml(config));\n }\n}\n\nasync function handleSet(key: string, value: string, global: boolean): Promise<void> {\n const configPath = global ? getGlobalConfigPath() : getRepoConfigPath(process.cwd());\n\n if (!global && !findRepoRoot()) {\n printError(\"Not in a repository. Use --global to set global config.\");\n process.exitCode = 1;\n return;\n }\n\n // Load existing config or start with empty object\n let config = (await loadConfigFile(configPath)) ?? {};\n\n // Parse value - try JSON first, fall back to string\n let parsedValue: unknown;\n try {\n parsedValue = JSON.parse(value);\n } catch {\n // Check for boolean strings\n if (value === \"true\") {\n parsedValue = true;\n } else if (value === \"false\") {\n parsedValue = false;\n } else if (!Number.isNaN(Number(value)) && value.trim() !== \"\") {\n parsedValue = Number(value);\n } else {\n parsedValue = value;\n }\n }\n\n // Update config with new value\n config = setByPath(config, key, parsedValue);\n\n // Validate against schema before saving\n const schema = global ? neoConfigSchema : repoOverrideConfigSchema;\n const validation = schema.safeParse(config);\n\n if (!validation.success) {\n const issues = validation.error.issues\n .map((i) => ` - ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\");\n printError(`Invalid config value:\\n${issues}`);\n process.exitCode = 1;\n return;\n }\n\n await saveConfigFile(configPath, config);\n printSuccess(`Set ${key} = ${JSON.stringify(parsedValue)}`);\n}\n\nasync function handleUnset(key: string, global: boolean): Promise<void> {\n const configPath = global ? getGlobalConfigPath() : getRepoConfigPath(process.cwd());\n\n if (!global && !findRepoRoot()) {\n printError(\"Not in a repository. Use --global to unset global config.\");\n process.exitCode = 1;\n return;\n }\n\n const config = await loadConfigFile(configPath);\n\n if (!config) {\n printError(`Config file not found: ${configPath}`);\n process.exitCode = 1;\n return;\n }\n\n // Check if key exists\n if (getByPath(config, key) === undefined) {\n printError(`Key not found: ${key}`);\n process.exitCode = 1;\n return;\n }\n\n const updatedConfig = unsetByPath(config, key);\n await saveConfigFile(configPath, updatedConfig);\n printSuccess(`Unset ${key}`);\n}\n\nfunction handlePath(): void {\n const globalPath = getGlobalConfigPath();\n const globalExists = existsSync(globalPath);\n\n console.log(`Global: ${globalPath}${globalExists ? \"\" : \" (not found)\"}`);\n\n const repoRoot = findRepoRoot();\n if (repoRoot) {\n const repoPath = getRepoConfigPath(repoRoot);\n const repoExists = existsSync(repoPath);\n console.log(`Repo: ${repoPath}${repoExists ? \"\" : \" (not found)\"}`);\n } else {\n console.log(\"Repo: (not in a repository)\");\n }\n}\n\n// ─── Command definition ─────────────────────────────────────\n\nexport default defineCommand({\n meta: {\n name: \"config\",\n description: \"Manage neo configuration\",\n },\n args: {\n action: {\n type: \"positional\",\n description: \"Action: get, list, set, unset, path\",\n required: false,\n },\n key: {\n type: \"positional\",\n description: \"Config key (dot notation, e.g., budget.dailyCapUsd)\",\n required: false,\n },\n value: {\n type: \"positional\",\n description: \"Value to set\",\n required: false,\n },\n global: {\n type: \"boolean\",\n description: \"Use global config (~/.neo/config.yml)\",\n default: false,\n alias: \"g\",\n },\n format: {\n type: \"string\",\n description: \"Output format: yaml, json (for list)\",\n default: \"yaml\",\n alias: \"f\",\n },\n },\n async run({ args }) {\n const action = (args.action as string | undefined) ?? \"list\";\n const key = args.key as string | undefined;\n const value = args.value as string | undefined;\n const global = args.global as boolean;\n const format = args.format as string;\n\n switch (action) {\n case \"get\":\n if (!key) {\n printError(\"Usage: neo config get <key>\");\n process.exitCode = 1;\n return;\n }\n return handleGet(key);\n\n case \"list\":\n return handleList(format);\n\n case \"set\":\n if (!key || value === undefined) {\n printError(\"Usage: neo config set <key> <value> [--global]\");\n process.exitCode = 1;\n return;\n }\n return handleSet(key, value, global);\n\n case \"unset\":\n if (!key) {\n printError(\"Usage: neo config unset <key> [--global]\");\n process.exitCode = 1;\n return;\n }\n return handleUnset(key, global);\n\n case \"path\":\n return handlePath();\n\n default:\n printError(`Unknown action: ${action}. Use: get, list, set, unset, path`);\n process.exitCode = 1;\n }\n },\n});\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveRepoFilter
3
- } from "./chunk-CP54H7WA.js";
3
+ } from "./chunk-3ZP3BQXB.js";
4
4
  import {
5
5
  printError,
6
6
  printJson,
@@ -38,11 +38,6 @@ var cost_default = defineCommand({
38
38
  description: "Show cost breakdown from journals (today, by agent, by run)"
39
39
  },
40
40
  args: {
41
- all: {
42
- type: "boolean",
43
- description: "Show costs from all repos",
44
- default: false
45
- },
46
41
  repo: {
47
42
  type: "string",
48
43
  description: "Filter by repo name or path"
@@ -66,7 +61,7 @@ var cost_default = defineCommand({
66
61
  process.exitCode = 1;
67
62
  return;
68
63
  }
69
- const filter = await resolveRepoFilter({ all: args.all, repo: args.repo });
64
+ const filter = await resolveRepoFilter({ repo: args.repo });
70
65
  if (filter.mode !== "all") {
71
66
  const slug = filter.repoSlug;
72
67
  entries = entries.filter((e) => {
@@ -131,4 +126,4 @@ var cost_default = defineCommand({
131
126
  export {
132
127
  cost_default as default
133
128
  };
134
- //# sourceMappingURL=cost-DNGKT4UC.js.map
129
+ //# sourceMappingURL=cost-OQGFNBBG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/cost.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { CostEntry } from \"@neotx/core\";\nimport { getJournalsDir, toRepoSlug } from \"@neotx/core\";\nimport { defineCommand } from \"citty\";\nimport { printError, printJson, printTable } from \"../output.js\";\nimport { resolveRepoFilter } from \"../repo-filter.js\";\n\nasync function readCostEntries(journalDir: string): Promise<CostEntry[]> {\n if (!existsSync(journalDir)) return [];\n const files = await readdir(journalDir);\n const costFiles = files\n .filter((f) => f.startsWith(\"cost-\"))\n .sort()\n .reverse();\n const entries: CostEntry[] = [];\n\n for (const file of costFiles) {\n const content = await readFile(path.join(journalDir, file), \"utf-8\");\n for (const line of content.trim().split(\"\\n\")) {\n if (!line.trim()) continue;\n entries.push(JSON.parse(line) as CostEntry);\n }\n }\n\n return entries;\n}\n\nfunction isToday(timestamp: string): boolean {\n const d = new Date(timestamp);\n const now = new Date();\n return (\n d.getUTCFullYear() === now.getUTCFullYear() &&\n d.getUTCMonth() === now.getUTCMonth() &&\n d.getUTCDate() === now.getUTCDate()\n );\n}\n\nexport default defineCommand({\n meta: {\n name: \"cost\",\n description: \"Show cost breakdown from journals (today, by agent, by run)\",\n },\n args: {\n repo: {\n type: \"string\",\n description: \"Filter by repo name or path\",\n },\n short: {\n type: \"boolean\",\n description: \"Compact output for supervisor agents (saves tokens)\",\n default: false,\n },\n output: {\n type: \"string\",\n description: \"Output format: json\",\n },\n },\n async run({ args }) {\n const jsonOutput = args.output === \"json\";\n const journalDir = getJournalsDir();\n let entries = await readCostEntries(journalDir);\n\n if (entries.length === 0) {\n printError(\"No cost data found.\");\n process.exitCode = 1;\n return;\n }\n\n // Filter by repo if specified\n const filter = await resolveRepoFilter({ repo: args.repo });\n if (filter.mode !== \"all\") {\n const slug = filter.repoSlug;\n entries = entries.filter((e) => {\n if (!e.repo) return false;\n return toRepoSlug({ path: e.repo }) === slug;\n });\n }\n\n const todayEntries = entries.filter((e) => isToday(e.timestamp));\n const todayTotal = todayEntries.reduce((sum, e) => sum + e.costUsd, 0);\n const allTimeTotal = entries.reduce((sum, e) => sum + e.costUsd, 0);\n\n // Breakdown by agent (today)\n const byAgent = new Map<string, { cost: number; runs: number }>();\n for (const e of todayEntries) {\n const prev = byAgent.get(e.agent) ?? { cost: 0, runs: 0 };\n byAgent.set(e.agent, { cost: prev.cost + e.costUsd, runs: prev.runs + 1 });\n }\n\n // Breakdown by repo (today, only in --all mode)\n const byRepo = new Map<string, { cost: number; runs: number }>();\n if (filter.mode === \"all\") {\n for (const e of todayEntries) {\n const repo = e.repo ?? \"unknown\";\n const prev = byRepo.get(repo) ?? { cost: 0, runs: 0 };\n byRepo.set(repo, { cost: prev.cost + e.costUsd, runs: prev.runs + 1 });\n }\n }\n\n if (jsonOutput) {\n printJson({\n today: {\n total: todayTotal,\n sessions: todayEntries.length,\n byAgent: Object.fromEntries(byAgent),\n ...(byRepo.size > 0 ? { byRepo: Object.fromEntries(byRepo) } : {}),\n },\n allTime: {\n total: allTimeTotal,\n sessions: entries.length,\n },\n });\n return;\n }\n\n if (args.short) {\n const agents = [...byAgent.entries()]\n .map(([name, data]) => `${name}=$${data.cost.toFixed(4)}`)\n .join(\" \");\n console.log(`today=$${todayTotal.toFixed(4)} sessions=${todayEntries.length} ${agents}`);\n return;\n }\n\n console.log(`Today: $${todayTotal.toFixed(4)} (${todayEntries.length} sessions)`);\n console.log(`All time: $${allTimeTotal.toFixed(4)} (${entries.length} sessions)`);\n\n if (byAgent.size > 0) {\n console.log(\"\");\n printTable(\n [\"AGENT\", \"COST TODAY\", \"SESSIONS\"],\n [...byAgent.entries()]\n .sort((a, b) => b[1].cost - a[1].cost)\n .map(([name, data]) => [name, `$${data.cost.toFixed(4)}`, String(data.runs)]),\n );\n }\n\n if (byRepo.size > 0) {\n console.log(\"\");\n printTable(\n [\"REPO\", \"COST TODAY\", \"SESSIONS\"],\n [...byRepo.entries()]\n .sort((a, b) => b[1].cost - a[1].cost)\n .map(([repo, data]) => [repo, `$${data.cost.toFixed(4)}`, String(data.runs)]),\n );\n }\n },\n});\n"],"mappings":"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","names":[]}
@@ -1,6 +1,11 @@
1
+ import {
2
+ resolveAgentsPackageDir
3
+ } from "../chunk-F622JUDY.js";
4
+
1
5
  // src/daemon/supervisor-worker.ts
2
6
  import { createWriteStream } from "fs";
3
7
  import { mkdir } from "fs/promises";
8
+ import path from "path";
4
9
  import { getSupervisorDir, loadGlobalConfig, SupervisorDaemon } from "@neotx/core";
5
10
  async function main() {
6
11
  const name = process.argv[2];
@@ -16,7 +21,8 @@ async function main() {
16
21
  process.stderr.write = logStream.write.bind(logStream);
17
22
  try {
18
23
  const config = await loadGlobalConfig();
19
- const daemon = new SupervisorDaemon({ name, config });
24
+ const defaultInstructionsPath = path.join(resolveAgentsPackageDir(), "SUPERVISOR.md");
25
+ const daemon = new SupervisorDaemon({ name, config, defaultInstructionsPath });
20
26
  await daemon.start();
21
27
  } catch (error) {
22
28
  const msg = error instanceof Error ? error.message : String(error);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/daemon/supervisor-worker.ts"],"sourcesContent":["/**\n * Detached worker process for the supervisor daemon.\n *\n * Launched via child_process.fork() from the supervise command.\n * Runs the SupervisorDaemon which starts the heartbeat loop,\n * webhook server, and event queue.\n *\n * Usage: node supervisor-worker.js <name>\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { getSupervisorDir, loadGlobalConfig, SupervisorDaemon } from \"@neotx/core\";\n\nasync function main(): Promise<void> {\n const name = process.argv[2];\n if (!name) {\n process.stderr.write(\"Usage: supervisor-worker.js <name>\\n\");\n process.exit(1);\n }\n\n // Redirect stdout/stderr to a log file\n const dir = getSupervisorDir(name);\n await mkdir(dir, { recursive: true });\n const logPath = `${dir}/daemon.log`;\n const logStream = createWriteStream(logPath, { flags: \"a\" });\n process.stdout.write = logStream.write.bind(logStream);\n process.stderr.write = logStream.write.bind(logStream);\n\n try {\n const config = await loadGlobalConfig();\n const daemon = new SupervisorDaemon({ name, config });\n await daemon.start();\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[supervisor-worker] Fatal: ${msg}`);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":";AAUA,SAAS,yBAAyB;AAClC,SAAS,aAAa;AACtB,SAAS,kBAAkB,kBAAkB,wBAAwB;AAErE,eAAe,OAAsB;AACnC,QAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,MAAI,CAAC,MAAM;AACT,YAAQ,OAAO,MAAM,sCAAsC;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,MAAM,iBAAiB,IAAI;AACjC,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAU,GAAG,GAAG;AACtB,QAAM,YAAY,kBAAkB,SAAS,EAAE,OAAO,IAAI,CAAC;AAC3D,UAAQ,OAAO,QAAQ,UAAU,MAAM,KAAK,SAAS;AACrD,UAAQ,OAAO,QAAQ,UAAU,MAAM,KAAK,SAAS;AAErD,MAAI;AACF,UAAM,SAAS,MAAM,iBAAiB;AACtC,UAAM,SAAS,IAAI,iBAAiB,EAAE,MAAM,OAAO,CAAC;AACpD,UAAM,OAAO,MAAM;AAAA,EACrB,SAAS,OAAO;AACd,UAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,YAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;","names":[]}
1
+ {"version":3,"sources":["../../src/daemon/supervisor-worker.ts"],"sourcesContent":["/**\n * Detached worker process for the supervisor daemon.\n *\n * Launched via child_process.fork() from the supervise command.\n * Runs the SupervisorDaemon which starts the heartbeat loop,\n * webhook server, and event queue.\n *\n * Usage: node supervisor-worker.js <name>\n */\n\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getSupervisorDir, loadGlobalConfig, SupervisorDaemon } from \"@neotx/core\";\nimport { resolveAgentsPackageDir } from \"../resolve.js\";\n\nasync function main(): Promise<void> {\n const name = process.argv[2];\n if (!name) {\n process.stderr.write(\"Usage: supervisor-worker.js <name>\\n\");\n process.exit(1);\n }\n\n // Redirect stdout/stderr to a log file\n const dir = getSupervisorDir(name);\n await mkdir(dir, { recursive: true });\n const logPath = `${dir}/daemon.log`;\n const logStream = createWriteStream(logPath, { flags: \"a\" });\n process.stdout.write = logStream.write.bind(logStream);\n process.stderr.write = logStream.write.bind(logStream);\n\n try {\n const config = await loadGlobalConfig();\n const defaultInstructionsPath = path.join(resolveAgentsPackageDir(), \"SUPERVISOR.md\");\n const daemon = new SupervisorDaemon({ name, config, defaultInstructionsPath });\n await daemon.start();\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[supervisor-worker] Fatal: ${msg}`);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":";;;;;AAUA,SAAS,yBAAyB;AAClC,SAAS,aAAa;AACtB,OAAO,UAAU;AACjB,SAAS,kBAAkB,kBAAkB,wBAAwB;AAGrE,eAAe,OAAsB;AACnC,QAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,MAAI,CAAC,MAAM;AACT,YAAQ,OAAO,MAAM,sCAAsC;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,MAAM,iBAAiB,IAAI;AACjC,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAU,GAAG,GAAG;AACtB,QAAM,YAAY,kBAAkB,SAAS,EAAE,OAAO,IAAI,CAAC;AAC3D,UAAQ,OAAO,QAAQ,UAAU,MAAM,KAAK,SAAS;AACrD,UAAQ,OAAO,QAAQ,UAAU,MAAM,KAAK,SAAS;AAErD,MAAI;AACF,UAAM,SAAS,MAAM,iBAAiB;AACtC,UAAM,0BAA0B,KAAK,KAAK,wBAAwB,GAAG,eAAe;AACpF,UAAM,SAAS,IAAI,iBAAiB,EAAE,MAAM,QAAQ,wBAAwB,CAAC;AAC7E,UAAM,OAAO,MAAM;AAAA,EACrB,SAAS,OAAO;AACd,UAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,YAAQ,MAAM,8BAA8B,GAAG,EAAE;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,KAAK;","names":[]}
@@ -19,8 +19,29 @@ async function main() {
19
19
  const logPath = getRunLogPath(repoSlug, runId);
20
20
  await mkdir(path.dirname(logPath), { recursive: true });
21
21
  const logStream = createWriteStream(logPath, { flags: "a" });
22
+ function writeLog(msg) {
23
+ logStream.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
24
+ `);
25
+ }
22
26
  process.stdout.write = logStream.write.bind(logStream);
23
27
  process.stderr.write = logStream.write.bind(logStream);
28
+ process.on("uncaughtException", (err) => {
29
+ writeLog(`[worker] UNCAUGHT EXCEPTION: ${err.message}
30
+ ${err.stack}`);
31
+ logStream.end();
32
+ process.exit(1);
33
+ });
34
+ process.on("unhandledRejection", (reason) => {
35
+ writeLog(`[worker] UNHANDLED REJECTION: ${String(reason)}`);
36
+ });
37
+ for (const sig of ["SIGTERM", "SIGINT", "SIGHUP"]) {
38
+ process.on(sig, () => {
39
+ writeLog(`[worker] Received ${sig}, exiting`);
40
+ logStream.end();
41
+ process.exit(1);
42
+ });
43
+ }
44
+ writeLog(`[worker] Starting run ${runId} (PID ${process.pid})`);
24
45
  const dispatchPath = getRunDispatchPath(repoSlug, runId);
25
46
  const runPath = path.join(getRepoRunsDir(repoSlug), `${runId}.json`);
26
47
  try {
@@ -28,6 +49,7 @@ async function main() {
28
49
  const request = JSON.parse(raw);
29
50
  await unlink(dispatchPath).catch(() => {
30
51
  });
52
+ writeLog(`[worker] Dispatch loaded: agent=${request.agentName} repo=${request.repo}`);
31
53
  const config = await loadGlobalConfig();
32
54
  const agentRegistry = new AgentRegistry(
33
55
  request.bundledAgentsDir,
@@ -38,28 +60,23 @@ async function main() {
38
60
  if (!agent) {
39
61
  throw new Error(`Agent "${request.agentName}" not found`);
40
62
  }
41
- const orchestrator = new Orchestrator(config);
63
+ const orchestrator = new Orchestrator(config, { skipOrphanRecovery: true });
42
64
  orchestrator.registerAgent(agent);
43
- orchestrator.registerWorkflow({
44
- name: `_run_${request.agentName}`,
45
- description: `Detached dispatch to ${request.agentName}`,
46
- steps: {
47
- run: { agent: request.agentName }
48
- }
49
- });
50
65
  await updatePersistedRun(runPath, { pid: process.pid });
51
66
  const safetyTimeout = setTimeout(() => {
52
67
  console.error("[worker] Safety timeout reached, forcing exit");
53
68
  process.exit(1);
54
69
  }, config.sessions.maxDurationMs + 6e4);
55
70
  safetyTimeout.unref();
71
+ writeLog("[worker] Starting orchestrator...");
56
72
  await orchestrator.start();
57
- await updatePersistedRun(runPath, { status: "running", pid: process.pid });
73
+ writeLog("[worker] Dispatching...");
58
74
  const result = await orchestrator.dispatch({
59
75
  runId,
60
- workflow: `_run_${request.agentName}`,
76
+ agent: request.agentName,
61
77
  repo: request.repo,
62
78
  prompt: request.prompt,
79
+ ...request.branch ? { branch: request.branch } : {},
63
80
  priority: request.priority ?? "medium",
64
81
  metadata: request.metadata
65
82
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/daemon/worker.ts"],"sourcesContent":["/**\n * Detached worker process for `neo run -d`.\n *\n * Launched via child_process.fork() from the run command.\n * Reads dispatch parameters from a .dispatch.json file, runs the orchestrator,\n * and persists results. Stdout/stderr are redirected to a log file.\n *\n * Usage: node worker.js <runId> <repoSlug>\n */\n\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { PersistedRun } from \"@neotx/core\";\nimport {\n AgentRegistry,\n getRepoRunsDir,\n getRunDispatchPath,\n getRunLogPath,\n loadGlobalConfig,\n Orchestrator,\n} from \"@neotx/core\";\n\ninterface DispatchRequest {\n agentName: string;\n repo: string;\n prompt: string;\n priority?: \"critical\" | \"high\" | \"medium\" | \"low\";\n metadata?: Record<string, unknown>;\n bundledAgentsDir: string;\n customAgentsDir?: string;\n}\n\nasync function main(): Promise<void> {\n const [runId, repoSlug] = process.argv.slice(2);\n if (!runId || !repoSlug) {\n process.stderr.write(\"Usage: worker.js <runId> <repoSlug>\\n\");\n process.exit(1);\n }\n\n // Redirect stdout/stderr to log file\n const logPath = getRunLogPath(repoSlug, runId);\n await mkdir(path.dirname(logPath), { recursive: true });\n const logStream = createWriteStream(logPath, { flags: \"a\" });\n process.stdout.write = logStream.write.bind(logStream);\n process.stderr.write = logStream.write.bind(logStream);\n\n const dispatchPath = getRunDispatchPath(repoSlug, runId);\n const runPath = path.join(getRepoRunsDir(repoSlug), `${runId}.json`);\n\n try {\n // Read dispatch request\n const raw = await readFile(dispatchPath, \"utf-8\");\n const request = JSON.parse(raw) as DispatchRequest;\n\n // Clean up dispatch file\n await unlink(dispatchPath).catch(() => {});\n\n // Load config and agents\n const config = await loadGlobalConfig();\n const agentRegistry = new AgentRegistry(\n request.bundledAgentsDir,\n request.customAgentsDir && existsSync(request.customAgentsDir)\n ? request.customAgentsDir\n : undefined,\n );\n await agentRegistry.load();\n\n const agent = agentRegistry.get(request.agentName);\n if (!agent) {\n throw new Error(`Agent \"${request.agentName}\" not found`);\n }\n\n // Create orchestrator\n const orchestrator = new Orchestrator(config);\n orchestrator.registerAgent(agent);\n orchestrator.registerWorkflow({\n name: `_run_${request.agentName}`,\n description: `Detached dispatch to ${request.agentName}`,\n steps: {\n run: { agent: request.agentName },\n },\n });\n\n // Update persisted run with PID\n await updatePersistedRun(runPath, { pid: process.pid });\n\n // Safety timeout — ensure the process eventually exits\n const safetyTimeout = setTimeout(() => {\n console.error(\"[worker] Safety timeout reached, forcing exit\");\n process.exit(1);\n }, config.sessions.maxDurationMs + 60_000);\n safetyTimeout.unref();\n\n await orchestrator.start();\n\n // Re-assert running status — orchestrator.start() calls recoverOrphanedRuns()\n // which marks any \"running\" persisted runs as \"failed\"\n await updatePersistedRun(runPath, { status: \"running\", pid: process.pid });\n\n const result = await orchestrator.dispatch({\n runId,\n workflow: `_run_${request.agentName}`,\n repo: request.repo,\n prompt: request.prompt,\n priority: request.priority ?? \"medium\",\n metadata: request.metadata,\n });\n\n await orchestrator.shutdown();\n\n console.log(`[worker] Run ${runId} completed: ${result.status}`);\n console.log(`[worker] Cost: $${result.costUsd.toFixed(4)}`);\n if (result.branch) {\n console.log(`[worker] Branch: ${result.branch}`);\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n console.error(`[worker] Run ${runId} failed: ${errorMsg}`);\n\n // Update persisted run to failed status\n await updatePersistedRun(runPath, {\n status: \"failed\",\n updatedAt: new Date().toISOString(),\n }).catch(() => {});\n } finally {\n logStream.end();\n process.exit(0);\n }\n}\n\nasync function updatePersistedRun(runPath: string, updates: Partial<PersistedRun>): Promise<void> {\n try {\n const raw = await readFile(runPath, \"utf-8\");\n const run = JSON.parse(raw) as PersistedRun;\n Object.assign(run, updates);\n await writeFile(runPath, JSON.stringify(run, null, 2), \"utf-8\");\n } catch {\n // Non-critical\n }\n}\n\nmain();\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/daemon/worker.ts"],"sourcesContent":["/**\n * Detached worker process for `neo run -d`.\n *\n * Launched via child_process.fork() from the run command.\n * Reads dispatch parameters from a .dispatch.json file, runs the orchestrator,\n * and persists results. Stdout/stderr are redirected to a log file.\n *\n * Usage: node worker.js <runId> <repoSlug>\n */\n\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { PersistedRun } from \"@neotx/core\";\nimport {\n AgentRegistry,\n getRepoRunsDir,\n getRunDispatchPath,\n getRunLogPath,\n loadGlobalConfig,\n Orchestrator,\n} from \"@neotx/core\";\n\ninterface DispatchRequest {\n agentName: string;\n repo: string;\n prompt: string;\n branch?: string;\n priority?: \"critical\" | \"high\" | \"medium\" | \"low\";\n metadata?: Record<string, unknown>;\n bundledAgentsDir: string;\n customAgentsDir?: string;\n}\n\nasync function main(): Promise<void> {\n const [runId, repoSlug] = process.argv.slice(2);\n if (!runId || !repoSlug) {\n process.stderr.write(\"Usage: worker.js <runId> <repoSlug>\\n\");\n process.exit(1);\n }\n\n // Redirect stdout/stderr to log file\n const logPath = getRunLogPath(repoSlug, runId);\n await mkdir(path.dirname(logPath), { recursive: true });\n const logStream = createWriteStream(logPath, { flags: \"a\" });\n\n function writeLog(msg: string): void {\n logStream.write(`${new Date().toISOString()} ${msg}\\n`);\n }\n\n process.stdout.write = logStream.write.bind(logStream);\n process.stderr.write = logStream.write.bind(logStream);\n\n // Catch crashes and signals so we always leave a trace\n process.on(\"uncaughtException\", (err) => {\n writeLog(`[worker] UNCAUGHT EXCEPTION: ${err.message}\\n${err.stack}`);\n logStream.end();\n process.exit(1);\n });\n process.on(\"unhandledRejection\", (reason) => {\n writeLog(`[worker] UNHANDLED REJECTION: ${String(reason)}`);\n });\n for (const sig of [\"SIGTERM\", \"SIGINT\", \"SIGHUP\"] as const) {\n process.on(sig, () => {\n writeLog(`[worker] Received ${sig}, exiting`);\n logStream.end();\n process.exit(1);\n });\n }\n\n writeLog(`[worker] Starting run ${runId} (PID ${process.pid})`);\n\n const dispatchPath = getRunDispatchPath(repoSlug, runId);\n const runPath = path.join(getRepoRunsDir(repoSlug), `${runId}.json`);\n\n try {\n // Read dispatch request\n const raw = await readFile(dispatchPath, \"utf-8\");\n const request = JSON.parse(raw) as DispatchRequest;\n\n // Clean up dispatch file\n await unlink(dispatchPath).catch(() => {});\n writeLog(`[worker] Dispatch loaded: agent=${request.agentName} repo=${request.repo}`);\n\n // Load config and agents\n const config = await loadGlobalConfig();\n const agentRegistry = new AgentRegistry(\n request.bundledAgentsDir,\n request.customAgentsDir && existsSync(request.customAgentsDir)\n ? request.customAgentsDir\n : undefined,\n );\n await agentRegistry.load();\n\n const agent = agentRegistry.get(request.agentName);\n if (!agent) {\n throw new Error(`Agent \"${request.agentName}\" not found`);\n }\n\n // Create orchestrator — skip orphan recovery to prevent false positives on concurrent launches\n const orchestrator = new Orchestrator(config, { skipOrphanRecovery: true });\n orchestrator.registerAgent(agent);\n\n // Update persisted run with PID\n await updatePersistedRun(runPath, { pid: process.pid });\n\n // Safety timeout — ensure the process eventually exits\n const safetyTimeout = setTimeout(() => {\n console.error(\"[worker] Safety timeout reached, forcing exit\");\n process.exit(1);\n }, config.sessions.maxDurationMs + 60_000);\n safetyTimeout.unref();\n\n writeLog(\"[worker] Starting orchestrator...\");\n await orchestrator.start();\n\n writeLog(\"[worker] Dispatching...\");\n const result = await orchestrator.dispatch({\n runId,\n agent: request.agentName,\n repo: request.repo,\n prompt: request.prompt,\n ...(request.branch ? { branch: request.branch } : {}),\n priority: request.priority ?? \"medium\",\n metadata: request.metadata,\n });\n\n await orchestrator.shutdown();\n\n console.log(`[worker] Run ${runId} completed: ${result.status}`);\n console.log(`[worker] Cost: $${result.costUsd.toFixed(4)}`);\n if (result.branch) {\n console.log(`[worker] Branch: ${result.branch}`);\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n console.error(`[worker] Run ${runId} failed: ${errorMsg}`);\n\n // Update persisted run to failed status\n await updatePersistedRun(runPath, {\n status: \"failed\",\n updatedAt: new Date().toISOString(),\n }).catch(() => {});\n } finally {\n logStream.end();\n process.exit(0);\n }\n}\n\nasync function updatePersistedRun(runPath: string, updates: Partial<PersistedRun>): Promise<void> {\n try {\n const raw = await readFile(runPath, \"utf-8\");\n const run = JSON.parse(raw) as PersistedRun;\n Object.assign(run, updates);\n await writeFile(runPath, JSON.stringify(run, null, 2), \"utf-8\");\n } catch {\n // Non-critical\n }\n}\n\nmain();\n"],"mappings":"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","names":[]}