stageflow 0.1.0 → 0.3.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 +190 -13
- package/dist/agent/activity.d.ts +10 -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/fakeAgent.js +4 -0
- package/dist/agent/piAdapter.d.ts +16 -0
- package/dist/agent/piAdapter.js +88 -21
- package/dist/agent/port.d.ts +2 -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/ciIdentity.d.ts +14 -0
- package/dist/cli/ciIdentity.js +52 -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 +26 -0
- package/dist/cli/runCommand.js +209 -0
- package/dist/cli/runOutput.d.ts +19 -0
- package/dist/cli/runOutput.js +125 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +20 -3
- package/dist/cli.d.ts +12 -1
- package/dist/cli.js +82 -66
- 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 +84 -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 +16 -0
- package/dist/config/normalizePipelineStageEntry.js +186 -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 +3 -0
- package/dist/config/resolveForkEmitContext.js +12 -0
- package/dist/config/resolvePipelineDag.d.ts +5 -1
- package/dist/config/resolvePipelineDag.js +54 -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.js +7 -0
- package/dist/envelope/forkChoice.d.ts +3 -0
- package/dist/envelope/forkChoice.js +47 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/mcp/projectRun.d.ts +3 -0
- package/dist/mcp/projectRun.js +7 -0
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/tools.js +28 -11
- 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/runstore/normalizeCatalogPath.d.ts +1 -0
- package/dist/runstore/normalizeCatalogPath.js +4 -0
- package/dist/runstore/port.d.ts +15 -0
- package/dist/runstore/runProjection.js +11 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +46 -4
- package/dist/runstore/sqlite/schema.d.ts +1 -1
- package/dist/runstore/sqlite/schema.js +7 -1
- package/dist/runtime/credentialBinding.d.ts +8 -6
- package/dist/runtime/credentialBinding.js +18 -24
- package/dist/runtime/pipelineRunner.d.ts +11 -0
- package/dist/runtime/pipelineRunner.js +25 -1
- package/dist/runtime/pipelineScheduler.d.ts +3 -0
- package/dist/runtime/pipelineScheduler.js +70 -14
- 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 +27 -10
- package/dist/runtime/runManager.d.ts +8 -0
- package/dist/runtime/runManager.js +49 -18
- package/dist/runtime/settingsFile.d.ts +12 -0
- package/dist/runtime/settingsFile.js +87 -6
- package/dist/runtime/stageAttemptBootstrap.js +3 -0
- package/dist/runtime/stageProcessLauncher.d.ts +1 -0
- package/dist/runtime/stageProcessLauncher.js +6 -0
- package/dist/runtime/stageRecovery.js +3 -1
- package/dist/runtime/stageRunner.d.ts +2 -0
- package/dist/runtime/stageRunner.js +9 -2
- package/dist/runtime/stageWorker.js +4 -6
- package/dist/runtime/stageWorkerProtocol.d.ts +1 -0
- package/dist/server/http.js +33 -9
- package/dist/tools/emitStageEnvelope.d.ts +3 -1
- package/dist/tools/emitStageEnvelope.js +11 -1
- package/dist/types/envelope.d.ts +1 -0
- package/dist/types/forkChoice.d.ts +8 -0
- package/dist/types/forkChoice.js +1 -0
- package/dist/types/pipeline.d.ts +51 -2
- package/dist/types/stageflowManifest.d.ts +23 -0
- package/dist/types/stageflowManifest.js +1 -0
- package/dist/ui/assets/index-CFSzDZje.js +118 -0
- package/dist/ui/assets/{index-DCsDopak.css → index-DefBlEvN.css} +1 -1
- package/dist/ui/index.html +3 -2
- package/dist/ui/stageflow-icon.svg +12 -0
- package/package.json +7 -5
- 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
- package/dist/ui/assets/index-Cry0Tpfx.js +0 -118
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
function normalizeRoot(dir) {
|
|
6
|
+
try {
|
|
7
|
+
return realpathSync(path.resolve(dir));
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return path.resolve(dir);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function findProjectRoot(startDir) {
|
|
14
|
+
const normalized = path.resolve(startDir);
|
|
15
|
+
if (cache.has(normalized)) {
|
|
16
|
+
return cache.get(normalized) ?? null;
|
|
17
|
+
}
|
|
18
|
+
let result = null;
|
|
19
|
+
try {
|
|
20
|
+
const stdout = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
21
|
+
cwd: normalized,
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
24
|
+
});
|
|
25
|
+
const trimmed = stdout.trim();
|
|
26
|
+
if (trimmed.length > 0) {
|
|
27
|
+
result = normalizeRoot(trimmed);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
result = walkForGitRoot(normalized);
|
|
32
|
+
}
|
|
33
|
+
cache.set(normalized, result);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
function walkForGitRoot(startDir) {
|
|
37
|
+
let dir = path.resolve(startDir);
|
|
38
|
+
for (;;) {
|
|
39
|
+
if (existsSync(path.join(dir, ".git"))) {
|
|
40
|
+
return normalizeRoot(dir);
|
|
41
|
+
}
|
|
42
|
+
const parent = path.dirname(dir);
|
|
43
|
+
if (parent === dir) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
dir = parent;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function clearFindProjectRootCacheForTests() {
|
|
50
|
+
cache.clear();
|
|
51
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export function globalStageflowHome() {
|
|
5
|
+
return path.join(os.homedir(), ".stageflow");
|
|
6
|
+
}
|
|
7
|
+
export function ensureGlobalHome() {
|
|
8
|
+
const globalHome = globalStageflowHome();
|
|
9
|
+
mkdirSync(globalHome, { recursive: true });
|
|
10
|
+
const agentDir = path.join(globalHome, "agent");
|
|
11
|
+
mkdirSync(agentDir, { recursive: true });
|
|
12
|
+
try {
|
|
13
|
+
chmodSync(agentDir, 0o700);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// best-effort on non-POSIX
|
|
17
|
+
}
|
|
18
|
+
return globalHome;
|
|
19
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type ProjectContext = {
|
|
2
|
+
invocationCwd: string;
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
globalHome: string;
|
|
5
|
+
isGitProject: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function resolveProjectContext(invocationCwd: string): ProjectContext;
|
|
8
|
+
export declare function legacyProjectContext(cwd: string): ProjectContext;
|
|
9
|
+
export declare function asProjectContext(input: ProjectContext | string): ProjectContext;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { findProjectRoot } from "./findProjectRoot.js";
|
|
4
|
+
import { ensureGlobalHome, globalStageflowHome } from "./globalHome.js";
|
|
5
|
+
export function resolveProjectContext(invocationCwd) {
|
|
6
|
+
const resolvedInvocation = path.resolve(invocationCwd);
|
|
7
|
+
const gitRoot = findProjectRoot(resolvedInvocation);
|
|
8
|
+
const globalHome = ensureGlobalHome();
|
|
9
|
+
if (gitRoot !== null) {
|
|
10
|
+
return {
|
|
11
|
+
invocationCwd: resolvedInvocation,
|
|
12
|
+
projectRoot: gitRoot,
|
|
13
|
+
globalHome,
|
|
14
|
+
isGitProject: true,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
invocationCwd: resolvedInvocation,
|
|
19
|
+
projectRoot: os.homedir(),
|
|
20
|
+
globalHome,
|
|
21
|
+
isGitProject: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function legacyProjectContext(cwd) {
|
|
25
|
+
const invocationCwd = path.resolve(cwd);
|
|
26
|
+
return {
|
|
27
|
+
invocationCwd,
|
|
28
|
+
projectRoot: invocationCwd,
|
|
29
|
+
globalHome: globalStageflowHome(),
|
|
30
|
+
isGitProject: false,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function asProjectContext(input) {
|
|
34
|
+
if (typeof input !== "string") {
|
|
35
|
+
return input;
|
|
36
|
+
}
|
|
37
|
+
return resolveProjectContext(input);
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LoadedManifest } from "../types/stageflowManifest.js";
|
|
2
|
+
import type { LoadIssue } from "../config/loadOutcome.js";
|
|
3
|
+
import type { ProjectContext } from "./resolveProjectContext.js";
|
|
4
|
+
export type CatalogManifestStatus = "ok" | "missing" | "invalid" | "not_git";
|
|
5
|
+
export type StageflowContext = ProjectContext & {
|
|
6
|
+
manifest: LoadedManifest | null;
|
|
7
|
+
manifestStatus: CatalogManifestStatus;
|
|
8
|
+
manifestIssues: LoadIssue[];
|
|
9
|
+
};
|
|
10
|
+
export declare function projectContextFromStageflow(ctx: StageflowContext): ProjectContext;
|
|
11
|
+
export declare function resolveStageflowContext(invocationCwd: string): Promise<StageflowContext>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { loadStageflowManifestOutcome, manifestPathForProject, } from "../config/loadStageflowManifest.js";
|
|
5
|
+
import { findProjectRoot } from "./findProjectRoot.js";
|
|
6
|
+
import { ensureGlobalHome } from "./globalHome.js";
|
|
7
|
+
export function projectContextFromStageflow(ctx) {
|
|
8
|
+
return {
|
|
9
|
+
invocationCwd: ctx.invocationCwd,
|
|
10
|
+
projectRoot: ctx.projectRoot,
|
|
11
|
+
globalHome: ctx.globalHome,
|
|
12
|
+
isGitProject: ctx.isGitProject,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export async function resolveStageflowContext(invocationCwd) {
|
|
16
|
+
const resolvedInvocation = path.resolve(invocationCwd);
|
|
17
|
+
const gitRoot = findProjectRoot(resolvedInvocation);
|
|
18
|
+
const globalHome = ensureGlobalHome();
|
|
19
|
+
if (gitRoot === null) {
|
|
20
|
+
return {
|
|
21
|
+
invocationCwd: resolvedInvocation,
|
|
22
|
+
projectRoot: os.homedir(),
|
|
23
|
+
globalHome,
|
|
24
|
+
isGitProject: false,
|
|
25
|
+
manifest: null,
|
|
26
|
+
manifestStatus: "not_git",
|
|
27
|
+
manifestIssues: [],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const manifestPath = manifestPathForProject(gitRoot);
|
|
31
|
+
try {
|
|
32
|
+
await access(manifestPath);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {
|
|
36
|
+
invocationCwd: resolvedInvocation,
|
|
37
|
+
projectRoot: gitRoot,
|
|
38
|
+
globalHome,
|
|
39
|
+
isGitProject: true,
|
|
40
|
+
manifest: null,
|
|
41
|
+
manifestStatus: "missing",
|
|
42
|
+
manifestIssues: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const outcome = await loadStageflowManifestOutcome(gitRoot);
|
|
46
|
+
if (!outcome.ok) {
|
|
47
|
+
return {
|
|
48
|
+
invocationCwd: resolvedInvocation,
|
|
49
|
+
projectRoot: gitRoot,
|
|
50
|
+
globalHome,
|
|
51
|
+
isGitProject: true,
|
|
52
|
+
manifest: null,
|
|
53
|
+
manifestStatus: "invalid",
|
|
54
|
+
manifestIssues: outcome.issues,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
invocationCwd: resolvedInvocation,
|
|
59
|
+
projectRoot: gitRoot,
|
|
60
|
+
globalHome,
|
|
61
|
+
isGitProject: true,
|
|
62
|
+
manifest: outcome.value,
|
|
63
|
+
manifestStatus: "ok",
|
|
64
|
+
manifestIssues: [],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeCatalogPath(raw: string): string;
|
package/dist/runstore/port.d.ts
CHANGED
|
@@ -36,7 +36,13 @@ export type RunMeta = {
|
|
|
36
36
|
task_id?: string;
|
|
37
37
|
updated_at?: string;
|
|
38
38
|
checkout_root?: string;
|
|
39
|
+
git_sha?: string;
|
|
40
|
+
ci_pr_url?: string;
|
|
41
|
+
ci_job_url?: string;
|
|
39
42
|
pipeline_dag?: RunPipelineDagSnapshot;
|
|
43
|
+
pipeline_path?: string;
|
|
44
|
+
task_path?: string;
|
|
45
|
+
project_root?: string;
|
|
40
46
|
};
|
|
41
47
|
export type CreatedRun = {
|
|
42
48
|
runId: string;
|
|
@@ -81,6 +87,9 @@ export type RunSummary = {
|
|
|
81
87
|
run_id: string;
|
|
82
88
|
pipeline_id: string;
|
|
83
89
|
task_id?: string;
|
|
90
|
+
pipeline_path?: string;
|
|
91
|
+
task_path?: string;
|
|
92
|
+
project_root?: string;
|
|
84
93
|
status: RunStatus;
|
|
85
94
|
created_at: string;
|
|
86
95
|
updated_at?: string;
|
|
@@ -108,7 +117,13 @@ export type CreateRunInput = {
|
|
|
108
117
|
taskYaml: string;
|
|
109
118
|
taskId?: string;
|
|
110
119
|
checkoutRoot?: string;
|
|
120
|
+
gitSha?: string;
|
|
121
|
+
ciPrUrl?: string;
|
|
122
|
+
ciJobUrl?: string;
|
|
111
123
|
pipelineDag?: RunPipelineDagSnapshot;
|
|
124
|
+
pipelinePath?: string;
|
|
125
|
+
taskPath?: string;
|
|
126
|
+
projectRoot?: string;
|
|
112
127
|
};
|
|
113
128
|
/**
|
|
114
129
|
* Persistence port for pipeline run state.
|
|
@@ -65,14 +65,24 @@ function failedFieldsFromStages(stages) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
function resolveListedStatus(stages, meta) {
|
|
68
|
+
if (stages.length === 0)
|
|
69
|
+
return meta.status ?? "created";
|
|
68
70
|
const derived = deriveStatusFromStages(stages);
|
|
69
|
-
|
|
71
|
+
if (meta.status === "succeeded" && derived === "running") {
|
|
72
|
+
const hasActiveStage = stages.some((s) => s.status === "running" || s.status === "waiting_for_input");
|
|
73
|
+
if (!hasActiveStage)
|
|
74
|
+
return "succeeded";
|
|
75
|
+
}
|
|
76
|
+
return derived;
|
|
70
77
|
}
|
|
71
78
|
export function projectRunSummary(meta, stages) {
|
|
72
79
|
return {
|
|
73
80
|
run_id: meta.run_id,
|
|
74
81
|
pipeline_id: meta.pipeline_id,
|
|
75
82
|
task_id: meta.task_id,
|
|
83
|
+
...(meta.pipeline_path !== undefined ? { pipeline_path: meta.pipeline_path } : {}),
|
|
84
|
+
...(meta.task_path !== undefined ? { task_path: meta.task_path } : {}),
|
|
85
|
+
...(meta.project_root !== undefined ? { project_root: meta.project_root } : {}),
|
|
76
86
|
status: resolveListedStatus(stages, meta),
|
|
77
87
|
created_at: meta.created_at,
|
|
78
88
|
updated_at: meta.updated_at,
|
|
@@ -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";
|
|
@@ -27,12 +28,30 @@ function ensureCheckoutRootColumn(db) {
|
|
|
27
28
|
db.exec(`ALTER TABLE runs ADD COLUMN checkout_root TEXT`);
|
|
28
29
|
}
|
|
29
30
|
}
|
|
31
|
+
function ensureCiIdentityColumns(db) {
|
|
32
|
+
const cols = db.prepare(`PRAGMA table_info(runs)`).all();
|
|
33
|
+
const names = new Set(cols.map((c) => c.name));
|
|
34
|
+
for (const name of ["git_sha", "ci_pr_url", "ci_job_url"]) {
|
|
35
|
+
if (!names.has(name)) {
|
|
36
|
+
db.exec(`ALTER TABLE runs ADD COLUMN ${name} TEXT`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
30
40
|
function ensurePipelineDagColumn(db) {
|
|
31
41
|
const cols = db.prepare(`PRAGMA table_info(runs)`).all();
|
|
32
42
|
if (!cols.some((c) => c.name === "pipeline_dag_json")) {
|
|
33
43
|
db.exec(`ALTER TABLE runs ADD COLUMN pipeline_dag_json TEXT`);
|
|
34
44
|
}
|
|
35
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
|
+
}
|
|
36
55
|
function ensureStageExecutionsTable(db) {
|
|
37
56
|
db.exec(`
|
|
38
57
|
CREATE TABLE IF NOT EXISTS stage_executions (
|
|
@@ -87,7 +106,9 @@ export class SqliteRunStore {
|
|
|
87
106
|
this.db.pragma(`busy_timeout = ${readSqliteBusyTimeoutMs()}`);
|
|
88
107
|
this.db.exec(SCHEMA_SQL);
|
|
89
108
|
ensureCheckoutRootColumn(this.db);
|
|
109
|
+
ensureCiIdentityColumns(this.db);
|
|
90
110
|
ensurePipelineDagColumn(this.db);
|
|
111
|
+
ensureRunLocatorColumns(this.db);
|
|
91
112
|
ensureStageExecutionsTable(this.db);
|
|
92
113
|
ensureStageEventsAttemptColumn(this.db);
|
|
93
114
|
this.migratePromise = importDiskRunsIfEmpty(this.db, storeRoot).then(() => undefined);
|
|
@@ -105,11 +126,20 @@ export class SqliteRunStore {
|
|
|
105
126
|
const workspaceDir = this.getWorkspaceDir(runId);
|
|
106
127
|
await mkdir(path.join(workspaceDir, "stages"), { recursive: true });
|
|
107
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;
|
|
108
138
|
this.db
|
|
109
139
|
.prepare(`INSERT INTO runs
|
|
110
|
-
(run_id, pipeline_id, task_id, task_yaml, status, created_at, updated_at, checkout_root, pipeline_dag_json)
|
|
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)
|
|
111
141
|
VALUES
|
|
112
|
-
(@run_id, @pipeline_id, @task_id, @task_yaml, @status, @created_at, @updated_at, @checkout_root, @pipeline_dag_json)`)
|
|
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)`)
|
|
113
143
|
.run({
|
|
114
144
|
run_id: runId,
|
|
115
145
|
pipeline_id: input.pipelineId,
|
|
@@ -122,6 +152,12 @@ export class SqliteRunStore {
|
|
|
122
152
|
pipeline_dag_json: input.pipelineDag
|
|
123
153
|
? JSON.stringify(input.pipelineDag)
|
|
124
154
|
: null,
|
|
155
|
+
git_sha: input.gitSha ?? null,
|
|
156
|
+
ci_pr_url: input.ciPrUrl ?? null,
|
|
157
|
+
ci_job_url: input.ciJobUrl ?? null,
|
|
158
|
+
pipeline_path: pipelinePath,
|
|
159
|
+
task_path: taskPath,
|
|
160
|
+
project_root: projectRoot,
|
|
125
161
|
});
|
|
126
162
|
return { runId, workspaceDir };
|
|
127
163
|
}
|
|
@@ -378,7 +414,7 @@ export class SqliteRunStore {
|
|
|
378
414
|
async listRuns() {
|
|
379
415
|
await this.ready();
|
|
380
416
|
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
|
|
417
|
+
.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
|
|
382
418
|
FROM runs ORDER BY created_at DESC`)
|
|
383
419
|
.all();
|
|
384
420
|
const summaries = [];
|
|
@@ -410,11 +446,17 @@ export class SqliteRunStore {
|
|
|
410
446
|
task_id: row.task_id ?? undefined,
|
|
411
447
|
updated_at: row.updated_at,
|
|
412
448
|
...(row.checkout_root != null ? { checkout_root: row.checkout_root } : {}),
|
|
449
|
+
...(row.git_sha != null ? { git_sha: row.git_sha } : {}),
|
|
450
|
+
...(row.ci_pr_url != null ? { ci_pr_url: row.ci_pr_url } : {}),
|
|
451
|
+
...(row.ci_job_url != null ? { ci_job_url: row.ci_job_url } : {}),
|
|
452
|
+
...(row.pipeline_path != null ? { pipeline_path: row.pipeline_path } : {}),
|
|
453
|
+
...(row.task_path != null ? { task_path: row.task_path } : {}),
|
|
454
|
+
...(row.project_root != null ? { project_root: row.project_root } : {}),
|
|
413
455
|
};
|
|
414
456
|
}
|
|
415
457
|
getRunRow(runId) {
|
|
416
458
|
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
|
|
459
|
+
.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
|
|
418
460
|
FROM runs WHERE run_id = ?`)
|
|
419
461
|
.get(runId);
|
|
420
462
|
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 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";
|
|
@@ -8,7 +8,13 @@ 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,
|
|
15
|
+
pipeline_path TEXT,
|
|
16
|
+
task_path TEXT,
|
|
17
|
+
project_root TEXT
|
|
12
18
|
);
|
|
13
19
|
|
|
14
20
|
CREATE TABLE IF NOT EXISTS stages (
|
|
@@ -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";
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { chmodSync, existsSync,
|
|
1
|
+
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
4
|
+
import { ensureGlobalHome } from "../project/globalHome.js";
|
|
5
|
+
import { resolveProjectContext, } from "../project/resolveProjectContext.js";
|
|
6
|
+
import { parseCredentialSource, readCredentialSourceFromContext, writeCredentialSourceToContext, } from "./settingsFile.js";
|
|
7
|
+
export { parseCredentialSource, readCredentialSourceFromContext, writeCredentialSourceToContext, };
|
|
8
|
+
export function sfOwnedAgentDir() {
|
|
9
|
+
return path.join(ensureGlobalHome(), "agent");
|
|
9
10
|
}
|
|
10
|
-
export function sfOwnedAuthPath(
|
|
11
|
-
return path.join(sfOwnedAgentDir(
|
|
11
|
+
export function sfOwnedAuthPath() {
|
|
12
|
+
return path.join(sfOwnedAgentDir(), "auth.json");
|
|
12
13
|
}
|
|
13
14
|
export function piHomeAuthPath(override) {
|
|
14
15
|
return (override ??
|
|
@@ -25,18 +26,9 @@ export function isUsableAuthFile(authPath) {
|
|
|
25
26
|
return false;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
export function ensureSfOwnedAuthStore(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const agentDir = sfOwnedAgentDir(cwd);
|
|
32
|
-
mkdirSync(agentDir, { recursive: true });
|
|
33
|
-
try {
|
|
34
|
-
chmodSync(agentDir, 0o700);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
// best-effort on non-POSIX
|
|
38
|
-
}
|
|
39
|
-
const authPath = sfOwnedAuthPath(cwd);
|
|
29
|
+
export function ensureSfOwnedAuthStore() {
|
|
30
|
+
ensureGlobalHome();
|
|
31
|
+
const authPath = sfOwnedAuthPath();
|
|
40
32
|
if (!existsSync(authPath)) {
|
|
41
33
|
writeFileSync(authPath, "{}\n", { encoding: "utf8", mode: 0o600 });
|
|
42
34
|
}
|
|
@@ -48,14 +40,15 @@ export function ensureSfOwnedAuthStore(cwd) {
|
|
|
48
40
|
}
|
|
49
41
|
return authPath;
|
|
50
42
|
}
|
|
51
|
-
export function resolveCredentialBinding(
|
|
43
|
+
export function resolveCredentialBinding(ctx, options = {}) {
|
|
44
|
+
const projectCtx = typeof ctx === "string" ? resolveProjectContext(ctx) : ctx;
|
|
52
45
|
const piHome = piHomeAuthPath(options.piHomeAuthPath);
|
|
53
|
-
const persisted =
|
|
46
|
+
const persisted = readCredentialSourceFromContext(projectCtx);
|
|
54
47
|
if (persisted !== undefined) {
|
|
55
48
|
if (persisted === "sf_owned") {
|
|
56
49
|
return {
|
|
57
50
|
source: "sf_owned",
|
|
58
|
-
authPath: ensureSfOwnedAuthStore(
|
|
51
|
+
authPath: ensureSfOwnedAuthStore(),
|
|
59
52
|
provisional: false,
|
|
60
53
|
};
|
|
61
54
|
}
|
|
@@ -74,7 +67,8 @@ export function resolveCredentialBinding(cwd, options = {}) {
|
|
|
74
67
|
}
|
|
75
68
|
return {
|
|
76
69
|
source: "sf_owned",
|
|
77
|
-
authPath: ensureSfOwnedAuthStore(
|
|
70
|
+
authPath: ensureSfOwnedAuthStore(),
|
|
78
71
|
provisional: true,
|
|
79
72
|
};
|
|
80
73
|
}
|
|
74
|
+
export { readCredentialSourceFromFile, writeCredentialSourceToFile, } from "./settingsFile.js";
|
|
@@ -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;
|
|
@@ -29,11 +31,13 @@ export type PreparedPipeline = {
|
|
|
29
31
|
agent: AgentPort;
|
|
30
32
|
store: RunStore;
|
|
31
33
|
cwd: string;
|
|
34
|
+
projectRoot: string;
|
|
32
35
|
checkoutRoot?: string;
|
|
33
36
|
hitl?: StageHitlController;
|
|
34
37
|
executionMode?: StageExecutionMode;
|
|
35
38
|
stageProcessLauncher?: StageProcessLauncher;
|
|
36
39
|
operatorCatalog?: OperatorCatalog;
|
|
40
|
+
skipGates?: boolean;
|
|
37
41
|
};
|
|
38
42
|
export type ExecuteStagesOptions = {
|
|
39
43
|
maxActiveStagesPerRun?: number;
|
|
@@ -52,12 +56,14 @@ export declare function runPipeline(options: {
|
|
|
52
56
|
taskYaml?: string;
|
|
53
57
|
pipeline: string;
|
|
54
58
|
cwd?: string;
|
|
59
|
+
projectRoot?: string;
|
|
55
60
|
checkoutOverride?: string;
|
|
56
61
|
hitl?: StageHitlController;
|
|
57
62
|
maxActiveStagesPerRun?: number;
|
|
58
63
|
executionMode?: StageExecutionMode;
|
|
59
64
|
stageProcessLauncher?: StageProcessLauncher;
|
|
60
65
|
operatorCatalog?: OperatorCatalog;
|
|
66
|
+
skipGates?: boolean;
|
|
61
67
|
}): Promise<PipelineRunResult>;
|
|
62
68
|
/** Create the run immediately, then execute stages in the returned promise. */
|
|
63
69
|
export declare function startPipeline(options: {
|
|
@@ -67,11 +73,16 @@ export declare function startPipeline(options: {
|
|
|
67
73
|
taskYaml?: string;
|
|
68
74
|
pipeline: string;
|
|
69
75
|
cwd?: string;
|
|
76
|
+
projectRoot?: string;
|
|
70
77
|
checkoutOverride?: string;
|
|
78
|
+
gitSha?: string;
|
|
79
|
+
ciPrUrl?: string;
|
|
80
|
+
ciJobUrl?: string;
|
|
71
81
|
hitl?: StageHitlController;
|
|
72
82
|
maxActiveStagesPerRun?: number;
|
|
73
83
|
executionMode?: StageExecutionMode;
|
|
74
84
|
stageProcessLauncher?: StageProcessLauncher;
|
|
75
85
|
operatorCatalog?: OperatorCatalog;
|
|
86
|
+
skipGates?: boolean;
|
|
76
87
|
}): Promise<StartedPipeline>;
|
|
77
88
|
export declare function resolveTaskPath(taskArg: string, cwd?: string): string;
|