@neotx/cli 0.1.0-alpha.15 → 0.1.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -10
- package/dist/activity-LWUVGQVN.js +86 -0
- package/dist/activity-LWUVGQVN.js.map +1 -0
- package/dist/config-NYF6AJXU.js +282 -0
- package/dist/config-NYF6AJXU.js.map +1 -0
- package/dist/daemon/worker.js +1 -8
- package/dist/daemon/worker.js.map +1 -1
- package/dist/decision-2BY7JK4O.js +342 -0
- package/dist/decision-2BY7JK4O.js.map +1 -0
- package/dist/guide-UQRNA3FC.js +23 -0
- package/dist/guide-UQRNA3FC.js.map +1 -0
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/{run-QB2JHTLX.js → run-OF53USMD.js} +3 -10
- package/dist/run-OF53USMD.js.map +1 -0
- package/dist/{runs-SSBMKO53.js → runs-TAASM3YF.js} +3 -4
- package/dist/{runs-SSBMKO53.js.map → runs-TAASM3YF.js.map} +1 -1
- package/dist/status-LQOFOJJI.js +90 -0
- package/dist/status-LQOFOJJI.js.map +1 -0
- package/dist/{supervise-R3W7ZF33.js → supervise-632LRYWF.js} +2 -2
- package/dist/supervisor-3RUX5SPH.js +16 -0
- package/dist/supervisor-3RUX5SPH.js.map +1 -0
- package/dist/{tui-ODHFX3ZZ.js → tui-VKF3WVXC.js} +253 -14
- package/dist/tui-VKF3WVXC.js.map +1 -0
- package/dist/webhooks-PUKAHFHE.js +151 -0
- package/dist/webhooks-PUKAHFHE.js.map +1 -0
- package/package.json +3 -3
- package/dist/run-QB2JHTLX.js.map +0 -1
- package/dist/tui-ODHFX3ZZ.js.map +0 -1
- /package/dist/{supervise-R3W7ZF33.js.map → supervise-632LRYWF.js.map} +0 -0
package/README.md
CHANGED
|
@@ -5,13 +5,13 @@ The `neo` command-line interface for orchestrating autonomous developer agents.
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install -g neotx
|
|
8
|
+
npm install -g @neotx/cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Or with
|
|
11
|
+
Or with pnpm:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
14
|
+
pnpm add -g @neotx/cli
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Requires Node.js 22 or later.
|
|
@@ -58,11 +58,11 @@ Dispatch an agent to execute a task in an isolated clone.
|
|
|
58
58
|
```bash
|
|
59
59
|
neo run developer --prompt "Implement user authentication"
|
|
60
60
|
neo run architect --prompt "Design the caching layer" --repo /path/to/repo
|
|
61
|
-
neo run reviewer
|
|
61
|
+
neo run reviewer --prompt "Review the auth changes" --priority high
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
**Arguments:**
|
|
65
|
-
- `<agent>` - Agent name (e.g., `developer`, `architect`, `reviewer
|
|
65
|
+
- `<agent>` - Agent name (e.g., `developer`, `architect`, `reviewer`)
|
|
66
66
|
|
|
67
67
|
**Options:**
|
|
68
68
|
| Flag | Description |
|
|
@@ -140,14 +140,16 @@ neo logs --output json # Output as JSON
|
|
|
140
140
|
Log a structured progress report to the supervisor activity log.
|
|
141
141
|
|
|
142
142
|
```bash
|
|
143
|
-
neo log
|
|
144
|
-
neo log action "
|
|
145
|
-
neo log
|
|
146
|
-
neo log
|
|
143
|
+
neo log progress "3/5 endpoints done"
|
|
144
|
+
neo log action "Pushed to branch"
|
|
145
|
+
neo log decision "Chose JWT over sessions — simpler for MVP"
|
|
146
|
+
neo log blocker "Tests failing, missing dependency"
|
|
147
|
+
neo log milestone "All tests passing, PR opened"
|
|
148
|
+
neo log discovery "Repo uses Prisma + PostgreSQL"
|
|
147
149
|
```
|
|
148
150
|
|
|
149
151
|
**Arguments:**
|
|
150
|
-
- `<type>` - Report type: `
|
|
152
|
+
- `<type>` - Report type: `progress`, `action`, `decision`, `blocker`, `milestone`, `discovery`
|
|
151
153
|
- `<message>` - Message to log
|
|
152
154
|
|
|
153
155
|
**Options:**
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
printError,
|
|
3
|
+
printJson,
|
|
4
|
+
printTable
|
|
5
|
+
} from "./chunk-YQIWMDXL.js";
|
|
6
|
+
|
|
7
|
+
// src/commands/supervisor/activity.ts
|
|
8
|
+
import { getSupervisorDir, StatusReader } from "@neotx/core";
|
|
9
|
+
import { defineCommand } from "citty";
|
|
10
|
+
var DEFAULT_NAME = "supervisor";
|
|
11
|
+
var DEFAULT_LIMIT = 50;
|
|
12
|
+
function formatTimestamp(iso) {
|
|
13
|
+
const date = new Date(iso);
|
|
14
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
15
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
16
|
+
}
|
|
17
|
+
function formatActivityTable(entries) {
|
|
18
|
+
const headers = ["Timestamp", "Type", "Summary"];
|
|
19
|
+
const rows = entries.map((e) => [formatTimestamp(e.timestamp), e.type, e.summary]);
|
|
20
|
+
printTable(headers, rows);
|
|
21
|
+
}
|
|
22
|
+
var activity_default = defineCommand({
|
|
23
|
+
meta: {
|
|
24
|
+
name: "activity",
|
|
25
|
+
description: "Show supervisor activity log"
|
|
26
|
+
},
|
|
27
|
+
args: {
|
|
28
|
+
name: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Supervisor instance name",
|
|
31
|
+
default: DEFAULT_NAME
|
|
32
|
+
},
|
|
33
|
+
type: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Filter by activity type (decision, action, error, event, message, plan, dispatch)"
|
|
36
|
+
},
|
|
37
|
+
since: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Show activity since this ISO timestamp"
|
|
40
|
+
},
|
|
41
|
+
until: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "Show activity until this ISO timestamp"
|
|
44
|
+
},
|
|
45
|
+
limit: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Maximum number of entries to show",
|
|
48
|
+
default: String(DEFAULT_LIMIT)
|
|
49
|
+
},
|
|
50
|
+
json: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
description: "Output as JSON",
|
|
53
|
+
default: false
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
async run({ args }) {
|
|
57
|
+
const name = args.name;
|
|
58
|
+
const dataDir = getSupervisorDir(name);
|
|
59
|
+
const reader = new StatusReader(dataDir);
|
|
60
|
+
const limit = Number.parseInt(args.limit, 10) || DEFAULT_LIMIT;
|
|
61
|
+
const type = args.type;
|
|
62
|
+
const entries = reader.queryActivity({
|
|
63
|
+
type,
|
|
64
|
+
since: args.since,
|
|
65
|
+
until: args.until,
|
|
66
|
+
limit
|
|
67
|
+
});
|
|
68
|
+
if (entries.length === 0) {
|
|
69
|
+
if (args.json) {
|
|
70
|
+
printJson([]);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
printError("No activity found");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (args.json) {
|
|
77
|
+
printJson(entries);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
formatActivityTable(entries);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
export {
|
|
84
|
+
activity_default as default
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=activity-LWUVGQVN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/supervisor/activity.ts"],"sourcesContent":["import { type ActivityEntry, getSupervisorDir, StatusReader } from \"@neotx/core\";\nimport { defineCommand } from \"citty\";\nimport { printError, printJson, printTable } from \"../../output.js\";\n\nconst DEFAULT_NAME = \"supervisor\";\nconst DEFAULT_LIMIT = 50;\n\nfunction formatTimestamp(iso: string): string {\n const date = new Date(iso);\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;\n}\n\nfunction formatActivityTable(entries: ActivityEntry[]): void {\n const headers = [\"Timestamp\", \"Type\", \"Summary\"];\n const rows = entries.map((e) => [formatTimestamp(e.timestamp), e.type, e.summary]);\n printTable(headers, rows);\n}\n\nexport default defineCommand({\n meta: {\n name: \"activity\",\n description: \"Show supervisor activity log\",\n },\n args: {\n name: {\n type: \"string\",\n description: \"Supervisor instance name\",\n default: DEFAULT_NAME,\n },\n type: {\n type: \"string\",\n description:\n \"Filter by activity type (decision, action, error, event, message, plan, dispatch)\",\n },\n since: {\n type: \"string\",\n description: \"Show activity since this ISO timestamp\",\n },\n until: {\n type: \"string\",\n description: \"Show activity until this ISO timestamp\",\n },\n limit: {\n type: \"string\",\n description: \"Maximum number of entries to show\",\n default: String(DEFAULT_LIMIT),\n },\n json: {\n type: \"boolean\",\n description: \"Output as JSON\",\n default: false,\n },\n },\n async run({ args }) {\n const name = args.name;\n const dataDir = getSupervisorDir(name);\n const reader = new StatusReader(dataDir);\n\n const limit = Number.parseInt(args.limit, 10) || DEFAULT_LIMIT;\n const type = args.type as\n | \"decision\"\n | \"action\"\n | \"error\"\n | \"event\"\n | \"message\"\n | \"plan\"\n | \"dispatch\"\n | undefined;\n\n const entries = reader.queryActivity({\n type,\n since: args.since,\n until: args.until,\n limit,\n });\n\n if (entries.length === 0) {\n if (args.json) {\n printJson([]);\n return;\n }\n printError(\"No activity found\");\n return;\n }\n\n if (args.json) {\n printJson(entries);\n return;\n }\n\n formatActivityTable(entries);\n },\n});\n"],"mappings":";;;;;;;AAAA,SAA6B,kBAAkB,oBAAoB;AACnE,SAAS,qBAAqB;AAG9B,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAEtB,SAAS,gBAAgB,KAAqB;AAC5C,QAAM,OAAO,IAAI,KAAK,GAAG;AACzB,QAAM,MAAM,CAAC,MAAc,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AACvD,SAAO,GAAG,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC;AAC7J;AAEA,SAAS,oBAAoB,SAAgC;AAC3D,QAAM,UAAU,CAAC,aAAa,QAAQ,SAAS;AAC/C,QAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,SAAS,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC;AACjF,aAAW,SAAS,IAAI;AAC1B;AAEA,IAAO,mBAAQ,cAAc;AAAA,EAC3B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,OAAO,aAAa;AAAA,IAC/B;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,UAAM,OAAO,KAAK;AAClB,UAAM,UAAU,iBAAiB,IAAI;AACrC,UAAM,SAAS,IAAI,aAAa,OAAO;AAEvC,UAAM,QAAQ,OAAO,SAAS,KAAK,OAAO,EAAE,KAAK;AACjD,UAAM,OAAO,KAAK;AAUlB,UAAM,UAAU,OAAO,cAAc;AAAA,MACnC;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,WAAW,GAAG;AACxB,UAAI,KAAK,MAAM;AACb,kBAAU,CAAC,CAAC;AACZ;AAAA,MACF;AACA,iBAAW,mBAAmB;AAC9B;AAAA,IACF;AAEA,QAAI,KAAK,MAAM;AACb,gBAAU,OAAO;AACjB;AAAA,IACF;AAEA,wBAAoB,OAAO;AAAA,EAC7B;AACF,CAAC;","names":[]}
|
|
@@ -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":[]}
|
package/dist/daemon/worker.js
CHANGED
|
@@ -62,13 +62,6 @@ ${err.stack}`);
|
|
|
62
62
|
}
|
|
63
63
|
const orchestrator = new Orchestrator(config, { skipOrphanRecovery: true });
|
|
64
64
|
orchestrator.registerAgent(agent);
|
|
65
|
-
orchestrator.registerWorkflow({
|
|
66
|
-
name: `_run_${request.agentName}`,
|
|
67
|
-
description: `Detached dispatch to ${request.agentName}`,
|
|
68
|
-
steps: {
|
|
69
|
-
run: { agent: request.agentName }
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
65
|
await updatePersistedRun(runPath, { pid: process.pid });
|
|
73
66
|
const safetyTimeout = setTimeout(() => {
|
|
74
67
|
console.error("[worker] Safety timeout reached, forcing exit");
|
|
@@ -80,7 +73,7 @@ ${err.stack}`);
|
|
|
80
73
|
writeLog("[worker] Dispatching...");
|
|
81
74
|
const result = await orchestrator.dispatch({
|
|
82
75
|
runId,
|
|
83
|
-
|
|
76
|
+
agent: request.agentName,
|
|
84
77
|
repo: request.repo,
|
|
85
78
|
prompt: request.prompt,
|
|
86
79
|
...request.branch ? { branch: request.branch } : {},
|
|
@@ -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 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
|
|
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":[]}
|