@stackwright-pro/mcp 0.2.0-alpha.111 → 0.2.0-alpha.113
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/dist/integrity.js +8 -4
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +8 -4
- package/dist/integrity.mjs.map +1 -1
- package/dist/pipeline-constants.d.mts +5 -3
- package/dist/pipeline-constants.d.ts +5 -3
- package/dist/pipeline-constants.js +47 -1
- package/dist/pipeline-constants.js.map +1 -1
- package/dist/pipeline-constants.mjs +47 -1
- package/dist/pipeline-constants.mjs.map +1 -1
- package/dist/server.js +349 -67
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +340 -58
- package/dist/server.mjs.map +1 -1
- package/package.json +8 -8
package/dist/server.mjs
CHANGED
|
@@ -556,7 +556,7 @@ function registerDashboardTools(server2) {
|
|
|
556
556
|
yamlLines.push(` header: Status`);
|
|
557
557
|
yamlLines.push(` type: badge`);
|
|
558
558
|
}
|
|
559
|
-
const
|
|
559
|
+
const yaml4 = yamlLines.join("\n");
|
|
560
560
|
return {
|
|
561
561
|
content: [
|
|
562
562
|
{
|
|
@@ -566,7 +566,7 @@ function registerDashboardTools(server2) {
|
|
|
566
566
|
Layout: ${layout}
|
|
567
567
|
|
|
568
568
|
\`\`\`yaml
|
|
569
|
-
${
|
|
569
|
+
${yaml4}
|
|
570
570
|
\`\`\`
|
|
571
571
|
|
|
572
572
|
\u{1F4A1} Add this to pages/dashboard/content.yml and validate with stackwright_validate_pages.`
|
|
@@ -647,7 +647,7 @@ ${yaml3}
|
|
|
647
647
|
yamlLines.push(" action: navigate");
|
|
648
648
|
yamlLines.push(` href: "/${entity}"`);
|
|
649
649
|
yamlLines.push(" style: secondary");
|
|
650
|
-
const
|
|
650
|
+
const yaml4 = yamlLines.join("\n");
|
|
651
651
|
return {
|
|
652
652
|
content: [
|
|
653
653
|
{
|
|
@@ -655,7 +655,7 @@ ${yaml3}
|
|
|
655
655
|
text: `\u{1F4C4} Generated Detail Page for: ${entity}
|
|
656
656
|
|
|
657
657
|
\`\`\`yaml
|
|
658
|
-
${
|
|
658
|
+
${yaml4}
|
|
659
659
|
\`\`\`
|
|
660
660
|
|
|
661
661
|
\u{1F4A1} Add this to pages/${entity}/[${slugField}]/content.yml and validate.`
|
|
@@ -2826,21 +2826,40 @@ function readAllManifestPages(artifactsDir) {
|
|
|
2826
2826
|
return allPages;
|
|
2827
2827
|
}
|
|
2828
2828
|
for (const entry of entries) {
|
|
2829
|
-
if (
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2829
|
+
if (entry.endsWith("-manifest.json")) {
|
|
2830
|
+
const filePath = join5(artifactsDir, entry);
|
|
2831
|
+
try {
|
|
2832
|
+
const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
2833
|
+
if (!Array.isArray(raw.pages)) continue;
|
|
2834
|
+
for (const page of raw.pages) {
|
|
2835
|
+
if (typeof page.slug !== "string" || !page.slug) continue;
|
|
2836
|
+
allPages.push({
|
|
2837
|
+
slug: page.slug,
|
|
2838
|
+
authRequired: page.authRequired,
|
|
2839
|
+
requiredRoles: page.requiredRoles,
|
|
2840
|
+
source: entry
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
} catch {
|
|
2844
|
+
}
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
if (entry === "workflow-config.json" || entry.startsWith("workflow-config-")) {
|
|
2848
|
+
const filePath = join5(artifactsDir, entry);
|
|
2849
|
+
try {
|
|
2850
|
+
const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
2851
|
+
const route = raw.workflow?.route;
|
|
2852
|
+
if (typeof route !== "string" || !route) continue;
|
|
2853
|
+
const slug = route.startsWith("/") ? route.slice(1) : route;
|
|
2836
2854
|
allPages.push({
|
|
2837
|
-
slug
|
|
2838
|
-
authRequired:
|
|
2839
|
-
requiredRoles:
|
|
2855
|
+
slug,
|
|
2856
|
+
authRequired: true,
|
|
2857
|
+
requiredRoles: void 0,
|
|
2840
2858
|
source: entry
|
|
2841
2859
|
});
|
|
2860
|
+
} catch {
|
|
2842
2861
|
}
|
|
2843
|
-
|
|
2862
|
+
continue;
|
|
2844
2863
|
}
|
|
2845
2864
|
}
|
|
2846
2865
|
return allPages;
|
|
@@ -2848,7 +2867,7 @@ function readAllManifestPages(artifactsDir) {
|
|
|
2848
2867
|
function registerReadAuthManifestsTool(server2) {
|
|
2849
2868
|
server2.tool(
|
|
2850
2869
|
"stackwright_pro_read_auth_manifests",
|
|
2851
|
-
"Read ALL
|
|
2870
|
+
"Read ALL route-producing artifacts from the pipeline artifacts directory: *-manifest.json files (pages, dashboard, geo, form-wizard) AND workflow-config.json / workflow-config-*.json (different shape: workflow.route). Deterministically derives protectedRoutes and uncoveredSlugs. Uses the both-forms invariant (swp-n7kk): every authRequired route registers BOTH /{slug} (exact) AND /{slug}/:path* (sub-path). Workflow routes appear in uncoveredSlugs (no requiredRoles declared) for auth-reconcile to resolve via cross-cutting rules from auth-config.json. Extended by swp-hng0 to cover all four manifest types (was: pages+dashboard only).",
|
|
2852
2871
|
{
|
|
2853
2872
|
rbacRoles: z11.array(z11.string()).describe(
|
|
2854
2873
|
'RBAC role hierarchy in descending privilege order, e.g. ["ESF8_COORDINATOR", "HOSPITAL_EM", "OBSERVER"]. Used to compute the lowestRole for each authRequired page.'
|
|
@@ -2994,7 +3013,7 @@ function enrichErrors(issues, synonyms) {
|
|
|
2994
3013
|
});
|
|
2995
3014
|
}
|
|
2996
3015
|
function handleValidateYamlFragment(input) {
|
|
2997
|
-
const { schemaName, yaml:
|
|
3016
|
+
const { schemaName, yaml: yaml4 } = input;
|
|
2998
3017
|
if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
|
|
2999
3018
|
return {
|
|
3000
3019
|
valid: false,
|
|
@@ -3004,7 +3023,7 @@ function handleValidateYamlFragment(input) {
|
|
|
3004
3023
|
const name = schemaName;
|
|
3005
3024
|
let parsed;
|
|
3006
3025
|
try {
|
|
3007
|
-
parsed = yamlLoad2(
|
|
3026
|
+
parsed = yamlLoad2(yaml4);
|
|
3008
3027
|
} catch (err) {
|
|
3009
3028
|
return {
|
|
3010
3029
|
valid: false,
|
|
@@ -3030,8 +3049,8 @@ function registerValidateYamlFragmentTool(server2) {
|
|
|
3030
3049
|
),
|
|
3031
3050
|
yaml: z12.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
|
|
3032
3051
|
},
|
|
3033
|
-
async ({ schemaName, yaml:
|
|
3034
|
-
const result = handleValidateYamlFragment({ schemaName, yaml:
|
|
3052
|
+
async ({ schemaName, yaml: yaml4 }) => {
|
|
3053
|
+
const result = handleValidateYamlFragment({ schemaName, yaml: yaml4 });
|
|
3035
3054
|
return {
|
|
3036
3055
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
3037
3056
|
};
|
|
@@ -3244,6 +3263,9 @@ var MANIFEST_NAME_TO_PHASE = {
|
|
|
3244
3263
|
"stackwright-pro-dashboard-otter": "dashboard",
|
|
3245
3264
|
"stackwright-pro-form-wizard-otter": "workflow",
|
|
3246
3265
|
"stackwright-pro-geo-otter": "geo",
|
|
3266
|
+
// auth-reconcile runs AFTER pages+dashboard+geo+workflow (wave 6)
|
|
3267
|
+
// Reads auth-config.json + all four manifests; emits auth-reconcile-config.json + stackwright.auth.yml
|
|
3268
|
+
"stackwright-pro-auth-reconcile-otter": "auth-reconcile",
|
|
3247
3269
|
"stackwright-pro-polish-otter": "polish",
|
|
3248
3270
|
"stackwright-services-otter": "services",
|
|
3249
3271
|
"stackwright-pro-qa-otter": "qa"
|
|
@@ -3459,13 +3481,19 @@ var PHASE_ORDER = [
|
|
|
3459
3481
|
"api",
|
|
3460
3482
|
"data",
|
|
3461
3483
|
"auth",
|
|
3462
|
-
// moved earlier
|
|
3484
|
+
// wave 2 — moved earlier, only depends on design-language.json (designer)
|
|
3463
3485
|
"geo",
|
|
3464
3486
|
"workflow",
|
|
3465
3487
|
"services",
|
|
3466
3488
|
"pages",
|
|
3467
3489
|
"dashboard",
|
|
3490
|
+
// auth-reconcile: wave 6 — post-manifest reconciliation
|
|
3491
|
+
// Reads auth-config.json + pages/dashboard/geo/workflow manifests;
|
|
3492
|
+
// emits auth-reconcile-config.json + stackwright.auth.yml (Gate 4 single source of truth)
|
|
3493
|
+
"auth-reconcile",
|
|
3494
|
+
// wave 7: polish — depends on auth-reconcile (replaces auth-config.json direct dep)
|
|
3468
3495
|
"polish",
|
|
3496
|
+
// wave 8: qa — terminal QA gate, depends on polish + transitively everything
|
|
3469
3497
|
"qa"
|
|
3470
3498
|
];
|
|
3471
3499
|
var PHASE_ARTIFACT = {
|
|
@@ -3479,6 +3507,7 @@ var PHASE_ARTIFACT = {
|
|
|
3479
3507
|
dashboard: "dashboard-manifest.json",
|
|
3480
3508
|
workflow: "workflow-config.json",
|
|
3481
3509
|
services: "services-config.json",
|
|
3510
|
+
"auth-reconcile": "auth-reconcile-config.json",
|
|
3482
3511
|
polish: "polish-manifest.json",
|
|
3483
3512
|
geo: "geo-manifest.json",
|
|
3484
3513
|
qa: "qa-findings.json"
|
|
@@ -3493,16 +3522,36 @@ var PHASE_TO_OTTER2 = {
|
|
|
3493
3522
|
pages: "stackwright-pro-page-otter",
|
|
3494
3523
|
dashboard: "stackwright-pro-dashboard-otter",
|
|
3495
3524
|
workflow: "stackwright-pro-form-wizard-otter",
|
|
3525
|
+
"auth-reconcile": "stackwright-pro-auth-reconcile-otter",
|
|
3496
3526
|
services: "stackwright-services-otter",
|
|
3497
3527
|
polish: "stackwright-pro-polish-otter",
|
|
3498
3528
|
geo: "stackwright-pro-geo-otter",
|
|
3499
3529
|
qa: "stackwright-pro-qa-otter"
|
|
3500
3530
|
};
|
|
3531
|
+
var AUTO_EXECUTE_PHASES = /* @__PURE__ */ new Set(["auth-reconcile"]);
|
|
3532
|
+
var PhaseStatusSchema = z14.object({
|
|
3533
|
+
status: z14.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending"),
|
|
3534
|
+
questionsCollected: z14.boolean().default(false),
|
|
3535
|
+
answered: z14.boolean().default(false),
|
|
3536
|
+
executed: z14.boolean().default(false),
|
|
3537
|
+
artifactWritten: z14.boolean().default(false),
|
|
3538
|
+
retryCount: z14.number().default(0),
|
|
3539
|
+
inFlight: z14.boolean().default(false)
|
|
3540
|
+
});
|
|
3541
|
+
var PipelineStateSchema = z14.object({
|
|
3542
|
+
version: z14.literal("1.0"),
|
|
3543
|
+
currentPhase: z14.string(),
|
|
3544
|
+
status: z14.enum(["setup", "questions", "execution", "done"]),
|
|
3545
|
+
phases: z14.record(z14.string(), PhaseStatusSchema),
|
|
3546
|
+
startedAt: z14.string(),
|
|
3547
|
+
updatedAt: z14.string()
|
|
3548
|
+
});
|
|
3501
3549
|
function isValidPhase2(phase) {
|
|
3502
3550
|
return PHASE_ORDER.includes(phase);
|
|
3503
3551
|
}
|
|
3504
3552
|
function defaultPhaseStatus() {
|
|
3505
3553
|
return {
|
|
3554
|
+
status: "pending",
|
|
3506
3555
|
questionsCollected: false,
|
|
3507
3556
|
answered: false,
|
|
3508
3557
|
executed: false,
|
|
@@ -3533,10 +3582,9 @@ function readState(cwd) {
|
|
|
3533
3582
|
const p = statePath(cwd);
|
|
3534
3583
|
if (!existsSync7(p)) return createDefaultState();
|
|
3535
3584
|
const raw = JSON.parse(safeReadSync(p));
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
return raw;
|
|
3585
|
+
const result = PipelineStateSchema.safeParse(raw);
|
|
3586
|
+
if (!result.success) return createDefaultState();
|
|
3587
|
+
return result.data;
|
|
3540
3588
|
}
|
|
3541
3589
|
function safeWriteSync(filePath, content) {
|
|
3542
3590
|
if (existsSync7(filePath)) {
|
|
@@ -3625,13 +3673,16 @@ function handleSetPipelineState(input) {
|
|
|
3625
3673
|
isError: true
|
|
3626
3674
|
};
|
|
3627
3675
|
}
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3676
|
+
if (input.field === "artifactWritten") {
|
|
3677
|
+
return {
|
|
3678
|
+
text: JSON.stringify({
|
|
3679
|
+
error: true,
|
|
3680
|
+
message: "Cannot set 'artifactWritten' directly. Use stackwright_pro_validate_artifact instead \u2014 it verifies the file exists on disk before flagging."
|
|
3681
|
+
}),
|
|
3682
|
+
isError: true
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3685
|
+
const VALID_FIELDS = ["questionsCollected", "answered", "executed", "inFlight"];
|
|
3635
3686
|
if (input.field && !VALID_FIELDS.includes(input.field)) {
|
|
3636
3687
|
return {
|
|
3637
3688
|
text: JSON.stringify({
|
|
@@ -3652,6 +3703,15 @@ function handleSetPipelineState(input) {
|
|
|
3652
3703
|
isError: true
|
|
3653
3704
|
};
|
|
3654
3705
|
}
|
|
3706
|
+
if (update.field === "artifactWritten") {
|
|
3707
|
+
return {
|
|
3708
|
+
text: JSON.stringify({
|
|
3709
|
+
error: true,
|
|
3710
|
+
message: "Cannot set 'artifactWritten' directly. Use stackwright_pro_validate_artifact instead \u2014 it verifies the file exists on disk before flagging."
|
|
3711
|
+
}),
|
|
3712
|
+
isError: true
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3655
3715
|
if (!VALID_FIELDS.includes(update.field)) {
|
|
3656
3716
|
return {
|
|
3657
3717
|
text: JSON.stringify({
|
|
@@ -3677,6 +3737,9 @@ function handleSetPipelineState(input) {
|
|
|
3677
3737
|
if (input.field && input.value !== void 0) {
|
|
3678
3738
|
phaseState[input.field] = input.value;
|
|
3679
3739
|
}
|
|
3740
|
+
if (input.phaseStatus !== void 0) {
|
|
3741
|
+
phaseState.status = input.phaseStatus;
|
|
3742
|
+
}
|
|
3680
3743
|
if (input.incrementRetry) {
|
|
3681
3744
|
phaseState.retryCount += 1;
|
|
3682
3745
|
}
|
|
@@ -3703,16 +3766,6 @@ function handleSetPipelineState(input) {
|
|
|
3703
3766
|
},
|
|
3704
3767
|
{ cwd }
|
|
3705
3768
|
);
|
|
3706
|
-
if (input.field === "artifactWritten" && input.value === true && input.phase) {
|
|
3707
|
-
emit({ type: "phase_complete", phase: input.phase }, { cwd });
|
|
3708
|
-
}
|
|
3709
|
-
if (input.updates) {
|
|
3710
|
-
for (const update of input.updates) {
|
|
3711
|
-
if (update.field === "artifactWritten" && update.value === true) {
|
|
3712
|
-
emit({ type: "phase_complete", phase: update.phase }, { cwd });
|
|
3713
|
-
}
|
|
3714
|
-
}
|
|
3715
|
-
}
|
|
3716
3769
|
} catch {
|
|
3717
3770
|
}
|
|
3718
3771
|
return { text: JSON.stringify(state), isError: false };
|
|
@@ -3732,6 +3785,9 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3732
3785
|
isError: true
|
|
3733
3786
|
};
|
|
3734
3787
|
}
|
|
3788
|
+
if (AUTO_EXECUTE_PHASES.has(phase)) {
|
|
3789
|
+
return { text: JSON.stringify({ ready: true, phase, autoExecute: true }), isError: false };
|
|
3790
|
+
}
|
|
3735
3791
|
const answerFile = join7(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3736
3792
|
if (!existsSync7(answerFile)) {
|
|
3737
3793
|
return {
|
|
@@ -3761,6 +3817,10 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3761
3817
|
const answeredPhases = [];
|
|
3762
3818
|
const missingPhases = [];
|
|
3763
3819
|
for (const phase2 of PHASE_ORDER) {
|
|
3820
|
+
if (AUTO_EXECUTE_PHASES.has(phase2)) {
|
|
3821
|
+
answeredPhases.push(phase2);
|
|
3822
|
+
continue;
|
|
3823
|
+
}
|
|
3764
3824
|
const answerFile = join7(answersDir, `${phase2}.json`);
|
|
3765
3825
|
if (existsSync7(answerFile)) {
|
|
3766
3826
|
try {
|
|
@@ -4089,6 +4149,8 @@ var PHASE_REQUIRED_KEYS = {
|
|
|
4089
4149
|
dashboard: ["version", "generatedBy"],
|
|
4090
4150
|
workflow: ["version", "generatedBy"],
|
|
4091
4151
|
services: ["version", "generatedBy", "flows"],
|
|
4152
|
+
// auth-reconcile: must have version, generatedBy, and merged protectedRoutes
|
|
4153
|
+
"auth-reconcile": ["version", "generatedBy", "protectedRoutes"],
|
|
4092
4154
|
polish: ["version", "generatedBy"],
|
|
4093
4155
|
geo: ["version", "generatedBy", "geoCollections"],
|
|
4094
4156
|
// qa: skipped=true path only needs version+generatedBy+skipped;
|
|
@@ -4368,6 +4430,28 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
4368
4430
|
null,
|
|
4369
4431
|
2
|
|
4370
4432
|
),
|
|
4433
|
+
"auth-reconcile": JSON.stringify(
|
|
4434
|
+
{
|
|
4435
|
+
version: "1.0",
|
|
4436
|
+
generatedBy: "stackwright-pro-auth-reconcile-otter",
|
|
4437
|
+
authType: "<oidc|saml|cac|oauth2>",
|
|
4438
|
+
rbacRoles: ["<HIGHEST_ROLE>", "<MID_ROLE>", "<LOWEST_ROLE>"],
|
|
4439
|
+
rbacDefaultRole: "<LOWEST_ROLE>",
|
|
4440
|
+
protectedRoutes: [
|
|
4441
|
+
{ pattern: "/dashboard", requiredRole: "<role>" },
|
|
4442
|
+
{ pattern: "/dashboard/:path*", requiredRole: "<role>" }
|
|
4443
|
+
],
|
|
4444
|
+
uncoveredSlugs: [],
|
|
4445
|
+
manifestsRead: ["pages-manifest.json", "dashboard-manifest.json", "geo-manifest.json"],
|
|
4446
|
+
summary: {
|
|
4447
|
+
totalRoutes: 2,
|
|
4448
|
+
manifestDerived: 2,
|
|
4449
|
+
crossCutting: 0
|
|
4450
|
+
}
|
|
4451
|
+
},
|
|
4452
|
+
null,
|
|
4453
|
+
2
|
|
4454
|
+
),
|
|
4371
4455
|
polish: JSON.stringify(
|
|
4372
4456
|
{
|
|
4373
4457
|
version: "1.0",
|
|
@@ -4791,6 +4875,7 @@ function _validateArtifactInner(input) {
|
|
|
4791
4875
|
if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
|
|
4792
4876
|
const ps = state.phases[phase];
|
|
4793
4877
|
ps.artifactWritten = true;
|
|
4878
|
+
ps.status = "completed";
|
|
4794
4879
|
});
|
|
4795
4880
|
const topKeys = Object.keys(artifact).slice(0, 5).join(", ");
|
|
4796
4881
|
const result = {
|
|
@@ -4818,6 +4903,7 @@ function handleValidateArtifact(input) {
|
|
|
4818
4903
|
const parsed = JSON.parse(result.text);
|
|
4819
4904
|
if (parsed.valid === true && parsed.artifactPath) {
|
|
4820
4905
|
emit({ type: "file_write", path: parsed.artifactPath, phase }, { cwd });
|
|
4906
|
+
emit({ type: "phase_complete", phase }, { cwd });
|
|
4821
4907
|
}
|
|
4822
4908
|
emit(
|
|
4823
4909
|
{
|
|
@@ -4918,12 +5004,19 @@ function registerPipelineTools(server2) {
|
|
|
4918
5004
|
`Atomic read\u2192modify\u2192write pipeline state. ${DESC}`,
|
|
4919
5005
|
{
|
|
4920
5006
|
phase: z14.string().optional().describe('Phase to update, e.g. "designer"'),
|
|
4921
|
-
field: z14.enum(["questionsCollected", "answered", "executed", "
|
|
5007
|
+
field: z14.enum(["questionsCollected", "answered", "executed", "inFlight"]).optional().describe(
|
|
5008
|
+
"Boolean field to set. NOTE: artifactWritten is intentionally absent \u2014 use stackwright_pro_validate_artifact instead (it verifies the file exists on disk before flagging)."
|
|
5009
|
+
),
|
|
4922
5010
|
// inFlight: set true BEFORE specialist invoke (dataflow mode, swp-ioc7); false after.
|
|
4923
5011
|
value: boolCoerce(z14.boolean().optional()).describe(
|
|
4924
5012
|
'Value for the field \u2014 must be a JSON boolean (true/false), NOT the string "true"/"false"'
|
|
4925
5013
|
),
|
|
4926
|
-
|
|
5014
|
+
// phaseStatus: per-phase execution enum — DISTINCT from top-level `status` (pipeline flow).
|
|
5015
|
+
// Named `phaseStatus` to avoid ambiguity. Sets PhaseStatus.status on the given phase.
|
|
5016
|
+
phaseStatus: z14.enum(["pending", "running", "completed", "skipped", "failed"]).optional().describe(
|
|
5017
|
+
"Per-phase execution status (pending/running/completed/skipped/failed). Distinct from top-level `status` (pipeline flow). Use phaseStatus: 'skipped' when a specialist otter is unregistered."
|
|
5018
|
+
),
|
|
5019
|
+
status: z14.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level pipeline flow status override \u2014 distinct from phaseStatus"),
|
|
4927
5020
|
incrementRetry: boolCoerce(z14.boolean().optional()).describe(
|
|
4928
5021
|
"Bump retryCount by 1 \u2014 must be a JSON boolean"
|
|
4929
5022
|
),
|
|
@@ -4935,8 +5028,8 @@ function registerPipelineTools(server2) {
|
|
|
4935
5028
|
"questionsCollected",
|
|
4936
5029
|
"answered",
|
|
4937
5030
|
"executed",
|
|
4938
|
-
"artifactWritten",
|
|
4939
5031
|
"inFlight"
|
|
5032
|
+
// artifactWritten intentionally absent — use validate_artifact (swp-og9c)
|
|
4940
5033
|
]),
|
|
4941
5034
|
value: z14.boolean()
|
|
4942
5035
|
})
|
|
@@ -4948,6 +5041,7 @@ function registerPipelineTools(server2) {
|
|
|
4948
5041
|
...args.phase != null ? { phase: args.phase } : {},
|
|
4949
5042
|
...args.field != null ? { field: args.field } : {},
|
|
4950
5043
|
...args.value != null ? { value: args.value } : {},
|
|
5044
|
+
...args.phaseStatus != null ? { phaseStatus: args.phaseStatus } : {},
|
|
4951
5045
|
...args.status != null ? { status: args.status } : {},
|
|
4952
5046
|
...args.incrementRetry != null ? { incrementRetry: args.incrementRetry } : {},
|
|
4953
5047
|
...args.updates != null ? { updates: args.updates } : {}
|
|
@@ -5109,6 +5203,20 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
5109
5203
|
description: "Stackwright config (themeName + customTheme writeback)"
|
|
5110
5204
|
}
|
|
5111
5205
|
],
|
|
5206
|
+
// swp-hng0: auth-reconcile otter — reads all manifests, re-emits auth-reconcile-config.json
|
|
5207
|
+
// AND overwrites stackwright.auth.yml (Gate 4: single source of truth after reconciliation)
|
|
5208
|
+
"stackwright-pro-auth-reconcile-otter": [
|
|
5209
|
+
{
|
|
5210
|
+
prefix: ".stackwright/artifacts/",
|
|
5211
|
+
suffix: ".json",
|
|
5212
|
+
description: "Auth reconcile artifact (auth-reconcile-config.json)"
|
|
5213
|
+
},
|
|
5214
|
+
{
|
|
5215
|
+
prefix: "stackwright.auth.",
|
|
5216
|
+
suffix: ".yml",
|
|
5217
|
+
description: "Canonical auth sidecar (stackwright.auth.yml \u2014 Gate 4 rewrite)"
|
|
5218
|
+
}
|
|
5219
|
+
],
|
|
5112
5220
|
"stackwright-pro-auth-otter": [
|
|
5113
5221
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Auth config artifact" },
|
|
5114
5222
|
{
|
|
@@ -6532,7 +6640,11 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6532
6640
|
],
|
|
6533
6641
|
[
|
|
6534
6642
|
"stackwright-pro-auth-otter.json",
|
|
6535
|
-
"
|
|
6643
|
+
"c9302132ac4180c70dd452814eb4aa4fb9a1d6a9683f6e01ffce509a6001c6cf"
|
|
6644
|
+
],
|
|
6645
|
+
[
|
|
6646
|
+
"stackwright-pro-auth-reconcile-otter.json",
|
|
6647
|
+
"7ffd67d51568c7b2a1eb2ea0ac2b2a8041eb9ddd9c1eb5b95744e55ba049b385"
|
|
6536
6648
|
],
|
|
6537
6649
|
[
|
|
6538
6650
|
"stackwright-pro-dashboard-otter.json",
|
|
@@ -6540,7 +6652,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6540
6652
|
],
|
|
6541
6653
|
[
|
|
6542
6654
|
"stackwright-pro-data-otter.json",
|
|
6543
|
-
"
|
|
6655
|
+
"7d1de846488d84eebf8798aef4d75f4f4d250c1ae070ce1200ead3bb8a545eaf"
|
|
6544
6656
|
],
|
|
6545
6657
|
[
|
|
6546
6658
|
"stackwright-pro-designer-otter.json",
|
|
@@ -6552,7 +6664,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6552
6664
|
],
|
|
6553
6665
|
[
|
|
6554
6666
|
"stackwright-pro-foreman-otter.json",
|
|
6555
|
-
"
|
|
6667
|
+
"65aa3273274b2b1859d432c3d1166c4a73971861036bf18c35acd8a5357fcf26"
|
|
6556
6668
|
],
|
|
6557
6669
|
[
|
|
6558
6670
|
"stackwright-pro-form-wizard-otter.json",
|
|
@@ -6568,7 +6680,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6568
6680
|
],
|
|
6569
6681
|
[
|
|
6570
6682
|
"stackwright-pro-polish-otter.json",
|
|
6571
|
-
"
|
|
6683
|
+
"3ae1252a60727ebca67e33a807d5061cafb88de52f3b98a9d4641f9db725a6ed"
|
|
6572
6684
|
],
|
|
6573
6685
|
[
|
|
6574
6686
|
"stackwright-pro-qa-otter.json",
|
|
@@ -8482,12 +8594,181 @@ function registerListSpecsTool(server2) {
|
|
|
8482
8594
|
);
|
|
8483
8595
|
}
|
|
8484
8596
|
|
|
8485
|
-
// src/tools/
|
|
8597
|
+
// src/tools/describe-endpoint.ts
|
|
8486
8598
|
import { z as z25 } from "zod";
|
|
8487
|
-
import {
|
|
8599
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
8600
|
+
import { extname as extname2 } from "path";
|
|
8601
|
+
import yaml2 from "js-yaml";
|
|
8602
|
+
function resolveRef(root, ref) {
|
|
8603
|
+
if (!ref.startsWith("#/")) {
|
|
8604
|
+
throw new Error(`[describe_endpoint] External $refs not supported: "${ref}"`);
|
|
8605
|
+
}
|
|
8606
|
+
const parts = ref.slice(2).split("/");
|
|
8607
|
+
let current = root;
|
|
8608
|
+
for (const part of parts) {
|
|
8609
|
+
const key = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
8610
|
+
if (current == null || typeof current !== "object") {
|
|
8611
|
+
throw new Error(
|
|
8612
|
+
`[describe_endpoint] $ref "${ref}" cannot be resolved \u2014 segment "${key}" not found`
|
|
8613
|
+
);
|
|
8614
|
+
}
|
|
8615
|
+
current = current[key];
|
|
8616
|
+
}
|
|
8617
|
+
if (current == null || typeof current !== "object") {
|
|
8618
|
+
throw new Error(`[describe_endpoint] $ref "${ref}" resolved to a non-object value`);
|
|
8619
|
+
}
|
|
8620
|
+
return current;
|
|
8621
|
+
}
|
|
8622
|
+
function deref(root, obj) {
|
|
8623
|
+
if (obj == null || typeof obj !== "object") return {};
|
|
8624
|
+
const o = obj;
|
|
8625
|
+
if (typeof o["$ref"] === "string") {
|
|
8626
|
+
const resolved = resolveRef(root, o["$ref"]);
|
|
8627
|
+
return deref(root, resolved);
|
|
8628
|
+
}
|
|
8629
|
+
return o;
|
|
8630
|
+
}
|
|
8631
|
+
function loadSpec(specPath) {
|
|
8632
|
+
const raw = readFileSync16(specPath, "utf-8");
|
|
8633
|
+
const ext = extname2(specPath).toLowerCase();
|
|
8634
|
+
if (ext === ".json") {
|
|
8635
|
+
return JSON.parse(raw);
|
|
8636
|
+
}
|
|
8637
|
+
const parsed = yaml2.load(raw);
|
|
8638
|
+
if (parsed == null || typeof parsed !== "object") {
|
|
8639
|
+
throw new Error(`[describe_endpoint] Spec file "${specPath}" did not parse to an object`);
|
|
8640
|
+
}
|
|
8641
|
+
return parsed;
|
|
8642
|
+
}
|
|
8643
|
+
function extractParams(root, pathItem, operation) {
|
|
8644
|
+
const pathLevelRaw = Array.isArray(pathItem["parameters"]) ? pathItem["parameters"] : [];
|
|
8645
|
+
const opLevelRaw = Array.isArray(operation["parameters"]) ? operation["parameters"] : [];
|
|
8646
|
+
const merged = /* @__PURE__ */ new Map();
|
|
8647
|
+
for (const raw of [...pathLevelRaw, ...opLevelRaw]) {
|
|
8648
|
+
const param = deref(root, raw);
|
|
8649
|
+
const name = String(param["name"] ?? "");
|
|
8650
|
+
const inVal = String(param["in"] ?? "");
|
|
8651
|
+
if (name && inVal) {
|
|
8652
|
+
merged.set(`${name}:${inVal}`, param);
|
|
8653
|
+
}
|
|
8654
|
+
}
|
|
8655
|
+
const pathParams = [];
|
|
8656
|
+
const queryParams = [];
|
|
8657
|
+
for (const param of merged.values()) {
|
|
8658
|
+
const inVal = String(param["in"] ?? "");
|
|
8659
|
+
const name = String(param["name"] ?? "");
|
|
8660
|
+
const schema = deref(root, param["schema"] ?? {});
|
|
8661
|
+
if (inVal === "path") {
|
|
8662
|
+
pathParams.push({ name, in: "path", required: true, schema });
|
|
8663
|
+
} else if (inVal === "query") {
|
|
8664
|
+
const required = param["required"] === true;
|
|
8665
|
+
queryParams.push({ name, in: "query", required, schema });
|
|
8666
|
+
}
|
|
8667
|
+
}
|
|
8668
|
+
return { pathParams, queryParams };
|
|
8669
|
+
}
|
|
8670
|
+
function extractSecurity(root, globalSecurity, operationSecurity) {
|
|
8671
|
+
const securityRequirements = operationSecurity !== void 0 ? operationSecurity : globalSecurity;
|
|
8672
|
+
if (securityRequirements.length === 0) return [];
|
|
8673
|
+
const schemes = root["components"]?.["securitySchemes"];
|
|
8674
|
+
if (!schemes || typeof schemes !== "object") return [];
|
|
8675
|
+
const result = [];
|
|
8676
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8677
|
+
for (const req of securityRequirements) {
|
|
8678
|
+
if (!req || typeof req !== "object") continue;
|
|
8679
|
+
for (const schemeName of Object.keys(req)) {
|
|
8680
|
+
if (seen.has(schemeName)) continue;
|
|
8681
|
+
seen.add(schemeName);
|
|
8682
|
+
const schemeRaw = schemes[schemeName];
|
|
8683
|
+
if (!schemeRaw) continue;
|
|
8684
|
+
const scheme = deref(root, schemeRaw);
|
|
8685
|
+
const type = String(scheme["type"] ?? "unknown");
|
|
8686
|
+
if (type === "apiKey") {
|
|
8687
|
+
const inVal = String(scheme["in"] ?? "header");
|
|
8688
|
+
const paramName = String(scheme["name"] ?? schemeName);
|
|
8689
|
+
result.push({ name: schemeName, type, in: inVal, paramName });
|
|
8690
|
+
} else if (type === "http") {
|
|
8691
|
+
const httpScheme = String(scheme["scheme"] ?? "bearer").toLowerCase();
|
|
8692
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8693
|
+
void httpScheme;
|
|
8694
|
+
} else if (type === "oauth2") {
|
|
8695
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8696
|
+
} else if (type === "openIdConnect") {
|
|
8697
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8698
|
+
} else {
|
|
8699
|
+
result.push({ name: schemeName, type, in: "unknown", paramName: schemeName });
|
|
8700
|
+
}
|
|
8701
|
+
}
|
|
8702
|
+
}
|
|
8703
|
+
return result;
|
|
8704
|
+
}
|
|
8705
|
+
function handleDescribeEndpoint(input) {
|
|
8706
|
+
const { specPath, path: path5, method } = input;
|
|
8707
|
+
const normalizedMethod = method.toLowerCase();
|
|
8708
|
+
const root = loadSpec(specPath);
|
|
8709
|
+
const globalSecurity = Array.isArray(root["security"]) ? root["security"] : [];
|
|
8710
|
+
const paths = root["paths"];
|
|
8711
|
+
if (!paths) {
|
|
8712
|
+
return { pathParams: [], queryParams: [], security: [] };
|
|
8713
|
+
}
|
|
8714
|
+
const pathItem = deref(root, paths[path5]);
|
|
8715
|
+
if (!pathItem || Object.keys(pathItem).length === 0) {
|
|
8716
|
+
throw new Error(`[describe_endpoint] Path "${path5}" not found in spec "${specPath}"`);
|
|
8717
|
+
}
|
|
8718
|
+
const operation = deref(root, pathItem[normalizedMethod]);
|
|
8719
|
+
if (!operation || Object.keys(operation).length === 0) {
|
|
8720
|
+
throw new Error(
|
|
8721
|
+
`[describe_endpoint] Method "${method.toUpperCase()}" not found at path "${path5}" in spec "${specPath}"`
|
|
8722
|
+
);
|
|
8723
|
+
}
|
|
8724
|
+
const { pathParams, queryParams } = extractParams(root, pathItem, operation);
|
|
8725
|
+
const operationSecurity = Array.isArray(operation["security"]) ? operation["security"] : void 0;
|
|
8726
|
+
const security = extractSecurity(root, globalSecurity, operationSecurity);
|
|
8727
|
+
return { pathParams, queryParams, security };
|
|
8728
|
+
}
|
|
8729
|
+
function registerDescribeEndpointTool(server2) {
|
|
8730
|
+
server2.tool(
|
|
8731
|
+
"stackwright_pro_describe_endpoint",
|
|
8732
|
+
[
|
|
8733
|
+
"Inspect an OpenAPI 3.0/3.1 spec and return resolved parameter and security details for a",
|
|
8734
|
+
"specific operation. Deterministic \u2014 no LLM. Handles $ref resolution within the spec document.",
|
|
8735
|
+
"",
|
|
8736
|
+
"Returns:",
|
|
8737
|
+
" pathParams \u2014 path parameters [{name, in, required, schema}]",
|
|
8738
|
+
" queryParams \u2014 query parameters [{name, in, required, schema}]",
|
|
8739
|
+
" security \u2014 applicable security schemes [{name, type, in, paramName}]",
|
|
8740
|
+
"",
|
|
8741
|
+
"Used by the data-otter to populate params: entries in stackwright.collections.yml",
|
|
8742
|
+
"from required query params and resolved auth query params (swp-8r8i + swp-1l6l)."
|
|
8743
|
+
].join("\n"),
|
|
8744
|
+
{
|
|
8745
|
+
specPath: z25.string().describe("Absolute path to the OpenAPI spec file (.yaml, .yml, or .json)"),
|
|
8746
|
+
path: z25.string().describe('The API path to inspect (e.g. "/stations/{stationId}/observations")'),
|
|
8747
|
+
method: z25.string().describe('HTTP method (case-insensitive: "get", "post", "put", etc.)')
|
|
8748
|
+
},
|
|
8749
|
+
async ({ specPath, path: path5, method }) => {
|
|
8750
|
+
try {
|
|
8751
|
+
const result = handleDescribeEndpoint({ specPath, path: path5, method });
|
|
8752
|
+
return {
|
|
8753
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
8754
|
+
};
|
|
8755
|
+
} catch (err) {
|
|
8756
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8757
|
+
return {
|
|
8758
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
8759
|
+
isError: true
|
|
8760
|
+
};
|
|
8761
|
+
}
|
|
8762
|
+
}
|
|
8763
|
+
);
|
|
8764
|
+
}
|
|
8765
|
+
|
|
8766
|
+
// src/tools/consolidate-integrations.ts
|
|
8767
|
+
import { z as z26 } from "zod";
|
|
8768
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync9 } from "fs";
|
|
8488
8769
|
import { lockSync as lockSync2 } from "proper-lockfile";
|
|
8489
8770
|
import { join as join17, basename as basename3 } from "path";
|
|
8490
|
-
import
|
|
8771
|
+
import yaml3 from "js-yaml";
|
|
8491
8772
|
var BASE_MOCK_PORT = 4011;
|
|
8492
8773
|
var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
8493
8774
|
function deriveNameFromFilename(filename) {
|
|
@@ -8520,7 +8801,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8520
8801
|
entries.forEach((filename, index) => {
|
|
8521
8802
|
let artifact = {};
|
|
8522
8803
|
try {
|
|
8523
|
-
const raw =
|
|
8804
|
+
const raw = readFileSync17(join17(artifactsDir, filename), "utf-8");
|
|
8524
8805
|
artifact = JSON.parse(raw);
|
|
8525
8806
|
} catch {
|
|
8526
8807
|
}
|
|
@@ -8557,8 +8838,8 @@ function handleConsolidateIntegrations(input) {
|
|
|
8557
8838
|
let existing = {};
|
|
8558
8839
|
if (existsSync16(integrationsYmlPath)) {
|
|
8559
8840
|
try {
|
|
8560
|
-
const raw =
|
|
8561
|
-
existing =
|
|
8841
|
+
const raw = readFileSync17(integrationsYmlPath, "utf-8");
|
|
8842
|
+
existing = yaml3.load(raw) ?? {};
|
|
8562
8843
|
} catch {
|
|
8563
8844
|
existing = {};
|
|
8564
8845
|
}
|
|
@@ -8575,7 +8856,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8575
8856
|
try {
|
|
8576
8857
|
const header = `# stackwright.integrations.yml \u2014 Auto-generated by stackwright_pro_consolidate_integrations
|
|
8577
8858
|
`;
|
|
8578
|
-
const body =
|
|
8859
|
+
const body = yaml3.dump(merged, { lineWidth: 120, noRefs: true });
|
|
8579
8860
|
writeFileSync10(integrationsYmlPath, header + body, "utf-8");
|
|
8580
8861
|
} finally {
|
|
8581
8862
|
release();
|
|
@@ -8604,7 +8885,7 @@ function registerConsolidateIntegrationsTool(server2) {
|
|
|
8604
8885
|
"no per-spec artifacts are found."
|
|
8605
8886
|
].join(" "),
|
|
8606
8887
|
{
|
|
8607
|
-
projectRoot:
|
|
8888
|
+
projectRoot: z26.string().describe("Absolute path to the project root directory")
|
|
8608
8889
|
},
|
|
8609
8890
|
async ({ projectRoot }) => {
|
|
8610
8891
|
try {
|
|
@@ -8662,7 +8943,7 @@ var package_default = {
|
|
|
8662
8943
|
"test:coverage": "vitest run --coverage"
|
|
8663
8944
|
},
|
|
8664
8945
|
name: "@stackwright-pro/mcp",
|
|
8665
|
-
version: "0.2.0-alpha.
|
|
8946
|
+
version: "0.2.0-alpha.113",
|
|
8666
8947
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
8667
8948
|
license: "SEE LICENSE IN LICENSE",
|
|
8668
8949
|
main: "./dist/server.js",
|
|
@@ -8745,6 +9026,7 @@ registerCompileTools(server);
|
|
|
8745
9026
|
registerStripLegacyIntegrationsTool(server);
|
|
8746
9027
|
registerEmitterTools(server);
|
|
8747
9028
|
registerListSpecsTool(server);
|
|
9029
|
+
registerDescribeEndpointTool(server);
|
|
8748
9030
|
registerConsolidateIntegrationsTool(server);
|
|
8749
9031
|
registerContentTypeTools(server);
|
|
8750
9032
|
registerPageTools(server);
|