godmode 0.0.1 → 0.0.2
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 +34 -0
- package/dist/{add-ZRNIX6DM.js → add-AJERMPEA.js} +11 -9
- package/dist/chunk-2J2VDCQ4.js +503 -0
- package/dist/{chunk-EHS56XXY.js → chunk-EXWYJU2I.js} +1 -1
- package/dist/{chunk-I4WS4MMI.js → chunk-GJCJO7YT.js} +3 -3
- package/dist/index.js +1424 -275
- package/dist/{prompt-DCFFOQ7K.js → prompt-SWOWWAOH.js} +1 -1
- package/dist/{mcp-server.js → server-4YVOAJJG.js} +3 -3
- package/package.json +27 -9
- package/dist/chunk-FBMQ3AC4.js +0 -339
- package/dist/mcp-NZIEMQOM.js +0 -58
package/dist/index.js
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
execute
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GJCJO7YT.js";
|
|
5
5
|
import {
|
|
6
|
+
GODMODE_EXTENSIONS_DIR,
|
|
7
|
+
GODMODE_HOME,
|
|
6
8
|
listApis,
|
|
7
9
|
loadManifest,
|
|
10
|
+
loadMultiManifest,
|
|
8
11
|
removeApi,
|
|
9
12
|
updateApi,
|
|
10
13
|
validateGraphQLFlags
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-2J2VDCQ4.js";
|
|
12
15
|
import {
|
|
13
16
|
executeMcpTool,
|
|
14
17
|
validateMcpFlags
|
|
15
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-EXWYJU2I.js";
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
22
|
+
import { resolve as resolve5 } from "path";
|
|
16
23
|
|
|
17
24
|
// src/env.ts
|
|
18
25
|
import { readFileSync } from "fs";
|
|
@@ -35,7 +42,7 @@ function loadEnv() {
|
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
// src/match.ts
|
|
45
|
+
// ../../interfaces/api/src/match.ts
|
|
39
46
|
function matchRoute(manifest, userSegments, method) {
|
|
40
47
|
if (userSegments.length > 0) {
|
|
41
48
|
const ver = manifest.versions.find((v) => v.name === userSegments[0]);
|
|
@@ -88,47 +95,73 @@ function suggestRoutes(manifest, userSegments) {
|
|
|
88
95
|
});
|
|
89
96
|
}
|
|
90
97
|
|
|
98
|
+
// ../../interfaces/mcp/src/command.ts
|
|
99
|
+
import { resolve as resolve2 } from "path";
|
|
100
|
+
import { readFile } from "fs/promises";
|
|
101
|
+
import { spawn } from "child_process";
|
|
102
|
+
async function runMcp(rt, args) {
|
|
103
|
+
const name = args[0];
|
|
104
|
+
if (!name || name === "--help" || name === "-h") {
|
|
105
|
+
console.log(`Serve a registered API as an MCP server over stdio.
|
|
106
|
+
|
|
107
|
+
Usage:
|
|
108
|
+
godmode mcp <name> [options]
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
--filter <text> Fuzzy-filter routes by resource name
|
|
112
|
+
--method <method> Filter by HTTP method (get, post, etc.)
|
|
113
|
+
|
|
114
|
+
For package-based extensions, spawns the extension's MCP server.
|
|
115
|
+
For spec-based extensions, serves routes as MCP tools.`);
|
|
116
|
+
process.exit(name ? 0 : 1);
|
|
117
|
+
}
|
|
118
|
+
let filter;
|
|
119
|
+
let method;
|
|
120
|
+
for (let i = 1; i < args.length; i++) {
|
|
121
|
+
if (args[i] === "--filter" && args[i + 1]) filter = args[++i];
|
|
122
|
+
else if (args[i] === "--method" && args[i + 1]) method = args[++i];
|
|
123
|
+
}
|
|
124
|
+
const pkgName = name.startsWith("@") ? name : `@godmode-cli/${name}`;
|
|
125
|
+
const pkgDir = resolve2(rt.godmodeHome, "node_modules", pkgName);
|
|
126
|
+
const mcpConfigPath = resolve2(pkgDir, ".mcp.json");
|
|
127
|
+
try {
|
|
128
|
+
const mcpConfig = JSON.parse(await readFile(mcpConfigPath, "utf-8"));
|
|
129
|
+
const serverKey = Object.keys(mcpConfig.mcpServers)[0];
|
|
130
|
+
const server = mcpConfig.mcpServers[serverKey];
|
|
131
|
+
if (server.type === "stdio" && server.command) {
|
|
132
|
+
const child = spawn(server.command, server.args || [], {
|
|
133
|
+
stdio: "inherit",
|
|
134
|
+
cwd: pkgDir,
|
|
135
|
+
env: { ...process.env, ...server.env }
|
|
136
|
+
});
|
|
137
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
const manifest = await rt.loadManifest(name);
|
|
143
|
+
const { serveMcp } = await import("./server-4YVOAJJG.js");
|
|
144
|
+
await serveMcp(manifest, { filter, method });
|
|
145
|
+
}
|
|
146
|
+
|
|
91
147
|
// src/args.ts
|
|
148
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
|
|
92
149
|
function parseArgs(args) {
|
|
93
150
|
const segments = [];
|
|
94
151
|
const headers = {};
|
|
95
152
|
const query = {};
|
|
96
153
|
const body = {};
|
|
97
154
|
let method = "get";
|
|
98
|
-
let
|
|
155
|
+
let explicitMethod = false;
|
|
99
156
|
let filter;
|
|
100
157
|
let methodFilter;
|
|
101
158
|
let all = false;
|
|
102
|
-
|
|
103
|
-
|
|
159
|
+
const verbose = process.env.GODMODE_VERBOSE === "1";
|
|
160
|
+
const dryRun = process.env.GODMODE_DRY_RUN === "1";
|
|
104
161
|
let help = false;
|
|
105
162
|
for (let i = 0; i < args.length; i++) {
|
|
106
163
|
const arg = args[i];
|
|
107
164
|
switch (arg) {
|
|
108
|
-
// HTTP methods
|
|
109
|
-
case "-g":
|
|
110
|
-
case "--get":
|
|
111
|
-
method = "get";
|
|
112
|
-
break;
|
|
113
|
-
case "-po":
|
|
114
|
-
case "--post":
|
|
115
|
-
method = "post";
|
|
116
|
-
break;
|
|
117
|
-
case "-pu":
|
|
118
|
-
case "--put":
|
|
119
|
-
method = "put";
|
|
120
|
-
break;
|
|
121
|
-
case "-pa":
|
|
122
|
-
case "--patch":
|
|
123
|
-
method = "patch";
|
|
124
|
-
break;
|
|
125
|
-
case "-d":
|
|
126
|
-
case "--delete":
|
|
127
|
-
method = "delete";
|
|
128
|
-
break;
|
|
129
|
-
case "--head":
|
|
130
|
-
method = "head";
|
|
131
|
-
break;
|
|
132
165
|
// Options
|
|
133
166
|
case "-H":
|
|
134
167
|
case "--header": {
|
|
@@ -137,25 +170,18 @@ function parseArgs(args) {
|
|
|
137
170
|
if (idx > 0) headers[val.slice(0, idx).trim()] = val.slice(idx + 1).trim();
|
|
138
171
|
break;
|
|
139
172
|
}
|
|
140
|
-
case "
|
|
141
|
-
token = args[++i];
|
|
142
|
-
break;
|
|
173
|
+
case "-F":
|
|
143
174
|
case "--filter":
|
|
144
175
|
filter = args[++i];
|
|
145
176
|
break;
|
|
177
|
+
case "-X":
|
|
146
178
|
case "--method":
|
|
147
179
|
methodFilter = args[++i];
|
|
148
180
|
break;
|
|
181
|
+
case "-A":
|
|
149
182
|
case "--all":
|
|
150
183
|
all = true;
|
|
151
184
|
break;
|
|
152
|
-
case "-v":
|
|
153
|
-
case "--verbose":
|
|
154
|
-
verbose = true;
|
|
155
|
-
break;
|
|
156
|
-
case "--dry-run":
|
|
157
|
-
dryRun = true;
|
|
158
|
-
break;
|
|
159
185
|
case "-h":
|
|
160
186
|
case "--help":
|
|
161
187
|
help = true;
|
|
@@ -166,6 +192,11 @@ function parseArgs(args) {
|
|
|
166
192
|
`);
|
|
167
193
|
process.exit(1);
|
|
168
194
|
}
|
|
195
|
+
if (!segments.length && !explicitMethod && HTTP_METHODS.has(arg.toUpperCase())) {
|
|
196
|
+
method = arg.toLowerCase();
|
|
197
|
+
explicitMethod = true;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
169
200
|
const eqeq = arg.indexOf("==");
|
|
170
201
|
const eq = arg.indexOf("=");
|
|
171
202
|
if (eqeq > 0) {
|
|
@@ -177,8 +208,7 @@ function parseArgs(args) {
|
|
|
177
208
|
}
|
|
178
209
|
}
|
|
179
210
|
}
|
|
180
|
-
|
|
181
|
-
return { segments, method, headers, query, body, token, filter, methodFilter, all, verbose, dryRun, help };
|
|
211
|
+
return { segments, method, explicitMethod, headers, query, body, filter, methodFilter, all, verbose, dryRun, help };
|
|
182
212
|
}
|
|
183
213
|
async function readStdin() {
|
|
184
214
|
if (process.stdin.isTTY) return void 0;
|
|
@@ -190,6 +220,171 @@ async function readStdin() {
|
|
|
190
220
|
|
|
191
221
|
// src/help.ts
|
|
192
222
|
import fuzzysort from "fuzzysort";
|
|
223
|
+
|
|
224
|
+
// ../cli-tools/src/index.ts
|
|
225
|
+
var USE_COLOR = process.stdout.isTTY;
|
|
226
|
+
var DIM = USE_COLOR ? "\x1B[2m" : "";
|
|
227
|
+
var ITALIC = USE_COLOR ? "\x1B[3m" : "";
|
|
228
|
+
var RESET = USE_COLOR ? "\x1B[0m" : "";
|
|
229
|
+
var RED = USE_COLOR ? "\x1B[31m" : "";
|
|
230
|
+
function visibleLength(s) {
|
|
231
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
232
|
+
}
|
|
233
|
+
function wrapText(text, width) {
|
|
234
|
+
if (text.length <= width) return [text];
|
|
235
|
+
const out = [];
|
|
236
|
+
let line = "";
|
|
237
|
+
for (const word of text.split(/\s+/)) {
|
|
238
|
+
if (!line) {
|
|
239
|
+
line = word;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (line.length + 1 + word.length <= width) {
|
|
243
|
+
line += " " + word;
|
|
244
|
+
} else {
|
|
245
|
+
out.push(line);
|
|
246
|
+
line = word;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (line) out.push(line);
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
252
|
+
function renderSections(sections, maxLineWidth = 80) {
|
|
253
|
+
const INDENT = " ";
|
|
254
|
+
const GAP = " ";
|
|
255
|
+
const flattened = sections.map((s) => {
|
|
256
|
+
if (!s.rows.length) return { title: s.title, rows: [] };
|
|
257
|
+
const cols = Math.max(...s.rows.map((r) => r.length));
|
|
258
|
+
const widths = Array(cols).fill(0);
|
|
259
|
+
for (const row of s.rows) {
|
|
260
|
+
for (let i = 0; i < cols; i++) {
|
|
261
|
+
widths[i] = Math.max(widths[i], visibleLength(row[i] ?? ""));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const rows = s.rows.map((row) => {
|
|
265
|
+
const left = row.slice(0, cols - 1).map((cell, i) => (cell ?? "") + " ".repeat(widths[i] - visibleLength(cell ?? ""))).join(GAP).replace(/\s+$/, "");
|
|
266
|
+
const desc = row[cols - 1] ?? "";
|
|
267
|
+
return [left, desc];
|
|
268
|
+
});
|
|
269
|
+
return { title: s.title, rows };
|
|
270
|
+
});
|
|
271
|
+
const allRows = flattened.flatMap((s) => s.rows);
|
|
272
|
+
if (!allRows.length) return;
|
|
273
|
+
const rowsWithDesc = allRows.filter((r) => r[1].length > 0);
|
|
274
|
+
const widthCandidates = (rowsWithDesc.length ? rowsWithDesc : allRows).map((r) => visibleLength(r[0]));
|
|
275
|
+
const globalLeft = Math.max(...widthCandidates);
|
|
276
|
+
const descBudget = Math.max(20, maxLineWidth - INDENT.length - globalLeft - GAP.length);
|
|
277
|
+
for (const s of flattened) {
|
|
278
|
+
if (!s.rows.length) continue;
|
|
279
|
+
if (s.title) {
|
|
280
|
+
console.log("");
|
|
281
|
+
console.log(s.title);
|
|
282
|
+
}
|
|
283
|
+
for (const [left, desc] of s.rows) {
|
|
284
|
+
const pad = " ".repeat(Math.max(0, globalLeft - visibleLength(left)));
|
|
285
|
+
const plain = desc.replace(/\x1b\[[0-9;]*m/g, "");
|
|
286
|
+
if (!plain) {
|
|
287
|
+
console.log(`${INDENT}${left}${pad}`.trimEnd());
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (plain.length <= descBudget) {
|
|
291
|
+
console.log(`${INDENT}${left}${pad}${GAP}${desc}`);
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const italic = /^\x1b\[3m/.test(desc);
|
|
295
|
+
const wrap = (s2) => italic ? `${ITALIC}${s2}${RESET}` : s2;
|
|
296
|
+
const lines = wrapText(plain, Math.max(10, descBudget));
|
|
297
|
+
console.log(`${INDENT}${left}${pad}${GAP}${wrap(lines[0])}`);
|
|
298
|
+
for (let i = 1; i < lines.length; i++) {
|
|
299
|
+
console.log(`${INDENT}${" ".repeat(Math.max(0, globalLeft))}${GAP}${wrap(lines[i])}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function authMissingLabel(type) {
|
|
305
|
+
switch (type) {
|
|
306
|
+
case "api-key":
|
|
307
|
+
return "missing api-key";
|
|
308
|
+
case "basic":
|
|
309
|
+
return "missing basic auth credentials";
|
|
310
|
+
case "bearer":
|
|
311
|
+
default:
|
|
312
|
+
return "missing bearer token";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
var HelpPage = class {
|
|
316
|
+
title() {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
tagline() {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
authNote() {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
usage() {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
sections() {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
footer() {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
toJSON() {
|
|
335
|
+
return {
|
|
336
|
+
title: this.title(),
|
|
337
|
+
tagline: this.tagline(),
|
|
338
|
+
authNote: this.authNote(),
|
|
339
|
+
usage: this.usage(),
|
|
340
|
+
sections: this.sections(),
|
|
341
|
+
footer: this.footer()
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
render() {
|
|
345
|
+
const d = this.toJSON();
|
|
346
|
+
let wrote = false;
|
|
347
|
+
if (d.title) {
|
|
348
|
+
console.log(d.title);
|
|
349
|
+
wrote = true;
|
|
350
|
+
}
|
|
351
|
+
if (d.tagline) {
|
|
352
|
+
if (wrote) console.log("");
|
|
353
|
+
console.log(d.tagline);
|
|
354
|
+
wrote = true;
|
|
355
|
+
}
|
|
356
|
+
if (d.authNote && !d.authNote.present) {
|
|
357
|
+
const arrow = USE_COLOR ? `${RED}-->${RESET}` : "-->";
|
|
358
|
+
const label = authMissingLabel(d.authNote.authType);
|
|
359
|
+
const body = USE_COLOR ? `${RED}${label}${RESET}` : label;
|
|
360
|
+
if (wrote) console.log("");
|
|
361
|
+
console.log(`${arrow} ${d.authNote.env}: ${body}`);
|
|
362
|
+
wrote = true;
|
|
363
|
+
}
|
|
364
|
+
if (d.usage.length) {
|
|
365
|
+
if (wrote) console.log("");
|
|
366
|
+
for (let i = 0; i < d.usage.length; i++) {
|
|
367
|
+
const prefix = i === 0 ? "Usage:" : " or:";
|
|
368
|
+
console.log(`${prefix} ${d.usage[i]}`);
|
|
369
|
+
}
|
|
370
|
+
wrote = true;
|
|
371
|
+
}
|
|
372
|
+
renderSections(d.sections);
|
|
373
|
+
if (d.footer) {
|
|
374
|
+
if (d.footer.extras?.length) {
|
|
375
|
+
console.log("");
|
|
376
|
+
for (const line of d.footer.extras) console.log(line);
|
|
377
|
+
}
|
|
378
|
+
if (d.footer.reportBugs || d.footer.homepage) {
|
|
379
|
+
console.log("");
|
|
380
|
+
if (d.footer.reportBugs) console.log(`Report bugs to <${d.footer.reportBugs}>.`);
|
|
381
|
+
if (d.footer.homepage) console.log(`Godmode home page: <${d.footer.homepage}>.`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/help.ts
|
|
193
388
|
function buildTrie(routes) {
|
|
194
389
|
const root = { name: "", isParam: false, methods: [], children: [] };
|
|
195
390
|
for (const route of routes) {
|
|
@@ -243,42 +438,31 @@ function getChildren(node) {
|
|
|
243
438
|
result.sort((a, b) => a.name.localeCompare(b.name));
|
|
244
439
|
return result;
|
|
245
440
|
}
|
|
246
|
-
var
|
|
247
|
-
var METHOD_LABEL = {
|
|
441
|
+
var METHOD_LABEL = USE_COLOR ? {
|
|
248
442
|
get: "\x1B[1;38;5;34mGET\x1B[0m",
|
|
249
443
|
post: "\x1B[1;38;5;25mPOST\x1B[0m",
|
|
250
444
|
put: "\x1B[1;38;5;172mPUT\x1B[0m",
|
|
251
445
|
patch: "\x1B[1;38;5;30mPATCH\x1B[0m",
|
|
252
446
|
delete: "\x1B[1;38;5;160mDELETE\x1B[0m"
|
|
447
|
+
} : { get: "GET", post: "POST", put: "PUT", patch: "PATCH", delete: "DELETE" };
|
|
448
|
+
var INTERFACE_LABEL = {
|
|
449
|
+
api: "API",
|
|
450
|
+
graphql: "GraphQL",
|
|
451
|
+
mcp: "MCP",
|
|
452
|
+
skill: "Skill"
|
|
253
453
|
};
|
|
254
|
-
var
|
|
255
|
-
var
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
case "delete":
|
|
267
|
-
return "delete";
|
|
268
|
-
default:
|
|
269
|
-
return method;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function getNodeActions(node) {
|
|
273
|
-
const actions = /* @__PURE__ */ new Set();
|
|
274
|
-
const paramChild = node.children.find((c) => c.isParam);
|
|
275
|
-
const isLeaf = !paramChild;
|
|
276
|
-
for (const ep of node.methods) actions.add(methodToAction(ep.method, isLeaf));
|
|
277
|
-
if (paramChild) {
|
|
278
|
-
for (const ep of paramChild.methods) actions.add(methodToAction(ep.method, true));
|
|
279
|
-
}
|
|
280
|
-
return [...actions].sort((a, b) => ACTION_ORDER.indexOf(a) - ACTION_ORDER.indexOf(b));
|
|
281
|
-
}
|
|
454
|
+
var titleCase = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
455
|
+
var ROOT_OPTION_ROWS = [
|
|
456
|
+
["-v, --version", "output version information and exit"]
|
|
457
|
+
];
|
|
458
|
+
var INTERFACE_OPTION_ROWS = [
|
|
459
|
+
["-H, --header <key:value>", "add a request header"],
|
|
460
|
+
["-A, --all", "list all resources"],
|
|
461
|
+
["-F, --filter <text>", "fuzzy-filter resources by name"],
|
|
462
|
+
["-X, --method <verb>", "filter resources by HTTP method"],
|
|
463
|
+
["-h, --help", "show help for this subcommand"],
|
|
464
|
+
["-v, --version", "show extension spec versions"]
|
|
465
|
+
];
|
|
282
466
|
function getParamName(node) {
|
|
283
467
|
return node.children.find((c) => c.isParam)?.name || null;
|
|
284
468
|
}
|
|
@@ -290,251 +474,1208 @@ function methodLabels(node) {
|
|
|
290
474
|
const order = ["get", "post", "put", "patch", "delete"];
|
|
291
475
|
return order.filter((m) => methods.has(m)).map((m) => METHOD_LABEL[m]).join(" ");
|
|
292
476
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
Usage:
|
|
298
|
-
godmode <api> <resource> [id] [flags]
|
|
299
|
-
godmode <api> /path [flags]
|
|
300
|
-
|
|
301
|
-
Setup:
|
|
302
|
-
create Create own custom API entrypoint
|
|
303
|
-
add <name|file> Add API as CLI command from <name>.yaml config
|
|
304
|
-
update <name> Re-fetch OpenAPI spec and rebuild routes
|
|
305
|
-
remove <name> Unregister an API
|
|
306
|
-
list Show all registered APIs
|
|
307
|
-
mcp <name> Serve registered API as MCP server (stdio)
|
|
308
|
-
... Run \x1B[2mgodmode add --help\x1B[0m for config format
|
|
309
|
-
|
|
310
|
-
Navigation:
|
|
311
|
-
<api> --help Show resources, auth, and usage
|
|
312
|
-
<api> <resource> --help Show operations and sub-resources
|
|
313
|
-
|
|
314
|
-
Methods:
|
|
315
|
-
-g, --get GET (default)
|
|
316
|
-
-po, --post POST
|
|
317
|
-
-pu, --put PUT
|
|
318
|
-
-pa, --patch PATCH
|
|
319
|
-
-d, --delete DELETE
|
|
320
|
-
|
|
321
|
-
Data (httpie-style):
|
|
322
|
-
key=value Body field (JSON, implies POST)
|
|
323
|
-
key==value Query param (URL)
|
|
324
|
-
|
|
325
|
-
Options:
|
|
326
|
-
-H <key:value> Add header
|
|
327
|
-
--token <tok> Auth token (overrides config)
|
|
328
|
-
--dry-run Preview request without sending
|
|
329
|
-
-v, --verbose Show full request/response
|
|
330
|
-
|
|
331
|
-
Use "godmode <api> --help" for API-specific usage.`);
|
|
332
|
-
}
|
|
333
|
-
function showApiHelp(manifest, apiName, path, filter, methodFilter, all) {
|
|
334
|
-
const root = buildTrie(manifest.routes);
|
|
335
|
-
const nav = navigateTrie(root, path);
|
|
336
|
-
if (!nav) {
|
|
337
|
-
console.log("No matching resource.");
|
|
338
|
-
return;
|
|
477
|
+
var RootHelp = class extends HelpPage {
|
|
478
|
+
tagline() {
|
|
479
|
+
return "A control surface of agentic I/O.";
|
|
339
480
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
481
|
+
usage() {
|
|
482
|
+
return [
|
|
483
|
+
"godmode <extension> [args]...",
|
|
484
|
+
"godmode <extension> <interface> [args]..."
|
|
485
|
+
];
|
|
486
|
+
}
|
|
487
|
+
sections() {
|
|
488
|
+
return [
|
|
489
|
+
{ title: "Built-in extensions:", rows: [
|
|
490
|
+
["ext", "Install and manage godmode extensions"],
|
|
491
|
+
["agent", "Coding-agent workflows"]
|
|
492
|
+
] },
|
|
493
|
+
{ title: "Options:", rows: ROOT_OPTION_ROWS }
|
|
494
|
+
];
|
|
495
|
+
}
|
|
496
|
+
footer() {
|
|
497
|
+
return {
|
|
498
|
+
extras: [
|
|
499
|
+
'Run "godmode ext list" to see installed extensions.',
|
|
500
|
+
'Run "godmode <extension> --help" for extension-specific usage.'
|
|
501
|
+
],
|
|
502
|
+
reportBugs: "https://github.com/tomsiwik/godmode/issues",
|
|
503
|
+
homepage: "https://godmode.so"
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
var ExtensionOverview = class extends HelpPage {
|
|
508
|
+
constructor(multi) {
|
|
509
|
+
super();
|
|
510
|
+
this.multi = multi;
|
|
511
|
+
}
|
|
512
|
+
title() {
|
|
513
|
+
return titleCase(this.multi.name || this.multi.slug);
|
|
514
|
+
}
|
|
515
|
+
usage() {
|
|
516
|
+
const declared = Object.keys(this.multi.interfaces);
|
|
517
|
+
const args = (iface) => iface === "mcp" ? " <tool> [args]" : iface === "graphql" ? " <query> [flags]" : " <method> <resource> [id] [flags]";
|
|
518
|
+
return declared.map((iface) => `godmode ${this.multi.slug} ${iface}${args(iface)}`);
|
|
519
|
+
}
|
|
520
|
+
sections() {
|
|
521
|
+
const declared = Object.keys(this.multi.interfaces);
|
|
522
|
+
return [
|
|
523
|
+
{ title: "Interfaces:", rows: declared.map((iface) => {
|
|
524
|
+
const d = this.multi.interfaces[iface];
|
|
525
|
+
const url = d && "url" in d && d.url ? d.url : "(local)";
|
|
526
|
+
return [iface, url];
|
|
527
|
+
}) },
|
|
528
|
+
{ title: "Options:", rows: [["-v, --version", "show extension spec versions"]] }
|
|
529
|
+
];
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
var ExtensionVersionPage = class extends HelpPage {
|
|
533
|
+
constructor(multi) {
|
|
534
|
+
super();
|
|
535
|
+
this.multi = multi;
|
|
536
|
+
}
|
|
537
|
+
title() {
|
|
538
|
+
return titleCase(this.multi.name || this.multi.slug);
|
|
539
|
+
}
|
|
540
|
+
sections() {
|
|
541
|
+
const declared = Object.keys(this.multi.interfaces);
|
|
542
|
+
return [
|
|
543
|
+
{ title: "", rows: declared.map((iface) => {
|
|
544
|
+
const d = this.multi.interfaces[iface];
|
|
545
|
+
return [iface, d?.specVersion || "(unversioned)"];
|
|
546
|
+
}) }
|
|
547
|
+
];
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
var InterfaceHelp = class extends HelpPage {
|
|
551
|
+
constructor(manifest, apiName, rawPath = [], opts = {}) {
|
|
552
|
+
super();
|
|
553
|
+
this.manifest = manifest;
|
|
554
|
+
this.apiName = apiName;
|
|
555
|
+
this.opts = opts;
|
|
556
|
+
const HTTP_VERBS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
|
|
557
|
+
const cleaned = [...rawPath];
|
|
558
|
+
while (cleaned.length && HTTP_VERBS.has(cleaned[cleaned.length - 1].toUpperCase())) {
|
|
559
|
+
cleaned.pop();
|
|
362
560
|
}
|
|
363
|
-
|
|
364
|
-
|
|
561
|
+
this.path = cleaned;
|
|
562
|
+
}
|
|
563
|
+
path;
|
|
564
|
+
get ifaceType() {
|
|
565
|
+
return this.manifest.config.type;
|
|
566
|
+
}
|
|
567
|
+
getNav() {
|
|
568
|
+
const root = buildTrie(this.manifest.routes);
|
|
569
|
+
return navigateTrie(root, this.path);
|
|
570
|
+
}
|
|
571
|
+
title() {
|
|
572
|
+
if (this.path.length) return null;
|
|
573
|
+
return `${titleCase(this.manifest.config.name || this.apiName)} ${INTERFACE_LABEL[this.ifaceType] || this.ifaceType}`;
|
|
574
|
+
}
|
|
575
|
+
authNote() {
|
|
576
|
+
const auth = this.manifest.config.auth;
|
|
577
|
+
if (!auth?.env) return null;
|
|
578
|
+
return {
|
|
579
|
+
env: auth.env,
|
|
580
|
+
authType: auth.type || "bearer",
|
|
581
|
+
present: !!process.env[auth.env]
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
usage() {
|
|
585
|
+
if (!this.path.length) {
|
|
586
|
+
const multi = this.opts.multi;
|
|
587
|
+
const declared = multi ? Object.keys(multi.interfaces) : [this.ifaceType];
|
|
588
|
+
const ordered = [this.ifaceType, ...declared.filter((k) => k !== this.ifaceType)];
|
|
589
|
+
const args = (iface) => iface === "mcp" ? " <tool> [args]" : iface === "graphql" ? " <query> [flags]" : " <method> <resource> [id] [flags]";
|
|
590
|
+
return ordered.map((iface) => `godmode ${this.apiName} ${iface}${args(iface)}`);
|
|
591
|
+
}
|
|
592
|
+
const nav = this.getNav();
|
|
593
|
+
if (!nav) return [];
|
|
594
|
+
const paramHint = getParamName(nav.node);
|
|
365
595
|
const idRef = paramHint ? ` [${paramHint}]` : "";
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
for (const [name, schema] of Object.entries(props)) {
|
|
393
|
-
const req = required.has(name) ? `\x1B[1;38;5;160m[REQUIRED]${RESET} ` : "";
|
|
394
|
-
const desc = schema.description ? `${DIM}${ITALIC}${schema.description}${RESET}` : "";
|
|
395
|
-
console.log(` ${name.padEnd(28)}${req}${desc}`);
|
|
596
|
+
const methodSlot = this.ifaceType === "api" ? "<method> " : "";
|
|
597
|
+
return [`godmode ${this.apiName} ${this.ifaceType} ${methodSlot}${nav.fullPath.join(" ")}${idRef} [flags]`];
|
|
598
|
+
}
|
|
599
|
+
sections() {
|
|
600
|
+
const nav = this.getNav();
|
|
601
|
+
if (!nav) return [];
|
|
602
|
+
const sections = [];
|
|
603
|
+
if (!this.path.length) {
|
|
604
|
+
if (this.ifaceType === "api") {
|
|
605
|
+
const methodsPresent = new Set(this.manifest.routes.map((r) => r.method.toLowerCase()));
|
|
606
|
+
const METHOD_DESC = {
|
|
607
|
+
get: "retrieve a resource",
|
|
608
|
+
post: "create a resource or send a command",
|
|
609
|
+
put: "replace a resource",
|
|
610
|
+
patch: "modify a resource",
|
|
611
|
+
delete: "remove a resource",
|
|
612
|
+
head: "retrieve headers only"
|
|
613
|
+
};
|
|
614
|
+
const order = ["get", "post", "put", "patch", "delete", "head"];
|
|
615
|
+
const methodsHere = order.filter((m) => methodsPresent.has(m));
|
|
616
|
+
if (methodsHere.length) {
|
|
617
|
+
sections.push({
|
|
618
|
+
title: "Methods:",
|
|
619
|
+
rows: methodsHere.map((m) => [METHOD_LABEL[m] || m.toUpperCase(), METHOD_DESC[m]])
|
|
620
|
+
});
|
|
621
|
+
}
|
|
396
622
|
}
|
|
397
|
-
console.log("");
|
|
398
|
-
console.log(`Example:
|
|
399
|
-
godmode ${apiName} ${toolName} ${[...required].map((r) => `${r}=...`).join(" ")}`);
|
|
400
|
-
console.log("");
|
|
401
623
|
} else {
|
|
402
|
-
const
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
624
|
+
const mcpTools = this.manifest.config._mcpTools;
|
|
625
|
+
const mcpTool = mcpTools?.find((t) => t.name === this.path[this.path.length - 1]);
|
|
626
|
+
if (mcpTool?.inputSchema?.properties) {
|
|
627
|
+
const props = mcpTool.inputSchema.properties;
|
|
628
|
+
const required = new Set(mcpTool.inputSchema.required || []);
|
|
629
|
+
const rows = [];
|
|
630
|
+
for (const [name, schema] of Object.entries(props)) {
|
|
631
|
+
const req = required.has(name) ? `${RED}[REQUIRED]${RESET}` : "";
|
|
632
|
+
const desc = schema.description ? `${ITALIC}${schema.description}${RESET}` : "";
|
|
633
|
+
rows.push([name, req, desc]);
|
|
411
634
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
for (const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
635
|
+
sections.push({ title: "Parameters:", rows });
|
|
636
|
+
} else {
|
|
637
|
+
const order = ["get", "post", "put", "patch", "delete"];
|
|
638
|
+
const pc = nav.node.children.find((c) => c.isParam);
|
|
639
|
+
const items = [];
|
|
640
|
+
for (const ep of nav.node.methods) items.push({ method: ep.method, summary: ep.summary, needsId: false });
|
|
641
|
+
if (pc) for (const ep of pc.methods) items.push({ method: ep.method, summary: ep.summary, needsId: true });
|
|
642
|
+
if (items.length) {
|
|
643
|
+
const rows = [];
|
|
644
|
+
for (const m of order) {
|
|
645
|
+
for (const r of items.filter((r2) => r2.method === m)) {
|
|
646
|
+
const label = METHOD_LABEL[m] || m.toUpperCase();
|
|
647
|
+
const idArg = r.needsId ? `<${pc.name}>` : "";
|
|
648
|
+
const desc = r.summary ? `${ITALIC}${r.summary}${RESET}` : "";
|
|
649
|
+
rows.push([label, idArg, desc]);
|
|
650
|
+
}
|
|
427
651
|
}
|
|
652
|
+
sections.push({ title: "Methods:", rows });
|
|
428
653
|
}
|
|
429
|
-
const maxCmd = Math.max(...lines.map((l) => l.cmd.length));
|
|
430
|
-
for (const l of lines) {
|
|
431
|
-
const desc = l.desc ? ` ${ITALIC}${l.desc}${RESET}` : "";
|
|
432
|
-
console.log(` ${l.cmd.padEnd(maxCmd + 2)}${l.label}${desc}`);
|
|
433
|
-
}
|
|
434
|
-
console.log("");
|
|
435
654
|
}
|
|
436
655
|
}
|
|
656
|
+
const children = getChildren(nav.node).filter((c) => !c.isParam);
|
|
657
|
+
const childNames = [...new Set(children.map((c) => c.name))];
|
|
658
|
+
const resourceSection = buildResourceSection(
|
|
659
|
+
children,
|
|
660
|
+
childNames,
|
|
661
|
+
this.manifest,
|
|
662
|
+
this.opts.filter,
|
|
663
|
+
this.opts.methodFilter,
|
|
664
|
+
this.opts.all
|
|
665
|
+
);
|
|
666
|
+
if (resourceSection) sections.push(resourceSection.section);
|
|
667
|
+
sections.push({ title: "Options:", rows: INTERFACE_OPTION_ROWS });
|
|
668
|
+
return sections;
|
|
437
669
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
670
|
+
/** Drilled-in view also shows param status lines (ANSI-only). */
|
|
671
|
+
render() {
|
|
672
|
+
super.render();
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
function showHelp() {
|
|
676
|
+
new RootHelp().render();
|
|
677
|
+
}
|
|
678
|
+
function showExtensionOverview(multi) {
|
|
679
|
+
new ExtensionOverview(multi).render();
|
|
680
|
+
}
|
|
681
|
+
function showExtensionVersion(multi) {
|
|
682
|
+
new ExtensionVersionPage(multi).render();
|
|
683
|
+
}
|
|
684
|
+
function showApiHelp(manifest, apiName, path, filter, methodFilter, all, multi) {
|
|
685
|
+
new InterfaceHelp(manifest, apiName, path, { multi, filter, methodFilter, all }).render();
|
|
686
|
+
}
|
|
687
|
+
async function showVersion() {
|
|
688
|
+
const { readFile: readFile2 } = await import("fs/promises");
|
|
689
|
+
const { resolve: resolve6, dirname } = await import("path");
|
|
690
|
+
const { fileURLToPath } = await import("url");
|
|
691
|
+
const root = resolve6(dirname(fileURLToPath(import.meta.url)), "..");
|
|
692
|
+
const pkg = JSON.parse(await readFile2(resolve6(root, "package.json"), "utf-8"));
|
|
693
|
+
const license = await readFile2(resolve6(root, "LICENSE"), "utf-8");
|
|
694
|
+
const copyright = license.match(/^Copyright.*$/m)?.[0] ?? "";
|
|
695
|
+
console.log(`godmode ${pkg.version}
|
|
696
|
+
|
|
697
|
+
${pkg.license} License
|
|
698
|
+
${copyright}`);
|
|
699
|
+
}
|
|
700
|
+
function buildResourceSection(children, childNames, manifest, filter, methodFilter, all) {
|
|
701
|
+
if (!childNames.length) return null;
|
|
702
|
+
const ALL_METHODS = ["get", "post", "put", "patch", "delete"];
|
|
703
|
+
const mf = methodFilter ? fuzzysort.go(methodFilter, ALL_METHODS)[0]?.target : void 0;
|
|
704
|
+
if (methodFilter && !mf) return null;
|
|
705
|
+
const candidates = mf ? childNames.filter((n) => {
|
|
706
|
+
const child = children.find((c) => c.name === n);
|
|
707
|
+
if (!child) return false;
|
|
708
|
+
const methods = /* @__PURE__ */ new Set();
|
|
709
|
+
for (const ep of child.methods) methods.add(ep.method);
|
|
710
|
+
const pc = child.children.find((c) => c.isParam);
|
|
711
|
+
if (pc) for (const ep of pc.methods) methods.add(ep.method);
|
|
712
|
+
return methods.has(mf);
|
|
713
|
+
}) : childNames;
|
|
714
|
+
const filtered = filter ? fuzzysort.go(filter, candidates).map((r) => r.target) : candidates;
|
|
715
|
+
const title = filter || mf ? `Resources${filter ? ` matching "${filter}"` : ""}${mf ? ` with ${mf.toUpperCase()}` : ""}:` : "Resources:";
|
|
716
|
+
if (!filtered.length) return { section: { title, rows: [[" No matches.", ""]] } };
|
|
717
|
+
const limit = filter || mf || all ? filtered.length : 5;
|
|
718
|
+
const shown = filtered.slice(0, limit);
|
|
719
|
+
const more = filtered.length - shown.length;
|
|
720
|
+
const rows = [];
|
|
721
|
+
for (const n of shown) {
|
|
722
|
+
const child = children.find((c) => c.name === n);
|
|
723
|
+
const labels = child ? methodLabels(child) : "";
|
|
724
|
+
const subCount = child ? getChildren(child).filter((c) => !c.isParam).length : 0;
|
|
725
|
+
const sub = subCount ? `(${subCount} sub)` : "";
|
|
726
|
+
const rawDesc = manifest.resourceDescriptions[n] || "";
|
|
727
|
+
const desc = rawDesc ? `${ITALIC}${rawDesc}${RESET}` : "";
|
|
728
|
+
const parts = [labels, sub, desc].filter(Boolean);
|
|
729
|
+
rows.push([n, parts.join(" ")]);
|
|
730
|
+
}
|
|
731
|
+
if (more) {
|
|
732
|
+
const verb = mf ? ` ${mf.toUpperCase()}` : "";
|
|
733
|
+
const hintText = `${more} more${verb} resources. Use options to display more`;
|
|
734
|
+
const dots = USE_COLOR ? `${DIM}...${RESET}` : "...";
|
|
735
|
+
const body = USE_COLOR ? `${DIM}${hintText}${RESET}` : hintText;
|
|
736
|
+
rows.push([dots, body]);
|
|
737
|
+
}
|
|
738
|
+
return { section: { title, rows } };
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// ../../commands/agent/dist/index.js
|
|
742
|
+
import { spawnSync } from "child_process";
|
|
743
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
744
|
+
import { resolve as resolve3 } from "path";
|
|
745
|
+
import { closeSync, existsSync as existsSync2, openSync, readFileSync as readFileSync2, readSync, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
746
|
+
import { resolve as resolve22 } from "path";
|
|
747
|
+
import { randomUUID } from "crypto";
|
|
748
|
+
import { mkdirSync, readFileSync as readFileSync4, writeFileSync, existsSync, readdirSync } from "fs";
|
|
749
|
+
import { homedir } from "os";
|
|
750
|
+
import { resolve as resolve4 } from "path";
|
|
751
|
+
var DEFAULT_HARNESS_ORDER = ["claude", "codex", "gemini", "pi"];
|
|
752
|
+
var HARNESSES = {
|
|
753
|
+
claude: {
|
|
754
|
+
id: "claude",
|
|
755
|
+
displayName: "Claude Code",
|
|
756
|
+
command: "claude",
|
|
757
|
+
modelFlags: ["--model"],
|
|
758
|
+
effortFlags: ["--effort"],
|
|
759
|
+
helpArgs: ["--help"],
|
|
760
|
+
promptHints: ["[prompt]", "your prompt"],
|
|
761
|
+
buildTurnArgs: ({ prompt, model, effort, passthroughArgs, resumeToken }) => [
|
|
762
|
+
"-p",
|
|
763
|
+
"--verbose",
|
|
764
|
+
"--output-format",
|
|
765
|
+
"stream-json",
|
|
766
|
+
...resumeToken ? ["--resume", resumeToken] : [],
|
|
767
|
+
...model ? ["--model", model] : [],
|
|
768
|
+
...effort ? ["--effort", effort] : [],
|
|
769
|
+
prompt,
|
|
770
|
+
...passthroughArgs
|
|
771
|
+
]
|
|
772
|
+
},
|
|
773
|
+
codex: {
|
|
774
|
+
id: "codex",
|
|
775
|
+
displayName: "Codex CLI",
|
|
776
|
+
command: "codex",
|
|
777
|
+
modelFlags: ["-m", "--model"],
|
|
778
|
+
effortFlags: [],
|
|
779
|
+
helpArgs: ["--help"],
|
|
780
|
+
promptHints: ["[prompt]", "optional user prompt"],
|
|
781
|
+
buildTurnArgs: ({ prompt, model, passthroughArgs }) => [
|
|
782
|
+
"exec",
|
|
783
|
+
"--json",
|
|
784
|
+
...model ? ["-m", model] : [],
|
|
785
|
+
prompt,
|
|
786
|
+
...passthroughArgs
|
|
787
|
+
]
|
|
788
|
+
},
|
|
789
|
+
gemini: {
|
|
790
|
+
id: "gemini",
|
|
791
|
+
displayName: "Gemini CLI",
|
|
792
|
+
command: "gemini",
|
|
793
|
+
modelFlags: ["-m", "--model"],
|
|
794
|
+
effortFlags: [],
|
|
795
|
+
helpArgs: ["--help"],
|
|
796
|
+
promptHints: ["query", "initial prompt"],
|
|
797
|
+
buildTurnArgs: ({ prompt, model, passthroughArgs, resumeToken }) => [
|
|
798
|
+
...resumeToken ? ["--resume", resumeToken] : [],
|
|
799
|
+
...model ? ["-m", model] : [],
|
|
800
|
+
"-p",
|
|
801
|
+
prompt,
|
|
802
|
+
"-o",
|
|
803
|
+
"stream-json",
|
|
804
|
+
...passthroughArgs
|
|
805
|
+
]
|
|
806
|
+
},
|
|
807
|
+
pi: {
|
|
808
|
+
id: "pi",
|
|
809
|
+
displayName: "Pi",
|
|
810
|
+
command: "pi",
|
|
811
|
+
modelFlags: ["--model"],
|
|
812
|
+
effortFlags: ["--thinking"],
|
|
813
|
+
helpArgs: ["--help"],
|
|
814
|
+
promptHints: ["[messages...]", "initial prompt"],
|
|
815
|
+
buildTurnArgs: ({ prompt, model, effort, passthroughArgs, resumeToken, sessionDir }) => [
|
|
816
|
+
"--print",
|
|
817
|
+
"--mode",
|
|
818
|
+
"json",
|
|
819
|
+
...sessionDir ? ["--session-dir", sessionDir] : [],
|
|
820
|
+
...resumeToken ? ["--continue"] : [],
|
|
821
|
+
...model ? ["--model", model] : [],
|
|
822
|
+
...effort ? ["--thinking", effort] : [],
|
|
823
|
+
prompt,
|
|
824
|
+
...passthroughArgs
|
|
825
|
+
]
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
function detectHarness(executor, requested) {
|
|
829
|
+
const ids = requested ? [requested] : DEFAULT_HARNESS_ORDER;
|
|
830
|
+
for (const id of ids) {
|
|
831
|
+
const harness = HARNESSES[id];
|
|
832
|
+
const result = executor(harness.command, harness.helpArgs);
|
|
833
|
+
if (!result.error && result.status === 0) return harness;
|
|
834
|
+
}
|
|
835
|
+
throw new Error(requested ? `Harness "${requested}" not found.` : "No supported coding harness found.");
|
|
836
|
+
}
|
|
837
|
+
function event(ts, id, turn, partial) {
|
|
838
|
+
return { ts, id, turn, ...partial };
|
|
839
|
+
}
|
|
840
|
+
function normalizeTurn(harness, id, turn, stdout, stderr, ts) {
|
|
841
|
+
const lines = stdout.split(/\r?\n/);
|
|
842
|
+
if (harness === "claude") return parseClaude(lines, id, turn, stderr, ts);
|
|
843
|
+
if (harness === "gemini") return parseGemini(lines, id, turn, stderr, ts);
|
|
844
|
+
if (harness === "pi") return parsePi(lines, id, turn, stderr, ts);
|
|
845
|
+
return {
|
|
846
|
+
sessionId: null,
|
|
847
|
+
assistantText: stdout.trim(),
|
|
848
|
+
events: [
|
|
849
|
+
event(ts, id, turn, { type: "turn.started" }),
|
|
850
|
+
event(ts, id, turn, { type: "assistant.completed", text: stdout.trim() }),
|
|
851
|
+
event(ts, id, turn, { type: "turn.completed", status: "completed" })
|
|
852
|
+
]
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
function parseClaude(lines, id, turn, stderr, ts) {
|
|
856
|
+
const events = [];
|
|
857
|
+
let sessionId = null;
|
|
858
|
+
let assistantText = "";
|
|
859
|
+
let usage;
|
|
860
|
+
let timing;
|
|
861
|
+
for (const line of lines) {
|
|
862
|
+
if (!line.trim()) continue;
|
|
863
|
+
let parsed;
|
|
864
|
+
try {
|
|
865
|
+
parsed = JSON.parse(line);
|
|
866
|
+
} catch {
|
|
867
|
+
continue;
|
|
445
868
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
869
|
+
if (parsed.type === "system" && parsed.subtype === "init") {
|
|
870
|
+
sessionId = parsed.session_id ?? sessionId;
|
|
871
|
+
events.push(event(ts, id, turn, { type: "turn.started" }));
|
|
872
|
+
events.push(event(ts, id, turn, { type: "state.changed", state: "starting" }));
|
|
873
|
+
}
|
|
874
|
+
if (parsed.type === "assistant") {
|
|
875
|
+
const text = (parsed.message?.content ?? []).filter((item) => item.type === "text").map((item) => item.text).join("");
|
|
876
|
+
if (text) assistantText = text;
|
|
877
|
+
if (text) events.push(event(ts, id, turn, { type: "assistant.completed", text }));
|
|
878
|
+
}
|
|
879
|
+
if (parsed.type === "result") {
|
|
880
|
+
sessionId = parsed.session_id ?? sessionId;
|
|
881
|
+
usage = {
|
|
882
|
+
inputTokens: parsed.usage?.input_tokens,
|
|
883
|
+
outputTokens: parsed.usage?.output_tokens,
|
|
884
|
+
cachedTokens: parsed.usage?.cache_read_input_tokens
|
|
885
|
+
};
|
|
886
|
+
timing = { durationMs: parsed.duration_ms };
|
|
887
|
+
events.push(event(ts, id, turn, { type: "turn.completed", status: parsed.is_error ? "failed" : "completed" }));
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
for (const line of stderr.split(/\r?\n/).filter(Boolean)) events.push(event(ts, id, turn, { type: "warning", message: line }));
|
|
891
|
+
return { sessionId, assistantText, events, usage, timing };
|
|
892
|
+
}
|
|
893
|
+
function parseGemini(lines, id, turn, stderr, ts) {
|
|
894
|
+
const events = [];
|
|
895
|
+
let sessionId = null;
|
|
896
|
+
let assistantText = "";
|
|
897
|
+
let usage;
|
|
898
|
+
let timing;
|
|
899
|
+
for (const line of lines) {
|
|
900
|
+
if (!line.trim()) continue;
|
|
901
|
+
let parsed;
|
|
902
|
+
try {
|
|
903
|
+
parsed = JSON.parse(line);
|
|
904
|
+
} catch {
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (parsed.type === "init") {
|
|
908
|
+
sessionId = parsed.session_id ?? sessionId;
|
|
909
|
+
events.push(event(ts, id, turn, { type: "turn.started" }));
|
|
910
|
+
events.push(event(ts, id, turn, { type: "state.changed", state: "starting" }));
|
|
911
|
+
}
|
|
912
|
+
if (parsed.type === "message" && parsed.role === "assistant" && parsed.delta === true && typeof parsed.content === "string") {
|
|
913
|
+
assistantText += parsed.content;
|
|
914
|
+
events.push(event(ts, id, turn, { type: "assistant.delta", text: parsed.content }));
|
|
915
|
+
}
|
|
916
|
+
if (parsed.type === "result") {
|
|
917
|
+
usage = {
|
|
918
|
+
inputTokens: parsed.stats?.input_tokens,
|
|
919
|
+
outputTokens: parsed.stats?.output_tokens,
|
|
920
|
+
cachedTokens: parsed.stats?.cached,
|
|
921
|
+
totalTokens: parsed.stats?.total_tokens
|
|
922
|
+
};
|
|
923
|
+
timing = { durationMs: parsed.stats?.duration_ms };
|
|
924
|
+
events.push(event(ts, id, turn, { type: "assistant.completed", text: assistantText }));
|
|
925
|
+
events.push(event(ts, id, turn, { type: "turn.completed", status: parsed.status === "success" ? "completed" : "failed" }));
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
for (const line of stderr.split(/\r?\n/).filter(Boolean)) events.push(event(ts, id, turn, { type: "warning", message: line }));
|
|
929
|
+
return { sessionId, assistantText, events, usage, timing };
|
|
930
|
+
}
|
|
931
|
+
function parsePi(lines, id, turn, stderr, ts) {
|
|
932
|
+
const events = [];
|
|
933
|
+
let sessionId = null;
|
|
934
|
+
let assistantText = "";
|
|
935
|
+
for (const line of lines) {
|
|
936
|
+
if (!line.trim()) continue;
|
|
937
|
+
let parsed;
|
|
938
|
+
try {
|
|
939
|
+
parsed = JSON.parse(line);
|
|
940
|
+
} catch {
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
if (parsed.type === "session") {
|
|
944
|
+
sessionId = parsed.id ?? sessionId;
|
|
945
|
+
events.push(event(ts, id, turn, { type: "turn.started" }));
|
|
946
|
+
events.push(event(ts, id, turn, { type: "state.changed", state: "starting" }));
|
|
947
|
+
}
|
|
948
|
+
if (parsed.type === "turn_start") events.push(event(ts, id, turn, { type: "state.changed", state: "running" }));
|
|
949
|
+
if (parsed.type === "message_update" && parsed.assistantMessageEvent?.type === "thinking_start") {
|
|
950
|
+
events.push(event(ts, id, turn, { type: "state.changed", state: "thinking" }));
|
|
951
|
+
}
|
|
952
|
+
if (parsed.type === "message_update" && parsed.assistantMessageEvent?.type === "text_delta") {
|
|
953
|
+
const delta = parsed.assistantMessageEvent.delta ?? "";
|
|
954
|
+
assistantText += delta;
|
|
955
|
+
if (delta) events.push(event(ts, id, turn, { type: "assistant.delta", text: delta }));
|
|
956
|
+
}
|
|
957
|
+
if (parsed.type === "turn_end") {
|
|
958
|
+
const text = (parsed.message?.content ?? []).filter((item) => item.type === "text").map((item) => item.text).join("");
|
|
959
|
+
if (text) assistantText = text;
|
|
960
|
+
events.push(event(ts, id, turn, { type: "assistant.completed", text: assistantText }));
|
|
961
|
+
events.push(event(ts, id, turn, { type: "turn.completed", status: "completed" }));
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
for (const line of stderr.split(/\r?\n/).filter(Boolean)) events.push(event(ts, id, turn, { type: "warning", message: line }));
|
|
965
|
+
return { sessionId, assistantText, events };
|
|
966
|
+
}
|
|
967
|
+
var GODMODE_HOME2 = process.platform === "linux" && process.env.XDG_CONFIG_HOME ? resolve4(process.env.XDG_CONFIG_HOME, "godmode") : resolve4(homedir(), ".godmode");
|
|
968
|
+
var CODING_AGENTS_HOME = resolve4(GODMODE_HOME2, "coding-agents");
|
|
969
|
+
var RUNS_DIR = resolve4(CODING_AGENTS_HOME, "runs");
|
|
970
|
+
var ACTIVE_RUNS_PATH = resolve4(CODING_AGENTS_HOME, "active-runs.json");
|
|
971
|
+
function timestamp() {
|
|
972
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
973
|
+
}
|
|
974
|
+
function ensureDirs() {
|
|
975
|
+
mkdirSync(RUNS_DIR, { recursive: true });
|
|
976
|
+
}
|
|
977
|
+
function shellEscape(value) {
|
|
978
|
+
return `'${value.replace(/'/g, `"'"'`)}'`;
|
|
979
|
+
}
|
|
980
|
+
function writeJson(path, value) {
|
|
981
|
+
mkdirSync(resolve4(path, ".."), { recursive: true });
|
|
982
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
983
|
+
`);
|
|
984
|
+
}
|
|
985
|
+
function readJson(path) {
|
|
986
|
+
try {
|
|
987
|
+
return JSON.parse(readFileSync4(path, "utf-8"));
|
|
988
|
+
} catch {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function readSettingsFile(path) {
|
|
993
|
+
return readJson(path) ?? {};
|
|
994
|
+
}
|
|
995
|
+
function loadSettings(cwd = process.cwd()) {
|
|
996
|
+
const globalSettings = readSettingsFile(resolve4(GODMODE_HOME2, "settings.json"));
|
|
997
|
+
const projectSettings = readSettingsFile(resolve4(cwd, ".godmode", "settings.json"));
|
|
998
|
+
return {
|
|
999
|
+
...globalSettings,
|
|
1000
|
+
...projectSettings,
|
|
1001
|
+
plugins: {
|
|
1002
|
+
...globalSettings.plugins ?? {},
|
|
1003
|
+
...projectSettings.plugins ?? {},
|
|
1004
|
+
"coding-agents": {
|
|
1005
|
+
...globalSettings.plugins?.["coding-agents"] ?? {},
|
|
1006
|
+
...projectSettings.plugins?.["coding-agents"] ?? {}
|
|
475
1007
|
}
|
|
476
|
-
|
|
477
|
-
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function loadActiveRuns() {
|
|
1012
|
+
return readJson(ACTIVE_RUNS_PATH) ?? {};
|
|
1013
|
+
}
|
|
1014
|
+
function saveActiveRun(cwd, id) {
|
|
1015
|
+
const current = loadActiveRuns();
|
|
1016
|
+
current[cwd] = id;
|
|
1017
|
+
writeJson(ACTIVE_RUNS_PATH, current);
|
|
1018
|
+
}
|
|
1019
|
+
function activeRunIdForCwd(cwd) {
|
|
1020
|
+
return loadActiveRuns()[cwd] ?? null;
|
|
1021
|
+
}
|
|
1022
|
+
function runDir(id) {
|
|
1023
|
+
return resolve4(RUNS_DIR, id);
|
|
1024
|
+
}
|
|
1025
|
+
function runPath(id) {
|
|
1026
|
+
return resolve4(runDir(id), "run.json");
|
|
1027
|
+
}
|
|
1028
|
+
function turnDir(id, turn) {
|
|
1029
|
+
return resolve4(runDir(id), "turns", String(turn).padStart(4, "0"));
|
|
1030
|
+
}
|
|
1031
|
+
function loadRun(id) {
|
|
1032
|
+
const run = readJson(runPath(id));
|
|
1033
|
+
if (!run) throw new Error(`Run not found: ${id}`);
|
|
1034
|
+
return run;
|
|
1035
|
+
}
|
|
1036
|
+
function saveRun(run) {
|
|
1037
|
+
run.updatedAt = timestamp();
|
|
1038
|
+
writeJson(runPath(run.id), run);
|
|
1039
|
+
}
|
|
1040
|
+
function listRuns() {
|
|
1041
|
+
ensureDirs();
|
|
1042
|
+
return readdirSync(RUNS_DIR, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => readJson(runPath(entry.name))).filter((value) => !!value).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
1043
|
+
}
|
|
1044
|
+
function createRun(cwd, harness, model, effort) {
|
|
1045
|
+
ensureDirs();
|
|
1046
|
+
const id = `run-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
1047
|
+
const run = {
|
|
1048
|
+
id,
|
|
1049
|
+
cwd,
|
|
1050
|
+
harness: harness.id,
|
|
1051
|
+
harnessName: harness.displayName,
|
|
1052
|
+
sessions: {
|
|
1053
|
+
zmx: `godmode-agent-${harness.id}-${id.replace(/^run-/, "")}`
|
|
1054
|
+
},
|
|
1055
|
+
model: model ?? null,
|
|
1056
|
+
effort: effort ?? null,
|
|
1057
|
+
status: "idle",
|
|
1058
|
+
createdAt: timestamp(),
|
|
1059
|
+
updatedAt: timestamp(),
|
|
1060
|
+
lastTurn: 0
|
|
1061
|
+
};
|
|
1062
|
+
mkdirSync(runDir(id), { recursive: true });
|
|
1063
|
+
mkdirSync(resolve4(runDir(id), "turns"), { recursive: true });
|
|
1064
|
+
saveRun(run);
|
|
1065
|
+
saveActiveRun(cwd, id);
|
|
1066
|
+
return run;
|
|
1067
|
+
}
|
|
1068
|
+
function buildShellCommand(harness, run, turn, prompt, passthroughArgs) {
|
|
1069
|
+
const dir = turnDir(run.id, turn);
|
|
1070
|
+
mkdirSync(dir, { recursive: true });
|
|
1071
|
+
const stdoutPath = resolve4(dir, "stdout.log");
|
|
1072
|
+
const stderrPath = resolve4(dir, "stderr.log");
|
|
1073
|
+
const exitCodePath = resolve4(dir, "exit-code.txt");
|
|
1074
|
+
const sessionDir = resolve4(runDir(run.id), "pi-session");
|
|
1075
|
+
mkdirSync(sessionDir, { recursive: true });
|
|
1076
|
+
const args = harness.buildTurnArgs({
|
|
1077
|
+
prompt,
|
|
1078
|
+
model: run.model ?? void 0,
|
|
1079
|
+
effort: run.effort ?? void 0,
|
|
1080
|
+
passthroughArgs,
|
|
1081
|
+
resumeToken: run.sessions[run.harness] ?? void 0,
|
|
1082
|
+
sessionDir
|
|
1083
|
+
});
|
|
1084
|
+
const command = `${[harness.command, ...args].map(shellEscape).join(" ")} > ${shellEscape(stdoutPath)} 2> ${shellEscape(stderrPath)}`;
|
|
1085
|
+
const shell = `${command}; code=$?; printf '%s' "$code" > ${shellEscape(exitCodePath)}; exit "$code"`;
|
|
1086
|
+
return { shell, stdoutPath, stderrPath, exitCodePath };
|
|
1087
|
+
}
|
|
1088
|
+
function waitForFile(path, timeoutMs = 3e5) {
|
|
1089
|
+
const started = Date.now();
|
|
1090
|
+
while (!existsSync(path)) {
|
|
1091
|
+
if (Date.now() - started > timeoutMs) throw new Error(`Timed out waiting for ${path}`);
|
|
1092
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
function latestTurn(run) {
|
|
1096
|
+
if (run.lastTurn < 1) throw new Error(`Run ${run.id} has no turns yet.`);
|
|
1097
|
+
const turn = readJson(resolve4(turnDir(run.id, run.lastTurn), "normalized.json"));
|
|
1098
|
+
if (!turn) throw new Error(`Turn ${run.lastTurn} not found for ${run.id}`);
|
|
1099
|
+
return turn;
|
|
1100
|
+
}
|
|
1101
|
+
function resolveRunId(cwd, explicit) {
|
|
1102
|
+
if (explicit) return explicit;
|
|
1103
|
+
const active = activeRunIdForCwd(cwd);
|
|
1104
|
+
if (!active) throw new Error(`No active coding-agent run for ${cwd}`);
|
|
1105
|
+
return active;
|
|
1106
|
+
}
|
|
1107
|
+
function renderTurn(turn, outputMode) {
|
|
1108
|
+
if (outputMode === "assistant-text") return turn.assistant.text;
|
|
1109
|
+
if (outputMode === "raw") {
|
|
1110
|
+
const stdout = existsSync2(turn.paths.stdout) ? readFileSync2(turn.paths.stdout, "utf-8") : "";
|
|
1111
|
+
const stderr = existsSync2(turn.paths.stderr) ? readFileSync2(turn.paths.stderr, "utf-8") : "";
|
|
1112
|
+
return JSON.stringify({ stdout, stderr }, null, 2);
|
|
1113
|
+
}
|
|
1114
|
+
if (outputMode === "events") {
|
|
1115
|
+
return existsSync2(turn.paths.events) ? readFileSync2(turn.paths.events, "utf-8").trimEnd() : "";
|
|
1116
|
+
}
|
|
1117
|
+
return JSON.stringify(turn, null, 2);
|
|
1118
|
+
}
|
|
1119
|
+
function renderStatus(run) {
|
|
1120
|
+
return JSON.stringify(run, null, 2);
|
|
1121
|
+
}
|
|
1122
|
+
function writeEvents(path, events) {
|
|
1123
|
+
writeFileSyncSafe(path, `${events.map((event2) => JSON.stringify(event2)).join("\n")}
|
|
1124
|
+
`);
|
|
1125
|
+
}
|
|
1126
|
+
function writeFileSyncSafe(path, content) {
|
|
1127
|
+
writeFileSync2(path, content);
|
|
1128
|
+
}
|
|
1129
|
+
function attachHelp() {
|
|
1130
|
+
return [
|
|
1131
|
+
"Usage: godmode agent attach run <id>",
|
|
1132
|
+
" or: godmode agent attach session <zmx-session-id>"
|
|
1133
|
+
].join("\n");
|
|
1134
|
+
}
|
|
1135
|
+
var AgentHelp = class extends HelpPage {
|
|
1136
|
+
usage() {
|
|
1137
|
+
return [
|
|
1138
|
+
"godmode agent start [--harness <name>] [--model <id>] <prompt>",
|
|
1139
|
+
"godmode agent send [--harness <name>] [--model <id>] <prompt>",
|
|
1140
|
+
"godmode agent attach run <id>",
|
|
1141
|
+
"godmode agent attach session <session-id>",
|
|
1142
|
+
"godmode agent output [id] [--json|--assistant-text|--events|--raw]",
|
|
1143
|
+
"godmode agent status [id]",
|
|
1144
|
+
"godmode agent list"
|
|
1145
|
+
];
|
|
1146
|
+
}
|
|
1147
|
+
sections() {
|
|
1148
|
+
return [
|
|
1149
|
+
{ title: "Options:", rows: [
|
|
1150
|
+
[" --harness <name>", "coding agent harness (claude, codex, gemini, etc.)"],
|
|
1151
|
+
[" --model <id>", "model identifier passed to the harness"],
|
|
1152
|
+
[" --effort <level>", "effort level passed to the harness"],
|
|
1153
|
+
[" --json", "output machine-readable events"],
|
|
1154
|
+
[" --follow", "stream output until the run completes"]
|
|
1155
|
+
] },
|
|
1156
|
+
{ title: "Shortcuts:", rows: [
|
|
1157
|
+
["godmode agent <prompt>", "send prompt to active run, or start a new run"]
|
|
1158
|
+
] }
|
|
1159
|
+
];
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
function agentHelp() {
|
|
1163
|
+
const lines = [];
|
|
1164
|
+
const origLog = console.log;
|
|
1165
|
+
console.log = (...args) => {
|
|
1166
|
+
lines.push(args.join(" "));
|
|
1167
|
+
};
|
|
1168
|
+
try {
|
|
1169
|
+
new AgentHelp().render();
|
|
1170
|
+
} finally {
|
|
1171
|
+
console.log = origLog;
|
|
1172
|
+
}
|
|
1173
|
+
return lines.join("\n");
|
|
1174
|
+
}
|
|
1175
|
+
function followOutput(run, turn, outputMode, writer) {
|
|
1176
|
+
const dir = turnDir(run.id, turn);
|
|
1177
|
+
const stdoutPath = resolve22(dir, "stdout.log");
|
|
1178
|
+
const stderrPath = resolve22(dir, "stderr.log");
|
|
1179
|
+
const statePath = runPath(run.id);
|
|
1180
|
+
let assistant = "";
|
|
1181
|
+
const emit = (event2) => {
|
|
1182
|
+
if (outputMode === "events") writer.write(`${JSON.stringify(event2)}
|
|
1183
|
+
`);
|
|
1184
|
+
if (outputMode === "assistant-text") {
|
|
1185
|
+
if (event2.type === "assistant.delta" && event2.text) writer.write(event2.text);
|
|
1186
|
+
if (event2.type === "assistant.completed" && !assistant && event2.text) writer.write(event2.text);
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
streamFile(stdoutPath, (line) => {
|
|
1190
|
+
const normalized = normalizeTurn(run.harness, run.id, turn, `${line}
|
|
1191
|
+
`, "", timestamp());
|
|
1192
|
+
for (const event2 of normalized.events) {
|
|
1193
|
+
if (event2.type === "assistant.delta" && event2.text) assistant += event2.text;
|
|
1194
|
+
emit(event2);
|
|
1195
|
+
}
|
|
1196
|
+
}, () => (readJson2(statePath)?.status ?? "completed") === "running");
|
|
1197
|
+
if (outputMode === "assistant-text") writer.write("\n");
|
|
1198
|
+
if (outputMode === "events" && existsSync2(stderrPath)) {
|
|
1199
|
+
for (const line of readFileSync2(stderrPath, "utf-8").split(/\r?\n/).filter(Boolean)) {
|
|
1200
|
+
writer.write(`${JSON.stringify({ ts: timestamp(), id: run.id, turn, type: "warning", message: line })}
|
|
1201
|
+
`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
function streamFile(path, onLine, until) {
|
|
1206
|
+
let offset = 0;
|
|
1207
|
+
let pending = "";
|
|
1208
|
+
while (true) {
|
|
1209
|
+
if (existsSync2(path)) {
|
|
1210
|
+
const size = statSync(path).size;
|
|
1211
|
+
if (size > offset) {
|
|
1212
|
+
const fd = openSync(path, "r");
|
|
1213
|
+
const buffer = Buffer.alloc(size - offset);
|
|
1214
|
+
try {
|
|
1215
|
+
const bytes = buffer.length ? readSync(fd, buffer, 0, buffer.length, offset) : 0;
|
|
1216
|
+
offset += bytes;
|
|
1217
|
+
pending += buffer.toString("utf-8", 0, bytes);
|
|
1218
|
+
const lines = pending.split(/\r?\n/);
|
|
1219
|
+
pending = lines.pop() ?? "";
|
|
1220
|
+
for (const line of lines) if (line) onLine(line);
|
|
1221
|
+
} finally {
|
|
1222
|
+
closeSync(fd);
|
|
1223
|
+
}
|
|
478
1224
|
}
|
|
479
1225
|
}
|
|
480
|
-
|
|
481
|
-
|
|
1226
|
+
if (!until()) {
|
|
1227
|
+
if (pending) onLine(pending);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
function readJson2(path) {
|
|
1234
|
+
try {
|
|
1235
|
+
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
1236
|
+
} catch {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function defaultExecutor(command, args, stdio = "pipe") {
|
|
1241
|
+
const spawnStdio = stdio === "inherit" ? "inherit" : "pipe";
|
|
1242
|
+
const result = spawnSync(command, args, { encoding: "utf-8", stdio: spawnStdio });
|
|
1243
|
+
return {
|
|
1244
|
+
status: result.status,
|
|
1245
|
+
stdout: stdio === "pipe" && typeof result.stdout === "string" ? result.stdout : "",
|
|
1246
|
+
stderr: stdio === "pipe" && typeof result.stderr === "string" ? result.stderr : "",
|
|
1247
|
+
error: result.error ?? void 0
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
function parseOutputMode(args) {
|
|
1251
|
+
let outputMode = "json";
|
|
1252
|
+
let follow = false;
|
|
1253
|
+
const remaining = [];
|
|
1254
|
+
for (const arg of args) {
|
|
1255
|
+
if (arg === "--json") outputMode = "json";
|
|
1256
|
+
else if (arg === "--assistant-text") outputMode = "assistant-text";
|
|
1257
|
+
else if (arg === "--events") outputMode = "events";
|
|
1258
|
+
else if (arg === "--raw") outputMode = "raw";
|
|
1259
|
+
else if (arg === "--follow") follow = true;
|
|
1260
|
+
else remaining.push(arg);
|
|
1261
|
+
}
|
|
1262
|
+
return { outputMode, follow, remaining };
|
|
1263
|
+
}
|
|
1264
|
+
function parseStartSendArgs(action, argv) {
|
|
1265
|
+
const { outputMode, remaining } = parseOutputMode(argv);
|
|
1266
|
+
let harness;
|
|
1267
|
+
let model;
|
|
1268
|
+
let effort;
|
|
1269
|
+
let passthroughArgs = [];
|
|
1270
|
+
const promptParts = [];
|
|
1271
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
1272
|
+
const arg = remaining[i];
|
|
1273
|
+
if (arg === "--") {
|
|
1274
|
+
passthroughArgs = remaining.slice(i + 1);
|
|
1275
|
+
break;
|
|
1276
|
+
}
|
|
1277
|
+
if (arg === "--harness") {
|
|
1278
|
+
const value = remaining[++i];
|
|
1279
|
+
if (!value || !(value in HARNESSES)) throw new Error("Missing or invalid value for --harness");
|
|
1280
|
+
harness = value;
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
if (arg === "--model") {
|
|
1284
|
+
model = remaining[++i];
|
|
1285
|
+
if (!model) throw new Error("Missing value for --model");
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
if (arg === "--effort") {
|
|
1289
|
+
effort = remaining[++i];
|
|
1290
|
+
if (!effort) throw new Error("Missing value for --effort");
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
if (arg === "--help" || arg === "-h") throw new Error(agentHelp());
|
|
1294
|
+
if (arg.startsWith("-")) throw new Error(`Unknown flag: ${arg}. Use -- to pass flags through to the native harness.`);
|
|
1295
|
+
promptParts.push(arg);
|
|
1296
|
+
}
|
|
1297
|
+
return { action, prompt: promptParts.join(" ").trim(), harness, model, effort, passthroughArgs, outputMode };
|
|
1298
|
+
}
|
|
1299
|
+
function parseOutputArgs(argv) {
|
|
1300
|
+
const { outputMode, follow, remaining } = parseOutputMode(argv);
|
|
1301
|
+
return { outputMode, follow, id: remaining.find((arg) => !arg.startsWith("-")) };
|
|
1302
|
+
}
|
|
1303
|
+
function classifyStatus(exitCode, stdout, stderr) {
|
|
1304
|
+
const text = `${stdout}
|
|
1305
|
+
${stderr}`.toLowerCase();
|
|
1306
|
+
if (exitCode === 0) return "completed";
|
|
1307
|
+
if (text.includes("auth") || text.includes("api key") || text.includes("permission") || text.includes("may not exist")) return "blocked";
|
|
1308
|
+
return "failed";
|
|
1309
|
+
}
|
|
1310
|
+
function resolveRunAndHarness(parsed, executor, cwd, options) {
|
|
1311
|
+
const settings = options.settings ?? loadSettings(cwd);
|
|
1312
|
+
const plugin = settings.plugins?.["coding-agents"];
|
|
1313
|
+
const requestedHarnessId = parsed.harness ?? plugin?.harness;
|
|
1314
|
+
let model = parsed.model ?? plugin?.model;
|
|
1315
|
+
let effort = parsed.effort ?? plugin?.effort;
|
|
1316
|
+
let run;
|
|
1317
|
+
let harness;
|
|
1318
|
+
if (parsed.action === "start") {
|
|
1319
|
+
harness = detectHarness(executor, requestedHarnessId);
|
|
1320
|
+
if (effort && harness.effortFlags.length === 0) throw new Error(`Harness "${harness.id}" does not support --effort.`);
|
|
1321
|
+
run = createRun(cwd, harness, model, effort);
|
|
1322
|
+
} else {
|
|
1323
|
+
const active = activeRunIdForCwd(cwd);
|
|
1324
|
+
if (active) {
|
|
1325
|
+
run = loadRun(active);
|
|
1326
|
+
if (parsed.harness && parsed.harness !== run.harness) throw new Error(`Active run uses harness ${run.harness}; start a new run for ${parsed.harness}.`);
|
|
1327
|
+
harness = detectHarness(executor, run.harness);
|
|
1328
|
+
model = parsed.model ?? run.model ?? plugin?.model ?? void 0;
|
|
1329
|
+
effort = parsed.effort ?? run.effort ?? plugin?.effort ?? void 0;
|
|
1330
|
+
if (effort && harness.effortFlags.length === 0) throw new Error(`Harness "${harness.id}" does not support --effort.`);
|
|
1331
|
+
run.model = model ?? run.model;
|
|
1332
|
+
run.effort = effort ?? run.effort;
|
|
1333
|
+
} else {
|
|
1334
|
+
harness = detectHarness(executor, requestedHarnessId);
|
|
1335
|
+
if (effort && harness.effortFlags.length === 0) throw new Error(`Harness "${harness.id}" does not support --effort.`);
|
|
1336
|
+
run = createRun(cwd, harness, model, effort);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
run.status = "running";
|
|
1340
|
+
run.model = model ?? run.model;
|
|
1341
|
+
run.effort = effort ?? run.effort;
|
|
1342
|
+
run.lastTurn += 1;
|
|
1343
|
+
saveRun(run);
|
|
1344
|
+
saveActiveRun(cwd, run.id);
|
|
1345
|
+
return { run, harness };
|
|
1346
|
+
}
|
|
1347
|
+
function executeTurn(parsed, options) {
|
|
1348
|
+
const executor = options.executor ?? defaultExecutor;
|
|
1349
|
+
const writer = options.writer ?? process.stdout;
|
|
1350
|
+
const errorWriter = options.errorWriter ?? process.stderr;
|
|
1351
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1352
|
+
const zmxCheck = executor("zmx", ["--help"]);
|
|
1353
|
+
if (zmxCheck.error || zmxCheck.status !== 0) {
|
|
1354
|
+
errorWriter.write("zmx not found. Install zmx to use coding agents.\n");
|
|
1355
|
+
return 2;
|
|
1356
|
+
}
|
|
1357
|
+
const { run, harness } = resolveRunAndHarness(parsed, executor, cwd, options);
|
|
1358
|
+
const { shell, stdoutPath, stderrPath, exitCodePath } = buildShellCommand(harness, run, run.lastTurn, parsed.prompt, parsed.passthroughArgs);
|
|
1359
|
+
const launch = executor("zmx", ["run", run.sessions.zmx, "sh", "-lc", shell], "ignore");
|
|
1360
|
+
if (launch.error || launch.status !== 0) {
|
|
1361
|
+
run.status = "failed";
|
|
1362
|
+
saveRun(run);
|
|
1363
|
+
errorWriter.write(`Failed to queue turn in zmx session ${run.sessions.zmx}.
|
|
1364
|
+
`);
|
|
1365
|
+
return 3;
|
|
1366
|
+
}
|
|
1367
|
+
waitForFile(exitCodePath);
|
|
1368
|
+
const exitCode = Number(readFileSync3(exitCodePath, "utf-8").trim() || "1");
|
|
1369
|
+
const stdout = existsSync3(stdoutPath) ? readFileSync3(stdoutPath, "utf-8") : "";
|
|
1370
|
+
const stderr = existsSync3(stderrPath) ? readFileSync3(stderrPath, "utf-8") : "";
|
|
1371
|
+
const normalized = normalizeTurn(run.harness, run.id, run.lastTurn, stdout, stderr, (/* @__PURE__ */ new Date()).toISOString());
|
|
1372
|
+
const turnStatus = classifyStatus(exitCode, stdout, stderr);
|
|
1373
|
+
const turn = {
|
|
1374
|
+
id: run.id,
|
|
1375
|
+
turn: run.lastTurn,
|
|
1376
|
+
status: turnStatus,
|
|
1377
|
+
prompt: parsed.prompt,
|
|
1378
|
+
harness: run.harness,
|
|
1379
|
+
sessions: {
|
|
1380
|
+
zmx: run.sessions.zmx,
|
|
1381
|
+
...normalized.sessionId || run.sessions[run.harness] ? { [run.harness]: normalized.sessionId ?? run.sessions[run.harness] } : {}
|
|
1382
|
+
},
|
|
1383
|
+
assistant: { text: normalized.assistantText },
|
|
1384
|
+
usage: normalized.usage,
|
|
1385
|
+
timing: normalized.timing,
|
|
1386
|
+
paths: {
|
|
1387
|
+
stdout: stdoutPath,
|
|
1388
|
+
stderr: stderrPath,
|
|
1389
|
+
events: resolve3(turnDir(run.id, run.lastTurn), "events.jsonl")
|
|
1390
|
+
},
|
|
1391
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1392
|
+
};
|
|
1393
|
+
writeEvents(turn.paths.events, normalized.events);
|
|
1394
|
+
writeJson(resolve3(turnDir(run.id, run.lastTurn), "normalized.json"), turn);
|
|
1395
|
+
if (turn.sessions[run.harness]) run.sessions[run.harness] = turn.sessions[run.harness];
|
|
1396
|
+
run.status = turnStatus;
|
|
1397
|
+
saveRun(run);
|
|
1398
|
+
writer.write(`${renderTurn(turn, parsed.outputMode)}
|
|
1399
|
+
`);
|
|
1400
|
+
return turnStatus === "completed" ? 0 : 1;
|
|
1401
|
+
}
|
|
1402
|
+
function attachToTarget(argv, options) {
|
|
1403
|
+
const executor = options.executor ?? defaultExecutor;
|
|
1404
|
+
const errorWriter = options.errorWriter ?? process.stderr;
|
|
1405
|
+
if (argv.length < 2) {
|
|
1406
|
+
errorWriter.write(`${attachHelp()}
|
|
1407
|
+
`);
|
|
1408
|
+
return 1;
|
|
1409
|
+
}
|
|
1410
|
+
const session = argv[0] === "run" ? loadRun(argv[1]).sessions.zmx : argv[0] === "session" ? argv[1] : null;
|
|
1411
|
+
if (!session) {
|
|
1412
|
+
errorWriter.write(`${attachHelp()}
|
|
1413
|
+
`);
|
|
1414
|
+
return 1;
|
|
1415
|
+
}
|
|
1416
|
+
return executor("zmx", ["attach", session], "inherit").status ?? 1;
|
|
1417
|
+
}
|
|
1418
|
+
function outputCommand(argv, options) {
|
|
1419
|
+
const writer = options.writer ?? process.stdout;
|
|
1420
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1421
|
+
const parsed = parseOutputArgs(argv);
|
|
1422
|
+
const run = loadRun(resolveRunId(cwd, parsed.id));
|
|
1423
|
+
if (parsed.follow && run.status === "running") {
|
|
1424
|
+
followOutput(run, run.lastTurn, parsed.outputMode, writer);
|
|
1425
|
+
return 0;
|
|
1426
|
+
}
|
|
1427
|
+
writer.write(`${renderTurn(latestTurn(run), parsed.outputMode)}
|
|
1428
|
+
`);
|
|
1429
|
+
return 0;
|
|
1430
|
+
}
|
|
1431
|
+
function statusCommand(argv, options) {
|
|
1432
|
+
const writer = options.writer ?? process.stdout;
|
|
1433
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1434
|
+
writer.write(`${renderStatus(loadRun(resolveRunId(cwd, argv.find((arg) => !arg.startsWith("-")))))}
|
|
1435
|
+
`);
|
|
1436
|
+
return 0;
|
|
1437
|
+
}
|
|
1438
|
+
function listCommand(options) {
|
|
1439
|
+
const writer = options.writer ?? process.stdout;
|
|
1440
|
+
writer.write(`${JSON.stringify(listRuns(), null, 2)}
|
|
1441
|
+
`);
|
|
1442
|
+
return 0;
|
|
1443
|
+
}
|
|
1444
|
+
async function runAgentCommand(argv, options = {}) {
|
|
1445
|
+
ensureDirs();
|
|
1446
|
+
const writer = options.writer ?? process.stdout;
|
|
1447
|
+
const errorWriter = options.errorWriter ?? process.stderr;
|
|
1448
|
+
if (!argv.length) {
|
|
1449
|
+
writer.write(`${agentHelp()}
|
|
1450
|
+
`);
|
|
1451
|
+
return 0;
|
|
1452
|
+
}
|
|
1453
|
+
try {
|
|
1454
|
+
const [cmd, ...rest] = argv;
|
|
1455
|
+
if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
1456
|
+
writer.write(`${agentHelp()}
|
|
1457
|
+
`);
|
|
1458
|
+
return 0;
|
|
1459
|
+
}
|
|
1460
|
+
if (cmd === "start") return executeTurn(parseStartSendArgs("start", rest), options);
|
|
1461
|
+
if (cmd === "send") return executeTurn(parseStartSendArgs("send", rest), options);
|
|
1462
|
+
if (cmd === "attach") return attachToTarget(rest, options);
|
|
1463
|
+
if (cmd === "output") return outputCommand(rest, options);
|
|
1464
|
+
if (cmd === "status") return statusCommand(rest, options);
|
|
1465
|
+
if (cmd === "list") return listCommand(options);
|
|
1466
|
+
return executeTurn(parseStartSendArgs("send", argv), options);
|
|
1467
|
+
} catch (error) {
|
|
1468
|
+
errorWriter.write(`${error.message || error}
|
|
1469
|
+
`);
|
|
1470
|
+
return 1;
|
|
482
1471
|
}
|
|
483
1472
|
}
|
|
484
1473
|
|
|
485
1474
|
// src/index.ts
|
|
486
1475
|
loadEnv();
|
|
1476
|
+
var VALID_INTERFACES = /* @__PURE__ */ new Set(["api", "graphql", "mcp", "skill"]);
|
|
1477
|
+
var RESERVED_SLUGS = /* @__PURE__ */ new Set(["ext", "agent"]);
|
|
487
1478
|
async function main() {
|
|
488
1479
|
const args = process.argv.slice(2);
|
|
489
1480
|
if (!args.length || args.length === 1 && (args[0] === "-h" || args[0] === "--help")) {
|
|
490
1481
|
showHelp();
|
|
491
1482
|
return;
|
|
492
1483
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const { configWizard } = await import("./prompt-DCFFOQ7K.js");
|
|
496
|
-
await configWizard();
|
|
1484
|
+
if (args.length === 1 && (args[0] === "--version" || args[0] === "-v")) {
|
|
1485
|
+
await showVersion();
|
|
497
1486
|
return;
|
|
498
1487
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
1488
|
+
const extensionSlug = args[0];
|
|
1489
|
+
const rest = args.slice(1);
|
|
1490
|
+
if (extensionSlug === "ext") {
|
|
1491
|
+
await runExt(rest);
|
|
502
1492
|
return;
|
|
503
1493
|
}
|
|
504
|
-
if (
|
|
505
|
-
|
|
506
|
-
|
|
1494
|
+
if (extensionSlug === "agent") {
|
|
1495
|
+
await runAgent(rest);
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
const multi = installedExtension(extensionSlug);
|
|
1499
|
+
if (!multi) {
|
|
1500
|
+
process.stderr.write(`'${extensionSlug}' is not an installed extension.
|
|
1501
|
+
`);
|
|
1502
|
+
process.stderr.write(`Try 'godmode ext list' to see installed extensions.
|
|
1503
|
+
`);
|
|
1504
|
+
process.stderr.write(`Try 'godmode --help' for more information.
|
|
1505
|
+
`);
|
|
1506
|
+
process.exit(1);
|
|
1507
|
+
}
|
|
1508
|
+
const declared = declaredInterfaces(multi);
|
|
1509
|
+
const first = rest[0];
|
|
1510
|
+
if (!first || first === "--help" || first === "-h") {
|
|
1511
|
+
showExtensionOverview(multi);
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
if (first === "--version" || first === "-v") {
|
|
1515
|
+
showExtensionVersion(multi);
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (!VALID_INTERFACES.has(first)) {
|
|
1519
|
+
process.stderr.write(`Missing interface.
|
|
1520
|
+
`);
|
|
1521
|
+
process.stderr.write(`'${extensionSlug}' declares: ${declared.join(", ")}.
|
|
1522
|
+
`);
|
|
1523
|
+
process.stderr.write(`Try 'godmode ${extensionSlug} ${declared[0]} ${first}' or 'godmode ${extensionSlug} --help'.
|
|
1524
|
+
`);
|
|
1525
|
+
process.exit(1);
|
|
1526
|
+
}
|
|
1527
|
+
if (!declared.includes(first)) {
|
|
1528
|
+
process.stderr.write(`'${extensionSlug}' does not declare a '${first}' interface.
|
|
1529
|
+
`);
|
|
1530
|
+
process.stderr.write(`Declared: ${declared.join(", ")}.
|
|
1531
|
+
`);
|
|
1532
|
+
process.stderr.write(`Try 'godmode ${extensionSlug} --help' for more information.
|
|
1533
|
+
`);
|
|
1534
|
+
process.exit(1);
|
|
1535
|
+
}
|
|
1536
|
+
await runInterface(first, extensionSlug, rest.slice(1));
|
|
1537
|
+
}
|
|
1538
|
+
function installedExtension(name) {
|
|
1539
|
+
const extPath = resolve5(GODMODE_EXTENSIONS_DIR, `${name}.json`);
|
|
1540
|
+
if (!existsSync4(extPath)) return null;
|
|
1541
|
+
try {
|
|
1542
|
+
return JSON.parse(readFileSync5(extPath, "utf-8"));
|
|
1543
|
+
} catch {
|
|
1544
|
+
return null;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function declaredInterfaces(m) {
|
|
1548
|
+
return Object.keys(m.interfaces);
|
|
1549
|
+
}
|
|
1550
|
+
function showExtHelp() {
|
|
1551
|
+
console.log(`Install, inspect, and manage godmode extensions.
|
|
1552
|
+
|
|
1553
|
+
Usage: godmode ext <command> [args]
|
|
1554
|
+
|
|
1555
|
+
Commands:
|
|
1556
|
+
install <name|folder> Install an extension
|
|
1557
|
+
uninstall <name> Uninstall an extension
|
|
1558
|
+
update <name> Re-fetch spec, rebuild routes
|
|
1559
|
+
list Show installed extensions
|
|
1560
|
+
create Interactive manifest wizard`);
|
|
1561
|
+
}
|
|
1562
|
+
async function runExt(rest) {
|
|
1563
|
+
const cmd = rest[0];
|
|
1564
|
+
if (!cmd || cmd === "--help" || cmd === "-h") {
|
|
1565
|
+
showExtHelp();
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
if (cmd === "install") {
|
|
1569
|
+
const target = rest[1];
|
|
1570
|
+
if (!target) {
|
|
1571
|
+
console.error("Usage: godmode ext install <name|folder>");
|
|
1572
|
+
process.exit(1);
|
|
1573
|
+
}
|
|
1574
|
+
if (RESERVED_SLUGS.has(target)) {
|
|
1575
|
+
process.stderr.write(`'${target}' is a reserved extension slug and cannot be installed.
|
|
1576
|
+
`);
|
|
1577
|
+
process.stderr.write(`Reserved: ${[...RESERVED_SLUGS].join(", ")}.
|
|
1578
|
+
`);
|
|
507
1579
|
process.exit(1);
|
|
508
1580
|
}
|
|
509
|
-
await
|
|
1581
|
+
const { runAdd } = await import("./add-AJERMPEA.js");
|
|
1582
|
+
await runAdd(rest.slice(1));
|
|
510
1583
|
return;
|
|
511
1584
|
}
|
|
512
|
-
if (cmd === "
|
|
513
|
-
if (!
|
|
514
|
-
console.error("Usage: godmode
|
|
1585
|
+
if (cmd === "uninstall") {
|
|
1586
|
+
if (!rest[1]) {
|
|
1587
|
+
console.error("Usage: godmode ext uninstall <name>");
|
|
515
1588
|
process.exit(1);
|
|
516
1589
|
}
|
|
517
|
-
await removeApi(
|
|
1590
|
+
await removeApi(rest[1]);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
if (cmd === "update") {
|
|
1594
|
+
if (!rest[1]) {
|
|
1595
|
+
console.error("Usage: godmode ext update <name>");
|
|
1596
|
+
process.exit(1);
|
|
1597
|
+
}
|
|
1598
|
+
await updateApi(rest[1]);
|
|
518
1599
|
return;
|
|
519
1600
|
}
|
|
520
1601
|
if (cmd === "list") {
|
|
521
1602
|
await listApis();
|
|
522
1603
|
return;
|
|
523
1604
|
}
|
|
524
|
-
if (cmd === "
|
|
525
|
-
const {
|
|
526
|
-
await
|
|
1605
|
+
if (cmd === "create") {
|
|
1606
|
+
const { configWizard } = await import("./prompt-SWOWWAOH.js");
|
|
1607
|
+
await configWizard();
|
|
527
1608
|
return;
|
|
528
1609
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
1610
|
+
if (installedExtension(cmd)) {
|
|
1611
|
+
const multi = installedExtension(cmd);
|
|
1612
|
+
const ifaces = declaredInterfaces(multi);
|
|
1613
|
+
process.stderr.write(`'${cmd}' is an installed extension, not an ext command.
|
|
1614
|
+
|
|
1615
|
+
`);
|
|
1616
|
+
process.stderr.write(`Did you mean:
|
|
1617
|
+
`);
|
|
1618
|
+
process.stderr.write(` godmode ${cmd} --help
|
|
1619
|
+
`);
|
|
1620
|
+
for (const i of ifaces) {
|
|
1621
|
+
process.stderr.write(` godmode ${cmd} ${i} --help
|
|
1622
|
+
`);
|
|
1623
|
+
}
|
|
1624
|
+
process.stderr.write(`
|
|
1625
|
+
Try 'godmode ext --help' for more information.
|
|
1626
|
+
`);
|
|
1627
|
+
process.exit(1);
|
|
1628
|
+
}
|
|
1629
|
+
process.stderr.write(`Unknown ext command '${cmd}'.
|
|
1630
|
+
`);
|
|
1631
|
+
process.stderr.write(`Try 'godmode ext --help' for more information.
|
|
1632
|
+
`);
|
|
1633
|
+
process.exit(1);
|
|
1634
|
+
}
|
|
1635
|
+
async function runAgent(rest) {
|
|
1636
|
+
const code = await runAgentCommand(rest);
|
|
1637
|
+
if (code !== 0) process.exit(code);
|
|
1638
|
+
}
|
|
1639
|
+
async function runInterface(iface, extensionName, rest) {
|
|
1640
|
+
if (iface === "skill") {
|
|
1641
|
+
process.stderr.write(`Skill interface not yet implemented.
|
|
1642
|
+
`);
|
|
1643
|
+
process.exit(1);
|
|
1644
|
+
}
|
|
1645
|
+
if (rest.includes("--version") || rest.includes("-v")) {
|
|
1646
|
+
const multi2 = await loadMultiManifest(extensionName);
|
|
1647
|
+
showExtensionVersion(multi2);
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
const parsed = parseArgs(rest);
|
|
1651
|
+
const ifaceKey = iface;
|
|
1652
|
+
const multi = await loadMultiManifest(extensionName);
|
|
1653
|
+
const manifest = await loadManifest(extensionName, ifaceKey);
|
|
1654
|
+
const implicitMethodFilter = parsed.methodFilter || (parsed.explicitMethod ? parsed.method : void 0);
|
|
1655
|
+
if (parsed.help) {
|
|
1656
|
+
showApiHelp(manifest, extensionName, parsed.segments, parsed.filter, implicitMethodFilter, parsed.all, multi);
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
if (iface === "mcp" && !parsed.segments.length) {
|
|
1660
|
+
await runMcp(
|
|
1661
|
+
{ godmodeHome: GODMODE_HOME, loadManifest: (n) => loadManifest(n, "mcp") },
|
|
1662
|
+
[extensionName, ...rest]
|
|
1663
|
+
);
|
|
534
1664
|
return;
|
|
535
1665
|
}
|
|
536
|
-
if (
|
|
537
|
-
|
|
1666
|
+
if (!parsed.segments.length) {
|
|
1667
|
+
showApiHelp(manifest, extensionName, parsed.segments, parsed.filter, implicitMethodFilter, parsed.all, multi);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
if (iface === "api" && !parsed.explicitMethod) {
|
|
1671
|
+
process.stderr.write(`Missing HTTP method. Try: godmode ${extensionName} api GET ${parsed.segments.join(" ")}
|
|
1672
|
+
`);
|
|
1673
|
+
process.stderr.write(`Valid methods: GET, POST, PUT, PATCH, DELETE, HEAD.
|
|
1674
|
+
`);
|
|
1675
|
+
process.exit(1);
|
|
1676
|
+
}
|
|
1677
|
+
if (iface === "graphql") {
|
|
1678
|
+
const err = validateGraphQLFlags(parsed.method, parsed.query, parsed.body, extensionName);
|
|
538
1679
|
if (err) {
|
|
539
1680
|
process.stderr.write(err + "\n");
|
|
540
1681
|
process.exit(1);
|
|
@@ -564,7 +1705,6 @@ async function main() {
|
|
|
564
1705
|
headers: parsed.headers,
|
|
565
1706
|
query,
|
|
566
1707
|
body,
|
|
567
|
-
token: parsed.token,
|
|
568
1708
|
verbose: parsed.verbose,
|
|
569
1709
|
dryRun: parsed.dryRun
|
|
570
1710
|
});
|
|
@@ -572,14 +1712,23 @@ async function main() {
|
|
|
572
1712
|
}
|
|
573
1713
|
const match = matchRoute(manifest, parsed.segments, parsed.method);
|
|
574
1714
|
if (!match) {
|
|
1715
|
+
const HTTP_VERBS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
|
|
1716
|
+
const trailingVerb = parsed.segments.map((s) => s.toUpperCase()).find((s) => HTTP_VERBS.has(s));
|
|
1717
|
+
if (trailingVerb) {
|
|
1718
|
+
const rest2 = parsed.segments.filter((s) => s.toUpperCase() !== trailingVerb);
|
|
1719
|
+
process.stderr.write(`No route matching: ${parsed.segments.join(" ")}
|
|
1720
|
+
`);
|
|
1721
|
+
process.stderr.write(`Method goes first: try 'godmode ${extensionName} ${iface} ${trailingVerb} ${rest2.join(" ")}'
|
|
1722
|
+
`);
|
|
1723
|
+
process.exit(1);
|
|
1724
|
+
}
|
|
575
1725
|
process.stderr.write(`No ${parsed.method.toUpperCase()} route matching: ${parsed.segments.join(" ")}
|
|
576
1726
|
`);
|
|
577
1727
|
for (const m of ["get", "post", "put", "patch", "delete"]) {
|
|
578
1728
|
if (m === parsed.method) continue;
|
|
579
1729
|
const alt = matchRoute(manifest, parsed.segments, m);
|
|
580
1730
|
if (alt) {
|
|
581
|
-
|
|
582
|
-
process.stderr.write(` try: godmode ${apiName} ${parsed.segments.join(" ")} ${flag}
|
|
1731
|
+
process.stderr.write(` try: godmode ${extensionName} api ${m.toUpperCase()} ${parsed.segments.join(" ")}
|
|
583
1732
|
`);
|
|
584
1733
|
}
|
|
585
1734
|
}
|