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.
Files changed (132) hide show
  1. package/README.md +190 -13
  2. package/dist/agent/activity.d.ts +10 -1
  3. package/dist/agent/activity.js +37 -2
  4. package/dist/agent/activityObserver.d.ts +5 -1
  5. package/dist/agent/activityObserver.js +174 -11
  6. package/dist/agent/fakeAgent.js +4 -0
  7. package/dist/agent/piAdapter.d.ts +16 -0
  8. package/dist/agent/piAdapter.js +88 -21
  9. package/dist/agent/port.d.ts +2 -0
  10. package/dist/agent/providerAuth.js +12 -7
  11. package/dist/catalog/displayCatalogPath.d.ts +12 -0
  12. package/dist/catalog/displayCatalogPath.js +28 -0
  13. package/dist/cli/ciIdentity.d.ts +14 -0
  14. package/dist/cli/ciIdentity.js +52 -0
  15. package/dist/cli/initCommand.d.ts +9 -0
  16. package/dist/cli/initCommand.js +71 -0
  17. package/dist/cli/initTemplates.d.ts +3 -0
  18. package/dist/cli/initTemplates.js +19 -0
  19. package/dist/cli/operatorCatalog.d.ts +10 -0
  20. package/dist/cli/operatorCatalog.js +16 -0
  21. package/dist/cli/runCommand.d.ts +26 -0
  22. package/dist/cli/runCommand.js +209 -0
  23. package/dist/cli/runOutput.d.ts +19 -0
  24. package/dist/cli/runOutput.js +125 -0
  25. package/dist/cli/validateCommand.d.ts +1 -1
  26. package/dist/cli/validateCommand.js +20 -3
  27. package/dist/cli.d.ts +12 -1
  28. package/dist/cli.js +82 -66
  29. package/dist/config/browseCatalog.d.ts +45 -0
  30. package/dist/config/browseCatalog.js +170 -0
  31. package/dist/config/createPipeline.d.ts +12 -3
  32. package/dist/config/createPipeline.js +292 -100
  33. package/dist/config/createStage.d.ts +10 -4
  34. package/dist/config/createStage.js +44 -14
  35. package/dist/config/listConfig.d.ts +14 -19
  36. package/dist/config/listConfig.js +26 -191
  37. package/dist/config/listModelsFromManifest.d.ts +2 -0
  38. package/dist/config/listModelsFromManifest.js +4 -0
  39. package/dist/config/loadOutcome.d.ts +4 -0
  40. package/dist/config/loadPipeline.d.ts +1 -15
  41. package/dist/config/loadPipeline.js +84 -142
  42. package/dist/config/loadStage.d.ts +4 -0
  43. package/dist/config/loadStage.js +60 -32
  44. package/dist/config/loadStageflowManifest.d.ts +5 -0
  45. package/dist/config/loadStageflowManifest.js +157 -0
  46. package/dist/config/mergePipelineIncludes.d.ts +9 -0
  47. package/dist/config/mergePipelineIncludes.js +141 -0
  48. package/dist/config/normalizePipelineStageEntry.d.ts +16 -0
  49. package/dist/config/normalizePipelineStageEntry.js +186 -0
  50. package/dist/config/pipelineStageKeys.d.ts +4 -0
  51. package/dist/config/pipelineStageKeys.js +14 -0
  52. package/dist/config/resolveCatalogContext.d.ts +13 -0
  53. package/dist/config/resolveCatalogContext.js +13 -0
  54. package/dist/config/resolveForkEmitContext.d.ts +3 -0
  55. package/dist/config/resolveForkEmitContext.js +12 -0
  56. package/dist/config/resolvePipelineDag.d.ts +5 -1
  57. package/dist/config/resolvePipelineDag.js +54 -36
  58. package/dist/config/scanCatalogPaths.d.ts +4 -0
  59. package/dist/config/scanCatalogPaths.js +83 -0
  60. package/dist/config/validateCatalog.d.ts +22 -4
  61. package/dist/config/validateCatalog.js +169 -132
  62. package/dist/envelope/check.js +7 -0
  63. package/dist/envelope/forkChoice.d.ts +3 -0
  64. package/dist/envelope/forkChoice.js +47 -0
  65. package/dist/index.d.ts +4 -0
  66. package/dist/index.js +4 -0
  67. package/dist/mcp/projectRun.d.ts +3 -0
  68. package/dist/mcp/projectRun.js +7 -0
  69. package/dist/mcp/server.js +1 -1
  70. package/dist/mcp/tools.js +28 -11
  71. package/dist/project/findProjectRoot.d.ts +2 -0
  72. package/dist/project/findProjectRoot.js +51 -0
  73. package/dist/project/globalHome.d.ts +2 -0
  74. package/dist/project/globalHome.js +19 -0
  75. package/dist/project/resolveProjectContext.d.ts +9 -0
  76. package/dist/project/resolveProjectContext.js +38 -0
  77. package/dist/project/resolveStageflowContext.d.ts +11 -0
  78. package/dist/project/resolveStageflowContext.js +66 -0
  79. package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
  80. package/dist/runstore/normalizeCatalogPath.js +4 -0
  81. package/dist/runstore/port.d.ts +15 -0
  82. package/dist/runstore/runProjection.js +11 -1
  83. package/dist/runstore/sqlite/SqliteRunStore.js +46 -4
  84. package/dist/runstore/sqlite/schema.d.ts +1 -1
  85. package/dist/runstore/sqlite/schema.js +7 -1
  86. package/dist/runtime/credentialBinding.d.ts +8 -6
  87. package/dist/runtime/credentialBinding.js +18 -24
  88. package/dist/runtime/pipelineRunner.d.ts +11 -0
  89. package/dist/runtime/pipelineRunner.js +25 -1
  90. package/dist/runtime/pipelineScheduler.d.ts +3 -0
  91. package/dist/runtime/pipelineScheduler.js +70 -14
  92. package/dist/runtime/reloadRunCatalog.d.ts +5 -0
  93. package/dist/runtime/reloadRunCatalog.js +44 -0
  94. package/dist/runtime/resumeReconstruct.d.ts +1 -0
  95. package/dist/runtime/resumeReconstruct.js +27 -10
  96. package/dist/runtime/runManager.d.ts +8 -0
  97. package/dist/runtime/runManager.js +49 -18
  98. package/dist/runtime/settingsFile.d.ts +12 -0
  99. package/dist/runtime/settingsFile.js +87 -6
  100. package/dist/runtime/stageAttemptBootstrap.js +3 -0
  101. package/dist/runtime/stageProcessLauncher.d.ts +1 -0
  102. package/dist/runtime/stageProcessLauncher.js +6 -0
  103. package/dist/runtime/stageRecovery.js +3 -1
  104. package/dist/runtime/stageRunner.d.ts +2 -0
  105. package/dist/runtime/stageRunner.js +9 -2
  106. package/dist/runtime/stageWorker.js +4 -6
  107. package/dist/runtime/stageWorkerProtocol.d.ts +1 -0
  108. package/dist/server/http.js +33 -9
  109. package/dist/tools/emitStageEnvelope.d.ts +3 -1
  110. package/dist/tools/emitStageEnvelope.js +11 -1
  111. package/dist/types/envelope.d.ts +1 -0
  112. package/dist/types/forkChoice.d.ts +8 -0
  113. package/dist/types/forkChoice.js +1 -0
  114. package/dist/types/pipeline.d.ts +51 -2
  115. package/dist/types/stageflowManifest.d.ts +23 -0
  116. package/dist/types/stageflowManifest.js +1 -0
  117. package/dist/ui/assets/index-CFSzDZje.js +118 -0
  118. package/dist/ui/assets/{index-DCsDopak.css → index-DefBlEvN.css} +1 -1
  119. package/dist/ui/index.html +3 -2
  120. package/dist/ui/stageflow-icon.svg +12 -0
  121. package/package.json +7 -5
  122. package/dist/agent/cursorExtension.d.ts +0 -12
  123. package/dist/agent/cursorExtension.js +0 -88
  124. package/dist/runstore/catalog.d.ts +0 -8
  125. package/dist/runstore/catalog.js +0 -16
  126. package/dist/runstore/disk/DiskRunStore.d.ts +0 -30
  127. package/dist/runstore/disk/DiskRunStore.js +0 -238
  128. package/dist/runstore/layout.d.ts +0 -22
  129. package/dist/runstore/layout.js +0 -58
  130. package/dist/runtime/hitlSeams.d.ts +0 -38
  131. package/dist/runtime/hitlSeams.js +0 -2
  132. 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,2 @@
1
+ export declare function globalStageflowHome(): string;
2
+ export declare function ensureGlobalHome(): string;
@@ -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;
@@ -0,0 +1,4 @@
1
+ import path from "node:path";
2
+ export function normalizeCatalogPath(raw) {
3
+ return path.resolve(raw);
4
+ }
@@ -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
- return stages.length > 0 ? derived : (meta.status ?? derived);
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 { parseCredentialSource, readCredentialSourceFromFile, writeCredentialSourceToFile, type CredentialSource } from "./settingsFile.js";
2
- export { parseCredentialSource, readCredentialSourceFromFile, writeCredentialSourceToFile, };
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(cwd: string): string;
12
- export declare function sfOwnedAuthPath(cwd: string): string;
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(cwd: string): string;
16
- export declare function resolveCredentialBinding(cwd: string, options?: ResolveCredentialBindingOptions): CredentialBinding;
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, mkdirSync, readFileSync, writeFileSync } from "node:fs";
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 { storeRootFor } from "../runstore/paths.js";
5
- import { parseCredentialSource, readCredentialSourceFromFile, writeCredentialSourceToFile, } from "./settingsFile.js";
6
- export { parseCredentialSource, readCredentialSourceFromFile, writeCredentialSourceToFile, };
7
- export function sfOwnedAgentDir(cwd) {
8
- return path.join(storeRootFor(cwd), "agent");
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(cwd) {
11
- return path.join(sfOwnedAgentDir(cwd), "auth.json");
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(cwd) {
29
- const storeRoot = storeRootFor(cwd);
30
- mkdirSync(storeRoot, { recursive: true });
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(cwd, options = {}) {
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 = readCredentialSourceFromFile(cwd);
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(cwd),
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(cwd),
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;