stageflow 0.3.0 → 0.9.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 +44 -14
- package/dist/agent/activity.d.ts +2 -0
- package/dist/agent/cursorProvider.d.ts +1 -10
- package/dist/agent/cursorProvider.js +18 -10
- package/dist/agent/fakeAgent.js +11 -7
- package/dist/agent/piAdapter.d.ts +2 -1
- package/dist/agent/piAdapter.js +28 -25
- package/dist/agent/port.d.ts +6 -1
- package/dist/agent/port.js +3 -0
- package/dist/cli/artifactCommand.d.ts +10 -0
- package/dist/cli/artifactCommand.js +120 -0
- package/dist/cli/envelopeCommand.d.ts +32 -0
- package/dist/cli/envelopeCommand.js +285 -0
- package/dist/cli/exportRunCommand.d.ts +10 -0
- package/dist/cli/exportRunCommand.js +150 -0
- package/dist/cli/handoffFormat.d.ts +22 -0
- package/dist/cli/handoffFormat.js +61 -0
- package/dist/cli/runCommand.d.ts +5 -1
- package/dist/cli/runCommand.js +36 -5
- package/dist/cli/runOutput.d.ts +4 -1
- package/dist/cli/runOutput.js +36 -8
- package/dist/cli/runsCommand.d.ts +22 -0
- package/dist/cli/runsCommand.js +591 -0
- package/dist/cli/skillsCommand.d.ts +16 -0
- package/dist/cli/skillsCommand.js +459 -0
- package/dist/cli/validateOutput.d.ts +2 -2
- package/dist/cli/validateOutput.js +2 -2
- package/dist/cli.js +110 -5
- package/dist/config/loadPipeline.js +5 -1
- package/dist/config/normalizePipelineStageEntry.d.ts +2 -0
- package/dist/config/normalizePipelineStageEntry.js +25 -1
- package/dist/config/pipelineStageKeys.js +1 -1
- package/dist/config/resolveForkEmitContext.d.ts +2 -1
- package/dist/config/resolveForkEmitContext.js +23 -1
- package/dist/config/resolvePipelineDag.js +36 -4
- package/dist/envelope/check.d.ts +2 -0
- package/dist/envelope/check.js +51 -0
- package/dist/envelope/cloneForks.d.ts +2 -0
- package/dist/envelope/cloneForks.js +62 -0
- package/dist/envelope/forkChoice.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/mcp/catalogTools.d.ts +3 -0
- package/dist/mcp/catalogTools.js +187 -0
- package/dist/mcp/controlTools.d.ts +3 -0
- package/dist/mcp/controlTools.js +147 -0
- package/dist/mcp/deps.d.ts +11 -0
- package/dist/mcp/deps.js +1 -0
- package/dist/mcp/projectRun.d.ts +1 -40
- package/dist/mcp/projectRun.js +1 -64
- package/dist/mcp/resources.d.ts +5 -0
- package/dist/mcp/resources.js +45 -0
- package/dist/mcp/server.d.ts +16 -3
- package/dist/mcp/server.js +159 -3
- package/dist/mcp/toolResults.d.ts +7 -0
- package/dist/mcp/toolResults.js +6 -0
- package/dist/mcp/tools.d.ts +2 -7
- package/dist/mcp/tools.js +52 -103
- package/dist/mcp/waitRun.d.ts +46 -0
- package/dist/mcp/waitRun.js +146 -0
- package/dist/mcp/waitingGates.d.ts +12 -0
- package/dist/mcp/waitingGates.js +83 -0
- package/dist/package-meta.d.ts +1 -0
- package/dist/package-meta.js +1 -0
- package/dist/projection/projectRun.d.ts +44 -0
- package/dist/projection/projectRun.js +71 -0
- package/dist/prompt/priorEnvelope.d.ts +1 -1
- package/dist/prompt/priorEnvelope.js +4 -1
- package/dist/runstore/paths.js +2 -0
- package/dist/runstore/pipelineDagSnapshot.d.ts +9 -0
- package/dist/runstore/pipelineDagSnapshot.js +90 -1
- package/dist/runstore/port.d.ts +13 -2
- package/dist/runstore/port.js +4 -1
- package/dist/runstore/runProjection.d.ts +1 -1
- package/dist/runstore/runProjection.js +14 -3
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +3 -2
- package/dist/runstore/sqlite/SqliteRunStore.js +35 -3
- package/dist/runstore/stageInstanceId.d.ts +4 -0
- package/dist/runstore/stageInstanceId.js +23 -0
- package/dist/runstore/syntheticStageSnapshot.d.ts +1 -1
- package/dist/runstore/syntheticStageSnapshot.js +2 -1
- package/dist/runstore/trackProjection.js +28 -12
- package/dist/runstore/workspaceLayout.d.ts +1 -0
- package/dist/runstore/workspaceLayout.js +1 -1
- package/dist/runtime/cloneSchedule.d.ts +18 -0
- package/dist/runtime/cloneSchedule.js +233 -0
- package/dist/runtime/envelopeRouting.d.ts +2 -1
- package/dist/runtime/envelopeRouting.js +80 -13
- package/dist/runtime/pipelineScheduler.d.ts +6 -2
- package/dist/runtime/pipelineScheduler.js +166 -30
- package/dist/runtime/resumeReconstruct.js +8 -3
- package/dist/runtime/runChangeBus.d.ts +18 -0
- package/dist/runtime/runChangeBus.js +83 -0
- package/dist/runtime/runManager.d.ts +7 -0
- package/dist/runtime/runManager.js +31 -2
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +4 -1
- package/dist/runtime/stageAttemptBootstrap.d.ts +1 -0
- package/dist/runtime/stageAttemptBootstrap.js +17 -9
- package/dist/runtime/stageRunner.d.ts +1 -0
- package/dist/runtime/stageRunner.js +17 -15
- package/dist/runtime/stageWorker.js +11 -4
- package/dist/server/bootstrap.d.ts +32 -0
- package/dist/server/bootstrap.js +56 -0
- package/dist/server/createHttpHost.d.ts +34 -0
- package/dist/server/createHttpHost.js +70 -0
- package/dist/server/http.d.ts +6 -13
- package/dist/server/http.js +295 -374
- package/dist/server/mcpHost.d.ts +22 -0
- package/dist/server/mcpHost.js +21 -0
- package/dist/server/operatorResults.d.ts +13 -0
- package/dist/server/operatorResults.js +22 -0
- package/dist/tools/emitStageEnvelope.d.ts +11 -2
- package/dist/tools/emitStageEnvelope.js +22 -6
- package/dist/types/envelope.d.ts +2 -0
- package/dist/types/forkChoice.d.ts +22 -0
- package/dist/types/pipeline.d.ts +8 -0
- package/dist/ui/assets/{index-DefBlEvN.css → index-C3N7MXAC.css} +1 -1
- package/dist/ui/assets/index-CSoGDA3A.js +118 -0
- package/dist/ui/index.html +2 -2
- package/package.json +2 -1
- package/skills/install-suite.sh +107 -0
- package/skills/stageflow/SKILL.md +26 -0
- package/skills/stageflow/references/control-surface.md +28 -0
- package/skills/stageflow/scripts/detect-host.mjs +54 -0
- package/skills/stageflow/scripts/detect-host.test.mjs +97 -0
- package/skills/stageflow-author/SKILL.md +58 -0
- package/skills/stageflow-author/assets/examples/branch-decision/hotfix.yaml +10 -0
- package/skills/stageflow-author/assets/examples/branch-decision/release-gate.pipeline.yaml +12 -0
- package/skills/stageflow-author/assets/examples/branch-decision/run-tests.yaml +13 -0
- package/skills/stageflow-author/assets/examples/branch-decision/ship.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/draft.yaml +9 -0
- package/skills/stageflow-author/assets/examples/linear-review/publish.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review-loop.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review.yaml +15 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/gather.yaml +9 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/research-digest.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/send.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/summarize.yaml +9 -0
- package/skills/stageflow-author/references/catalog-mapping.md +131 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +29 -0
- package/skills/stageflow-author/references/stage-prompt-template.md +55 -0
- package/skills/stageflow-author/references/validate-and-report.md +38 -0
- package/skills/stageflow-delegate/SKILL.md +18 -0
- package/skills/stageflow-delegate/references/authoring-or-run.md +15 -0
- package/skills/stageflow-delegate/references/example-walkthrough.md +30 -0
- package/skills/stageflow-delegate/references/pattern-detection.md +44 -0
- package/skills/stageflow-run/SKILL.md +182 -0
- package/skills/stageflow-run/references/mcp-call.md +17 -0
- package/skills/stageflow-run/references/native-question-ui.md +78 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +52 -0
- package/skills/stageflow-run/scripts/mcp-call.mjs +299 -0
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +271 -0
- package/skills/stageflow-session-capture/SKILL.md +94 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/example.pipeline.yaml +7 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/implement.yaml +8 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/research.yaml +8 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +40 -0
- package/skills/stageflow-session-capture/references/transcript-sources.md +25 -0
- package/skills/stageflow-session-capture/scripts/check-provider-gate.mjs +43 -0
- package/skills/stageflow-session-capture/scripts/locate-session-transcript.mjs +141 -0
- package/skills/stageflow-session-capture/scripts/resolve-catalog-id.mjs +119 -0
- package/skills/stageflow-setup/SKILL.md +110 -0
- package/dist/ui/assets/index-CFSzDZje.js +0 -118
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveSkillByName } from "../config/listSkills.js";
|
|
2
|
-
import { resolveForkEmitContext } from "../config/resolveForkEmitContext.js";
|
|
2
|
+
import { resolveCloneEmitContext, resolveForkEmitContext } from "../config/resolveForkEmitContext.js";
|
|
3
3
|
import { buildCompletedEnvelopesFromRun, resolvePriorEnvelope, } from "./envelopeRouting.js";
|
|
4
4
|
import { resumeSessionFilePath } from "./stageAttemptContext.js";
|
|
5
5
|
import { buildStageRoots, rootsForStageWorker, withResolvedAuthPath, } from "./stageRoots.js";
|
|
@@ -39,40 +39,48 @@ async function openStageWithOperatorCatalog(agent, input, catalog) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
function resolveAttemptRoots(input) {
|
|
42
|
+
function resolveAttemptRoots(input, stageId) {
|
|
43
43
|
const baseRoots = input.roots ??
|
|
44
44
|
(input.workerRoots
|
|
45
|
-
? rootsForStageWorker(input.workspaceDir,
|
|
46
|
-
: buildStageRoots(input.workspaceDir,
|
|
45
|
+
? rootsForStageWorker(input.workspaceDir, stageId, input.stage.model, input.checkoutRoot, input.attemptCtx)
|
|
46
|
+
: buildStageRoots(input.workspaceDir, stageId, input.checkoutRoot, input.attemptCtx));
|
|
47
47
|
return input.factoryCwd !== undefined
|
|
48
48
|
? withResolvedAuthPath(baseRoots, input.factoryCwd)
|
|
49
49
|
: baseRoots;
|
|
50
50
|
}
|
|
51
51
|
export async function openStageAttempt(input) {
|
|
52
|
+
const stageId = input.stageId ?? input.stage.id;
|
|
53
|
+
const definitionId = input.stage.id;
|
|
52
54
|
const completedEnvelopes = input.completedEnvelopes ??
|
|
53
|
-
(await buildCompletedEnvelopesFromRun(input.store, input.runId));
|
|
55
|
+
(await buildCompletedEnvelopesFromRun(input.store, input.runId, undefined, input.dag));
|
|
54
56
|
const priorResult = await resolvePriorEnvelope({
|
|
55
57
|
dag: input.dag,
|
|
56
|
-
stageId
|
|
58
|
+
stageId,
|
|
57
59
|
completedEnvelopes,
|
|
58
60
|
store: input.store,
|
|
59
61
|
runId: input.runId,
|
|
60
62
|
});
|
|
61
63
|
if (!priorResult.ok)
|
|
62
64
|
return priorResult;
|
|
63
|
-
const roots = resolveAttemptRoots(input);
|
|
65
|
+
const roots = resolveAttemptRoots(input, stageId);
|
|
64
66
|
const attempt = input.attemptCtx?.attempt ?? 1;
|
|
65
67
|
const resumeToken = input.resumeToken ??
|
|
66
|
-
resumeSessionFilePath(input.workspaceDir,
|
|
67
|
-
const forkEmitContext = resolveForkEmitContext(input.dag,
|
|
68
|
+
resumeSessionFilePath(input.workspaceDir, stageId, attempt);
|
|
69
|
+
const forkEmitContext = resolveForkEmitContext(input.dag, definitionId);
|
|
70
|
+
const cloneEmitContext = resolveCloneEmitContext(input.dag, definitionId);
|
|
68
71
|
const opened = await openStageWithOperatorCatalog(input.agent, {
|
|
69
72
|
roots,
|
|
70
73
|
stage: input.stage,
|
|
74
|
+
stageId,
|
|
71
75
|
task: input.task,
|
|
72
76
|
priorEnvelope: priorResult.prior,
|
|
77
|
+
...(priorResult.joinPriors !== undefined
|
|
78
|
+
? { priorEnvelopes: priorResult.joinPriors }
|
|
79
|
+
: {}),
|
|
73
80
|
resumeToken,
|
|
74
81
|
onActivity: input.onActivity,
|
|
75
82
|
forkEmitContext,
|
|
83
|
+
cloneEmitContext,
|
|
76
84
|
}, input.operatorCatalog);
|
|
77
85
|
if (!opened.ok)
|
|
78
86
|
return opened;
|
|
@@ -114,28 +114,29 @@ export async function runStageYieldLoop(options) {
|
|
|
114
114
|
}
|
|
115
115
|
export async function runStage(options) {
|
|
116
116
|
const { agent, store, runId, stage, task, dag, checkoutRoot, workspaceDir, hitl, skipStarted, existingHandle, workerMode, roots: rootsOverride, attemptCtx, factoryCwd, operatorCatalog, completedEnvelopes, skipGates, } = options;
|
|
117
|
+
const stageId = options.stageId ?? stage.id;
|
|
117
118
|
const attemptOpt = attemptCtx?.eventOptions();
|
|
118
119
|
const baseRoots = rootsOverride ??
|
|
119
|
-
buildStageRoots(workspaceDir ?? store.getWorkspaceDir(runId),
|
|
120
|
+
buildStageRoots(workspaceDir ?? store.getWorkspaceDir(runId), stageId, checkoutRoot, attemptCtx);
|
|
120
121
|
const roots = factoryCwd !== undefined
|
|
121
122
|
? withResolvedAuthPath(baseRoots, factoryCwd)
|
|
122
123
|
: baseRoots;
|
|
123
124
|
const attempt = attemptCtx?.attempt ?? 1;
|
|
124
|
-
await ensureStageExecutionForAttempt(store, runId,
|
|
125
|
-
await store.ensureAttemptWorkspace(runId,
|
|
125
|
+
await ensureStageExecutionForAttempt(store, runId, stageId, attempt);
|
|
126
|
+
await store.ensureAttemptWorkspace(runId, stageId, attempt);
|
|
126
127
|
if (!skipStarted) {
|
|
127
128
|
await appendLifecycleEventAndPatchExecution({
|
|
128
129
|
store,
|
|
129
130
|
runId,
|
|
130
|
-
stageId
|
|
131
|
+
stageId,
|
|
131
132
|
event: { event: "started" },
|
|
132
133
|
attemptCtx,
|
|
133
134
|
});
|
|
134
135
|
}
|
|
135
|
-
console.error(`Running stage ${
|
|
136
|
+
console.error(`Running stage ${stageId} (${stage.model})...`);
|
|
136
137
|
let activityChain = Promise.resolve();
|
|
137
138
|
const enqueueActivity = (event) => {
|
|
138
|
-
activityChain = activityChain.then(() => store.appendStageEvent(runId,
|
|
139
|
+
activityChain = activityChain.then(() => store.appendStageEvent(runId, stageId, event, attemptOpt));
|
|
139
140
|
};
|
|
140
141
|
let handle;
|
|
141
142
|
if (existingHandle) {
|
|
@@ -147,12 +148,13 @@ export async function runStage(options) {
|
|
|
147
148
|
store,
|
|
148
149
|
runId,
|
|
149
150
|
stage,
|
|
151
|
+
stageId,
|
|
150
152
|
task,
|
|
151
153
|
dag: dag ?? {
|
|
152
154
|
nodes: [
|
|
153
|
-
{ id:
|
|
155
|
+
{ id: stageId, needs: null, ancestors: [], stageIndex: 0 },
|
|
154
156
|
],
|
|
155
|
-
roots: [
|
|
157
|
+
roots: [stageId],
|
|
156
158
|
childrenOf: {},
|
|
157
159
|
},
|
|
158
160
|
checkoutRoot,
|
|
@@ -170,11 +172,11 @@ export async function runStage(options) {
|
|
|
170
172
|
await appendLifecycleEventAndPatchExecution({
|
|
171
173
|
store,
|
|
172
174
|
runId,
|
|
173
|
-
stageId
|
|
175
|
+
stageId,
|
|
174
176
|
event: { event: "failed", reason: opened.reason },
|
|
175
177
|
attemptCtx,
|
|
176
178
|
});
|
|
177
|
-
console.error(`Stage ${
|
|
179
|
+
console.error(`Stage ${stageId} failed: ${opened.reason}`);
|
|
178
180
|
return { ok: false, reason: opened.reason };
|
|
179
181
|
}
|
|
180
182
|
handle = opened.handle;
|
|
@@ -184,7 +186,7 @@ export async function runStage(options) {
|
|
|
184
186
|
outcome = await runStageYieldLoop({
|
|
185
187
|
handle,
|
|
186
188
|
runId,
|
|
187
|
-
stageId
|
|
189
|
+
stageId,
|
|
188
190
|
hitl,
|
|
189
191
|
workerMode,
|
|
190
192
|
store: workerMode ? store : undefined,
|
|
@@ -195,15 +197,15 @@ export async function runStage(options) {
|
|
|
195
197
|
catch (err) {
|
|
196
198
|
const reason = err instanceof Error ? err.message : String(err);
|
|
197
199
|
await activityChain;
|
|
198
|
-
hitl?.clearLiveWait(runId,
|
|
200
|
+
hitl?.clearLiveWait(runId, stageId);
|
|
199
201
|
await appendLifecycleEventAndPatchExecution({
|
|
200
202
|
store,
|
|
201
203
|
runId,
|
|
202
|
-
stageId
|
|
204
|
+
stageId,
|
|
203
205
|
event: { event: "failed", reason },
|
|
204
206
|
attemptCtx,
|
|
205
207
|
});
|
|
206
|
-
console.error(`Stage ${
|
|
208
|
+
console.error(`Stage ${stageId} failed: ${reason}`);
|
|
207
209
|
await handle.close().catch(() => undefined);
|
|
208
210
|
return { ok: false, reason };
|
|
209
211
|
}
|
|
@@ -216,7 +218,7 @@ export async function runStage(options) {
|
|
|
216
218
|
return await finalizeStageResult({
|
|
217
219
|
store,
|
|
218
220
|
runId,
|
|
219
|
-
stageId
|
|
221
|
+
stageId,
|
|
220
222
|
result: outcome,
|
|
221
223
|
activityChain,
|
|
222
224
|
attemptCtx,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PiAgentAdapter } from "../agent/piAdapter.js";
|
|
2
|
+
import { definitionIdForInstance } from "../runstore/stageInstanceId.js";
|
|
2
3
|
import { createRunStore } from "../runstore/createStore.js";
|
|
3
4
|
import { loadRunContext } from "./resumeReconstruct.js";
|
|
4
5
|
import { bindPiAgentDirEnv, rootsForStageWorker, withResolvedAuthPath, } from "./stageRoots.js";
|
|
@@ -9,13 +10,15 @@ import { outcomeToWorkerResult, STAGE_WORKER_EXIT, } from "./stageWorkerProtocol
|
|
|
9
10
|
export async function runStageWorker(input) {
|
|
10
11
|
const store = createRunStore({ rootDir: input.rootDir });
|
|
11
12
|
const { meta, task, loaded } = await loadRunContext(store, input.runId, input.rootDir);
|
|
12
|
-
const
|
|
13
|
+
const definitionId = definitionIdForInstance(meta.pipeline_dag, input.stageId);
|
|
14
|
+
const stage = loaded.stages.find((s) => s.id === definitionId);
|
|
13
15
|
if (!stage) {
|
|
14
16
|
return {
|
|
15
17
|
ok: false,
|
|
16
18
|
reason: `Stage ${input.stageId} not in pipeline ${meta.pipeline_id}`,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
21
|
+
const dag = meta.pipeline_dag ?? loaded.dag;
|
|
19
22
|
const agent = new PiAgentAdapter();
|
|
20
23
|
const workspaceDir = store.getWorkspaceDir(input.runId);
|
|
21
24
|
const checkoutRoot = meta.checkout_root;
|
|
@@ -32,8 +35,9 @@ export async function runStageWorker(input) {
|
|
|
32
35
|
store,
|
|
33
36
|
runId: input.runId,
|
|
34
37
|
stage,
|
|
38
|
+
stageId: input.stageId,
|
|
35
39
|
task,
|
|
36
|
-
dag
|
|
40
|
+
dag,
|
|
37
41
|
checkoutRoot,
|
|
38
42
|
workspaceDir,
|
|
39
43
|
factoryCwd: input.rootDir,
|
|
@@ -54,12 +58,14 @@ export async function runStageWorker(input) {
|
|
|
54
58
|
store,
|
|
55
59
|
runId: input.runId,
|
|
56
60
|
stage,
|
|
61
|
+
stageId: input.stageId,
|
|
57
62
|
task,
|
|
58
|
-
dag
|
|
63
|
+
dag,
|
|
59
64
|
checkoutRoot,
|
|
60
65
|
workspaceDir,
|
|
61
66
|
skipStarted: true,
|
|
62
67
|
existingHandle: opened.handle,
|
|
68
|
+
workerMode: true,
|
|
63
69
|
roots,
|
|
64
70
|
attemptCtx,
|
|
65
71
|
factoryCwd: input.rootDir,
|
|
@@ -73,8 +79,9 @@ export async function runStageWorker(input) {
|
|
|
73
79
|
store,
|
|
74
80
|
runId: input.runId,
|
|
75
81
|
stage,
|
|
82
|
+
stageId: input.stageId,
|
|
76
83
|
task,
|
|
77
|
-
dag
|
|
84
|
+
dag,
|
|
78
85
|
checkoutRoot,
|
|
79
86
|
workspaceDir,
|
|
80
87
|
workerMode: true,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AgentPort } from "../agent/port.js";
|
|
2
|
+
import type { ProviderAuthContext } from "../agent/providerAuth.js";
|
|
3
|
+
import { type RunStoreKind } from "../runstore/createStore.js";
|
|
4
|
+
import type { RunStore } from "../runstore/port.js";
|
|
5
|
+
import { RunManager } from "../runtime/runManager.js";
|
|
6
|
+
import { type RunChangeBus } from "../runtime/runChangeBus.js";
|
|
7
|
+
import { type McpHttpHandler } from "../mcp/server.js";
|
|
8
|
+
export type StageflowHostOptions = {
|
|
9
|
+
agent: AgentPort;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
agentDir?: string;
|
|
12
|
+
rootDir?: string;
|
|
13
|
+
store?: RunStore;
|
|
14
|
+
storeKind?: RunStoreKind;
|
|
15
|
+
maxConcurrent?: number;
|
|
16
|
+
providerAuthContext?: ProviderAuthContext;
|
|
17
|
+
mcpStateless?: boolean;
|
|
18
|
+
runChangeBus?: RunChangeBus;
|
|
19
|
+
};
|
|
20
|
+
export type StageflowHostBootstrap = {
|
|
21
|
+
cwd: string;
|
|
22
|
+
agentDir: string;
|
|
23
|
+
rootDir: string;
|
|
24
|
+
isGitProject: boolean;
|
|
25
|
+
store: RunStore;
|
|
26
|
+
manager: RunManager;
|
|
27
|
+
runChangeBus: RunChangeBus;
|
|
28
|
+
mcpStateless: boolean;
|
|
29
|
+
providerAuthContext: ProviderAuthContext | undefined;
|
|
30
|
+
mcpHandler: McpHttpHandler;
|
|
31
|
+
};
|
|
32
|
+
export declare function bootstrapStageflowHost(options: StageflowHostOptions): Promise<StageflowHostBootstrap>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { createRunStore } from "../runstore/createStore.js";
|
|
3
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
4
|
+
import { findProjectRoot } from "../project/findProjectRoot.js";
|
|
5
|
+
import { RunManager } from "../runtime/runManager.js";
|
|
6
|
+
import { createRunChangeBus, getRunChangeBusFromWrappedStore, isRunStoreWrapped, wrapRunStoreWithChangeBus, } from "../runtime/runChangeBus.js";
|
|
7
|
+
import { createMcpHttpHandler, resolveMcpStateless, } from "../mcp/server.js";
|
|
8
|
+
export async function bootstrapStageflowHost(options) {
|
|
9
|
+
const invocationCwd = options.cwd ?? process.cwd();
|
|
10
|
+
const ctx = await resolveStageflowContext(invocationCwd);
|
|
11
|
+
const cwd = ctx.invocationCwd;
|
|
12
|
+
const agentDir = options.agentDir ?? getAgentDir();
|
|
13
|
+
const rootDir = options.rootDir ?? ctx.projectRoot;
|
|
14
|
+
const isGitProject = options.rootDir !== undefined
|
|
15
|
+
? findProjectRoot(rootDir) !== null
|
|
16
|
+
: ctx.isGitProject;
|
|
17
|
+
const rawStore = options.store ??
|
|
18
|
+
createRunStore({ rootDir, kind: options.storeKind });
|
|
19
|
+
const boundBus = isRunStoreWrapped(rawStore)
|
|
20
|
+
? getRunChangeBusFromWrappedStore(rawStore)
|
|
21
|
+
: undefined;
|
|
22
|
+
if (boundBus !== undefined &&
|
|
23
|
+
options.runChangeBus !== undefined &&
|
|
24
|
+
options.runChangeBus !== boundBus) {
|
|
25
|
+
throw new Error("runChangeBus does not match the bus already bound to the provided store");
|
|
26
|
+
}
|
|
27
|
+
const runChangeBus = options.runChangeBus ?? boundBus ?? createRunChangeBus();
|
|
28
|
+
const store = wrapRunStoreWithChangeBus(rawStore, runChangeBus);
|
|
29
|
+
const manager = new RunManager({
|
|
30
|
+
agent: options.agent,
|
|
31
|
+
cwd,
|
|
32
|
+
projectRoot: rootDir,
|
|
33
|
+
isGitProject,
|
|
34
|
+
store,
|
|
35
|
+
maxConcurrent: options.maxConcurrent,
|
|
36
|
+
operatorCatalog: { cwd, agentDir },
|
|
37
|
+
});
|
|
38
|
+
await manager.attachWaitingStages();
|
|
39
|
+
await manager.reconcileOrphanedStages();
|
|
40
|
+
const mcpStateless = resolveMcpStateless({
|
|
41
|
+
mcpStateless: options.mcpStateless,
|
|
42
|
+
});
|
|
43
|
+
const mcpHandler = createMcpHttpHandler({ manager, store, cwd, runChangeBus }, { mcpStateless });
|
|
44
|
+
return {
|
|
45
|
+
cwd,
|
|
46
|
+
agentDir,
|
|
47
|
+
rootDir,
|
|
48
|
+
isGitProject,
|
|
49
|
+
store,
|
|
50
|
+
manager,
|
|
51
|
+
runChangeBus,
|
|
52
|
+
mcpStateless,
|
|
53
|
+
providerAuthContext: options.providerAuthContext,
|
|
54
|
+
mcpHandler,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse, type Server } from "node:http";
|
|
2
|
+
import type { RunStore } from "../runstore/port.js";
|
|
3
|
+
import type { RunManager } from "../runtime/runManager.js";
|
|
4
|
+
import type { RunChangeBus } from "../runtime/runChangeBus.js";
|
|
5
|
+
import type { StageflowHostBootstrap } from "./bootstrap.js";
|
|
6
|
+
export declare const DEFAULT_PORT = 3847;
|
|
7
|
+
export declare function json(res: ServerResponse, status: number, body: unknown): void;
|
|
8
|
+
export type HttpHostRouteContext = {
|
|
9
|
+
req: IncomingMessage;
|
|
10
|
+
res: ServerResponse;
|
|
11
|
+
url: URL;
|
|
12
|
+
pathname: string;
|
|
13
|
+
method: string;
|
|
14
|
+
json: typeof json;
|
|
15
|
+
boot: StageflowHostBootstrap;
|
|
16
|
+
};
|
|
17
|
+
export type CreateHttpHostOptions = {
|
|
18
|
+
boot: StageflowHostBootstrap;
|
|
19
|
+
host: string;
|
|
20
|
+
port: number;
|
|
21
|
+
routes: (ctx: HttpHostRouteContext) => Promise<boolean | void>;
|
|
22
|
+
};
|
|
23
|
+
export type HttpHostEnvelope = {
|
|
24
|
+
server: Server;
|
|
25
|
+
port: number;
|
|
26
|
+
host: string;
|
|
27
|
+
url: string;
|
|
28
|
+
mcpUrl: string;
|
|
29
|
+
manager: RunManager;
|
|
30
|
+
store: RunStore;
|
|
31
|
+
runChangeBus: RunChangeBus;
|
|
32
|
+
mcpStateless: boolean;
|
|
33
|
+
};
|
|
34
|
+
export declare function createHttpHost(options: CreateHttpHostOptions): Promise<HttpHostEnvelope>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createServer, } from "node:http";
|
|
2
|
+
import { localhostHostValidation, localhostOriginValidation, } from "@modelcontextprotocol/node";
|
|
3
|
+
export const DEFAULT_PORT = 3847;
|
|
4
|
+
export function json(res, status, body) {
|
|
5
|
+
const payload = JSON.stringify(body);
|
|
6
|
+
res.writeHead(status, {
|
|
7
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
8
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
9
|
+
});
|
|
10
|
+
res.end(payload);
|
|
11
|
+
}
|
|
12
|
+
export async function createHttpHost(options) {
|
|
13
|
+
const { boot, host, port, routes } = options;
|
|
14
|
+
const validateMcpHost = localhostHostValidation();
|
|
15
|
+
const validateMcpOrigin = localhostOriginValidation();
|
|
16
|
+
const server = createServer(async (req, res) => {
|
|
17
|
+
const method = req.method ?? "GET";
|
|
18
|
+
const url = new URL(req.url ?? "/", `http://${host}:${port}`);
|
|
19
|
+
const pathname = url.pathname;
|
|
20
|
+
if (pathname === "/mcp") {
|
|
21
|
+
if (!validateMcpHost(req, res) || !validateMcpOrigin(req, res)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
await boot.mcpHandler.handle(req, res);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (!res.headersSent) {
|
|
29
|
+
res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
|
|
30
|
+
res.end(err instanceof Error ? err.message : String(err));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const handled = await routes({
|
|
36
|
+
req,
|
|
37
|
+
res,
|
|
38
|
+
url,
|
|
39
|
+
pathname,
|
|
40
|
+
method,
|
|
41
|
+
json,
|
|
42
|
+
boot,
|
|
43
|
+
});
|
|
44
|
+
if (handled)
|
|
45
|
+
return;
|
|
46
|
+
json(res, 404, { error: "Not found" });
|
|
47
|
+
});
|
|
48
|
+
server.requestTimeout = 0;
|
|
49
|
+
server.on("close", () => {
|
|
50
|
+
void boot.mcpHandler.close();
|
|
51
|
+
});
|
|
52
|
+
await new Promise((resolve, reject) => {
|
|
53
|
+
server.listen(port, host, () => resolve());
|
|
54
|
+
server.on("error", reject);
|
|
55
|
+
});
|
|
56
|
+
const address = server.address();
|
|
57
|
+
const boundPort = address && typeof address !== "string" ? address.port : port;
|
|
58
|
+
const url = `http://${host}:${boundPort}`;
|
|
59
|
+
return {
|
|
60
|
+
server,
|
|
61
|
+
port: boundPort,
|
|
62
|
+
host,
|
|
63
|
+
url,
|
|
64
|
+
mcpUrl: `${url}/mcp`,
|
|
65
|
+
manager: boot.manager,
|
|
66
|
+
store: boot.store,
|
|
67
|
+
runChangeBus: boot.runChangeBus,
|
|
68
|
+
mcpStateless: boot.mcpStateless,
|
|
69
|
+
};
|
|
70
|
+
}
|
package/dist/server/http.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { type Server } from "node:http";
|
|
2
1
|
import type { AgentPort } from "../agent/port.js";
|
|
3
2
|
import { type ProviderAuthContext } from "../agent/providerAuth.js";
|
|
4
|
-
import {
|
|
3
|
+
import type { RunStoreKind } from "../runstore/createStore.js";
|
|
5
4
|
import type { RunStore } from "../runstore/port.js";
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import type { RunChangeBus } from "../runtime/runChangeBus.js";
|
|
6
|
+
import { DEFAULT_PORT, type HttpHostEnvelope } from "./createHttpHost.js";
|
|
8
7
|
export type UiServerOptions = {
|
|
9
8
|
agent: AgentPort;
|
|
10
9
|
cwd?: string;
|
|
@@ -17,15 +16,9 @@ export type UiServerOptions = {
|
|
|
17
16
|
uiDistDir?: string;
|
|
18
17
|
maxConcurrent?: number;
|
|
19
18
|
providerAuthContext?: ProviderAuthContext;
|
|
19
|
+
mcpStateless?: boolean;
|
|
20
|
+
runChangeBus?: RunChangeBus;
|
|
20
21
|
};
|
|
21
22
|
export declare function defaultUiDistDir(): string;
|
|
22
|
-
export declare function startUiServer(options: UiServerOptions): Promise<
|
|
23
|
-
server: Server;
|
|
24
|
-
port: number;
|
|
25
|
-
host: string;
|
|
26
|
-
url: string;
|
|
27
|
-
mcpUrl: string;
|
|
28
|
-
manager: RunManager;
|
|
29
|
-
store: RunStore;
|
|
30
|
-
}>;
|
|
23
|
+
export declare function startUiServer(options: UiServerOptions): Promise<HttpHostEnvelope>;
|
|
31
24
|
export { DEFAULT_PORT };
|