@kbediako/codex-orchestrator 0.1.0
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 +7 -0
- package/README.md +238 -0
- package/dist/bin/codex-orchestrator.js +507 -0
- package/dist/orchestrator/src/agents/builder.js +16 -0
- package/dist/orchestrator/src/agents/index.js +4 -0
- package/dist/orchestrator/src/agents/planner.js +17 -0
- package/dist/orchestrator/src/agents/reviewer.js +13 -0
- package/dist/orchestrator/src/agents/tester.js +13 -0
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
- package/dist/orchestrator/src/cli/adapters/index.js +4 -0
- package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
- package/dist/orchestrator/src/cli/doctor.js +48 -0
- package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
- package/dist/orchestrator/src/cli/exec/command.js +56 -0
- package/dist/orchestrator/src/cli/exec/context.js +108 -0
- package/dist/orchestrator/src/cli/exec/experience.js +77 -0
- package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
- package/dist/orchestrator/src/cli/exec/learning.js +62 -0
- package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
- package/dist/orchestrator/src/cli/exec/summary.js +109 -0
- package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
- package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
- package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
- package/dist/orchestrator/src/cli/exec/types.js +1 -0
- package/dist/orchestrator/src/cli/init.js +64 -0
- package/dist/orchestrator/src/cli/mcp.js +124 -0
- package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
- package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
- package/dist/orchestrator/src/cli/orchestrator.js +554 -0
- package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
- package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
- package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
- package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
- package/dist/orchestrator/src/cli/run/environment.js +24 -0
- package/dist/orchestrator/src/cli/run/manifest.js +367 -0
- package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
- package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
- package/dist/orchestrator/src/cli/selfCheck.js +12 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
- package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
- package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
- package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
- package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
- package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
- package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
- package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
- package/dist/orchestrator/src/cli/types.js +1 -0
- package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
- package/dist/orchestrator/src/cli/ui/controller.js +26 -0
- package/dist/orchestrator/src/cli/ui/store.js +240 -0
- package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
- package/dist/orchestrator/src/cli/utils/fs.js +8 -0
- package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
- package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
- package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
- package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
- package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
- package/dist/orchestrator/src/cli/utils/runId.js +7 -0
- package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
- package/dist/orchestrator/src/cli/utils/strings.js +8 -0
- package/dist/orchestrator/src/cli/utils/time.js +6 -0
- package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
- package/dist/orchestrator/src/control-plane/index.js +3 -0
- package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
- package/dist/orchestrator/src/control-plane/types.js +1 -0
- package/dist/orchestrator/src/control-plane/validator.js +50 -0
- package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
- package/dist/orchestrator/src/events/EventBus.js +25 -0
- package/dist/orchestrator/src/learning/crystalizer.js +108 -0
- package/dist/orchestrator/src/learning/harvester.js +146 -0
- package/dist/orchestrator/src/learning/manifest.js +56 -0
- package/dist/orchestrator/src/learning/runner.js +177 -0
- package/dist/orchestrator/src/learning/validator.js +164 -0
- package/dist/orchestrator/src/logger.js +20 -0
- package/dist/orchestrator/src/manager.js +388 -0
- package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
- package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
- package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
- package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
- package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
- package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
- package/dist/orchestrator/src/persistence/lockFile.js +26 -0
- package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
- package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
- package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
- package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
- package/dist/orchestrator/src/privacy/guard.js +111 -0
- package/dist/orchestrator/src/scheduler/index.js +1 -0
- package/dist/orchestrator/src/scheduler/plan.js +171 -0
- package/dist/orchestrator/src/scheduler/types.js +1 -0
- package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
- package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
- package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
- package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
- package/dist/orchestrator/src/types.js +1 -0
- package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
- package/dist/orchestrator/src/utils/errorMessage.js +14 -0
- package/dist/orchestrator/src/utils/executionMode.js +69 -0
- package/dist/packages/control-plane-schemas/src/index.js +1 -0
- package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
- package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
- package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
- package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
- package/dist/packages/orchestrator/src/index.js +3 -0
- package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
- package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
- package/dist/packages/orchestrator/src/notifications/index.js +74 -0
- package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
- package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
- package/dist/packages/sdk-node/src/orchestrator.js +195 -0
- package/dist/packages/shared/config/designConfig.js +495 -0
- package/dist/packages/shared/config/env.js +37 -0
- package/dist/packages/shared/config/index.js +2 -0
- package/dist/packages/shared/design-artifacts/writer.js +221 -0
- package/dist/packages/shared/events/serializer.js +84 -0
- package/dist/packages/shared/events/types.js +1 -0
- package/dist/packages/shared/manifest/artifactUtils.js +36 -0
- package/dist/packages/shared/manifest/designArtifacts.js +665 -0
- package/dist/packages/shared/manifest/fileIO.js +29 -0
- package/dist/packages/shared/manifest/toolRuns.js +78 -0
- package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
- package/dist/packages/shared/manifest/types.js +5 -0
- package/dist/packages/shared/manifest/validator.js +73 -0
- package/dist/packages/shared/manifest/writer.js +2 -0
- package/dist/packages/shared/streams/stdio.js +112 -0
- package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
- package/dist/scripts/design/pipeline/componentize.js +74 -0
- package/dist/scripts/design/pipeline/context.js +34 -0
- package/dist/scripts/design/pipeline/extract.js +249 -0
- package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
- package/dist/scripts/design/pipeline/prepare.js +46 -0
- package/dist/scripts/design/pipeline/reference.js +94 -0
- package/dist/scripts/design/pipeline/state.js +206 -0
- package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
- package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
- package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
- package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
- package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
- package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
- package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
- package/dist/scripts/design/pipeline/visual-regression.js +137 -0
- package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
- package/package.json +97 -0
- package/schemas/manifest.json +1064 -0
- package/templates/README.md +12 -0
- package/templates/codex/mcp-client.json +8 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { CodexOrchestrator } from '../orchestrator/src/cli/orchestrator.js';
|
|
4
|
+
import { formatPlanPreview } from '../orchestrator/src/cli/utils/planFormatter.js';
|
|
5
|
+
import { executeExecCommand } from '../orchestrator/src/cli/exec/command.js';
|
|
6
|
+
import { resolveEnvironment, sanitizeTaskId } from '../orchestrator/src/cli/run/environment.js';
|
|
7
|
+
import { RunEventEmitter } from '../orchestrator/src/cli/events/runEvents.js';
|
|
8
|
+
import { evaluateInteractiveGate } from '../orchestrator/src/cli/utils/interactive.js';
|
|
9
|
+
import { buildSelfCheckResult } from '../orchestrator/src/cli/selfCheck.js';
|
|
10
|
+
import { initCodexTemplates, formatInitSummary } from '../orchestrator/src/cli/init.js';
|
|
11
|
+
import { runDoctor, formatDoctorSummary } from '../orchestrator/src/cli/doctor.js';
|
|
12
|
+
import { loadPackageInfo } from '../orchestrator/src/cli/utils/packageInfo.js';
|
|
13
|
+
import { serveMcp } from '../orchestrator/src/cli/mcp.js';
|
|
14
|
+
async function main() {
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const command = args.shift();
|
|
17
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
18
|
+
printHelp();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (command === '--version' || command === '-v') {
|
|
22
|
+
printVersion();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const orchestrator = new CodexOrchestrator();
|
|
26
|
+
try {
|
|
27
|
+
switch (command) {
|
|
28
|
+
case 'start':
|
|
29
|
+
await handleStart(orchestrator, args);
|
|
30
|
+
break;
|
|
31
|
+
case 'plan':
|
|
32
|
+
await handlePlan(orchestrator, args);
|
|
33
|
+
break;
|
|
34
|
+
case 'resume':
|
|
35
|
+
await handleResume(orchestrator, args);
|
|
36
|
+
break;
|
|
37
|
+
case 'status':
|
|
38
|
+
await handleStatus(orchestrator, args);
|
|
39
|
+
break;
|
|
40
|
+
case 'exec':
|
|
41
|
+
await handleExec(args);
|
|
42
|
+
break;
|
|
43
|
+
case 'self-check':
|
|
44
|
+
await handleSelfCheck(args);
|
|
45
|
+
break;
|
|
46
|
+
case 'init':
|
|
47
|
+
await handleInit(args);
|
|
48
|
+
break;
|
|
49
|
+
case 'doctor':
|
|
50
|
+
await handleDoctor(args);
|
|
51
|
+
break;
|
|
52
|
+
case 'mcp':
|
|
53
|
+
await handleMcp(args);
|
|
54
|
+
break;
|
|
55
|
+
case 'version':
|
|
56
|
+
printVersion();
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
console.error(`Unknown command: ${command}`);
|
|
60
|
+
printHelp();
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error(error?.message ?? String(error));
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function parseArgs(raw) {
|
|
70
|
+
const positionals = [];
|
|
71
|
+
const flags = {};
|
|
72
|
+
const queue = [...raw];
|
|
73
|
+
while (queue.length > 0) {
|
|
74
|
+
const token = queue.shift();
|
|
75
|
+
if (!token) {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (token === '--') {
|
|
79
|
+
positionals.push(...queue);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
if (!token.startsWith('--')) {
|
|
83
|
+
positionals.push(token);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const key = token.slice(2);
|
|
87
|
+
if (queue[0] && !queue[0].startsWith('--')) {
|
|
88
|
+
flags[key] = queue.shift();
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
flags[key] = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { positionals, flags };
|
|
95
|
+
}
|
|
96
|
+
function resolveTargetStageId(flags) {
|
|
97
|
+
const target = flags['target'];
|
|
98
|
+
if (typeof target === 'string' && target.trim().length > 0) {
|
|
99
|
+
return target.trim();
|
|
100
|
+
}
|
|
101
|
+
const alias = flags['target-stage'];
|
|
102
|
+
if (typeof alias === 'string' && alias.trim().length > 0) {
|
|
103
|
+
return alias.trim();
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
async function handleStart(orchestrator, rawArgs) {
|
|
108
|
+
const { positionals, flags } = parseArgs(rawArgs);
|
|
109
|
+
const pipelineId = positionals[0];
|
|
110
|
+
const format = flags['format'] === 'json' ? 'json' : 'text';
|
|
111
|
+
const interactiveRequested = Boolean(flags['interactive'] || flags['ui']);
|
|
112
|
+
const interactiveDisabled = Boolean(flags['no-interactive']);
|
|
113
|
+
const runEvents = new RunEventEmitter();
|
|
114
|
+
const gate = evaluateInteractiveGate({
|
|
115
|
+
requested: interactiveRequested,
|
|
116
|
+
disabled: interactiveDisabled,
|
|
117
|
+
format,
|
|
118
|
+
stdoutIsTTY: process.stdout.isTTY === true,
|
|
119
|
+
stderrIsTTY: process.stderr.isTTY === true,
|
|
120
|
+
term: process.env.TERM ?? null
|
|
121
|
+
});
|
|
122
|
+
const hud = await maybeStartHud(gate, runEvents);
|
|
123
|
+
if (!gate.enabled && interactiveRequested && !interactiveDisabled && gate.reason) {
|
|
124
|
+
console.error(`[HUD disabled] ${gate.reason}`);
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const result = await orchestrator.start({
|
|
128
|
+
pipelineId,
|
|
129
|
+
taskId: typeof flags['task'] === 'string' ? flags['task'] : undefined,
|
|
130
|
+
parentRunId: typeof flags['parent-run'] === 'string' ? flags['parent-run'] : undefined,
|
|
131
|
+
approvalPolicy: typeof flags['approval-policy'] === 'string' ? flags['approval-policy'] : undefined,
|
|
132
|
+
targetStageId: resolveTargetStageId(flags),
|
|
133
|
+
runEvents
|
|
134
|
+
});
|
|
135
|
+
hud?.stop();
|
|
136
|
+
const payload = {
|
|
137
|
+
run_id: result.manifest.run_id,
|
|
138
|
+
status: result.manifest.status,
|
|
139
|
+
artifact_root: result.manifest.artifact_root,
|
|
140
|
+
manifest: `${result.manifest.artifact_root}/manifest.json`,
|
|
141
|
+
log_path: result.manifest.log_path
|
|
142
|
+
};
|
|
143
|
+
if (format === 'json') {
|
|
144
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
console.log(`Run started: ${payload.run_id}`);
|
|
148
|
+
console.log(`Status: ${payload.status}`);
|
|
149
|
+
console.log(`Manifest: ${payload.manifest}`);
|
|
150
|
+
console.log(`Log: ${payload.log_path}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
hud?.stop();
|
|
155
|
+
runEvents.dispose();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function handlePlan(orchestrator, rawArgs) {
|
|
159
|
+
const { positionals, flags } = parseArgs(rawArgs);
|
|
160
|
+
const pipelineId = positionals[0];
|
|
161
|
+
const format = flags['format'] === 'json' ? 'json' : 'text';
|
|
162
|
+
const result = await orchestrator.plan({
|
|
163
|
+
pipelineId,
|
|
164
|
+
taskId: typeof flags['task'] === 'string' ? flags['task'] : undefined,
|
|
165
|
+
targetStageId: resolveTargetStageId(flags)
|
|
166
|
+
});
|
|
167
|
+
if (format === 'json') {
|
|
168
|
+
console.log(JSON.stringify(result, null, 2));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
process.stdout.write(`${formatPlanPreview(result)}\n`);
|
|
172
|
+
}
|
|
173
|
+
async function handleResume(orchestrator, rawArgs) {
|
|
174
|
+
const { positionals, flags } = parseArgs(rawArgs);
|
|
175
|
+
const runId = (flags['run'] ?? positionals[0]);
|
|
176
|
+
if (!runId) {
|
|
177
|
+
throw new Error('resume requires --run <run-id>.');
|
|
178
|
+
}
|
|
179
|
+
const format = flags['format'] === 'json' ? 'json' : 'text';
|
|
180
|
+
const interactiveRequested = Boolean(flags['interactive'] || flags['ui']);
|
|
181
|
+
const interactiveDisabled = Boolean(flags['no-interactive']);
|
|
182
|
+
const runEvents = new RunEventEmitter();
|
|
183
|
+
const gate = evaluateInteractiveGate({
|
|
184
|
+
requested: interactiveRequested,
|
|
185
|
+
disabled: interactiveDisabled,
|
|
186
|
+
format,
|
|
187
|
+
stdoutIsTTY: process.stdout.isTTY === true,
|
|
188
|
+
stderrIsTTY: process.stderr.isTTY === true,
|
|
189
|
+
term: process.env.TERM ?? null
|
|
190
|
+
});
|
|
191
|
+
const hud = await maybeStartHud(gate, runEvents);
|
|
192
|
+
if (!gate.enabled && interactiveRequested && !interactiveDisabled && gate.reason) {
|
|
193
|
+
console.error(`[HUD disabled] ${gate.reason}`);
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const result = await orchestrator.resume({
|
|
197
|
+
runId,
|
|
198
|
+
resumeToken: typeof flags['token'] === 'string' ? flags['token'] : undefined,
|
|
199
|
+
actor: typeof flags['actor'] === 'string' ? flags['actor'] : undefined,
|
|
200
|
+
reason: typeof flags['reason'] === 'string' ? flags['reason'] : undefined,
|
|
201
|
+
targetStageId: resolveTargetStageId(flags),
|
|
202
|
+
runEvents
|
|
203
|
+
});
|
|
204
|
+
hud?.stop();
|
|
205
|
+
const payload = {
|
|
206
|
+
run_id: result.manifest.run_id,
|
|
207
|
+
status: result.manifest.status,
|
|
208
|
+
artifact_root: result.manifest.artifact_root,
|
|
209
|
+
manifest: `${result.manifest.artifact_root}/manifest.json`,
|
|
210
|
+
log_path: result.manifest.log_path
|
|
211
|
+
};
|
|
212
|
+
if (format === 'json') {
|
|
213
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
console.log(`Run resumed: ${payload.run_id}`);
|
|
217
|
+
console.log(`Status: ${payload.status}`);
|
|
218
|
+
console.log(`Manifest: ${payload.manifest}`);
|
|
219
|
+
console.log(`Log: ${payload.log_path}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
hud?.stop();
|
|
224
|
+
runEvents.dispose();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function handleStatus(orchestrator, rawArgs) {
|
|
228
|
+
const { positionals, flags } = parseArgs(rawArgs);
|
|
229
|
+
const runId = (flags['run'] ?? positionals[0]);
|
|
230
|
+
if (!runId) {
|
|
231
|
+
throw new Error('status requires --run <run-id>.');
|
|
232
|
+
}
|
|
233
|
+
const watch = Boolean(flags['watch']);
|
|
234
|
+
const format = flags['format'] === 'json' ? 'json' : 'text';
|
|
235
|
+
const interval = parseInt(flags['interval'] ?? '10', 10);
|
|
236
|
+
if (!watch) {
|
|
237
|
+
await orchestrator.status({ runId, format });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const terminal = new Set(['succeeded', 'failed', 'cancelled']);
|
|
241
|
+
while (true) {
|
|
242
|
+
const manifest = await orchestrator.status({ runId, format });
|
|
243
|
+
if (terminal.has(manifest.status)) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function maybeStartHud(gate, emitter) {
|
|
250
|
+
if (!gate.enabled) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const { startHud } = await import('../orchestrator/src/cli/ui/controller.js');
|
|
254
|
+
return startHud({ emitter, footerNote: 'interactive HUD (read-only)' });
|
|
255
|
+
}
|
|
256
|
+
async function handleExec(rawArgs) {
|
|
257
|
+
const parsed = parseExecArgs(rawArgs);
|
|
258
|
+
if (parsed.commandTokens.length === 0) {
|
|
259
|
+
throw new Error('exec requires a command to run.');
|
|
260
|
+
}
|
|
261
|
+
const isInteractive = process.stdout.isTTY === true && process.stderr.isTTY === true;
|
|
262
|
+
const outputMode = parsed.requestedMode ?? (isInteractive ? 'interactive' : 'jsonl');
|
|
263
|
+
const env = resolveEnvironment();
|
|
264
|
+
if (parsed.taskId) {
|
|
265
|
+
env.taskId = sanitizeTaskId(parsed.taskId);
|
|
266
|
+
}
|
|
267
|
+
const context = {
|
|
268
|
+
env,
|
|
269
|
+
stdout: process.stdout,
|
|
270
|
+
stderr: process.stderr
|
|
271
|
+
};
|
|
272
|
+
const invocation = {
|
|
273
|
+
command: parsed.commandTokens[0],
|
|
274
|
+
args: parsed.commandTokens.slice(1),
|
|
275
|
+
cwd: parsed.cwd,
|
|
276
|
+
outputMode,
|
|
277
|
+
notifyTargets: parsed.notifyTargets,
|
|
278
|
+
otelEndpoint: parsed.otelEndpoint,
|
|
279
|
+
jsonPretty: parsed.jsonPretty
|
|
280
|
+
};
|
|
281
|
+
const result = await executeExecCommand(context, invocation);
|
|
282
|
+
if (result.exitCode !== null) {
|
|
283
|
+
process.exitCode = result.exitCode;
|
|
284
|
+
}
|
|
285
|
+
else if (result.status !== 'succeeded') {
|
|
286
|
+
process.exitCode = 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function handleSelfCheck(rawArgs) {
|
|
290
|
+
const { flags } = parseArgs(rawArgs);
|
|
291
|
+
const format = flags['format'] === 'json' ? 'json' : 'text';
|
|
292
|
+
const result = buildSelfCheckResult();
|
|
293
|
+
if (format === 'json') {
|
|
294
|
+
console.log(JSON.stringify(result, null, 2));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
console.log(`Status: ${result.status}`);
|
|
298
|
+
console.log(`Name: ${result.name}`);
|
|
299
|
+
console.log(`Version: ${result.version}`);
|
|
300
|
+
console.log(`Node: ${result.node}`);
|
|
301
|
+
console.log(`Timestamp: ${result.timestamp}`);
|
|
302
|
+
}
|
|
303
|
+
async function handleInit(rawArgs) {
|
|
304
|
+
const { positionals, flags } = parseArgs(rawArgs);
|
|
305
|
+
const template = positionals[0];
|
|
306
|
+
if (!template) {
|
|
307
|
+
throw new Error('init requires a template name (e.g. init codex).');
|
|
308
|
+
}
|
|
309
|
+
if (template !== 'codex') {
|
|
310
|
+
throw new Error(`Unknown init template: ${template}`);
|
|
311
|
+
}
|
|
312
|
+
const cwd = typeof flags['cwd'] === 'string' ? flags['cwd'] : process.cwd();
|
|
313
|
+
const force = Boolean(flags['force']);
|
|
314
|
+
const result = await initCodexTemplates({ template, cwd, force });
|
|
315
|
+
const summary = formatInitSummary(result, cwd);
|
|
316
|
+
for (const line of summary) {
|
|
317
|
+
console.log(line);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async function handleDoctor(rawArgs) {
|
|
321
|
+
const { flags } = parseArgs(rawArgs);
|
|
322
|
+
const format = flags['format'] === 'json' ? 'json' : 'text';
|
|
323
|
+
const result = runDoctor();
|
|
324
|
+
if (format === 'json') {
|
|
325
|
+
console.log(JSON.stringify(result, null, 2));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const summary = formatDoctorSummary(result);
|
|
329
|
+
for (const line of summary) {
|
|
330
|
+
console.log(line);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function handleMcp(rawArgs) {
|
|
334
|
+
const { positionals, flags } = parseArgs(rawArgs);
|
|
335
|
+
const subcommand = positionals.shift();
|
|
336
|
+
if (!subcommand) {
|
|
337
|
+
throw new Error('mcp requires a subcommand (serve).');
|
|
338
|
+
}
|
|
339
|
+
if (subcommand !== 'serve') {
|
|
340
|
+
throw new Error(`Unknown mcp subcommand: ${subcommand}`);
|
|
341
|
+
}
|
|
342
|
+
const repoRoot = typeof flags['repo'] === 'string' ? flags['repo'] : undefined;
|
|
343
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
344
|
+
await serveMcp({ repoRoot, dryRun, extraArgs: positionals });
|
|
345
|
+
}
|
|
346
|
+
function parseExecArgs(rawArgs) {
|
|
347
|
+
const notifyTargets = [];
|
|
348
|
+
let otelEndpoint = null;
|
|
349
|
+
let requestedMode = null;
|
|
350
|
+
let jsonPretty = true;
|
|
351
|
+
let cwd;
|
|
352
|
+
let taskId;
|
|
353
|
+
const commandTokens = [];
|
|
354
|
+
let afterDoubleDash = false;
|
|
355
|
+
const readValue = (currentIndex, inlineValue) => {
|
|
356
|
+
if (typeof inlineValue === 'string') {
|
|
357
|
+
return { value: inlineValue, nextIndex: currentIndex };
|
|
358
|
+
}
|
|
359
|
+
const nextToken = rawArgs[currentIndex + 1];
|
|
360
|
+
if (nextToken && !nextToken.startsWith('--')) {
|
|
361
|
+
return { value: nextToken, nextIndex: currentIndex + 1 };
|
|
362
|
+
}
|
|
363
|
+
return { value: null, nextIndex: currentIndex };
|
|
364
|
+
};
|
|
365
|
+
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
366
|
+
const token = rawArgs[i];
|
|
367
|
+
if (afterDoubleDash) {
|
|
368
|
+
commandTokens.push(token);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (token === '--') {
|
|
372
|
+
afterDoubleDash = true;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (!token.startsWith('--')) {
|
|
376
|
+
commandTokens.push(token);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const [rawKey, inlineValue] = token.slice(2).split('=', 2);
|
|
380
|
+
switch (rawKey) {
|
|
381
|
+
case 'json': {
|
|
382
|
+
if (requestedMode && requestedMode !== 'json') {
|
|
383
|
+
throw new Error('Cannot combine --json with other output modes.');
|
|
384
|
+
}
|
|
385
|
+
requestedMode = 'json';
|
|
386
|
+
let modeValue = inlineValue;
|
|
387
|
+
if (!modeValue) {
|
|
388
|
+
const probe = rawArgs[i + 1];
|
|
389
|
+
if (probe === 'compact' || probe === 'pretty') {
|
|
390
|
+
modeValue = probe;
|
|
391
|
+
i += 1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
jsonPretty = modeValue === 'compact' ? false : true;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
case 'jsonl':
|
|
398
|
+
if (requestedMode && requestedMode !== 'jsonl') {
|
|
399
|
+
throw new Error('Cannot combine --jsonl with other output modes.');
|
|
400
|
+
}
|
|
401
|
+
requestedMode = 'jsonl';
|
|
402
|
+
break;
|
|
403
|
+
case 'notify': {
|
|
404
|
+
const { value, nextIndex } = readValue(i, inlineValue);
|
|
405
|
+
if (!value) {
|
|
406
|
+
throw new Error('--notify requires a target URI.');
|
|
407
|
+
}
|
|
408
|
+
i = nextIndex;
|
|
409
|
+
value
|
|
410
|
+
.split(',')
|
|
411
|
+
.map((entry) => entry.trim())
|
|
412
|
+
.filter(Boolean)
|
|
413
|
+
.forEach((entry) => notifyTargets.push(entry));
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
case 'otel-endpoint': {
|
|
417
|
+
const { value, nextIndex } = readValue(i, inlineValue);
|
|
418
|
+
if (!value) {
|
|
419
|
+
throw new Error('--otel-endpoint requires a URL.');
|
|
420
|
+
}
|
|
421
|
+
i = nextIndex;
|
|
422
|
+
otelEndpoint = value;
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case 'cwd': {
|
|
426
|
+
const { value, nextIndex } = readValue(i, inlineValue);
|
|
427
|
+
if (!value) {
|
|
428
|
+
throw new Error('--cwd requires a path.');
|
|
429
|
+
}
|
|
430
|
+
i = nextIndex;
|
|
431
|
+
cwd = value;
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case 'task': {
|
|
435
|
+
const { value, nextIndex } = readValue(i, inlineValue);
|
|
436
|
+
if (!value) {
|
|
437
|
+
throw new Error('--task requires an identifier.');
|
|
438
|
+
}
|
|
439
|
+
i = nextIndex;
|
|
440
|
+
taskId = value;
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
default:
|
|
444
|
+
throw new Error(`Unknown exec option: --${rawKey}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
commandTokens,
|
|
449
|
+
notifyTargets,
|
|
450
|
+
otelEndpoint,
|
|
451
|
+
requestedMode,
|
|
452
|
+
jsonPretty,
|
|
453
|
+
cwd,
|
|
454
|
+
taskId
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function printHelp() {
|
|
458
|
+
console.log(`Usage: codex-orchestrator <command> [options]
|
|
459
|
+
|
|
460
|
+
Commands:
|
|
461
|
+
start [pipeline] Start a new run (default pipeline is diagnostics).
|
|
462
|
+
--task <id> Override task identifier (defaults to MCP_RUNNER_TASK_ID).
|
|
463
|
+
--parent-run <id> Link run to parent run id.
|
|
464
|
+
--approval-policy <p> Record approval policy metadata.
|
|
465
|
+
--format json Emit machine-readable output.
|
|
466
|
+
--target <stage-id> Focus plan/build metadata on a specific stage (alias: --target-stage).
|
|
467
|
+
--interactive | --ui Enable read-only HUD when running in a TTY.
|
|
468
|
+
--no-interactive Force disable HUD (default is off unless requested).
|
|
469
|
+
|
|
470
|
+
plan [pipeline] Preview pipeline stages without executing.
|
|
471
|
+
--task <id> Override task identifier.
|
|
472
|
+
--format json Emit machine-readable output.
|
|
473
|
+
--target <stage-id> Highlight the stage chosen for orchestration (alias: --target-stage).
|
|
474
|
+
|
|
475
|
+
exec [command] Run a one-off command with unified exec runtime.
|
|
476
|
+
--json [compact] Emit final JSON summary (optional compact mode).
|
|
477
|
+
--jsonl Stream JSONL events (default when STDIN is non-interactive).
|
|
478
|
+
--notify <uri> Send run summary notifications (repeatable, comma-separated supported).
|
|
479
|
+
--otel-endpoint <url> Forward events to OTEL collector endpoint.
|
|
480
|
+
--cwd <path> Override working directory for the command.
|
|
481
|
+
--task <id> Override task identifier for manifest routing.
|
|
482
|
+
|
|
483
|
+
resume --run <id> [options]
|
|
484
|
+
--token <resume-token> Verify the resume token before restarting.
|
|
485
|
+
--actor <name> Record who resumed the run.
|
|
486
|
+
--reason <text> Record why the run was resumed.
|
|
487
|
+
--target <stage-id> Override stage selection before resuming (alias: --target-stage).
|
|
488
|
+
--format json Emit machine-readable output.
|
|
489
|
+
--interactive | --ui Enable read-only HUD when running in a TTY.
|
|
490
|
+
--no-interactive Force disable HUD (default is off unless requested).
|
|
491
|
+
|
|
492
|
+
status --run <id> [--watch] [--interval N] [--format json]
|
|
493
|
+
|
|
494
|
+
self-check [--format json]
|
|
495
|
+
init codex [--cwd <path>] [--force]
|
|
496
|
+
doctor [--format json]
|
|
497
|
+
mcp serve [--repo <path>] [--dry-run] [-- <extra args>]
|
|
498
|
+
version | --version
|
|
499
|
+
|
|
500
|
+
help Show this message.
|
|
501
|
+
`);
|
|
502
|
+
}
|
|
503
|
+
void main();
|
|
504
|
+
function printVersion() {
|
|
505
|
+
const pkg = loadPackageInfo();
|
|
506
|
+
console.log(pkg.version ?? 'unknown');
|
|
507
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class FunctionalBuilderAgent {
|
|
2
|
+
strategy;
|
|
3
|
+
constructor(strategy) {
|
|
4
|
+
this.strategy = strategy;
|
|
5
|
+
}
|
|
6
|
+
async build(input) {
|
|
7
|
+
const result = await this.strategy(input);
|
|
8
|
+
if (!result || !result.subtaskId) {
|
|
9
|
+
throw new Error('Builder strategy must return a BuildResult with subtaskId');
|
|
10
|
+
}
|
|
11
|
+
if (typeof result.success !== 'boolean') {
|
|
12
|
+
throw new Error('Builder strategy must return a BuildResult with success flag');
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Planner agent that delegates to a provided strategy function. This keeps the
|
|
3
|
+
* orchestrator core agnostic of how plans are generated (Agents SDK, MCP, etc.).
|
|
4
|
+
*/
|
|
5
|
+
export class FunctionalPlannerAgent {
|
|
6
|
+
strategy;
|
|
7
|
+
constructor(strategy) {
|
|
8
|
+
this.strategy = strategy;
|
|
9
|
+
}
|
|
10
|
+
async plan(context) {
|
|
11
|
+
const result = await this.strategy(context);
|
|
12
|
+
if (!result || !Array.isArray(result.items)) {
|
|
13
|
+
throw new Error('Planner strategy must return PlanResult.items');
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class FunctionalReviewerAgent {
|
|
2
|
+
strategy;
|
|
3
|
+
constructor(strategy) {
|
|
4
|
+
this.strategy = strategy;
|
|
5
|
+
}
|
|
6
|
+
async review(input) {
|
|
7
|
+
const result = await this.strategy(input);
|
|
8
|
+
if (!result || !result.decision) {
|
|
9
|
+
throw new Error('Reviewer strategy must include a decision');
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class FunctionalTesterAgent {
|
|
2
|
+
strategy;
|
|
3
|
+
constructor(strategy) {
|
|
4
|
+
this.strategy = strategy;
|
|
5
|
+
}
|
|
6
|
+
async test(input) {
|
|
7
|
+
const result = await this.strategy(input);
|
|
8
|
+
if (!result || typeof result.success !== 'boolean') {
|
|
9
|
+
throw new Error('Tester strategy must return a TestResult with success flag');
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class CommandBuilder {
|
|
2
|
+
executePipeline;
|
|
3
|
+
constructor(executePipeline) {
|
|
4
|
+
this.executePipeline = executePipeline;
|
|
5
|
+
}
|
|
6
|
+
async build(input) {
|
|
7
|
+
const result = await this.executePipeline();
|
|
8
|
+
return {
|
|
9
|
+
subtaskId: input.target.id,
|
|
10
|
+
artifacts: [
|
|
11
|
+
{ path: result.manifestPath, description: 'CLI run manifest' },
|
|
12
|
+
{ path: result.logPath, description: 'Runner log (ndjson)' }
|
|
13
|
+
],
|
|
14
|
+
mode: input.mode,
|
|
15
|
+
runId: input.runId,
|
|
16
|
+
success: result.success,
|
|
17
|
+
notes: result.notes.join('\n') || undefined
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|