stageflow 0.2.0 → 0.8.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 +179 -14
- package/dist/agent/activity.d.ts +12 -1
- package/dist/agent/activity.js +37 -2
- package/dist/agent/activityObserver.d.ts +5 -1
- package/dist/agent/activityObserver.js +174 -11
- package/dist/agent/cursorProvider.d.ts +1 -10
- package/dist/agent/cursorProvider.js +18 -10
- package/dist/agent/fakeAgent.js +15 -7
- package/dist/agent/piAdapter.d.ts +18 -1
- package/dist/agent/piAdapter.js +115 -45
- package/dist/agent/port.d.ts +7 -0
- package/dist/agent/port.js +3 -0
- package/dist/agent/providerAuth.js +12 -7
- package/dist/catalog/displayCatalogPath.d.ts +12 -0
- package/dist/catalog/displayCatalogPath.js +28 -0
- package/dist/cli/artifactCommand.d.ts +10 -0
- package/dist/cli/artifactCommand.js +120 -0
- package/dist/cli/envelopeCommand.d.ts +32 -0
- package/dist/cli/envelopeCommand.js +285 -0
- package/dist/cli/exportRunCommand.d.ts +10 -0
- package/dist/cli/exportRunCommand.js +150 -0
- package/dist/cli/handoffFormat.d.ts +22 -0
- package/dist/cli/handoffFormat.js +61 -0
- package/dist/cli/initCommand.d.ts +9 -0
- package/dist/cli/initCommand.js +71 -0
- package/dist/cli/initTemplates.d.ts +3 -0
- package/dist/cli/initTemplates.js +19 -0
- package/dist/cli/operatorCatalog.d.ts +10 -0
- package/dist/cli/operatorCatalog.js +16 -0
- package/dist/cli/runCommand.d.ts +7 -1
- package/dist/cli/runCommand.js +83 -11
- package/dist/cli/runOutput.d.ts +4 -1
- package/dist/cli/runOutput.js +36 -8
- package/dist/cli/skillsCommand.d.ts +16 -0
- package/dist/cli/skillsCommand.js +459 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +20 -3
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +128 -22
- package/dist/config/browseCatalog.d.ts +45 -0
- package/dist/config/browseCatalog.js +170 -0
- package/dist/config/createPipeline.d.ts +12 -3
- package/dist/config/createPipeline.js +292 -100
- package/dist/config/createStage.d.ts +10 -4
- package/dist/config/createStage.js +44 -14
- package/dist/config/listConfig.d.ts +14 -19
- package/dist/config/listConfig.js +26 -191
- package/dist/config/listModelsFromManifest.d.ts +2 -0
- package/dist/config/listModelsFromManifest.js +4 -0
- package/dist/config/loadOutcome.d.ts +4 -0
- package/dist/config/loadPipeline.d.ts +1 -15
- package/dist/config/loadPipeline.js +88 -142
- package/dist/config/loadStage.d.ts +4 -0
- package/dist/config/loadStage.js +60 -32
- package/dist/config/loadStageflowManifest.d.ts +5 -0
- package/dist/config/loadStageflowManifest.js +157 -0
- package/dist/config/mergePipelineIncludes.d.ts +9 -0
- package/dist/config/mergePipelineIncludes.js +141 -0
- package/dist/config/normalizePipelineStageEntry.d.ts +18 -0
- package/dist/config/normalizePipelineStageEntry.js +210 -0
- package/dist/config/pipelineStageKeys.d.ts +4 -0
- package/dist/config/pipelineStageKeys.js +14 -0
- package/dist/config/resolveCatalogContext.d.ts +13 -0
- package/dist/config/resolveCatalogContext.js +13 -0
- package/dist/config/resolveForkEmitContext.d.ts +4 -0
- package/dist/config/resolveForkEmitContext.js +34 -0
- package/dist/config/resolvePipelineDag.d.ts +5 -1
- package/dist/config/resolvePipelineDag.js +86 -36
- package/dist/config/scanCatalogPaths.d.ts +4 -0
- package/dist/config/scanCatalogPaths.js +83 -0
- package/dist/config/validateCatalog.d.ts +22 -4
- package/dist/config/validateCatalog.js +169 -132
- package/dist/envelope/check.d.ts +2 -0
- package/dist/envelope/check.js +58 -0
- package/dist/envelope/cloneForks.d.ts +2 -0
- package/dist/envelope/cloneForks.js +62 -0
- package/dist/envelope/forkChoice.d.ts +3 -0
- package/dist/envelope/forkChoice.js +47 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/mcp/catalogTools.d.ts +3 -0
- package/dist/mcp/catalogTools.js +187 -0
- package/dist/mcp/controlTools.d.ts +3 -0
- package/dist/mcp/controlTools.js +147 -0
- package/dist/mcp/deps.d.ts +11 -0
- package/dist/mcp/deps.js +1 -0
- package/dist/mcp/projectRun.d.ts +1 -37
- package/dist/mcp/projectRun.js +1 -57
- package/dist/mcp/resources.d.ts +5 -0
- package/dist/mcp/resources.js +45 -0
- package/dist/mcp/server.d.ts +16 -3
- package/dist/mcp/server.js +159 -3
- package/dist/mcp/toolResults.d.ts +7 -0
- package/dist/mcp/toolResults.js +6 -0
- package/dist/mcp/tools.d.ts +2 -7
- package/dist/mcp/tools.js +52 -86
- package/dist/mcp/waitRun.d.ts +46 -0
- package/dist/mcp/waitRun.js +146 -0
- package/dist/mcp/waitingGates.d.ts +12 -0
- package/dist/mcp/waitingGates.js +83 -0
- package/dist/project/findProjectRoot.d.ts +2 -0
- package/dist/project/findProjectRoot.js +51 -0
- package/dist/project/globalHome.d.ts +2 -0
- package/dist/project/globalHome.js +19 -0
- package/dist/project/resolveProjectContext.d.ts +9 -0
- package/dist/project/resolveProjectContext.js +38 -0
- package/dist/project/resolveStageflowContext.d.ts +11 -0
- package/dist/project/resolveStageflowContext.js +66 -0
- package/dist/projection/projectRun.d.ts +44 -0
- package/dist/projection/projectRun.js +71 -0
- package/dist/prompt/priorEnvelope.d.ts +1 -1
- package/dist/prompt/priorEnvelope.js +4 -1
- package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
- package/dist/runstore/normalizeCatalogPath.js +4 -0
- package/dist/runstore/paths.js +2 -0
- package/dist/runstore/pipelineDagSnapshot.d.ts +9 -0
- package/dist/runstore/pipelineDagSnapshot.js +90 -1
- package/dist/runstore/port.d.ts +22 -2
- package/dist/runstore/port.js +4 -1
- package/dist/runstore/runProjection.d.ts +1 -1
- package/dist/runstore/runProjection.js +25 -4
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +3 -2
- package/dist/runstore/sqlite/SqliteRunStore.js +65 -7
- package/dist/runstore/sqlite/schema.d.ts +1 -1
- package/dist/runstore/sqlite/schema.js +4 -1
- package/dist/runstore/stageInstanceId.d.ts +4 -0
- package/dist/runstore/stageInstanceId.js +23 -0
- package/dist/runstore/syntheticStageSnapshot.d.ts +1 -1
- package/dist/runstore/syntheticStageSnapshot.js +2 -1
- package/dist/runstore/trackProjection.js +28 -12
- package/dist/runstore/workspaceLayout.d.ts +1 -0
- package/dist/runstore/workspaceLayout.js +1 -1
- package/dist/runtime/cloneSchedule.d.ts +18 -0
- package/dist/runtime/cloneSchedule.js +233 -0
- package/dist/runtime/credentialBinding.d.ts +8 -6
- package/dist/runtime/credentialBinding.js +18 -24
- package/dist/runtime/envelopeRouting.d.ts +2 -1
- package/dist/runtime/envelopeRouting.js +80 -13
- package/dist/runtime/pipelineRunner.d.ts +3 -0
- package/dist/runtime/pipelineRunner.js +15 -1
- package/dist/runtime/pipelineScheduler.d.ts +8 -2
- package/dist/runtime/pipelineScheduler.js +215 -39
- package/dist/runtime/reloadRunCatalog.d.ts +5 -0
- package/dist/runtime/reloadRunCatalog.js +44 -0
- package/dist/runtime/resumeReconstruct.d.ts +1 -0
- package/dist/runtime/resumeReconstruct.js +31 -10
- package/dist/runtime/runChangeBus.d.ts +18 -0
- package/dist/runtime/runChangeBus.js +83 -0
- package/dist/runtime/runManager.d.ts +4 -0
- package/dist/runtime/runManager.js +36 -16
- package/dist/runtime/settingsFile.d.ts +12 -0
- package/dist/runtime/settingsFile.js +87 -6
- package/dist/runtime/stageAttemptBootstrap.d.ts +1 -0
- package/dist/runtime/stageAttemptBootstrap.js +18 -7
- package/dist/runtime/stageProcessLauncher.js +3 -0
- package/dist/runtime/stageRecovery.js +3 -1
- package/dist/runtime/stageRunner.d.ts +1 -0
- package/dist/runtime/stageRunner.js +17 -15
- package/dist/runtime/stageWorker.js +13 -10
- package/dist/server/bootstrap.d.ts +32 -0
- package/dist/server/bootstrap.js +56 -0
- package/dist/server/createHttpHost.d.ts +34 -0
- package/dist/server/createHttpHost.js +70 -0
- package/dist/server/http.d.ts +6 -13
- package/dist/server/http.js +297 -352
- package/dist/server/mcpHost.d.ts +22 -0
- package/dist/server/mcpHost.js +21 -0
- package/dist/server/operatorResults.d.ts +13 -0
- package/dist/server/operatorResults.js +22 -0
- package/dist/tools/emitStageEnvelope.d.ts +12 -1
- package/dist/tools/emitStageEnvelope.js +27 -1
- package/dist/types/envelope.d.ts +3 -0
- package/dist/types/forkChoice.d.ts +30 -0
- package/dist/types/forkChoice.js +1 -0
- package/dist/types/pipeline.d.ts +59 -2
- package/dist/types/stageflowManifest.d.ts +23 -0
- package/dist/types/stageflowManifest.js +1 -0
- package/dist/ui/assets/{index-DCsDopak.css → index-C3N7MXAC.css} +1 -1
- package/dist/ui/assets/index-CSoGDA3A.js +118 -0
- package/dist/ui/index.html +3 -2
- package/dist/ui/stageflow-icon.svg +12 -0
- package/package.json +8 -5
- package/skills/install-suite.sh +107 -0
- package/skills/stageflow/SKILL.md +26 -0
- package/skills/stageflow/references/control-surface.md +26 -0
- package/skills/stageflow/scripts/detect-host.mjs +54 -0
- package/skills/stageflow/scripts/detect-host.test.mjs +97 -0
- package/skills/stageflow-author/SKILL.md +58 -0
- package/skills/stageflow-author/assets/examples/branch-decision/hotfix.yaml +10 -0
- package/skills/stageflow-author/assets/examples/branch-decision/release-gate.pipeline.yaml +12 -0
- package/skills/stageflow-author/assets/examples/branch-decision/run-tests.yaml +13 -0
- package/skills/stageflow-author/assets/examples/branch-decision/ship.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/draft.yaml +9 -0
- package/skills/stageflow-author/assets/examples/linear-review/publish.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review-loop.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review.yaml +15 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/gather.yaml +9 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/research-digest.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/send.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/summarize.yaml +9 -0
- package/skills/stageflow-author/references/catalog-mapping.md +131 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +29 -0
- package/skills/stageflow-author/references/stage-prompt-template.md +55 -0
- package/skills/stageflow-author/references/validate-and-report.md +38 -0
- package/skills/stageflow-delegate/SKILL.md +18 -0
- package/skills/stageflow-delegate/references/authoring-or-run.md +15 -0
- package/skills/stageflow-delegate/references/example-walkthrough.md +30 -0
- package/skills/stageflow-delegate/references/pattern-detection.md +44 -0
- package/skills/stageflow-run/SKILL.md +151 -0
- package/skills/stageflow-run/references/mcp-call.md +17 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +52 -0
- package/skills/stageflow-run/scripts/mcp-call.mjs +299 -0
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +271 -0
- package/skills/stageflow-session-capture/SKILL.md +94 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/example.pipeline.yaml +7 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/implement.yaml +8 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/research.yaml +8 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +40 -0
- package/skills/stageflow-session-capture/references/transcript-sources.md +25 -0
- package/skills/stageflow-session-capture/scripts/check-provider-gate.mjs +43 -0
- package/skills/stageflow-session-capture/scripts/locate-session-transcript.mjs +141 -0
- package/skills/stageflow-session-capture/scripts/resolve-catalog-id.mjs +119 -0
- package/skills/stageflow-setup/SKILL.md +110 -0
- package/dist/ui/assets/index-Cry0Tpfx.js +0 -118
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StageEnvelope } from "../../types/envelope.js";
|
|
2
2
|
import type { StageLogLine } from "../../agent/activity.js";
|
|
3
|
-
import { type CreateRunInput, type CreatedRun, type RunDetail, type RunMeta, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent } from "../port.js";
|
|
3
|
+
import { type CreateRunInput, type CreatedRun, type ListRunsFilter, type RunDetail, type RunMeta, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent } from "../port.js";
|
|
4
4
|
export declare class SqliteRunStore implements RunStore {
|
|
5
5
|
private readonly storeRoot;
|
|
6
6
|
private readonly db;
|
|
@@ -11,6 +11,7 @@ export declare class SqliteRunStore implements RunStore {
|
|
|
11
11
|
getWorkspaceDir(runId: string): string;
|
|
12
12
|
createRun(input: CreateRunInput): Promise<CreatedRun>;
|
|
13
13
|
updateRunStatus(runId: string, status: RunStatus): Promise<void>;
|
|
14
|
+
updatePipelineDag(runId: string, dag: RunPipelineDagSnapshot): Promise<void>;
|
|
14
15
|
readRunMeta(runId: string): Promise<RunMeta>;
|
|
15
16
|
readTaskYaml(runId: string): Promise<string>;
|
|
16
17
|
ensureStageWorkspace(runId: string, stageId: string): Promise<void>;
|
|
@@ -37,7 +38,7 @@ export declare class SqliteRunStore implements RunStore {
|
|
|
37
38
|
*/
|
|
38
39
|
private updateStageStatusDenorm;
|
|
39
40
|
listStageEvents(runId: string, stageId: string, attempt?: number): Promise<StageLogEvent[]>;
|
|
40
|
-
listRuns(): Promise<RunSummary[]>;
|
|
41
|
+
listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
|
|
41
42
|
readRun(runId: string): Promise<RunDetail>;
|
|
42
43
|
private readPipelineDagSnapshotFromRow;
|
|
43
44
|
private runMetaFromRow;
|
|
@@ -2,6 +2,7 @@ import Database from "better-sqlite3";
|
|
|
2
2
|
import { mkdir } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { projectRunDetail, projectRunSummary, orderStageSnapshots } from "../runProjection.js";
|
|
5
|
+
import { normalizeCatalogPath } from "../normalizeCatalogPath.js";
|
|
5
6
|
import { buildStageSnapshotFromStore } from "../stageSnapshot.js";
|
|
6
7
|
import { parsePipelineDagSnapshot } from "../pipelineDagSnapshot.js";
|
|
7
8
|
import { newRunId, runWorkspaceDir } from "../paths.js";
|
|
@@ -42,6 +43,15 @@ function ensurePipelineDagColumn(db) {
|
|
|
42
43
|
db.exec(`ALTER TABLE runs ADD COLUMN pipeline_dag_json TEXT`);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
46
|
+
function ensureRunLocatorColumns(db) {
|
|
47
|
+
const cols = db.prepare(`PRAGMA table_info(runs)`).all();
|
|
48
|
+
const names = new Set(cols.map((c) => c.name));
|
|
49
|
+
for (const name of ["pipeline_path", "task_path", "project_root"]) {
|
|
50
|
+
if (!names.has(name)) {
|
|
51
|
+
db.exec(`ALTER TABLE runs ADD COLUMN ${name} TEXT`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
45
55
|
function ensureStageExecutionsTable(db) {
|
|
46
56
|
db.exec(`
|
|
47
57
|
CREATE TABLE IF NOT EXISTS stage_executions (
|
|
@@ -98,6 +108,7 @@ export class SqliteRunStore {
|
|
|
98
108
|
ensureCheckoutRootColumn(this.db);
|
|
99
109
|
ensureCiIdentityColumns(this.db);
|
|
100
110
|
ensurePipelineDagColumn(this.db);
|
|
111
|
+
ensureRunLocatorColumns(this.db);
|
|
101
112
|
ensureStageExecutionsTable(this.db);
|
|
102
113
|
ensureStageEventsAttemptColumn(this.db);
|
|
103
114
|
this.migratePromise = importDiskRunsIfEmpty(this.db, storeRoot).then(() => undefined);
|
|
@@ -115,11 +126,20 @@ export class SqliteRunStore {
|
|
|
115
126
|
const workspaceDir = this.getWorkspaceDir(runId);
|
|
116
127
|
await mkdir(path.join(workspaceDir, "stages"), { recursive: true });
|
|
117
128
|
const now = new Date().toISOString();
|
|
129
|
+
const pipelinePath = input.pipelinePath
|
|
130
|
+
? normalizeCatalogPath(input.pipelinePath)
|
|
131
|
+
: null;
|
|
132
|
+
const taskPath = input.taskPath
|
|
133
|
+
? normalizeCatalogPath(input.taskPath)
|
|
134
|
+
: null;
|
|
135
|
+
const projectRoot = input.projectRoot
|
|
136
|
+
? normalizeCatalogPath(input.projectRoot)
|
|
137
|
+
: null;
|
|
118
138
|
this.db
|
|
119
139
|
.prepare(`INSERT INTO runs
|
|
120
|
-
(run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json, git_sha, ci_pr_url, ci_job_url)
|
|
140
|
+
(run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json, git_sha, ci_pr_url, ci_job_url, pipeline_path, task_path, project_root)
|
|
121
141
|
VALUES
|
|
122
|
-
(@run_id, @pipeline_id, @task_id, @task_yaml, @status, @created_at, @updated_at, @checkout_root, @pipeline_dag_json, @git_sha, @ci_pr_url, @ci_job_url)`)
|
|
142
|
+
(@run_id, @pipeline_id, @task_id, @task_yaml, @status, @created_at, @updated_at, @checkout_root, @pipeline_dag_json, @git_sha, @ci_pr_url, @ci_job_url, @pipeline_path, @task_path, @project_root)`)
|
|
123
143
|
.run({
|
|
124
144
|
run_id: runId,
|
|
125
145
|
pipeline_id: input.pipelineId,
|
|
@@ -135,6 +155,9 @@ export class SqliteRunStore {
|
|
|
135
155
|
git_sha: input.gitSha ?? null,
|
|
136
156
|
ci_pr_url: input.ciPrUrl ?? null,
|
|
137
157
|
ci_job_url: input.ciJobUrl ?? null,
|
|
158
|
+
pipeline_path: pipelinePath,
|
|
159
|
+
task_path: taskPath,
|
|
160
|
+
project_root: projectRoot,
|
|
138
161
|
});
|
|
139
162
|
return { runId, workspaceDir };
|
|
140
163
|
}
|
|
@@ -151,6 +174,19 @@ export class SqliteRunStore {
|
|
|
151
174
|
throw new Error(`Run not found: ${runId}`);
|
|
152
175
|
}
|
|
153
176
|
}
|
|
177
|
+
async updatePipelineDag(runId, dag) {
|
|
178
|
+
await this.ready();
|
|
179
|
+
const result = this.db
|
|
180
|
+
.prepare(`UPDATE runs SET pipeline_dag_json = @pipeline_dag_json, updated_at = @updated_at WHERE run_id = @run_id`)
|
|
181
|
+
.run({
|
|
182
|
+
run_id: runId,
|
|
183
|
+
pipeline_dag_json: JSON.stringify(dag),
|
|
184
|
+
updated_at: new Date().toISOString(),
|
|
185
|
+
});
|
|
186
|
+
if (result.changes === 0) {
|
|
187
|
+
throw new Error(`Run not found: ${runId}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
154
190
|
async readRunMeta(runId) {
|
|
155
191
|
await this.ready();
|
|
156
192
|
return this.runMetaFromRow(this.getRunRow(runId));
|
|
@@ -388,12 +424,29 @@ export class SqliteRunStore {
|
|
|
388
424
|
await this.ready();
|
|
389
425
|
return this.loadEvents(runId, stageId, attempt);
|
|
390
426
|
}
|
|
391
|
-
async listRuns() {
|
|
427
|
+
async listRuns(filter) {
|
|
392
428
|
await this.ready();
|
|
429
|
+
const clauses = [];
|
|
430
|
+
const params = [];
|
|
431
|
+
if (filter?.status !== undefined) {
|
|
432
|
+
clauses.push("status = ?");
|
|
433
|
+
params.push(filter.status);
|
|
434
|
+
}
|
|
435
|
+
if (filter?.since !== undefined) {
|
|
436
|
+
clauses.push("created_at >= ?");
|
|
437
|
+
params.push(filter.since);
|
|
438
|
+
}
|
|
439
|
+
if (filter?.pipeline !== undefined && filter.pipeline.trim().length > 0) {
|
|
440
|
+
const pipeline = filter.pipeline.trim();
|
|
441
|
+
const normalized = normalizeCatalogPath(pipeline);
|
|
442
|
+
clauses.push("(pipeline_id = ? OR pipeline_path = ? OR pipeline_path = ?)");
|
|
443
|
+
params.push(pipeline, pipeline, normalized);
|
|
444
|
+
}
|
|
445
|
+
const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
393
446
|
const rows = this.db
|
|
394
|
-
.prepare(`SELECT run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json, git_sha, ci_pr_url, ci_job_url
|
|
395
|
-
FROM runs ORDER BY created_at DESC`)
|
|
396
|
-
.all();
|
|
447
|
+
.prepare(`SELECT run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json, git_sha, ci_pr_url, ci_job_url, pipeline_path, task_path, project_root
|
|
448
|
+
FROM runs ${where} ORDER BY created_at DESC`)
|
|
449
|
+
.all(...params);
|
|
397
450
|
const summaries = [];
|
|
398
451
|
for (const row of rows) {
|
|
399
452
|
const dagSnapshot = this.readPipelineDagSnapshotFromRow(row);
|
|
@@ -415,6 +468,7 @@ export class SqliteRunStore {
|
|
|
415
468
|
return parsePipelineDagSnapshot(JSON.parse(row.pipeline_dag_json));
|
|
416
469
|
}
|
|
417
470
|
runMetaFromRow(row) {
|
|
471
|
+
const pipeline_dag = this.readPipelineDagSnapshotFromRow(row);
|
|
418
472
|
return {
|
|
419
473
|
run_id: row.run_id,
|
|
420
474
|
pipeline_id: row.pipeline_id,
|
|
@@ -426,11 +480,15 @@ export class SqliteRunStore {
|
|
|
426
480
|
...(row.git_sha != null ? { git_sha: row.git_sha } : {}),
|
|
427
481
|
...(row.ci_pr_url != null ? { ci_pr_url: row.ci_pr_url } : {}),
|
|
428
482
|
...(row.ci_job_url != null ? { ci_job_url: row.ci_job_url } : {}),
|
|
483
|
+
...(row.pipeline_path != null ? { pipeline_path: row.pipeline_path } : {}),
|
|
484
|
+
...(row.task_path != null ? { task_path: row.task_path } : {}),
|
|
485
|
+
...(row.project_root != null ? { project_root: row.project_root } : {}),
|
|
486
|
+
...(pipeline_dag ? { pipeline_dag } : {}),
|
|
429
487
|
};
|
|
430
488
|
}
|
|
431
489
|
getRunRow(runId) {
|
|
432
490
|
const row = this.db
|
|
433
|
-
.prepare(`SELECT run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json, git_sha, ci_pr_url, ci_job_url
|
|
491
|
+
.prepare(`SELECT run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json, git_sha, ci_pr_url, ci_job_url, pipeline_path, task_path, project_root
|
|
434
492
|
FROM runs WHERE run_id = ?`)
|
|
435
493
|
.get(runId);
|
|
436
494
|
if (!row)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS runs (\n run_id TEXT PRIMARY KEY,\n pipeline_id TEXT NOT NULL,\n task_id TEXT,\n task_yaml TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n checkout_root TEXT,\n pipeline_dag_json TEXT,\n git_sha TEXT,\n ci_pr_url TEXT,\n ci_job_url TEXT\n);\n\nCREATE TABLE IF NOT EXISTS stages (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n status TEXT,\n summary TEXT,\n envelope_json TEXT,\n started_at TEXT,\n finished_at TEXT,\n PRIMARY KEY (run_id, stage_id),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE TABLE IF NOT EXISTS stage_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n at TEXT NOT NULL,\n event TEXT NOT NULL,\n payload_json TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_status_created\n ON runs (status, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_runs_pipeline_created\n ON runs (pipeline_id, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_stage_events_run_stage_at\n ON stage_events (run_id, stage_id, at);\n\nCREATE TABLE IF NOT EXISTS stage_executions (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n attempt INTEGER NOT NULL,\n status TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT,\n envelope_json TEXT,\n PRIMARY KEY (run_id, stage_id, attempt),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_stage_executions_run_stage\n ON stage_executions (run_id, stage_id, attempt);\n";
|
|
1
|
+
export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS runs (\n run_id TEXT PRIMARY KEY,\n pipeline_id TEXT NOT NULL,\n task_id TEXT,\n task_yaml TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n checkout_root TEXT,\n pipeline_dag_json TEXT,\n git_sha TEXT,\n ci_pr_url TEXT,\n ci_job_url TEXT,\n pipeline_path TEXT,\n task_path TEXT,\n project_root TEXT\n);\n\nCREATE TABLE IF NOT EXISTS stages (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n status TEXT,\n summary TEXT,\n envelope_json TEXT,\n started_at TEXT,\n finished_at TEXT,\n PRIMARY KEY (run_id, stage_id),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE TABLE IF NOT EXISTS stage_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n at TEXT NOT NULL,\n event TEXT NOT NULL,\n payload_json TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_status_created\n ON runs (status, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_runs_pipeline_created\n ON runs (pipeline_id, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_stage_events_run_stage_at\n ON stage_events (run_id, stage_id, at);\n\nCREATE TABLE IF NOT EXISTS stage_executions (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n attempt INTEGER NOT NULL,\n status TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT,\n envelope_json TEXT,\n PRIMARY KEY (run_id, stage_id, attempt),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_stage_executions_run_stage\n ON stage_executions (run_id, stage_id, attempt);\n";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RunPipelineDagSnapshot } from "./port.js";
|
|
2
|
+
export declare function mintCloneInstanceId(catalogId: string, n: number): string;
|
|
3
|
+
export declare function mintCloneInstanceIds(catalogId: string, count: number): string[];
|
|
4
|
+
export declare function definitionIdForInstance(snapshot: RunPipelineDagSnapshot | null | undefined, instanceId: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { guardStageId } from "./workspaceLayout.js";
|
|
2
|
+
function guardPositiveInteger(n, label) {
|
|
3
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
4
|
+
throw new Error(`${label} must be a positive integer`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function mintCloneInstanceId(catalogId, n) {
|
|
8
|
+
guardPositiveInteger(n, "n");
|
|
9
|
+
guardStageId(catalogId);
|
|
10
|
+
const instanceId = `${catalogId}~${n}`;
|
|
11
|
+
guardStageId(instanceId);
|
|
12
|
+
return instanceId;
|
|
13
|
+
}
|
|
14
|
+
export function mintCloneInstanceIds(catalogId, count) {
|
|
15
|
+
guardPositiveInteger(count, "count");
|
|
16
|
+
return Array.from({ length: count }, (_, i) => mintCloneInstanceId(catalogId, i + 1));
|
|
17
|
+
}
|
|
18
|
+
export function definitionIdForInstance(snapshot, instanceId) {
|
|
19
|
+
const node = snapshot?.nodes.find((n) => n.id === instanceId);
|
|
20
|
+
if (!node)
|
|
21
|
+
return instanceId;
|
|
22
|
+
return node.definition_id ?? node.id;
|
|
23
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { StageSnapshot } from "./port.js";
|
|
2
|
-
export declare function syntheticPendingSnapshot(stageId: string): StageSnapshot;
|
|
2
|
+
export declare function syntheticPendingSnapshot(stageId: string, definitionId?: string): StageSnapshot;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function syntheticPendingSnapshot(stageId) {
|
|
1
|
+
export function syntheticPendingSnapshot(stageId, definitionId) {
|
|
2
2
|
return {
|
|
3
3
|
stage_id: stageId,
|
|
4
4
|
status: "pending",
|
|
@@ -6,5 +6,6 @@ export function syntheticPendingSnapshot(stageId) {
|
|
|
6
6
|
envelope: null,
|
|
7
7
|
artifacts: [],
|
|
8
8
|
attempt_count: 1,
|
|
9
|
+
...(definitionId !== undefined ? { definition_id: definitionId } : {}),
|
|
9
10
|
};
|
|
10
11
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { linearCompatDagSnapshot } from "./pipelineDagSnapshot.js";
|
|
1
|
+
import { instancesOfDefinition, linearCompatDagSnapshot, } from "./pipelineDagSnapshot.js";
|
|
2
2
|
function stageStarted(stage) {
|
|
3
3
|
return stage.events.some((e) => e.event === "started" || e.event === "resumed");
|
|
4
4
|
}
|
|
5
5
|
function runHalted(stages) {
|
|
6
6
|
return stages.some((s) => s.status === "failed");
|
|
7
7
|
}
|
|
8
|
+
function predecessorIds(dag, needs) {
|
|
9
|
+
const preds = instancesOfDefinition(dag, needs);
|
|
10
|
+
return preds.length > 0 ? preds : [needs];
|
|
11
|
+
}
|
|
8
12
|
function computeLayers(dag) {
|
|
9
13
|
const layers = new Map();
|
|
10
14
|
const nodeById = new Map(dag.nodes.map((n) => [n.id, n]));
|
|
@@ -17,7 +21,8 @@ function computeLayers(dag) {
|
|
|
17
21
|
layers.set(id, 0);
|
|
18
22
|
return 0;
|
|
19
23
|
}
|
|
20
|
-
const
|
|
24
|
+
const fromIds = predecessorIds(dag, node.needs);
|
|
25
|
+
const l = Math.max(...fromIds.map(layerFor)) + 1;
|
|
21
26
|
layers.set(id, l);
|
|
22
27
|
return l;
|
|
23
28
|
}
|
|
@@ -32,6 +37,8 @@ function deriveReadiness(stage, dag, snapshotsById, halted) {
|
|
|
32
37
|
return { readiness: "succeeded" };
|
|
33
38
|
if (status === "failed")
|
|
34
39
|
return { readiness: "failed" };
|
|
40
|
+
if (status === "skipped")
|
|
41
|
+
return { readiness: "skipped" };
|
|
35
42
|
if (status === "running")
|
|
36
43
|
return { readiness: "running" };
|
|
37
44
|
if (status === "waiting_for_input")
|
|
@@ -42,9 +49,13 @@ function deriveReadiness(stage, dag, snapshotsById, halted) {
|
|
|
42
49
|
const node = dag.nodes.find((n) => n.id === stage.stage_id);
|
|
43
50
|
const need = node?.needs;
|
|
44
51
|
if (need) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
const fromIds = predecessorIds(dag, need);
|
|
53
|
+
const unresolved = fromIds.filter((id) => {
|
|
54
|
+
const predecessor = snapshotsById.get(id);
|
|
55
|
+
return !predecessor || predecessor.status !== "succeeded";
|
|
56
|
+
});
|
|
57
|
+
if (unresolved.length > 0) {
|
|
58
|
+
return { readiness: "blocked", blocked_by: unresolved };
|
|
48
59
|
}
|
|
49
60
|
}
|
|
50
61
|
return { readiness: "ready" };
|
|
@@ -63,6 +74,7 @@ export function buildPipelineTrack(input) {
|
|
|
63
74
|
}
|
|
64
75
|
const node = dag.nodes.find((n) => n.id === stageId);
|
|
65
76
|
const { readiness, blocked_by } = deriveReadiness(stage, dag, snapshotsById, halted);
|
|
77
|
+
const definitionId = node?.definition_id ?? stage.definition_id ?? stageId;
|
|
66
78
|
const trackNode = {
|
|
67
79
|
stage_id: stageId,
|
|
68
80
|
status: stage.status,
|
|
@@ -70,10 +82,11 @@ export function buildPipelineTrack(input) {
|
|
|
70
82
|
layer: layers.get(stageId) ?? 0,
|
|
71
83
|
layer_order: node?.stageIndex ?? dag.stage_ids.indexOf(stageId),
|
|
72
84
|
attempt_count: stage.attempt_count,
|
|
85
|
+
definition_id: definitionId,
|
|
73
86
|
};
|
|
74
87
|
if (blocked_by?.length)
|
|
75
88
|
trackNode.blocked_by = blocked_by;
|
|
76
|
-
const gateKinds = dag.gate_kinds?.[stageId];
|
|
89
|
+
const gateKinds = dag.gate_kinds?.[definitionId] ?? dag.gate_kinds?.[stageId];
|
|
77
90
|
if (gateKinds?.length)
|
|
78
91
|
trackNode.gate_kinds = gateKinds;
|
|
79
92
|
return trackNode;
|
|
@@ -82,13 +95,16 @@ export function buildPipelineTrack(input) {
|
|
|
82
95
|
for (const node of dag.nodes) {
|
|
83
96
|
if (!node.needs)
|
|
84
97
|
continue;
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
const fromIds = predecessorIds(dag, node.needs);
|
|
99
|
+
for (const fromId of fromIds) {
|
|
100
|
+
const fromStage = snapshotsById.get(fromId);
|
|
101
|
+
const edge = { from: fromId, to: node.id };
|
|
102
|
+
if (fromStage?.status === "succeeded" &&
|
|
103
|
+
fromStage.envelope?.summary) {
|
|
104
|
+
edge.envelope_summary = fromStage.envelope.summary;
|
|
105
|
+
}
|
|
106
|
+
edges.push(edge);
|
|
90
107
|
}
|
|
91
|
-
edges.push(edge);
|
|
92
108
|
}
|
|
93
109
|
return { nodes, edges };
|
|
94
110
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { stageDir } from "./paths.js";
|
|
2
|
+
export declare function guardStageId(stageId: string): void;
|
|
2
3
|
export declare function attemptWorkspaceDir(workspaceDir: string, stageId: string, attempt: number): string;
|
|
3
4
|
export declare function attemptLogPath(workspaceDir: string, stageId: string, attempt: number): string;
|
|
4
5
|
export declare function attemptEnvelopePath(workspaceDir: string, stageId: string, attempt: number): string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access, lstat, mkdir, readdir, realpath } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
export { stageDir } from "./paths.js";
|
|
4
|
-
function guardStageId(stageId) {
|
|
4
|
+
export function guardStageId(stageId) {
|
|
5
5
|
if (stageId.includes("..") ||
|
|
6
6
|
stageId.includes("/") ||
|
|
7
7
|
stageId.includes("\\")) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RunPipelineDagSnapshot } from "../runstore/port.js";
|
|
2
|
+
import type { StageEnvelope } from "../types/envelope.js";
|
|
3
|
+
import type { ResolvedPipelineDag } from "../types/pipeline.js";
|
|
4
|
+
import type { StageScheduleState } from "./pipelineScheduler.js";
|
|
5
|
+
export declare function isCloneInstance(dag: ResolvedPipelineDag, stageId: string): boolean;
|
|
6
|
+
export declare function sequentialLaterCloneIds(dag: ResolvedPipelineDag, stageId: string, completedEnvelopes: Map<string, StageEnvelope>): string[];
|
|
7
|
+
export declare function cloneFanoutConflict(dag: ResolvedPipelineDag, envelope: StageEnvelope): string | undefined;
|
|
8
|
+
export declare function protectedClonableChildIds(dag: ResolvedPipelineDag, predecessorId: string, envelope: StageEnvelope): Set<string>;
|
|
9
|
+
export declare function cloneRetryDownstream(dag: ResolvedPipelineDag, stageIds: string[]): Set<string>;
|
|
10
|
+
export declare function cloneFailureContinuesSchedule(dag: ResolvedPipelineDag, stageId: string, completedEnvelopes: Map<string, StageEnvelope>): boolean;
|
|
11
|
+
export declare function cloneFailFastSkipIds(dag: ResolvedPipelineDag, stageId: string, completedEnvelopes: Map<string, StageEnvelope>): string[];
|
|
12
|
+
export declare function cloneScheduleAllowsRun(dag: ResolvedPipelineDag, stageId: string, states: Map<string, StageScheduleState>, completedEnvelopes: Map<string, StageEnvelope>): boolean;
|
|
13
|
+
export type ApplyCloneForksResult = {
|
|
14
|
+
dag: RunPipelineDagSnapshot;
|
|
15
|
+
skippedIds: string[];
|
|
16
|
+
};
|
|
17
|
+
export declare function applyCloneForksToSchedule(dag: ResolvedPipelineDag, predecessorId: string, envelope: StageEnvelope, states: Map<string, StageScheduleState>): ApplyCloneForksResult;
|
|
18
|
+
export declare function applyCloneForksFromEnvelopes(dag: ResolvedPipelineDag, states: Map<string, StageScheduleState>, completedEnvelopes: Map<string, StageEnvelope>): ApplyCloneForksResult;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { appendCloneInstances, instancesOfDefinition, } from "../runstore/pipelineDagSnapshot.js";
|
|
2
|
+
import { definitionIdForInstance } from "../runstore/stageInstanceId.js";
|
|
3
|
+
import { collectDownstreamClosure, collectDownstreamStageIds } from "./dagTraversal.js";
|
|
4
|
+
function asDagSnapshot(dag) {
|
|
5
|
+
const snapshot = dag;
|
|
6
|
+
if (Array.isArray(snapshot.stage_ids))
|
|
7
|
+
return snapshot;
|
|
8
|
+
return {
|
|
9
|
+
...dag,
|
|
10
|
+
stage_ids: dag.nodes.map((n) => n.id),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function definitionInstances(dag, catalogId) {
|
|
14
|
+
const snapshot = dag;
|
|
15
|
+
if (Array.isArray(snapshot.stage_ids)) {
|
|
16
|
+
return instancesOfDefinition(snapshot, catalogId);
|
|
17
|
+
}
|
|
18
|
+
return dag.nodes.some((n) => n.id === catalogId) ? [catalogId] : [];
|
|
19
|
+
}
|
|
20
|
+
export function isCloneInstance(dag, stageId) {
|
|
21
|
+
return definitionIdForInstance(asDagSnapshot(dag), stageId) !== stageId;
|
|
22
|
+
}
|
|
23
|
+
function isParallelCloneInstance(dag, stageId, completedEnvelopes) {
|
|
24
|
+
if (!isCloneInstance(dag, stageId))
|
|
25
|
+
return false;
|
|
26
|
+
const defId = definitionIdForInstance(asDagSnapshot(dag), stageId);
|
|
27
|
+
for (const envelope of completedEnvelopes.values()) {
|
|
28
|
+
for (const item of envelope.clone_forks ?? []) {
|
|
29
|
+
if (item.action === "fanout" &&
|
|
30
|
+
item.mode === "parallel" &&
|
|
31
|
+
item.successor_id === defId &&
|
|
32
|
+
definitionInstances(dag, defId).includes(stageId)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function isSequentialFanoutSuccessor(completedEnvelopes, successorId) {
|
|
40
|
+
for (const envelope of completedEnvelopes.values()) {
|
|
41
|
+
for (const item of envelope.clone_forks ?? []) {
|
|
42
|
+
if (item.action === "fanout" &&
|
|
43
|
+
item.mode === "sequential" &&
|
|
44
|
+
item.successor_id === successorId) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
function isSequentialCloneInstance(dag, stageId, completedEnvelopes) {
|
|
52
|
+
if (!isCloneInstance(dag, stageId))
|
|
53
|
+
return false;
|
|
54
|
+
const defId = definitionIdForInstance(asDagSnapshot(dag), stageId);
|
|
55
|
+
if (!isSequentialFanoutSuccessor(completedEnvelopes, defId))
|
|
56
|
+
return false;
|
|
57
|
+
return definitionInstances(dag, defId).includes(stageId);
|
|
58
|
+
}
|
|
59
|
+
function sequentialInstanceList(dag, stageId, completedEnvelopes) {
|
|
60
|
+
for (const envelope of completedEnvelopes.values()) {
|
|
61
|
+
for (const item of envelope.clone_forks ?? []) {
|
|
62
|
+
if (item.action !== "fanout" || item.mode !== "sequential")
|
|
63
|
+
continue;
|
|
64
|
+
const instances = definitionInstances(dag, item.successor_id);
|
|
65
|
+
if (instances.includes(stageId))
|
|
66
|
+
return instances;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function sequentialPreviousUnsatisfied(dag, stageId, states, completedEnvelopes) {
|
|
72
|
+
const instances = sequentialInstanceList(dag, stageId, completedEnvelopes);
|
|
73
|
+
if (instances === undefined)
|
|
74
|
+
return false;
|
|
75
|
+
const i = instances.indexOf(stageId);
|
|
76
|
+
if (i <= 0)
|
|
77
|
+
return false;
|
|
78
|
+
return states.get(instances[i - 1]) !== "succeeded";
|
|
79
|
+
}
|
|
80
|
+
export function sequentialLaterCloneIds(dag, stageId, completedEnvelopes) {
|
|
81
|
+
const instances = sequentialInstanceList(dag, stageId, completedEnvelopes);
|
|
82
|
+
if (instances === undefined)
|
|
83
|
+
return [];
|
|
84
|
+
const i = instances.indexOf(stageId);
|
|
85
|
+
if (i === -1)
|
|
86
|
+
return [];
|
|
87
|
+
return instances.slice(i + 1);
|
|
88
|
+
}
|
|
89
|
+
function joinSuccessorIds(dag, successorId) {
|
|
90
|
+
return dag.nodes.filter((n) => n.needs === successorId).map((n) => n.id);
|
|
91
|
+
}
|
|
92
|
+
function joinAndDownstreamIds(dag, cloneStageId) {
|
|
93
|
+
const defId = definitionIdForInstance(asDagSnapshot(dag), cloneStageId);
|
|
94
|
+
const ids = [];
|
|
95
|
+
for (const joinId of joinSuccessorIds(dag, defId)) {
|
|
96
|
+
ids.push(joinId);
|
|
97
|
+
for (const desc of collectDownstreamStageIds(dag, joinId)) {
|
|
98
|
+
ids.push(desc);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return ids;
|
|
102
|
+
}
|
|
103
|
+
export function cloneFanoutConflict(dag, envelope) {
|
|
104
|
+
const snapshot = asDagSnapshot(dag);
|
|
105
|
+
for (const item of envelope.clone_forks ?? []) {
|
|
106
|
+
if (item.action !== "fanout")
|
|
107
|
+
continue;
|
|
108
|
+
if (definitionInstances(dag, item.successor_id).some((id) => id !== item.successor_id)) {
|
|
109
|
+
return `clone fan-out for "${item.successor_id}" is already instanced`;
|
|
110
|
+
}
|
|
111
|
+
if (!snapshot.stage_ids.includes(item.successor_id)) {
|
|
112
|
+
return `clone fan-out for "${item.successor_id}" is already instanced`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
export function protectedClonableChildIds(dag, predecessorId, envelope) {
|
|
118
|
+
const ids = new Set();
|
|
119
|
+
for (const childId of dag.childrenOf[predecessorId] ?? []) {
|
|
120
|
+
const child = dag.nodes.find((n) => n.id === childId);
|
|
121
|
+
if (child?.clonable === true)
|
|
122
|
+
ids.add(childId);
|
|
123
|
+
}
|
|
124
|
+
for (const item of envelope.clone_forks ?? []) {
|
|
125
|
+
ids.add(item.successor_id);
|
|
126
|
+
}
|
|
127
|
+
return ids;
|
|
128
|
+
}
|
|
129
|
+
export function cloneRetryDownstream(dag, stageIds) {
|
|
130
|
+
const downstream = collectDownstreamClosure(dag, stageIds);
|
|
131
|
+
for (const id of stageIds) {
|
|
132
|
+
const defId = definitionIdForInstance(asDagSnapshot(dag), id);
|
|
133
|
+
for (const child of dag.childrenOf[defId] ?? []) {
|
|
134
|
+
downstream.add(child);
|
|
135
|
+
for (const desc of collectDownstreamStageIds(dag, child)) {
|
|
136
|
+
downstream.add(desc);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return downstream;
|
|
141
|
+
}
|
|
142
|
+
export function cloneFailureContinuesSchedule(dag, stageId, completedEnvelopes) {
|
|
143
|
+
return (isParallelCloneInstance(dag, stageId, completedEnvelopes) ||
|
|
144
|
+
isSequentialCloneInstance(dag, stageId, completedEnvelopes));
|
|
145
|
+
}
|
|
146
|
+
export function cloneFailFastSkipIds(dag, stageId, completedEnvelopes) {
|
|
147
|
+
if (isParallelCloneInstance(dag, stageId, completedEnvelopes)) {
|
|
148
|
+
return joinAndDownstreamIds(dag, stageId);
|
|
149
|
+
}
|
|
150
|
+
if (!isSequentialCloneInstance(dag, stageId, completedEnvelopes))
|
|
151
|
+
return [];
|
|
152
|
+
const instances = sequentialInstanceList(dag, stageId, completedEnvelopes);
|
|
153
|
+
if (instances === undefined)
|
|
154
|
+
return [];
|
|
155
|
+
const idx = instances.indexOf(stageId);
|
|
156
|
+
const ids = [];
|
|
157
|
+
for (const id of instances.slice(idx + 1)) {
|
|
158
|
+
ids.push(id);
|
|
159
|
+
}
|
|
160
|
+
ids.push(...joinAndDownstreamIds(dag, stageId));
|
|
161
|
+
return ids;
|
|
162
|
+
}
|
|
163
|
+
export function cloneScheduleAllowsRun(dag, stageId, states, completedEnvelopes) {
|
|
164
|
+
const node = dag.nodes.find((n) => n.id === stageId);
|
|
165
|
+
if (!node)
|
|
166
|
+
return false;
|
|
167
|
+
if (definitionInstances(dag, stageId).some((id) => id !== stageId)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
if (node.needs === null) {
|
|
171
|
+
return !sequentialPreviousUnsatisfied(dag, stageId, states, completedEnvelopes);
|
|
172
|
+
}
|
|
173
|
+
const instances = definitionInstances(dag, node.needs);
|
|
174
|
+
if (instances.length <= 1) {
|
|
175
|
+
const parentId = instances[0] ?? node.needs;
|
|
176
|
+
if (states.get(parentId) !== "succeeded")
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
else if (!instances.every((id) => states.get(id) === "succeeded")) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return !sequentialPreviousUnsatisfied(dag, stageId, states, completedEnvelopes);
|
|
183
|
+
}
|
|
184
|
+
export function applyCloneForksToSchedule(dag, predecessorId, envelope, states) {
|
|
185
|
+
let current = asDagSnapshot(dag);
|
|
186
|
+
const skippedIds = [];
|
|
187
|
+
const skipPending = (stageId) => {
|
|
188
|
+
if (states.get(stageId) === "pending") {
|
|
189
|
+
states.set(stageId, "skipped");
|
|
190
|
+
skippedIds.push(stageId);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
for (const item of envelope.clone_forks ?? []) {
|
|
194
|
+
if (item.action === "skip") {
|
|
195
|
+
skipPending(item.successor_id);
|
|
196
|
+
for (const desc of collectDownstreamStageIds(current, item.successor_id)) {
|
|
197
|
+
skipPending(desc);
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (item.action !== "fanout")
|
|
202
|
+
continue;
|
|
203
|
+
const existing = definitionInstances(current, item.successor_id);
|
|
204
|
+
if (existing.some((id) => id !== item.successor_id)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const { snapshot, instanceIds } = appendCloneInstances(current, {
|
|
208
|
+
catalogId: item.successor_id,
|
|
209
|
+
predecessorId,
|
|
210
|
+
count: item.clones.length,
|
|
211
|
+
});
|
|
212
|
+
current = snapshot;
|
|
213
|
+
states.delete(item.successor_id);
|
|
214
|
+
for (const id of instanceIds) {
|
|
215
|
+
states.set(id, "pending");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { dag: current, skippedIds };
|
|
219
|
+
}
|
|
220
|
+
export function applyCloneForksFromEnvelopes(dag, states, completedEnvelopes) {
|
|
221
|
+
let current = asDagSnapshot(dag);
|
|
222
|
+
const skippedIds = [];
|
|
223
|
+
const predecessors = [...current.nodes];
|
|
224
|
+
for (const node of predecessors) {
|
|
225
|
+
const envelope = completedEnvelopes.get(node.id);
|
|
226
|
+
if (!envelope?.clone_forks?.length)
|
|
227
|
+
continue;
|
|
228
|
+
const applied = applyCloneForksToSchedule(current, node.id, envelope, states);
|
|
229
|
+
current = applied.dag;
|
|
230
|
+
skippedIds.push(...applied.skippedIds);
|
|
231
|
+
}
|
|
232
|
+
return { dag: current, skippedIds };
|
|
233
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { type ProjectContext } from "../project/resolveProjectContext.js";
|
|
2
|
+
import { parseCredentialSource, readCredentialSourceFromContext, writeCredentialSourceToContext, type CredentialSource } from "./settingsFile.js";
|
|
3
|
+
export { parseCredentialSource, readCredentialSourceFromContext, writeCredentialSourceToContext, };
|
|
3
4
|
export type CredentialBinding = {
|
|
4
5
|
source: CredentialSource;
|
|
5
6
|
authPath: string;
|
|
@@ -8,9 +9,10 @@ export type CredentialBinding = {
|
|
|
8
9
|
export type ResolveCredentialBindingOptions = {
|
|
9
10
|
piHomeAuthPath?: string;
|
|
10
11
|
};
|
|
11
|
-
export declare function sfOwnedAgentDir(
|
|
12
|
-
export declare function sfOwnedAuthPath(
|
|
12
|
+
export declare function sfOwnedAgentDir(): string;
|
|
13
|
+
export declare function sfOwnedAuthPath(): string;
|
|
13
14
|
export declare function piHomeAuthPath(override?: string): string;
|
|
14
15
|
export declare function isUsableAuthFile(authPath: string): boolean;
|
|
15
|
-
export declare function ensureSfOwnedAuthStore(
|
|
16
|
-
export declare function resolveCredentialBinding(
|
|
16
|
+
export declare function ensureSfOwnedAuthStore(): string;
|
|
17
|
+
export declare function resolveCredentialBinding(ctx: ProjectContext | string, options?: ResolveCredentialBindingOptions): CredentialBinding;
|
|
18
|
+
export { readCredentialSourceFromFile, writeCredentialSourceToFile, } from "./settingsFile.js";
|