stageflow 0.27.0 → 0.31.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 +4 -0
- package/builtin-skills/browser/SKILL.md +42 -0
- package/dist/agent/claudeAdapter.js +2 -2
- package/dist/agent/cursorProvider.d.ts +10 -0
- package/dist/agent/cursorProvider.js +83 -8
- package/dist/agent/fakeAgent.d.ts +1 -0
- package/dist/agent/fakeAgent.js +13 -0
- package/dist/agent/piAdapter.d.ts +3 -24
- package/dist/agent/piAdapter.js +49 -47
- package/dist/agent/piSessionFactory.d.ts +38 -0
- package/dist/agent/piSessionFactory.js +60 -0
- package/dist/agent/port.d.ts +2 -0
- package/dist/agent/providerAuth.d.ts +7 -3
- package/dist/agent/providerAuth.js +5 -3
- package/dist/agent/streamLogRedact.d.ts +6 -0
- package/dist/agent/streamLogRedact.js +23 -1
- package/dist/browser/anchor.d.ts +27 -0
- package/dist/browser/anchor.js +127 -0
- package/dist/browser/auditSink.d.ts +38 -0
- package/dist/browser/auditSink.js +34 -0
- package/dist/browser/browserHost.d.ts +86 -0
- package/dist/browser/browserHost.js +7 -0
- package/dist/browser/browserSweep.d.ts +14 -0
- package/dist/browser/browserSweep.js +54 -0
- package/dist/browser/browserTeardown.d.ts +56 -0
- package/dist/browser/browserTeardown.js +235 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
- package/dist/browser/fakeRemoteBrowserHost.js +34 -0
- package/dist/browser/gateHandoff.d.ts +25 -0
- package/dist/browser/gateHandoff.js +83 -0
- package/dist/browser/humanLogin.d.ts +18 -0
- package/dist/browser/humanLogin.js +34 -0
- package/dist/browser/keyProvider.d.ts +10 -0
- package/dist/browser/keyProvider.js +39 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +152 -0
- package/dist/browser/localProfileLock.d.ts +2 -0
- package/dist/browser/localProfileLock.js +218 -0
- package/dist/browser/localProfileStore.d.ts +5 -0
- package/dist/browser/localProfileStore.js +91 -0
- package/dist/browser/loginCheck.d.ts +53 -0
- package/dist/browser/loginCheck.js +147 -0
- package/dist/browser/memoryProfileLock.d.ts +2 -0
- package/dist/browser/memoryProfileLock.js +53 -0
- package/dist/browser/memoryProfileStore.d.ts +2 -0
- package/dist/browser/memoryProfileStore.js +31 -0
- package/dist/browser/navigationAudit.d.ts +16 -0
- package/dist/browser/navigationAudit.js +53 -0
- package/dist/browser/persistedEnv.d.ts +3 -0
- package/dist/browser/persistedEnv.js +15 -0
- package/dist/browser/profileLock.d.ts +40 -0
- package/dist/browser/profileLock.js +10 -0
- package/dist/browser/profileStore.d.ts +23 -0
- package/dist/browser/profileStore.js +39 -0
- package/dist/browser/runLiveness.d.ts +13 -0
- package/dist/browser/runLiveness.js +39 -0
- package/dist/browser/sitePolicy.d.ts +10 -0
- package/dist/browser/sitePolicy.js +61 -0
- package/dist/browser/stageBrowserEnv.d.ts +22 -0
- package/dist/browser/stageBrowserEnv.js +147 -0
- package/dist/browser/stageProfileLock.d.ts +17 -0
- package/dist/browser/stageProfileLock.js +39 -0
- package/dist/cli/browserCommand.d.ts +44 -0
- package/dist/cli/browserCommand.js +472 -0
- package/dist/cli/doctorCommand.js +7 -0
- package/dist/cli/hostClient.d.ts +20 -0
- package/dist/cli/hostClient.js +36 -2
- package/dist/cli/initCommand.d.ts +1 -0
- package/dist/cli/initCommand.js +23 -7
- package/dist/cli/initTemplates.d.ts +5 -2
- package/dist/cli/initTemplates.js +25 -2
- package/dist/cli/providersCommand.d.ts +1 -1
- package/dist/cli/providersCommand.js +4 -4
- package/dist/cli/runsCommand.js +6 -2
- package/dist/cli/triggerCommand.d.ts +12 -0
- package/dist/cli/triggerCommand.js +183 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +23 -6
- package/dist/cli.js +32 -3
- package/dist/config/browseCatalog.d.ts +1 -0
- package/dist/config/browseCatalog.js +5 -4
- package/dist/config/builtinSkills.d.ts +4 -0
- package/dist/config/builtinSkills.js +23 -0
- package/dist/config/catalogRelativePath.d.ts +1 -0
- package/dist/config/catalogRelativePath.js +10 -2
- package/dist/config/createTrigger.d.ts +27 -0
- package/dist/config/createTrigger.js +232 -0
- package/dist/config/draftPackage.d.ts +117 -0
- package/dist/config/draftPackage.js +550 -0
- package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
- package/dist/config/ensureCatalogScanRoot.js +73 -0
- package/dist/config/hostConfig.d.ts +2 -0
- package/dist/config/hostConfig.js +27 -0
- package/dist/config/listSkills.d.ts +2 -2
- package/dist/config/listSkills.js +17 -3
- package/dist/config/loadOutcome.d.ts +6 -0
- package/dist/config/loadPipeline.js +16 -0
- package/dist/config/loadStage.js +7 -0
- package/dist/config/loadStageflowManifest.js +25 -0
- package/dist/config/loadTrigger.d.ts +9 -0
- package/dist/config/loadTrigger.js +120 -0
- package/dist/config/multiProjectCatalog.d.ts +2 -0
- package/dist/config/multiProjectCatalog.js +26 -18
- package/dist/config/normalizePipelineStageEntry.js +8 -1
- package/dist/config/parseCompletionContract.js +6 -0
- package/dist/config/pipelineStageKeys.js +1 -0
- package/dist/config/publishDraftPackage.d.ts +7 -0
- package/dist/config/publishDraftPackage.js +25 -0
- package/dist/config/resolveCatalogContext.js +1 -1
- package/dist/config/resolvePipelineDag.js +1 -1
- package/dist/config/scanCatalogPaths.d.ts +1 -1
- package/dist/config/scanCatalogPaths.js +6 -2
- package/dist/config/stageBrowser.d.ts +12 -0
- package/dist/config/stageBrowser.js +118 -0
- package/dist/config/triggerCatalog.d.ts +10 -0
- package/dist/config/triggerCatalog.js +37 -0
- package/dist/config/validateCatalog.d.ts +13 -3
- package/dist/config/validateCatalog.js +83 -2
- package/dist/config/validateCompletionContract.js +8 -0
- package/dist/envelope/preEmitChecks.d.ts +1 -1
- package/dist/envelope/preEmitChecks.js +8 -0
- package/dist/logging/redact.d.ts +4 -0
- package/dist/logging/redact.js +17 -0
- package/dist/mcp/catalogTools.js +43 -8
- package/dist/mcp/controlTools.js +3 -1
- package/dist/mcp/deps.d.ts +2 -0
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/tools.js +2 -0
- package/dist/mcp/triggerTools.d.ts +3 -0
- package/dist/mcp/triggerTools.js +176 -0
- package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
- package/dist/operatorAgent/docsRetrieval.js +269 -0
- package/dist/operatorAgent/draftContext.d.ts +16 -0
- package/dist/operatorAgent/draftContext.js +68 -0
- package/dist/operatorAgent/fakeHost.d.ts +39 -0
- package/dist/operatorAgent/fakeHost.js +92 -0
- package/dist/operatorAgent/host.d.ts +40 -0
- package/dist/operatorAgent/host.js +179 -0
- package/dist/operatorAgent/index.d.ts +9 -0
- package/dist/operatorAgent/index.js +8 -0
- package/dist/operatorAgent/piModel.d.ts +80 -0
- package/dist/operatorAgent/piModel.js +528 -0
- package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
- package/dist/operatorAgent/proposals.d.ts +32 -0
- package/dist/operatorAgent/proposals.js +144 -0
- package/dist/operatorAgent/types.d.ts +128 -0
- package/dist/operatorAgent/types.js +1 -0
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/project/findManifestRoot.d.ts +3 -0
- package/dist/project/findManifestRoot.js +35 -0
- package/dist/project/resolveStageflowContext.d.ts +7 -0
- package/dist/project/resolveStageflowContext.js +44 -28
- package/dist/runstore/assertTmpdir.js +6 -2
- package/dist/runstore/diskUsage.d.ts +1 -1
- package/dist/runstore/diskUsage.js +2 -7
- package/dist/runstore/port.d.ts +28 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -1
- package/dist/runstore/sqlite/SqliteRunStore.js +90 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
- package/dist/runstore/sqlite/migrations/index.js +6 -0
- package/dist/runtime/answerResume.d.ts +1 -0
- package/dist/runtime/answerResume.js +3 -1
- package/dist/runtime/completionCheckRunner.d.ts +12 -1
- package/dist/runtime/completionCheckRunner.js +22 -0
- package/dist/runtime/credentialBinding.d.ts +7 -2
- package/dist/runtime/credentialBinding.js +20 -28
- package/dist/runtime/emailSource.d.ts +72 -0
- package/dist/runtime/emailSource.js +304 -0
- package/dist/runtime/githubPollSource.d.ts +86 -0
- package/dist/runtime/githubPollSource.js +284 -0
- package/dist/runtime/pipelineRunner.d.ts +4 -0
- package/dist/runtime/pipelineRunner.js +3 -0
- package/dist/runtime/pipelineScheduler.d.ts +2 -0
- package/dist/runtime/pipelineScheduler.js +86 -1
- package/dist/runtime/repositoryMaterialize.d.ts +1 -1
- package/dist/runtime/runManager.d.ts +11 -0
- package/dist/runtime/runManager.js +161 -3
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +1 -0
- package/dist/runtime/scheduleSource.d.ts +38 -0
- package/dist/runtime/scheduleSource.js +119 -0
- package/dist/runtime/settingsFile.d.ts +7 -2
- package/dist/runtime/settingsFile.js +66 -56
- package/dist/runtime/stageAttemptBootstrap.js +55 -3
- package/dist/runtime/stageEnvironment.js +1 -0
- package/dist/runtime/stageHitl.d.ts +1 -0
- package/dist/runtime/stageHitl.js +5 -1
- package/dist/runtime/stageProcessLauncher.d.ts +3 -0
- package/dist/runtime/stageProcessLauncher.js +52 -5
- package/dist/runtime/stageRunner.d.ts +5 -0
- package/dist/runtime/stageRunner.js +30 -6
- package/dist/runtime/stageStreamLog.d.ts +1 -0
- package/dist/runtime/stageStreamLog.js +2 -1
- package/dist/runtime/triggerCatalog.d.ts +6 -0
- package/dist/runtime/triggerCatalog.js +24 -0
- package/dist/runtime/triggerEventMatch.d.ts +2 -0
- package/dist/runtime/triggerEventMatch.js +6 -0
- package/dist/runtime/triggerPort.d.ts +14 -0
- package/dist/runtime/triggerPort.js +1 -0
- package/dist/runtime/triggerRunner.d.ts +17 -0
- package/dist/runtime/triggerRunner.js +89 -0
- package/dist/runtime/verifiedStageExecution.d.ts +7 -0
- package/dist/runtime/verifiedStageExecution.js +14 -0
- package/dist/runtime/webhookSignature.d.ts +1 -0
- package/dist/runtime/webhookSignature.js +12 -0
- package/dist/server/bootstrap.d.ts +6 -0
- package/dist/server/bootstrap.js +70 -0
- package/dist/server/controlToken.js +13 -0
- package/dist/server/createHttpHost.js +3 -0
- package/dist/server/healthSurfaces.js +31 -6
- package/dist/server/http.d.ts +15 -0
- package/dist/server/http.js +1251 -74
- package/dist/server/requestAuthContext.d.ts +1 -1
- package/dist/server/triggerRoutes.d.ts +15 -0
- package/dist/server/triggerRoutes.js +268 -0
- package/dist/tools/askOperator.d.ts +3 -2
- package/dist/types/completion.d.ts +3 -0
- package/dist/types/pipeline.d.ts +3 -1
- package/dist/types/preEmitCheck.d.ts +7 -0
- package/dist/types/stage.d.ts +14 -0
- package/dist/types/stageflowManifest.d.ts +3 -0
- package/dist/types/trigger.d.ts +18 -0
- package/dist/types/trigger.js +1 -0
- package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
- package/dist/ui/assets/index-p8Iou3zk.js +133 -0
- package/dist/ui/index.html +2 -2
- package/dist/workshop/autosave.d.ts +58 -0
- package/dist/workshop/autosave.js +222 -0
- package/dist/workshop/buildStore.d.ts +79 -0
- package/dist/workshop/buildStore.js +326 -0
- package/dist/workshop/chatTurn.d.ts +232 -0
- package/dist/workshop/chatTurn.js +475 -0
- package/dist/workshop/modelSettings.d.ts +18 -0
- package/dist/workshop/modelSettings.js +44 -0
- package/dist/workshop/sessionStore.d.ts +61 -0
- package/dist/workshop/sessionStore.js +254 -0
- package/dist/workshop/workshopPin.d.ts +72 -0
- package/dist/workshop/workshopPin.js +193 -0
- package/examples/README.md +2 -1
- package/examples/browser-session/README.md +77 -0
- package/examples/browser-session/browser-session.pipeline.yaml +50 -0
- package/examples/browser-session/browser-session.task.yaml +6 -0
- package/examples/browser-session/check-login.yaml +22 -0
- package/examples/browser-session/fixture-server.mjs +64 -0
- package/examples/browser-session/human-login.yaml +31 -0
- package/examples/browser-session/merge.yaml +36 -0
- package/examples/browser-session/work-a.yaml +41 -0
- package/examples/browser-session/work-b.yaml +41 -0
- package/examples/conditional-fork/decide.yaml +1 -1
- package/examples/github-release/README.md +17 -3
- package/examples/stage-mcp/README.md +3 -2
- package/examples/stageflow.yaml +51 -0
- package/package.json +9 -4
- package/skills/stageflow/references/control-surface.md +3 -1
- package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
- package/skills/stageflow-run/SKILL.md +2 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +3 -1
- package/skills/stageflow-setup/SKILL.md +11 -0
- package/dist/ui/assets/index-B6hlSXXn.js +0 -120
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RunStore } from "../runstore/port.js";
|
|
2
|
+
import type { RunManager, StartRunResult } from "./runManager.js";
|
|
3
|
+
import type { TaskFile } from "../types/task.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolves a trigger id to its `*.trigger.yaml` catalog definition, resolves its
|
|
6
|
+
* `pipeline`/`task` refs to catalog files, and starts a real run through
|
|
7
|
+
* `RunManager.startRun` — the same internal entry point the CLI/MCP/console use.
|
|
8
|
+
* On a successful start, records the fire on the trigger's store row.
|
|
9
|
+
*
|
|
10
|
+
* `task` presence on the trigger definition is the either/or mode switch: a
|
|
11
|
+
* catalog-mode trigger (`task` set) rejects a supplied `options.task`; a
|
|
12
|
+
* dynamic-mode trigger (`task` absent) requires one.
|
|
13
|
+
*/
|
|
14
|
+
export declare function fireTrigger(triggerId: string, store: RunStore, runManager: RunManager, options?: {
|
|
15
|
+
cwd?: string;
|
|
16
|
+
task?: TaskFile;
|
|
17
|
+
}): Promise<StartRunResult>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { catalogContextFromStageflow } from "../config/resolveCatalogContext.js";
|
|
3
|
+
import { getCatalogScanPaths } from "../config/browseCatalog.js";
|
|
4
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
5
|
+
import { findCatalogPathById, findTriggerDefinition } from "./triggerCatalog.js";
|
|
6
|
+
/**
|
|
7
|
+
* Resolves a trigger id to its `*.trigger.yaml` catalog definition, resolves its
|
|
8
|
+
* `pipeline`/`task` refs to catalog files, and starts a real run through
|
|
9
|
+
* `RunManager.startRun` — the same internal entry point the CLI/MCP/console use.
|
|
10
|
+
* On a successful start, records the fire on the trigger's store row.
|
|
11
|
+
*
|
|
12
|
+
* `task` presence on the trigger definition is the either/or mode switch: a
|
|
13
|
+
* catalog-mode trigger (`task` set) rejects a supplied `options.task`; a
|
|
14
|
+
* dynamic-mode trigger (`task` absent) requires one.
|
|
15
|
+
*/
|
|
16
|
+
export async function fireTrigger(triggerId, store, runManager, options = {}) {
|
|
17
|
+
const cwd = options.cwd ?? process.cwd();
|
|
18
|
+
const ctx = catalogContextFromStageflow(await resolveStageflowContext(cwd));
|
|
19
|
+
const scanPaths = await getCatalogScanPaths(ctx);
|
|
20
|
+
if (!scanPaths) {
|
|
21
|
+
return { ok: false, reason: "No Stageflow catalog found", status: 404 };
|
|
22
|
+
}
|
|
23
|
+
const found = await findTriggerDefinition(scanPaths.triggerPaths, triggerId);
|
|
24
|
+
if (!found) {
|
|
25
|
+
return { ok: false, reason: `Trigger not found: ${triggerId}`, status: 404 };
|
|
26
|
+
}
|
|
27
|
+
const { path: triggerPath, definition: trigger } = found;
|
|
28
|
+
if (!trigger.enabled) {
|
|
29
|
+
return { ok: false, reason: `Trigger is disabled: ${triggerId}`, status: 409 };
|
|
30
|
+
}
|
|
31
|
+
const suppliedTask = options.task;
|
|
32
|
+
if (trigger.task !== undefined && suppliedTask !== undefined) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
reason: `Trigger "${triggerId}" already has a catalog task "${trigger.task}"; task override is not allowed`,
|
|
36
|
+
status: 422,
|
|
37
|
+
code: "trigger.task_override_not_allowed",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (trigger.task === undefined && suppliedTask === undefined) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
reason: `Trigger "${triggerId}" has no catalog task; a task must be supplied when firing`,
|
|
44
|
+
status: 422,
|
|
45
|
+
code: "trigger.task_required",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const pipelinePath = await findCatalogPathById(scanPaths.pipelinePaths, trigger.pipeline);
|
|
49
|
+
if (!pipelinePath) {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
reason: `Trigger "${triggerId}" references unknown pipeline "${trigger.pipeline}"`,
|
|
53
|
+
status: 400,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
let taskInput;
|
|
57
|
+
if (trigger.task !== undefined) {
|
|
58
|
+
const taskPath = await findCatalogPathById(scanPaths.taskPaths, trigger.task);
|
|
59
|
+
if (!taskPath) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
reason: `Trigger "${triggerId}" references unknown task "${trigger.task}"`,
|
|
63
|
+
status: 400,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
taskInput = taskPath;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
taskInput = suppliedTask;
|
|
70
|
+
}
|
|
71
|
+
const projectRoot = ctx.projectRoot ?? undefined;
|
|
72
|
+
const definitionRef = projectRoot !== undefined
|
|
73
|
+
? path.relative(projectRoot, triggerPath).replace(/\\/g, "/")
|
|
74
|
+
: triggerPath;
|
|
75
|
+
await store.upsertTrigger({
|
|
76
|
+
id: triggerId,
|
|
77
|
+
definitionRef,
|
|
78
|
+
enabled: trigger.enabled,
|
|
79
|
+
});
|
|
80
|
+
const started = await runManager.startRun({
|
|
81
|
+
pipeline: pipelinePath,
|
|
82
|
+
task: taskInput,
|
|
83
|
+
...(projectRoot !== undefined ? { projectRoot } : {}),
|
|
84
|
+
});
|
|
85
|
+
if (started.ok) {
|
|
86
|
+
await store.recordTriggerFired(triggerId, started.runId);
|
|
87
|
+
}
|
|
88
|
+
return started;
|
|
89
|
+
}
|
|
@@ -2,6 +2,8 @@ import type { StageRunResult } from "../agent/port.js";
|
|
|
2
2
|
import type { RunStore } from "../runstore/port.js";
|
|
3
3
|
import type { ResolvedPipelineDag } from "../types/pipeline.js";
|
|
4
4
|
import type { StageConfig } from "../types/stage.js";
|
|
5
|
+
import type { BrowserRunner } from "../browser/browserHost.js";
|
|
6
|
+
import { type LoginCheckConfig, type LoginCheckOptions } from "../browser/loginCheck.js";
|
|
5
7
|
import type { StageRoots } from "./stageRoots.js";
|
|
6
8
|
/** Sidecar written before completion command checks so they can inspect fork_choice. */
|
|
7
9
|
export declare const COMPLETION_CANDIDATE_ENVELOPE_FILENAME = "completion-candidate-envelope.json";
|
|
@@ -29,4 +31,9 @@ export declare function createVerifiedStageExecution(options: {
|
|
|
29
31
|
dag?: ResolvedPipelineDag;
|
|
30
32
|
roots: StageRoots;
|
|
31
33
|
commandEnv?: NodeJS.ProcessEnv;
|
|
34
|
+
browserLogin?: {
|
|
35
|
+
check: LoginCheckConfig;
|
|
36
|
+
runner?: BrowserRunner;
|
|
37
|
+
options?: LoginCheckOptions;
|
|
38
|
+
};
|
|
32
39
|
}): VerifiedStageExecution;
|
|
@@ -9,6 +9,9 @@ import { COMPLETION_VERIFICATION_FAILURE_PREFIX, } from "../types/completion.js"
|
|
|
9
9
|
import { runCompletionContract, } from "./completionCheckRunner.js";
|
|
10
10
|
import { readCompletionCheckoutBaseline, writeCompletionCheckoutBaseline, } from "./completionCheckoutBaseline.js";
|
|
11
11
|
import { createGitCheckoutCapability } from "./gitCheckoutCapability.js";
|
|
12
|
+
import { readStagePersistedBrowserEnv } from "../browser/stageBrowserEnv.js";
|
|
13
|
+
import { defaultBrowserRunner } from "../browser/browserTeardown.js";
|
|
14
|
+
import { runLoginCheck, } from "../browser/loginCheck.js";
|
|
12
15
|
/** Sidecar written before completion command checks so they can inspect fork_choice. */
|
|
13
16
|
export const COMPLETION_CANDIDATE_ENVELOPE_FILENAME = "completion-candidate-envelope.json";
|
|
14
17
|
export function completionVerificationFailureReason(failedCheckIds) {
|
|
@@ -88,6 +91,17 @@ export function createVerifiedStageExecution(options) {
|
|
|
88
91
|
checkoutBefore,
|
|
89
92
|
checkoutError,
|
|
90
93
|
checklistAttestations: candidate.envelope.checklist_attestations,
|
|
94
|
+
...(options.browserLogin !== undefined
|
|
95
|
+
? {
|
|
96
|
+
browserLogin: async () => {
|
|
97
|
+
const env = await readStagePersistedBrowserEnv(roots.runWorkspaceDir, stageId);
|
|
98
|
+
if (env === undefined) {
|
|
99
|
+
throw new Error("browser session env is missing for this stage");
|
|
100
|
+
}
|
|
101
|
+
return runLoginCheck(env, options.browserLogin.check, options.browserLogin.runner ?? defaultBrowserRunner, options.browserLogin.options);
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
: {}),
|
|
91
105
|
onCheckStart: async (check) => {
|
|
92
106
|
await store.upsertVerificationCheckResult(runId, stageId, {
|
|
93
107
|
check_id: check.id,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function verifyHmacSignature(rawBody: Buffer, headerValue: string, secret: string, scheme: "hex" | "base64"): boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
export function verifyHmacSignature(rawBody, headerValue, secret, scheme) {
|
|
3
|
+
const eqIndex = headerValue.indexOf("=");
|
|
4
|
+
const hasPrefix = eqIndex !== -1 && eqIndex < headerValue.length - 1;
|
|
5
|
+
const provided = hasPrefix ? headerValue.slice(eqIndex + 1) : headerValue;
|
|
6
|
+
const computed = crypto.createHmac("sha256", secret).update(rawBody).digest(scheme);
|
|
7
|
+
const providedBuffer = Buffer.from(provided, scheme);
|
|
8
|
+
const computedBuffer = Buffer.from(computed, scheme);
|
|
9
|
+
if (providedBuffer.length !== computedBuffer.length)
|
|
10
|
+
return false;
|
|
11
|
+
return crypto.timingSafeEqual(providedBuffer, computedBuffer);
|
|
12
|
+
}
|
|
@@ -45,6 +45,12 @@ export type StageflowHostBootstrap = {
|
|
|
45
45
|
/** Periodic retention GC handle when enabled; already `.unref()`'d. */
|
|
46
46
|
gcInterval?: NodeJS.Timeout;
|
|
47
47
|
stopGcInterval: () => void;
|
|
48
|
+
/** Stops the schedule-trigger tick loop started at boot. */
|
|
49
|
+
stopScheduleSource: () => void;
|
|
50
|
+
/** Stops the GitHub poll-trigger loop started at boot. */
|
|
51
|
+
stopGithubPollSource: () => void;
|
|
52
|
+
/** Stops the email-trigger IMAP/IDLE listeners started at boot. */
|
|
53
|
+
stopEmailSource: () => void;
|
|
48
54
|
/** Filesystem classification for `$STAGEFLOW_HOME` (Slot 8). */
|
|
49
55
|
storeFilesystem?: StoreFilesystemClassification;
|
|
50
56
|
/**
|
package/dist/server/bootstrap.js
CHANGED
|
@@ -12,10 +12,15 @@ import { assertStoreFilesystemSupported } from "../runstore/storeFilesystem.js";
|
|
|
12
12
|
import { assertStoreQuickCheck, isSqliteCorruptError, maybeCheckpointResidualWal, rethrowAsStoreIntegrityFailed, } from "../runstore/sqlite/applyStorePragmas.js";
|
|
13
13
|
import { RunManager } from "../runtime/runManager.js";
|
|
14
14
|
import { PI_CODING_AGENT_DIR_ENV } from "../runtime/stageRoots.js";
|
|
15
|
+
import { ScheduleSource, triggerTickIntervalMsFromEnv, } from "../runtime/scheduleSource.js";
|
|
16
|
+
import { GithubPollSource, githubPollIntervalMsFromEnv, } from "../runtime/githubPollSource.js";
|
|
17
|
+
import { EmailSource } from "../runtime/emailSource.js";
|
|
18
|
+
import { fireTrigger } from "../runtime/triggerRunner.js";
|
|
15
19
|
import { createRunChangeBus, getRunChangeBusFromWrappedStore, isRunStoreWrapped, wrapRunStoreWithChangeBus, } from "../runtime/runChangeBus.js";
|
|
16
20
|
import { createMcpHttpHandler, resolveMcpStateless, } from "../mcp/server.js";
|
|
17
21
|
import { loadHostConfig, } from "../config/hostConfig.js";
|
|
18
22
|
import { bootProviderConfig } from "../agent/bootProviderConfig.js";
|
|
23
|
+
import { readCursorApiKey } from "../agent/cursorProvider.js";
|
|
19
24
|
import { applyPendingRestoreAtBoot, } from "../runstore/restore.js";
|
|
20
25
|
import { assertTmpdirUsable } from "../runstore/assertTmpdir.js";
|
|
21
26
|
import { logger as rootLogger } from "../logging/logger.js";
|
|
@@ -76,6 +81,11 @@ export async function bootstrapStageflowHost(options) {
|
|
|
76
81
|
const ctx = await resolveStageflowContext(invocationCwd);
|
|
77
82
|
const cwd = ctx.invocationCwd;
|
|
78
83
|
ensureGlobalHome();
|
|
84
|
+
const cursorKey = readCursorApiKey(env);
|
|
85
|
+
if (cursorKey !== undefined &&
|
|
86
|
+
(env.CURSOR_API_KEY === undefined || env.CURSOR_API_KEY.trim() === "")) {
|
|
87
|
+
process.env.CURSOR_API_KEY = cursorKey;
|
|
88
|
+
}
|
|
79
89
|
assertTmpdirUsable(env);
|
|
80
90
|
const hostConfig = options.hostConfig ??
|
|
81
91
|
(options.skipHostConfig
|
|
@@ -149,6 +159,9 @@ export async function bootstrapStageflowHost(options) {
|
|
|
149
159
|
close: async () => { },
|
|
150
160
|
},
|
|
151
161
|
stopGcInterval: () => { },
|
|
162
|
+
stopScheduleSource: () => { },
|
|
163
|
+
stopGithubPollSource: () => { },
|
|
164
|
+
stopEmailSource: () => { },
|
|
152
165
|
storeFilesystem,
|
|
153
166
|
serveBlocked,
|
|
154
167
|
...(hostConfig !== undefined ? { hostConfig } : {}),
|
|
@@ -231,6 +244,7 @@ export async function bootstrapStageflowHost(options) {
|
|
|
231
244
|
});
|
|
232
245
|
await manager.attachWaitingStages();
|
|
233
246
|
await manager.reconcileOrphanedStages();
|
|
247
|
+
await manager.sweepBrowserSessions().catch(() => undefined);
|
|
234
248
|
await manager.autoResumeInterruptedStages();
|
|
235
249
|
await manager.resumeStalledSchedules();
|
|
236
250
|
await manager.reenqueuePersistedQueuedRuns();
|
|
@@ -240,6 +254,58 @@ export async function bootstrapStageflowHost(options) {
|
|
|
240
254
|
if (gcInterval !== undefined)
|
|
241
255
|
clearInterval(gcInterval);
|
|
242
256
|
};
|
|
257
|
+
const scheduleSource = new ScheduleSource({
|
|
258
|
+
store,
|
|
259
|
+
cwd,
|
|
260
|
+
intervalMs: triggerTickIntervalMsFromEnv(env),
|
|
261
|
+
logError: (message) => bootLog.error("trigger.schedule_source_failed", message),
|
|
262
|
+
});
|
|
263
|
+
const onTriggerFire = async (event) => {
|
|
264
|
+
try {
|
|
265
|
+
const result = await fireTrigger(event.triggerId, store, manager, {
|
|
266
|
+
cwd,
|
|
267
|
+
task: event.task,
|
|
268
|
+
});
|
|
269
|
+
if (!result.ok) {
|
|
270
|
+
bootLog.error("trigger.fire_failed", `trigger "${event.triggerId}" fire failed: ${result.reason}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
bootLog.error("trigger.fire_failed", `trigger "${event.triggerId}" fire threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
try {
|
|
278
|
+
// Boot-time catch-up: fire once for any schedule trigger whose next_run_at
|
|
279
|
+
// already passed while the Host was down, then reschedule from now —
|
|
280
|
+
// before starting the periodic ticker so this pass never races it.
|
|
281
|
+
await scheduleSource.tick(onTriggerFire);
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
bootLog.error("trigger.schedule_catchup_failed", `boot catch-up tick failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
285
|
+
}
|
|
286
|
+
await scheduleSource.start(onTriggerFire);
|
|
287
|
+
const stopScheduleSource = () => {
|
|
288
|
+
void scheduleSource.stop();
|
|
289
|
+
};
|
|
290
|
+
const githubPollSource = new GithubPollSource({
|
|
291
|
+
store,
|
|
292
|
+
cwd,
|
|
293
|
+
intervalMs: githubPollIntervalMsFromEnv(env),
|
|
294
|
+
logError: (message) => bootLog.error("trigger.github_poll_source_failed", message),
|
|
295
|
+
});
|
|
296
|
+
await githubPollSource.start(onTriggerFire);
|
|
297
|
+
const stopGithubPollSource = () => {
|
|
298
|
+
void githubPollSource.stop();
|
|
299
|
+
};
|
|
300
|
+
const emailSource = new EmailSource({
|
|
301
|
+
store,
|
|
302
|
+
cwd,
|
|
303
|
+
logError: (message) => bootLog.error("trigger.email_source_failed", message),
|
|
304
|
+
});
|
|
305
|
+
await emailSource.start(onTriggerFire);
|
|
306
|
+
const stopEmailSource = () => {
|
|
307
|
+
void emailSource.stop();
|
|
308
|
+
};
|
|
243
309
|
const mcpStateless = resolveMcpStateless({
|
|
244
310
|
mcpStateless: options.mcpStateless,
|
|
245
311
|
});
|
|
@@ -251,6 +317,7 @@ export async function bootstrapStageflowHost(options) {
|
|
|
251
317
|
runChangeBus,
|
|
252
318
|
providerAuthContext: options.providerAuthContext,
|
|
253
319
|
projectRoot: rootDir,
|
|
320
|
+
...(providerBoot !== undefined ? { providerBoot } : {}),
|
|
254
321
|
}, { mcpStateless });
|
|
255
322
|
return {
|
|
256
323
|
a2a: await createA2aHost({
|
|
@@ -274,6 +341,9 @@ export async function bootstrapStageflowHost(options) {
|
|
|
274
341
|
...(providerBoot !== undefined ? { providerBoot } : {}),
|
|
275
342
|
...(gcInterval !== undefined ? { gcInterval } : {}),
|
|
276
343
|
stopGcInterval,
|
|
344
|
+
stopScheduleSource,
|
|
345
|
+
stopGithubPollSource,
|
|
346
|
+
stopEmailSource,
|
|
277
347
|
storeFilesystem,
|
|
278
348
|
};
|
|
279
349
|
}
|
|
@@ -158,11 +158,24 @@ export class BindRefusedError extends Error {
|
|
|
158
158
|
this.name = "BindRefusedError";
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* `POST /api/triggers/:id/webhook` is exempt from bearer auth: an external
|
|
163
|
+
* webhook sender can't present Stageflow's own control token, and the route
|
|
164
|
+
* authenticates the request itself via per-trigger HMAC signature
|
|
165
|
+
* verification instead. It still goes through `assertAllowedHttpAccess`
|
|
166
|
+
* (loopback/allowed-hosts gating) the same as every other `/api/*` route —
|
|
167
|
+
* only the bearer-token requirement is carved out here.
|
|
168
|
+
*/
|
|
169
|
+
function isTriggerWebhookRoute(method, pathname) {
|
|
170
|
+
return method === "POST" && /^\/api\/triggers\/[^/]+\/webhook$/.test(pathname);
|
|
171
|
+
}
|
|
161
172
|
export function requiredScopeFor(method, pathname) {
|
|
162
173
|
if (pathname === "/mcp")
|
|
163
174
|
return "drive";
|
|
164
175
|
if (!pathname.startsWith("/api/"))
|
|
165
176
|
return null;
|
|
177
|
+
if (isTriggerWebhookRoute(method, pathname))
|
|
178
|
+
return null;
|
|
166
179
|
if (pathname === "/api/backup" || pathname.startsWith("/api/backup/")) {
|
|
167
180
|
return "drive";
|
|
168
181
|
}
|
|
@@ -135,6 +135,9 @@ export async function createHttpHost(options) {
|
|
|
135
135
|
server.maxConnections = maxConnections;
|
|
136
136
|
server.on("close", () => {
|
|
137
137
|
boot.stopGcInterval();
|
|
138
|
+
boot.stopScheduleSource();
|
|
139
|
+
boot.stopGithubPollSource();
|
|
140
|
+
boot.stopEmailSource();
|
|
138
141
|
void boot.mcpHandler.close();
|
|
139
142
|
});
|
|
140
143
|
await new Promise((resolve, reject) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getAuthStatus, } from "../agent/providerAuth.js";
|
|
1
2
|
import { PACKAGE_VERSION, BUILD_SHA } from "../package-meta.js";
|
|
2
3
|
import { globalStageflowHome } from "../project/globalHome.js";
|
|
3
4
|
import { redactHostConfig } from "../config/hostConfig.js";
|
|
@@ -6,6 +7,31 @@ import { json } from "./createHttpHost.js";
|
|
|
6
7
|
import { livezBody, probeGitVersion, readSchemaHealth, runReadyzChecks, } from "../diagnostics/checks.js";
|
|
7
8
|
import { buildEgressHealth } from "../net/proxy.js";
|
|
8
9
|
import { toolchainHealthMap } from "../preflight/toolchain.js";
|
|
10
|
+
function bootProvidersSnapshot(providerBoot) {
|
|
11
|
+
return providerBoot
|
|
12
|
+
? {
|
|
13
|
+
configured: providerBoot.configured,
|
|
14
|
+
failures: providerBoot.failures,
|
|
15
|
+
}
|
|
16
|
+
: { configured: [], failures: [] };
|
|
17
|
+
}
|
|
18
|
+
async function providersLiveSummary(cwd, authCtx) {
|
|
19
|
+
const note = "Live auth inspect — prefer list_providers / sf providers status. boot_providers is Host boot env only and may be empty when CLI credentials are configured.";
|
|
20
|
+
if (authCtx === undefined) {
|
|
21
|
+
return { configured: [], note };
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const status = await getAuthStatus(cwd, undefined, authCtx);
|
|
25
|
+
const rows = Array.isArray(status) ? status : [status];
|
|
26
|
+
return {
|
|
27
|
+
configured: rows.filter((r) => r.configured).map((r) => r.providerId),
|
|
28
|
+
note,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { configured: [], note };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
9
35
|
export function handleLivez(_req, res) {
|
|
10
36
|
json(res, 200, livezBody());
|
|
11
37
|
}
|
|
@@ -54,6 +80,8 @@ export async function buildRichHealthPayload(boot) {
|
|
|
54
80
|
store: boot.store,
|
|
55
81
|
bootCwd: boot.cwd,
|
|
56
82
|
});
|
|
83
|
+
const boot_providers = bootProvidersSnapshot(boot.providerBoot);
|
|
84
|
+
const providers_live = await providersLiveSummary(boot.cwd, boot.providerAuthContext);
|
|
57
85
|
const payload = {
|
|
58
86
|
...capacity,
|
|
59
87
|
capacity: {
|
|
@@ -78,12 +106,9 @@ export async function buildRichHealthPayload(boot) {
|
|
|
78
106
|
kind: r.kind,
|
|
79
107
|
read_only: r.read_only,
|
|
80
108
|
})),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
failures: boot.providerBoot.failures,
|
|
85
|
-
}
|
|
86
|
-
: { configured: [], failures: [] },
|
|
109
|
+
boot_providers,
|
|
110
|
+
providers_live,
|
|
111
|
+
providers: boot_providers,
|
|
87
112
|
};
|
|
88
113
|
if (boot.hostConfig) {
|
|
89
114
|
payload.config = redactHostConfig(boot.hostConfig);
|
package/dist/server/http.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { AgentPort } from "../agent/port.js";
|
|
2
2
|
import { type ProviderAuthContext } from "../agent/providerAuth.js";
|
|
3
|
+
import { WorkshopChatSessionRegistry } from "../workshop/chatTurn.js";
|
|
4
|
+
import { type OperatorAgentHost } from "../operatorAgent/index.js";
|
|
3
5
|
import type { RunStoreKind } from "../runstore/createStore.js";
|
|
4
6
|
import type { RunStore } from "../runstore/port.js";
|
|
5
7
|
import type { RunManager } from "../runtime/runManager.js";
|
|
@@ -24,6 +26,8 @@ export type UiServerOptions = {
|
|
|
24
26
|
runChangeBus?: RunChangeBus;
|
|
25
27
|
allowedHosts?: AllowedHosts;
|
|
26
28
|
controlTokens?: ControlTokens;
|
|
29
|
+
/** Optional Workshop Operator Agent Host (fake in tests). */
|
|
30
|
+
workshopOperatorHost?: OperatorAgentHost;
|
|
27
31
|
};
|
|
28
32
|
export declare function defaultUiDistDir(): string;
|
|
29
33
|
export type OperatorRouteDeps = {
|
|
@@ -39,6 +43,17 @@ export type OperatorRouteDeps = {
|
|
|
39
43
|
controlTokens?: ControlTokens;
|
|
40
44
|
/** Live shutdown controller; set after listen so restore can beginDrain. */
|
|
41
45
|
getShutdown?: () => import("./shutdown.js").ShutdownController | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Optional Operator Agent Host for Workshop chat (tests inject the fake host).
|
|
48
|
+
* Distinct from stage-execution AgentPort. Defaults to live Pi Workshop Author
|
|
49
|
+
* (fake remains available via createWorkshopOperatorHost / test injection).
|
|
50
|
+
*/
|
|
51
|
+
workshopOperatorHost?: OperatorAgentHost;
|
|
52
|
+
/**
|
|
53
|
+
* Optional durable Workshop chat registry (host sessions keyed by session id).
|
|
54
|
+
* Defaults to a registry owned by this route surface.
|
|
55
|
+
*/
|
|
56
|
+
workshopChatRegistry?: WorkshopChatSessionRegistry;
|
|
42
57
|
};
|
|
43
58
|
/**
|
|
44
59
|
* The full operator-console route surface: REST API (runs, stages,
|