@gh-symphony/cli 0.0.19 → 0.0.21
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 +30 -2
- package/dist/chunk-A67CMOYE.js +684 -0
- package/dist/{chunk-TILHWBP6.js → chunk-C67H3OUL.js} +239 -36
- package/dist/{chunk-RN2PACNV.js → chunk-JN3TQVFV.js} +721 -74
- package/dist/{chunk-GKENCODJ.js → chunk-KY6WKH66.js} +437 -101
- package/dist/{chunk-6CI3UUMH.js → chunk-MYVJ6HK4.js} +950 -1240
- package/dist/{chunk-M3IFVLQS.js → chunk-QEONJ5DZ.js} +978 -72
- package/dist/{chunk-H2YXSYOZ.js → chunk-S6VIK4FF.js} +59 -31
- package/dist/chunk-SXGT7LOF.js +1060 -0
- package/dist/{doctor-IYHCFXOZ.js → doctor-4HBRICHP.js} +102 -37
- package/dist/index.js +38 -17
- package/dist/{init-KZT6YNOH.js → init-HZ3JEDGQ.js} +7 -2
- package/dist/{project-DNALEWO3.js → project-25NQ4J4Y.js} +8 -6
- package/dist/{recover-C3V2QAUB.js → recover-L3MJHHDA.js} +4 -2
- package/dist/{repo-HDDE7OUI.js → repo-TDCWQR6P.js} +72 -14
- package/dist/{run-XI2S5Y4V.js → run-XJQ6BF7U.js} +4 -2
- package/dist/{setup-K4CYYJBF.js → setup-B2SVLW2R.js} +46 -8
- package/dist/{start-M6IQGRFO.js → start-I2CC7BLW.js} +6 -4
- package/dist/{upgrade-F4VE4XBS.js → upgrade-OJXPZRYE.js} +2 -2
- package/dist/{version-Y5RYNWMF.js → version-TBDCTKDO.js} +1 -1
- package/dist/worker-entry.js +522 -867
- package/dist/{workflow-TBIFY5MO.js → workflow-BLJH2HC3.js} +176 -10
- package/package.json +5 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../core/
|
|
3
|
+
// ../core/src/workflow/lifecycle.ts
|
|
4
4
|
var DEFAULT_WORKFLOW_LIFECYCLE = {
|
|
5
5
|
stateFieldName: "Status",
|
|
6
6
|
activeStates: ["Todo", "In Progress"],
|
|
@@ -21,8 +21,9 @@ function normalizeWorkflowState(state) {
|
|
|
21
21
|
return state.trim().toLowerCase();
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// ../core/
|
|
24
|
+
// ../core/src/workflow/config.ts
|
|
25
25
|
var DEFAULT_CODEX_COMMAND = "codex app-server";
|
|
26
|
+
var DEFAULT_CLAUDE_COMMAND = "claude";
|
|
26
27
|
var DEFAULT_AGENT_COMMAND = DEFAULT_CODEX_COMMAND;
|
|
27
28
|
var DEFAULT_HOOK_TIMEOUT_MS = 6e4;
|
|
28
29
|
var DEFAULT_POLL_INTERVAL_MS = 3e4;
|
|
@@ -84,6 +85,7 @@ var DEFAULT_WORKFLOW_DEFINITION = {
|
|
|
84
85
|
workspace: DEFAULT_WORKFLOW_WORKSPACE,
|
|
85
86
|
hooks: DEFAULT_WORKFLOW_HOOKS,
|
|
86
87
|
agent: DEFAULT_WORKFLOW_AGENT,
|
|
88
|
+
runtime: null,
|
|
87
89
|
codex: DEFAULT_WORKFLOW_CODEX,
|
|
88
90
|
lifecycle: DEFAULT_WORKFLOW_LIFECYCLE,
|
|
89
91
|
format: "default",
|
|
@@ -92,8 +94,20 @@ var DEFAULT_WORKFLOW_DEFINITION = {
|
|
|
92
94
|
hookPath: null,
|
|
93
95
|
maxConcurrentByState: {}
|
|
94
96
|
};
|
|
97
|
+
function resolveWorkflowRuntimeCommand(workflow) {
|
|
98
|
+
if (!workflow.runtime) {
|
|
99
|
+
return workflow.codex.command;
|
|
100
|
+
}
|
|
101
|
+
if (workflow.runtime.args.length === 0) {
|
|
102
|
+
return workflow.runtime.command;
|
|
103
|
+
}
|
|
104
|
+
return [workflow.runtime.command, ...workflow.runtime.args].join(" ");
|
|
105
|
+
}
|
|
106
|
+
function resolveWorkflowRuntimeTimeouts(workflow) {
|
|
107
|
+
return workflow.runtime?.timeouts ?? workflow.codex;
|
|
108
|
+
}
|
|
95
109
|
|
|
96
|
-
// ../core/
|
|
110
|
+
// ../core/src/workflow/parser.ts
|
|
97
111
|
function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
|
|
98
112
|
const compatibilityMode = options.compatibilityMode ?? "strict";
|
|
99
113
|
const frontMatterMatch = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
@@ -111,16 +125,39 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
|
|
|
111
125
|
const workspace = readObject(frontMatter, "workspace");
|
|
112
126
|
const hooks = readObject(frontMatter, "hooks");
|
|
113
127
|
const agent = readObject(frontMatter, "agent");
|
|
114
|
-
const
|
|
128
|
+
const runtimeNode = readOptionalRuntimeObject(frontMatter);
|
|
129
|
+
const hasRuntime = runtimeNode !== null;
|
|
130
|
+
const codex = hasRuntime ? readObject(frontMatter, "codex") : readRequiredObject(frontMatter, "codex");
|
|
115
131
|
const trackerKind = readRequiredString(tracker, "kind", env);
|
|
116
132
|
const activeStates = readStringList(tracker, "active_states") ?? DEFAULT_WORKFLOW_TRACKER.activeStates;
|
|
117
133
|
const terminalStates = readStringList(tracker, "terminal_states") ?? DEFAULT_WORKFLOW_TRACKER.terminalStates;
|
|
118
134
|
const blockerCheckStates = readStringList(tracker, "blocker_check_states") ?? DEFAULT_WORKFLOW_TRACKER.blockerCheckStates;
|
|
119
|
-
const maxConcurrentAgentsByState = readNumberMap(
|
|
120
|
-
|
|
135
|
+
const maxConcurrentAgentsByState = readNumberMap(
|
|
136
|
+
agent,
|
|
137
|
+
"max_concurrent_agents_by_state"
|
|
138
|
+
);
|
|
139
|
+
const runtime = hasRuntime ? parseRuntimeConfig(runtimeNode, env) : null;
|
|
140
|
+
const codexConfig = {
|
|
141
|
+
command: readOptionalString(codex, "command", env) ?? DEFAULT_AGENT_COMMAND,
|
|
142
|
+
approvalPolicy: readOptionalString(codex, "approval_policy", env),
|
|
143
|
+
threadSandbox: readOptionalString(codex, "thread_sandbox", env),
|
|
144
|
+
turnSandboxPolicy: readOptionalString(codex, "turn_sandbox_policy", env),
|
|
145
|
+
turnTimeoutMs: readOptionalIntegerLike(codex, "turn_timeout_ms") ?? DEFAULT_TURN_TIMEOUT_MS,
|
|
146
|
+
readTimeoutMs: readOptionalIntegerLike(codex, "read_timeout_ms") ?? DEFAULT_READ_TIMEOUT_MS,
|
|
147
|
+
stallTimeoutMs: readOptionalIntegerLike(codex, "stall_timeout_ms") ?? DEFAULT_STALL_TIMEOUT_MS
|
|
148
|
+
};
|
|
149
|
+
const agentCommand = resolveWorkflowRuntimeCommand({
|
|
150
|
+
runtime,
|
|
151
|
+
codex: codexConfig
|
|
152
|
+
});
|
|
121
153
|
const parsed = {
|
|
122
154
|
promptTemplate,
|
|
123
|
-
continuationGuidance: readOptionalWorkflowString(
|
|
155
|
+
continuationGuidance: readOptionalWorkflowString(
|
|
156
|
+
frontMatter,
|
|
157
|
+
"continuationGuidance",
|
|
158
|
+
"continuation_guidance",
|
|
159
|
+
env
|
|
160
|
+
),
|
|
124
161
|
tracker: {
|
|
125
162
|
kind: trackerKind,
|
|
126
163
|
endpoint: readOptionalString(tracker, "endpoint", env),
|
|
@@ -154,15 +191,8 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
|
|
|
154
191
|
maxTurns: readOptionalIntegerLike(agent, "max_turns") ?? DEFAULT_MAX_TURNS,
|
|
155
192
|
retryBaseDelayMs: readOptionalIntegerLike(agent, "retry_base_delay_ms") ?? DEFAULT_BASE_DELAY_MS
|
|
156
193
|
},
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
approvalPolicy: readOptionalString(codex, "approval_policy", env),
|
|
160
|
-
threadSandbox: readOptionalString(codex, "thread_sandbox", env),
|
|
161
|
-
turnSandboxPolicy: readOptionalString(codex, "turn_sandbox_policy", env),
|
|
162
|
-
turnTimeoutMs: readOptionalIntegerLike(codex, "turn_timeout_ms") ?? DEFAULT_TURN_TIMEOUT_MS,
|
|
163
|
-
readTimeoutMs: readOptionalIntegerLike(codex, "read_timeout_ms") ?? DEFAULT_READ_TIMEOUT_MS,
|
|
164
|
-
stallTimeoutMs: readOptionalIntegerLike(codex, "stall_timeout_ms") ?? DEFAULT_STALL_TIMEOUT_MS
|
|
165
|
-
},
|
|
194
|
+
runtime,
|
|
195
|
+
codex: codexConfig,
|
|
166
196
|
lifecycle: {
|
|
167
197
|
stateFieldName: readOptionalString(tracker, "state_field", env) ?? DEFAULT_WORKFLOW_TRACKER.stateFieldName,
|
|
168
198
|
activeStates,
|
|
@@ -171,7 +201,7 @@ function parseWorkflowMarkdown(markdown, env = process.env, options = {}) {
|
|
|
171
201
|
},
|
|
172
202
|
format: "front-matter",
|
|
173
203
|
githubProjectId: readOptionalString(tracker, "project_id", env),
|
|
174
|
-
agentCommand
|
|
204
|
+
agentCommand,
|
|
175
205
|
hookPath: readOptionalString(hooks, "after_create", env),
|
|
176
206
|
maxConcurrentByState: maxConcurrentAgentsByState
|
|
177
207
|
};
|
|
@@ -209,17 +239,25 @@ function parseBlock(lines, startIndex, indent) {
|
|
|
209
239
|
break;
|
|
210
240
|
}
|
|
211
241
|
if (lineIndent > indent) {
|
|
212
|
-
throw new Error(
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Invalid workflow front matter indentation near "${line.trim()}".`
|
|
244
|
+
);
|
|
213
245
|
}
|
|
214
246
|
const trimmed = line.trim();
|
|
215
247
|
if (trimmed.startsWith("- ")) {
|
|
216
248
|
if (collectionType === "object") {
|
|
217
|
-
throw new Error(
|
|
249
|
+
throw new Error(
|
|
250
|
+
"Cannot mix array and object values in workflow front matter."
|
|
251
|
+
);
|
|
218
252
|
}
|
|
219
253
|
collectionType = "array";
|
|
220
254
|
const itemText = trimmed.slice(2).trim();
|
|
221
255
|
if (itemText === "|" || itemText === "|-") {
|
|
222
|
-
const [multiline, nextIndex3] = parseMultilineScalar(
|
|
256
|
+
const [multiline, nextIndex3] = parseMultilineScalar(
|
|
257
|
+
lines,
|
|
258
|
+
index + 1,
|
|
259
|
+
indent + 2
|
|
260
|
+
);
|
|
223
261
|
arrayValues.push(multiline);
|
|
224
262
|
index = nextIndex3;
|
|
225
263
|
continue;
|
|
@@ -235,7 +273,9 @@ function parseBlock(lines, startIndex, indent) {
|
|
|
235
273
|
continue;
|
|
236
274
|
}
|
|
237
275
|
if (collectionType === "array") {
|
|
238
|
-
throw new Error(
|
|
276
|
+
throw new Error(
|
|
277
|
+
"Cannot mix object and array values in workflow front matter."
|
|
278
|
+
);
|
|
239
279
|
}
|
|
240
280
|
collectionType = "object";
|
|
241
281
|
const separatorIndex = trimmed.indexOf(":");
|
|
@@ -245,7 +285,11 @@ function parseBlock(lines, startIndex, indent) {
|
|
|
245
285
|
const key = trimmed.slice(0, separatorIndex).trim();
|
|
246
286
|
const remainder = trimmed.slice(separatorIndex + 1).trim();
|
|
247
287
|
if (remainder === "|" || remainder === "|-") {
|
|
248
|
-
const [multiline, nextIndex2] = parseMultilineScalar(
|
|
288
|
+
const [multiline, nextIndex2] = parseMultilineScalar(
|
|
289
|
+
lines,
|
|
290
|
+
index + 1,
|
|
291
|
+
indent + 2
|
|
292
|
+
);
|
|
249
293
|
objectValues[key] = multiline;
|
|
250
294
|
index = nextIndex2;
|
|
251
295
|
continue;
|
|
@@ -284,29 +328,131 @@ function countIndent(line) {
|
|
|
284
328
|
return line.match(/^ */)?.[0].length ?? 0;
|
|
285
329
|
}
|
|
286
330
|
function parseScalar(value) {
|
|
287
|
-
if (value === "null")
|
|
288
|
-
|
|
289
|
-
if (value === "
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (/^-?\d+$/.test(value))
|
|
294
|
-
return Number.parseInt(value, 10);
|
|
331
|
+
if (value === "null") return null;
|
|
332
|
+
if (value === "true") return true;
|
|
333
|
+
if (value === "false") return false;
|
|
334
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
335
|
+
return parseInlineArray(value);
|
|
336
|
+
}
|
|
337
|
+
if (/^-?\d+$/.test(value)) return Number.parseInt(value, 10);
|
|
295
338
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
296
339
|
return value.slice(1, -1);
|
|
297
340
|
}
|
|
298
341
|
return value;
|
|
299
342
|
}
|
|
300
|
-
function
|
|
343
|
+
function parseInlineArray(value) {
|
|
344
|
+
const inner = value.slice(1, -1).trim();
|
|
345
|
+
if (!inner) {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
return splitInlineArrayEntries(inner).map((entry) => parseScalar(entry));
|
|
349
|
+
}
|
|
350
|
+
function splitInlineArrayEntries(inner) {
|
|
351
|
+
const entries = [];
|
|
352
|
+
let current = "";
|
|
353
|
+
let quote = null;
|
|
354
|
+
for (const char of inner) {
|
|
355
|
+
if (quote) {
|
|
356
|
+
current += char;
|
|
357
|
+
if (char === quote) {
|
|
358
|
+
quote = null;
|
|
359
|
+
}
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (char === '"' || char === "'") {
|
|
363
|
+
quote = char;
|
|
364
|
+
current += char;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (char === ",") {
|
|
368
|
+
pushInlineArrayEntry(entries, current, "middle");
|
|
369
|
+
current = "";
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
current += char;
|
|
373
|
+
}
|
|
374
|
+
if (quote) {
|
|
375
|
+
throw new Error("Workflow front matter inline array has an unterminated string.");
|
|
376
|
+
}
|
|
377
|
+
pushInlineArrayEntry(entries, current, "end");
|
|
378
|
+
return entries;
|
|
379
|
+
}
|
|
380
|
+
function pushInlineArrayEntry(entries, entry, position) {
|
|
381
|
+
const trimmed = entry.trim();
|
|
382
|
+
if (!trimmed) {
|
|
383
|
+
const reason = position === "end" ? "has a trailing comma" : "contains an empty item";
|
|
384
|
+
throw new Error(`Workflow front matter inline array ${reason}.`);
|
|
385
|
+
}
|
|
386
|
+
entries.push(trimmed);
|
|
387
|
+
}
|
|
388
|
+
function parseRuntimeConfig(runtime, env) {
|
|
389
|
+
const kind = readRuntimeKind(runtime, env);
|
|
390
|
+
const isolation = readObject(runtime, "isolation", "runtime.isolation");
|
|
391
|
+
const auth = readObject(runtime, "auth", "runtime.auth");
|
|
392
|
+
const timeouts = readObject(runtime, "timeouts", "runtime.timeouts");
|
|
393
|
+
const configuredCommand = readOptionalString(runtime, "command", env);
|
|
394
|
+
const command = configuredCommand ?? defaultRuntimeCommand(kind);
|
|
395
|
+
if (!command) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
'Workflow front matter field "runtime.command" is required for runtime.kind "custom".'
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
kind,
|
|
402
|
+
command,
|
|
403
|
+
args: readRuntimeArgs(runtime),
|
|
404
|
+
isolation: {
|
|
405
|
+
bare: readOptionalBoolean(isolation, "bare", "runtime.isolation.bare") ?? false,
|
|
406
|
+
strictMcpConfig: readOptionalBoolean(
|
|
407
|
+
isolation,
|
|
408
|
+
"strict_mcp_config",
|
|
409
|
+
"runtime.isolation.strict_mcp_config"
|
|
410
|
+
) ?? false
|
|
411
|
+
},
|
|
412
|
+
auth: {
|
|
413
|
+
env: readOptionalString(auth, "env", env)
|
|
414
|
+
},
|
|
415
|
+
timeouts: {
|
|
416
|
+
turnTimeoutMs: readOptionalIntegerLike(timeouts, "turn_timeout_ms") ?? DEFAULT_TURN_TIMEOUT_MS,
|
|
417
|
+
readTimeoutMs: readOptionalIntegerLike(timeouts, "read_timeout_ms") ?? DEFAULT_READ_TIMEOUT_MS,
|
|
418
|
+
stallTimeoutMs: readOptionalIntegerLike(timeouts, "stall_timeout_ms") ?? DEFAULT_STALL_TIMEOUT_MS
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
function readRuntimeKind(runtime, env) {
|
|
423
|
+
const kind = readRequiredString(runtime, "kind", env);
|
|
424
|
+
if (kind === "codex-app-server" || kind === "claude-print" || kind === "custom") {
|
|
425
|
+
return kind;
|
|
426
|
+
}
|
|
427
|
+
throw new Error(
|
|
428
|
+
`Unsupported workflow runtime kind "${kind}". Supported values: codex-app-server, claude-print, custom.`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
function defaultRuntimeCommand(kind) {
|
|
432
|
+
if (kind === "claude-print") {
|
|
433
|
+
return DEFAULT_CLAUDE_COMMAND;
|
|
434
|
+
}
|
|
435
|
+
if (kind === "codex-app-server") {
|
|
436
|
+
return DEFAULT_AGENT_COMMAND;
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
function readObject(input, key, path = key) {
|
|
301
441
|
const value = input[key];
|
|
302
442
|
if (value === void 0 || value === null) {
|
|
303
443
|
return {};
|
|
304
444
|
}
|
|
305
445
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
306
|
-
throw new Error(`Workflow front matter field "${
|
|
446
|
+
throw new Error(`Workflow front matter field "${path}" must be an object.`);
|
|
307
447
|
}
|
|
308
448
|
return value;
|
|
309
449
|
}
|
|
450
|
+
function readOptionalRuntimeObject(input) {
|
|
451
|
+
if (input.runtime === void 0 || input.runtime === null) {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
return readObject(input, "runtime");
|
|
455
|
+
}
|
|
310
456
|
function readRequiredObject(input, key) {
|
|
311
457
|
if (!(key in input)) {
|
|
312
458
|
throw new Error(`Workflow front matter field "${key}" is required.`);
|
|
@@ -342,7 +488,31 @@ function readStringList(input, key) {
|
|
|
342
488
|
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
343
489
|
}
|
|
344
490
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
345
|
-
throw new Error(
|
|
491
|
+
throw new Error(
|
|
492
|
+
`Workflow front matter field "${key}" must be an array of strings or comma-separated string.`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
return value;
|
|
496
|
+
}
|
|
497
|
+
function readRuntimeArgs(input) {
|
|
498
|
+
const value = input.args;
|
|
499
|
+
if (value === void 0 || value === null) {
|
|
500
|
+
return [];
|
|
501
|
+
}
|
|
502
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
'Workflow front matter field "runtime.args" must be an array of strings.'
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
return value;
|
|
508
|
+
}
|
|
509
|
+
function readOptionalBoolean(input, key, path = key) {
|
|
510
|
+
const value = input[key];
|
|
511
|
+
if (value === void 0 || value === null) {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
if (typeof value !== "boolean") {
|
|
515
|
+
throw new Error(`Workflow front matter field "${path}" must be a boolean.`);
|
|
346
516
|
}
|
|
347
517
|
return value;
|
|
348
518
|
}
|
|
@@ -377,7 +547,9 @@ function readNumberMap(input, key) {
|
|
|
377
547
|
result[entryKey] = Number.parseInt(entryValue, 10);
|
|
378
548
|
continue;
|
|
379
549
|
}
|
|
380
|
-
throw new Error(
|
|
550
|
+
throw new Error(
|
|
551
|
+
`Workflow front matter field "${key}.${entryKey}" must be an integer.`
|
|
552
|
+
);
|
|
381
553
|
}
|
|
382
554
|
return result;
|
|
383
555
|
}
|
|
@@ -386,27 +558,39 @@ function resolveEnvironmentValue(value, env) {
|
|
|
386
558
|
if (value.startsWith("env:") && envTokenMatch) {
|
|
387
559
|
const resolved = env[envTokenMatch[1]];
|
|
388
560
|
if (!resolved) {
|
|
389
|
-
throw new Error(
|
|
561
|
+
throw new Error(
|
|
562
|
+
`Workflow front matter requires environment variable ${envTokenMatch[1]}.`
|
|
563
|
+
);
|
|
390
564
|
}
|
|
391
565
|
return resolved;
|
|
392
566
|
}
|
|
393
567
|
return value.replace(/\$\{([A-Z0-9_]+)\}/g, (_, name) => {
|
|
394
568
|
const resolved = env[name];
|
|
395
569
|
if (!resolved) {
|
|
396
|
-
throw new Error(
|
|
570
|
+
throw new Error(
|
|
571
|
+
`Workflow front matter requires environment variable ${name}.`
|
|
572
|
+
);
|
|
397
573
|
}
|
|
398
574
|
return resolved;
|
|
399
575
|
});
|
|
400
576
|
}
|
|
401
577
|
function matchOptionalSection(markdown, heading) {
|
|
402
578
|
const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
403
|
-
const pattern = new RegExp(
|
|
579
|
+
const pattern = new RegExp(
|
|
580
|
+
`## ${escapedHeading}\\n\\n([\\s\\S]*?)(?=\\n## |$)`
|
|
581
|
+
);
|
|
404
582
|
const match = markdown.match(pattern);
|
|
405
583
|
return match?.[1]?.trim() ?? null;
|
|
406
584
|
}
|
|
407
585
|
|
|
408
|
-
// ../core/
|
|
409
|
-
import {
|
|
586
|
+
// ../core/src/workflow/render.ts
|
|
587
|
+
import {
|
|
588
|
+
Liquid,
|
|
589
|
+
ParseError,
|
|
590
|
+
RenderError,
|
|
591
|
+
TokenizationError,
|
|
592
|
+
UndefinedVariableError
|
|
593
|
+
} from "liquidjs";
|
|
410
594
|
function buildPromptVariables(issue, options) {
|
|
411
595
|
return {
|
|
412
596
|
issue: {
|
|
@@ -459,7 +643,10 @@ function flattenVariables(obj, prefix = "") {
|
|
|
459
643
|
for (const [key, value] of Object.entries(obj)) {
|
|
460
644
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
461
645
|
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
462
|
-
for (const [nestedKey, nestedValue] of flattenVariables(
|
|
646
|
+
for (const [nestedKey, nestedValue] of flattenVariables(
|
|
647
|
+
value,
|
|
648
|
+
fullKey
|
|
649
|
+
)) {
|
|
463
650
|
result.set(nestedKey, nestedValue);
|
|
464
651
|
}
|
|
465
652
|
} else {
|
|
@@ -470,19 +657,22 @@ function flattenVariables(obj, prefix = "") {
|
|
|
470
657
|
}
|
|
471
658
|
function renderLegacyPrompt(template, variables) {
|
|
472
659
|
const flatVars = flattenVariables(variables);
|
|
473
|
-
return template.replace(
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
660
|
+
return template.replace(
|
|
661
|
+
/\{\{([a-zA-Z_][a-zA-Z0-9_.]*)\}\}/g,
|
|
662
|
+
(match, key) => {
|
|
663
|
+
const value = flatVars.get(key);
|
|
664
|
+
if (value === void 0) {
|
|
665
|
+
return match;
|
|
666
|
+
}
|
|
667
|
+
if (value === null) {
|
|
668
|
+
return "";
|
|
669
|
+
}
|
|
670
|
+
return String(value);
|
|
480
671
|
}
|
|
481
|
-
|
|
482
|
-
});
|
|
672
|
+
);
|
|
483
673
|
}
|
|
484
674
|
|
|
485
|
-
// ../core/
|
|
675
|
+
// ../core/src/contracts/status-surface.ts
|
|
486
676
|
var WORKFLOW_EXECUTION_PHASES = [
|
|
487
677
|
"planning",
|
|
488
678
|
"human-review",
|
|
@@ -506,7 +696,7 @@ function isSessionExitClassification(value) {
|
|
|
506
696
|
return typeof value === "string" && SESSION_EXIT_CLASSIFICATIONS.includes(value);
|
|
507
697
|
}
|
|
508
698
|
|
|
509
|
-
// ../core/
|
|
699
|
+
// ../core/src/contracts/run-attempt-phase.ts
|
|
510
700
|
var RUN_ATTEMPT_PHASES = [
|
|
511
701
|
"preparing_workspace",
|
|
512
702
|
"building_prompt",
|
|
@@ -524,7 +714,7 @@ function isRunAttemptPhase(value) {
|
|
|
524
714
|
return typeof value === "string" && RUN_ATTEMPT_PHASES.includes(value);
|
|
525
715
|
}
|
|
526
716
|
|
|
527
|
-
// ../core/
|
|
717
|
+
// ../core/src/contracts/orchestrator-channel.ts
|
|
528
718
|
function isRecord(value) {
|
|
529
719
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
530
720
|
}
|
|
@@ -616,7 +806,7 @@ function isOrchestratorChannelEvent(value) {
|
|
|
616
806
|
return false;
|
|
617
807
|
}
|
|
618
808
|
|
|
619
|
-
// ../core/
|
|
809
|
+
// ../core/src/workflow/loader.ts
|
|
620
810
|
import { createHash } from "crypto";
|
|
621
811
|
import { access, readFile, stat } from "fs/promises";
|
|
622
812
|
import { constants } from "fs";
|
|
@@ -683,7 +873,7 @@ function toWorkflowResolution(workflowPath, workflow, metadata) {
|
|
|
683
873
|
};
|
|
684
874
|
}
|
|
685
875
|
|
|
686
|
-
// ../core/
|
|
876
|
+
// ../core/src/workflow/exit-classification.ts
|
|
687
877
|
function classifySessionExit(params) {
|
|
688
878
|
if (params.userInputRequired) {
|
|
689
879
|
return "user-input-required";
|
|
@@ -706,7 +896,7 @@ function classifySessionExit(params) {
|
|
|
706
896
|
return "error";
|
|
707
897
|
}
|
|
708
898
|
|
|
709
|
-
// ../core/
|
|
899
|
+
// ../core/src/orchestration/retry-policy.ts
|
|
710
900
|
function calculateRetryDelay(attempt, options = {}) {
|
|
711
901
|
const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
|
|
712
902
|
const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
|
|
@@ -718,7 +908,100 @@ function scheduleRetryAt(now, attempt, options = {}) {
|
|
|
718
908
|
return new Date(now.getTime() + calculateRetryDelay(attempt, options));
|
|
719
909
|
}
|
|
720
910
|
|
|
721
|
-
// ../core/
|
|
911
|
+
// ../core/src/runtime/credentials.ts
|
|
912
|
+
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
913
|
+
var TOKEN_REUSE_WINDOW_MS = 60 * 1e3;
|
|
914
|
+
var CODEX_ENV_KEYS = [
|
|
915
|
+
"OPENAI_API_KEY",
|
|
916
|
+
"OPENAI_BASE_URL",
|
|
917
|
+
"OPENAI_ORG_ID",
|
|
918
|
+
"OPENAI_PROJECT"
|
|
919
|
+
];
|
|
920
|
+
function extractEnvForCodex(env) {
|
|
921
|
+
return pickRuntimeEnv(env, CODEX_ENV_KEYS);
|
|
922
|
+
}
|
|
923
|
+
function toAgentCredentialCacheEntry(brokerResponse, now = /* @__PURE__ */ new Date()) {
|
|
924
|
+
return {
|
|
925
|
+
env: brokerResponse.env,
|
|
926
|
+
expires_at: brokerResponse.expires_at,
|
|
927
|
+
cachedAt: now.toISOString()
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
function shouldReuseAgentCredentialCache(entry, now = /* @__PURE__ */ new Date()) {
|
|
931
|
+
if (Object.keys(entry.env).length === 0) {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
if (!entry.expires_at) {
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
const expiresAt = Date.parse(entry.expires_at);
|
|
938
|
+
if (Number.isNaN(expiresAt)) {
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
return expiresAt - now.getTime() > TOKEN_REUSE_WINDOW_MS;
|
|
942
|
+
}
|
|
943
|
+
async function readAgentCredentialCache(path, readFileImpl = readFile2) {
|
|
944
|
+
try {
|
|
945
|
+
return normalizeAgentCredentialCacheEntry(
|
|
946
|
+
JSON.parse(await readFileImpl(path, "utf8"))
|
|
947
|
+
);
|
|
948
|
+
} catch {
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
async function writeAgentCredentialCache(path, brokerResponse, writeFileImpl = writeFile, now = /* @__PURE__ */ new Date()) {
|
|
953
|
+
const entry = toAgentCredentialCacheEntry(brokerResponse, now);
|
|
954
|
+
await writeFileImpl(path, JSON.stringify(entry), "utf8");
|
|
955
|
+
return entry;
|
|
956
|
+
}
|
|
957
|
+
function pickRuntimeEnv(env, keys) {
|
|
958
|
+
const resolved = {};
|
|
959
|
+
for (const key of keys) {
|
|
960
|
+
const value = env[key];
|
|
961
|
+
if (value) {
|
|
962
|
+
resolved[key] = value;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return resolved;
|
|
966
|
+
}
|
|
967
|
+
function normalizeAgentCredentialCacheEntry(payload) {
|
|
968
|
+
if (!isRecord2(payload)) {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
if (!isRecord2(payload.env)) {
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
const env = Object.fromEntries(
|
|
975
|
+
Object.entries(payload.env).filter(
|
|
976
|
+
(entry) => typeof entry[1] === "string"
|
|
977
|
+
)
|
|
978
|
+
);
|
|
979
|
+
if (Object.keys(env).length === 0) {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
return {
|
|
983
|
+
env,
|
|
984
|
+
expires_at: typeof payload.expires_at === "string" ? payload.expires_at : void 0,
|
|
985
|
+
cachedAt: typeof payload.cachedAt === "string" ? payload.cachedAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
function isRecord2(value) {
|
|
989
|
+
return value !== null && typeof value === "object";
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// ../core/src/runtime/events.ts
|
|
993
|
+
var DEFAULT_AGENT_INPUT_REQUIRED_REASON = "turn_input_required: agent requires user input";
|
|
994
|
+
function buildAgentInputRequiredReason(prompt) {
|
|
995
|
+
if (typeof prompt === "string") {
|
|
996
|
+
const trimmedPrompt = prompt.trim();
|
|
997
|
+
if (trimmedPrompt) {
|
|
998
|
+
return `turn_input_required: ${trimmedPrompt}`;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return DEFAULT_AGENT_INPUT_REQUIRED_REASON;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// ../core/src/workspace/env-file.ts
|
|
722
1005
|
import { existsSync, readFileSync } from "fs";
|
|
723
1006
|
function readEnvFile(path) {
|
|
724
1007
|
if (!existsSync(path)) {
|
|
@@ -735,10 +1018,10 @@ function readEnvFile(path) {
|
|
|
735
1018
|
}, {});
|
|
736
1019
|
}
|
|
737
1020
|
|
|
738
|
-
// ../core/
|
|
1021
|
+
// ../core/src/workspace/safety.ts
|
|
739
1022
|
import { resolve } from "path";
|
|
740
1023
|
|
|
741
|
-
// ../core/
|
|
1024
|
+
// ../core/src/workspace/identity.ts
|
|
742
1025
|
import { resolve as resolve2, join } from "path";
|
|
743
1026
|
import { createHash as createHash2 } from "crypto";
|
|
744
1027
|
function deriveIssueWorkspaceKey(identity, issueIdentifier) {
|
|
@@ -766,12 +1049,14 @@ function resolveIssueWorkspaceDirectory(projectDirectory, workspaceKey) {
|
|
|
766
1049
|
const normalizedProjectDirectory = resolve2(projectDirectory);
|
|
767
1050
|
const candidate = resolve2(normalizedProjectDirectory, "issues", workspaceKey);
|
|
768
1051
|
if (!candidate.startsWith(`${normalizedProjectDirectory}/`)) {
|
|
769
|
-
throw new Error(
|
|
1052
|
+
throw new Error(
|
|
1053
|
+
"Issue workspace path escapes the configured project directory."
|
|
1054
|
+
);
|
|
770
1055
|
}
|
|
771
1056
|
return candidate;
|
|
772
1057
|
}
|
|
773
1058
|
|
|
774
|
-
// ../core/
|
|
1059
|
+
// ../core/src/workspace/hooks.ts
|
|
775
1060
|
import { spawn } from "child_process";
|
|
776
1061
|
var DEFAULT_HOOK_TIMEOUT_MS2 = 6e4;
|
|
777
1062
|
async function executeHook(options) {
|
|
@@ -906,10 +1191,20 @@ function normalizeHookCommand(command) {
|
|
|
906
1191
|
return command;
|
|
907
1192
|
}
|
|
908
1193
|
|
|
909
|
-
// ../core/
|
|
1194
|
+
// ../core/src/observability/snapshot-builder.ts
|
|
910
1195
|
function buildProjectSnapshot(input) {
|
|
911
|
-
const {
|
|
912
|
-
|
|
1196
|
+
const {
|
|
1197
|
+
project,
|
|
1198
|
+
activeRuns,
|
|
1199
|
+
allRuns,
|
|
1200
|
+
summary,
|
|
1201
|
+
lastTickAt,
|
|
1202
|
+
lastError,
|
|
1203
|
+
rateLimits
|
|
1204
|
+
} = input;
|
|
1205
|
+
const cumulativeTokenUsageByIssue = aggregateTokenUsageByIssue(
|
|
1206
|
+
allRuns ?? activeRuns
|
|
1207
|
+
);
|
|
913
1208
|
return {
|
|
914
1209
|
projectId: project.projectId,
|
|
915
1210
|
slug: project.slug,
|
|
@@ -941,7 +1236,10 @@ function buildProjectSnapshot(input) {
|
|
|
941
1236
|
lastEventAt: run.lastEventAt ?? null,
|
|
942
1237
|
executionPhase: run.executionPhase ?? null,
|
|
943
1238
|
runPhase: run.runPhase ?? null,
|
|
944
|
-
tokenUsage: attachCumulativeTokenUsage(
|
|
1239
|
+
tokenUsage: attachCumulativeTokenUsage(
|
|
1240
|
+
run.tokenUsage,
|
|
1241
|
+
cumulativeTokenUsageByIssue.get(run.issueId)
|
|
1242
|
+
)
|
|
945
1243
|
})),
|
|
946
1244
|
retryQueue: activeRuns.filter((run) => run.status === "retrying" && run.retryKind).map((run) => ({
|
|
947
1245
|
runId: run.runId,
|
|
@@ -1010,11 +1308,11 @@ function aggregateTokenUsage(runs, lastTickAt) {
|
|
|
1010
1308
|
return { inputTokens, outputTokens, totalTokens, secondsRunning };
|
|
1011
1309
|
}
|
|
1012
1310
|
|
|
1013
|
-
// ../core/
|
|
1014
|
-
import { readFile as
|
|
1311
|
+
// ../core/src/observability/fs-reader.ts
|
|
1312
|
+
import { readFile as readFile3, readdir } from "fs/promises";
|
|
1015
1313
|
async function readJsonFile(path) {
|
|
1016
1314
|
try {
|
|
1017
|
-
const raw = await
|
|
1315
|
+
const raw = await readFile3(path, "utf8");
|
|
1018
1316
|
return JSON.parse(raw);
|
|
1019
1317
|
} catch (error) {
|
|
1020
1318
|
if (isFileMissing(error)) {
|
|
@@ -1034,10 +1332,12 @@ async function safeReadDir(path) {
|
|
|
1034
1332
|
}
|
|
1035
1333
|
}
|
|
1036
1334
|
function isFileMissing(error) {
|
|
1037
|
-
return Boolean(
|
|
1335
|
+
return Boolean(
|
|
1336
|
+
error && typeof error === "object" && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
1337
|
+
);
|
|
1038
1338
|
}
|
|
1039
1339
|
|
|
1040
|
-
// ../core/
|
|
1340
|
+
// ../core/src/observability/event-formatter.ts
|
|
1041
1341
|
function formatEventMessage(event) {
|
|
1042
1342
|
switch (event.event) {
|
|
1043
1343
|
case "run-dispatched":
|
|
@@ -1064,6 +1364,8 @@ function formatEventMessage(event) {
|
|
|
1064
1364
|
return `Turn ${event.turnCount} completed in ${event.durationMs}ms`;
|
|
1065
1365
|
case "turn_failed":
|
|
1066
1366
|
return event.error ?? `Turn ${event.turnCount} failed`;
|
|
1367
|
+
case "session_invalidated":
|
|
1368
|
+
return event.reason;
|
|
1067
1369
|
default:
|
|
1068
1370
|
return null;
|
|
1069
1371
|
}
|
|
@@ -1102,9 +1404,11 @@ function parseRunEventLine(line) {
|
|
|
1102
1404
|
}
|
|
1103
1405
|
}
|
|
1104
1406
|
|
|
1105
|
-
// ../core/
|
|
1407
|
+
// ../core/src/observability/status-assembler.ts
|
|
1106
1408
|
function isMatchingIssueRun(run, projectId, issueId, issueIdentifier) {
|
|
1107
|
-
return Boolean(
|
|
1409
|
+
return Boolean(
|
|
1410
|
+
run && run.projectId === projectId && (run.issueId === issueId || run.issueIdentifier === issueIdentifier)
|
|
1411
|
+
);
|
|
1108
1412
|
}
|
|
1109
1413
|
function mapIssueOrchestrationStateToStatus(state) {
|
|
1110
1414
|
switch (state) {
|
|
@@ -1123,6 +1427,593 @@ function mapIssueOrchestrationStateToStatus(state) {
|
|
|
1123
1427
|
}
|
|
1124
1428
|
}
|
|
1125
1429
|
|
|
1430
|
+
// ../runtime-claude/src/preflight.ts
|
|
1431
|
+
import { execFileSync } from "child_process";
|
|
1432
|
+
import { constants as constants2 } from "fs";
|
|
1433
|
+
import { access as access2, readFile as readFile4 } from "fs/promises";
|
|
1434
|
+
import { isAbsolute, join as join2, resolve as resolve3 } from "path";
|
|
1435
|
+
var DEFAULT_DEPENDENCIES = {
|
|
1436
|
+
execFileSync,
|
|
1437
|
+
readFile: readFile4,
|
|
1438
|
+
access: access2,
|
|
1439
|
+
fetchImpl: fetch,
|
|
1440
|
+
platform: process.platform
|
|
1441
|
+
};
|
|
1442
|
+
var CREDENTIAL_BROKER_TIMEOUT_MS = 5e3;
|
|
1443
|
+
async function runClaudePreflight(options, dependencies = {}) {
|
|
1444
|
+
const deps = { ...DEFAULT_DEPENDENCIES, ...dependencies };
|
|
1445
|
+
const env = options.env ?? process.env;
|
|
1446
|
+
const command = resolveRuntimeCommandBinary(options.command) ?? "claude";
|
|
1447
|
+
const checks = [];
|
|
1448
|
+
checks.push(checkClaudeBinary(command, options.cwd, deps));
|
|
1449
|
+
checks.push(await checkAnthropicApiKey(env, options, deps));
|
|
1450
|
+
checks.push(await checkWorkspaceMcpConfig(options.cwd, deps));
|
|
1451
|
+
if (options.includeGhAuth) {
|
|
1452
|
+
checks.push(checkGhAuthentication(options.cwd, deps));
|
|
1453
|
+
}
|
|
1454
|
+
return {
|
|
1455
|
+
ok: checks.every((check) => check.status !== "fail"),
|
|
1456
|
+
checks
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
function formatClaudePreflightText(report) {
|
|
1460
|
+
const lines = ["Claude runtime preflight"];
|
|
1461
|
+
for (const check of report.checks) {
|
|
1462
|
+
const label = check.status === "pass" ? "PASS" : check.status === "warn" ? "WARN" : "FAIL";
|
|
1463
|
+
lines.push(`${label} ${check.title}`);
|
|
1464
|
+
lines.push(` ${check.summary}`);
|
|
1465
|
+
if (check.remediation) {
|
|
1466
|
+
lines.push(` Fix: ${check.remediation}`);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return lines.join("\n");
|
|
1470
|
+
}
|
|
1471
|
+
function pass(id, title, summary, details) {
|
|
1472
|
+
return { id, title, status: "pass", summary, details };
|
|
1473
|
+
}
|
|
1474
|
+
function warn(id, title, summary, remediation, details) {
|
|
1475
|
+
return { id, title, status: "warn", summary, remediation, details };
|
|
1476
|
+
}
|
|
1477
|
+
function fail(id, title, summary, remediation, details) {
|
|
1478
|
+
return { id, title, status: "fail", summary, remediation, details };
|
|
1479
|
+
}
|
|
1480
|
+
function checkClaudeBinary(command, cwd, deps) {
|
|
1481
|
+
const executable = resolveExecutableCommand(command, cwd);
|
|
1482
|
+
try {
|
|
1483
|
+
const locatedPath = locateClaudeBinary(executable, cwd, deps);
|
|
1484
|
+
const version = deps.execFileSync(executable, ["--version"], {
|
|
1485
|
+
encoding: "utf8",
|
|
1486
|
+
cwd,
|
|
1487
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1488
|
+
}).toString().trim();
|
|
1489
|
+
return pass(
|
|
1490
|
+
"claude_binary",
|
|
1491
|
+
"Claude CLI binary",
|
|
1492
|
+
version ? `${executable} is available: ${version}.` : `${executable} is available, but --version returned an empty response.`,
|
|
1493
|
+
{ command: executable, path: locatedPath, version }
|
|
1494
|
+
);
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
return fail(
|
|
1497
|
+
"claude_binary",
|
|
1498
|
+
"Claude CLI binary",
|
|
1499
|
+
`${executable} could not be found or executed from PATH.`,
|
|
1500
|
+
`Install Claude Code and ensure '${executable}' is on PATH, then re-run the command.`,
|
|
1501
|
+
{
|
|
1502
|
+
command: executable,
|
|
1503
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
function resolveExecutableCommand(command, cwd) {
|
|
1509
|
+
if ((command.includes("/") || command.includes("\\")) && !isAbsolute(command)) {
|
|
1510
|
+
return resolve3(cwd, command);
|
|
1511
|
+
}
|
|
1512
|
+
return command;
|
|
1513
|
+
}
|
|
1514
|
+
function locateClaudeBinary(command, cwd, deps) {
|
|
1515
|
+
if (command.includes("/") || command.includes("\\")) {
|
|
1516
|
+
return command;
|
|
1517
|
+
}
|
|
1518
|
+
try {
|
|
1519
|
+
const locator = deps.platform === "win32" ? "where" : "which";
|
|
1520
|
+
return deps.execFileSync(locator, [command], {
|
|
1521
|
+
encoding: "utf8",
|
|
1522
|
+
cwd,
|
|
1523
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1524
|
+
}).toString().split(/\r?\n/).find((line) => line.trim())?.trim() ?? null;
|
|
1525
|
+
} catch {
|
|
1526
|
+
return null;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
function checkGhAuthentication(cwd, deps) {
|
|
1530
|
+
try {
|
|
1531
|
+
deps.execFileSync("gh", ["auth", "status"], {
|
|
1532
|
+
encoding: "utf8",
|
|
1533
|
+
cwd,
|
|
1534
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1535
|
+
});
|
|
1536
|
+
return pass(
|
|
1537
|
+
"gh_authentication",
|
|
1538
|
+
"GitHub CLI authentication",
|
|
1539
|
+
"gh auth status succeeded."
|
|
1540
|
+
);
|
|
1541
|
+
} catch (error) {
|
|
1542
|
+
return fail(
|
|
1543
|
+
"gh_authentication",
|
|
1544
|
+
"GitHub CLI authentication",
|
|
1545
|
+
"gh auth status failed or no GitHub login is configured.",
|
|
1546
|
+
"Run 'gh auth login --scopes repo,read:org,project' and re-run the command.",
|
|
1547
|
+
{ error: error instanceof Error ? error.message : String(error) }
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
async function checkAnthropicApiKey(env, options, deps) {
|
|
1552
|
+
if (env.ANTHROPIC_API_KEY?.trim()) {
|
|
1553
|
+
return pass(
|
|
1554
|
+
"anthropic_api_key",
|
|
1555
|
+
"Anthropic API key",
|
|
1556
|
+
"ANTHROPIC_API_KEY is configured in the environment.",
|
|
1557
|
+
{ source: "env" }
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
const brokerUrl = env.AGENT_CREDENTIAL_BROKER_URL?.trim();
|
|
1561
|
+
const brokerSecret = env.AGENT_CREDENTIAL_BROKER_SECRET?.trim();
|
|
1562
|
+
if (!brokerUrl || !brokerSecret) {
|
|
1563
|
+
return fail(
|
|
1564
|
+
"anthropic_api_key",
|
|
1565
|
+
"Anthropic API key",
|
|
1566
|
+
"Neither ANTHROPIC_API_KEY nor an agent credential broker is configured.",
|
|
1567
|
+
"Set ANTHROPIC_API_KEY or configure AGENT_CREDENTIAL_BROKER_URL and AGENT_CREDENTIAL_BROKER_SECRET.",
|
|
1568
|
+
{ source: "missing" }
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
if (options.probeCredentialBroker === false) {
|
|
1572
|
+
return pass(
|
|
1573
|
+
"anthropic_api_key",
|
|
1574
|
+
"Anthropic API key",
|
|
1575
|
+
"Agent credential broker configuration is present.",
|
|
1576
|
+
{ source: "broker", brokerUrl }
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
try {
|
|
1580
|
+
const response = await deps.fetchImpl(brokerUrl, {
|
|
1581
|
+
method: "POST",
|
|
1582
|
+
headers: {
|
|
1583
|
+
accept: "application/json",
|
|
1584
|
+
authorization: `Bearer ${brokerSecret}`
|
|
1585
|
+
},
|
|
1586
|
+
signal: AbortSignal.timeout(CREDENTIAL_BROKER_TIMEOUT_MS)
|
|
1587
|
+
});
|
|
1588
|
+
const payload = await response.json();
|
|
1589
|
+
if (response.ok && payload.env?.ANTHROPIC_API_KEY?.trim()) {
|
|
1590
|
+
return pass(
|
|
1591
|
+
"anthropic_api_key",
|
|
1592
|
+
"Anthropic API key",
|
|
1593
|
+
"Agent credential broker is reachable and returned ANTHROPIC_API_KEY.",
|
|
1594
|
+
{ source: "broker", brokerUrl }
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
return fail(
|
|
1598
|
+
"anthropic_api_key",
|
|
1599
|
+
"Anthropic API key",
|
|
1600
|
+
payload.error ? `Agent credential broker did not return ANTHROPIC_API_KEY: ${payload.error}.` : "Agent credential broker did not return ANTHROPIC_API_KEY.",
|
|
1601
|
+
"Set ANTHROPIC_API_KEY or configure the credential broker to return ANTHROPIC_API_KEY.",
|
|
1602
|
+
{ source: "broker", brokerUrl, status: response.status }
|
|
1603
|
+
);
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
return fail(
|
|
1606
|
+
"anthropic_api_key",
|
|
1607
|
+
"Anthropic API key",
|
|
1608
|
+
"Agent credential broker could not be reached.",
|
|
1609
|
+
"Set ANTHROPIC_API_KEY or fix AGENT_CREDENTIAL_BROKER_URL and AGENT_CREDENTIAL_BROKER_SECRET.",
|
|
1610
|
+
{
|
|
1611
|
+
source: "broker",
|
|
1612
|
+
brokerUrl,
|
|
1613
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1614
|
+
}
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
async function checkWorkspaceMcpConfig(cwd, deps) {
|
|
1619
|
+
const path = join2(cwd, ".mcp.json");
|
|
1620
|
+
try {
|
|
1621
|
+
await deps.access(path, constants2.R_OK);
|
|
1622
|
+
} catch (error) {
|
|
1623
|
+
const err = error;
|
|
1624
|
+
if (err.code === "ENOENT") {
|
|
1625
|
+
return warn(
|
|
1626
|
+
"claude_mcp_config",
|
|
1627
|
+
"Workspace .mcp.json",
|
|
1628
|
+
".mcp.json was not found in the workspace root. Claude can still run without a workspace MCP config.",
|
|
1629
|
+
void 0,
|
|
1630
|
+
{ path, reason: "missing" }
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
return warn(
|
|
1634
|
+
"claude_mcp_config",
|
|
1635
|
+
"Workspace .mcp.json",
|
|
1636
|
+
`.mcp.json exists but is not readable: ${err.message}.`,
|
|
1637
|
+
"Fix .mcp.json file permissions if this workspace needs Claude MCP servers.",
|
|
1638
|
+
{ path, reason: "unreadable", error: err.message }
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
try {
|
|
1642
|
+
const content = await deps.readFile(path, "utf8");
|
|
1643
|
+
JSON.parse(content);
|
|
1644
|
+
return pass(
|
|
1645
|
+
"claude_mcp_config",
|
|
1646
|
+
"Workspace .mcp.json",
|
|
1647
|
+
".mcp.json is readable and contains valid JSON.",
|
|
1648
|
+
{ path }
|
|
1649
|
+
);
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
return fail(
|
|
1652
|
+
"claude_mcp_config",
|
|
1653
|
+
"Workspace .mcp.json",
|
|
1654
|
+
`.mcp.json could not be parsed as JSON: ${error instanceof Error ? error.message : String(error)}.`,
|
|
1655
|
+
"Fix or remove the workspace root .mcp.json file, then re-run the command.",
|
|
1656
|
+
{ path, reason: "invalid_json" }
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
function isClaudeRuntimeCommand(command) {
|
|
1661
|
+
return resolveClaudeCommandBinary(command) != null;
|
|
1662
|
+
}
|
|
1663
|
+
function resolveClaudeCommandBinary(command) {
|
|
1664
|
+
const binary = resolveRuntimeCommandBinary(command);
|
|
1665
|
+
return binary != null && isClaudeBinaryName(binary) ? binary : null;
|
|
1666
|
+
}
|
|
1667
|
+
function resolveRuntimeCommandBinary(command) {
|
|
1668
|
+
const normalized = (command ?? "").trim();
|
|
1669
|
+
if (!normalized) {
|
|
1670
|
+
return null;
|
|
1671
|
+
}
|
|
1672
|
+
const tokens = tokenizeRuntimeCommand(normalized);
|
|
1673
|
+
if (tokens.length === 0) {
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
const first = stripClaudeCommandQuotes(tokens[0]);
|
|
1677
|
+
if ((first === "bash" || first === "sh" || first === "zsh" || first === "fish") && tokens.length >= 3) {
|
|
1678
|
+
const flagIndex = tokens.findIndex((token) => {
|
|
1679
|
+
const value = stripClaudeCommandQuotes(token);
|
|
1680
|
+
return value === "-c" || value === "-lc";
|
|
1681
|
+
});
|
|
1682
|
+
if (flagIndex >= 0 && flagIndex + 1 < tokens.length) {
|
|
1683
|
+
const shellCommand = stripClaudeCommandQuotes(tokens[flagIndex + 1]);
|
|
1684
|
+
return resolveShellCommandClaudeBinary(shellCommand) ?? resolveRuntimeCommandBinary(shellCommand);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return first;
|
|
1688
|
+
}
|
|
1689
|
+
function stripClaudeCommandQuotes(value) {
|
|
1690
|
+
return value.replace(/^['"]|['"]$/g, "");
|
|
1691
|
+
}
|
|
1692
|
+
function tokenizeRuntimeCommand(command) {
|
|
1693
|
+
return command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
1694
|
+
}
|
|
1695
|
+
function resolveShellCommandClaudeBinary(command) {
|
|
1696
|
+
for (const segment of command.split(/&&|\|\||[;\n]/g)) {
|
|
1697
|
+
const tokens = tokenizeRuntimeCommand(segment);
|
|
1698
|
+
for (const token of tokens) {
|
|
1699
|
+
const value = stripClaudeCommandQuotes(token);
|
|
1700
|
+
if (value.includes("=") && !value.startsWith("/") && !value.startsWith("./")) {
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
if (isClaudeBinaryName(value)) {
|
|
1704
|
+
return value;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return null;
|
|
1709
|
+
}
|
|
1710
|
+
function isClaudeBinaryName(command) {
|
|
1711
|
+
const normalized = (command.split(/[\\/]/).pop() ?? command).toLowerCase().replace(/\.(exe|cmd|bat)$/i, "");
|
|
1712
|
+
return normalized === "claude" || normalized === "claude-code";
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// ../runtime-claude/src/adapter.ts
|
|
1716
|
+
import { randomUUID } from "crypto";
|
|
1717
|
+
import { rm } from "fs/promises";
|
|
1718
|
+
import { join as join5 } from "path";
|
|
1719
|
+
|
|
1720
|
+
// ../runtime-claude/src/mcp-compose.ts
|
|
1721
|
+
import { mkdir, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
|
|
1722
|
+
import { basename, dirname, join as join3, resolve as resolve4 } from "path";
|
|
1723
|
+
|
|
1724
|
+
// ../tool-github-graphql/src/tool.ts
|
|
1725
|
+
import { readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
|
|
1726
|
+
var DEFAULT_GITHUB_GRAPHQL_API_URL = "https://api.github.com/graphql";
|
|
1727
|
+
var TOKEN_REUSE_WINDOW_MS2 = 60 * 1e3;
|
|
1728
|
+
async function executeGitHubGraphQL(invocation, config, fetchImpl = fetch) {
|
|
1729
|
+
const token = await resolveGitHubGraphQLToken(config, {
|
|
1730
|
+
fetchImpl
|
|
1731
|
+
});
|
|
1732
|
+
const response = await fetchImpl(
|
|
1733
|
+
config.apiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL,
|
|
1734
|
+
{
|
|
1735
|
+
method: "POST",
|
|
1736
|
+
headers: {
|
|
1737
|
+
"content-type": "application/json",
|
|
1738
|
+
authorization: `Bearer ${token}`
|
|
1739
|
+
},
|
|
1740
|
+
body: JSON.stringify(invocation)
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
const payload = await response.json();
|
|
1744
|
+
if (!response.ok) {
|
|
1745
|
+
throw new Error(
|
|
1746
|
+
`GitHub GraphQL request failed with status ${response.status}: ${JSON.stringify(payload)}`
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
if (payload.errors?.length) {
|
|
1750
|
+
throw new Error(payload.errors.map((error) => error.message).join("; "));
|
|
1751
|
+
}
|
|
1752
|
+
return payload;
|
|
1753
|
+
}
|
|
1754
|
+
async function resolveGitHubGraphQLToken(config, dependencies = {}) {
|
|
1755
|
+
if (config.token) {
|
|
1756
|
+
return config.token;
|
|
1757
|
+
}
|
|
1758
|
+
if (!config.tokenBrokerUrl || !config.tokenBrokerSecret) {
|
|
1759
|
+
throw new Error(
|
|
1760
|
+
"Either GITHUB_GRAPHQL_TOKEN or the runtime token broker configuration is required."
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
const now = dependencies.now ?? /* @__PURE__ */ new Date();
|
|
1764
|
+
const readFileImpl = dependencies.readFileImpl ?? readFile5;
|
|
1765
|
+
const writeFileImpl = dependencies.writeFileImpl ?? writeFile2;
|
|
1766
|
+
const cachedToken = config.tokenCachePath ? await readCachedToken(config.tokenCachePath, readFileImpl) : null;
|
|
1767
|
+
if (cachedToken && cachedToken.expiresAt.getTime() - now.getTime() > TOKEN_REUSE_WINDOW_MS2) {
|
|
1768
|
+
return cachedToken.token;
|
|
1769
|
+
}
|
|
1770
|
+
const fetchImpl = dependencies.fetchImpl ?? fetch;
|
|
1771
|
+
const response = await fetchImpl(config.tokenBrokerUrl, {
|
|
1772
|
+
method: "POST",
|
|
1773
|
+
headers: {
|
|
1774
|
+
accept: "application/json",
|
|
1775
|
+
authorization: `Bearer ${config.tokenBrokerSecret}`
|
|
1776
|
+
}
|
|
1777
|
+
});
|
|
1778
|
+
const payload = await response.json();
|
|
1779
|
+
if (!response.ok || !payload.token || !payload.expiresAt) {
|
|
1780
|
+
throw new Error(
|
|
1781
|
+
payload.error ?? `Runtime token broker request failed with status ${response.status}.`
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
if (config.tokenCachePath) {
|
|
1785
|
+
await writeFileImpl(config.tokenCachePath, JSON.stringify(payload), "utf8");
|
|
1786
|
+
}
|
|
1787
|
+
return payload.token;
|
|
1788
|
+
}
|
|
1789
|
+
async function readStdin() {
|
|
1790
|
+
const chunks = [];
|
|
1791
|
+
for await (const chunk of process.stdin) {
|
|
1792
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
1793
|
+
}
|
|
1794
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1795
|
+
}
|
|
1796
|
+
async function main() {
|
|
1797
|
+
const rawInput = await readStdin();
|
|
1798
|
+
const invocation = JSON.parse(rawInput);
|
|
1799
|
+
const result = await executeGitHubGraphQL(invocation, {
|
|
1800
|
+
token: process.env.GITHUB_GRAPHQL_TOKEN,
|
|
1801
|
+
apiUrl: process.env.GITHUB_GRAPHQL_API_URL,
|
|
1802
|
+
tokenBrokerUrl: process.env.GITHUB_TOKEN_BROKER_URL,
|
|
1803
|
+
tokenBrokerSecret: process.env.GITHUB_TOKEN_BROKER_SECRET,
|
|
1804
|
+
tokenCachePath: process.env.GITHUB_TOKEN_CACHE_PATH
|
|
1805
|
+
});
|
|
1806
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
1807
|
+
`);
|
|
1808
|
+
}
|
|
1809
|
+
if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
|
|
1810
|
+
main().catch((error) => {
|
|
1811
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1812
|
+
process.stderr.write(`${message}
|
|
1813
|
+
`);
|
|
1814
|
+
process.exitCode = 1;
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
async function readCachedToken(path, readFileImpl) {
|
|
1818
|
+
try {
|
|
1819
|
+
const payload = JSON.parse(await readFileImpl(path, "utf8"));
|
|
1820
|
+
if (!payload.token || !payload.expiresAt) {
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
return {
|
|
1824
|
+
token: payload.token,
|
|
1825
|
+
expiresAt: new Date(payload.expiresAt)
|
|
1826
|
+
};
|
|
1827
|
+
} catch {
|
|
1828
|
+
return null;
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// ../tool-github-graphql/src/mcp-server.ts
|
|
1833
|
+
import { fileURLToPath } from "url";
|
|
1834
|
+
var TOOL_SCHEMA = {
|
|
1835
|
+
name: "github_graphql",
|
|
1836
|
+
description: "Execute GitHub GraphQL queries for the active workspace so the agent can mutate project and issue state directly.",
|
|
1837
|
+
inputSchema: {
|
|
1838
|
+
type: "object",
|
|
1839
|
+
properties: {
|
|
1840
|
+
query: {
|
|
1841
|
+
type: "string",
|
|
1842
|
+
description: "GraphQL query or mutation document."
|
|
1843
|
+
},
|
|
1844
|
+
variables: {
|
|
1845
|
+
type: "object",
|
|
1846
|
+
description: "Variables for the GraphQL document."
|
|
1847
|
+
},
|
|
1848
|
+
operationName: {
|
|
1849
|
+
type: "string",
|
|
1850
|
+
description: "Optional GraphQL operation name."
|
|
1851
|
+
}
|
|
1852
|
+
},
|
|
1853
|
+
required: ["query"],
|
|
1854
|
+
additionalProperties: false
|
|
1855
|
+
}
|
|
1856
|
+
};
|
|
1857
|
+
var lineBuffer = "";
|
|
1858
|
+
function resolveGitHubGraphQLMcpServerEntryPoint() {
|
|
1859
|
+
return fileURLToPath(new URL("./mcp-server.js", import.meta.url));
|
|
1860
|
+
}
|
|
1861
|
+
function sendResponse(id, result) {
|
|
1862
|
+
const msg = JSON.stringify({ jsonrpc: "2.0", id, result });
|
|
1863
|
+
process.stdout.write(msg + "\n");
|
|
1864
|
+
}
|
|
1865
|
+
function sendError(id, code, message) {
|
|
1866
|
+
const msg = JSON.stringify({
|
|
1867
|
+
jsonrpc: "2.0",
|
|
1868
|
+
id,
|
|
1869
|
+
error: { code, message }
|
|
1870
|
+
});
|
|
1871
|
+
process.stdout.write(msg + "\n");
|
|
1872
|
+
}
|
|
1873
|
+
async function handleRequest(msg) {
|
|
1874
|
+
const id = msg.id ?? null;
|
|
1875
|
+
switch (msg.method) {
|
|
1876
|
+
case "initialize": {
|
|
1877
|
+
sendResponse(id, {
|
|
1878
|
+
protocolVersion: "2024-11-05",
|
|
1879
|
+
capabilities: { tools: {} },
|
|
1880
|
+
serverInfo: {
|
|
1881
|
+
name: "github-symphony-graphql",
|
|
1882
|
+
version: "0.1.0"
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
process.stdout.write(
|
|
1886
|
+
JSON.stringify({
|
|
1887
|
+
jsonrpc: "2.0",
|
|
1888
|
+
method: "notifications/initialized"
|
|
1889
|
+
}) + "\n"
|
|
1890
|
+
);
|
|
1891
|
+
break;
|
|
1892
|
+
}
|
|
1893
|
+
case "tools/list": {
|
|
1894
|
+
sendResponse(id, { tools: [TOOL_SCHEMA] });
|
|
1895
|
+
break;
|
|
1896
|
+
}
|
|
1897
|
+
case "tools/call": {
|
|
1898
|
+
const params = msg.params;
|
|
1899
|
+
if (params.name !== "github_graphql") {
|
|
1900
|
+
sendError(id, -32602, `Unknown tool: ${params.name}`);
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
const args = params.arguments ?? {};
|
|
1904
|
+
const invocation = {
|
|
1905
|
+
query: args.query,
|
|
1906
|
+
variables: args.variables,
|
|
1907
|
+
operationName: args.operationName
|
|
1908
|
+
};
|
|
1909
|
+
try {
|
|
1910
|
+
const result = await executeGitHubGraphQL(invocation, {
|
|
1911
|
+
token: process.env.GITHUB_GRAPHQL_TOKEN,
|
|
1912
|
+
apiUrl: process.env.GITHUB_GRAPHQL_API_URL,
|
|
1913
|
+
tokenBrokerUrl: process.env.GITHUB_TOKEN_BROKER_URL,
|
|
1914
|
+
tokenBrokerSecret: process.env.GITHUB_TOKEN_BROKER_SECRET,
|
|
1915
|
+
tokenCachePath: process.env.GITHUB_TOKEN_CACHE_PATH
|
|
1916
|
+
});
|
|
1917
|
+
sendResponse(id, {
|
|
1918
|
+
content: [
|
|
1919
|
+
{
|
|
1920
|
+
type: "text",
|
|
1921
|
+
text: JSON.stringify(result, null, 2)
|
|
1922
|
+
}
|
|
1923
|
+
]
|
|
1924
|
+
});
|
|
1925
|
+
} catch (err) {
|
|
1926
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1927
|
+
sendResponse(id, {
|
|
1928
|
+
content: [{ type: "text", text: message }],
|
|
1929
|
+
isError: true
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
break;
|
|
1933
|
+
}
|
|
1934
|
+
case "notifications/initialized":
|
|
1935
|
+
case "ping": {
|
|
1936
|
+
if (id !== null && id !== void 0) {
|
|
1937
|
+
sendResponse(id, {});
|
|
1938
|
+
}
|
|
1939
|
+
break;
|
|
1940
|
+
}
|
|
1941
|
+
default: {
|
|
1942
|
+
if (id !== null && id !== void 0) {
|
|
1943
|
+
sendError(id, -32601, `Method not found: ${msg.method}`);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
async function main2() {
|
|
1949
|
+
process.stdin.setEncoding("utf8");
|
|
1950
|
+
process.stdin.on("data", (chunk) => {
|
|
1951
|
+
lineBuffer += chunk;
|
|
1952
|
+
const lines = lineBuffer.split("\n");
|
|
1953
|
+
lineBuffer = lines.pop() ?? "";
|
|
1954
|
+
for (const line of lines) {
|
|
1955
|
+
const trimmed = line.trim();
|
|
1956
|
+
if (!trimmed) continue;
|
|
1957
|
+
try {
|
|
1958
|
+
const msg = JSON.parse(trimmed);
|
|
1959
|
+
void handleRequest(msg);
|
|
1960
|
+
} catch (err) {
|
|
1961
|
+
process.stderr.write(
|
|
1962
|
+
`[github-graphql-mcp] parse error: ${err instanceof Error ? err.message : String(err)}
|
|
1963
|
+
`
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
});
|
|
1968
|
+
process.stdin.on("end", () => {
|
|
1969
|
+
process.exit(0);
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
|
|
1973
|
+
main2().catch((err) => {
|
|
1974
|
+
process.stderr.write(
|
|
1975
|
+
`[github-graphql-mcp] fatal: ${err instanceof Error ? err.message : String(err)}
|
|
1976
|
+
`
|
|
1977
|
+
);
|
|
1978
|
+
process.exitCode = 1;
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// ../tool-github-graphql/src/mcp-entry.ts
|
|
1983
|
+
var DEFAULT_GITHUB_GRAPHQL_API_URL2 = "https://api.github.com/graphql";
|
|
1984
|
+
function createGitHubGraphQLMcpServerEntry(options = {}) {
|
|
1985
|
+
return {
|
|
1986
|
+
command: "node",
|
|
1987
|
+
args: [resolveGitHubGraphQLMcpServerEntryPoint()],
|
|
1988
|
+
env: {
|
|
1989
|
+
GITHUB_GRAPHQL_API_URL: options.githubGraphqlApiUrl ?? DEFAULT_GITHUB_GRAPHQL_API_URL2,
|
|
1990
|
+
...options.githubToken ? {
|
|
1991
|
+
GITHUB_GRAPHQL_TOKEN: options.githubToken
|
|
1992
|
+
} : {},
|
|
1993
|
+
...options.githubTokenBrokerUrl ? {
|
|
1994
|
+
GITHUB_TOKEN_BROKER_URL: options.githubTokenBrokerUrl
|
|
1995
|
+
} : {},
|
|
1996
|
+
...options.githubTokenBrokerSecret ? {
|
|
1997
|
+
GITHUB_TOKEN_BROKER_SECRET: options.githubTokenBrokerSecret
|
|
1998
|
+
} : {},
|
|
1999
|
+
...options.githubTokenCachePath ? {
|
|
2000
|
+
GITHUB_TOKEN_CACHE_PATH: options.githubTokenCachePath
|
|
2001
|
+
} : {},
|
|
2002
|
+
...options.githubProjectId ? {
|
|
2003
|
+
GITHUB_PROJECT_ID: options.githubProjectId
|
|
2004
|
+
} : {}
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// ../runtime-claude/src/spawn.ts
|
|
2010
|
+
import { spawn as spawn2 } from "child_process";
|
|
2011
|
+
import { finished } from "stream/promises";
|
|
2012
|
+
|
|
2013
|
+
// ../runtime-claude/src/session-store.ts
|
|
2014
|
+
import { mkdir as mkdir2, readFile as readFile7, rename, writeFile as writeFile4 } from "fs/promises";
|
|
2015
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2016
|
+
|
|
1126
2017
|
export {
|
|
1127
2018
|
isOrchestratorChannelEvent,
|
|
1128
2019
|
DEFAULT_WORKFLOW_LIFECYCLE,
|
|
@@ -1130,6 +2021,8 @@ export {
|
|
|
1130
2021
|
isStateTerminal,
|
|
1131
2022
|
matchesWorkflowState,
|
|
1132
2023
|
DEFAULT_MAX_FAILURE_RETRIES,
|
|
2024
|
+
resolveWorkflowRuntimeCommand,
|
|
2025
|
+
resolveWorkflowRuntimeTimeouts,
|
|
1133
2026
|
parseWorkflowMarkdown,
|
|
1134
2027
|
WorkflowConfigStore,
|
|
1135
2028
|
createDefaultWorkflowResolution,
|
|
@@ -1138,6 +2031,12 @@ export {
|
|
|
1138
2031
|
renderPrompt,
|
|
1139
2032
|
classifySessionExit,
|
|
1140
2033
|
scheduleRetryAt,
|
|
2034
|
+
extractEnvForCodex,
|
|
2035
|
+
shouldReuseAgentCredentialCache,
|
|
2036
|
+
readAgentCredentialCache,
|
|
2037
|
+
writeAgentCredentialCache,
|
|
2038
|
+
DEFAULT_AGENT_INPUT_REQUIRED_REASON,
|
|
2039
|
+
buildAgentInputRequiredReason,
|
|
1141
2040
|
readEnvFile,
|
|
1142
2041
|
deriveIssueWorkspaceKey,
|
|
1143
2042
|
deriveIssueWorkspaceKeyFromIdentifier,
|
|
@@ -1151,5 +2050,12 @@ export {
|
|
|
1151
2050
|
isFileMissing,
|
|
1152
2051
|
parseRecentEvents,
|
|
1153
2052
|
isMatchingIssueRun,
|
|
1154
|
-
mapIssueOrchestrationStateToStatus
|
|
2053
|
+
mapIssueOrchestrationStateToStatus,
|
|
2054
|
+
resolveGitHubGraphQLToken,
|
|
2055
|
+
createGitHubGraphQLMcpServerEntry,
|
|
2056
|
+
runClaudePreflight,
|
|
2057
|
+
formatClaudePreflightText,
|
|
2058
|
+
isClaudeRuntimeCommand,
|
|
2059
|
+
resolveClaudeCommandBinary,
|
|
2060
|
+
resolveRuntimeCommandBinary
|
|
1155
2061
|
};
|