@oisincoveney/pipeline 3.24.3 → 4.0.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/README.md +12 -0
- package/dist/argo-graph.js +4 -2
- package/dist/argo-submit.d.ts +1 -1
- package/dist/argo-submit.js +39 -31
- package/dist/argo-workflow.d.ts +0 -1
- package/dist/cli/doctor.js +5 -3
- package/dist/cli/factory-commands.js +1 -2
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +8 -2
- package/dist/cli/run-command.d.ts +1 -1
- package/dist/cli/run-commands.js +7 -5
- package/dist/cli/run-service.d.ts +0 -1
- package/dist/cli/run-service.js +11 -8
- package/dist/cli/submit-options.js +10 -4
- package/dist/commands/coordinator-command.js +35 -0
- package/dist/commands/pipeline-command.js +4 -2
- package/dist/commands/runner-command-command.js +11 -6
- package/dist/commands/runner-node-command.js +31 -0
- package/dist/commands/ticket/create.js +4 -2
- package/dist/commands/ticket/sequence.js +2 -1
- package/dist/commands/ticket/shared.d.ts +4 -1
- package/dist/config/defaults.d.ts +0 -1
- package/dist/config/load.d.ts +0 -1
- package/dist/config/load.js +3 -1
- package/dist/config/schema/catalog.d.ts +2 -1
- package/dist/config/schema/catalog.js +2 -1
- package/dist/config/schemas.d.ts +7 -3
- package/dist/config/schemas.js +3 -1
- package/dist/config/validate.d.ts +0 -1
- package/dist/config.d.ts +2 -2
- package/dist/context/repo-map.js +5 -2
- package/dist/coordinator/composition.js +169 -0
- package/dist/coordinator/config.js +71 -0
- package/dist/coordinator/run-owner.js +64 -0
- package/dist/coordinator/wave-advance.js +148 -0
- package/dist/coordinator/wave-scheduler.js +50 -0
- package/dist/factory/create-experiment.js +252 -84
- package/dist/hooks.d.ts +0 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -1
- package/dist/install-commands/claude-code.js +4 -0
- package/dist/install-commands/opencode.js +2 -1
- package/dist/install-commands/planner.js +6 -3
- package/dist/loop/argo-poll.d.ts +0 -1
- package/dist/loop/argo-poll.js +3 -64
- package/dist/loop/controller-deps.js +9 -7
- package/dist/loop/controller.js +5 -3
- package/dist/loop/loop-controller-entrypoint.js +2 -1
- package/dist/loop/run-terminal-poll.js +69 -0
- package/dist/mcp/gateway-doctor.js +1 -31
- package/dist/mcp/repo-local-backends.js +2 -1
- package/dist/moka-global-config.d.ts +0 -1
- package/dist/moka-global-config.js +6 -3
- package/dist/moka-submit.d.ts +17 -3
- package/dist/pipeline-runtime.d.ts +5 -4
- package/dist/pipeline-runtime.js +4 -2
- package/dist/planning/compile.d.ts +2 -2
- package/dist/planning/generate.d.ts +3 -3
- package/dist/planning/generate.js +5 -3
- package/dist/planning/graph.d.ts +0 -1
- package/dist/remote/argo/model.d.ts +1 -1
- package/dist/remote/jobs/execution-identity.js +70 -0
- package/dist/remote/jobs/job-outcome.js +108 -0
- package/dist/remote/jobs/manifest.js +96 -0
- package/dist/remote/jobs/model.d.ts +158 -0
- package/dist/remote/jobs/model.js +162 -0
- package/dist/remote/jobs/node-bootstrap.js +213 -0
- package/dist/remote/jobs/policy.js +123 -0
- package/dist/remote/jobs/reconcile.js +100 -0
- package/dist/remote/jobs/run-spec-client.js +64 -0
- package/dist/remote/jobs/run-spec-path.js +31 -0
- package/dist/remote/jobs/run-spec-server.d.ts +34 -0
- package/dist/remote/jobs/run-spec-server.js +116 -0
- package/dist/remote/jobs/submit.d.ts +23 -0
- package/dist/remote/jobs/submit.js +116 -0
- package/dist/remote/jobs/watch.js +59 -0
- package/dist/remote/submit/argo-submission.d.ts +3 -1
- package/dist/remote/submit/compilation.d.ts +2 -0
- package/dist/remote/submit/hook-events.d.ts +1 -0
- package/dist/remote/submit/io.d.ts +7 -1
- package/dist/remote/submit/jobs-submission.d.ts +59 -0
- package/dist/remote/submit/jobs-submission.js +97 -0
- package/dist/remote/submit/jobs-submit-result.d.ts +25 -0
- package/dist/remote/submit/jobs-submit-result.js +34 -0
- package/dist/remote/submit/options.d.ts +0 -1
- package/dist/remote/submit/service.d.ts +40 -1
- package/dist/remote/submit/service.js +23 -1
- package/dist/run-control/contracts.d.ts +12 -1
- package/dist/run-control/contracts.js +28 -1
- package/dist/run-control/execution-store.js +0 -0
- package/dist/run-control/next-node.js +2 -1
- package/dist/run-control/postgres/postgres-run-control-store.js +4 -4
- package/dist/run-control/resume-command.js +2 -1
- package/dist/run-control/run-artifacts-command.js +2 -1
- package/dist/run-control/run-record.js +3 -1
- package/dist/run-control/runtime-reporter.js +3 -1
- package/dist/run-control/supervisor.js +2 -1
- package/dist/run-state/git-refs.js +2 -1
- package/dist/runner-command/environment-commands.js +91 -0
- package/dist/runner-command/lifecycle-context.js +5 -2
- package/dist/runner-command/lifecycle.js +1 -1
- package/dist/runner-command/pre-schedule.js +3 -2
- package/dist/runner-command/run.js +65 -48
- package/dist/runner-command/select-ready-wave.js +1 -1
- package/dist/runner-command-contract.d.ts +5 -6
- package/dist/runner-command-contract.js +11 -10
- package/dist/runner-event-schema.d.ts +12 -13
- package/dist/runner.d.ts +2 -2
- package/dist/runner.js +6 -5
- package/dist/runtime/actor-ids.d.ts +1 -0
- package/dist/runtime/agent-node/agent-node.js +3 -2
- package/dist/runtime/agent-node/output-finalization.js +3 -2
- package/dist/runtime/agent-node/prompt-rendering.js +2 -1
- package/dist/runtime/agent-node/session-execution.js +17 -9
- package/dist/runtime/builtins/builtins.js +10 -4
- package/dist/runtime/changed-files/changed-files.js +5 -2
- package/dist/runtime/contracts/contracts.d.ts +3 -0
- package/dist/runtime/drain-merge/drain-merge.js +2 -1
- package/dist/runtime/durable-store/durable-store.d.ts +2 -0
- package/dist/runtime/durable-store/postgres/client.js +29 -0
- package/dist/runtime/durable-store/postgres/migrate-substrate.js +6 -4
- package/dist/runtime/durable-store/postgres/postgres-store.js +2 -3
- package/dist/runtime/events/events.js +27 -17
- package/dist/runtime/events/index.js +2 -2
- package/dist/runtime/execution-envelope.js +44 -0
- package/dist/runtime/gates/kinds/acceptance/acceptance.js +2 -1
- package/dist/runtime/handoff.d.ts +2 -1
- package/dist/runtime/hooks/module-hook.js +2 -1
- package/dist/runtime/journal-acquisition.d.ts +2 -1
- package/dist/runtime/json-validation/json-validation.js +2 -1
- package/dist/runtime/launch-plan-args.js +39 -0
- package/dist/runtime/node-execution.js +5 -3
- package/dist/runtime/node-state-store.d.ts +3 -0
- package/dist/runtime/open-pull-request/open-pull-request.js +76 -18
- package/dist/runtime/opencode-runtime.js +32 -10
- package/dist/runtime/opencode-session-executor.js +8 -21
- package/dist/runtime/parallel-node/parallel-node.js +2 -1
- package/dist/runtime/run-journal.d.ts +2 -0
- package/dist/runtime/scheduled-dependencies.d.ts +1 -0
- package/dist/runtime/scheduled-dependencies.js +2 -1
- package/dist/runtime/scheduler.js +5 -3
- package/dist/runtime/services/backlog-service.d.ts +1 -2
- package/dist/runtime/services/kubernetes-argo-service.d.ts +0 -1
- package/dist/runtime/services/kubernetes-jobs-read-service.js +115 -0
- package/dist/runtime/services/kubernetes-jobs-service.d.ts +37 -0
- package/dist/runtime/services/kubernetes-jobs-service.js +45 -0
- package/dist/runtime/services/kubernetes-jobs-watch-service.js +61 -0
- package/dist/runtime/services/kubernetes-lease-service.js +21 -0
- package/dist/runtime/services/mcp-gateway-service.js +63 -15
- package/dist/runtime/services/runner-event-sink-http-service.js +2 -1
- package/dist/runtime/workflow-execution.js +2 -1
- package/dist/runtime/workflow-lifecycle.js +2 -1
- package/dist/runtime/yeet-event-record.js +104 -0
- package/dist/runtime/yeet-executor.js +184 -0
- package/dist/schedule/backlog-context.js +4 -2
- package/dist/schedule/passes/open-pull-request.js +4 -2
- package/dist/schedule/prompts.js +2 -1
- package/dist/schema-boundary.d.ts +4 -1
- package/dist/tickets/apply-ticket-plan.js +2 -1
- package/dist/tickets/backlog-task-store.d.ts +2 -1
- package/dist/tickets/backlog-task-store.js +4 -2
- package/dist/tickets/ticket-graph-dto.d.ts +6 -7
- package/dist/tickets/ticket-graph-dto.js +5 -5
- package/dist/tickets/ticket-graph.d.ts +1 -2
- package/dist/tickets/ticket-plan.d.ts +1 -1
- package/dist/tickets/ticket-selection.d.ts +2 -0
- package/dist/workflow-submit-contract.d.ts +0 -1
- package/docs/adr-moka-postgres-schema-isolation.md +48 -0
- package/docs/pipeline-console-runner-contract.md +10 -6
- package/docs/runtime-test-subsystems.md +200 -0
- package/package.json +3 -36
package/README.md
CHANGED
|
@@ -379,6 +379,18 @@ Package and container publishing is owned by GitHub Actions. Do not publish from
|
|
|
379
379
|
a workstation with `npm publish`, `semantic-release`, Docker pushes, or direct
|
|
380
380
|
registry commands.
|
|
381
381
|
|
|
382
|
+
`@oisincoveney/pipeline` is published by `engine`'s own root-level
|
|
383
|
+
`.github/workflows/publish-pipeline.yml`, from this directory's current
|
|
384
|
+
(contract-v2) source, on every push to `main` under `runner/**`. The legacy
|
|
385
|
+
`oisin-ee/pipeline-runner` repository's publish workflow is retired — it
|
|
386
|
+
predates the ENG-67.3 contract-v2 bump and must not be used to cut a release.
|
|
387
|
+
The workflow authenticates to `registry.npmjs.org` via npm Trusted Publishing
|
|
388
|
+
(OIDC) rather than a static `NPM_TOKEN`; no npm credential is stored as a
|
|
389
|
+
GitHub secret in this org. Trusted Publishing must be configured on
|
|
390
|
+
npmjs.com for `@oisincoveney/pipeline`, bound to `oisin-ee/engine` and the
|
|
391
|
+
`publish-pipeline.yml` workflow filename, before this workflow can publish. A
|
|
392
|
+
successful release also triggers the runner container image build.
|
|
393
|
+
|
|
382
394
|
The authoritative package version lives in the npm registry, not in this
|
|
383
395
|
repository. `package.json` pins the `0.0.0-development` semantic-release
|
|
384
396
|
sentinel and is never bumped in git; semantic-release derives and publishes the
|
package/dist/argo-graph.js
CHANGED
|
@@ -75,8 +75,9 @@ const assertUniqueNodeIds = (nodes) => {
|
|
|
75
75
|
};
|
|
76
76
|
const resolveDependencyTaskNames = (context, nodeIds) => uniqueStrings(resolveExecutableDependencyIds(context.nodeById, nodeIds).map((id) => argoTaskName(id)));
|
|
77
77
|
const compileExecutableNode = (context, node, inheritedNeeds) => {
|
|
78
|
+
const dependencies = resolveDependencyTaskNames(context, [...inheritedNeeds, ...node.needs]);
|
|
78
79
|
return parseStrictWithSchema(argoExecutableTaskSchema, {
|
|
79
|
-
dependencies
|
|
80
|
+
dependencies,
|
|
80
81
|
nodeId: node.id,
|
|
81
82
|
taskName: argoTaskName(node.id),
|
|
82
83
|
templateName: argoTemplateName(node.id)
|
|
@@ -90,7 +91,8 @@ const compileArgoNodes = (context, nodes, inheritedNeeds, initialTasks = []) =>
|
|
|
90
91
|
}));
|
|
91
92
|
const compileArgoNode = (input) => Match.value(input.node.kind).pipe(Match.whenOr("agent", "builtin", "command", () => [...input.tasks, compileExecutableNode(input.context, input.node, input.inheritedNeeds)]), Match.when("group", () => input.tasks), Match.when("parallel", () => compileArgoNodes(input.context, input.node.children ?? [], [...input.inheritedNeeds, ...input.node.needs], input.tasks)), Match.exhaustive);
|
|
92
93
|
const compileArgoExecutionGraphUnchecked = (plan) => {
|
|
93
|
-
const
|
|
94
|
+
const context = { nodeById: indexPlannedNodesById(plan.topologicalOrder) };
|
|
95
|
+
const tasks = compileArgoNodes(context, plan.topologicalOrder, []);
|
|
94
96
|
const terminalTasks = terminalDependencyItems(tasks, (task) => task.taskName, (task) => task.dependencies);
|
|
95
97
|
return {
|
|
96
98
|
tasks,
|
package/dist/argo-submit.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PipelineConfig } from "./config/schemas.js";
|
|
2
|
+
import "./config.js";
|
|
2
3
|
import { CoreApi, KubernetesArgoIoDependencies, WorkflowApi } from "./runtime/services/kubernetes-argo-service.js";
|
|
3
4
|
import { workflowSubmitResultSchema } from "./workflow-submit-contract.js";
|
|
4
5
|
import * as Schema from "effect/Schema";
|
|
5
|
-
|
|
6
6
|
//#region src/argo-submit.d.ts
|
|
7
7
|
declare const commandScheduleOptionsSchema: Schema.Struct<{
|
|
8
8
|
readonly command: Schema.mutable<Schema.$Array<Schema.NonEmptyString>>;
|
package/dist/argo-submit.js
CHANGED
|
@@ -169,8 +169,9 @@ const ownRunConfigMaps = (input) => {
|
|
|
169
169
|
error: new ArgoSubmitError({ message: missingWorkflowUidMessage(input.result) }),
|
|
170
170
|
result: input.result
|
|
171
171
|
}).pipe(Effect.as(input.result));
|
|
172
|
+
const body = configMapOwnerReferencesPatch(ownerReference.value);
|
|
172
173
|
return patchRunConfigMapOwnerReferences({
|
|
173
|
-
body
|
|
174
|
+
body,
|
|
174
175
|
configMapNames: input.configMapNames,
|
|
175
176
|
dependencies: input.dependencies,
|
|
176
177
|
namespace: input.namespace,
|
|
@@ -229,12 +230,13 @@ const workflowSubmitResult = (response, workflow, base) => {
|
|
|
229
230
|
const submitWorkflowManifest = (input) => Effect.gen(function* effectBody() {
|
|
230
231
|
const service = yield* KubernetesArgoService;
|
|
231
232
|
const workflow = applyWorkflowFieldOverrides(input.workflow, input.workflowFieldOverrides);
|
|
232
|
-
|
|
233
|
+
const response = yield* service.createWorkflow({
|
|
233
234
|
body: parseStrictWithSchema(runnerArgoWorkflowManifestSchema, workflow),
|
|
234
235
|
dependencies: input.dependencies,
|
|
235
236
|
namespace: input.namespace,
|
|
236
237
|
options: input.options
|
|
237
|
-
})
|
|
238
|
+
});
|
|
239
|
+
return workflowSubmitResult(response, workflow, {
|
|
238
240
|
namespace: input.namespace,
|
|
239
241
|
...input.resultExtras
|
|
240
242
|
});
|
|
@@ -294,8 +296,9 @@ const normalizeRunnerPayloadForSubmit = (input) => {
|
|
|
294
296
|
const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
295
297
|
const { config: _config, ...schemaOptions } = rawOptions;
|
|
296
298
|
const options = parseStrictWithSchema(submitDynamicRunnerArgoWorkflowOptionsSchema, schemaOptions);
|
|
299
|
+
const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
|
|
297
300
|
const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
|
|
298
|
-
payload:
|
|
301
|
+
payload: parsedPayload,
|
|
299
302
|
payloadJson: options.payloadJson
|
|
300
303
|
});
|
|
301
304
|
if (payload.workflow.id !== options.workflowId) return Effect.fail(new ArgoSubmitError({ message: `Runner payload workflow '${payload.workflow.id}' does not match dynamic workflow '${options.workflowId}'` }));
|
|
@@ -335,18 +338,20 @@ const submitDynamicRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
|
335
338
|
workflowId: options.workflowId
|
|
336
339
|
});
|
|
337
340
|
return Effect.gen(function* effectBody() {
|
|
341
|
+
const configMaps = dynamicRunConfigMaps({
|
|
342
|
+
labels,
|
|
343
|
+
namespace: options.namespace,
|
|
344
|
+
payloadConfigMapName,
|
|
345
|
+
payloadJson
|
|
346
|
+
});
|
|
347
|
+
const createdConfigMapNames = yield* createRunConfigMaps({
|
|
348
|
+
configMaps,
|
|
349
|
+
dependencies,
|
|
350
|
+
namespace: options.namespace,
|
|
351
|
+
options
|
|
352
|
+
});
|
|
338
353
|
return yield* submitWorkflowWithRunConfigMaps({
|
|
339
|
-
configMapNames:
|
|
340
|
-
configMaps: dynamicRunConfigMaps({
|
|
341
|
-
labels,
|
|
342
|
-
namespace: options.namespace,
|
|
343
|
-
payloadConfigMapName,
|
|
344
|
-
payloadJson
|
|
345
|
-
}),
|
|
346
|
-
dependencies,
|
|
347
|
-
namespace: options.namespace,
|
|
348
|
-
options
|
|
349
|
-
}),
|
|
354
|
+
configMapNames: createdConfigMapNames,
|
|
350
355
|
dependencies,
|
|
351
356
|
namespace: options.namespace,
|
|
352
357
|
options,
|
|
@@ -367,8 +372,9 @@ const compileSubmitArgoGraph = (compiled) => Effect.try({
|
|
|
367
372
|
const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
368
373
|
const { config, ...schemaOptions } = rawOptions;
|
|
369
374
|
const options = parseStrictWithSchema(submitRunnerArgoWorkflowOptionsSchema, schemaOptions);
|
|
375
|
+
const parsedPayload = parseStrictWithSchema(runnerCommandPayloadSchema, parseRunnerCommandPayload(options.payloadJson));
|
|
370
376
|
const { payload, payloadJson } = normalizeRunnerPayloadForSubmit({
|
|
371
|
-
payload:
|
|
377
|
+
payload: parsedPayload,
|
|
372
378
|
payloadJson: options.payloadJson
|
|
373
379
|
});
|
|
374
380
|
const compiled = compileScheduleArtifact(config, parseScheduleArtifact(options.scheduleYaml, "schedule.yaml"));
|
|
@@ -415,22 +421,24 @@ const submitRunnerArgoWorkflowEffect = (rawOptions, dependencies) => {
|
|
|
415
421
|
});
|
|
416
422
|
return Effect.gen(function* effectBody() {
|
|
417
423
|
const graph = yield* graphEffect;
|
|
424
|
+
const configMaps = staticRunConfigMaps({
|
|
425
|
+
labels,
|
|
426
|
+
namespace: options.namespace,
|
|
427
|
+
payloadConfigMapName,
|
|
428
|
+
payloadJson,
|
|
429
|
+
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
430
|
+
scheduleYaml: options.scheduleYaml,
|
|
431
|
+
taskDescriptorConfigMapName,
|
|
432
|
+
tasks: graph.tasks
|
|
433
|
+
});
|
|
434
|
+
const createdConfigMapNames = yield* createRunConfigMaps({
|
|
435
|
+
configMaps,
|
|
436
|
+
dependencies,
|
|
437
|
+
namespace: options.namespace,
|
|
438
|
+
options
|
|
439
|
+
});
|
|
418
440
|
return yield* submitWorkflowWithRunConfigMaps({
|
|
419
|
-
configMapNames:
|
|
420
|
-
configMaps: staticRunConfigMaps({
|
|
421
|
-
labels,
|
|
422
|
-
namespace: options.namespace,
|
|
423
|
-
payloadConfigMapName,
|
|
424
|
-
payloadJson,
|
|
425
|
-
scheduleConfigMapName: scheduleArtifactConfigMapName,
|
|
426
|
-
scheduleYaml: options.scheduleYaml,
|
|
427
|
-
taskDescriptorConfigMapName,
|
|
428
|
-
tasks: graph.tasks
|
|
429
|
-
}),
|
|
430
|
-
dependencies,
|
|
431
|
-
namespace: options.namespace,
|
|
432
|
-
options
|
|
433
|
-
}),
|
|
441
|
+
configMapNames: createdConfigMapNames,
|
|
434
442
|
dependencies,
|
|
435
443
|
namespace: options.namespace,
|
|
436
444
|
options,
|
package/dist/argo-workflow.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WorkflowExecutionPlan } from "./planning/compile.js";
|
|
2
2
|
import { buildDynamicRunnerArgoWorkflowOptionsSchema, buildRunnerArgoWorkflowOptionsSchema } from "./remote/argo/model.js";
|
|
3
|
-
|
|
4
3
|
//#region src/argo-workflow.d.ts
|
|
5
4
|
declare const runnerArgoWorkflowManifestSchema: import("effect/Schema").Struct<{
|
|
6
5
|
readonly apiVersion: import("effect/Schema").Literal<"argoproj.io/v1alpha1">;
|
package/dist/cli/doctor.js
CHANGED
|
@@ -188,10 +188,11 @@ const checkMokaAgents = async (cwd, config) => {
|
|
|
188
188
|
passed: true
|
|
189
189
|
} };
|
|
190
190
|
try {
|
|
191
|
-
const
|
|
191
|
+
const result = await execa("opencode", OPENCODE_AGENT_LIST_ARGS, {
|
|
192
192
|
cwd,
|
|
193
193
|
stdin: "ignore"
|
|
194
|
-
})
|
|
194
|
+
});
|
|
195
|
+
const visible = visibleAgentNames(result.stdout);
|
|
195
196
|
if (!visible.recognized) return skippedAgentVisibility("OpenCode agent listing output was not recognized");
|
|
196
197
|
if (visible.ambiguous && expected.every((name) => !visible.names.has(name))) return skippedAgentVisibility("OpenCode agent listing output did not include recognizable MoKa agent names");
|
|
197
198
|
const missing = expected.filter((name) => !visible.names.has(name));
|
|
@@ -211,7 +212,8 @@ const checkMokaAgents = async (cwd, config) => {
|
|
|
211
212
|
const headlessPermissionWarning = (path, entry) => {
|
|
212
213
|
try {
|
|
213
214
|
if (!statSync(path).isFile()) return [];
|
|
214
|
-
const
|
|
215
|
+
const parsed = matter(readFileSync(path, "utf-8"));
|
|
216
|
+
const risky = interactivePermissionPaths(parsed.data.permission);
|
|
215
217
|
if (risky.length === 0) return [];
|
|
216
218
|
return [{
|
|
217
219
|
detail: `${entry} requires interactive permission prompts at ${risky.join(", ")}; headless MoKa runs may block.`,
|
|
@@ -4,14 +4,13 @@ import { Option } from "commander";
|
|
|
4
4
|
//#region src/cli/factory-commands.ts
|
|
5
5
|
const hasText = (value) => typeof value === "string" && value !== "";
|
|
6
6
|
const registerFactoryCommands = (program) => {
|
|
7
|
-
program.command("create-experiment").description("Birth a fleet experiment: copier-stamp momokaya-template, create+push the org repo, register it in infra's fleet registry").requiredOption("--name <name>", "app name (kebab-case)").addOption(new Option("--flavor <flavor>", "app flavor").choices(["web", "expo-web"]).default("web")).option("--no-db", "skip the database surface").option("--
|
|
7
|
+
program.command("create-experiment").description("Birth a fleet experiment: copier-stamp momokaya-template, create+push the org repo, register it in infra's fleet registry").requiredOption("--name <name>", "app name (kebab-case)").addOption(new Option("--flavor <flavor>", "app flavor").choices(["web", "expo-web"]).default("web")).option("--no-db", "skip the database surface").option("--org <org>", "GitHub org for the new repo").option("--template-src <source>", "copier template source").option("--template-ref <ref>", "template tag/ref (default: latest tag)").option("--infra-repo-url <url>", "infra repo the registry entry lands in").action(async (flags) => {
|
|
8
8
|
const result = await runCreateExperiment({
|
|
9
9
|
db: flags.db,
|
|
10
10
|
flavor: flags.flavor,
|
|
11
11
|
...hasText(flags.infraRepoUrl) ? { infraRepoUrl: flags.infraRepoUrl } : {},
|
|
12
12
|
name: flags.name,
|
|
13
13
|
...hasText(flags.org) ? { org: flags.org } : {},
|
|
14
|
-
previews: flags.previews,
|
|
15
14
|
...hasText(flags.templateRef) ? { templateRef: flags.templateRef } : {},
|
|
16
15
|
...hasText(flags.templateSrc) ? { templateSource: flags.templateSrc } : {}
|
|
17
16
|
});
|
package/dist/cli/program.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { RunCommand } from "./run-command.js";
|
|
2
2
|
import { TicketCommandOptions } from "../commands/ticket/shared.js";
|
|
3
|
+
import "../commands/ticket-command.js";
|
|
3
4
|
import { Effect } from "effect";
|
|
4
5
|
import { Command } from "commander";
|
|
5
|
-
|
|
6
6
|
//#region src/cli/program.d.ts
|
|
7
7
|
interface CliProgramOptions {
|
|
8
8
|
readonly runCommand?: RunCommand;
|
package/dist/cli/program.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { loadPipelineConfig } from "../config/load.js";
|
|
2
2
|
import "../config.js";
|
|
3
3
|
import { registerBenchCommand } from "../commands/bench-command.js";
|
|
4
|
+
import { registerCoordinatorCommand } from "../commands/coordinator-command.js";
|
|
4
5
|
import { registerConfiguredEntrypointCommands } from "../commands/pipeline-command.js";
|
|
5
6
|
import { registerRunnerCommandCommand } from "../commands/runner-command-command.js";
|
|
7
|
+
import { registerRunnerNodeCommand } from "../commands/runner-node-command.js";
|
|
6
8
|
import { registerTicketCommand } from "../commands/ticket-command.js";
|
|
7
9
|
import { addMokaSubmitOptions, runMokaSubmitFromCli } from "./submit-options.js";
|
|
8
10
|
import { registerRunControlCommands } from "../run-control/commands.js";
|
|
@@ -32,6 +34,8 @@ const registerApplicationCommands = (program, options) => {
|
|
|
32
34
|
registerLoopCommand(program);
|
|
33
35
|
registerFactoryCommands(program);
|
|
34
36
|
registerRunnerCommandCommand(program);
|
|
37
|
+
registerRunnerNodeCommand(program);
|
|
38
|
+
registerCoordinatorCommand(program);
|
|
35
39
|
registerBenchCommand(program);
|
|
36
40
|
registerTicketCommand(program, {
|
|
37
41
|
...options.ticketCommand,
|
|
@@ -49,7 +53,8 @@ const runEntrypointCommand = async (entrypoint, task) => {
|
|
|
49
53
|
await execute(task, { entrypoint });
|
|
50
54
|
};
|
|
51
55
|
const registerEntrypointCommands = (program, configuredPipeline) => {
|
|
52
|
-
|
|
56
|
+
const configuredEntrypointCommands = registerConfiguredEntrypointCommands(program, configuredPipeline, runEntrypointCommand);
|
|
57
|
+
configureEntrypointHelp(program, configuredEntrypointCommands);
|
|
53
58
|
};
|
|
54
59
|
const isPackageVersionRecord = (value) => typeof value === "object" && value !== null && "version" in value && typeof value.version === "string";
|
|
55
60
|
const readPackageVersion = () => {
|
|
@@ -60,7 +65,8 @@ const readPackageVersion = () => {
|
|
|
60
65
|
const createBaseProgram = () => new Command().name("moka").description("Submit work to Momokaya").version(readPackageVersion()).exitOverride();
|
|
61
66
|
const loadConfiguredEntrypoints = (cwd) => loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
62
67
|
const createCliProgram = (options = {}) => {
|
|
63
|
-
const
|
|
68
|
+
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
69
|
+
const configuredPipeline = loadConfiguredEntrypoints(cwd);
|
|
64
70
|
const program = createBaseProgram();
|
|
65
71
|
registerApplicationCommands(program, options);
|
|
66
72
|
registerEntrypointCommands(program, configuredPipeline);
|
package/dist/cli/run-commands.js
CHANGED
|
@@ -23,7 +23,8 @@ const createResolvedRunCommand = (options) => async (call) => {
|
|
|
23
23
|
await runLocalResolvedTask(resolvedTask, execution, runControl);
|
|
24
24
|
},
|
|
25
25
|
runRemoteSubmit: async ({ descriptionParts: parts, execution }) => {
|
|
26
|
-
|
|
26
|
+
const result = await runMokaSubmitFromCli(parts, remoteSubmitFlags(execution));
|
|
27
|
+
printMokaSubmitResult(result);
|
|
27
28
|
}
|
|
28
29
|
});
|
|
29
30
|
};
|
|
@@ -31,13 +32,14 @@ const registerRunCommands = (program, options = {}) => {
|
|
|
31
32
|
const dispatchResolvedRunCommand = createResolvedRunCommand(options);
|
|
32
33
|
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 id from package config").option("--detach", "start a supervised controller process in the background").addOption(new Option("--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("--target <target>", "execution target").choices([...MOKA_RUN_TARGETS]).default("local")).option("--workflow <workflow>", "workflow id from package config").action(async (descriptionParts, flags) => {
|
|
33
34
|
const task = descriptionParts.join(" ");
|
|
35
|
+
const resolution = resolveMokaRun({
|
|
36
|
+
flags,
|
|
37
|
+
task
|
|
38
|
+
});
|
|
34
39
|
await dispatchResolvedRunCommand({
|
|
35
40
|
descriptionParts,
|
|
36
41
|
flags,
|
|
37
|
-
resolution
|
|
38
|
-
flags,
|
|
39
|
-
task
|
|
40
|
-
}),
|
|
42
|
+
resolution,
|
|
41
43
|
task
|
|
42
44
|
});
|
|
43
45
|
});
|
package/dist/cli/run-service.js
CHANGED
|
@@ -171,7 +171,8 @@ const runPipelineSafely = async (inputs, runner, runStoreReporter) => await runW
|
|
|
171
171
|
});
|
|
172
172
|
const runAndPrintPipelineWithStore = async (inputs, store) => {
|
|
173
173
|
const runner = inputs.pipelineRunner ?? runPipelineFromConfig;
|
|
174
|
-
const
|
|
174
|
+
const terminalReporter = createTerminalRuntimeReporter();
|
|
175
|
+
const runStoreReporter = await createRunStoreReporter(inputs, terminalReporter, store);
|
|
175
176
|
printSupervisedFollowUp(inputs);
|
|
176
177
|
const result = await runPipelineSafely(inputs, runner, runStoreReporter);
|
|
177
178
|
console.log(formatRuntimeResult(result));
|
|
@@ -318,14 +319,16 @@ const prepareDetachedRun = async (input) => {
|
|
|
318
319
|
const runDetachedResolvedTask = async (task, execution, runControl) => {
|
|
319
320
|
const worktreePath = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
320
321
|
const runId = generateRuntimeRunId();
|
|
322
|
+
const config = loadPipelineConfig(worktreePath, { allowMissingLintFileReferences: true });
|
|
323
|
+
const prepared = await prepareDetachedRun({
|
|
324
|
+
config,
|
|
325
|
+
execution,
|
|
326
|
+
runId,
|
|
327
|
+
task,
|
|
328
|
+
worktreePath
|
|
329
|
+
});
|
|
321
330
|
await persistDetachedRunController({
|
|
322
|
-
prepared
|
|
323
|
-
config: loadPipelineConfig(worktreePath, { allowMissingLintFileReferences: true }),
|
|
324
|
-
execution,
|
|
325
|
-
runId,
|
|
326
|
-
task,
|
|
327
|
-
worktreePath
|
|
328
|
-
}),
|
|
331
|
+
prepared,
|
|
329
332
|
runControl,
|
|
330
333
|
runId,
|
|
331
334
|
task,
|
|
@@ -22,6 +22,10 @@ const resolveMokaBrokerAuth = (globalConfig) => {
|
|
|
22
22
|
if (brokerAuth === void 0) throw new Error("momokaya.submit.brokerAuth is required for remote submit");
|
|
23
23
|
return brokerAuth;
|
|
24
24
|
};
|
|
25
|
+
const deliveryFromFlags = (flags) => ({
|
|
26
|
+
...flags.preview === void 0 ? {} : { preview: flags.preview },
|
|
27
|
+
pullRequest: flags.openPr === true
|
|
28
|
+
});
|
|
25
29
|
const submitMokaCommandInput = (input, flags, commonOptions) => {
|
|
26
30
|
if (flags.quick === true || flags.schedule !== void 0 && flags.schedule !== "") throw new Error("--command cannot be combined with --quick or --schedule");
|
|
27
31
|
if (input.length === 0) throw new Error("Command argv is required when --command is set");
|
|
@@ -52,7 +56,7 @@ const addRunnerArgoOptions = (command, options = {}) => {
|
|
|
52
56
|
"Never"
|
|
53
57
|
]).default("Always")).option("--image-pull-secret <name>", "imagePullSecret name");
|
|
54
58
|
};
|
|
55
|
-
const addMokaSubmitOptions = (command) => addRunnerArgoOptions(command.option("--quick", "submit the compact graph").option("--command", "treat input after -- as explicit argv").option("--schedule <path>", "approved schedule YAML to submit").option("--event-url <url>", "runner event sink URL").option("--open-pr", "append an open-pull-request delivery node
|
|
59
|
+
const addMokaSubmitOptions = (command) => addRunnerArgoOptions(command.option("--quick", "submit the compact graph").option("--command", "treat input after -- as explicit argv").option("--schedule <path>", "approved schedule YAML to submit").option("--event-url <url>", "runner event sink URL").option("--open-pr", "append an open-pull-request delivery node").option("--preview", "add the canonical preview label to pull-request delivery").option("--no-preview", "remove the canonical preview label from existing pull-request delivery").option("--task <text>", "task description for command-mode metadata").option("--db-auth-secret-name <name>", "override momokaya.submit.dbAuth secret name").option("--db-auth-secret-key <key>", "override momokaya.submit.dbAuth secret key").option("--skip-db-auth", "omit MOKA_DB_URL injection regardless of global config").option("--mcp-gateway-auth-secret-name <name>", "override momokaya.submit.mcpGatewayAuth secret name").option("--mcp-gateway-auth-secret-key <key>", "override momokaya.submit.mcpGatewayAuth secret key").option("--skip-mcp-gateway-auth", "omit PIPELINE_MCP_GATEWAY_AUTHORIZATION injection regardless of global config").option("--npm-registry-auth-secret-name <name>", "override momokaya.submit.npmRegistryAuthSecretName").option("--skip-npm-registry-auth", "omit the /root/.npmrc mount regardless of global config"), { kubeconfig: true });
|
|
56
60
|
const parseImagePullPolicy = (value) => {
|
|
57
61
|
if (value === "IfNotPresent" || value === "Never") return value;
|
|
58
62
|
return "Always";
|
|
@@ -67,7 +71,7 @@ const mokaCommonSubmitOptions = (input) => {
|
|
|
67
71
|
secretName: input.flags.dbAuthSecretName,
|
|
68
72
|
skip: input.flags.skipDbAuth
|
|
69
73
|
}, momokaya?.submit.dbAuth),
|
|
70
|
-
delivery:
|
|
74
|
+
delivery: deliveryFromFlags(input.flags),
|
|
71
75
|
eventAuthSecretKey: momokaya?.submit.eventAuthSecretKey,
|
|
72
76
|
eventAuthSecretName: momokaya?.submit.eventAuthSecretName,
|
|
73
77
|
eventUrl: input.eventUrl,
|
|
@@ -107,11 +111,13 @@ const buildMokaSubmitInputFromCli = (input) => {
|
|
|
107
111
|
};
|
|
108
112
|
const runMokaSubmitFromCli = async (input, flags) => {
|
|
109
113
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
114
|
+
const config = loadPipelineConfig(cwd, { allowMissingLintFileReferences: true });
|
|
115
|
+
const globalConfig = loadMokaGlobalConfig() ?? void 0;
|
|
110
116
|
return await submitMoka(buildMokaSubmitInputFromCli({
|
|
111
|
-
config
|
|
117
|
+
config,
|
|
112
118
|
cwd,
|
|
113
119
|
flags,
|
|
114
|
-
globalConfig
|
|
120
|
+
globalConfig,
|
|
115
121
|
input
|
|
116
122
|
}));
|
|
117
123
|
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createCoordinator } from "../coordinator/composition.js";
|
|
2
|
+
//#region src/commands/coordinator-command.ts
|
|
3
|
+
/**
|
|
4
|
+
* `coordinator` — the deployed coordinator process entrypoint (ENG-57.1,
|
|
5
|
+
* doc-1 decision 3). Starts the coordinator's RunSpec registry/HTTP server
|
|
6
|
+
* and default Jobs transport from the validated `~/.config/moka/config.yaml`,
|
|
7
|
+
* then blocks until SIGTERM/SIGINT (the chart Deployment's normal shutdown
|
|
8
|
+
* signal) so the RunSpec server's listening socket is released cleanly
|
|
9
|
+
* (AC#4) instead of the pod being killed out from under an open listener.
|
|
10
|
+
*
|
|
11
|
+
* Submission itself (`coordinator.submit`) is not triggered from this
|
|
12
|
+
* command — ENG-57.2/57.4 own wiring a caller (dynamic wave scheduling, run
|
|
13
|
+
* truth) onto the composed `Coordinator`. This command only owns the
|
|
14
|
+
* process lifecycle the chart Deployment needs to keep that composition
|
|
15
|
+
* alive and reachable.
|
|
16
|
+
*/
|
|
17
|
+
const registerCoordinatorCommand = (program) => {
|
|
18
|
+
program.command("coordinator").description("Start the coordinator's RunSpec service and Jobs-transport composition (ENG-57.1)").action(async () => {
|
|
19
|
+
const coordinator = await createCoordinator();
|
|
20
|
+
const shutdown = async () => {
|
|
21
|
+
await coordinator.close();
|
|
22
|
+
process.exitCode = 0;
|
|
23
|
+
};
|
|
24
|
+
await new Promise((resolve) => {
|
|
25
|
+
process.once("SIGTERM", () => {
|
|
26
|
+
shutdown().then(resolve);
|
|
27
|
+
});
|
|
28
|
+
process.once("SIGINT", () => {
|
|
29
|
+
shutdown().then(resolve);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { registerCoordinatorCommand };
|
|
@@ -30,7 +30,8 @@ const configureEntrypointOptions = (command, entrypoint) => {
|
|
|
30
30
|
return command;
|
|
31
31
|
};
|
|
32
32
|
const createEntrypointCommand = (program, id, entrypoint) => {
|
|
33
|
-
|
|
33
|
+
const command = program.command(id).description(entrypoint.description ?? `Run the ${id} workflow`).argument("<description...>", "task description");
|
|
34
|
+
return configureEntrypointOptions(command, entrypoint);
|
|
34
35
|
};
|
|
35
36
|
const registerEntrypointAction = (command, id, serviceLive) => {
|
|
36
37
|
command.action(async (descriptionParts, flags) => {
|
|
@@ -43,7 +44,8 @@ const registerConfiguredEntrypointCommands = (program, config, runEntrypoint) =>
|
|
|
43
44
|
const reservedCommands = new Set(program.commands.map((command) => command.name()));
|
|
44
45
|
for (const [id, entrypoint] of Object.entries(config.entrypoints)) {
|
|
45
46
|
if (reservedCommands.has(id)) continue;
|
|
46
|
-
|
|
47
|
+
const command = createEntrypointCommand(program, id, entrypoint);
|
|
48
|
+
registerEntrypointAction(command, id, serviceLive);
|
|
47
49
|
registered.add(id);
|
|
48
50
|
reservedCommands.add(id);
|
|
49
51
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runRunnerCommand } from "../runner-command/run.js";
|
|
2
|
+
import { runPreSchedulePhase } from "../runner-command/pre-schedule.js";
|
|
2
3
|
import { runRunnerFinalize } from "../runner-command/finalize.js";
|
|
3
4
|
import { runRunnerLifecycle } from "../runner-command/lifecycle.js";
|
|
4
|
-
import { runPreSchedulePhase } from "../runner-command/pre-schedule.js";
|
|
5
5
|
import { runSelectReadyWave } from "../runner-command/select-ready-wave.js";
|
|
6
6
|
import { Context, Effect, Layer } from "effect";
|
|
7
7
|
//#region src/commands/runner-command-command.ts
|
|
@@ -32,19 +32,24 @@ const setProcessExitCode = (exitCode) => Effect.sync(() => {
|
|
|
32
32
|
process.exitCode = exitCode;
|
|
33
33
|
});
|
|
34
34
|
const runRunnerCommandEffect = (options) => Effect.gen(function* runRunnerCommandEffect() {
|
|
35
|
-
|
|
35
|
+
const exitCode = yield* (yield* RunnerCommandService).run(options);
|
|
36
|
+
yield* setProcessExitCode(exitCode);
|
|
36
37
|
});
|
|
37
38
|
const runRunnerLifecycleEffect = (options) => Effect.gen(function* runRunnerLifecycleEffect() {
|
|
38
|
-
|
|
39
|
+
const exitCode = yield* (yield* RunnerCommandService).lifecycle(options);
|
|
40
|
+
yield* setProcessExitCode(exitCode);
|
|
39
41
|
});
|
|
40
42
|
const runRunnerFinalizeEffect = (options) => Effect.gen(function* runRunnerFinalizeEffect() {
|
|
41
|
-
|
|
43
|
+
const exitCode = yield* (yield* RunnerCommandService).finalize(options);
|
|
44
|
+
yield* setProcessExitCode(exitCode);
|
|
42
45
|
});
|
|
43
46
|
const runPreScheduleEffect = (options) => Effect.gen(function* runPreScheduleEffect() {
|
|
44
|
-
|
|
47
|
+
const exitCode = yield* (yield* RunnerCommandService).preSchedule(options);
|
|
48
|
+
yield* setProcessExitCode(exitCode);
|
|
45
49
|
});
|
|
46
50
|
const runSelectReadyWaveEffect = (options) => Effect.gen(function* runSelectReadyWaveEffect() {
|
|
47
|
-
|
|
51
|
+
const exitCode = yield* (yield* RunnerCommandService).selectReadyWave(options);
|
|
52
|
+
yield* setProcessExitCode(exitCode);
|
|
48
53
|
});
|
|
49
54
|
const runRunnerProgram = async (program) => await Effect.runPromise(Effect.provide(program, RunnerCommandServiceLive));
|
|
50
55
|
const registerRunnerCommandCommand = (program) => {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { runJobNodeFromEnv } from "../remote/jobs/node-bootstrap.js";
|
|
2
|
+
//#region src/commands/runner-node-command.ts
|
|
3
|
+
/** Parse a commander string option into a non-negative integer, failing loudly on a bad value. */
|
|
4
|
+
const parseNonNegativeIntOption = (value, label) => {
|
|
5
|
+
if (value === void 0) return;
|
|
6
|
+
const parsed = Number(value);
|
|
7
|
+
if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`--${label} must be a non-negative integer, got '${value}'`);
|
|
8
|
+
return parsed;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* `runner-node` — the Jobs transport container entrypoint (doc-1 decision 3,
|
|
12
|
+
* ENG-18). Fetches this node's RunSpec from the coordinator API (base URL +
|
|
13
|
+
* token supplied via the Job's env), runs it through the yeet executor, and
|
|
14
|
+
* exits with the agent's exit code so the Job's backoffLimit only retries a
|
|
15
|
+
* genuine crash (a non-zero process exit), never a semantic gate outcome.
|
|
16
|
+
*/
|
|
17
|
+
const registerRunnerNodeCommand = (program) => {
|
|
18
|
+
program.command("runner-node").description("Fetch a node's RunSpec from the coordinator API and run it via yeet (Jobs transport)").option("--run-id <id>", "Run id (defaults to the PIPELINE_RUN_ID env)").option("--node-id <id>", "Node id (defaults to the PIPELINE_NODE_ID env)").option("--generation <n>", "Execution generation (defaults to the PIPELINE_EXECUTION_GENERATION env, or 0)").option("--attempt <n>", "Crash-retry attempt (defaults to the PIPELINE_EXECUTION_ATTEMPT env, or 1)").action(async (options) => {
|
|
19
|
+
const generation = parseNonNegativeIntOption(options.generation, "generation");
|
|
20
|
+
const attempt = parseNonNegativeIntOption(options.attempt, "attempt");
|
|
21
|
+
const result = await runJobNodeFromEnv({
|
|
22
|
+
...options.nodeId === void 0 ? {} : { nodeId: options.nodeId },
|
|
23
|
+
...options.runId === void 0 ? {} : { runId: options.runId },
|
|
24
|
+
...generation === void 0 ? {} : { generation },
|
|
25
|
+
...attempt === void 0 ? {} : { attempt }
|
|
26
|
+
}, process.env);
|
|
27
|
+
process.exitCode = result.agentResult.exitCode;
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { registerRunnerNodeCommand };
|
|
@@ -100,12 +100,14 @@ const runTicketScoperEffect = (launchPlan, executor) => Effect.gen(function* eff
|
|
|
100
100
|
const formatAppliedTicketPlan = (applied) => ["Created tickets:", ...Object.entries(applied.taskIdsByKey).map(([key, taskId]) => ` ${key}: ${taskId}`)].join("\n");
|
|
101
101
|
const printTicketCreateEffect = (worktreePath, request, flags, executor) => Effect.gen(function* effectBody() {
|
|
102
102
|
yield* validateTicketCreateFlagsEffect(flags);
|
|
103
|
-
const
|
|
103
|
+
const launchPlan = yield* ticketScoperLaunchPlanEffect(worktreePath, request);
|
|
104
|
+
const ticketPlan = yield* parseTicketPlanEffect(yield* runTicketScoperEffect(launchPlan, executor));
|
|
104
105
|
if (flags.dryRun === true) {
|
|
105
106
|
yield* writeLineEffect(renderTicketPlanDryRun(ticketPlan));
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
+
const applied = yield* applyTicketPlanEffect(ticketPlan, worktreePath, { parentId: flags.parent });
|
|
110
|
+
yield* writeLineEffect(formatAppliedTicketPlan(applied));
|
|
109
111
|
});
|
|
110
112
|
const registerCreateSubcommand = (ticketCommand, options) => {
|
|
111
113
|
ticketCommand.command("create").description("Create a validated Backlog ticket plan").argument("<request...>", "ticket planning request").option("--dry-run", "render Backlog commands without writing tasks").option("--apply", "apply the validated ticket plan through Backlog").option("--parent <task-id>", "existing parent task for applied children").action(async (requestParts, flags) => {
|
|
@@ -5,7 +5,8 @@ import { Effect, Option } from "effect";
|
|
|
5
5
|
const formatSequence = (batches) => batches.map((batch, index) => [`Sequence ${index + 1}:`, ...batch.map((id) => ` ${id}`)].join("\n")).join("\n\n");
|
|
6
6
|
const printTicketSequenceEffect = (worktreePath, flags) => Effect.gen(function* effectBody() {
|
|
7
7
|
const loaded = yield* loadTicketGraphEffect(worktreePath, Option.fromUndefinedOr(flags.root));
|
|
8
|
-
|
|
8
|
+
const batches = yield* sequenceTicketBatchesEffect(loaded.graph, loaded.scopedIds);
|
|
9
|
+
yield* writeLineEffect(formatSequence(batches));
|
|
9
10
|
});
|
|
10
11
|
const registerSequenceSubcommand = (ticketCommand, _options) => {
|
|
11
12
|
ticketCommand.command("sequence").description("Print dependency execution batches for Backlog tickets").option("--root <ticket-id>", "sequence one ticket tree").option("--plain", "print plain text output").action(async (flags) => {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { RunCommand } from "../../cli/run-command.js";
|
|
2
2
|
import { AgentResult, RunnerExecutionOptions, RunnerLaunchPlan } from "../../runner.js";
|
|
3
3
|
import { BacklogService } from "../../runtime/services/backlog-service.js";
|
|
4
|
+
import "../../runtime/services/repo-io-service.js";
|
|
5
|
+
import "../../tickets/backlog-task-store.js";
|
|
6
|
+
import "../../tickets/ticket-graph.js";
|
|
7
|
+
import "../../tickets/ticket-selection.js";
|
|
4
8
|
import { Effect, Layer, Option } from "effect";
|
|
5
|
-
|
|
6
9
|
//#region src/commands/ticket/shared.d.ts
|
|
7
10
|
type TicketPlanExecutor = (plan: RunnerLaunchPlan, options: RunnerExecutionOptions) => Promise<AgentResult>;
|
|
8
11
|
interface TicketCommandOptions {
|
package/dist/config/load.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { PipelineConfig, PipelineConfigParts, PipelineConfigValidationOptions } from "./schemas.js";
|
|
2
|
-
|
|
3
2
|
//#region src/config/load.d.ts
|
|
4
3
|
declare const loadPackagePipelineConfig: (projectRoot: string, options?: PipelineConfigValidationOptions) => PipelineConfig;
|
|
5
4
|
declare const loadPipelineConfig: (projectRoot: string, options?: PipelineConfigValidationOptions) => PipelineConfig;
|
package/dist/config/load.js
CHANGED
|
@@ -47,6 +47,7 @@ const assembledPipelineConfig = (pipeline, profiles, runners) => ({
|
|
|
47
47
|
rules: profiles.rules,
|
|
48
48
|
runner_command: pipeline.runner_command,
|
|
49
49
|
runners: runners.runners,
|
|
50
|
+
runtime: pipeline.runtime,
|
|
50
51
|
scheduler: pipeline.scheduler,
|
|
51
52
|
schedules: pipeline.schedules,
|
|
52
53
|
skills: profiles.skills,
|
|
@@ -58,7 +59,8 @@ const assembledPipelineConfig = (pipeline, profiles, runners) => ({
|
|
|
58
59
|
const parsePipelineConfigPartsEffect = (sources, projectRoot = "", sourcePaths, options) => Effect.gen(function* effectBody() {
|
|
59
60
|
const runners = yield* parseConfigYamlAs(sources.runners, sourcePaths.runners, runnersFileSchema);
|
|
60
61
|
const profiles = yield* parseConfigYamlAs(sources.profiles, sourcePaths.profiles, profilesFileSchema);
|
|
61
|
-
|
|
62
|
+
const pipeline = yield* parsePipelineFileEffect(sources.pipeline, sourcePaths.pipeline);
|
|
63
|
+
return validatePipelineConfig(assembledPipelineConfig(pipeline, profiles, runners), projectRoot, options);
|
|
62
64
|
});
|
|
63
65
|
const loadPackagePipelineConfig = (projectRoot, options = {}) => {
|
|
64
66
|
return runConfigIoSync(parsePipelineConfigPartsEffect({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//#region src/config/schema/catalog.d.ts
|
|
2
2
|
declare const RUNNER_TYPES: readonly ["opencode", "command"];
|
|
3
|
+
declare const RUNTIME_EXECUTORS: readonly ["sdk", "yeet"];
|
|
3
4
|
declare const NODE_KINDS: readonly ["agent", "command", "builtin", "group", "parallel"];
|
|
4
5
|
declare const HOOK_EVENTS: readonly ["workflow.start", "workflow.success", "workflow.failure", "workflow.complete", "node.start", "node.success", "node.error", "node.finish", "gate.failure"];
|
|
5
6
|
declare const GATE_KINDS: readonly ["acceptance", "artifact", "builtin", "changed_files", "command", "json_schema", "verdict"];
|
|
@@ -8,4 +9,4 @@ declare const SCHEDULING_ROLES: readonly ["coverage", "implementation"];
|
|
|
8
9
|
declare const MCP_GATEWAY_BACKEND_LOCALITIES: readonly ["repo-local", "repo-scoped-remote", "shared-remote"];
|
|
9
10
|
declare const MCP_GATEWAY_WORKSPACE_PATH_SOURCES: readonly ["PIPELINE_TARGET_PATH", "cwd"];
|
|
10
11
|
//#endregion
|
|
11
|
-
export { GATE_KINDS, HOOK_EVENTS, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NODE_KINDS, RUNNER_TYPES, SCHEDULE_BASELINES, SCHEDULING_ROLES };
|
|
12
|
+
export { GATE_KINDS, HOOK_EVENTS, MCP_GATEWAY_BACKEND_LOCALITIES, MCP_GATEWAY_WORKSPACE_PATH_SOURCES, NODE_KINDS, RUNNER_TYPES, RUNTIME_EXECUTORS, SCHEDULE_BASELINES, SCHEDULING_ROLES };
|