stageflow 0.1.0 → 0.2.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 +33 -0
- package/dist/cli/ciIdentity.d.ts +14 -0
- package/dist/cli/ciIdentity.js +52 -0
- package/dist/cli/runCommand.d.ts +24 -0
- package/dist/cli/runCommand.js +168 -0
- package/dist/cli/runOutput.d.ts +19 -0
- package/dist/cli/runOutput.js +125 -0
- package/dist/cli.d.ts +11 -1
- package/dist/cli.js +41 -58
- package/dist/mcp/server.js +1 -1
- package/dist/runstore/port.d.ts +6 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +20 -4
- package/dist/runstore/sqlite/schema.d.ts +1 -1
- package/dist/runstore/sqlite/schema.js +4 -1
- package/dist/runtime/pipelineRunner.d.ts +8 -0
- package/dist/runtime/pipelineRunner.js +10 -0
- package/dist/runtime/pipelineScheduler.d.ts +1 -0
- package/dist/runtime/pipelineScheduler.js +19 -3
- package/dist/runtime/resumeReconstruct.js +4 -3
- package/dist/runtime/runManager.d.ts +4 -0
- package/dist/runtime/runManager.js +14 -3
- package/dist/runtime/stageProcessLauncher.d.ts +1 -0
- package/dist/runtime/stageProcessLauncher.js +3 -0
- package/dist/runtime/stageRunner.d.ts +2 -0
- package/dist/runtime/stageRunner.js +9 -2
- package/dist/runtime/stageWorker.js +2 -0
- package/dist/runtime/stageWorkerProtocol.d.ts +1 -0
- package/package.json +1 -1
- package/dist/agent/cursorExtension.d.ts +0 -12
- package/dist/agent/cursorExtension.js +0 -88
- package/dist/runstore/catalog.d.ts +0 -8
- package/dist/runstore/catalog.js +0 -16
- package/dist/runstore/disk/DiskRunStore.d.ts +0 -30
- package/dist/runstore/disk/DiskRunStore.js +0 -238
- package/dist/runstore/layout.d.ts +0 -22
- package/dist/runstore/layout.js +0 -58
- package/dist/runtime/hitlSeams.d.ts +0 -38
- package/dist/runtime/hitlSeams.js +0 -2
|
@@ -27,6 +27,15 @@ function ensureCheckoutRootColumn(db) {
|
|
|
27
27
|
db.exec(`ALTER TABLE runs ADD COLUMN checkout_root TEXT`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
function ensureCiIdentityColumns(db) {
|
|
31
|
+
const cols = db.prepare(`PRAGMA table_info(runs)`).all();
|
|
32
|
+
const names = new Set(cols.map((c) => c.name));
|
|
33
|
+
for (const name of ["git_sha", "ci_pr_url", "ci_job_url"]) {
|
|
34
|
+
if (!names.has(name)) {
|
|
35
|
+
db.exec(`ALTER TABLE runs ADD COLUMN ${name} TEXT`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
30
39
|
function ensurePipelineDagColumn(db) {
|
|
31
40
|
const cols = db.prepare(`PRAGMA table_info(runs)`).all();
|
|
32
41
|
if (!cols.some((c) => c.name === "pipeline_dag_json")) {
|
|
@@ -87,6 +96,7 @@ export class SqliteRunStore {
|
|
|
87
96
|
this.db.pragma(`busy_timeout = ${readSqliteBusyTimeoutMs()}`);
|
|
88
97
|
this.db.exec(SCHEMA_SQL);
|
|
89
98
|
ensureCheckoutRootColumn(this.db);
|
|
99
|
+
ensureCiIdentityColumns(this.db);
|
|
90
100
|
ensurePipelineDagColumn(this.db);
|
|
91
101
|
ensureStageExecutionsTable(this.db);
|
|
92
102
|
ensureStageEventsAttemptColumn(this.db);
|
|
@@ -107,9 +117,9 @@ export class SqliteRunStore {
|
|
|
107
117
|
const now = new Date().toISOString();
|
|
108
118
|
this.db
|
|
109
119
|
.prepare(`INSERT INTO runs
|
|
110
|
-
(run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json)
|
|
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)
|
|
111
121
|
VALUES
|
|
112
|
-
(@run_id, @pipeline_id, @task_id, @task_yaml, @status, @created_at, @updated_at, @checkout_root, @pipeline_dag_json)`)
|
|
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)`)
|
|
113
123
|
.run({
|
|
114
124
|
run_id: runId,
|
|
115
125
|
pipeline_id: input.pipelineId,
|
|
@@ -122,6 +132,9 @@ export class SqliteRunStore {
|
|
|
122
132
|
pipeline_dag_json: input.pipelineDag
|
|
123
133
|
? JSON.stringify(input.pipelineDag)
|
|
124
134
|
: null,
|
|
135
|
+
git_sha: input.gitSha ?? null,
|
|
136
|
+
ci_pr_url: input.ciPrUrl ?? null,
|
|
137
|
+
ci_job_url: input.ciJobUrl ?? null,
|
|
125
138
|
});
|
|
126
139
|
return { runId, workspaceDir };
|
|
127
140
|
}
|
|
@@ -378,7 +391,7 @@ export class SqliteRunStore {
|
|
|
378
391
|
async listRuns() {
|
|
379
392
|
await this.ready();
|
|
380
393
|
const rows = this.db
|
|
381
|
-
.prepare(`SELECT run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json
|
|
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
|
|
382
395
|
FROM runs ORDER BY created_at DESC`)
|
|
383
396
|
.all();
|
|
384
397
|
const summaries = [];
|
|
@@ -410,11 +423,14 @@ export class SqliteRunStore {
|
|
|
410
423
|
task_id: row.task_id ?? undefined,
|
|
411
424
|
updated_at: row.updated_at,
|
|
412
425
|
...(row.checkout_root != null ? { checkout_root: row.checkout_root } : {}),
|
|
426
|
+
...(row.git_sha != null ? { git_sha: row.git_sha } : {}),
|
|
427
|
+
...(row.ci_pr_url != null ? { ci_pr_url: row.ci_pr_url } : {}),
|
|
428
|
+
...(row.ci_job_url != null ? { ci_job_url: row.ci_job_url } : {}),
|
|
413
429
|
};
|
|
414
430
|
}
|
|
415
431
|
getRunRow(runId) {
|
|
416
432
|
const row = this.db
|
|
417
|
-
.prepare(`SELECT run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json
|
|
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
|
|
418
434
|
FROM runs WHERE run_id = ?`)
|
|
419
435
|
.get(runId);
|
|
420
436
|
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);\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);\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";
|
|
@@ -8,7 +8,10 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
8
8
|
created_at TEXT NOT NULL,
|
|
9
9
|
updated_at TEXT NOT NULL,
|
|
10
10
|
checkout_root TEXT,
|
|
11
|
-
pipeline_dag_json TEXT
|
|
11
|
+
pipeline_dag_json TEXT,
|
|
12
|
+
git_sha TEXT,
|
|
13
|
+
ci_pr_url TEXT,
|
|
14
|
+
ci_job_url TEXT
|
|
12
15
|
);
|
|
13
16
|
|
|
14
17
|
CREATE TABLE IF NOT EXISTS stages (
|
|
@@ -8,8 +8,10 @@ import { type StageExecutionMode } from "./stageConcurrency.js";
|
|
|
8
8
|
import { StageProcessLauncher } from "./stageProcessLauncher.js";
|
|
9
9
|
import type { OperatorCatalog } from "./stageAttemptBootstrap.js";
|
|
10
10
|
export { PipelineValidationError } from "./pipelineValidationError.js";
|
|
11
|
+
export type PipelineRunOutcome = "succeeded" | "failed" | "waiting";
|
|
11
12
|
export type PipelineRunResult = {
|
|
12
13
|
ok: boolean;
|
|
14
|
+
outcome: PipelineRunOutcome;
|
|
13
15
|
runDir: string;
|
|
14
16
|
runId: string;
|
|
15
17
|
reason?: string;
|
|
@@ -34,6 +36,7 @@ export type PreparedPipeline = {
|
|
|
34
36
|
executionMode?: StageExecutionMode;
|
|
35
37
|
stageProcessLauncher?: StageProcessLauncher;
|
|
36
38
|
operatorCatalog?: OperatorCatalog;
|
|
39
|
+
skipGates?: boolean;
|
|
37
40
|
};
|
|
38
41
|
export type ExecuteStagesOptions = {
|
|
39
42
|
maxActiveStagesPerRun?: number;
|
|
@@ -58,6 +61,7 @@ export declare function runPipeline(options: {
|
|
|
58
61
|
executionMode?: StageExecutionMode;
|
|
59
62
|
stageProcessLauncher?: StageProcessLauncher;
|
|
60
63
|
operatorCatalog?: OperatorCatalog;
|
|
64
|
+
skipGates?: boolean;
|
|
61
65
|
}): Promise<PipelineRunResult>;
|
|
62
66
|
/** Create the run immediately, then execute stages in the returned promise. */
|
|
63
67
|
export declare function startPipeline(options: {
|
|
@@ -68,10 +72,14 @@ export declare function startPipeline(options: {
|
|
|
68
72
|
pipeline: string;
|
|
69
73
|
cwd?: string;
|
|
70
74
|
checkoutOverride?: string;
|
|
75
|
+
gitSha?: string;
|
|
76
|
+
ciPrUrl?: string;
|
|
77
|
+
ciJobUrl?: string;
|
|
71
78
|
hitl?: StageHitlController;
|
|
72
79
|
maxActiveStagesPerRun?: number;
|
|
73
80
|
executionMode?: StageExecutionMode;
|
|
74
81
|
stageProcessLauncher?: StageProcessLauncher;
|
|
75
82
|
operatorCatalog?: OperatorCatalog;
|
|
83
|
+
skipGates?: boolean;
|
|
76
84
|
}): Promise<StartedPipeline>;
|
|
77
85
|
export declare function resolveTaskPath(taskArg: string, cwd?: string): string;
|
|
@@ -46,6 +46,9 @@ async function preparePipeline(options) {
|
|
|
46
46
|
taskYaml,
|
|
47
47
|
taskId: task.id,
|
|
48
48
|
checkoutRoot,
|
|
49
|
+
gitSha: options.gitSha,
|
|
50
|
+
ciPrUrl: options.ciPrUrl,
|
|
51
|
+
ciJobUrl: options.ciJobUrl,
|
|
49
52
|
pipelineDag: buildPipelineDagSnapshotFromLoaded(loaded),
|
|
50
53
|
});
|
|
51
54
|
const executionMode = readStageExecutionMode(process.env, options.executionMode);
|
|
@@ -62,6 +65,7 @@ async function preparePipeline(options) {
|
|
|
62
65
|
executionMode,
|
|
63
66
|
stageProcessLauncher,
|
|
64
67
|
operatorCatalog: options.operatorCatalog,
|
|
68
|
+
skipGates: options.skipGates,
|
|
65
69
|
};
|
|
66
70
|
}
|
|
67
71
|
export async function executeStages(prepared, options) {
|
|
@@ -94,6 +98,7 @@ export async function runPipeline(options) {
|
|
|
94
98
|
executionMode: options.executionMode,
|
|
95
99
|
stageProcessLauncher: options.stageProcessLauncher,
|
|
96
100
|
operatorCatalog: options.operatorCatalog,
|
|
101
|
+
skipGates: options.skipGates,
|
|
97
102
|
});
|
|
98
103
|
return executeStages(prepared, {
|
|
99
104
|
maxActiveStagesPerRun: options.maxActiveStagesPerRun,
|
|
@@ -112,10 +117,14 @@ export async function startPipeline(options) {
|
|
|
112
117
|
pipeline: options.pipeline,
|
|
113
118
|
cwd,
|
|
114
119
|
checkoutOverride: options.checkoutOverride,
|
|
120
|
+
gitSha: options.gitSha,
|
|
121
|
+
ciPrUrl: options.ciPrUrl,
|
|
122
|
+
ciJobUrl: options.ciJobUrl,
|
|
115
123
|
hitl: options.hitl,
|
|
116
124
|
executionMode: options.executionMode,
|
|
117
125
|
stageProcessLauncher: options.stageProcessLauncher,
|
|
118
126
|
operatorCatalog: options.operatorCatalog,
|
|
127
|
+
skipGates: options.skipGates,
|
|
119
128
|
});
|
|
120
129
|
const done = executeStages(prepared, {
|
|
121
130
|
maxActiveStagesPerRun: options.maxActiveStagesPerRun,
|
|
@@ -125,6 +134,7 @@ export async function startPipeline(options) {
|
|
|
125
134
|
await prepared.store.updateRunStatus(prepared.run.runId, "failed").catch(() => undefined);
|
|
126
135
|
return {
|
|
127
136
|
ok: false,
|
|
137
|
+
outcome: "failed",
|
|
128
138
|
runDir: prepared.run.workspaceDir,
|
|
129
139
|
runId: prepared.run.runId,
|
|
130
140
|
reason: err instanceof Error ? err.message : String(err),
|
|
@@ -93,7 +93,8 @@ export async function resumeRun(options) {
|
|
|
93
93
|
const hasActive = [...hydrated.states.values()].some((s) => s === "active");
|
|
94
94
|
if (hasActive) {
|
|
95
95
|
return {
|
|
96
|
-
ok:
|
|
96
|
+
ok: false,
|
|
97
|
+
outcome: "waiting",
|
|
97
98
|
runDir: prepared.run.workspaceDir,
|
|
98
99
|
runId: prepared.run.runId,
|
|
99
100
|
};
|
|
@@ -107,6 +108,7 @@ export async function resumeRun(options) {
|
|
|
107
108
|
await prepared.store.updateRunStatus(prepared.run.runId, allSucceeded ? "succeeded" : "failed");
|
|
108
109
|
return {
|
|
109
110
|
ok: allSucceeded,
|
|
111
|
+
outcome: allSucceeded ? "succeeded" : "failed",
|
|
110
112
|
runDir: prepared.run.workspaceDir,
|
|
111
113
|
runId: prepared.run.runId,
|
|
112
114
|
...(allSucceeded
|
|
@@ -344,6 +346,7 @@ export async function runPipelineDag(options) {
|
|
|
344
346
|
...(prepared.operatorCatalog !== undefined
|
|
345
347
|
? { operatorCatalog: prepared.operatorCatalog }
|
|
346
348
|
: {}),
|
|
349
|
+
...(prepared.skipGates ? { skipGates: true } : {}),
|
|
347
350
|
});
|
|
348
351
|
if (launchResult.type === "succeeded") {
|
|
349
352
|
try {
|
|
@@ -377,6 +380,7 @@ export async function runPipelineDag(options) {
|
|
|
377
380
|
factoryCwd: cwd,
|
|
378
381
|
operatorCatalog: prepared.operatorCatalog,
|
|
379
382
|
completedEnvelopes,
|
|
383
|
+
skipGates: prepared.skipGates,
|
|
380
384
|
});
|
|
381
385
|
if (isRunStageWaiting(result)) {
|
|
382
386
|
await onStageFailure(stageId, WAIT_WITHOUT_WORKER_DISPATCH);
|
|
@@ -461,7 +465,12 @@ export async function runPipelineDag(options) {
|
|
|
461
465
|
}
|
|
462
466
|
const hasWaiting = [...states.values()].some((s) => s === "waiting");
|
|
463
467
|
if (hasWaiting && !schedulingHalted) {
|
|
464
|
-
return {
|
|
468
|
+
return {
|
|
469
|
+
ok: false,
|
|
470
|
+
outcome: "waiting",
|
|
471
|
+
runDir: run.workspaceDir,
|
|
472
|
+
runId: run.runId,
|
|
473
|
+
};
|
|
465
474
|
}
|
|
466
475
|
if (schedulingHalted) {
|
|
467
476
|
markSkippedPending();
|
|
@@ -470,6 +479,7 @@ export async function runPipelineDag(options) {
|
|
|
470
479
|
}
|
|
471
480
|
return {
|
|
472
481
|
ok: false,
|
|
482
|
+
outcome: "failed",
|
|
473
483
|
runDir: run.workspaceDir,
|
|
474
484
|
runId: run.runId,
|
|
475
485
|
reason: firstFailureReason,
|
|
@@ -482,6 +492,7 @@ export async function runPipelineDag(options) {
|
|
|
482
492
|
}
|
|
483
493
|
return {
|
|
484
494
|
ok: false,
|
|
495
|
+
outcome: "failed",
|
|
485
496
|
runDir: run.workspaceDir,
|
|
486
497
|
runId: run.runId,
|
|
487
498
|
reason: firstFailureReason ?? "pipeline incomplete",
|
|
@@ -490,5 +501,10 @@ export async function runPipelineDag(options) {
|
|
|
490
501
|
if (retryContext === undefined) {
|
|
491
502
|
await store.updateRunStatus(run.runId, "succeeded");
|
|
492
503
|
}
|
|
493
|
-
return {
|
|
504
|
+
return {
|
|
505
|
+
ok: true,
|
|
506
|
+
outcome: "succeeded",
|
|
507
|
+
runDir: run.workspaceDir,
|
|
508
|
+
runId: run.runId,
|
|
509
|
+
};
|
|
494
510
|
}
|
|
@@ -118,9 +118,10 @@ export async function reconstructAndContinue(ctx) {
|
|
|
118
118
|
executionMode: ctx.executionMode,
|
|
119
119
|
stageProcessLauncher: ctx.stageProcessLauncher,
|
|
120
120
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
if (rest.outcome === "failed") {
|
|
122
|
+
return { ok: false, reason: rest.reason };
|
|
123
|
+
}
|
|
124
|
+
return { ok: true };
|
|
124
125
|
}
|
|
125
126
|
catch (err) {
|
|
126
127
|
const reason = err instanceof Error
|
|
@@ -100,6 +100,10 @@ export declare class RunManager {
|
|
|
100
100
|
pipeline: string;
|
|
101
101
|
task?: string | TaskFile;
|
|
102
102
|
checkoutOverride?: string;
|
|
103
|
+
skipGates?: boolean;
|
|
104
|
+
gitSha?: string;
|
|
105
|
+
ciPrUrl?: string;
|
|
106
|
+
ciJobUrl?: string;
|
|
103
107
|
}): Promise<StartRunResult>;
|
|
104
108
|
rerun(runId: string): Promise<StartRunResult>;
|
|
105
109
|
retryStage(runId: string, stageId: string): Promise<RetryStageResult>;
|
|
@@ -347,7 +347,11 @@ export class RunManager {
|
|
|
347
347
|
status: 400,
|
|
348
348
|
};
|
|
349
349
|
}
|
|
350
|
-
return this.reserveAndStartPipeline(taskYaml, input.pipeline, `task file ${label}`, cwd, input.checkoutOverride
|
|
350
|
+
return this.reserveAndStartPipeline(taskYaml, input.pipeline, `task file ${label}`, cwd, input.checkoutOverride, input.skipGates, {
|
|
351
|
+
gitSha: input.gitSha,
|
|
352
|
+
ciPrUrl: input.ciPrUrl,
|
|
353
|
+
ciJobUrl: input.ciJobUrl,
|
|
354
|
+
});
|
|
351
355
|
}
|
|
352
356
|
async rerun(runId) {
|
|
353
357
|
const cwd = this.options.cwd ?? process.cwd();
|
|
@@ -549,7 +553,10 @@ export class RunManager {
|
|
|
549
553
|
executionMode: this.executionMode,
|
|
550
554
|
stageProcessLauncher: launcher,
|
|
551
555
|
});
|
|
552
|
-
|
|
556
|
+
if (rest.outcome === "failed") {
|
|
557
|
+
return { ok: false, reason: rest.reason };
|
|
558
|
+
}
|
|
559
|
+
return { ok: true };
|
|
553
560
|
}
|
|
554
561
|
catch (err) {
|
|
555
562
|
const reason = err instanceof Error
|
|
@@ -591,7 +598,7 @@ export class RunManager {
|
|
|
591
598
|
this.attachedWaiting.delete(`${runId}\0${stageId}`);
|
|
592
599
|
}
|
|
593
600
|
}
|
|
594
|
-
async reserveAndStartPipeline(taskYaml, pipelineId, taskLabel, cwd, checkoutOverride) {
|
|
601
|
+
async reserveAndStartPipeline(taskYaml, pipelineId, taskLabel, cwd, checkoutOverride, skipGates, ciIdentity) {
|
|
595
602
|
let checkoutKey;
|
|
596
603
|
try {
|
|
597
604
|
const task = loadTaskFromYaml(taskYaml, taskLabel);
|
|
@@ -619,11 +626,15 @@ export class RunManager {
|
|
|
619
626
|
pipeline: pipelineId,
|
|
620
627
|
cwd,
|
|
621
628
|
checkoutOverride,
|
|
629
|
+
gitSha: ciIdentity?.gitSha,
|
|
630
|
+
ciPrUrl: ciIdentity?.ciPrUrl,
|
|
631
|
+
ciJobUrl: ciIdentity?.ciJobUrl,
|
|
622
632
|
hitl: this.hitl,
|
|
623
633
|
maxActiveStagesPerRun: this.maxActiveStagesPerRun,
|
|
624
634
|
executionMode: this.executionMode,
|
|
625
635
|
stageProcessLauncher: this.stageProcessLauncher,
|
|
626
636
|
operatorCatalog: this.options.operatorCatalog,
|
|
637
|
+
skipGates,
|
|
627
638
|
});
|
|
628
639
|
this.track(reserved.provisionalId, started.runId, started.done);
|
|
629
640
|
return { ok: true, runId: started.runId, done: started.done };
|
|
@@ -135,6 +135,9 @@ export class StageProcessLauncher {
|
|
|
135
135
|
if (input.operatorCatalog?.agentDir !== undefined) {
|
|
136
136
|
args.push("--operator-agent-dir", input.operatorCatalog.agentDir);
|
|
137
137
|
}
|
|
138
|
+
if (input.skipGates) {
|
|
139
|
+
args.push("--skip-gates");
|
|
140
|
+
}
|
|
138
141
|
const child = fork(this.cliEntry, args, {
|
|
139
142
|
cwd: input.rootDir,
|
|
140
143
|
env: { ...process.env, ...this.env, [SF_STAGE_WORKER]: "1" },
|
|
@@ -43,6 +43,7 @@ export type RunStageOptions = {
|
|
|
43
43
|
factoryCwd?: string;
|
|
44
44
|
operatorCatalog?: OperatorCatalog;
|
|
45
45
|
completedEnvelopes?: Map<string, StageEnvelope>;
|
|
46
|
+
skipGates?: boolean;
|
|
46
47
|
};
|
|
47
48
|
/**
|
|
48
49
|
* Yield loop: next() until completed; on wait, coordinator enterWait (KTD6).
|
|
@@ -55,5 +56,6 @@ export declare function runStageYieldLoop(options: {
|
|
|
55
56
|
workerMode?: boolean;
|
|
56
57
|
store?: RunStore;
|
|
57
58
|
attemptCtx?: StageAttemptContext;
|
|
59
|
+
skipGates?: boolean;
|
|
58
60
|
}): Promise<RunStageYieldLoopResult>;
|
|
59
61
|
export declare function runStage(options: RunStageOptions): Promise<RunStageOutcome>;
|
|
@@ -73,10 +73,16 @@ async function finalizeStageResult(options) {
|
|
|
73
73
|
* Yield loop: next() until completed; on wait, coordinator enterWait (KTD6).
|
|
74
74
|
*/
|
|
75
75
|
export async function runStageYieldLoop(options) {
|
|
76
|
-
const { handle, runId, stageId, hitl, workerMode, store, attemptCtx } = options;
|
|
76
|
+
const { handle, runId, stageId, hitl, workerMode, store, attemptCtx, skipGates } = options;
|
|
77
77
|
while (true) {
|
|
78
78
|
const event = await handle.next();
|
|
79
79
|
if (event.status === "waiting_for_input") {
|
|
80
|
+
if (skipGates) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
reason: "skip-gates: stage requested wait",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
80
86
|
if (workerMode) {
|
|
81
87
|
if (!store) {
|
|
82
88
|
return {
|
|
@@ -107,7 +113,7 @@ export async function runStageYieldLoop(options) {
|
|
|
107
113
|
}
|
|
108
114
|
}
|
|
109
115
|
export async function runStage(options) {
|
|
110
|
-
const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, } = options;
|
|
116
|
+
const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, skipGates, } = options;
|
|
111
117
|
const attemptOpt = attemptCtx?.eventOptions();
|
|
112
118
|
const baseRoots = rootsOverride ??
|
|
113
119
|
buildStageRoots(workspaceDir ?? store.getWorkspaceDir(runId), stage.id, checkoutRoot, attemptCtx);
|
|
@@ -183,6 +189,7 @@ export async function runStage(options) {
|
|
|
183
189
|
workerMode,
|
|
184
190
|
store: workerMode ? store : undefined,
|
|
185
191
|
attemptCtx,
|
|
192
|
+
skipGates,
|
|
186
193
|
});
|
|
187
194
|
}
|
|
188
195
|
catch (err) {
|
|
@@ -68,6 +68,7 @@ export async function runStageWorker(input) {
|
|
|
68
68
|
attemptCtx,
|
|
69
69
|
factoryCwd: input.rootDir,
|
|
70
70
|
operatorCatalog: input.operatorCatalog,
|
|
71
|
+
skipGates: input.skipGates,
|
|
71
72
|
});
|
|
72
73
|
return outcome;
|
|
73
74
|
}
|
|
@@ -85,6 +86,7 @@ export async function runStageWorker(input) {
|
|
|
85
86
|
attemptCtx,
|
|
86
87
|
factoryCwd: input.rootDir,
|
|
87
88
|
operatorCatalog: input.operatorCatalog,
|
|
89
|
+
skipGates: input.skipGates,
|
|
88
90
|
});
|
|
89
91
|
}
|
|
90
92
|
finally {
|
package/package.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Locate the pi-cursor-sdk extension entry without opening full global package
|
|
3
|
-
* discovery. Stages stay sealed; only this allowlisted path is loaded.
|
|
4
|
-
*
|
|
5
|
-
* Resolution order:
|
|
6
|
-
* 1. SOFTWARE_FACTORY_CURSOR_EXTENSION (absolute path to the extension .ts/.js)
|
|
7
|
-
* 2. Path package from ~/.pi/agent/settings.json (same source interactive pi uses)
|
|
8
|
-
* 3. npm install under ~/.pi/agent/npm/node_modules/pi-cursor-sdk
|
|
9
|
-
* 4. Sibling checkout at ../pi-cursor-sdk relative to this repo
|
|
10
|
-
*/
|
|
11
|
-
export declare function resolveCursorExtensionPath(): string | undefined;
|
|
12
|
-
export declare function isCursorModelRef(modelRef: string): boolean;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
/**
|
|
6
|
-
* Locate the pi-cursor-sdk extension entry without opening full global package
|
|
7
|
-
* discovery. Stages stay sealed; only this allowlisted path is loaded.
|
|
8
|
-
*
|
|
9
|
-
* Resolution order:
|
|
10
|
-
* 1. SOFTWARE_FACTORY_CURSOR_EXTENSION (absolute path to the extension .ts/.js)
|
|
11
|
-
* 2. Path package from ~/.pi/agent/settings.json (same source interactive pi uses)
|
|
12
|
-
* 3. npm install under ~/.pi/agent/npm/node_modules/pi-cursor-sdk
|
|
13
|
-
* 4. Sibling checkout at ../pi-cursor-sdk relative to this repo
|
|
14
|
-
*/
|
|
15
|
-
export function resolveCursorExtensionPath() {
|
|
16
|
-
const fromEnv = process.env.SOFTWARE_FACTORY_CURSOR_EXTENSION?.trim();
|
|
17
|
-
if (fromEnv && existsSync(fromEnv)) {
|
|
18
|
-
return path.resolve(fromEnv);
|
|
19
|
-
}
|
|
20
|
-
const fromSettings = resolveFromPiSettings();
|
|
21
|
-
if (fromSettings) {
|
|
22
|
-
return fromSettings;
|
|
23
|
-
}
|
|
24
|
-
const npmEntry = path.join(os.homedir(), ".pi", "agent", "npm", "node_modules", "pi-cursor-sdk", "src", "index.ts");
|
|
25
|
-
if (existsSync(npmEntry)) {
|
|
26
|
-
return npmEntry;
|
|
27
|
-
}
|
|
28
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
-
const sibling = path.resolve(here, "../../../pi-cursor-sdk/src/index.ts");
|
|
30
|
-
if (existsSync(sibling)) {
|
|
31
|
-
return sibling;
|
|
32
|
-
}
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
export function isCursorModelRef(modelRef) {
|
|
36
|
-
const slash = modelRef.indexOf("/");
|
|
37
|
-
if (slash <= 0) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
return modelRef.slice(0, slash).toLowerCase() === "cursor";
|
|
41
|
-
}
|
|
42
|
-
function resolveFromPiSettings() {
|
|
43
|
-
const settingsPath = path.join(os.homedir(), ".pi", "agent", "settings.json");
|
|
44
|
-
if (!existsSync(settingsPath)) {
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
let settings;
|
|
48
|
-
try {
|
|
49
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return undefined;
|
|
53
|
-
}
|
|
54
|
-
if (!Array.isArray(settings.packages)) {
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
const agentDir = path.join(os.homedir(), ".pi", "agent");
|
|
58
|
-
for (const entry of settings.packages) {
|
|
59
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
const pkg = entry;
|
|
63
|
-
if (typeof pkg.source !== "string") {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (!pkg.source.includes("pi-cursor-sdk")) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
const packageRoot = path.resolve(agentDir, pkg.source);
|
|
70
|
-
if (Array.isArray(pkg.extensions)) {
|
|
71
|
-
for (const ext of pkg.extensions) {
|
|
72
|
-
if (typeof ext !== "string") {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const rel = ext.replace(/^\+/, "");
|
|
76
|
-
const full = path.resolve(packageRoot, rel);
|
|
77
|
-
if (existsSync(full)) {
|
|
78
|
-
return full;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const declared = path.join(packageRoot, "src", "index.ts");
|
|
83
|
-
if (existsSync(declared)) {
|
|
84
|
-
return declared;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Prefer RunStore.listRuns / readRun via createRunStore.
|
|
3
|
-
*/
|
|
4
|
-
export { deriveStatusFromStages, type StageLogEvent, type StageSnapshot, type RunSummary, type RunDetail, } from "./port.js";
|
|
5
|
-
import type { RunDetail, RunSummary, StageLogEvent } from "./port.js";
|
|
6
|
-
export declare function listRuns(rootDir: string): Promise<RunSummary[]>;
|
|
7
|
-
export declare function readRun(rootDir: string, runId: string): Promise<RunDetail>;
|
|
8
|
-
export declare function readStageLog(runDir: string, stageId: string): Promise<StageLogEvent[]>;
|
package/dist/runstore/catalog.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Prefer RunStore.listRuns / readRun via createRunStore.
|
|
3
|
-
*/
|
|
4
|
-
export { deriveStatusFromStages, } from "./port.js";
|
|
5
|
-
import { DiskRunStore } from "./disk/DiskRunStore.js";
|
|
6
|
-
import { storeRootFor } from "./paths.js";
|
|
7
|
-
import { readStageLogFromDisk } from "./disk/catalogHelpers.js";
|
|
8
|
-
export async function listRuns(rootDir) {
|
|
9
|
-
return new DiskRunStore(storeRootFor(rootDir)).listRuns();
|
|
10
|
-
}
|
|
11
|
-
export async function readRun(rootDir, runId) {
|
|
12
|
-
return new DiskRunStore(storeRootFor(rootDir)).readRun(runId);
|
|
13
|
-
}
|
|
14
|
-
export async function readStageLog(runDir, stageId) {
|
|
15
|
-
return readStageLogFromDisk(runDir, stageId);
|
|
16
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { StageEnvelope } from "../../types/envelope.js";
|
|
2
|
-
import type { StageLogLine } from "../../agent/activity.js";
|
|
3
|
-
import type { CreateRunInput, CreatedRun, RunMeta, RunStatus, RunStore, StageExecution, StageExecutionPatch, StageLogEvent } from "../port.js";
|
|
4
|
-
export declare class DiskRunStore implements RunStore {
|
|
5
|
-
private readonly storeRoot;
|
|
6
|
-
constructor(storeRoot: string);
|
|
7
|
-
getWorkspaceDir(runId: string): string;
|
|
8
|
-
createRun(input: CreateRunInput): Promise<CreatedRun>;
|
|
9
|
-
updateRunStatus(runId: string, status: RunStatus): Promise<void>;
|
|
10
|
-
readRunMeta(runId: string): Promise<RunMeta>;
|
|
11
|
-
readTaskYaml(runId: string): Promise<string>;
|
|
12
|
-
ensureStageWorkspace(runId: string, stageId: string): Promise<void>;
|
|
13
|
-
ensureAttemptWorkspace(runId: string, stageId: string, attempt: number): Promise<void>;
|
|
14
|
-
createStageExecution(runId: string, stageId: string): Promise<StageExecution>;
|
|
15
|
-
listStageExecutions(runId: string, stageId: string): Promise<StageExecution[]>;
|
|
16
|
-
getLatestStageExecution(runId: string, stageId: string): Promise<StageExecution | null>;
|
|
17
|
-
countStageAttempts(runId: string, stageId: string): Promise<number>;
|
|
18
|
-
getStageExecution(runId: string, stageId: string, attempt: number): Promise<StageExecution>;
|
|
19
|
-
updateStageExecution(runId: string, stageId: string, attempt: number, patch: StageExecutionPatch): Promise<void>;
|
|
20
|
-
writeEnvelope(runId: string, stageId: string, envelope: StageEnvelope, options?: {
|
|
21
|
-
attempt?: number;
|
|
22
|
-
}): Promise<void>;
|
|
23
|
-
readEnvelope(runId: string, stageId: string): Promise<StageEnvelope>;
|
|
24
|
-
appendStageEvent(runId: string, stageId: string, event: StageLogLine, options?: {
|
|
25
|
-
attempt?: number;
|
|
26
|
-
}): Promise<void>;
|
|
27
|
-
listStageEvents(runId: string, stageId: string, attempt?: number): Promise<StageLogEvent[]>;
|
|
28
|
-
listRuns(): Promise<import("../port.js").RunSummary[]>;
|
|
29
|
-
readRun(runId: string): Promise<import("../port.js").RunDetail>;
|
|
30
|
-
}
|