@oisincoveney/pipeline 2.8.4 → 2.9.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/.agents/skills/orchestrate/SKILL.md +45 -32
- package/README.md +51 -41
- package/defaults/profiles.yaml +1 -1
- package/dist/argo-submit.js +1 -1
- package/dist/cli/doctor.d.ts +21 -0
- package/dist/cli/doctor.js +268 -0
- package/dist/cli/format.js +6 -3
- package/dist/cli/program.d.ts +14 -16
- package/dist/cli/program.js +291 -104
- package/dist/cli/run-resolver.js +58 -0
- package/dist/commands/bench-command.js +12 -4
- package/dist/commands/pipeline-command.js +22 -5
- package/dist/commands/runner-command-command.js +32 -9
- package/dist/config/lint.js +44 -26
- package/dist/context/repo-map.js +72 -56
- package/dist/gates.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +20 -14
- package/dist/install-commands/claude-code.js +4 -33
- package/dist/install-commands/opencode.js +119 -171
- package/dist/mcp/repo-local-backends.js +51 -39
- package/dist/moka-submit.d.ts +6 -6
- package/dist/moka-submit.js +3 -3
- package/dist/pipeline-runtime.js +15 -5
- package/dist/planning/generate.js +2 -2
- package/dist/run-control/commands.js +340 -0
- package/dist/run-control/contracts.d.ts +21 -0
- package/dist/run-control/contracts.js +129 -0
- package/dist/run-control/detach.js +79 -0
- package/dist/run-control/runtime-reporter.js +187 -0
- package/dist/run-control/store.js +304 -0
- package/dist/run-control/supervisor.js +192 -0
- package/dist/runner-command/finalize.js +28 -37
- package/dist/runner-command/lifecycle-context.js +130 -63
- package/dist/runner-command/lifecycle.js +22 -31
- package/dist/runner-command/run.js +120 -72
- package/dist/runner-command/task-descriptor.js +11 -4
- package/dist/runner-event-schema.d.ts +6 -6
- package/dist/runner.js +1 -1
- package/dist/runtime/agent-node/agent-node.js +3 -3
- package/dist/runtime/builtins/builtins.js +1 -1
- package/dist/runtime/changed-files/changed-files.js +1 -1
- package/dist/runtime/context/context.js +1 -1
- package/dist/runtime/contracts/contracts.d.ts +4 -0
- package/dist/runtime/hooks/hooks.js +1 -1
- package/dist/runtime/json-validation/json-validation.js +49 -23
- package/dist/runtime/local-scheduler.js +49 -26
- package/dist/runtime/opencode-adapter.js +14 -10
- package/dist/runtime/opencode-runtime.js +22 -20
- package/dist/runtime/opencode-session-executor.js +1 -1
- package/dist/runtime/parallel-node/parallel-node.js +10 -0
- package/dist/runtime/run-journal.js +17 -10
- package/dist/runtime/services/file-system-service.js +29 -0
- package/dist/runtime/services/opencode-runtime-server-service.js +27 -0
- package/dist/runtime/services/repo-io-service.js +48 -0
- package/dist/runtime/services/run-journal-file-service.js +20 -0
- package/dist/runtime/services/runner-command-io-service.js +88 -0
- package/dist/runtime/workflow-lifecycle.js +76 -39
- package/dist/schedule/backlog-context.js +55 -29
- package/docs/operator-guide.md +73 -32
- package/package.json +1 -1
package/dist/cli/program.d.ts
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
1
|
import { runPipelineFromConfig } from "../pipeline-runtime.js";
|
|
2
|
+
import { RunEffort, RunMode, RunTarget } from "../run-control/contracts.js";
|
|
3
|
+
import { Effect } from "effect";
|
|
2
4
|
import { Command } from "commander";
|
|
3
5
|
|
|
4
6
|
//#region src/cli/program.d.ts
|
|
5
7
|
interface ExecuteOptions {
|
|
6
8
|
entrypoint?: string;
|
|
7
9
|
pipelineRunner?: typeof runPipelineFromConfig;
|
|
10
|
+
runControl?: RunControlOptions;
|
|
11
|
+
runId?: string;
|
|
12
|
+
runStoreMode?: RunStoreMode;
|
|
8
13
|
schedule?: string;
|
|
14
|
+
supervised?: boolean;
|
|
15
|
+
supervisor?: boolean;
|
|
9
16
|
workflow?: string;
|
|
10
17
|
}
|
|
18
|
+
type RunStoreMode = "create" | "reuse";
|
|
19
|
+
interface RunControlOptions {
|
|
20
|
+
effort?: RunEffort;
|
|
21
|
+
mode?: RunMode;
|
|
22
|
+
target?: RunTarget;
|
|
23
|
+
}
|
|
11
24
|
/**
|
|
12
25
|
* Config-driven `execute` entrypoint. Package-owned defaults are the source of
|
|
13
26
|
* truth; repo-local pipeline files are ignored by runtime loading.
|
|
14
27
|
*/
|
|
15
28
|
declare function execute(description: string, options?: ExecuteOptions): Promise<void>;
|
|
16
29
|
declare function quick(description: string, options?: Omit<ExecuteOptions, "entrypoint">): Promise<void>;
|
|
17
|
-
interface DoctorCheck {
|
|
18
|
-
detail: string;
|
|
19
|
-
name: string;
|
|
20
|
-
passed: boolean;
|
|
21
|
-
}
|
|
22
|
-
interface DoctorResult {
|
|
23
|
-
checks: DoctorCheck[];
|
|
24
|
-
passed: boolean;
|
|
25
|
-
}
|
|
26
|
-
interface DoctorFlags {
|
|
27
|
-
cluster?: boolean | string;
|
|
28
|
-
kubeContext?: string;
|
|
29
|
-
kubeconfig?: string;
|
|
30
|
-
}
|
|
31
30
|
declare function createCliProgram(): Command;
|
|
32
31
|
declare function runCli(argv: string[]): Promise<void>;
|
|
33
|
-
declare function runDoctor(cwd: string, options?: DoctorFlags): Promise<DoctorResult>;
|
|
34
32
|
//#endregion
|
|
35
|
-
export { createCliProgram, execute, quick, runCli
|
|
33
|
+
export { createCliProgram, execute, quick, runCli };
|
package/dist/cli/program.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
import { PipelineConfigError } from "../config/schemas.js";
|
|
2
1
|
import { loadPipelineConfig } from "../config/load.js";
|
|
3
2
|
import "../config.js";
|
|
3
|
+
import { configureGatewayHosts, localGatewayStatus, reconcileGateway, renderGatewayConfig, runGatewayDoctor, startLocalGateway } from "../mcp/gateway.js";
|
|
4
4
|
import { createOrchestratorLaunchPlan, createRunnerLaunchPlan } from "../runner.js";
|
|
5
5
|
import { compileWorkflowPlan } from "../planning/compile.js";
|
|
6
|
+
import { generateRuntimeRunId } from "../runtime/context/context.js";
|
|
7
|
+
import "../runtime/context/index.js";
|
|
8
|
+
import { runPipelineFromConfig } from "../pipeline-runtime.js";
|
|
6
9
|
import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "../planning/generate.js";
|
|
7
|
-
import { loadMokaGlobalConfig } from "../moka-global-config.js";
|
|
8
|
-
import { defaultClusterDoctorNamespace, runClusterDoctor } from "../cluster-doctor.js";
|
|
9
10
|
import { formatCodexAuthSyncResult, syncLocalCodexAuth } from "../codex-auth-sync.js";
|
|
10
11
|
import { registerBenchCommand } from "../commands/bench-command.js";
|
|
11
12
|
import { registerConfiguredEntrypointCommands } from "../commands/pipeline-command.js";
|
|
12
|
-
import { configureGatewayHosts, localGatewayStatus, reconcileGateway, renderGatewayConfig, runGatewayDoctor, startLocalGateway } from "../mcp/gateway.js";
|
|
13
|
-
import { generateRuntimeRunId } from "../runtime/context/context.js";
|
|
14
|
-
import "../runtime/context/index.js";
|
|
15
|
-
import { runPipelineFromConfig } from "../pipeline-runtime.js";
|
|
16
13
|
import { registerRunnerCommandCommand } from "../commands/runner-command-command.js";
|
|
17
14
|
import { formatConfigLintWarning, lintPipelineConfig } from "../config/lint.js";
|
|
18
15
|
import { formatInstallCommandsResult, installCommands, parseCommandHost } from "../install-commands.js";
|
|
19
16
|
import { formatPipelineInitResult, initPipelineProject } from "../pipeline-init.js";
|
|
17
|
+
import { createRun, runControlStatusPaths, updateRunController } from "../run-control/store.js";
|
|
18
|
+
import { registerRunControlCommands } from "../run-control/commands.js";
|
|
19
|
+
import { startDetachedRunController } from "../run-control/detach.js";
|
|
20
|
+
import { createRunStoreRuntimeReporter } from "../run-control/runtime-reporter.js";
|
|
21
|
+
import { createRunControlSupervisor } from "../run-control/supervisor.js";
|
|
22
|
+
import { runDoctor } from "./doctor.js";
|
|
20
23
|
import { createTerminalRuntimeReporter, formatDoctorResult, formatRuntimeFailure, formatRuntimeResult } from "./format.js";
|
|
24
|
+
import { MOKA_RUN_EFFORTS, MOKA_RUN_TARGETS, resolveMokaRun } from "./run-resolver.js";
|
|
21
25
|
import { addMokaSubmitOptions, runMokaSubmitFromCli } from "./submit-options.js";
|
|
26
|
+
import { Effect } from "effect";
|
|
22
27
|
import { readFileSync } from "node:fs";
|
|
23
28
|
import { resolve } from "node:path";
|
|
24
|
-
import {
|
|
25
|
-
import { Command, Help, Option } from "commander";
|
|
29
|
+
import { Command, Help, Option as Option$1 } from "commander";
|
|
26
30
|
//#region src/cli/program.ts
|
|
27
31
|
/**
|
|
28
32
|
* Config-driven `execute` entrypoint. Package-owned defaults are the source of
|
|
@@ -35,7 +39,12 @@ function execute(description, options = {}) {
|
|
|
35
39
|
return runConfiguredPipeline({
|
|
36
40
|
pipelineRunner: options.pipelineRunner,
|
|
37
41
|
entrypoint: options.entrypoint,
|
|
42
|
+
runId: options.runId,
|
|
43
|
+
runStoreMode: options.runStoreMode,
|
|
44
|
+
runControl: options.runControl,
|
|
38
45
|
schedule: options.schedule,
|
|
46
|
+
supervised: options.supervised,
|
|
47
|
+
supervisor: options.supervisor,
|
|
39
48
|
task: description,
|
|
40
49
|
workflow: options.workflow,
|
|
41
50
|
worktreePath
|
|
@@ -47,7 +56,11 @@ function execute(description, options = {}) {
|
|
|
47
56
|
function quick(description, options = {}) {
|
|
48
57
|
return execute(description, {
|
|
49
58
|
...options,
|
|
50
|
-
entrypoint: "quick"
|
|
59
|
+
entrypoint: "quick",
|
|
60
|
+
runControl: {
|
|
61
|
+
...options.runControl,
|
|
62
|
+
effort: "quick"
|
|
63
|
+
}
|
|
51
64
|
});
|
|
52
65
|
}
|
|
53
66
|
function withRunId(inputs) {
|
|
@@ -100,18 +113,117 @@ async function runConfiguredPipeline(rawInputs) {
|
|
|
100
113
|
}
|
|
101
114
|
async function runAndPrintPipeline(inputs) {
|
|
102
115
|
const runner = inputs.pipelineRunner ?? runPipelineFromConfig;
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
const runStoreReporter = await createRunStoreReporter(inputs, createTerminalRuntimeReporter());
|
|
117
|
+
if (inputs.supervised) console.log(formatSupervisedRunFollowUp(requireRunId(inputs.runId)));
|
|
118
|
+
let result;
|
|
119
|
+
try {
|
|
120
|
+
result = await runWithFlushedReporter(runStoreReporter.flush, () => runner({
|
|
121
|
+
config: inputs.config,
|
|
122
|
+
reporter: runStoreReporter.reporter,
|
|
123
|
+
entrypoint: inputs.entrypoint,
|
|
124
|
+
runId: inputs.runId,
|
|
125
|
+
task: inputs.task,
|
|
126
|
+
workflowId: inputs.workflow,
|
|
127
|
+
worktreePath: inputs.worktreePath
|
|
128
|
+
}));
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw runtimeErrorWithFollowUp(error, inputs);
|
|
131
|
+
}
|
|
132
|
+
console.log(formatRuntimeResult(result));
|
|
133
|
+
if (result.outcome !== "PASS") throw new Error(formatRuntimeFailureWithFollowUp(result, inputs));
|
|
134
|
+
}
|
|
135
|
+
async function runWithFlushedReporter(flush, run) {
|
|
136
|
+
try {
|
|
137
|
+
return await run();
|
|
138
|
+
} finally {
|
|
139
|
+
await flush();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function createLocalRunStoreRuntimeReporter(inputs, reporter) {
|
|
143
|
+
const runId = requireRunId(inputs.runId);
|
|
144
|
+
await createRun({
|
|
145
|
+
...resolvedRunControlOptions(inputs.runControl),
|
|
146
|
+
nodeIds: plannedRunStoreNodeIds(inputs),
|
|
147
|
+
runId,
|
|
148
|
+
workspaceRoot: inputs.worktreePath
|
|
149
|
+
});
|
|
150
|
+
return createRunStoreRuntimeReporter({
|
|
106
151
|
reporter,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
task: inputs.task,
|
|
110
|
-
workflowId: inputs.workflow,
|
|
111
|
-
worktreePath: inputs.worktreePath
|
|
152
|
+
runId,
|
|
153
|
+
workspaceRoot: inputs.worktreePath
|
|
112
154
|
});
|
|
113
|
-
|
|
114
|
-
|
|
155
|
+
}
|
|
156
|
+
function createRunStoreReporter(inputs, reporter) {
|
|
157
|
+
if (inputs.runStoreMode === "reuse") {
|
|
158
|
+
const runId = requireRunId(inputs.runId);
|
|
159
|
+
if (inputs.supervisor) {
|
|
160
|
+
const supervisor = createRunControlSupervisor({
|
|
161
|
+
reporter,
|
|
162
|
+
runId,
|
|
163
|
+
workspaceRoot: inputs.worktreePath
|
|
164
|
+
});
|
|
165
|
+
supervisor.start();
|
|
166
|
+
return {
|
|
167
|
+
flush: supervisor.stop,
|
|
168
|
+
reporter: supervisor.reporter
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return createRunStoreRuntimeReporter({
|
|
172
|
+
reporter,
|
|
173
|
+
runId,
|
|
174
|
+
workspaceRoot: inputs.worktreePath
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return createLocalRunStoreRuntimeReporter(inputs, reporter);
|
|
178
|
+
}
|
|
179
|
+
function requireRunId(runId) {
|
|
180
|
+
if (!runId) throw new Error("Run id is required for local run-control persistence.");
|
|
181
|
+
return runId;
|
|
182
|
+
}
|
|
183
|
+
function resolvedRunControlOptions(input) {
|
|
184
|
+
return {
|
|
185
|
+
effort: input?.effort ?? "normal",
|
|
186
|
+
mode: input?.mode ?? "write",
|
|
187
|
+
target: input?.target ?? "local"
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function plannedRunStoreNodeIds(inputs) {
|
|
191
|
+
if (inputs.pipelineRunner) return [];
|
|
192
|
+
const workflowId = resolveWorkflowSelection(inputs.config, inputs.workflow, inputs.entrypoint);
|
|
193
|
+
return compileWorkflowPlan(inputs.config, workflowId).topologicalOrder.map((node) => node.id);
|
|
194
|
+
}
|
|
195
|
+
function formatSupervisedRunFollowUp(runId) {
|
|
196
|
+
return [
|
|
197
|
+
`Run id: ${runId}`,
|
|
198
|
+
`Status: moka status ${runId}`,
|
|
199
|
+
`Logs: moka logs ${runId}`
|
|
200
|
+
].join("\n");
|
|
201
|
+
}
|
|
202
|
+
function formatDetachedRunFollowUp(runId) {
|
|
203
|
+
return [
|
|
204
|
+
`Run id: ${runId}`,
|
|
205
|
+
`Status: moka status ${runId}`,
|
|
206
|
+
`Logs: moka logs ${runId}`,
|
|
207
|
+
`Stop: moka stop ${runId}`
|
|
208
|
+
].join("\n");
|
|
209
|
+
}
|
|
210
|
+
function formatRuntimeFailureWithFollowUp(result, inputs) {
|
|
211
|
+
const message = formatRuntimeFailure(result);
|
|
212
|
+
if (!(inputs.supervised && inputs.runId)) return message;
|
|
213
|
+
return [
|
|
214
|
+
message,
|
|
215
|
+
"",
|
|
216
|
+
formatSupervisedRunFollowUp(inputs.runId)
|
|
217
|
+
].join("\n");
|
|
218
|
+
}
|
|
219
|
+
function runtimeErrorWithFollowUp(error, inputs) {
|
|
220
|
+
if (!(inputs.supervised && inputs.runId)) return error;
|
|
221
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
222
|
+
return new Error([
|
|
223
|
+
message,
|
|
224
|
+
"",
|
|
225
|
+
formatSupervisedRunFollowUp(inputs.runId)
|
|
226
|
+
].join("\n"));
|
|
115
227
|
}
|
|
116
228
|
function scheduledEntrypointId(config, workflowId, entrypointId) {
|
|
117
229
|
if (workflowId) return null;
|
|
@@ -124,13 +236,39 @@ function createCliProgram() {
|
|
|
124
236
|
const program = new Command();
|
|
125
237
|
program.name("moka").description("Submit work to Momokaya").version(readPackageVersion()).exitOverride();
|
|
126
238
|
const runAction = async (descriptionParts, flags) => {
|
|
239
|
+
const task = descriptionParts.join(" ");
|
|
240
|
+
const resolution = resolveMokaRun({
|
|
241
|
+
flags,
|
|
242
|
+
task
|
|
243
|
+
});
|
|
244
|
+
if (resolution.execution.kind === "remote-submit") {
|
|
245
|
+
printMokaSubmitResult(await runMokaSubmitFromCli(descriptionParts, remoteSubmitFlags(resolution.execution)));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const runControl = {
|
|
249
|
+
effort: resolution.effort,
|
|
250
|
+
mode: resolution.mode === "read" ? "read-only" : "write",
|
|
251
|
+
target: resolution.target
|
|
252
|
+
};
|
|
253
|
+
if (flags.detach) {
|
|
254
|
+
await runDetachedResolvedTask(task, resolution.execution, runControl);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
await runLocalResolvedTask(task, resolution.execution, runControl);
|
|
258
|
+
};
|
|
259
|
+
program.command("run").description("Primary command: run a workflow from package-owned @oisincoveney/pipeline config").argument("<description...>", "task description").option("--command", "treat input after -- as explicit argv for remote submission").option("--entrypoint <entrypoint>", "entrypoint alias from package config").option("--detach", "start a supervised controller process in the background").addOption(new Option$1("--effort <effort>", "run effort").choices([...MOKA_RUN_EFFORTS]).default("normal")).option("--read-only", "run the read-only inspect workflow").option("--schedule <schedule>", "approved schedule YAML to execute").addOption(new Option$1("--target <target>", "execution target").choices([...MOKA_RUN_TARGETS]).default("local")).option("--workflow <workflow>", "workflow id from package config").action(runAction);
|
|
260
|
+
program.command("run-controller", { hidden: true }).description("Internal detached run controller").argument("<description...>", "task description").requiredOption("--run-id <run-id>", "existing run id to supervise").option("--entrypoint <entrypoint>", "entrypoint alias from package config").option("--schedule <schedule>", "approved schedule YAML to execute").option("--workflow <workflow>", "workflow id from package config").action(async (descriptionParts, flags) => {
|
|
127
261
|
await execute(descriptionParts.join(" "), {
|
|
128
262
|
entrypoint: flags.entrypoint,
|
|
263
|
+
runId: flags.runId,
|
|
264
|
+
runStoreMode: "reuse",
|
|
129
265
|
schedule: flags.schedule,
|
|
266
|
+
supervised: true,
|
|
267
|
+
supervisor: true,
|
|
130
268
|
workflow: flags.workflow
|
|
131
269
|
});
|
|
132
|
-
};
|
|
133
|
-
program
|
|
270
|
+
});
|
|
271
|
+
registerRunControlCommands(program);
|
|
134
272
|
program.command("validate").description("Validate package-owned @oisincoveney/pipeline config and compile the workflow plan").option("--entrypoint <entrypoint>", "entrypoint alias from package config").option("--schedule <schedule>", "approved schedule YAML to validate").option("--strict", "fail when validation lint warnings are emitted").option("--no-lint", "skip validation lint warnings").option("--workflow <workflow>", "workflow id from package config").action((flags) => {
|
|
135
273
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
136
274
|
const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
@@ -145,9 +283,9 @@ function createCliProgram() {
|
|
|
145
283
|
const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
146
284
|
console.log(formatSelectedWorkflowPlan(config, cwd, flags));
|
|
147
285
|
});
|
|
148
|
-
program.command("doctor").description("Check local prerequisites for pipeline init and execution").option("--cluster [namespace]", "also check runner-job Kubernetes prerequisites").option("--kube-context <context>", "kubectl context for cluster checks").option("--kubeconfig <path>", "kubeconfig path for cluster checks").action(async (flags) => {
|
|
286
|
+
program.command("doctor").description("Check local prerequisites for pipeline init and execution").option("--cluster [namespace]", "also check runner-job Kubernetes prerequisites").option("--json", "print machine-readable readiness results").option("--kube-context <context>", "kubectl context for cluster checks").option("--kubeconfig <path>", "kubeconfig path for cluster checks").action(async (flags) => {
|
|
149
287
|
const result = await runDoctor(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), flags);
|
|
150
|
-
console.log(formatDoctorResult(result));
|
|
288
|
+
console.log(flags.json ? JSON.stringify(result) : formatDoctorResult(result));
|
|
151
289
|
if (!result.passed) throw new Error("Doctor checks failed.");
|
|
152
290
|
});
|
|
153
291
|
const gatewayCommand = program.command("mcp").description("Manage the hosted-first MCP gateway").command("gateway").description("Inspect and configure the pipeline MCP gateway");
|
|
@@ -161,7 +299,7 @@ function createCliProgram() {
|
|
|
161
299
|
const config = loadPipelineConfig(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), { allowMissingLintFileReferences: true });
|
|
162
300
|
console.log(renderGatewayConfig(config));
|
|
163
301
|
});
|
|
164
|
-
gatewayCommand.command("configure-host").description("Rewrite host MCP config to the singleton pipeline gateway").addOption(new Option("--host <host>", "host config to update").choices(["all", "opencode"]).default("all").argParser(parseGatewayHost)).addOption(new Option("--scope <scope>", "config scope to update").choices(["project", "global"]).default("project").argParser(parseGatewayHostScope)).action((flags) => {
|
|
302
|
+
gatewayCommand.command("configure-host").description("Rewrite host MCP config to the singleton pipeline gateway").addOption(new Option$1("--host <host>", "host config to update").choices(["all", "opencode"]).default("all").argParser(parseGatewayHost)).addOption(new Option$1("--scope <scope>", "config scope to update").choices(["project", "global"]).default("project").argParser(parseGatewayHostScope)).action((flags) => {
|
|
165
303
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
166
304
|
const result = configureGatewayHosts(loadPipelineConfig(cwd, { allowMissingLintFileReferences: true }), {
|
|
167
305
|
cwd,
|
|
@@ -189,14 +327,14 @@ function createCliProgram() {
|
|
|
189
327
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
190
328
|
console.log(await localGatewayStatus(cwd));
|
|
191
329
|
});
|
|
192
|
-
program.command("init").description("Initialize package-owned pipeline support without repo-local config").addOption(new Option("--skill-scope <scope>", "where to install default skills: project (repo-local copy) or personal (one inherited user/global install)").choices(["project", "personal"]).default("project")).action(async (flags) => {
|
|
330
|
+
program.command("init").description("Initialize package-owned pipeline support without repo-local config").addOption(new Option$1("--skill-scope <scope>", "where to install default skills: project (repo-local copy) or personal (one inherited user/global install)").choices(["project", "personal"]).default("project")).action(async (flags) => {
|
|
193
331
|
const result = await initPipelineProject({
|
|
194
332
|
cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd(),
|
|
195
333
|
scope: flags.skillScope
|
|
196
334
|
});
|
|
197
335
|
console.log(formatPipelineInitResult(result));
|
|
198
336
|
});
|
|
199
|
-
program.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option("--host <host>", "host command set to install").choices([
|
|
337
|
+
program.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option$1("--host <host>", "host command set to install").choices([
|
|
200
338
|
"all",
|
|
201
339
|
"opencode",
|
|
202
340
|
"claude-code"
|
|
@@ -216,14 +354,17 @@ function createCliProgram() {
|
|
|
216
354
|
console.log(formatCodexAuthSyncResult(result));
|
|
217
355
|
if (!result.ok) process.exitCode = 1;
|
|
218
356
|
});
|
|
219
|
-
addMokaSubmitOptions(program.command("submit").description(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
357
|
+
addMokaSubmitOptions(program.command("submit").description([
|
|
358
|
+
"Submit work to Momokaya as an Argo Workflow.",
|
|
359
|
+
"Compatibility alias for `moka run \"<task>\" --target remote --effort thorough`.",
|
|
360
|
+
"Quick equivalent: `moka run \"<task>\" --target remote --effort quick`.",
|
|
361
|
+
"Command equivalent: `moka run --target remote --command -- <argv...>`."
|
|
362
|
+
].join("\n")).argument("[input...]", "task description, or command argv with --command")).action(async (input, flags) => {
|
|
363
|
+
printMokaSubmitResult(await runMokaSubmitFromCli(input, flags));
|
|
223
364
|
});
|
|
224
365
|
registerRunnerCommandCommand(program);
|
|
225
366
|
registerBenchCommand(program);
|
|
226
|
-
const configuredEntrypointCommands = registerConfiguredEntrypointCommands(program,
|
|
367
|
+
const configuredEntrypointCommands = registerConfiguredEntrypointCommands(program, compatibilityPresetDescriptions(configuredPipeline), async (entrypoint, task, _opts) => {
|
|
227
368
|
await execute(task, { entrypoint });
|
|
228
369
|
});
|
|
229
370
|
if (configuredEntrypointCommands.size > 0) program.configureHelp({ subcommandTerm(command) {
|
|
@@ -232,6 +373,107 @@ function createCliProgram() {
|
|
|
232
373
|
} });
|
|
233
374
|
return program;
|
|
234
375
|
}
|
|
376
|
+
function runLocalResolvedTask(task, execution, runControl) {
|
|
377
|
+
return execute(task, {
|
|
378
|
+
entrypoint: execution.entrypoint,
|
|
379
|
+
runControl,
|
|
380
|
+
schedule: execution.schedule,
|
|
381
|
+
supervised: true,
|
|
382
|
+
workflow: execution.workflow
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
async function runDetachedResolvedTask(task, execution, runControl) {
|
|
386
|
+
const worktreePath = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
387
|
+
const runId = generateRuntimeRunId();
|
|
388
|
+
const prepared = await prepareDetachedRun({
|
|
389
|
+
config: loadPipelineConfig(worktreePath, { allowMissingLintFileReferences: true }),
|
|
390
|
+
execution,
|
|
391
|
+
runId,
|
|
392
|
+
task,
|
|
393
|
+
worktreePath
|
|
394
|
+
});
|
|
395
|
+
await createRun({
|
|
396
|
+
...resolvedRunControlOptions(runControl),
|
|
397
|
+
nodeIds: plannedRunStoreNodeIds({
|
|
398
|
+
config: prepared.config,
|
|
399
|
+
entrypoint: prepared.entrypoint,
|
|
400
|
+
runId,
|
|
401
|
+
runControl,
|
|
402
|
+
schedule: prepared.schedule,
|
|
403
|
+
task,
|
|
404
|
+
workflow: prepared.workflow,
|
|
405
|
+
worktreePath
|
|
406
|
+
}),
|
|
407
|
+
runId,
|
|
408
|
+
workspaceRoot: worktreePath
|
|
409
|
+
});
|
|
410
|
+
const launch = await startDetachedRunController({
|
|
411
|
+
entrypoint: prepared.entrypoint,
|
|
412
|
+
runId,
|
|
413
|
+
schedule: prepared.schedule,
|
|
414
|
+
task,
|
|
415
|
+
workflow: prepared.workflow,
|
|
416
|
+
workspaceRoot: worktreePath
|
|
417
|
+
});
|
|
418
|
+
await updateRunController({
|
|
419
|
+
controller: {
|
|
420
|
+
argv: launch.argv,
|
|
421
|
+
cwd: worktreePath,
|
|
422
|
+
paths: runControlStatusPaths({
|
|
423
|
+
runId,
|
|
424
|
+
workspaceRoot: worktreePath
|
|
425
|
+
}),
|
|
426
|
+
pid: launch.pid,
|
|
427
|
+
startedAt: launch.startedAt
|
|
428
|
+
},
|
|
429
|
+
runId,
|
|
430
|
+
workspaceRoot: worktreePath
|
|
431
|
+
});
|
|
432
|
+
console.log(formatDetachedRunFollowUp(runId));
|
|
433
|
+
}
|
|
434
|
+
async function prepareDetachedRun(input) {
|
|
435
|
+
if (input.execution.schedule) {
|
|
436
|
+
const schedule = resolve(input.execution.schedule);
|
|
437
|
+
const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(readFileSync(schedule, "utf8"), schedule), input.worktreePath);
|
|
438
|
+
return {
|
|
439
|
+
config: compiled.config,
|
|
440
|
+
schedule,
|
|
441
|
+
workflow: compiled.workflowId
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const scheduledEntrypoint = scheduledEntrypointId(input.config, input.execution.workflow, input.execution.entrypoint);
|
|
445
|
+
if (!scheduledEntrypoint) return {
|
|
446
|
+
config: input.config,
|
|
447
|
+
entrypoint: input.execution.entrypoint,
|
|
448
|
+
workflow: input.execution.workflow
|
|
449
|
+
};
|
|
450
|
+
const result = await generateScheduleArtifact({
|
|
451
|
+
config: input.config,
|
|
452
|
+
entrypointId: scheduledEntrypoint,
|
|
453
|
+
runId: input.runId,
|
|
454
|
+
task: input.task,
|
|
455
|
+
worktreePath: input.worktreePath
|
|
456
|
+
});
|
|
457
|
+
console.log(`Schedule generated: ${result.path}`);
|
|
458
|
+
const schedule = resolve(input.worktreePath, result.path);
|
|
459
|
+
const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(readFileSync(schedule, "utf8"), result.path), input.worktreePath);
|
|
460
|
+
return {
|
|
461
|
+
config: compiled.config,
|
|
462
|
+
schedule,
|
|
463
|
+
workflow: compiled.workflowId
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function remoteSubmitFlags(execution) {
|
|
467
|
+
return {
|
|
468
|
+
command: execution.command,
|
|
469
|
+
quick: execution.mode === "quick",
|
|
470
|
+
schedule: execution.schedule
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function printMokaSubmitResult(result) {
|
|
474
|
+
console.log(`Workflow submitted: ${result.workflowName} in ${result.namespace}`);
|
|
475
|
+
if (result.workflowUid) console.log(`Workflow UID: ${result.workflowUid}`);
|
|
476
|
+
}
|
|
235
477
|
function readPackageVersion() {
|
|
236
478
|
const packageJson = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8"));
|
|
237
479
|
if (typeof packageJson.version !== "string") throw new Error("Unable to read @oisincoveney/pipeline package version.");
|
|
@@ -240,11 +482,18 @@ function readPackageVersion() {
|
|
|
240
482
|
function loadConfiguredEntrypoints(cwd) {
|
|
241
483
|
return loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
242
484
|
}
|
|
243
|
-
function
|
|
244
|
-
const
|
|
485
|
+
function compatibilityPresetDescriptions(config) {
|
|
486
|
+
const descriptions = {
|
|
487
|
+
execute: "Compatibility preset for `moka run --effort thorough`: full planner-generated pipeline for repository work",
|
|
488
|
+
inspect: "Compatibility preset for `moka run --read-only`: read-only repository inspection",
|
|
489
|
+
quick: "Compatibility preset for `moka run --effort quick`: compact planner-generated pipeline for small work"
|
|
490
|
+
};
|
|
245
491
|
return {
|
|
246
492
|
...config,
|
|
247
|
-
entrypoints: Object.fromEntries(Object.entries(config.entrypoints).
|
|
493
|
+
entrypoints: Object.fromEntries(Object.entries(config.entrypoints).map(([id, entrypoint]) => [id, descriptions[id] ? {
|
|
494
|
+
...entrypoint,
|
|
495
|
+
description: descriptions[id]
|
|
496
|
+
} : entrypoint]))
|
|
248
497
|
};
|
|
249
498
|
}
|
|
250
499
|
function parseGatewayHostScope(value) {
|
|
@@ -255,77 +504,15 @@ function parseGatewayHost(value) {
|
|
|
255
504
|
if (value === "all" || value === "opencode") return value;
|
|
256
505
|
throw new Error("host must be all or opencode");
|
|
257
506
|
}
|
|
507
|
+
function runCliEffect(argv) {
|
|
508
|
+
return Effect.tryPromise({
|
|
509
|
+
catch: (error) => error,
|
|
510
|
+
try: () => createCliProgram().parseAsync(argv, { from: "node" })
|
|
511
|
+
}).pipe(Effect.asVoid);
|
|
512
|
+
}
|
|
258
513
|
async function runCli(argv) {
|
|
259
514
|
await createCliProgram().parseAsync(argv, { from: "node" });
|
|
260
515
|
}
|
|
261
|
-
async function runDoctor(cwd, options = {}) {
|
|
262
|
-
const commandChecks = await Promise.all([
|
|
263
|
-
checkCommand("npx", ["--version"], cwd),
|
|
264
|
-
checkCommand("opencode", ["--version"], cwd),
|
|
265
|
-
checkCommand("fallow", ["--version"], cwd)
|
|
266
|
-
]);
|
|
267
|
-
const configCheck = checkPipelineConfig(cwd);
|
|
268
|
-
const globalConfig = loadMokaGlobalConfig();
|
|
269
|
-
const clusterResult = options.cluster ? await runClusterDoctor({
|
|
270
|
-
kubeContext: options.kubeContext,
|
|
271
|
-
kubeconfigPath: options.kubeconfig ?? globalConfig?.momokaya.kubernetes.kubeconfig,
|
|
272
|
-
namespace: clusterNamespace(options.cluster, globalConfig?.momokaya.kubernetes.namespace)
|
|
273
|
-
}) : { checks: [] };
|
|
274
|
-
const checks = [
|
|
275
|
-
...commandChecks,
|
|
276
|
-
configCheck,
|
|
277
|
-
...clusterResult.checks
|
|
278
|
-
];
|
|
279
|
-
return {
|
|
280
|
-
checks,
|
|
281
|
-
passed: checks.every((check) => check.passed)
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
function clusterNamespace(value, configuredNamespace) {
|
|
285
|
-
return typeof value === "string" && value.length > 0 ? value : configuredNamespace ?? defaultClusterDoctorNamespace();
|
|
286
|
-
}
|
|
287
|
-
function checkCommand(name, args, cwd) {
|
|
288
|
-
return checkCommandWithRunner(name, name, args, cwd);
|
|
289
|
-
}
|
|
290
|
-
async function checkCommandWithRunner(name, command, args, cwd) {
|
|
291
|
-
try {
|
|
292
|
-
await execa(command, args, {
|
|
293
|
-
cwd,
|
|
294
|
-
stdin: "ignore"
|
|
295
|
-
});
|
|
296
|
-
return {
|
|
297
|
-
detail: "available",
|
|
298
|
-
name,
|
|
299
|
-
passed: true
|
|
300
|
-
};
|
|
301
|
-
} catch (err) {
|
|
302
|
-
const error = err;
|
|
303
|
-
return {
|
|
304
|
-
detail: (error.shortMessage || error.stderr || "not available").trim(),
|
|
305
|
-
name,
|
|
306
|
-
passed: false
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
function checkPipelineConfig(cwd) {
|
|
311
|
-
try {
|
|
312
|
-
loadPipelineConfig(cwd);
|
|
313
|
-
return {
|
|
314
|
-
detail: "valid",
|
|
315
|
-
name: "pipeline-config",
|
|
316
|
-
passed: true
|
|
317
|
-
};
|
|
318
|
-
} catch (err) {
|
|
319
|
-
let message = "invalid";
|
|
320
|
-
if (err instanceof PipelineConfigError) message = err.issues.map((issue) => issue.message).join("; ");
|
|
321
|
-
else if (err instanceof Error) message = err.message;
|
|
322
|
-
return {
|
|
323
|
-
detail: message || "missing or invalid",
|
|
324
|
-
name: "pipeline-config",
|
|
325
|
-
passed: false
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
516
|
function formatWorkflowPlan(config, worktreePath, workflowId) {
|
|
330
517
|
return formatCompiledWorkflowPlan(config, worktreePath, compileWorkflowPlan(config, workflowId));
|
|
331
518
|
}
|
|
@@ -392,4 +579,4 @@ function formatList(label, items) {
|
|
|
392
579
|
return items?.length ? `${label}=${items.join(",")}` : "";
|
|
393
580
|
}
|
|
394
581
|
//#endregion
|
|
395
|
-
export { createCliProgram, execute, quick, runCli,
|
|
582
|
+
export { createCliProgram, execute, quick, runCli, runCliEffect };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//#region src/cli/run-resolver.ts
|
|
2
|
+
const MOKA_RUN_EFFORTS = [
|
|
3
|
+
"normal",
|
|
4
|
+
"quick",
|
|
5
|
+
"thorough"
|
|
6
|
+
];
|
|
7
|
+
const MOKA_RUN_TARGETS = ["local", "remote"];
|
|
8
|
+
function resolveMokaRun(input) {
|
|
9
|
+
const flags = input.flags ?? {};
|
|
10
|
+
const effort = flags.effort ?? "normal";
|
|
11
|
+
const target = flags.target ?? "local";
|
|
12
|
+
const mode = flags.readOnly ? "read" : "write";
|
|
13
|
+
if (flags.command && target !== "remote") throw new Error("--command requires --target remote");
|
|
14
|
+
if (flags.detach && target !== "local") throw new Error("--detach requires --target local");
|
|
15
|
+
return {
|
|
16
|
+
effort,
|
|
17
|
+
execution: target === "remote" ? resolveRemoteSubmit(flags, effort) : resolveLocalRuntime(flags, effort),
|
|
18
|
+
mode,
|
|
19
|
+
target
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function resolveRemoteSubmit(flags, effort) {
|
|
23
|
+
return {
|
|
24
|
+
command: flags.command,
|
|
25
|
+
kind: "remote-submit",
|
|
26
|
+
mode: effort === "quick" ? "quick" : "full",
|
|
27
|
+
schedule: flags.schedule
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function resolveLocalRuntime(flags, effort) {
|
|
31
|
+
if (flags.schedule) return {
|
|
32
|
+
kind: "local-runtime",
|
|
33
|
+
schedule: flags.schedule
|
|
34
|
+
};
|
|
35
|
+
if (flags.workflow) return {
|
|
36
|
+
kind: "local-runtime",
|
|
37
|
+
workflow: flags.workflow
|
|
38
|
+
};
|
|
39
|
+
if (flags.readOnly) return {
|
|
40
|
+
kind: "local-runtime",
|
|
41
|
+
workflow: "inspect"
|
|
42
|
+
};
|
|
43
|
+
if (flags.entrypoint) return {
|
|
44
|
+
entrypoint: flags.entrypoint,
|
|
45
|
+
kind: "local-runtime"
|
|
46
|
+
};
|
|
47
|
+
if (effort === "quick") return {
|
|
48
|
+
entrypoint: "quick",
|
|
49
|
+
kind: "local-runtime"
|
|
50
|
+
};
|
|
51
|
+
if (effort === "thorough") return {
|
|
52
|
+
entrypoint: "execute",
|
|
53
|
+
kind: "local-runtime"
|
|
54
|
+
};
|
|
55
|
+
return { kind: "local-runtime" };
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { MOKA_RUN_EFFORTS, MOKA_RUN_TARGETS, resolveMokaRun };
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { buildEvalReport, renderEvalReport } from "../bench/eval-report.js";
|
|
2
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
3
|
import { readFileSync } from "node:fs";
|
|
3
4
|
//#region src/commands/bench-command.ts
|
|
5
|
+
var BenchCommandService = class extends Context.Tag("BenchCommandService")() {};
|
|
6
|
+
const BenchCommandServiceLive = Layer.succeed(BenchCommandService, {
|
|
7
|
+
readResults: (path) => Effect.try(() => JSON.parse(readFileSync(path, "utf8"))),
|
|
8
|
+
writeReport: (report) => Effect.try(() => process.stdout.write(`${report}\n`))
|
|
9
|
+
});
|
|
10
|
+
const runBenchCommand = (options) => Effect.gen(function* () {
|
|
11
|
+
const service = yield* BenchCommandService;
|
|
12
|
+
const report = renderEvalReport(buildEvalReport(yield* service.readResults(options.results)));
|
|
13
|
+
yield* service.writeReport(report);
|
|
14
|
+
});
|
|
4
15
|
/**
|
|
5
16
|
* PIPE-83.6: `moka bench` — score a flat single-agent baseline vs the pipeline
|
|
6
17
|
* (and ablations) over a recorded run set. Runs are produced by executing the
|
|
@@ -9,10 +20,7 @@ import { readFileSync } from "node:fs";
|
|
|
9
20
|
* comparison report.
|
|
10
21
|
*/
|
|
11
22
|
function registerBenchCommand(program) {
|
|
12
|
-
program.command("bench").description("Score a flat single-agent baseline vs the pipeline over recorded bench runs").requiredOption("--results <path>", "JSON file: array of { task, variant, resolved, costTokens, wallMs }").action((options) =>
|
|
13
|
-
const records = JSON.parse(readFileSync(options.results, "utf8"));
|
|
14
|
-
process.stdout.write(`${renderEvalReport(buildEvalReport(records))}\n`);
|
|
15
|
-
});
|
|
23
|
+
program.command("bench").description("Score a flat single-agent baseline vs the pipeline over recorded bench runs").requiredOption("--results <path>", "JSON file: array of { task, variant, resolved, costTokens, wallMs }").action((options) => Effect.runPromise(Effect.provide(runBenchCommand(options), BenchCommandServiceLive)));
|
|
16
24
|
}
|
|
17
25
|
//#endregion
|
|
18
26
|
export { registerBenchCommand };
|