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
@@ -11,9 +11,10 @@
11
11
  </script>
12
12
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13
13
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap" />
14
+ <link rel="icon" type="image/svg+xml" href="/stageflow-icon.svg" />
14
15
  <title>Stageflow</title>
15
- <script type="module" crossorigin src="/assets/index-Cry0Tpfx.js"></script>
16
- <link rel="stylesheet" crossorigin href="/assets/index-DCsDopak.css">
16
+ <script type="module" crossorigin src="/assets/index-CFSzDZje.js"></script>
17
+ <link rel="stylesheet" crossorigin href="/assets/index-DefBlEvN.css">
17
18
  </head>
18
19
  <body>
19
20
  <div id="root"></div>
@@ -0,0 +1,12 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" role="img" aria-label="Stageflow">
2
+ <path
3
+ d="M 12 20 C 28 20, 28 44, 52 44"
4
+ stroke="currentColor"
5
+ stroke-width="2.5"
6
+ stroke-linecap="round"
7
+ fill="none"
8
+ />
9
+ <circle cx="12" cy="20" r="5" fill="currentColor"/>
10
+ <circle cx="32" cy="32" r="5" fill="currentColor"/>
11
+ <circle cx="52" cy="44" r="5" fill="currentColor" opacity="0.45"/>
12
+ </svg>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stageflow",
3
- "version": "0.1.0",
4
- "description": "Stageflow — personal CLI pipeline runtime for configurable SDLC stages on Pi",
3
+ "version": "0.3.0",
4
+ "description": "Stageflow — CLI pipeline runtime for configurable stages on Pi",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "sf": "./dist/cli.js",
@@ -16,7 +16,7 @@
16
16
  "type": "git",
17
17
  "url": "git+https://github.com/tejasghutukade/stageflow.git"
18
18
  },
19
- "homepage": "https://github.com/tejasghutukade/stageflow#readme",
19
+ "homepage": "https://tejasghutukade.github.io/stageflow/",
20
20
  "bugs": {
21
21
  "url": "https://github.com/tejasghutukade/stageflow/issues"
22
22
  },
@@ -24,8 +24,9 @@
24
24
  "cli",
25
25
  "pipeline",
26
26
  "pi",
27
- "sdlc",
28
- "agent"
27
+ "agent",
28
+ "workflow",
29
+ "stages"
29
30
  ],
30
31
  "workspaces": [
31
32
  "ui"
@@ -42,6 +43,7 @@
42
43
  "ui:astryx": "npm run astryx -w ui",
43
44
  "copy:ui-dist": "tsx scripts/copy-ui-dist.ts",
44
45
  "verify:pack": "tsx scripts/verify-pack.ts",
46
+ "validate:examples": "npm run build && tsx scripts/validate-examples.ts",
45
47
  "prepublishOnly": "npm run build && npm run ui:build && npm run copy:ui-dist",
46
48
  "providers:list-login-capable": "tsx scripts/list-login-capable-providers.ts"
47
49
  },
@@ -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[]>;
@@ -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
- }
@@ -1,238 +0,0 @@
1
- import { appendFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { newRunId, runWorkspaceDir, runsDir } from "../paths.js";
4
- import { agentDir, artifactsDir, attemptAgentDir, attemptArtifactsDir, attemptEnvelopePath, attemptLogPath, envelopePath, stageDir, } from "../workspaceLayout.js";
5
- import { projectRunDetail, orderStageSnapshots } from "../runProjection.js";
6
- import { buildStageSnapshotFromStore } from "../stageSnapshot.js";
7
- import { buildRunSummaryFromDisk, fileExists, listStageIdsFromDisk, readRunMetaFromDisk, readStageLogFromDisk, taskIdFromCopy, } from "./catalogHelpers.js";
8
- const ensuredStageDirs = new Set();
9
- const EXECUTIONS_FILE = "executions.json";
10
- async function readStageLogFile(file) {
11
- if (!(await fileExists(file)))
12
- return [];
13
- const text = await readFile(file, "utf8");
14
- return text
15
- .split("\n")
16
- .map((line) => line.trim())
17
- .filter(Boolean)
18
- .map((line) => JSON.parse(line));
19
- }
20
- async function readExecutionsFromDisk(workspaceDir, stageId) {
21
- const file = path.join(workspaceDir, "stages", stageId, EXECUTIONS_FILE);
22
- if (!(await fileExists(file)))
23
- return [];
24
- const parsed = JSON.parse(await readFile(file, "utf8"));
25
- return [...parsed].sort((a, b) => a.attempt - b.attempt);
26
- }
27
- async function writeExecutionsToDisk(workspaceDir, stageId, executions) {
28
- const file = path.join(workspaceDir, "stages", stageId, EXECUTIONS_FILE);
29
- await mkdir(path.dirname(file), { recursive: true });
30
- const sorted = [...executions].sort((a, b) => a.attempt - b.attempt);
31
- await writeFile(file, `${JSON.stringify(sorted, null, 2)}\n`);
32
- }
33
- export class DiskRunStore {
34
- storeRoot;
35
- constructor(storeRoot) {
36
- this.storeRoot = storeRoot;
37
- }
38
- getWorkspaceDir(runId) {
39
- return runWorkspaceDir(this.storeRoot, runId);
40
- }
41
- async createRun(input) {
42
- const runId = newRunId();
43
- const workspaceDir = this.getWorkspaceDir(runId);
44
- await mkdir(workspaceDir, { recursive: true });
45
- const now = new Date().toISOString();
46
- const meta = {
47
- run_id: runId,
48
- pipeline_id: input.pipelineId,
49
- created_at: now,
50
- status: "running",
51
- task_id: input.taskId,
52
- updated_at: now,
53
- ...(input.checkoutRoot !== undefined
54
- ? { checkout_root: input.checkoutRoot }
55
- : {}),
56
- ...(input.pipelineDag ? { pipeline_dag: input.pipelineDag } : {}),
57
- };
58
- await Promise.all([
59
- writeFile(path.join(workspaceDir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`),
60
- writeFile(path.join(workspaceDir, "task.copy.yaml"), input.taskYaml),
61
- ]);
62
- return { runId, workspaceDir };
63
- }
64
- async updateRunStatus(runId, status) {
65
- const workspaceDir = this.getWorkspaceDir(runId);
66
- const meta = await readRunMetaFromDisk(workspaceDir);
67
- meta.status = status;
68
- meta.updated_at = new Date().toISOString();
69
- await writeFile(path.join(workspaceDir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`);
70
- }
71
- async readRunMeta(runId) {
72
- return readRunMetaFromDisk(this.getWorkspaceDir(runId));
73
- }
74
- async readTaskYaml(runId) {
75
- return readFile(path.join(this.getWorkspaceDir(runId), "task.copy.yaml"), "utf8");
76
- }
77
- async ensureStageWorkspace(runId, stageId) {
78
- const workspaceDir = this.getWorkspaceDir(runId);
79
- const dir = stageDir(workspaceDir, stageId);
80
- if (!ensuredStageDirs.has(dir)) {
81
- await mkdir(artifactsDir(workspaceDir, stageId), { recursive: true });
82
- await mkdir(agentDir(workspaceDir, stageId), { recursive: true });
83
- ensuredStageDirs.add(dir);
84
- }
85
- }
86
- async ensureAttemptWorkspace(runId, stageId, attempt) {
87
- const workspaceDir = this.getWorkspaceDir(runId);
88
- const key = `${runId}:${stageId}:${attempt}`;
89
- if (!ensuredStageDirs.has(key)) {
90
- await mkdir(attemptArtifactsDir(workspaceDir, stageId, attempt), {
91
- recursive: true,
92
- });
93
- await mkdir(attemptAgentDir(workspaceDir, stageId, attempt), {
94
- recursive: true,
95
- });
96
- ensuredStageDirs.add(key);
97
- }
98
- if (attempt === 1) {
99
- await this.ensureStageWorkspace(runId, stageId);
100
- }
101
- }
102
- async createStageExecution(runId, stageId) {
103
- const workspaceDir = this.getWorkspaceDir(runId);
104
- await this.ensureStageWorkspace(runId, stageId);
105
- const executions = await readExecutionsFromDisk(workspaceDir, stageId);
106
- const attempt = executions.length > 0 ? Math.max(...executions.map((e) => e.attempt)) + 1 : 1;
107
- const execution = {
108
- run_id: runId,
109
- stage_id: stageId,
110
- attempt,
111
- status: "pending",
112
- envelope: null,
113
- };
114
- executions.push(execution);
115
- await writeExecutionsToDisk(workspaceDir, stageId, executions);
116
- return execution;
117
- }
118
- async listStageExecutions(runId, stageId) {
119
- return readExecutionsFromDisk(this.getWorkspaceDir(runId), stageId);
120
- }
121
- async getLatestStageExecution(runId, stageId) {
122
- const executions = await this.listStageExecutions(runId, stageId);
123
- return executions.length > 0 ? executions[executions.length - 1] : null;
124
- }
125
- async countStageAttempts(runId, stageId) {
126
- const executions = await this.listStageExecutions(runId, stageId);
127
- return executions.length;
128
- }
129
- async getStageExecution(runId, stageId, attempt) {
130
- const executions = await this.listStageExecutions(runId, stageId);
131
- const found = executions.find((e) => e.attempt === attempt);
132
- if (!found) {
133
- throw new Error(`Stage execution not found: ${runId}/${stageId} attempt ${attempt}`);
134
- }
135
- return found;
136
- }
137
- async updateStageExecution(runId, stageId, attempt, patch) {
138
- const workspaceDir = this.getWorkspaceDir(runId);
139
- const executions = await readExecutionsFromDisk(workspaceDir, stageId);
140
- const index = executions.findIndex((e) => e.attempt === attempt);
141
- if (index < 0) {
142
- throw new Error(`Stage execution not found: ${runId}/${stageId} attempt ${attempt}`);
143
- }
144
- const current = executions[index];
145
- executions[index] = {
146
- ...current,
147
- ...(patch.status !== undefined ? { status: patch.status } : {}),
148
- ...(patch.started_at !== undefined ? { started_at: patch.started_at } : {}),
149
- ...(patch.finished_at !== undefined ? { finished_at: patch.finished_at } : {}),
150
- ...(patch.envelope !== undefined ? { envelope: patch.envelope } : {}),
151
- };
152
- await writeExecutionsToDisk(workspaceDir, stageId, executions);
153
- }
154
- async writeEnvelope(runId, stageId, envelope, options) {
155
- const attempt = options?.attempt ?? 1;
156
- await this.ensureStageWorkspace(runId, stageId);
157
- if (attempt >= 2) {
158
- await this.ensureAttemptWorkspace(runId, stageId, attempt);
159
- }
160
- const workspaceDir = this.getWorkspaceDir(runId);
161
- const file = attempt >= 2
162
- ? attemptEnvelopePath(workspaceDir, stageId, attempt)
163
- : envelopePath(workspaceDir, stageId);
164
- await writeFile(file, `${JSON.stringify(envelope, null, 2)}\n`);
165
- try {
166
- await this.updateStageExecution(runId, stageId, attempt, { envelope });
167
- }
168
- catch {
169
- // no execution row yet
170
- }
171
- }
172
- async readEnvelope(runId, stageId) {
173
- const file = envelopePath(this.getWorkspaceDir(runId), stageId);
174
- return JSON.parse(await readFile(file, "utf8"));
175
- }
176
- async appendStageEvent(runId, stageId, event, options) {
177
- const attempt = options?.attempt ?? 1;
178
- await this.ensureAttemptWorkspace(runId, stageId, attempt);
179
- const file = attemptLogPath(this.getWorkspaceDir(runId), stageId, attempt);
180
- const line = `${JSON.stringify({ ...event, at: new Date().toISOString() })}\n`;
181
- await appendFile(file, line);
182
- }
183
- async listStageEvents(runId, stageId, attempt) {
184
- const workspaceDir = this.getWorkspaceDir(runId);
185
- if (attempt != null) {
186
- return readStageLogFile(attemptLogPath(workspaceDir, stageId, attempt));
187
- }
188
- const events = [];
189
- events.push(...(await readStageLogFromDisk(workspaceDir, stageId)));
190
- const attemptsDir = path.join(workspaceDir, "stages", stageId, "attempts");
191
- if (await fileExists(attemptsDir)) {
192
- const entries = await readdir(attemptsDir, { withFileTypes: true });
193
- const attemptNums = entries
194
- .filter((e) => e.isDirectory())
195
- .map((e) => Number.parseInt(e.name, 10))
196
- .filter((n) => Number.isFinite(n) && n >= 2)
197
- .sort((a, b) => a - b);
198
- for (const n of attemptNums) {
199
- events.push(...(await readStageLogFile(attemptLogPath(workspaceDir, stageId, n))));
200
- }
201
- }
202
- return events.sort((a, b) => (a.at ?? "").localeCompare(b.at ?? ""));
203
- }
204
- async listRuns() {
205
- const root = runsDir(this.storeRoot);
206
- if (!(await fileExists(root)))
207
- return [];
208
- const entries = await readdir(root, { withFileTypes: true });
209
- const summaries = [];
210
- for (const entry of entries) {
211
- if (!entry.isDirectory())
212
- continue;
213
- try {
214
- summaries.push(await buildRunSummaryFromDisk(path.join(root, entry.name)));
215
- }
216
- catch {
217
- // skip malformed run folders
218
- }
219
- }
220
- summaries.sort((a, b) => b.created_at.localeCompare(a.created_at));
221
- return summaries;
222
- }
223
- async readRun(runId) {
224
- const workspaceDir = this.getWorkspaceDir(runId);
225
- if (!(await fileExists(path.join(workspaceDir, "meta.json")))) {
226
- throw new Error(`Run not found: ${runId}`);
227
- }
228
- const meta = await readRunMetaFromDisk(workspaceDir);
229
- const task_yaml = await readFile(path.join(workspaceDir, "task.copy.yaml"), "utf8");
230
- const stageIds = await listStageIdsFromDisk(workspaceDir, meta);
231
- const loaded = await Promise.all(stageIds.map((id) => buildStageSnapshotFromStore(this, runId, id, workspaceDir)));
232
- const stages = meta.pipeline_dag?.stage_ids?.length
233
- ? orderStageSnapshots(meta.pipeline_dag.stage_ids, loaded)
234
- : loaded;
235
- const task_id = await taskIdFromCopy(workspaceDir, meta);
236
- return projectRunDetail({ ...meta, task_id }, stages, task_yaml, meta.pipeline_dag ?? null);
237
- }
238
- }
@@ -1,22 +0,0 @@
1
- import type { StageEnvelope } from "../types/envelope.js";
2
- import type { RunMeta, RunStatus } from "./port.js";
3
- export type { RunMeta, RunStatus } from "./port.js";
4
- export { stageDir } from "./paths.js";
5
- export declare function runsRoot(rootDir: string): string;
6
- export declare function runDirFor(rootDir: string, runId: string): string;
7
- export declare function createRun(options: {
8
- rootDir: string;
9
- pipelineId: string;
10
- taskYaml: string;
11
- taskId?: string;
12
- }): Promise<{
13
- runId: string;
14
- runDir: string;
15
- }>;
16
- export declare function writeRunMeta(runDir: string, meta: RunMeta): Promise<void>;
17
- export declare function readRunMeta(runDir: string): Promise<RunMeta>;
18
- export declare function updateRunStatus(runDir: string, status: RunStatus): Promise<void>;
19
- export declare function ensureStageDirs(runDir: string, stageId: string): Promise<string>;
20
- export declare function writeEnvelope(runDir: string, stageId: string, envelope: StageEnvelope): Promise<string>;
21
- export declare function appendStageLog(runDir: string, stageId: string, event: Record<string, unknown>): Promise<void>;
22
- export declare function readEnvelope(runDir: string, stageId: string): Promise<StageEnvelope>;
@@ -1,58 +0,0 @@
1
- /**
2
- * @deprecated Prefer createRunStore() + RunStore. Thin disk helpers for legacy callers.
3
- */
4
- import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
5
- import path from "node:path";
6
- import { DiskRunStore } from "./disk/DiskRunStore.js";
7
- import { storeRootFor } from "./paths.js";
8
- export { stageDir } from "./paths.js";
9
- function disk(rootDir) {
10
- return new DiskRunStore(storeRootFor(rootDir));
11
- }
12
- export function runsRoot(rootDir) {
13
- return path.join(storeRootFor(rootDir), "runs");
14
- }
15
- export function runDirFor(rootDir, runId) {
16
- return path.join(runsRoot(rootDir), runId);
17
- }
18
- export async function createRun(options) {
19
- const created = await disk(options.rootDir).createRun({
20
- pipelineId: options.pipelineId,
21
- taskYaml: options.taskYaml,
22
- taskId: options.taskId,
23
- });
24
- return { runId: created.runId, runDir: created.workspaceDir };
25
- }
26
- export async function writeRunMeta(runDir, meta) {
27
- await writeFile(path.join(runDir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`);
28
- }
29
- export async function readRunMeta(runDir) {
30
- return JSON.parse(await readFile(path.join(runDir, "meta.json"), "utf8"));
31
- }
32
- export async function updateRunStatus(runDir, status) {
33
- const meta = await readRunMeta(runDir);
34
- meta.status = status;
35
- meta.updated_at = new Date().toISOString();
36
- await writeRunMeta(runDir, meta);
37
- }
38
- export async function ensureStageDirs(runDir, stageId) {
39
- const dir = path.join(runDir, "stages", stageId);
40
- await mkdir(path.join(dir, "artifacts"), { recursive: true });
41
- return dir;
42
- }
43
- export async function writeEnvelope(runDir, stageId, envelope) {
44
- const dir = await ensureStageDirs(runDir, stageId);
45
- const file = path.join(dir, "envelope.json");
46
- await writeFile(file, `${JSON.stringify(envelope, null, 2)}\n`);
47
- return file;
48
- }
49
- export async function appendStageLog(runDir, stageId, event) {
50
- const dir = await ensureStageDirs(runDir, stageId);
51
- const file = path.join(dir, "log.jsonl");
52
- const line = `${JSON.stringify({ ...event, at: new Date().toISOString() })}\n`;
53
- await appendFile(file, line);
54
- }
55
- export async function readEnvelope(runDir, stageId) {
56
- const file = path.join(runDir, "stages", stageId, "envelope.json");
57
- return JSON.parse(await readFile(file, "utf8"));
58
- }
@@ -1,38 +0,0 @@
1
- import type { OpaqueAnswer, OpaqueWaitRequest } from "../agent/port.js";
2
- /** Context for T2 answer/prompt matching (T2 owns kind schema later). */
3
- export type AnswerPromptAssertContext = {
4
- runId: string;
5
- stageId: string;
6
- answer: OpaqueAnswer;
7
- pendingRequest?: OpaqueWaitRequest;
8
- };
9
- /**
10
- * T2 pre-append gate. Stub accepts any opaque until ask-operator lands.
11
- * Call at deliverAnswer entry before any append or inject (KTD6 / KTD9).
12
- */
13
- export type AssertAnswerMatchesPrompt = (ctx: AnswerPromptAssertContext) => void | Promise<void>;
14
- export type HitlWaitEnterContext = {
15
- runId: string;
16
- stageId: string;
17
- request: OpaqueWaitRequest;
18
- };
19
- export type HitlAnswerDeliverContext = {
20
- runId: string;
21
- stageId: string;
22
- answer: OpaqueAnswer;
23
- pendingRequest?: OpaqueWaitRequest;
24
- };
25
- /**
26
- * T3 ordered hooks. No-op until durable Q&A owns operator_prompt / operator_answer.
27
- * T1 must not define event bodies here (KTD9).
28
- */
29
- export type HitlQaHooks = {
30
- onWaitEnter?: (ctx: HitlWaitEnterContext) => void | Promise<void>;
31
- onAnswerDeliver?: (ctx: HitlAnswerDeliverContext) => void | Promise<void>;
32
- };
33
- export declare const noopAssertAnswerMatchesPrompt: AssertAnswerMatchesPrompt;
34
- export declare const noopHitlQaHooks: HitlQaHooks;
35
- export type HitlSeams = {
36
- assertAnswerMatchesPrompt?: AssertAnswerMatchesPrompt;
37
- qaHooks?: HitlQaHooks;
38
- };
@@ -1,2 +0,0 @@
1
- export const noopAssertAnswerMatchesPrompt = () => { };
2
- export const noopHitlQaHooks = {};