stageflow 0.19.0 → 0.25.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 +16 -3
- package/dist/a2a/configDiscovery.d.ts +9 -0
- package/dist/a2a/configDiscovery.js +18 -0
- package/dist/a2a/contracts.d.ts +93 -0
- package/dist/a2a/contracts.js +163 -0
- package/dist/a2a/limits.d.ts +14 -0
- package/dist/a2a/limits.js +29 -0
- package/dist/a2a/registry.d.ts +44 -0
- package/dist/a2a/registry.js +159 -0
- package/dist/a2a/server.d.ts +29 -0
- package/dist/a2a/server.js +341 -0
- package/dist/a2a/service.d.ts +97 -0
- package/dist/a2a/service.js +496 -0
- package/dist/a2a/store.d.ts +106 -0
- package/dist/a2a/store.js +245 -0
- package/dist/agent/activity.d.ts +5 -0
- package/dist/agent/activityObserver.d.ts +3 -0
- package/dist/agent/activityObserver.js +6 -1
- package/dist/agent/piAdapter.js +7 -3
- package/dist/agent/port.d.ts +2 -0
- package/dist/agent/providerInspect.d.ts +19 -0
- package/dist/agent/providerInspect.js +35 -0
- package/dist/agent/streamLogRedact.d.ts +2 -0
- package/dist/agent/streamLogRedact.js +13 -0
- package/dist/cli/a2aCommand.d.ts +8 -0
- package/dist/cli/a2aCommand.js +131 -0
- package/dist/cli/artifactCommand.js +2 -2
- package/dist/cli/envelopeCommand.js +2 -2
- package/dist/cli/exportRunCommand.js +2 -2
- package/dist/cli/hostClient.d.ts +43 -0
- package/dist/cli/hostClient.js +182 -0
- package/dist/cli/providersCommand.js +5 -3
- package/dist/cli/runCommand.d.ts +6 -5
- package/dist/cli/runCommand.js +23 -31
- package/dist/cli/runOutput.d.ts +1 -1
- package/dist/cli/runStageCommand.d.ts +33 -0
- package/dist/cli/runStageCommand.js +407 -0
- package/dist/cli/runsCommand.d.ts +3 -7
- package/dist/cli/runsCommand.js +33 -94
- package/dist/cli.js +20 -5
- package/dist/config/describePipeline.d.ts +26 -0
- package/dist/config/describePipeline.js +37 -0
- package/dist/config/loadPipeline.d.ts +13 -1
- package/dist/config/loadPipeline.js +34 -4
- package/dist/config/mergePipelineIncludes.d.ts +11 -0
- package/dist/config/mergePipelineIncludes.js +75 -0
- package/dist/config/validateCatalog.d.ts +2 -2
- package/dist/config/validateCatalog.js +24 -17
- package/dist/mcp/catalogTools.js +285 -52
- package/dist/mcp/controlTools.js +77 -19
- package/dist/mcp/deps.d.ts +3 -0
- package/dist/mcp/projectMcpTools.d.ts +3 -0
- package/dist/mcp/projectMcpTools.js +25 -0
- package/dist/mcp/providerTools.d.ts +3 -0
- package/dist/mcp/providerTools.js +17 -0
- package/dist/mcp/readArtifact.d.ts +9 -0
- package/dist/mcp/readArtifact.js +11 -0
- package/dist/mcp/server.js +9 -1
- package/dist/mcp/tailStreamLog.d.ts +8 -0
- package/dist/mcp/tailStreamLog.js +28 -0
- package/dist/mcp/toolResults.d.ts +17 -0
- package/dist/mcp/toolResults.js +15 -0
- package/dist/mcp/tools.js +4 -0
- package/dist/mcp/waitRun.js +4 -4
- package/dist/mcp/waitingGates.d.ts +1 -0
- package/dist/mcp/waitingGates.js +3 -2
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/projection/projectRun.d.ts +3 -0
- package/dist/projection/projectRun.js +7 -0
- package/dist/runstore/createStore.d.ts +10 -0
- package/dist/runstore/createStore.js +13 -4
- package/dist/runstore/port.d.ts +17 -2
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +11 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +81 -26
- package/dist/runstore/sqlite/schema.d.ts +1 -1
- package/dist/runstore/sqlite/schema.js +6 -0
- package/dist/runstore/submission.d.ts +11 -0
- package/dist/runstore/submission.js +7 -0
- package/dist/runstore/workspaceLayout.d.ts +1 -0
- package/dist/runstore/workspaceLayout.js +3 -0
- package/dist/runtime/feedbackLoopCoordinator.d.ts +9 -1
- package/dist/runtime/feedbackLoopCoordinator.js +50 -20
- package/dist/runtime/feedbackLoopDecision.js +33 -1
- package/dist/runtime/pipelineRunner.d.ts +5 -3
- package/dist/runtime/pipelineRunner.js +6 -1
- package/dist/runtime/pipelineScheduler.js +32 -15
- package/dist/runtime/reloadRunCatalog.js +7 -1
- package/dist/runtime/resolveEnvelopeRef.d.ts +30 -0
- package/dist/runtime/resolveEnvelopeRef.js +79 -0
- package/dist/runtime/resumeReconstruct.js +2 -2
- package/dist/runtime/runManager.d.ts +24 -2
- package/dist/runtime/runManager.js +83 -19
- package/dist/runtime/settingsFile.d.ts +2 -0
- package/dist/runtime/settingsFile.js +19 -0
- package/dist/runtime/stageAttemptBootstrap.d.ts +3 -0
- package/dist/runtime/stageAttemptBootstrap.js +8 -2
- package/dist/runtime/stageStreamLog.d.ts +17 -0
- package/dist/runtime/stageStreamLog.js +89 -0
- package/dist/runtime/stageWorker.js +2 -1
- package/dist/server/bootstrap.d.ts +2 -0
- package/dist/server/bootstrap.js +22 -4
- package/dist/server/createHttpHost.js +6 -0
- package/dist/server/ensureGlobalService.d.ts +35 -0
- package/dist/server/ensureGlobalService.js +138 -0
- package/dist/server/http.d.ts +20 -1
- package/dist/server/http.js +450 -402
- package/dist/server/mcpHost.js +7 -8
- package/dist/server/operatorResults.d.ts +10 -0
- package/dist/server/operatorResults.js +24 -0
- package/dist/server/providerRoutes.d.ts +0 -6
- package/dist/server/providerRoutes.js +10 -18
- package/dist/types/pipeline.d.ts +8 -0
- package/dist/ui/assets/{index-DMVdl1qB.js → index-Da5EHU6M.js} +5 -5
- package/dist/ui/index.html +1 -1
- package/package.json +2 -1
- package/skills/stageflow/references/control-surface.md +2 -2
- package/skills/stageflow-run/SKILL.md +27 -9
- package/skills/stageflow-run/references/mcp-call.md +1 -1
- package/skills/stageflow-run/scripts/mcp-call.mjs +9 -0
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
+
import { A2A_USAGE, runA2aCommand } from "./cli/a2aCommand.js";
|
|
3
4
|
import { realpathSync } from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -10,6 +11,7 @@ import { EXPORT_RUN_USAGE, runExportRunCommand, } from "./cli/exportRunCommand.j
|
|
|
10
11
|
import { INIT_USAGE, runInitCommand } from "./cli/initCommand.js";
|
|
11
12
|
import { PROVIDERS_USAGE, runProvidersCommand } from "./cli/providersCommand.js";
|
|
12
13
|
import { RUN_USAGE, runRunCommand } from "./cli/runCommand.js";
|
|
14
|
+
import { RUN_STAGE_USAGE, runRunStageCommand } from "./cli/runStageCommand.js";
|
|
13
15
|
import { RUNS_USAGE, runRunsCommand } from "./cli/runsCommand.js";
|
|
14
16
|
import { resolveOperatorCatalog } from "./cli/operatorCatalog.js";
|
|
15
17
|
import { SKILLS_USAGE, runSkillsCommand } from "./cli/skillsCommand.js";
|
|
@@ -27,6 +29,7 @@ import { PACKAGE_VERSION } from "./package-meta.js";
|
|
|
27
29
|
const USAGE = `Usage:
|
|
28
30
|
sf init
|
|
29
31
|
sf run --task <path> --pipeline <path> [--checkout <path>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]
|
|
32
|
+
sf run-stage (--stage <path> | --stage-inline '<json>') (--task <path> | --task-inline '<json>' | --envelope-ref <runId>:<stageId>[:<attempt>]) [--checkout <path>] [--model <id>] [--blocking] [--timeout-ms <n>] [--json]
|
|
30
33
|
sf validate [--pipeline <path>] [--task <path>] [--strict] [--json]
|
|
31
34
|
sf graph --pipeline <path> [--json]
|
|
32
35
|
sf migrate-yaml [path] [--root <path>] [--write] [--json] [--force]
|
|
@@ -58,6 +61,8 @@ const USAGE = `Usage:
|
|
|
58
61
|
sf --version
|
|
59
62
|
sf -V
|
|
60
63
|
sf --help
|
|
64
|
+
sf a2a validate|list [--config <path>]
|
|
65
|
+
sf a2a add-caller <id> [--config <path>] [--token-env <NAME>]
|
|
61
66
|
|
|
62
67
|
Stageflow (sf) runs YAML-defined automatic stage pipelines.
|
|
63
68
|
|
|
@@ -67,10 +72,14 @@ ${INIT_USAGE}
|
|
|
67
72
|
|
|
68
73
|
${RUN_USAGE}
|
|
69
74
|
|
|
75
|
+
${RUN_STAGE_USAGE}
|
|
76
|
+
|
|
70
77
|
${VALIDATE_USAGE}
|
|
71
78
|
|
|
72
79
|
${GRAPH_USAGE}
|
|
73
80
|
|
|
81
|
+
${A2A_USAGE}
|
|
82
|
+
|
|
74
83
|
${MIGRATE_YAML_USAGE}
|
|
75
84
|
|
|
76
85
|
${ARTIFACT_USAGE}
|
|
@@ -103,8 +112,10 @@ function parseArgs(argv) {
|
|
|
103
112
|
if (command === "providers" ||
|
|
104
113
|
command === "validate" ||
|
|
105
114
|
command === "graph" ||
|
|
115
|
+
command === "a2a" ||
|
|
106
116
|
command === "migrate-yaml" ||
|
|
107
117
|
command === "run" ||
|
|
118
|
+
command === "run-stage" ||
|
|
108
119
|
command === "init" ||
|
|
109
120
|
command === "artifact" ||
|
|
110
121
|
command === "envelope" ||
|
|
@@ -282,6 +293,9 @@ async function main(argv) {
|
|
|
282
293
|
return 0;
|
|
283
294
|
}
|
|
284
295
|
const ctx = await resolveStageflowContext(process.cwd());
|
|
296
|
+
if (parsed.command === "a2a") {
|
|
297
|
+
return runA2aCommand(argv.slice(3), { cwd: ctx.invocationCwd, projectRoot: ctx.projectRoot });
|
|
298
|
+
}
|
|
285
299
|
if (parsed.command === "init") {
|
|
286
300
|
return runInitCommand(argv.slice(3), { cwd: ctx.invocationCwd });
|
|
287
301
|
}
|
|
@@ -306,8 +320,11 @@ async function main(argv) {
|
|
|
306
320
|
if (parsed.command === "run") {
|
|
307
321
|
return runRunCommand(argv.slice(3), {
|
|
308
322
|
cwd: ctx.invocationCwd,
|
|
309
|
-
|
|
310
|
-
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
if (parsed.command === "run-stage") {
|
|
326
|
+
return runRunStageCommand(argv.slice(3), {
|
|
327
|
+
cwd: ctx.invocationCwd,
|
|
311
328
|
});
|
|
312
329
|
}
|
|
313
330
|
if (parsed.command === "artifact") {
|
|
@@ -331,8 +348,6 @@ async function main(argv) {
|
|
|
331
348
|
if (parsed.command === "runs") {
|
|
332
349
|
return runRunsCommand(argv.slice(3), {
|
|
333
350
|
cwd: ctx.invocationCwd,
|
|
334
|
-
projectRoot: ctx.projectRoot,
|
|
335
|
-
isGitProject: ctx.isGitProject,
|
|
336
351
|
});
|
|
337
352
|
}
|
|
338
353
|
if (parsed.command === "providers") {
|
|
@@ -344,7 +359,7 @@ async function main(argv) {
|
|
|
344
359
|
projectRoot: ctx.projectRoot,
|
|
345
360
|
});
|
|
346
361
|
}
|
|
347
|
-
const store = createRunStore({ rootDir: ctx.
|
|
362
|
+
const store = createRunStore({ rootDir: ctx.globalHome });
|
|
348
363
|
const globalAgent = globalAgentBackendFromManifest(ctx.manifest);
|
|
349
364
|
if (parsed.command === "ui") {
|
|
350
365
|
const mcpStateless = resolveMcpStateless({
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CloneMode, FeedbackLoopConfig, LoadedPipeline, PipelineForkConfig, PipelineNeedEdge, ResolvedPipelineStageNode, RouteIfPredicate } from "../types/pipeline.js";
|
|
2
|
+
import type { StageGateKind } from "../types/stage.js";
|
|
3
|
+
export type PipelineDescribeNeedEdge = {
|
|
4
|
+
id: string;
|
|
5
|
+
on: PipelineNeedEdge["on"];
|
|
6
|
+
if?: RouteIfPredicate;
|
|
7
|
+
};
|
|
8
|
+
export type PipelineDescribeNeeds = string | null | PipelineDescribeNeedEdge[];
|
|
9
|
+
export type PipelineDescribeStage = {
|
|
10
|
+
id: string;
|
|
11
|
+
needs: PipelineDescribeNeeds;
|
|
12
|
+
fork?: PipelineForkConfig;
|
|
13
|
+
gate_kinds?: StageGateKind[];
|
|
14
|
+
clone_cap?: number;
|
|
15
|
+
clone_mode?: CloneMode;
|
|
16
|
+
feedback_loop?: FeedbackLoopConfig;
|
|
17
|
+
entry?: boolean;
|
|
18
|
+
replay_safe?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type PipelineDescribe = {
|
|
21
|
+
id: string;
|
|
22
|
+
path: string;
|
|
23
|
+
stages: PipelineDescribeStage[];
|
|
24
|
+
};
|
|
25
|
+
export declare function describePipelineNeeds(node: ResolvedPipelineStageNode): PipelineDescribeNeeds;
|
|
26
|
+
export declare function describePipeline(loaded: LoadedPipeline): PipelineDescribe;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { toPipelineNeeds } from "./pipelineNeeds.js";
|
|
2
|
+
export function describePipelineNeeds(node) {
|
|
3
|
+
if (node.needsEdges.length === 0)
|
|
4
|
+
return node.needs;
|
|
5
|
+
const collapsed = toPipelineNeeds(node.needsEdges);
|
|
6
|
+
if (typeof collapsed === "string")
|
|
7
|
+
return collapsed;
|
|
8
|
+
return collapsed.map((edge) => ({
|
|
9
|
+
id: edge.id,
|
|
10
|
+
on: [...edge.on],
|
|
11
|
+
...(edge.if !== undefined ? { if: edge.if } : {}),
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
export function describePipeline(loaded) {
|
|
15
|
+
const gateById = new Map(loaded.stages.map((stage) => [stage.id, stage.gate_kinds]));
|
|
16
|
+
const stages = loaded.dag.nodes.map((node) => {
|
|
17
|
+
const gateKinds = gateById.get(node.id);
|
|
18
|
+
return {
|
|
19
|
+
id: node.id,
|
|
20
|
+
needs: describePipelineNeeds(node),
|
|
21
|
+
...(node.fork !== undefined ? { fork: { ...node.fork } } : {}),
|
|
22
|
+
...(node.clone_cap !== undefined ? { clone_cap: node.clone_cap } : {}),
|
|
23
|
+
...(node.clone_mode !== undefined ? { clone_mode: node.clone_mode } : {}),
|
|
24
|
+
...(node.feedback_loop !== undefined
|
|
25
|
+
? { feedback_loop: { ...node.feedback_loop } }
|
|
26
|
+
: {}),
|
|
27
|
+
...(node.entry !== undefined ? { entry: node.entry } : {}),
|
|
28
|
+
...(node.replay_safe !== undefined ? { replay_safe: node.replay_safe } : {}),
|
|
29
|
+
...(gateKinds !== undefined ? { gate_kinds: [...gateKinds] } : {}),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
id: loaded.pipeline.id,
|
|
34
|
+
path: loaded.pipelinePath,
|
|
35
|
+
stages,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import type { LoadedPipeline } from "../types/pipeline.js";
|
|
1
|
+
import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
|
|
2
2
|
import { type LoadOutcome } from "./loadOutcome.js";
|
|
3
3
|
export type { LoadedPipeline } from "../types/pipeline.js";
|
|
4
4
|
export type { LoadIssue, LoadOutcome } from "./loadOutcome.js";
|
|
5
5
|
export declare function resolvePipelinePath(pipelinePath: string, cwd: string): Promise<string>;
|
|
6
|
+
/** Synthetic `pipelinePath` for a `LoadedPipeline` built from an inline object with no backing file. */
|
|
7
|
+
export declare const INLINE_PIPELINE_PATH = "<inline>";
|
|
8
|
+
export declare function loadPipelineFromObjectOutcome(raw: InlinePipelineDefinition, options?: {
|
|
9
|
+
cwd?: string;
|
|
10
|
+
projectRoot?: string;
|
|
11
|
+
requireIo?: boolean;
|
|
12
|
+
}): Promise<LoadOutcome<LoadedPipeline>>;
|
|
13
|
+
export declare function loadPipelineFromObject(raw: InlinePipelineDefinition, options?: {
|
|
14
|
+
cwd?: string;
|
|
15
|
+
projectRoot?: string;
|
|
16
|
+
requireIo?: boolean;
|
|
17
|
+
}): Promise<LoadedPipeline>;
|
|
6
18
|
export declare function loadPipelineOutcome(nameOrPath: string, options?: {
|
|
7
19
|
cwd?: string;
|
|
8
20
|
projectRoot?: string;
|
|
@@ -2,7 +2,7 @@ import { access } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { compilePayloadSchema, expandPayloadSchemaRefs, isPayloadSchemaSubset, UnresolvedSchemaRefError, } from "../envelope/payloadSchema.js";
|
|
4
4
|
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
5
|
-
import { mergePipelineStages } from "./mergePipelineIncludes.js";
|
|
5
|
+
import { mergeInlinePipelineStages, mergePipelineStages, } from "./mergePipelineIncludes.js";
|
|
6
6
|
import { normalizePipelineStageEntries, toWiringRefs, } from "./normalizePipelineStageEntry.js";
|
|
7
7
|
import { loadStageFromObjectOutcome, loadStageOutcome, afterCompletionForStage } from "./loadStage.js";
|
|
8
8
|
import { materializeStageModels } from "./materializeStageModels.js";
|
|
@@ -92,14 +92,44 @@ export async function resolvePipelinePath(pipelinePath, cwd) {
|
|
|
92
92
|
throw new Error(`Pipeline file not found: ${resolved}. Pass a filesystem path to --pipeline (e.g. pipelines/hello.pipeline.yaml), not a bare pipeline id.`);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
+
/** Synthetic `pipelinePath` for a `LoadedPipeline` built from an inline object with no backing file. */
|
|
96
|
+
export const INLINE_PIPELINE_PATH = "<inline>";
|
|
95
97
|
async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd, requireIo = true) {
|
|
96
98
|
const normalizedPipelinePath = path.normalize(path.resolve(pipelinePath));
|
|
97
99
|
const mergeOutcome = await mergePipelineStages(normalizedPipelinePath);
|
|
98
100
|
if (!mergeOutcome.ok) {
|
|
99
101
|
return loadFailure(mergeOutcome.issues);
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
return buildLoadedPipelineFromMerge(mergeOutcome.value, {
|
|
104
|
+
projectRoot,
|
|
105
|
+
requireIo,
|
|
106
|
+
pipelinePath: normalizedPipelinePath,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export async function loadPipelineFromObjectOutcome(raw, options = {}) {
|
|
110
|
+
const projectRoot = options.projectRoot ?? options.cwd ?? process.cwd();
|
|
111
|
+
const requireIo = options.requireIo !== false;
|
|
112
|
+
const mergeOutcome = await mergeInlinePipelineStages(raw);
|
|
113
|
+
if (!mergeOutcome.ok) {
|
|
114
|
+
return loadFailure(mergeOutcome.issues);
|
|
115
|
+
}
|
|
116
|
+
return buildLoadedPipelineFromMerge(mergeOutcome.value, {
|
|
117
|
+
projectRoot,
|
|
118
|
+
requireIo,
|
|
119
|
+
pipelinePath: INLINE_PIPELINE_PATH,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
export async function loadPipelineFromObject(raw, options = {}) {
|
|
123
|
+
const outcome = await loadPipelineFromObjectOutcome(raw, options);
|
|
124
|
+
if (!outcome.ok) {
|
|
125
|
+
throw new Error(outcome.issues[0].message);
|
|
126
|
+
}
|
|
127
|
+
return outcome.value;
|
|
128
|
+
}
|
|
129
|
+
async function buildLoadedPipelineFromMerge(merge, options) {
|
|
130
|
+
const { projectRoot, requireIo, pipelinePath } = options;
|
|
131
|
+
const { entries: rawEntries, pipelineId, agent: pipelineAgent, model: pipelineModel, schemas: pipelineSchemas, warnings: mergeWarnings, } = merge;
|
|
132
|
+
const ctx = { pipelineId, path: pipelinePath };
|
|
103
133
|
const warnings = [...mergeWarnings];
|
|
104
134
|
const normalizeOutcome = normalizePipelineStageEntries(rawEntries, ctx);
|
|
105
135
|
if (!normalizeOutcome.ok) {
|
|
@@ -274,7 +304,7 @@ async function loadPipelineFromPath(pipelinePath, cwd, projectRoot = cwd, requir
|
|
|
274
304
|
pipeline,
|
|
275
305
|
stages: loadedStages,
|
|
276
306
|
dag,
|
|
277
|
-
pipelinePath
|
|
307
|
+
pipelinePath,
|
|
278
308
|
stageSources,
|
|
279
309
|
}, warnings);
|
|
280
310
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PayloadSchemaMap } from "../envelope/payloadSchema.js";
|
|
2
|
+
import type { InlinePipelineDefinition } from "../types/pipeline.js";
|
|
2
3
|
import { type LoadIssue, type LoadOutcome } from "./loadOutcome.js";
|
|
3
4
|
export type RawMergedEntry = {
|
|
4
5
|
raw: unknown;
|
|
@@ -12,3 +13,13 @@ export declare function mergePipelineStages(rootPath: string): Promise<LoadOutco
|
|
|
12
13
|
schemas?: PayloadSchemaMap;
|
|
13
14
|
warnings: LoadIssue[];
|
|
14
15
|
}>>;
|
|
16
|
+
/** Synthetic `declaringPath` for stage entries with no backing file. */
|
|
17
|
+
export declare const INLINE_DECLARING_PATH = "<inline pipeline>";
|
|
18
|
+
export declare function mergeInlinePipelineStages(raw: InlinePipelineDefinition): Promise<LoadOutcome<{
|
|
19
|
+
entries: RawMergedEntry[];
|
|
20
|
+
pipelineId: string;
|
|
21
|
+
agent?: string;
|
|
22
|
+
model?: string;
|
|
23
|
+
schemas?: PayloadSchemaMap;
|
|
24
|
+
warnings: LoadIssue[];
|
|
25
|
+
}>>;
|
|
@@ -201,6 +201,81 @@ export async function mergePipelineStages(rootPath) {
|
|
|
201
201
|
warnings,
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
|
+
/** Synthetic `declaringPath` for stage entries with no backing file. */
|
|
205
|
+
export const INLINE_DECLARING_PATH = "<inline pipeline>";
|
|
206
|
+
export async function mergeInlinePipelineStages(raw) {
|
|
207
|
+
if (typeof raw.id !== "string" || !raw.id) {
|
|
208
|
+
return loadFailure([
|
|
209
|
+
{
|
|
210
|
+
code: "pipeline.invalid_shape",
|
|
211
|
+
message: `Invalid inline pipeline: id is required`,
|
|
212
|
+
category: "pipeline",
|
|
213
|
+
},
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
216
|
+
if (!Array.isArray(raw.stages) || raw.stages.length === 0) {
|
|
217
|
+
return loadFailure([
|
|
218
|
+
{
|
|
219
|
+
code: "pipeline.invalid_shape",
|
|
220
|
+
message: `Invalid inline pipeline ${raw.id}: stages must be non-empty`,
|
|
221
|
+
category: "pipeline",
|
|
222
|
+
pipelineId: raw.id,
|
|
223
|
+
},
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
const pipelineId = raw.id;
|
|
227
|
+
const agentField = parseAgentField(raw.agent);
|
|
228
|
+
if (!agentField.ok) {
|
|
229
|
+
return loadFailure([
|
|
230
|
+
{
|
|
231
|
+
code: "pipeline.invalid_agent",
|
|
232
|
+
message: `Invalid inline pipeline ${pipelineId}: ${agentField.message}`,
|
|
233
|
+
category: "pipeline",
|
|
234
|
+
pipelineId,
|
|
235
|
+
},
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
const agent = agentField.value;
|
|
239
|
+
const modelField = parseModelField(raw.model);
|
|
240
|
+
if (!modelField.ok) {
|
|
241
|
+
return loadFailure([
|
|
242
|
+
{
|
|
243
|
+
code: "pipeline.invalid_model",
|
|
244
|
+
message: `Invalid inline pipeline ${pipelineId}: ${modelField.message}`,
|
|
245
|
+
category: "pipeline",
|
|
246
|
+
pipelineId,
|
|
247
|
+
},
|
|
248
|
+
]);
|
|
249
|
+
}
|
|
250
|
+
const model = modelField.value;
|
|
251
|
+
const schemasOutcome = parsePipelineSchemas(raw.schemas, "<inline pipeline>", pipelineId);
|
|
252
|
+
if (!schemasOutcome.ok)
|
|
253
|
+
return schemasOutcome;
|
|
254
|
+
const schemas = schemasOutcome.value;
|
|
255
|
+
const entries = [];
|
|
256
|
+
for (let index = 0; index < raw.stages.length; index++) {
|
|
257
|
+
const entry = raw.stages[index];
|
|
258
|
+
if (isPlainObject(entry) && typeof entry.uses === "string") {
|
|
259
|
+
return loadFailure([
|
|
260
|
+
{
|
|
261
|
+
code: "pipeline.invalid_shape",
|
|
262
|
+
message: `Invalid inline pipeline ${pipelineId}: stage entry at index ${index} has "uses" — inline pipelines cannot reference external stage files, all stage bodies must be inline`,
|
|
263
|
+
category: "pipeline",
|
|
264
|
+
pipelineId,
|
|
265
|
+
},
|
|
266
|
+
]);
|
|
267
|
+
}
|
|
268
|
+
entries.push({ raw: entry, declaringPath: INLINE_DECLARING_PATH });
|
|
269
|
+
}
|
|
270
|
+
return loadSuccess({
|
|
271
|
+
entries,
|
|
272
|
+
pipelineId,
|
|
273
|
+
...(agent !== undefined ? { agent } : {}),
|
|
274
|
+
...(model !== undefined ? { model } : {}),
|
|
275
|
+
...(schemas !== undefined ? { schemas } : {}),
|
|
276
|
+
warnings: [],
|
|
277
|
+
});
|
|
278
|
+
}
|
|
204
279
|
function parsePipelineSchemas(raw, absPath, pipelineId) {
|
|
205
280
|
if (raw === undefined)
|
|
206
281
|
return loadSuccess(undefined);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LoadedPipeline } from "../types/pipeline.js";
|
|
1
|
+
import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
|
|
2
2
|
import type { LoadIssue } from "./loadOutcome.js";
|
|
3
3
|
export type ValidationSeverity = "error" | "warning";
|
|
4
4
|
export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task";
|
|
@@ -50,7 +50,7 @@ export type LoadPipelineValidatedResult = {
|
|
|
50
50
|
ok: false;
|
|
51
51
|
findings: ValidationFinding[];
|
|
52
52
|
};
|
|
53
|
-
export declare function loadPipelineValidated(nameOrPath: string, options?: {
|
|
53
|
+
export declare function loadPipelineValidated(nameOrPath: string | InlinePipelineDefinition, options?: {
|
|
54
54
|
cwd?: string;
|
|
55
55
|
projectRoot?: string;
|
|
56
56
|
validateStages?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { loadPipelineOutcome, resolvePipelinePath } from "./loadPipeline.js";
|
|
2
|
+
import { INLINE_PIPELINE_PATH, loadPipelineFromObjectOutcome, loadPipelineOutcome, resolvePipelinePath, } from "./loadPipeline.js";
|
|
3
3
|
import { loadStageOutcome } from "./loadStage.js";
|
|
4
4
|
import { loadTaskOutcome } from "./loadTask.js";
|
|
5
5
|
import { readYamlObject } from "./readYamlObject.js";
|
|
@@ -241,23 +241,30 @@ async function runPipelineValidation(nameOrPath, options) {
|
|
|
241
241
|
const { cwd, validateStages } = options;
|
|
242
242
|
const projectRoot = options.projectRoot ?? cwd;
|
|
243
243
|
let pipelinePath;
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
let outcome;
|
|
245
|
+
if (typeof nameOrPath === "string") {
|
|
246
|
+
try {
|
|
247
|
+
pipelinePath = await resolvePipelinePath(nameOrPath, cwd);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
findings: findingsFromLoadIssues(cwd, path.resolve(cwd, nameOrPath), [
|
|
254
|
+
{
|
|
255
|
+
code: "pipeline.load_error",
|
|
256
|
+
message,
|
|
257
|
+
category: "pipeline",
|
|
258
|
+
},
|
|
259
|
+
]),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
outcome = await loadPipelineOutcome(pipelinePath, { cwd, projectRoot });
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
pipelinePath = path.resolve(cwd, INLINE_PIPELINE_PATH);
|
|
266
|
+
outcome = await loadPipelineFromObjectOutcome(nameOrPath, { cwd, projectRoot });
|
|
246
267
|
}
|
|
247
|
-
catch (err) {
|
|
248
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
249
|
-
return {
|
|
250
|
-
ok: false,
|
|
251
|
-
findings: findingsFromLoadIssues(cwd, path.resolve(cwd, nameOrPath), [
|
|
252
|
-
{
|
|
253
|
-
code: "pipeline.load_error",
|
|
254
|
-
message,
|
|
255
|
-
category: "pipeline",
|
|
256
|
-
},
|
|
257
|
-
]),
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
const outcome = await loadPipelineOutcome(pipelinePath, { cwd, projectRoot });
|
|
261
268
|
const findings = [];
|
|
262
269
|
if (!outcome.ok) {
|
|
263
270
|
findings.push(...findingsFromLoadIssues(cwd, pipelinePath, outcome.issues));
|