@kbediako/codex-orchestrator 0.1.12 → 0.1.14-alpha.1
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/LICENSE +19 -5
- package/README.md +47 -2
- package/dist/bin/codex-orchestrator.js +93 -0
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +27 -3
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +17 -1
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +36 -1
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +28 -0
- package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +45 -0
- package/dist/orchestrator/src/cli/codexCliSetup.js +294 -0
- package/dist/orchestrator/src/cli/init.js +3 -0
- package/dist/orchestrator/src/cli/mcp.js +4 -2
- package/dist/orchestrator/src/cli/orchestrator.js +298 -28
- package/dist/orchestrator/src/cli/rlm/context.js +31 -3
- package/dist/orchestrator/src/cli/rlm/symbolic.js +152 -15
- package/dist/orchestrator/src/cli/rlmRunner.js +59 -5
- package/dist/orchestrator/src/cli/run/manifest.js +3 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +87 -0
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +24 -0
- package/dist/orchestrator/src/cli/skills.js +1 -1
- package/dist/orchestrator/src/cli/utils/codexCli.js +94 -0
- package/dist/orchestrator/src/cli/utils/codexPaths.js +13 -0
- package/dist/orchestrator/src/cli/utils/devtools.js +9 -12
- package/dist/orchestrator/src/cloud/CodexCloudTaskExecutor.js +255 -0
- package/dist/orchestrator/src/learning/crystalizer.js +2 -1
- package/dist/orchestrator/src/manager.js +1 -0
- package/dist/orchestrator/src/sync/CloudSyncWorker.js +37 -7
- package/dist/scripts/design/pipeline/context.js +3 -2
- package/dist/scripts/lib/run-manifests.js +14 -0
- package/docs/README.md +22 -2
- package/package.json +6 -2
- package/schemas/manifest.json +83 -0
- package/skills/collab-deliberation/SKILL.md +21 -0
- package/skills/collab-evals/SKILL.md +32 -0
- package/skills/delegate-early/SKILL.md +47 -0
- package/skills/delegation-usage/DELEGATION_GUIDE.md +5 -4
- package/skills/delegation-usage/SKILL.md +11 -5
- package/skills/docs-first/SKILL.md +2 -1
- package/templates/README.md +4 -0
|
@@ -274,8 +274,8 @@ async function runCodexAgent(input, env, repoRoot, nonInteractive, subagentsEnab
|
|
|
274
274
|
const output = await runCodexCompletion(prompt, env, repoRoot, nonInteractive, subagentsEnabled, true);
|
|
275
275
|
return { output };
|
|
276
276
|
}
|
|
277
|
-
async function
|
|
278
|
-
const { command, args } = resolveCodexCommand(
|
|
277
|
+
async function runCodexExec(args, env, repoRoot, nonInteractive, subagentsEnabled, mirrorOutput) {
|
|
278
|
+
const { command, args: resolvedArgs } = resolveCodexCommand(args, env);
|
|
279
279
|
const childEnv = { ...process.env, ...env };
|
|
280
280
|
if (nonInteractive) {
|
|
281
281
|
childEnv.CODEX_NON_INTERACTIVE = childEnv.CODEX_NON_INTERACTIVE ?? '1';
|
|
@@ -283,7 +283,7 @@ async function runCodexCompletion(prompt, env, repoRoot, nonInteractive, subagen
|
|
|
283
283
|
childEnv.CODEX_INTERACTIVE = childEnv.CODEX_INTERACTIVE ?? '0';
|
|
284
284
|
}
|
|
285
285
|
childEnv.CODEX_SUBAGENTS = subagentsEnabled ? '1' : '0';
|
|
286
|
-
const child = spawn(command,
|
|
286
|
+
const child = spawn(command, resolvedArgs, { cwd: repoRoot, env: childEnv, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
287
287
|
let stdout = '';
|
|
288
288
|
let stderr = '';
|
|
289
289
|
child.stdout?.on('data', (chunk) => {
|
|
@@ -311,8 +311,52 @@ async function runCodexCompletion(prompt, env, repoRoot, nonInteractive, subagen
|
|
|
311
311
|
}
|
|
312
312
|
});
|
|
313
313
|
});
|
|
314
|
+
return { stdout, stderr };
|
|
315
|
+
}
|
|
316
|
+
async function runCodexCompletion(prompt, env, repoRoot, nonInteractive, subagentsEnabled, mirrorOutput) {
|
|
317
|
+
const { stdout, stderr } = await runCodexExec(['exec', prompt], env, repoRoot, nonInteractive, subagentsEnabled, mirrorOutput);
|
|
318
|
+
return [stdout.trim(), stderr.trim()].filter(Boolean).join('\n');
|
|
319
|
+
}
|
|
320
|
+
async function runCodexJsonlCompletion(prompt, env, repoRoot, nonInteractive, mirrorOutput, extraArgs = []) {
|
|
321
|
+
const { stdout, stderr } = await runCodexExec(['exec', '--json', ...extraArgs, prompt], env, repoRoot, nonInteractive, false, mirrorOutput);
|
|
322
|
+
const message = extractAgentMessageFromJsonl(stdout);
|
|
323
|
+
if (message) {
|
|
324
|
+
return message;
|
|
325
|
+
}
|
|
314
326
|
return [stdout.trim(), stderr.trim()].filter(Boolean).join('\n');
|
|
315
327
|
}
|
|
328
|
+
function extractAgentMessageFromJsonl(raw) {
|
|
329
|
+
let lastMessage = null;
|
|
330
|
+
const lines = raw.split(/\r?\n/);
|
|
331
|
+
for (const line of lines) {
|
|
332
|
+
const trimmed = line.trim();
|
|
333
|
+
if (!trimmed.startsWith('{')) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
const parsed = JSON.parse(trimmed);
|
|
338
|
+
if ((parsed.type === 'item.completed' || parsed.type === 'item.updated') &&
|
|
339
|
+
parsed.item?.type === 'agent_message' &&
|
|
340
|
+
typeof parsed.item.text === 'string') {
|
|
341
|
+
lastMessage = parsed.item.text;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// ignore parse errors for non-json lines
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return lastMessage;
|
|
349
|
+
}
|
|
350
|
+
function buildCollabSubcallPrompt(prompt) {
|
|
351
|
+
return [
|
|
352
|
+
'Use collab tools to spawn a sub-agent with the prompt below.',
|
|
353
|
+
'Wait for the sub-agent, then close it.',
|
|
354
|
+
'Return only the sub-agent response text and nothing else.',
|
|
355
|
+
'',
|
|
356
|
+
'Sub-agent prompt:',
|
|
357
|
+
prompt
|
|
358
|
+
].join('\n');
|
|
359
|
+
}
|
|
316
360
|
function normalizeExitCode(code) {
|
|
317
361
|
if (typeof code === 'number' && Number.isInteger(code)) {
|
|
318
362
|
return code;
|
|
@@ -571,6 +615,7 @@ async function main() {
|
|
|
571
615
|
console.log(`Validator: ${validatorCommand}`);
|
|
572
616
|
}
|
|
573
617
|
const subagentsEnabled = envFlagEnabled(env.CODEX_SUBAGENTS) || envFlagEnabled(env.RLM_SUBAGENTS);
|
|
618
|
+
const symbolicCollabEnabled = envFlagEnabled(env.RLM_SYMBOLIC_COLLAB);
|
|
574
619
|
const nonInteractive = shouldForceNonInteractive(env);
|
|
575
620
|
if (mode === 'symbolic') {
|
|
576
621
|
const budgets = {
|
|
@@ -622,11 +667,20 @@ async function main() {
|
|
|
622
667
|
budgets,
|
|
623
668
|
runPlanner: (prompt, _attempt) => {
|
|
624
669
|
void _attempt;
|
|
625
|
-
return
|
|
670
|
+
return runCodexJsonlCompletion(prompt, env, repoRoot, nonInteractive, false);
|
|
626
671
|
},
|
|
627
672
|
runSubcall: (prompt, _meta) => {
|
|
628
673
|
void _meta;
|
|
629
|
-
|
|
674
|
+
if (!symbolicCollabEnabled) {
|
|
675
|
+
return runCodexCompletion(prompt, env, repoRoot, nonInteractive, false, false);
|
|
676
|
+
}
|
|
677
|
+
const collabPrompt = buildCollabSubcallPrompt(prompt);
|
|
678
|
+
return runCodexJsonlCompletion(collabPrompt, env, repoRoot, nonInteractive, true, [
|
|
679
|
+
'--enable',
|
|
680
|
+
'collab',
|
|
681
|
+
'--sandbox',
|
|
682
|
+
'read-only'
|
|
683
|
+
]);
|
|
630
684
|
},
|
|
631
685
|
logger: (line) => logger.info(line)
|
|
632
686
|
});
|
|
@@ -47,6 +47,7 @@ export async function bootstrapManifest(runId, options) {
|
|
|
47
47
|
resume_events: [],
|
|
48
48
|
approvals: [],
|
|
49
49
|
commands,
|
|
50
|
+
collab_tool_calls: [],
|
|
50
51
|
child_runs: [],
|
|
51
52
|
run_summary_path: null,
|
|
52
53
|
plan_target_id: options.planTargetId ?? null,
|
|
@@ -54,6 +55,7 @@ export async function bootstrapManifest(runId, options) {
|
|
|
54
55
|
instructions_sources: [],
|
|
55
56
|
prompt_packs: [],
|
|
56
57
|
guardrails_required: pipeline.guardrailsRequired !== false,
|
|
58
|
+
cloud_execution: null,
|
|
57
59
|
learning: {
|
|
58
60
|
validation: {
|
|
59
61
|
mode: 'per-task',
|
|
@@ -169,6 +171,7 @@ export function resetForResume(manifest) {
|
|
|
169
171
|
manifest.status = 'in_progress';
|
|
170
172
|
manifest.status_detail = 'resuming';
|
|
171
173
|
manifest.guardrail_status = undefined;
|
|
174
|
+
manifest.cloud_execution = null;
|
|
172
175
|
}
|
|
173
176
|
export function recordResumeEvent(manifest, event) {
|
|
174
177
|
manifest.resume_events.push({ ...event, timestamp: isoTimestamp() });
|
|
@@ -13,6 +13,7 @@ import { findPackageRoot } from '../utils/packageInfo.js';
|
|
|
13
13
|
const MAX_BUFFERED_OUTPUT_BYTES = 64 * 1024;
|
|
14
14
|
const EMIT_COMMAND_STREAM_MIRRORS = EnvUtils.getBoolean('CODEX_ORCHESTRATOR_EMIT_COMMAND_STREAMS', false);
|
|
15
15
|
export const MAX_CAPTURED_CHUNK_EVENTS = EnvUtils.getInt('CODEX_ORCHESTRATOR_EXEC_EVENT_MAX_CHUNKS', 500);
|
|
16
|
+
const MAX_COLLAB_TOOL_CALLS = EnvUtils.getInt('CODEX_ORCHESTRATOR_COLLAB_MAX_EVENTS', 200);
|
|
16
17
|
const PACKAGE_ROOT = findPackageRoot();
|
|
17
18
|
export async function runCommandStage(context, hooks = {}) {
|
|
18
19
|
const { env, paths, manifest, stage, index, events, persister, envOverrides } = context;
|
|
@@ -50,6 +51,33 @@ export async function runCommandStage(context, hooks = {}) {
|
|
|
50
51
|
let stderrBytes = 0;
|
|
51
52
|
let stdoutTruncated = false;
|
|
52
53
|
let stderrTruncated = false;
|
|
54
|
+
let collabBuffer = '';
|
|
55
|
+
let collabCount = manifest.collab_tool_calls?.length ?? 0;
|
|
56
|
+
const recordCollabToolCall = (record) => {
|
|
57
|
+
if (MAX_COLLAB_TOOL_CALLS <= 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (collabCount >= MAX_COLLAB_TOOL_CALLS) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!manifest.collab_tool_calls) {
|
|
64
|
+
manifest.collab_tool_calls = [];
|
|
65
|
+
}
|
|
66
|
+
manifest.collab_tool_calls.push(record);
|
|
67
|
+
collabCount += 1;
|
|
68
|
+
void persister?.schedule({ manifest: true });
|
|
69
|
+
};
|
|
70
|
+
const ingestCollabStdout = (data) => {
|
|
71
|
+
collabBuffer += data;
|
|
72
|
+
const lines = collabBuffer.split('\n');
|
|
73
|
+
collabBuffer = lines.pop() ?? '';
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
const record = parseCollabToolCallLine(line, stage.id, entry.index);
|
|
76
|
+
if (record) {
|
|
77
|
+
recordCollabToolCall(record);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
53
81
|
const handleEvent = (event) => {
|
|
54
82
|
if (!activeCorrelationId) {
|
|
55
83
|
activeCorrelationId = event.correlationId;
|
|
@@ -87,6 +115,9 @@ export async function runCommandStage(context, hooks = {}) {
|
|
|
87
115
|
message: event.payload.data,
|
|
88
116
|
source: event.payload.stream
|
|
89
117
|
});
|
|
118
|
+
if (event.payload.stream === 'stdout') {
|
|
119
|
+
ingestCollabStdout(event.payload.data);
|
|
120
|
+
}
|
|
90
121
|
break;
|
|
91
122
|
case 'exec:retry':
|
|
92
123
|
events?.toolCall({
|
|
@@ -192,6 +223,13 @@ export async function runCommandStage(context, hooks = {}) {
|
|
|
192
223
|
entry.exit_code = normalizedExitCode;
|
|
193
224
|
entry.summary = summary;
|
|
194
225
|
entry.status = result.status === 'succeeded' ? 'succeeded' : stage.allowFailure ? 'skipped' : 'failed';
|
|
226
|
+
if (collabBuffer.trim()) {
|
|
227
|
+
const record = parseCollabToolCallLine(collabBuffer, stage.id, entry.index);
|
|
228
|
+
if (record) {
|
|
229
|
+
recordCollabToolCall(record);
|
|
230
|
+
}
|
|
231
|
+
collabBuffer = '';
|
|
232
|
+
}
|
|
195
233
|
if (entry.status === 'failed') {
|
|
196
234
|
const errorDetails = {
|
|
197
235
|
exit_code: normalizedExitCode,
|
|
@@ -418,3 +456,52 @@ function truncate(value, length = 240) {
|
|
|
418
456
|
}
|
|
419
457
|
return `${value.slice(0, length)}…`;
|
|
420
458
|
}
|
|
459
|
+
function parseCollabToolCallLine(line, stageId, commandIndex) {
|
|
460
|
+
const trimmed = line.trim();
|
|
461
|
+
if (!trimmed || !trimmed.includes('"collab_tool_call"')) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
let parsed;
|
|
465
|
+
try {
|
|
466
|
+
parsed = JSON.parse(trimmed);
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
const record = parsed;
|
|
475
|
+
const eventType = record.type;
|
|
476
|
+
if (eventType !== 'item.started' && eventType !== 'item.completed' && eventType !== 'item.updated') {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const item = record.item;
|
|
480
|
+
if (!item || item.type !== 'collab_tool_call') {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
const receiverThreadIds = Array.isArray(item.receiver_thread_ids)
|
|
484
|
+
? item.receiver_thread_ids.filter((entry) => typeof entry === 'string')
|
|
485
|
+
: [];
|
|
486
|
+
return {
|
|
487
|
+
observed_at: isoTimestamp(),
|
|
488
|
+
stage_id: stageId,
|
|
489
|
+
command_index: commandIndex,
|
|
490
|
+
event_type: eventType,
|
|
491
|
+
item_id: typeof item.id === 'string' ? item.id : 'unknown',
|
|
492
|
+
tool: typeof item.tool === 'string' ? item.tool : 'unknown',
|
|
493
|
+
status: normalizeCollabStatus(item.status),
|
|
494
|
+
sender_thread_id: typeof item.sender_thread_id === 'string' ? item.sender_thread_id : 'unknown',
|
|
495
|
+
receiver_thread_ids: receiverThreadIds,
|
|
496
|
+
prompt: typeof item.prompt === 'string' ? item.prompt : null,
|
|
497
|
+
agents_states: item.agents_states && typeof item.agents_states === 'object'
|
|
498
|
+
? item.agents_states
|
|
499
|
+
: null
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function normalizeCollabStatus(value) {
|
|
503
|
+
if (value === 'completed' || value === 'failed' || value === 'in_progress') {
|
|
504
|
+
return value;
|
|
505
|
+
}
|
|
506
|
+
return 'in_progress';
|
|
507
|
+
}
|
|
@@ -27,6 +27,30 @@ export function applyPrivacyToRunSummary(runSummary, manifest) {
|
|
|
27
27
|
allowedFrames: manifest.privacy.totals.allowed_frames
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
+
export function applyCloudExecutionToRunSummary(runSummary, manifest) {
|
|
31
|
+
if (!manifest.cloud_execution) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
runSummary.cloudExecution = {
|
|
35
|
+
taskId: manifest.cloud_execution.task_id,
|
|
36
|
+
environmentId: manifest.cloud_execution.environment_id,
|
|
37
|
+
status: manifest.cloud_execution.status,
|
|
38
|
+
statusUrl: manifest.cloud_execution.status_url,
|
|
39
|
+
submittedAt: manifest.cloud_execution.submitted_at,
|
|
40
|
+
completedAt: manifest.cloud_execution.completed_at,
|
|
41
|
+
lastPolledAt: manifest.cloud_execution.last_polled_at,
|
|
42
|
+
pollCount: manifest.cloud_execution.poll_count,
|
|
43
|
+
pollIntervalSeconds: manifest.cloud_execution.poll_interval_seconds,
|
|
44
|
+
timeoutSeconds: manifest.cloud_execution.timeout_seconds,
|
|
45
|
+
attempts: manifest.cloud_execution.attempts,
|
|
46
|
+
diffPath: manifest.cloud_execution.diff_path,
|
|
47
|
+
diffUrl: manifest.cloud_execution.diff_url,
|
|
48
|
+
diffStatus: manifest.cloud_execution.diff_status,
|
|
49
|
+
applyStatus: manifest.cloud_execution.apply_status,
|
|
50
|
+
logPath: manifest.cloud_execution.log_path,
|
|
51
|
+
error: manifest.cloud_execution.error
|
|
52
|
+
};
|
|
53
|
+
}
|
|
30
54
|
export async function persistRunSummary(env, paths, manifest, runSummary, persister) {
|
|
31
55
|
const summaryPath = join(paths.runDir, 'run-summary.json');
|
|
32
56
|
await writeJsonAtomic(summaryPath, runSummary);
|
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { copyFile, mkdir, readdir, stat } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
4
|
import process from 'node:process';
|
|
5
|
-
import { resolveCodexHome } from './utils/
|
|
5
|
+
import { resolveCodexHome } from './utils/codexPaths.js';
|
|
6
6
|
import { findPackageRoot } from './utils/packageInfo.js';
|
|
7
7
|
export async function installSkills(options = {}) {
|
|
8
8
|
const pkgRoot = findPackageRoot();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { resolveCodexOrchestratorHome } from './codexPaths.js';
|
|
6
|
+
const CONFIG_FILENAME = 'codex-cli.json';
|
|
7
|
+
export function resolveCodexCliRoot(env = process.env) {
|
|
8
|
+
return join(resolveCodexOrchestratorHome(env), 'codex-cli');
|
|
9
|
+
}
|
|
10
|
+
export function resolveCodexCliConfigPath(env = process.env) {
|
|
11
|
+
return join(resolveCodexCliRoot(env), CONFIG_FILENAME);
|
|
12
|
+
}
|
|
13
|
+
export function resolveCodexCliBinPath(env = process.env) {
|
|
14
|
+
return join(resolveCodexCliRoot(env), 'bin', codexBinaryName());
|
|
15
|
+
}
|
|
16
|
+
export function resolveCodexCliBin(env = process.env) {
|
|
17
|
+
const override = env.CODEX_CLI_BIN?.trim();
|
|
18
|
+
if (override) {
|
|
19
|
+
return override;
|
|
20
|
+
}
|
|
21
|
+
const { config } = readCodexCliConfig(env);
|
|
22
|
+
if (config?.binary_path) {
|
|
23
|
+
return config.binary_path;
|
|
24
|
+
}
|
|
25
|
+
return 'codex';
|
|
26
|
+
}
|
|
27
|
+
export function resolveCodexCliReadiness(env = process.env) {
|
|
28
|
+
const configPath = resolveCodexCliConfigPath(env);
|
|
29
|
+
if (!existsSync(configPath)) {
|
|
30
|
+
return {
|
|
31
|
+
status: 'missing',
|
|
32
|
+
config: { status: 'missing', path: configPath },
|
|
33
|
+
binary: { status: 'missing', path: resolveCodexCliBinPath(env) }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const { config, error } = readCodexCliConfig(env);
|
|
37
|
+
if (error || !config) {
|
|
38
|
+
return {
|
|
39
|
+
status: 'invalid',
|
|
40
|
+
config: { status: 'invalid', path: configPath, error: error ?? 'Invalid config JSON.' },
|
|
41
|
+
binary: { status: 'missing', path: resolveCodexCliBinPath(env) }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const binaryPath = config.binary_path;
|
|
45
|
+
const binaryExists = binaryPath ? existsSync(binaryPath) : false;
|
|
46
|
+
const binaryStatus = binaryExists ? 'ok' : 'missing';
|
|
47
|
+
if (binaryExists && config.sha256) {
|
|
48
|
+
const actualSha256 = sha256FileSync(binaryPath);
|
|
49
|
+
if (actualSha256 !== config.sha256) {
|
|
50
|
+
return {
|
|
51
|
+
status: 'invalid',
|
|
52
|
+
config: {
|
|
53
|
+
status: 'invalid',
|
|
54
|
+
path: configPath,
|
|
55
|
+
error: `codex-cli sha256 mismatch (expected ${config.sha256}, got ${actualSha256})`
|
|
56
|
+
},
|
|
57
|
+
binary: { status: binaryStatus, path: binaryPath },
|
|
58
|
+
install: config
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const status = binaryStatus === 'ok' ? 'ok' : 'missing';
|
|
63
|
+
return {
|
|
64
|
+
status,
|
|
65
|
+
config: { status: 'ok', path: configPath },
|
|
66
|
+
binary: { status: binaryStatus, path: binaryPath },
|
|
67
|
+
install: config
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export function readCodexCliConfig(env = process.env) {
|
|
71
|
+
const configPath = resolveCodexCliConfigPath(env);
|
|
72
|
+
if (!existsSync(configPath)) {
|
|
73
|
+
return { config: null };
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
77
|
+
const parsed = JSON.parse(raw);
|
|
78
|
+
if (!parsed || typeof parsed.binary_path !== 'string') {
|
|
79
|
+
return { config: null, error: 'codex-cli.json missing binary_path.' };
|
|
80
|
+
}
|
|
81
|
+
return { config: parsed };
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return { config: null, error: error instanceof Error ? error.message : String(error) };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function codexBinaryName() {
|
|
88
|
+
return process.platform === 'win32' ? 'codex.exe' : 'codex';
|
|
89
|
+
}
|
|
90
|
+
function sha256FileSync(path) {
|
|
91
|
+
const hash = createHash('sha256');
|
|
92
|
+
hash.update(readFileSync(path));
|
|
93
|
+
return hash.digest('hex');
|
|
94
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
export function resolveCodexHome(env = process.env) {
|
|
5
|
+
const override = env.CODEX_HOME?.trim();
|
|
6
|
+
if (override) {
|
|
7
|
+
return override;
|
|
8
|
+
}
|
|
9
|
+
return join(homedir(), '.codex');
|
|
10
|
+
}
|
|
11
|
+
export function resolveCodexOrchestratorHome(env = process.env) {
|
|
12
|
+
return join(resolveCodexHome(env), 'orchestrator');
|
|
13
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
2
|
import { join } from 'node:path';
|
|
4
3
|
import process from 'node:process';
|
|
5
4
|
import { EnvUtils } from '../../../../packages/shared/config/env.js';
|
|
5
|
+
import { resolveCodexCliBin } from './codexCli.js';
|
|
6
|
+
import { resolveCodexHome } from './codexPaths.js';
|
|
6
7
|
export const DEVTOOLS_SKILL_NAME = 'chrome-devtools';
|
|
7
8
|
export const DEVTOOLS_CONFIG_OVERRIDE = 'mcp_servers.chrome-devtools.enabled=true';
|
|
8
9
|
const CONFIG_OVERRIDE_ENV_KEYS = ['CODEX_MCP_CONFIG_OVERRIDES', 'CODEX_CONFIG_OVERRIDES'];
|
|
@@ -32,13 +33,6 @@ export function isDevtoolsEnabled(env = process.env) {
|
|
|
32
33
|
}
|
|
33
34
|
return EnvUtils.isTrue(raw.trim().toLowerCase());
|
|
34
35
|
}
|
|
35
|
-
export function resolveCodexHome(env = process.env) {
|
|
36
|
-
const override = env.CODEX_HOME?.trim();
|
|
37
|
-
if (override) {
|
|
38
|
-
return override;
|
|
39
|
-
}
|
|
40
|
-
return join(homedir(), '.codex');
|
|
41
|
-
}
|
|
42
36
|
export function resolveCodexConfigPath(env = process.env) {
|
|
43
37
|
return join(resolveCodexHome(env), DEVTOOLS_CONFIG_FILENAME);
|
|
44
38
|
}
|
|
@@ -76,28 +70,31 @@ export function resolveDevtoolsReadiness(env = process.env) {
|
|
|
76
70
|
export function buildDevtoolsSetupPlan(env = process.env) {
|
|
77
71
|
const codexHome = resolveCodexHome(env);
|
|
78
72
|
const configPath = resolveCodexConfigPath(env);
|
|
73
|
+
const command = resolveCodexCliBin(env);
|
|
79
74
|
const args = [...DEVTOOLS_MCP_COMMAND];
|
|
80
75
|
return {
|
|
81
76
|
codexHome,
|
|
82
77
|
configPath,
|
|
83
|
-
command
|
|
78
|
+
command,
|
|
84
79
|
args,
|
|
85
|
-
commandLine: [
|
|
80
|
+
commandLine: [command, ...args].join(' '),
|
|
86
81
|
configSnippet: DEVTOOLS_CONFIG_SNIPPET
|
|
87
82
|
};
|
|
88
83
|
}
|
|
89
84
|
export function resolveCodexCommand(args, env = process.env) {
|
|
90
85
|
const overrides = parseConfigOverrides(env);
|
|
91
86
|
if (!isDevtoolsEnabled(env)) {
|
|
92
|
-
|
|
87
|
+
const command = resolveCodexCliBin(env);
|
|
88
|
+
return { command, args: applyConfigOverrides(overrides, args) };
|
|
93
89
|
}
|
|
94
90
|
const readiness = resolveDevtoolsReadiness(env);
|
|
95
91
|
if (readiness.status !== 'ok') {
|
|
96
92
|
throw new Error(formatDevtoolsPreflightError(readiness));
|
|
97
93
|
}
|
|
98
94
|
const mergedOverrides = dedupeOverrides([DEVTOOLS_CONFIG_OVERRIDE, ...overrides]);
|
|
95
|
+
const command = resolveCodexCliBin(env);
|
|
99
96
|
return {
|
|
100
|
-
command
|
|
97
|
+
command,
|
|
101
98
|
args: applyConfigOverrides(mergedOverrides, args)
|
|
102
99
|
};
|
|
103
100
|
}
|