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,118 @@
|
|
|
1
|
+
import { loadFailure, loadSuccess } from "./loadOutcome.js";
|
|
2
|
+
import { mergeToolRequires } from "./toolRequires.js";
|
|
3
|
+
export const BROWSER_TOOL_NAME = "agent-browser";
|
|
4
|
+
const ALLOWED_KEYS = new Set(["profile", "headed", "allow_domains", "login_url", "check"]);
|
|
5
|
+
const CHECK_KEYS = new Set(["url", "logged_in_url", "logged_out_url"]);
|
|
6
|
+
const REJECTED_KEYS = new Set(["path", "scope", "secret"]);
|
|
7
|
+
const PROFILE_NAME = /^[A-Za-z0-9_-]{1,64}$/;
|
|
8
|
+
const DOMAIN = /^(\*\.)?[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function fail(label, stageId, message) {
|
|
13
|
+
return loadFailure([
|
|
14
|
+
{
|
|
15
|
+
code: "stage.invalid_browser",
|
|
16
|
+
message: `Invalid stage ${label}: browser ${message}`,
|
|
17
|
+
category: "stage",
|
|
18
|
+
...(stageId !== undefined ? { stageId } : {}),
|
|
19
|
+
},
|
|
20
|
+
]);
|
|
21
|
+
}
|
|
22
|
+
function nonEmptyString(value) {
|
|
23
|
+
return typeof value === "string" && value.trim() !== "";
|
|
24
|
+
}
|
|
25
|
+
function parseCheck(raw, label, stageId) {
|
|
26
|
+
if (!isPlainObject(raw)) {
|
|
27
|
+
return fail(label, stageId, "check must be an object");
|
|
28
|
+
}
|
|
29
|
+
for (const key of Object.keys(raw)) {
|
|
30
|
+
if (!CHECK_KEYS.has(key)) {
|
|
31
|
+
return fail(label, stageId, `check unknown key "${key}" (allowed: url, logged_in_url, logged_out_url)`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (!nonEmptyString(raw.url)) {
|
|
35
|
+
return fail(label, stageId, "check.url must be a non-empty string");
|
|
36
|
+
}
|
|
37
|
+
const check = {
|
|
38
|
+
url: raw.url.trim(),
|
|
39
|
+
};
|
|
40
|
+
if (raw.logged_in_url !== undefined) {
|
|
41
|
+
if (!nonEmptyString(raw.logged_in_url)) {
|
|
42
|
+
return fail(label, stageId, "check.logged_in_url must be a non-empty string");
|
|
43
|
+
}
|
|
44
|
+
check.logged_in_url = raw.logged_in_url.trim();
|
|
45
|
+
}
|
|
46
|
+
if (raw.logged_out_url !== undefined) {
|
|
47
|
+
const list = Array.isArray(raw.logged_out_url)
|
|
48
|
+
? raw.logged_out_url
|
|
49
|
+
: [raw.logged_out_url];
|
|
50
|
+
if (list.length === 0 || !list.every(nonEmptyString)) {
|
|
51
|
+
return fail(label, stageId, "check.logged_out_url must be a non-empty string or a non-empty list of strings");
|
|
52
|
+
}
|
|
53
|
+
const trimmed = list.map((item) => item.trim());
|
|
54
|
+
check.logged_out_url = Array.isArray(raw.logged_out_url)
|
|
55
|
+
? trimmed
|
|
56
|
+
: trimmed[0];
|
|
57
|
+
}
|
|
58
|
+
return loadSuccess(check);
|
|
59
|
+
}
|
|
60
|
+
export function parseStageBrowser(raw, label, stageId) {
|
|
61
|
+
if (raw === undefined)
|
|
62
|
+
return loadSuccess(undefined);
|
|
63
|
+
if (!isPlainObject(raw)) {
|
|
64
|
+
return fail(label, stageId, "must be an object");
|
|
65
|
+
}
|
|
66
|
+
for (const key of Object.keys(raw)) {
|
|
67
|
+
if (REJECTED_KEYS.has(key)) {
|
|
68
|
+
return fail(label, stageId, `"${key}" is not allowed (the Host chooses profile location and scope)`);
|
|
69
|
+
}
|
|
70
|
+
if (!ALLOWED_KEYS.has(key)) {
|
|
71
|
+
return fail(label, stageId, `unknown key "${key}" (allowed: profile, headed, allow_domains, login_url, check)`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const browser = {};
|
|
75
|
+
if (raw.profile !== undefined) {
|
|
76
|
+
if (typeof raw.profile !== "string" || !PROFILE_NAME.test(raw.profile)) {
|
|
77
|
+
return fail(label, stageId, "profile must be 1-64 characters of letters, digits, dash, or underscore");
|
|
78
|
+
}
|
|
79
|
+
browser.profile = raw.profile;
|
|
80
|
+
}
|
|
81
|
+
if (raw.headed !== undefined) {
|
|
82
|
+
if (typeof raw.headed !== "boolean") {
|
|
83
|
+
return fail(label, stageId, "headed must be a boolean");
|
|
84
|
+
}
|
|
85
|
+
browser.headed = raw.headed;
|
|
86
|
+
}
|
|
87
|
+
if (raw.allow_domains !== undefined) {
|
|
88
|
+
if (!Array.isArray(raw.allow_domains) || raw.allow_domains.length === 0) {
|
|
89
|
+
return fail(label, stageId, "allow_domains must be a non-empty array of domain names");
|
|
90
|
+
}
|
|
91
|
+
const domains = [];
|
|
92
|
+
for (const item of raw.allow_domains) {
|
|
93
|
+
if (typeof item !== "string" || !DOMAIN.test(item.trim())) {
|
|
94
|
+
return fail(label, stageId, `allow_domains entry ${JSON.stringify(item)} must be a bare domain such as "example.com" or "*.example.com" (no scheme, port, or path)`);
|
|
95
|
+
}
|
|
96
|
+
domains.push(item.trim());
|
|
97
|
+
}
|
|
98
|
+
browser.allow_domains = domains;
|
|
99
|
+
}
|
|
100
|
+
if (raw.login_url !== undefined) {
|
|
101
|
+
if (!nonEmptyString(raw.login_url)) {
|
|
102
|
+
return fail(label, stageId, "login_url must be a non-empty string");
|
|
103
|
+
}
|
|
104
|
+
browser.login_url = raw.login_url.trim();
|
|
105
|
+
}
|
|
106
|
+
if (raw.check !== undefined) {
|
|
107
|
+
const check = parseCheck(raw.check, label, stageId);
|
|
108
|
+
if (!check.ok)
|
|
109
|
+
return check;
|
|
110
|
+
browser.check = check.value;
|
|
111
|
+
}
|
|
112
|
+
return loadSuccess(browser);
|
|
113
|
+
}
|
|
114
|
+
export function withBrowserRequires(stage, ctx) {
|
|
115
|
+
if (stage.browser === undefined)
|
|
116
|
+
return loadSuccess(stage.requires);
|
|
117
|
+
return mergeToolRequires([stage.requires, [{ tool: BROWSER_TOOL_NAME }]], { pipelineId: ctx.pipelineId, label: `stage "${ctx.stageId}"` });
|
|
118
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RunStore } from "../runstore/port.js";
|
|
2
|
+
import type { TriggerFile } from "../types/trigger.js";
|
|
3
|
+
export type TriggerListItem = TriggerFile & {
|
|
4
|
+
definition_ref: string;
|
|
5
|
+
last_fired_at?: string;
|
|
6
|
+
last_run_id?: string;
|
|
7
|
+
next_run_at?: string;
|
|
8
|
+
};
|
|
9
|
+
/** Catalog-authoritative trigger list, overlaid with each trigger's store-recorded fire state (if any). */
|
|
10
|
+
export declare function loadCatalogTriggers(cwd: string, store: RunStore): Promise<TriggerListItem[] | undefined>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { catalogContextFromStageflow } from "./resolveCatalogContext.js";
|
|
3
|
+
import { getCatalogScanPaths } from "./browseCatalog.js";
|
|
4
|
+
import { resolveStageflowContext } from "../project/resolveStageflowContext.js";
|
|
5
|
+
import { loadTriggerOutcome } from "./loadTrigger.js";
|
|
6
|
+
/** Catalog-authoritative trigger list, overlaid with each trigger's store-recorded fire state (if any). */
|
|
7
|
+
export async function loadCatalogTriggers(cwd, store) {
|
|
8
|
+
const ctx = catalogContextFromStageflow(await resolveStageflowContext(cwd));
|
|
9
|
+
const scanPaths = await getCatalogScanPaths(ctx);
|
|
10
|
+
if (!scanPaths)
|
|
11
|
+
return undefined;
|
|
12
|
+
const projectRoot = ctx.projectRoot ?? undefined;
|
|
13
|
+
const items = [];
|
|
14
|
+
for (const filePath of scanPaths.triggerPaths) {
|
|
15
|
+
const outcome = await loadTriggerOutcome(filePath);
|
|
16
|
+
if (!outcome.ok)
|
|
17
|
+
continue;
|
|
18
|
+
const definitionRef = projectRoot !== undefined
|
|
19
|
+
? path.relative(projectRoot, filePath).replace(/\\/g, "/")
|
|
20
|
+
: filePath;
|
|
21
|
+
const record = await store.getTrigger(outcome.value.id);
|
|
22
|
+
items.push({
|
|
23
|
+
...outcome.value,
|
|
24
|
+
definition_ref: definitionRef,
|
|
25
|
+
...(record?.last_fired_at !== undefined
|
|
26
|
+
? { last_fired_at: record.last_fired_at }
|
|
27
|
+
: {}),
|
|
28
|
+
...(record?.last_run_id !== undefined
|
|
29
|
+
? { last_run_id: record.last_run_id }
|
|
30
|
+
: {}),
|
|
31
|
+
...(record?.next_run_at !== undefined
|
|
32
|
+
? { next_run_at: record.next_run_at }
|
|
33
|
+
: {}),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return items.sort((a, b) => a.id.localeCompare(b.id));
|
|
37
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { InlinePipelineDefinition, LoadedPipeline } from "../types/pipeline.js";
|
|
2
|
+
import type { TriggerFile } from "../types/trigger.js";
|
|
2
3
|
import type { LoadIssue } from "./loadOutcome.js";
|
|
3
4
|
export type ValidationSeverity = "error" | "warning";
|
|
4
|
-
export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task";
|
|
5
|
-
export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
|
|
5
|
+
export type ValidationCategory = "pipeline" | "stage" | "catalog" | "task" | "trigger";
|
|
6
|
+
export type ValidationFindingCode = "pipeline.invalid_shape" | "pipeline.dag_error" | "pipeline.missing_stage" | "pipeline.load_error" | "pipeline.string_stage_ref" | "pipeline.stage_uses_inline_conflict" | "pipeline.stage_missing_body" | "pipeline.stage_id_mismatch" | "pipeline.invalid_completion" | "catalog.path_case_mismatch" | "pipeline.invalid_recovery" | "pipeline.include_cycle" | "pipeline.include_invalid" | "pipeline.include_duplicate_stage" | "pipeline.invalid_agent" | "pipeline.invalid_model" | "pipeline.invalid_verify" | "pipeline.io_incompatible" | "pipeline.model_applies" | "pipeline.route_if_invalid" | "pipeline.route_all_gated" | "pipeline.invalid_requires" | "pipeline.requires_conflict" | "stage.invalid_shape" | "stage.invalid_model" | "stage.missing_model" | "stage.invalid_payload_schema" | "stage.invalid_clone_input_schema" | "stage.unresolved_schema_ref" | "stage.invalid_clone_actions" | "stage.invalid_gate_kinds" | "stage.invalid_pre_emit_checks" | "stage.invalid_timeout_ms" | "stage.invalid_skill" | "stage.invalid_mcp" | "stage.invalid_browser" | "stage.invalid_secrets" | "stage.unknown_secret" | "stage.denied_secret" | "stage.invalid_requires" | "stage.invalid_agent" | "stage.invalid_io" | "stage.load_error" | "stage.id_filename_mismatch" | "task.invalid_shape" | "task.load_error" | "task.entry_input_unmet" | "task.binding_conflict" | "task.repository_ref_required" | "task.ref_without_repository" | "task.repository_invalid" | "trigger.invalid_shape" | "trigger.load_error" | "trigger.unknown_pipeline" | "trigger.unknown_task" | "catalog.duplicate_pipeline_id" | "catalog.manifest_missing" | "catalog.manifest_invalid" | "catalog.empty_catalog" | "catalog.manifest_load_error" | "catalog.invalid_mcp" | "catalog.mcp_command_missing" | "catalog.mixed_yaml_dialect" | "catalog.legacy_yaml" | "catalog.stageflow_home_absolute_path";
|
|
6
7
|
export type ValidationFinding = {
|
|
7
8
|
severity: ValidationSeverity;
|
|
8
9
|
code: ValidationFindingCode | string;
|
|
@@ -12,13 +13,14 @@ export type ValidationFinding = {
|
|
|
12
13
|
pipelineId?: string;
|
|
13
14
|
stageId?: string;
|
|
14
15
|
};
|
|
15
|
-
export type ValidationScope = "full" | "pipeline" | "task";
|
|
16
|
+
export type ValidationScope = "full" | "pipeline" | "task" | "trigger";
|
|
16
17
|
export type ValidateCatalogOptions = {
|
|
17
18
|
cwd?: string;
|
|
18
19
|
projectRoot?: string;
|
|
19
20
|
scope: ValidationScope;
|
|
20
21
|
pipeline?: string;
|
|
21
22
|
task?: string;
|
|
23
|
+
trigger?: string;
|
|
22
24
|
strict?: boolean;
|
|
23
25
|
};
|
|
24
26
|
export type ValidationResult = {
|
|
@@ -59,4 +61,12 @@ export declare function loadPipelineValidated(nameOrPath: string | InlinePipelin
|
|
|
59
61
|
projectRoot?: string;
|
|
60
62
|
validateStages?: boolean;
|
|
61
63
|
}): Promise<LoadPipelineValidatedResult>;
|
|
64
|
+
export declare function collectPipelineIdsFromPaths(pipelinePaths: string[]): Promise<Map<string, string[]>>;
|
|
65
|
+
export declare function collectTaskIdsFromPaths(taskPaths: string[]): Promise<Set<string>>;
|
|
66
|
+
export type TriggerCatalogRefs = {
|
|
67
|
+
pipelineIds: ReadonlySet<string>;
|
|
68
|
+
taskIds: ReadonlySet<string>;
|
|
69
|
+
};
|
|
70
|
+
/** Confirms a trigger's `pipeline`/`task` refs resolve against known catalog ids. */
|
|
71
|
+
export declare function findingsForTriggerRefs(cwd: string, absPath: string, trigger: TriggerFile, refs: TriggerCatalogRefs): ValidationFinding[];
|
|
62
72
|
export declare function validateCatalog(options: ValidateCatalogOptions): Promise<ValidationResult>;
|
|
@@ -3,6 +3,7 @@ import { globalStageflowHome } from "../project/globalHome.js";
|
|
|
3
3
|
import { INLINE_PIPELINE_PATH, loadPipelineFromObjectOutcome, loadPipelineOutcome, resolvePipelinePath, } from "./loadPipeline.js";
|
|
4
4
|
import { loadStageOutcome } from "./loadStage.js";
|
|
5
5
|
import { loadTaskOutcome } from "./loadTask.js";
|
|
6
|
+
import { loadTriggerOutcome } from "./loadTrigger.js";
|
|
6
7
|
import { readYamlObject } from "./readYamlObject.js";
|
|
7
8
|
import { resolveCatalogContext } from "./resolveCatalogContext.js";
|
|
8
9
|
import { getCatalogScanPaths } from "./browseCatalog.js";
|
|
@@ -103,6 +104,15 @@ function findingTaskError(cwd, absPath, message, code) {
|
|
|
103
104
|
category: "task",
|
|
104
105
|
}, "error");
|
|
105
106
|
}
|
|
107
|
+
function findingTriggerError(cwd, absPath, message, code) {
|
|
108
|
+
return baseFinding({
|
|
109
|
+
cwd,
|
|
110
|
+
absPath,
|
|
111
|
+
message,
|
|
112
|
+
code,
|
|
113
|
+
category: "trigger",
|
|
114
|
+
}, "error");
|
|
115
|
+
}
|
|
106
116
|
function findingCatalog(cwd, absPath, message, code, severity) {
|
|
107
117
|
return baseFinding({
|
|
108
118
|
cwd,
|
|
@@ -142,6 +152,11 @@ export function findingsFromLoadIssues(cwd, absPath, issues) {
|
|
|
142
152
|
findingTaskError(cwd, absPath, issue.message, issue.code),
|
|
143
153
|
];
|
|
144
154
|
}
|
|
155
|
+
if (issue.category === "trigger") {
|
|
156
|
+
return [
|
|
157
|
+
findingTriggerError(cwd, absPath, issue.message, issue.code),
|
|
158
|
+
];
|
|
159
|
+
}
|
|
145
160
|
if (issue.category === "catalog") {
|
|
146
161
|
const code = issue.code;
|
|
147
162
|
const severity = code === "catalog.manifest_missing" ||
|
|
@@ -469,7 +484,7 @@ function findingManifestMissing(cwd, projectRoot) {
|
|
|
469
484
|
function findingEmptyCatalog(cwd, manifestPath, kind) {
|
|
470
485
|
return findingCatalog(cwd, manifestPath, `Manifest catalog.${kind} is empty`, "catalog.empty_catalog", "warning");
|
|
471
486
|
}
|
|
472
|
-
async function collectPipelineIdsFromPaths(pipelinePaths) {
|
|
487
|
+
export async function collectPipelineIdsFromPaths(pipelinePaths) {
|
|
473
488
|
const idToPaths = new Map();
|
|
474
489
|
for (const filePath of pipelinePaths) {
|
|
475
490
|
try {
|
|
@@ -486,6 +501,20 @@ async function collectPipelineIdsFromPaths(pipelinePaths) {
|
|
|
486
501
|
}
|
|
487
502
|
return idToPaths;
|
|
488
503
|
}
|
|
504
|
+
export async function collectTaskIdsFromPaths(taskPaths) {
|
|
505
|
+
const ids = new Set();
|
|
506
|
+
for (const filePath of taskPaths) {
|
|
507
|
+
try {
|
|
508
|
+
const raw = await readYamlObject(filePath);
|
|
509
|
+
if (typeof raw?.id === "string")
|
|
510
|
+
ids.add(raw.id);
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
// unreadable files handled by task validation loop
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return ids;
|
|
517
|
+
}
|
|
489
518
|
function findingsForDuplicatePipelineIds(cwd, idToPaths) {
|
|
490
519
|
const findings = [];
|
|
491
520
|
for (const [pipelineId, paths] of idToPaths) {
|
|
@@ -530,7 +559,7 @@ async function validateManifestAll(invocationCwd) {
|
|
|
530
559
|
if (!scanPaths) {
|
|
531
560
|
return findings;
|
|
532
561
|
}
|
|
533
|
-
const { pipelinePaths, taskPaths } = scanPaths;
|
|
562
|
+
const { pipelinePaths, taskPaths, triggerPaths } = scanPaths;
|
|
534
563
|
for (const pipelinePath of pipelinePaths) {
|
|
535
564
|
const core = await runPipelineValidation(pipelinePath, {
|
|
536
565
|
cwd: projectRoot,
|
|
@@ -548,6 +577,32 @@ async function validateManifestAll(invocationCwd) {
|
|
|
548
577
|
}
|
|
549
578
|
const idToPaths = await collectPipelineIdsFromPaths(pipelinePaths);
|
|
550
579
|
findings.push(...findingsForDuplicatePipelineIds(projectRoot, idToPaths));
|
|
580
|
+
if (triggerPaths.length > 0) {
|
|
581
|
+
const pipelineIds = new Set(idToPaths.keys());
|
|
582
|
+
const taskIds = await collectTaskIdsFromPaths(taskPaths);
|
|
583
|
+
for (const triggerPath of triggerPaths) {
|
|
584
|
+
const outcome = await loadTriggerOutcome(triggerPath);
|
|
585
|
+
if (!outcome.ok) {
|
|
586
|
+
findings.push(...findingsFromLoadIssues(projectRoot, triggerPath, outcome.issues));
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
findings.push(...findingsForTriggerRefs(projectRoot, triggerPath, outcome.value, {
|
|
590
|
+
pipelineIds,
|
|
591
|
+
taskIds,
|
|
592
|
+
}));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return findings;
|
|
596
|
+
}
|
|
597
|
+
/** Confirms a trigger's `pipeline`/`task` refs resolve against known catalog ids. */
|
|
598
|
+
export function findingsForTriggerRefs(cwd, absPath, trigger, refs) {
|
|
599
|
+
const findings = [];
|
|
600
|
+
if (!refs.pipelineIds.has(trigger.pipeline)) {
|
|
601
|
+
findings.push(findingTriggerError(cwd, absPath, `Trigger "${trigger.id}" references unknown pipeline "${trigger.pipeline}"`, "trigger.unknown_pipeline"));
|
|
602
|
+
}
|
|
603
|
+
if (trigger.task !== undefined && !refs.taskIds.has(trigger.task)) {
|
|
604
|
+
findings.push(findingTriggerError(cwd, absPath, `Trigger "${trigger.id}" references unknown task "${trigger.task}"`, "trigger.unknown_task"));
|
|
605
|
+
}
|
|
551
606
|
return findings;
|
|
552
607
|
}
|
|
553
608
|
async function validateSingleTask(invocationCwd, taskArg) {
|
|
@@ -560,6 +615,26 @@ async function validateSingleTask(invocationCwd, taskArg) {
|
|
|
560
615
|
}
|
|
561
616
|
return [];
|
|
562
617
|
}
|
|
618
|
+
async function validateSingleTrigger(invocationCwd, triggerArg) {
|
|
619
|
+
const ctx = await resolveCatalogContext(invocationCwd);
|
|
620
|
+
const relCwd = ctx.projectRoot ?? invocationCwd;
|
|
621
|
+
const triggerPath = path.resolve(invocationCwd, triggerArg);
|
|
622
|
+
const outcome = await loadTriggerOutcome(triggerPath);
|
|
623
|
+
if (!outcome.ok) {
|
|
624
|
+
return findingsFromLoadIssues(relCwd, triggerPath, outcome.issues);
|
|
625
|
+
}
|
|
626
|
+
const scanPaths = await getCatalogScanPaths(ctx);
|
|
627
|
+
const pipelineIds = scanPaths
|
|
628
|
+
? new Set((await collectPipelineIdsFromPaths(scanPaths.pipelinePaths)).keys())
|
|
629
|
+
: new Set();
|
|
630
|
+
const taskIds = scanPaths
|
|
631
|
+
? await collectTaskIdsFromPaths(scanPaths.taskPaths)
|
|
632
|
+
: new Set();
|
|
633
|
+
return findingsForTriggerRefs(relCwd, triggerPath, outcome.value, {
|
|
634
|
+
pipelineIds,
|
|
635
|
+
taskIds,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
563
638
|
export async function validateCatalog(options) {
|
|
564
639
|
const cwd = options.cwd ?? process.cwd();
|
|
565
640
|
const strict = options.strict ?? false;
|
|
@@ -569,6 +644,9 @@ export async function validateCatalog(options) {
|
|
|
569
644
|
if (options.scope === "task" && !options.task) {
|
|
570
645
|
throw new Error("validateCatalog: task is required when scope is \"task\"");
|
|
571
646
|
}
|
|
647
|
+
if (options.scope === "trigger" && !options.trigger) {
|
|
648
|
+
throw new Error("validateCatalog: trigger is required when scope is \"trigger\"");
|
|
649
|
+
}
|
|
572
650
|
const allFindings = [];
|
|
573
651
|
if (options.scope === "pipeline") {
|
|
574
652
|
return validatePipeline(options.pipeline, {
|
|
@@ -581,6 +659,9 @@ export async function validateCatalog(options) {
|
|
|
581
659
|
else if (options.scope === "task") {
|
|
582
660
|
allFindings.push(...(await validateSingleTask(cwd, options.task)));
|
|
583
661
|
}
|
|
662
|
+
else if (options.scope === "trigger") {
|
|
663
|
+
allFindings.push(...(await validateSingleTrigger(cwd, options.trigger)));
|
|
664
|
+
}
|
|
584
665
|
else {
|
|
585
666
|
allFindings.push(...(await validateManifestAll(cwd)));
|
|
586
667
|
}
|
|
@@ -45,6 +45,14 @@ export function validateCompletionContractForStage(stage, completion) {
|
|
|
45
45
|
!stage.gate_kinds?.includes(check.kind)) {
|
|
46
46
|
return failure(stage.id, `gate check "${check.id}" requires gate_kinds to include "${check.kind}"`);
|
|
47
47
|
}
|
|
48
|
+
if (check.type === "browser_login") {
|
|
49
|
+
if (stage.browser?.check === undefined) {
|
|
50
|
+
return failure(stage.id, `check "${check.id}" requires the stage to declare browser.check`);
|
|
51
|
+
}
|
|
52
|
+
if (stage.browser.headed === false) {
|
|
53
|
+
return failure(stage.id, `check "${check.id}" is a human login check and needs a visible browser (browser.headed must not be false)`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
48
56
|
if (check.type === "checkout_changes") {
|
|
49
57
|
for (const field of check.path_fields ?? []) {
|
|
50
58
|
if (!isRequiredStringArrayField(stage.payload_schema, field)) {
|
|
@@ -26,4 +26,4 @@ export declare function declaredArtifactIssue(artifacts: readonly string[], chec
|
|
|
26
26
|
* `gate` check with no `readQaTrail` fails closed (cannot be verified). No
|
|
27
27
|
* `checks` (or an empty list) is a no-op.
|
|
28
28
|
*/
|
|
29
|
-
export declare function assertPreEmitChecks(envelope: Pick<StageEnvelope, "artifacts">, options: PreEmitCheckOptions | undefined): Promise<void>;
|
|
29
|
+
export declare function assertPreEmitChecks(envelope: Pick<StageEnvelope, "artifacts" | "payload">, options: PreEmitCheckOptions | undefined): Promise<void>;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* why this stays separate from `CompletionCheck`'s `artifact`/`gate` runners.
|
|
11
11
|
*/
|
|
12
12
|
import { preEmitGateIssue } from "../hitl/qaTrail.js";
|
|
13
|
+
import { loginCheckIssue } from "../browser/loginCheck.js";
|
|
13
14
|
import { EnvelopeError } from "../types/envelope.js";
|
|
14
15
|
function artifactListsBasename(artifact, basename) {
|
|
15
16
|
return artifact === basename || artifact.endsWith(`/${basename}`);
|
|
@@ -42,6 +43,13 @@ export async function assertPreEmitChecks(envelope, options) {
|
|
|
42
43
|
}
|
|
43
44
|
continue;
|
|
44
45
|
}
|
|
46
|
+
if (check.type === "browser_login_check") {
|
|
47
|
+
const issue = loginCheckIssue(envelope.payload, check);
|
|
48
|
+
if (issue !== undefined) {
|
|
49
|
+
throw new EnvelopeError(issue);
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
45
53
|
// check.type === "gate"
|
|
46
54
|
if (options?.readQaTrail === undefined) {
|
|
47
55
|
throw new EnvelopeError("declared gate kinds cannot be verified without a QA trail reader");
|
package/dist/logging/redact.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Cookie/storage/header/token shapes from browser commands; applied only to browser stages. */
|
|
2
|
+
export declare const BROWSER_SECRET_PATTERNS: RegExp[];
|
|
3
|
+
/** Default patterns plus browser patterns, for `RedactOptions.patterns` on browser stages. */
|
|
4
|
+
export declare const BROWSER_STAGE_PATTERNS: RegExp[];
|
|
1
5
|
export type NamedSecret = {
|
|
2
6
|
name: string;
|
|
3
7
|
value: string;
|
package/dist/logging/redact.js
CHANGED
|
@@ -6,6 +6,23 @@ const SECRET_PATTERNS = [
|
|
|
6
6
|
/\bBearer\s+[A-Za-z0-9\-._~+/]+=*/gi,
|
|
7
7
|
/\b[A-Za-z_]*KEY[A-Za-z_]*\s*[:=]\s*['"]?[A-Za-z0-9+/_-]{16,}['"]?/gi,
|
|
8
8
|
];
|
|
9
|
+
/** Cookie/storage/header/token shapes from browser commands; applied only to browser stages. */
|
|
10
|
+
export const BROWSER_SECRET_PATTERNS = [
|
|
11
|
+
/\b(?:Set-Cookie|Cookie)["']?\s*:\s*["']?[^\r\n"']+/gi,
|
|
12
|
+
/\b(?:Proxy-)?Authorization["']?\s*[:=]\s*["']?[^\r\n"']+/gi,
|
|
13
|
+
/\b(?:X-Api-Key|X-Auth-Token|X-CSRF-Token|X-Xsrf-Token)["']?\s*[:=]\s*["']?[^\s"']+/gi,
|
|
14
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,
|
|
15
|
+
/"name"\s*:\s*"[^"]*"\s*,\s*"value"\s*:\s*"[^"]*"/g,
|
|
16
|
+
/"value"\s*:\s*"[^"]*"(?=\s*,\s*"domain")/g,
|
|
17
|
+
/\b(?:li_at|li_rm|JSESSIONID|sessionid|session_id|sessid|csrftoken|auth_token|access_token|refresh_token|id_token)\b["']?\s*[:=]\s*["']?[A-Za-z0-9%+/_.=-]{8,}/gi,
|
|
18
|
+
/\b(?:li_at|li_rm|JSESSIONID|sessionid)\b[ \t|]+[A-Za-z0-9%+/_.=-]{16,}/g,
|
|
19
|
+
/"[^"\s]*(?:token|secret|authorization|jwt|session|password|passwd|api_?key|credential|csrf)[^"\s]*"\s*:\s*"[^"]{8,}"/gi,
|
|
20
|
+
];
|
|
21
|
+
/** Default patterns plus browser patterns, for `RedactOptions.patterns` on browser stages. */
|
|
22
|
+
export const BROWSER_STAGE_PATTERNS = [
|
|
23
|
+
...SECRET_PATTERNS,
|
|
24
|
+
...BROWSER_SECRET_PATTERNS,
|
|
25
|
+
];
|
|
9
26
|
const REDACTED = "[redacted]";
|
|
10
27
|
export function shouldRegisterValue(value) {
|
|
11
28
|
if (value.length < 8)
|
package/dist/mcp/catalogTools.js
CHANGED
|
@@ -9,6 +9,8 @@ import { validateCatalog } from "../config/validateCatalog.js";
|
|
|
9
9
|
import { writeAudit } from "../logging/audit.js";
|
|
10
10
|
import { logger as rootLogger } from "../logging/logger.js";
|
|
11
11
|
import { PACKAGE_VERSION, BUILD_SHA } from "../package-meta.js";
|
|
12
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
13
|
+
import { getAuthStatus, } from "../agent/providerAuth.js";
|
|
12
14
|
import { PipelineValidationError } from "../runtime/pipelineValidationError.js";
|
|
13
15
|
import { PipelinePreflightError } from "../runtime/pipelineRunner.js";
|
|
14
16
|
import { EnvelopeRefError, resolveEnvelopeRefsToTask, } from "../runtime/resolveEnvelopeRef.js";
|
|
@@ -144,7 +146,7 @@ const runStatusSchema = z.enum([
|
|
|
144
146
|
"cancelled",
|
|
145
147
|
]);
|
|
146
148
|
export function registerCatalogTools(server, deps) {
|
|
147
|
-
const { manager, store, cwd } = deps;
|
|
149
|
+
const { manager, store, cwd, providerAuthContext, providerBoot } = deps;
|
|
148
150
|
const agentDir = deps.agentDir ?? getAgentDir();
|
|
149
151
|
server.registerTool("list_skills", {
|
|
150
152
|
description: "List skills with name, description, and origin (run|checkout|host). Optional runId scopes to that run's resolution view (run-tier skills first, then checkout .pi/skills, then host).",
|
|
@@ -201,6 +203,7 @@ export function registerCatalogTools(server, deps) {
|
|
|
201
203
|
return textResult({
|
|
202
204
|
pipelines: result.items,
|
|
203
205
|
root_errors: result.root_errors,
|
|
206
|
+
...(result.tip !== undefined ? { tip: result.tip } : {}),
|
|
204
207
|
});
|
|
205
208
|
});
|
|
206
209
|
server.registerTool("list_tasks", {
|
|
@@ -225,6 +228,7 @@ export function registerCatalogTools(server, deps) {
|
|
|
225
228
|
return textResult({
|
|
226
229
|
tasks: result.items,
|
|
227
230
|
root_errors: result.root_errors,
|
|
231
|
+
...(result.tip !== undefined ? { tip: result.tip } : {}),
|
|
228
232
|
});
|
|
229
233
|
});
|
|
230
234
|
server.registerTool("list_models", {
|
|
@@ -278,14 +282,45 @@ export function registerCatalogTools(server, deps) {
|
|
|
278
282
|
return textResult({ runs });
|
|
279
283
|
});
|
|
280
284
|
server.registerTool("get_health", {
|
|
281
|
-
description: "Server health and soft-max run capacity: activeRunIds, activeCount, maxConcurrent, slotsAvailable, activeStageProcesses, version,
|
|
285
|
+
description: "Server health and soft-max run capacity: activeRunIds, activeCount, maxConcurrent, slotsAvailable, activeStageProcesses, version, stageflow_home, boot_providers (Host boot snapshot), providers_live (or pointer to list_providers), plus disk breakdown for the durable root. Start until slotsAvailable is 0; then wait for a run to finish or raise STAGEFLOW_MAX_CONCURRENT_RUNS.",
|
|
282
286
|
inputSchema: z.object({}),
|
|
283
|
-
}, async () =>
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
287
|
+
}, async () => {
|
|
288
|
+
const boot_providers = providerBoot
|
|
289
|
+
? {
|
|
290
|
+
configured: providerBoot.configured,
|
|
291
|
+
failures: providerBoot.failures,
|
|
292
|
+
}
|
|
293
|
+
: { configured: [], failures: [] };
|
|
294
|
+
let providers_live = {
|
|
295
|
+
configured: [],
|
|
296
|
+
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.",
|
|
297
|
+
};
|
|
298
|
+
if (providerAuthContext !== undefined) {
|
|
299
|
+
try {
|
|
300
|
+
const status = await getAuthStatus(cwd, undefined, providerAuthContext);
|
|
301
|
+
const rows = Array.isArray(status) ? status : [status];
|
|
302
|
+
providers_live = {
|
|
303
|
+
configured: rows
|
|
304
|
+
.filter((r) => r.configured)
|
|
305
|
+
.map((r) => r.providerId),
|
|
306
|
+
note: providers_live.note,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
/* keep empty configured + note */
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return textResult({
|
|
314
|
+
...(await manager.getHealthWithDisk()),
|
|
315
|
+
version: PACKAGE_VERSION,
|
|
316
|
+
build_sha: BUILD_SHA,
|
|
317
|
+
toolchain: toolchainHealthMap(),
|
|
318
|
+
stageflow_home: globalStageflowHome(),
|
|
319
|
+
boot_providers,
|
|
320
|
+
providers_live,
|
|
321
|
+
providers: boot_providers,
|
|
322
|
+
});
|
|
323
|
+
});
|
|
289
324
|
server.registerTool("preflight", {
|
|
290
325
|
description: "Check pipeline requires:/secrets:/mcp against the Host toolchain manifest and curated stage env before start_run. Accepts pipeline path or inline definition (same union as start_run). Returns { ok, checks } with status ok|missing_tool|tool_version_mismatch|unknown_version|secret_unavailable|unresolved_var. Does not create a Run. unknown_version is ok unless strict:true.",
|
|
291
326
|
inputSchema: z.object({
|
package/dist/mcp/controlTools.js
CHANGED
|
@@ -82,7 +82,9 @@ export function registerControlTools(server, deps) {
|
|
|
82
82
|
target_run_id: runId,
|
|
83
83
|
outcome: "ok",
|
|
84
84
|
});
|
|
85
|
-
return textResult(
|
|
85
|
+
return textResult(result.verification !== undefined
|
|
86
|
+
? { ok: true, verification: result.verification }
|
|
87
|
+
: { ok: true });
|
|
86
88
|
});
|
|
87
89
|
server.registerTool("decide_feedback_loop", {
|
|
88
90
|
description: "Resolve a feedback-loop wait_for_human decision (extend, continue, or abandon). Same semantics as POST /api/runs/:id/stages/:stageId/feedback-decision.",
|
package/dist/mcp/deps.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BootProviderResult } from "../agent/bootProviderConfig.js";
|
|
1
2
|
import type { ProviderAuthContext } from "../agent/providerAuth.js";
|
|
2
3
|
import type { RunStore } from "../runstore/port.js";
|
|
3
4
|
import type { RunChangeBus } from "../runtime/runChangeBus.js";
|
|
@@ -9,6 +10,7 @@ export type McpToolDeps = {
|
|
|
9
10
|
agentDir?: string;
|
|
10
11
|
providerAuthContext?: ProviderAuthContext;
|
|
11
12
|
projectRoot?: string;
|
|
13
|
+
providerBoot?: BootProviderResult;
|
|
12
14
|
};
|
|
13
15
|
export type McpHttpDeps = McpToolDeps & {
|
|
14
16
|
runChangeBus: RunChangeBus;
|
package/dist/mcp/server.js
CHANGED
|
@@ -43,6 +43,7 @@ function toolDepsFrom(deps) {
|
|
|
43
43
|
? { providerAuthContext: deps.providerAuthContext }
|
|
44
44
|
: {}),
|
|
45
45
|
...(deps.projectRoot !== undefined ? { projectRoot: deps.projectRoot } : {}),
|
|
46
|
+
...(deps.providerBoot !== undefined ? { providerBoot: deps.providerBoot } : {}),
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
function attachBusNotifications(server, bus) {
|
package/dist/mcp/tools.js
CHANGED
|
@@ -4,6 +4,7 @@ import { registerCheckoutTools } from "./checkoutTools.js";
|
|
|
4
4
|
import { registerControlTools } from "./controlTools.js";
|
|
5
5
|
import { registerProjectMcpTools } from "./projectMcpTools.js";
|
|
6
6
|
import { registerProviderTools } from "./providerTools.js";
|
|
7
|
+
import { registerTriggerTools } from "./triggerTools.js";
|
|
7
8
|
import { textResult } from "./toolResults.js";
|
|
8
9
|
import { DEFAULT_TIMEOUT_MS, waitRun } from "./waitRun.js";
|
|
9
10
|
import { registerGetStartedTool } from "./getStartedTool.js";
|
|
@@ -15,6 +16,7 @@ export function registerMcpTools(server, deps) {
|
|
|
15
16
|
registerCheckoutTools(server, deps);
|
|
16
17
|
registerProviderTools(server, deps);
|
|
17
18
|
registerProjectMcpTools(server, deps);
|
|
19
|
+
registerTriggerTools(server, deps);
|
|
18
20
|
server.registerTool("wait_run", {
|
|
19
21
|
description: "Long-poll until a run is waiting for HITL input and/or terminal (succeeded/failed), or until timeout_ms. Default until=any wakes on waiting OR terminal. Abort cancels only this wait (not the run). Optional notifications/progress when the client supplies _meta.progressToken. Compose: wait_run → answer_gate → wait_run.",
|
|
20
22
|
inputSchema: z.object({
|