@stackwright-pro/mcp 0.2.0-alpha.109 → 0.2.0-alpha.112
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 +3 -3
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +3 -3
- package/dist/integrity.mjs.map +1 -1
- package/dist/pipeline-constants.js +41 -21
- package/dist/pipeline-constants.js.map +1 -1
- package/dist/pipeline-constants.mjs +41 -21
- package/dist/pipeline-constants.mjs.map +1 -1
- package/dist/server.js +322 -182
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +322 -182
- package/dist/server.mjs.map +1 -1
- package/package.json +7 -7
package/dist/server.mjs
CHANGED
|
@@ -849,6 +849,9 @@ var BASELINE_DEPS = {
|
|
|
849
849
|
"@stackwright-pro/openapi": "latest",
|
|
850
850
|
"@stackwright-pro/auth": "latest",
|
|
851
851
|
"@stackwright-pro/auth-nextjs": "latest",
|
|
852
|
+
// SBOM compliance — every Pro raft is a compliance product (swp-ryqz)
|
|
853
|
+
"@stackwright-pro/sbom-enterprise": "latest",
|
|
854
|
+
"@stackwright/sbom-generator": "^0.2.2",
|
|
852
855
|
zod: "^3.23.0"
|
|
853
856
|
};
|
|
854
857
|
var BASELINE_DEV_DEPS = {
|
|
@@ -2002,7 +2005,7 @@ function registerOrchestrationTools(server2) {
|
|
|
2002
2005
|
}
|
|
2003
2006
|
|
|
2004
2007
|
// src/tools/pipeline.ts
|
|
2005
|
-
import { z as
|
|
2008
|
+
import { z as z14 } from "zod";
|
|
2006
2009
|
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync7, mkdirSync as mkdirSync4, lstatSync as lstatSync6, readdirSync as readdirSync5 } from "fs";
|
|
2007
2010
|
import { lockSync } from "proper-lockfile";
|
|
2008
2011
|
import { join as join7 } from "path";
|
|
@@ -2742,6 +2745,19 @@ function validateAuthConfig(artifact, artifactPath) {
|
|
|
2742
2745
|
// src/tools/auth-manifest-aggregator.ts
|
|
2743
2746
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync3 } from "fs";
|
|
2744
2747
|
import { join as join5 } from "path";
|
|
2748
|
+
import { z as z11 } from "zod";
|
|
2749
|
+
function computeLowestRole(requiredRoles, rbacRoles) {
|
|
2750
|
+
let lowestIndex = -1;
|
|
2751
|
+
let lowestRole = requiredRoles[0];
|
|
2752
|
+
for (const role of requiredRoles) {
|
|
2753
|
+
const idx = rbacRoles.indexOf(role);
|
|
2754
|
+
if (idx > lowestIndex) {
|
|
2755
|
+
lowestIndex = idx;
|
|
2756
|
+
lowestRole = role;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
return lowestRole;
|
|
2760
|
+
}
|
|
2745
2761
|
function extractPatternBaseSlug(pattern) {
|
|
2746
2762
|
const stripped = pattern.replace(/^\//, "");
|
|
2747
2763
|
if (!stripped) return null;
|
|
@@ -2775,12 +2791,101 @@ function extractManifestSlugs(artifactsDir) {
|
|
|
2775
2791
|
}
|
|
2776
2792
|
return slugs;
|
|
2777
2793
|
}
|
|
2794
|
+
function normalizeNextJsSlug(slug) {
|
|
2795
|
+
return slug.replace(/\[\[\.\.\.([\w]+)\]\]/g, ":$1*").replace(/\[\.\.\.([\w]+)\]/g, ":$1*").replace(/\[([\w]+)\]/g, ":$1");
|
|
2796
|
+
}
|
|
2797
|
+
function deriveProtectedRoutes(pages, rbacRoles) {
|
|
2798
|
+
const protectedRoutes = [];
|
|
2799
|
+
const uncoveredSlugs = [];
|
|
2800
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2801
|
+
for (const page of pages) {
|
|
2802
|
+
if (seen.has(page.slug)) continue;
|
|
2803
|
+
seen.add(page.slug);
|
|
2804
|
+
if (page.authRequired === false) continue;
|
|
2805
|
+
if (page.authRequired === true && Array.isArray(page.requiredRoles) && page.requiredRoles.length > 0) {
|
|
2806
|
+
const normalizedSlug = normalizeNextJsSlug(page.slug);
|
|
2807
|
+
const lowestRole = computeLowestRole(page.requiredRoles, rbacRoles);
|
|
2808
|
+
protectedRoutes.push({ pattern: `/${normalizedSlug}`, requiredRole: lowestRole });
|
|
2809
|
+
if (!normalizedSlug.endsWith("*")) {
|
|
2810
|
+
protectedRoutes.push({ pattern: `/${normalizedSlug}/:path*`, requiredRole: lowestRole });
|
|
2811
|
+
}
|
|
2812
|
+
continue;
|
|
2813
|
+
}
|
|
2814
|
+
const reason = page.authRequired === true ? "authRequired: true but requiredRoles is missing or empty" : "page emitter did not declare authRequired + requiredRoles";
|
|
2815
|
+
uncoveredSlugs.push({ source: page.source, slug: page.slug, reason });
|
|
2816
|
+
}
|
|
2817
|
+
return { protectedRoutes, uncoveredSlugs };
|
|
2818
|
+
}
|
|
2819
|
+
function readAllManifestPages(artifactsDir) {
|
|
2820
|
+
const allPages = [];
|
|
2821
|
+
if (!existsSync6(artifactsDir)) return allPages;
|
|
2822
|
+
let entries;
|
|
2823
|
+
try {
|
|
2824
|
+
entries = readdirSync3(artifactsDir);
|
|
2825
|
+
} catch {
|
|
2826
|
+
return allPages;
|
|
2827
|
+
}
|
|
2828
|
+
for (const entry of entries) {
|
|
2829
|
+
if (!entry.endsWith("-manifest.json")) continue;
|
|
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
|
+
}
|
|
2846
|
+
return allPages;
|
|
2847
|
+
}
|
|
2848
|
+
function registerReadAuthManifestsTool(server2) {
|
|
2849
|
+
server2.tool(
|
|
2850
|
+
"stackwright_pro_read_auth_manifests",
|
|
2851
|
+
"Read ALL *-manifest.json files from the pipeline artifacts directory (pages-manifest.json, dashboard-manifest.json, geo-manifest.json, etc.) and deterministically derive protectedRoutes and uncoveredSlugs. Uses the both-forms invariant (swp-n7kk): every authRequired route registers BOTH /{slug} (exact) AND /{slug}/:path* (sub-path). Call this in Step 1 of auth configuration instead of manually reading each manifest file. Fixes the Bead C issue where dashboard-manifest.json and geo-manifest.json were silently missed.",
|
|
2852
|
+
{
|
|
2853
|
+
rbacRoles: z11.array(z11.string()).describe(
|
|
2854
|
+
'RBAC role hierarchy in descending privilege order, e.g. ["ESF8_COORDINATOR", "HOSPITAL_EM", "OBSERVER"]. Used to compute the lowestRole for each authRequired page.'
|
|
2855
|
+
),
|
|
2856
|
+
artifactsDir: z11.string().optional().describe(
|
|
2857
|
+
"Absolute or relative path to the pipeline artifacts directory. Defaults to .stackwright/artifacts relative to cwd."
|
|
2858
|
+
)
|
|
2859
|
+
},
|
|
2860
|
+
(params) => {
|
|
2861
|
+
const cwd = process.cwd();
|
|
2862
|
+
const artifactsDir = params.artifactsDir ? params.artifactsDir : join5(cwd, ".stackwright", "artifacts");
|
|
2863
|
+
const allPages = readAllManifestPages(artifactsDir);
|
|
2864
|
+
const { protectedRoutes, uncoveredSlugs } = deriveProtectedRoutes(allPages, params.rbacRoles);
|
|
2865
|
+
const manifestsRead = [...new Set(allPages.map((p) => p.source))].sort();
|
|
2866
|
+
const result = {
|
|
2867
|
+
protectedRoutes,
|
|
2868
|
+
uncoveredSlugs,
|
|
2869
|
+
manifestsRead,
|
|
2870
|
+
summary: {
|
|
2871
|
+
totalPages: allPages.length,
|
|
2872
|
+
protectedCount: protectedRoutes.length,
|
|
2873
|
+
uncoveredCount: uncoveredSlugs.length,
|
|
2874
|
+
artifactsDir
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
return {
|
|
2878
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
);
|
|
2882
|
+
}
|
|
2778
2883
|
|
|
2779
2884
|
// src/tools/pipeline.ts
|
|
2780
2885
|
import { WorkflowFileSchema, authConfigSchema as authConfigSchema3 } from "@stackwright-pro/types";
|
|
2781
2886
|
|
|
2782
2887
|
// src/tools/get-schema.ts
|
|
2783
|
-
import { z as
|
|
2888
|
+
import { z as z13 } from "zod";
|
|
2784
2889
|
import {
|
|
2785
2890
|
WorkflowStepSchema as WorkflowStepSchema2,
|
|
2786
2891
|
WorkflowDefinitionSchema as WorkflowDefinitionSchema2,
|
|
@@ -2790,7 +2895,7 @@ import {
|
|
|
2790
2895
|
} from "@stackwright-pro/types";
|
|
2791
2896
|
|
|
2792
2897
|
// src/tools/validate-yaml-fragment.ts
|
|
2793
|
-
import { z as
|
|
2898
|
+
import { z as z12 } from "zod";
|
|
2794
2899
|
import { load as yamlLoad2 } from "js-yaml";
|
|
2795
2900
|
import {
|
|
2796
2901
|
WorkflowStepSchema,
|
|
@@ -2807,18 +2912,18 @@ var SUPPORTED_SCHEMA_NAMES = [
|
|
|
2807
2912
|
"navigation_item",
|
|
2808
2913
|
"auth_config"
|
|
2809
2914
|
];
|
|
2810
|
-
var NavigationLinkSchema =
|
|
2811
|
-
() =>
|
|
2812
|
-
label:
|
|
2813
|
-
href:
|
|
2814
|
-
children:
|
|
2915
|
+
var NavigationLinkSchema = z12.lazy(
|
|
2916
|
+
() => z12.object({
|
|
2917
|
+
label: z12.string().min(1),
|
|
2918
|
+
href: z12.string().min(1),
|
|
2919
|
+
children: z12.array(NavigationLinkSchema).optional()
|
|
2815
2920
|
})
|
|
2816
2921
|
);
|
|
2817
|
-
var NavigationSectionSchema =
|
|
2818
|
-
section:
|
|
2819
|
-
items:
|
|
2922
|
+
var NavigationSectionSchema = z12.object({
|
|
2923
|
+
section: z12.string().min(1),
|
|
2924
|
+
items: z12.array(NavigationLinkSchema)
|
|
2820
2925
|
});
|
|
2821
|
-
var NavigationItemSchema =
|
|
2926
|
+
var NavigationItemSchema = z12.union([
|
|
2822
2927
|
NavigationLinkSchema,
|
|
2823
2928
|
NavigationSectionSchema
|
|
2824
2929
|
]);
|
|
@@ -2920,10 +3025,10 @@ function registerValidateYamlFragmentTool(server2) {
|
|
|
2920
3025
|
"stackwright_pro_validate_yaml_fragment",
|
|
2921
3026
|
'Validates a YAML snippet against a canonical Zod schema before writing to disk. Returns actionable errors with "did you mean?" suggestions for common field-name drift (title vs label, id vs name, style vs theme.variant, etc.). Call this before stackwright_pro_safe_write to catch schema violations in-session.',
|
|
2922
3027
|
{
|
|
2923
|
-
schemaName:
|
|
3028
|
+
schemaName: z12.enum(SUPPORTED_SCHEMA_NAMES).describe(
|
|
2924
3029
|
"Schema to validate against. One of: workflow_step, workflow_definition, workflow_field, workflow_action, navigation_item, auth_config"
|
|
2925
3030
|
),
|
|
2926
|
-
yaml:
|
|
3031
|
+
yaml: z12.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
|
|
2927
3032
|
},
|
|
2928
3033
|
async ({ schemaName, yaml: yaml3 }) => {
|
|
2929
3034
|
const result = handleValidateYamlFragment({ schemaName, yaml: yaml3 });
|
|
@@ -2935,18 +3040,18 @@ function registerValidateYamlFragmentTool(server2) {
|
|
|
2935
3040
|
}
|
|
2936
3041
|
|
|
2937
3042
|
// src/tools/get-schema.ts
|
|
2938
|
-
var NavigationLinkSchema2 =
|
|
2939
|
-
() =>
|
|
2940
|
-
label:
|
|
2941
|
-
href:
|
|
2942
|
-
children:
|
|
3043
|
+
var NavigationLinkSchema2 = z13.lazy(
|
|
3044
|
+
() => z13.object({
|
|
3045
|
+
label: z13.string().min(1),
|
|
3046
|
+
href: z13.string().min(1),
|
|
3047
|
+
children: z13.array(NavigationLinkSchema2).optional()
|
|
2943
3048
|
})
|
|
2944
3049
|
);
|
|
2945
|
-
var NavigationSectionSchema2 =
|
|
2946
|
-
section:
|
|
2947
|
-
items:
|
|
3050
|
+
var NavigationSectionSchema2 = z13.object({
|
|
3051
|
+
section: z13.string().min(1),
|
|
3052
|
+
items: z13.array(NavigationLinkSchema2)
|
|
2948
3053
|
});
|
|
2949
|
-
var NavigationItemSchema2 =
|
|
3054
|
+
var NavigationItemSchema2 = z13.union([
|
|
2950
3055
|
NavigationLinkSchema2,
|
|
2951
3056
|
NavigationSectionSchema2
|
|
2952
3057
|
]);
|
|
@@ -3047,7 +3152,7 @@ function handleGetSchema(input) {
|
|
|
3047
3152
|
const synonyms = FIELD_SYNONYMS[name];
|
|
3048
3153
|
let jsonSchema;
|
|
3049
3154
|
try {
|
|
3050
|
-
const raw =
|
|
3155
|
+
const raw = z13.toJSONSchema(schema, { unrepresentable: "any" });
|
|
3051
3156
|
jsonSchema = raw;
|
|
3052
3157
|
} catch {
|
|
3053
3158
|
jsonSchema = { type: "object", description: "Schema introspection unavailable for this type" };
|
|
@@ -3101,7 +3206,7 @@ function registerGetSchemaTool(server2) {
|
|
|
3101
3206
|
"stackwright_pro_get_schema",
|
|
3102
3207
|
'Returns a compact, LLM-friendly summary of a canonical schema derived programmatically from Zod. Includes required/optional fields, types, constraints, and "did you mean?" warnings for common field-name drift. Use before authoring YAML to confirm exact field names.',
|
|
3103
3208
|
{
|
|
3104
|
-
schemaName:
|
|
3209
|
+
schemaName: z13.enum(SUPPORTED_SCHEMA_NAMES).describe(
|
|
3105
3210
|
"Schema to summarize. One of: workflow_step, workflow_definition, workflow_field, workflow_action, navigation_item, auth_config"
|
|
3106
3211
|
)
|
|
3107
3212
|
},
|
|
@@ -3393,11 +3498,29 @@ var PHASE_TO_OTTER2 = {
|
|
|
3393
3498
|
geo: "stackwright-pro-geo-otter",
|
|
3394
3499
|
qa: "stackwright-pro-qa-otter"
|
|
3395
3500
|
};
|
|
3501
|
+
var PhaseStatusSchema = z14.object({
|
|
3502
|
+
status: z14.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending"),
|
|
3503
|
+
questionsCollected: z14.boolean().default(false),
|
|
3504
|
+
answered: z14.boolean().default(false),
|
|
3505
|
+
executed: z14.boolean().default(false),
|
|
3506
|
+
artifactWritten: z14.boolean().default(false),
|
|
3507
|
+
retryCount: z14.number().default(0),
|
|
3508
|
+
inFlight: z14.boolean().default(false)
|
|
3509
|
+
});
|
|
3510
|
+
var PipelineStateSchema = z14.object({
|
|
3511
|
+
version: z14.literal("1.0"),
|
|
3512
|
+
currentPhase: z14.string(),
|
|
3513
|
+
status: z14.enum(["setup", "questions", "execution", "done"]),
|
|
3514
|
+
phases: z14.record(z14.string(), PhaseStatusSchema),
|
|
3515
|
+
startedAt: z14.string(),
|
|
3516
|
+
updatedAt: z14.string()
|
|
3517
|
+
});
|
|
3396
3518
|
function isValidPhase2(phase) {
|
|
3397
3519
|
return PHASE_ORDER.includes(phase);
|
|
3398
3520
|
}
|
|
3399
3521
|
function defaultPhaseStatus() {
|
|
3400
3522
|
return {
|
|
3523
|
+
status: "pending",
|
|
3401
3524
|
questionsCollected: false,
|
|
3402
3525
|
answered: false,
|
|
3403
3526
|
executed: false,
|
|
@@ -3428,10 +3551,9 @@ function readState(cwd) {
|
|
|
3428
3551
|
const p = statePath(cwd);
|
|
3429
3552
|
if (!existsSync7(p)) return createDefaultState();
|
|
3430
3553
|
const raw = JSON.parse(safeReadSync(p));
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
return raw;
|
|
3554
|
+
const result = PipelineStateSchema.safeParse(raw);
|
|
3555
|
+
if (!result.success) return createDefaultState();
|
|
3556
|
+
return result.data;
|
|
3435
3557
|
}
|
|
3436
3558
|
function safeWriteSync(filePath, content) {
|
|
3437
3559
|
if (existsSync7(filePath)) {
|
|
@@ -3520,13 +3642,16 @@ function handleSetPipelineState(input) {
|
|
|
3520
3642
|
isError: true
|
|
3521
3643
|
};
|
|
3522
3644
|
}
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3645
|
+
if (input.field === "artifactWritten") {
|
|
3646
|
+
return {
|
|
3647
|
+
text: JSON.stringify({
|
|
3648
|
+
error: true,
|
|
3649
|
+
message: "Cannot set 'artifactWritten' directly. Use stackwright_pro_validate_artifact instead \u2014 it verifies the file exists on disk before flagging."
|
|
3650
|
+
}),
|
|
3651
|
+
isError: true
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3654
|
+
const VALID_FIELDS = ["questionsCollected", "answered", "executed", "inFlight"];
|
|
3530
3655
|
if (input.field && !VALID_FIELDS.includes(input.field)) {
|
|
3531
3656
|
return {
|
|
3532
3657
|
text: JSON.stringify({
|
|
@@ -3547,6 +3672,15 @@ function handleSetPipelineState(input) {
|
|
|
3547
3672
|
isError: true
|
|
3548
3673
|
};
|
|
3549
3674
|
}
|
|
3675
|
+
if (update.field === "artifactWritten") {
|
|
3676
|
+
return {
|
|
3677
|
+
text: JSON.stringify({
|
|
3678
|
+
error: true,
|
|
3679
|
+
message: "Cannot set 'artifactWritten' directly. Use stackwright_pro_validate_artifact instead \u2014 it verifies the file exists on disk before flagging."
|
|
3680
|
+
}),
|
|
3681
|
+
isError: true
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3550
3684
|
if (!VALID_FIELDS.includes(update.field)) {
|
|
3551
3685
|
return {
|
|
3552
3686
|
text: JSON.stringify({
|
|
@@ -3572,6 +3706,9 @@ function handleSetPipelineState(input) {
|
|
|
3572
3706
|
if (input.field && input.value !== void 0) {
|
|
3573
3707
|
phaseState[input.field] = input.value;
|
|
3574
3708
|
}
|
|
3709
|
+
if (input.phaseStatus !== void 0) {
|
|
3710
|
+
phaseState.status = input.phaseStatus;
|
|
3711
|
+
}
|
|
3575
3712
|
if (input.incrementRetry) {
|
|
3576
3713
|
phaseState.retryCount += 1;
|
|
3577
3714
|
}
|
|
@@ -3598,16 +3735,6 @@ function handleSetPipelineState(input) {
|
|
|
3598
3735
|
},
|
|
3599
3736
|
{ cwd }
|
|
3600
3737
|
);
|
|
3601
|
-
if (input.field === "artifactWritten" && input.value === true && input.phase) {
|
|
3602
|
-
emit({ type: "phase_complete", phase: input.phase }, { cwd });
|
|
3603
|
-
}
|
|
3604
|
-
if (input.updates) {
|
|
3605
|
-
for (const update of input.updates) {
|
|
3606
|
-
if (update.field === "artifactWritten" && update.value === true) {
|
|
3607
|
-
emit({ type: "phase_complete", phase: update.phase }, { cwd });
|
|
3608
|
-
}
|
|
3609
|
-
}
|
|
3610
|
-
}
|
|
3611
3738
|
} catch {
|
|
3612
3739
|
}
|
|
3613
3740
|
return { text: JSON.stringify(state), isError: false };
|
|
@@ -4686,6 +4813,7 @@ function _validateArtifactInner(input) {
|
|
|
4686
4813
|
if (!state.phases[phase]) state.phases[phase] = defaultPhaseStatus();
|
|
4687
4814
|
const ps = state.phases[phase];
|
|
4688
4815
|
ps.artifactWritten = true;
|
|
4816
|
+
ps.status = "completed";
|
|
4689
4817
|
});
|
|
4690
4818
|
const topKeys = Object.keys(artifact).slice(0, 5).join(", ");
|
|
4691
4819
|
const result = {
|
|
@@ -4713,6 +4841,7 @@ function handleValidateArtifact(input) {
|
|
|
4713
4841
|
const parsed = JSON.parse(result.text);
|
|
4714
4842
|
if (parsed.valid === true && parsed.artifactPath) {
|
|
4715
4843
|
emit({ type: "file_write", path: parsed.artifactPath, phase }, { cwd });
|
|
4844
|
+
emit({ type: "phase_complete", phase }, { cwd });
|
|
4716
4845
|
}
|
|
4717
4846
|
emit(
|
|
4718
4847
|
{
|
|
@@ -4812,28 +4941,35 @@ function registerPipelineTools(server2) {
|
|
|
4812
4941
|
"stackwright_pro_set_pipeline_state",
|
|
4813
4942
|
`Atomic read\u2192modify\u2192write pipeline state. ${DESC}`,
|
|
4814
4943
|
{
|
|
4815
|
-
phase:
|
|
4816
|
-
field:
|
|
4944
|
+
phase: z14.string().optional().describe('Phase to update, e.g. "designer"'),
|
|
4945
|
+
field: z14.enum(["questionsCollected", "answered", "executed", "inFlight"]).optional().describe(
|
|
4946
|
+
"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)."
|
|
4947
|
+
),
|
|
4817
4948
|
// inFlight: set true BEFORE specialist invoke (dataflow mode, swp-ioc7); false after.
|
|
4818
|
-
value: boolCoerce(
|
|
4949
|
+
value: boolCoerce(z14.boolean().optional()).describe(
|
|
4819
4950
|
'Value for the field \u2014 must be a JSON boolean (true/false), NOT the string "true"/"false"'
|
|
4820
4951
|
),
|
|
4821
|
-
|
|
4822
|
-
|
|
4952
|
+
// phaseStatus: per-phase execution enum — DISTINCT from top-level `status` (pipeline flow).
|
|
4953
|
+
// Named `phaseStatus` to avoid ambiguity. Sets PhaseStatus.status on the given phase.
|
|
4954
|
+
phaseStatus: z14.enum(["pending", "running", "completed", "skipped", "failed"]).optional().describe(
|
|
4955
|
+
"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."
|
|
4956
|
+
),
|
|
4957
|
+
status: z14.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level pipeline flow status override \u2014 distinct from phaseStatus"),
|
|
4958
|
+
incrementRetry: boolCoerce(z14.boolean().optional()).describe(
|
|
4823
4959
|
"Bump retryCount by 1 \u2014 must be a JSON boolean"
|
|
4824
4960
|
),
|
|
4825
4961
|
updates: jsonCoerce(
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
phase:
|
|
4829
|
-
field:
|
|
4962
|
+
z14.array(
|
|
4963
|
+
z14.object({
|
|
4964
|
+
phase: z14.string(),
|
|
4965
|
+
field: z14.enum([
|
|
4830
4966
|
"questionsCollected",
|
|
4831
4967
|
"answered",
|
|
4832
4968
|
"executed",
|
|
4833
|
-
"artifactWritten",
|
|
4834
4969
|
"inFlight"
|
|
4970
|
+
// artifactWritten intentionally absent — use validate_artifact (swp-og9c)
|
|
4835
4971
|
]),
|
|
4836
|
-
value:
|
|
4972
|
+
value: z14.boolean()
|
|
4837
4973
|
})
|
|
4838
4974
|
).optional()
|
|
4839
4975
|
).describe("Batch updates \u2014 apply multiple field changes in one atomic read\u2192modify\u2192write")
|
|
@@ -4843,6 +4979,7 @@ function registerPipelineTools(server2) {
|
|
|
4843
4979
|
...args.phase != null ? { phase: args.phase } : {},
|
|
4844
4980
|
...args.field != null ? { field: args.field } : {},
|
|
4845
4981
|
...args.value != null ? { value: args.value } : {},
|
|
4982
|
+
...args.phaseStatus != null ? { phaseStatus: args.phaseStatus } : {},
|
|
4846
4983
|
...args.status != null ? { status: args.status } : {},
|
|
4847
4984
|
...args.incrementRetry != null ? { incrementRetry: args.incrementRetry } : {},
|
|
4848
4985
|
...args.updates != null ? { updates: args.updates } : {}
|
|
@@ -4853,7 +4990,7 @@ function registerPipelineTools(server2) {
|
|
|
4853
4990
|
"stackwright_pro_check_execution_ready",
|
|
4854
4991
|
`Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC}`,
|
|
4855
4992
|
{
|
|
4856
|
-
phase:
|
|
4993
|
+
phase: z14.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
|
|
4857
4994
|
},
|
|
4858
4995
|
async ({ phase }) => res(handleCheckExecutionReady(void 0, phase))
|
|
4859
4996
|
);
|
|
@@ -4873,9 +5010,9 @@ function registerPipelineTools(server2) {
|
|
|
4873
5010
|
"stackwright_pro_write_phase_questions",
|
|
4874
5011
|
`Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC}`,
|
|
4875
5012
|
{
|
|
4876
|
-
phase:
|
|
4877
|
-
responseText:
|
|
4878
|
-
questions: jsonCoerce(
|
|
5013
|
+
phase: z14.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
|
|
5014
|
+
responseText: z14.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
|
|
5015
|
+
questions: jsonCoerce(z14.array(z14.any()).optional()).describe(
|
|
4879
5016
|
"Questions array for direct specialist write"
|
|
4880
5017
|
)
|
|
4881
5018
|
},
|
|
@@ -4917,22 +5054,22 @@ function registerPipelineTools(server2) {
|
|
|
4917
5054
|
server2.tool(
|
|
4918
5055
|
"stackwright_pro_build_specialist_prompt",
|
|
4919
5056
|
`Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${DESC}`,
|
|
4920
|
-
{ phase:
|
|
5057
|
+
{ phase: z14.string().describe('Phase to build prompt for, e.g. "pages"') },
|
|
4921
5058
|
async ({ phase }) => res(handleBuildSpecialistPrompt({ phase }))
|
|
4922
5059
|
);
|
|
4923
5060
|
server2.tool(
|
|
4924
5061
|
"stackwright_pro_validate_artifact",
|
|
4925
5062
|
`Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${DESC}`,
|
|
4926
5063
|
{
|
|
4927
|
-
phase:
|
|
4928
|
-
responseText:
|
|
4929
|
-
artifact: jsonCoerce(
|
|
5064
|
+
phase: z14.string().describe('Phase that produced this artifact, e.g. "designer"'),
|
|
5065
|
+
responseText: z14.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
|
|
5066
|
+
artifact: jsonCoerce(z14.record(z14.string(), z14.unknown()).optional()).describe(
|
|
4930
5067
|
"Artifact object to validate and write directly (specialist direct path \u2014 skips off-script detection and JSON parsing)"
|
|
4931
5068
|
),
|
|
4932
|
-
integrationName:
|
|
5069
|
+
integrationName: z14.string().optional().describe(
|
|
4933
5070
|
'When phase="api": write to api-config-<integrationName>.json instead of api-config.json. Enables concurrent per-spec fan-out without collision (swp-530y).'
|
|
4934
5071
|
),
|
|
4935
|
-
workflowName:
|
|
5072
|
+
workflowName: z14.string().optional().describe(
|
|
4936
5073
|
`When phase="workflow": write to workflow-config-<workflowName>.json instead of workflow-config.json. Enables concurrent per-workflow fan-out without collision (swp-x8sm). Must be a kebab-case slug matching the workflow's id field.`
|
|
4937
5074
|
)
|
|
4938
5075
|
},
|
|
@@ -4961,7 +5098,7 @@ function registerPipelineTools(server2) {
|
|
|
4961
5098
|
"stackwright_pro_emit_event",
|
|
4962
5099
|
`FOREMAN ONLY \u2014 specialist otters that call this will receive an authorization rejection (no-op, not an error). Emit a telemetry event to .stackwright/pipeline-events.ndjson. Foreman calls this at phase boundaries and agent-invoke boundaries. Best-effort: failures are silent and never block. ${DESC}`,
|
|
4963
5100
|
{
|
|
4964
|
-
type:
|
|
5101
|
+
type: z14.enum([
|
|
4965
5102
|
"phase_start",
|
|
4966
5103
|
"phase_complete",
|
|
4967
5104
|
"phase_ready",
|
|
@@ -4970,20 +5107,20 @@ function registerPipelineTools(server2) {
|
|
|
4970
5107
|
]).describe("Event type to emit"),
|
|
4971
5108
|
// phase_ready: emitted in dataflow mode (swp-ioc7) when a phase enters the ready set
|
|
4972
5109
|
// (deps satisfied, not inFlight, not yet complete) — BEFORE invoking the specialist.
|
|
4973
|
-
phase:
|
|
4974
|
-
targetOtter:
|
|
4975
|
-
success: boolCoerce(
|
|
5110
|
+
phase: z14.string().optional().describe("Current phase name"),
|
|
5111
|
+
targetOtter: z14.string().optional().describe("For agent_invoke_* events: which otter is being invoked"),
|
|
5112
|
+
success: boolCoerce(z14.boolean().optional()).describe(
|
|
4976
5113
|
'For agent_invoke_complete: did the invocation succeed? (boolean; string "true"/"false" also accepted for caller tolerance)'
|
|
4977
5114
|
),
|
|
4978
|
-
otter:
|
|
4979
|
-
durationSec:
|
|
5115
|
+
otter: z14.string().optional().describe('Which otter is emitting (defaults to "foreman")'),
|
|
5116
|
+
durationSec: z14.number().optional().describe("Duration in seconds (for *_complete events)")
|
|
4980
5117
|
},
|
|
4981
5118
|
async ({ type, phase, targetOtter, success, otter, durationSec }) => res(handleEmitEvent({ type, phase, targetOtter, success, otter, durationSec }))
|
|
4982
5119
|
);
|
|
4983
5120
|
}
|
|
4984
5121
|
|
|
4985
5122
|
// src/tools/safe-write.ts
|
|
4986
|
-
import { z as
|
|
5123
|
+
import { z as z15 } from "zod";
|
|
4987
5124
|
import { writeFileSync as writeFileSync5, readFileSync as readFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync5, lstatSync as lstatSync7 } from "fs";
|
|
4988
5125
|
import { normalize, isAbsolute, dirname, join as join8 } from "path";
|
|
4989
5126
|
import { emit as emit2 } from "@stackwright-pro/telemetry";
|
|
@@ -5479,10 +5616,10 @@ function registerSafeWriteTools(server2) {
|
|
|
5479
5616
|
"stackwright_pro_safe_write",
|
|
5480
5617
|
DESC,
|
|
5481
5618
|
{
|
|
5482
|
-
callerOtter:
|
|
5483
|
-
filePath:
|
|
5484
|
-
content:
|
|
5485
|
-
createDirectories: boolCoerce(
|
|
5619
|
+
callerOtter: z15.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
|
|
5620
|
+
filePath: z15.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
|
|
5621
|
+
content: z15.string().describe("File content to write"),
|
|
5622
|
+
createDirectories: boolCoerce(z15.boolean().optional().default(true)).describe(
|
|
5486
5623
|
"Create parent directories if they don't exist. Default: true"
|
|
5487
5624
|
)
|
|
5488
5625
|
},
|
|
@@ -5499,9 +5636,12 @@ function registerSafeWriteTools(server2) {
|
|
|
5499
5636
|
}
|
|
5500
5637
|
|
|
5501
5638
|
// src/tools/auth.ts
|
|
5502
|
-
import { z as
|
|
5639
|
+
import { z as z16 } from "zod";
|
|
5503
5640
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
|
|
5504
5641
|
import { join as join9 } from "path";
|
|
5642
|
+
function normalizeProviderSlug(slug) {
|
|
5643
|
+
return slug.replace(/-/g, "_");
|
|
5644
|
+
}
|
|
5505
5645
|
function buildHierarchy(roles) {
|
|
5506
5646
|
const h = {};
|
|
5507
5647
|
for (let i = 0; i < roles.length - 1; i++) {
|
|
@@ -5756,13 +5896,12 @@ ${hierarchyToYaml(hierarchy, " ")}`;
|
|
|
5756
5896
|
const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
|
|
5757
5897
|
const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
|
|
5758
5898
|
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
5759
|
-
|
|
5760
|
-
${
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
certHeader: ${certHeader}
|
|
5899
|
+
type: pki
|
|
5900
|
+
${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
5901
|
+
caBundle: \${CAC_CA_BUNDLE}
|
|
5902
|
+
edipiLookup: ${edipiLookup}
|
|
5903
|
+
ocspEndpoint: \${CAC_OCSP_ENDPOINT}
|
|
5904
|
+
certHeader: ${certHeader}
|
|
5766
5905
|
${rbacSection}
|
|
5767
5906
|
protectedRoutes:
|
|
5768
5907
|
${routeLines}
|
|
@@ -5773,14 +5912,13 @@ ${auditSection}
|
|
|
5773
5912
|
const scopes2 = params.oidcScopes ?? "openid profile email";
|
|
5774
5913
|
const roleClaim = params.oidcRoleClaim ?? "roles";
|
|
5775
5914
|
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
5776
|
-
|
|
5915
|
+
type: oidc
|
|
5777
5916
|
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
roleClaim: ${roleClaim}
|
|
5917
|
+
discoveryUrl: \${OIDC_DISCOVERY_URL}
|
|
5918
|
+
clientId: \${OIDC_CLIENT_ID}
|
|
5919
|
+
clientSecret: \${OIDC_CLIENT_SECRET}
|
|
5920
|
+
scopes: ${scopes2}
|
|
5921
|
+
roleClaim: ${roleClaim}
|
|
5784
5922
|
${rbacSection}
|
|
5785
5923
|
protectedRoutes:
|
|
5786
5924
|
${routeLines}
|
|
@@ -5789,14 +5927,13 @@ ${auditSection}
|
|
|
5789
5927
|
}
|
|
5790
5928
|
const scopes = params.oauth2Scopes ?? "read write";
|
|
5791
5929
|
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
5792
|
-
|
|
5930
|
+
type: oauth2
|
|
5793
5931
|
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
scopes: ${scopes}
|
|
5932
|
+
authorizationUrl: \${OAUTH2_AUTH_URL}
|
|
5933
|
+
tokenUrl: \${OAUTH2_TOKEN_URL}
|
|
5934
|
+
clientId: \${OAUTH2_CLIENT_ID}
|
|
5935
|
+
clientSecret: \${OAUTH2_CLIENT_SECRET}
|
|
5936
|
+
scopes: ${scopes}
|
|
5800
5937
|
${rbacSection}
|
|
5801
5938
|
protectedRoutes:
|
|
5802
5939
|
${routeLines}
|
|
@@ -6003,14 +6140,13 @@ ${hierarchyToYaml(hierarchy, " ")}`;
|
|
|
6003
6140
|
const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
|
|
6004
6141
|
const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
|
|
6005
6142
|
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
6006
|
-
|
|
6143
|
+
type: pki
|
|
6007
6144
|
devOnly: true
|
|
6008
|
-
${
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
certHeader: ${certHeader}
|
|
6145
|
+
${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
6146
|
+
caBundle: ${caBundle}
|
|
6147
|
+
edipiLookup: ${edipiLookup}
|
|
6148
|
+
ocspEndpoint: ${ocspEndpoint}
|
|
6149
|
+
certHeader: ${certHeader}
|
|
6014
6150
|
${rbacSection}
|
|
6015
6151
|
protectedRoutes:
|
|
6016
6152
|
${routeLines}
|
|
@@ -6021,15 +6157,14 @@ ${auditSection}
|
|
|
6021
6157
|
const scopes2 = params.oidcScopes ?? "openid profile email";
|
|
6022
6158
|
const roleClaim = params.oidcRoleClaim ?? "roles";
|
|
6023
6159
|
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
6024
|
-
|
|
6160
|
+
type: oidc
|
|
6025
6161
|
devOnly: true
|
|
6026
6162
|
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
6027
|
-
oidc
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
roleClaim: ${roleClaim}
|
|
6163
|
+
discoveryUrl: https://dev-mock-oidc/.well-known/openid-configuration
|
|
6164
|
+
clientId: dev-mock-client
|
|
6165
|
+
clientSecret: dev-mock-secret
|
|
6166
|
+
scopes: ${scopes2}
|
|
6167
|
+
roleClaim: ${roleClaim}
|
|
6033
6168
|
${rbacSection}
|
|
6034
6169
|
protectedRoutes:
|
|
6035
6170
|
${routeLines}
|
|
@@ -6038,15 +6173,14 @@ ${auditSection}
|
|
|
6038
6173
|
}
|
|
6039
6174
|
const scopes = params.oauth2Scopes ?? "read write";
|
|
6040
6175
|
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
6041
|
-
|
|
6176
|
+
type: oauth2
|
|
6042
6177
|
devOnly: true
|
|
6043
6178
|
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
6044
|
-
oauth2
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
scopes: ${scopes}
|
|
6179
|
+
authorizationUrl: https://dev-mock-oauth2/authorize
|
|
6180
|
+
tokenUrl: https://dev-mock-oauth2/token
|
|
6181
|
+
clientId: dev-mock-client
|
|
6182
|
+
clientSecret: dev-mock-secret
|
|
6183
|
+
scopes: ${scopes}
|
|
6050
6184
|
${rbacSection}
|
|
6051
6185
|
protectedRoutes:
|
|
6052
6186
|
${routeLines}
|
|
@@ -6116,6 +6250,10 @@ function updatePackageJsonScripts(existingJson, roles, mockUsers) {
|
|
|
6116
6250
|
return JSON.stringify(pkg, null, 2) + "\n";
|
|
6117
6251
|
}
|
|
6118
6252
|
async function configureAuthHandler(params, cwd) {
|
|
6253
|
+
params = {
|
|
6254
|
+
...params,
|
|
6255
|
+
provider: params.provider != null ? normalizeProviderSlug(params.provider) : void 0
|
|
6256
|
+
};
|
|
6119
6257
|
const {
|
|
6120
6258
|
method,
|
|
6121
6259
|
provider,
|
|
@@ -6367,45 +6505,46 @@ function registerAuthTools(server2) {
|
|
|
6367
6505
|
"stackwright_pro_configure_auth",
|
|
6368
6506
|
"Generate authentication middleware and configuration for a Next.js Stackwright application. Writes `middleware.ts` (or `proxy.ts` for Next.js >=16) from a secure template, writes a standalone `stackwright.auth.yml` sibling file, and generates `.env.example` with required environment variables. \u26A0\uFE0F For CAC/PKI: generated `middleware.ts` carries a SECURITY REVIEW REQUIRED comment \u2014 certificate chain validation must be verified by a DoD security officer before production deployment. This is the ONLY approved path to generating `middleware.ts`. Never write TypeScript auth files directly.",
|
|
6369
6507
|
{
|
|
6370
|
-
method:
|
|
6371
|
-
|
|
6508
|
+
method: z16.enum(["cac", "oidc", "oauth2", "none"]),
|
|
6509
|
+
// Both hyphen and underscore accepted at the tool surface; normalized to underscore downstream.
|
|
6510
|
+
provider: z16.enum(["azure-ad", "azure_ad", "okta", "ping", "cognito", "custom"]).optional(),
|
|
6372
6511
|
// CAC
|
|
6373
|
-
cacCaBundle:
|
|
6374
|
-
cacEdipiLookup:
|
|
6375
|
-
cacOcspEndpoint:
|
|
6376
|
-
cacCertHeader:
|
|
6512
|
+
cacCaBundle: z16.string().optional(),
|
|
6513
|
+
cacEdipiLookup: z16.string().optional(),
|
|
6514
|
+
cacOcspEndpoint: z16.string().optional(),
|
|
6515
|
+
cacCertHeader: z16.string().optional(),
|
|
6377
6516
|
// OIDC
|
|
6378
|
-
oidcDiscoveryUrl:
|
|
6379
|
-
oidcClientId:
|
|
6380
|
-
oidcClientSecret:
|
|
6381
|
-
oidcScopes:
|
|
6382
|
-
oidcRoleClaim:
|
|
6517
|
+
oidcDiscoveryUrl: z16.string().optional(),
|
|
6518
|
+
oidcClientId: z16.string().optional(),
|
|
6519
|
+
oidcClientSecret: z16.string().optional(),
|
|
6520
|
+
oidcScopes: z16.string().optional(),
|
|
6521
|
+
oidcRoleClaim: z16.string().optional(),
|
|
6383
6522
|
// OAuth2
|
|
6384
|
-
oauth2AuthUrl:
|
|
6385
|
-
oauth2TokenUrl:
|
|
6386
|
-
oauth2ClientId:
|
|
6387
|
-
oauth2ClientSecret:
|
|
6388
|
-
oauth2Scopes:
|
|
6523
|
+
oauth2AuthUrl: z16.string().optional(),
|
|
6524
|
+
oauth2TokenUrl: z16.string().optional(),
|
|
6525
|
+
oauth2ClientId: z16.string().optional(),
|
|
6526
|
+
oauth2ClientSecret: z16.string().optional(),
|
|
6527
|
+
oauth2Scopes: z16.string().optional(),
|
|
6389
6528
|
// RBAC
|
|
6390
|
-
rbacRoles: jsonCoerce(
|
|
6391
|
-
rbacDefaultRole:
|
|
6529
|
+
rbacRoles: jsonCoerce(z16.array(z16.string()).optional()),
|
|
6530
|
+
rbacDefaultRole: z16.string().optional(),
|
|
6392
6531
|
// Audit
|
|
6393
|
-
auditEnabled: boolCoerce(
|
|
6394
|
-
auditRetentionDays: numCoerce(
|
|
6532
|
+
auditEnabled: boolCoerce(z16.boolean().optional()),
|
|
6533
|
+
auditRetentionDays: numCoerce(z16.number().int().positive().optional()),
|
|
6395
6534
|
// Routes — accepts bare strings (backward-compat) or per-route objects (swp-owu0)
|
|
6396
6535
|
protectedRoutes: jsonCoerce(
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6536
|
+
z16.array(
|
|
6537
|
+
z16.union([
|
|
6538
|
+
z16.string(),
|
|
6539
|
+
z16.object({ pattern: z16.string(), requiredRole: z16.string().optional() })
|
|
6401
6540
|
])
|
|
6402
6541
|
).optional()
|
|
6403
6542
|
),
|
|
6404
6543
|
// Injection for tests
|
|
6405
|
-
_cwd:
|
|
6406
|
-
devOnly: boolCoerce(
|
|
6407
|
-
mockUsers: jsonCoerce(
|
|
6408
|
-
nextMajorVersion: numCoerce(
|
|
6544
|
+
_cwd: z16.string().optional(),
|
|
6545
|
+
devOnly: boolCoerce(z16.boolean().optional()),
|
|
6546
|
+
mockUsers: jsonCoerce(z16.array(z16.object({ name: z16.string(), email: z16.string() })).optional()),
|
|
6547
|
+
nextMajorVersion: numCoerce(z16.number().int().positive().optional())
|
|
6409
6548
|
},
|
|
6410
6549
|
async (params) => {
|
|
6411
6550
|
const cwd = params._cwd ?? process.cwd();
|
|
@@ -6425,7 +6564,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6425
6564
|
],
|
|
6426
6565
|
[
|
|
6427
6566
|
"stackwright-pro-auth-otter.json",
|
|
6428
|
-
"
|
|
6567
|
+
"eee511fa214203ce88d51996c6b1b483b6ad3a0ae474cc7be5d380fdbbc43bb1"
|
|
6429
6568
|
],
|
|
6430
6569
|
[
|
|
6431
6570
|
"stackwright-pro-dashboard-otter.json",
|
|
@@ -6445,7 +6584,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6445
6584
|
],
|
|
6446
6585
|
[
|
|
6447
6586
|
"stackwright-pro-foreman-otter.json",
|
|
6448
|
-
"
|
|
6587
|
+
"65aa3273274b2b1859d432c3d1166c4a73971861036bf18c35acd8a5357fcf26"
|
|
6449
6588
|
],
|
|
6450
6589
|
[
|
|
6451
6590
|
"stackwright-pro-form-wizard-otter.json",
|
|
@@ -6461,7 +6600,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6461
6600
|
],
|
|
6462
6601
|
[
|
|
6463
6602
|
"stackwright-pro-polish-otter.json",
|
|
6464
|
-
"
|
|
6603
|
+
"4b8223d89af7f92b9051fee7bb29fc4b1ee2a5a9ab87e8e67a6d8be6122fa029"
|
|
6465
6604
|
],
|
|
6466
6605
|
[
|
|
6467
6606
|
"stackwright-pro-qa-otter.json",
|
|
@@ -6694,7 +6833,7 @@ function registerIntegrityTools(server2) {
|
|
|
6694
6833
|
}
|
|
6695
6834
|
|
|
6696
6835
|
// src/tools/domain.ts
|
|
6697
|
-
import { z as
|
|
6836
|
+
import { z as z17 } from "zod";
|
|
6698
6837
|
import { readFileSync as readFileSync11, existsSync as existsSync10 } from "fs";
|
|
6699
6838
|
import { join as join11 } from "path";
|
|
6700
6839
|
function handleListCollections(input) {
|
|
@@ -7080,7 +7219,7 @@ function registerDomainTools(server2) {
|
|
|
7080
7219
|
"stackwright_pro_resolve_data_strategy",
|
|
7081
7220
|
"Look up the data freshness strategy configuration from the user's answer. Returns mechanism, revalidation seconds, required packages, and the exact MCP tool call to make. Replaces the strategy table in the data-otter prompt.",
|
|
7082
7221
|
{
|
|
7083
|
-
strategy:
|
|
7222
|
+
strategy: z17.string().describe(
|
|
7084
7223
|
'The data-1 answer value: "pulse-fast", "isr-fast", "isr-standard", or "isr-slow"'
|
|
7085
7224
|
)
|
|
7086
7225
|
},
|
|
@@ -7090,7 +7229,7 @@ function registerDomainTools(server2) {
|
|
|
7090
7229
|
"stackwright_pro_validate_workflow",
|
|
7091
7230
|
"Validate a workflow definition against the Stackwright workflow schema. Checks step ID uniqueness, transition targets, terminal state existence, and service references. Call this after the workflow otter produces output.",
|
|
7092
7231
|
{
|
|
7093
|
-
workflow: jsonCoerce(
|
|
7232
|
+
workflow: jsonCoerce(z17.record(z17.string(), z17.unknown()).optional()).describe(
|
|
7094
7233
|
"Parsed workflow object. If omitted, reads from .stackwright/artifacts/workflow-config.json"
|
|
7095
7234
|
)
|
|
7096
7235
|
},
|
|
@@ -7099,7 +7238,7 @@ function registerDomainTools(server2) {
|
|
|
7099
7238
|
}
|
|
7100
7239
|
|
|
7101
7240
|
// src/tools/type-schemas.ts
|
|
7102
|
-
import { z as
|
|
7241
|
+
import { z as z18 } from "zod";
|
|
7103
7242
|
function buildTypeSchemaSummary() {
|
|
7104
7243
|
return {
|
|
7105
7244
|
version: "1.0",
|
|
@@ -7176,7 +7315,7 @@ function registerTypeSchemasTool(server2) {
|
|
|
7176
7315
|
"stackwright_pro_get_type_schemas",
|
|
7177
7316
|
"Returns a structured summary of all canonical @stackwright-pro/types schemas, organized by domain. Use this to determine which otter owns a given schema and what artifact key to expect.",
|
|
7178
7317
|
{
|
|
7179
|
-
format:
|
|
7318
|
+
format: z18.enum(["full", "domains-only"]).optional().default("full").describe("full = complete summary with all fields; domains-only = just domain names")
|
|
7180
7319
|
},
|
|
7181
7320
|
async ({ format }) => {
|
|
7182
7321
|
const summary = buildTypeSchemaSummary();
|
|
@@ -7192,7 +7331,7 @@ function registerTypeSchemasTool(server2) {
|
|
|
7192
7331
|
import * as fs2 from "fs";
|
|
7193
7332
|
import * as os from "os";
|
|
7194
7333
|
import * as path3 from "path";
|
|
7195
|
-
import { z as
|
|
7334
|
+
import { z as z19 } from "zod";
|
|
7196
7335
|
import { load as yamlLoad3, dump as yamlDump } from "js-yaml";
|
|
7197
7336
|
import { writePage, resolvePagesDir } from "@stackwright/cli";
|
|
7198
7337
|
import { isProContentType, isOssTypeWithProExtension } from "@stackwright-pro/types";
|
|
@@ -7304,10 +7443,10 @@ function registerProPageTools(server2) {
|
|
|
7304
7443
|
"stackwright_pro_write_page",
|
|
7305
7444
|
"Write or update a page's YAML content with Pro content-type awareness. Validates against the Stackwright schema (including Pro types like stats_grid, data_table_pulse, map_pulse, alert_banner). Invalid YAML or schema violations on OSS-type fields are rejected with field-level errors. Use this instead of stackwright_write_page for any page containing Pro content types.",
|
|
7306
7445
|
{
|
|
7307
|
-
projectRoot:
|
|
7308
|
-
slug:
|
|
7309
|
-
content:
|
|
7310
|
-
locale:
|
|
7446
|
+
projectRoot: z19.string().describe("Absolute path to the Stackwright project root"),
|
|
7447
|
+
slug: z19.string().describe('Page slug (e.g. "dashboard/main" or "/dashboard/main")'),
|
|
7448
|
+
content: z19.string().describe("Full YAML content for the page (content.yml contents)"),
|
|
7449
|
+
locale: z19.string().optional().describe(
|
|
7311
7450
|
'BCP 47 locale tag (e.g. "fr", "de"). If provided, writes content.<locale>.yml instead of content.yml.'
|
|
7312
7451
|
)
|
|
7313
7452
|
},
|
|
@@ -7566,7 +7705,7 @@ function registerScaffoldCleanupTools(server2) {
|
|
|
7566
7705
|
}
|
|
7567
7706
|
|
|
7568
7707
|
// src/tools/contrast.ts
|
|
7569
|
-
import { z as
|
|
7708
|
+
import { z as z20 } from "zod";
|
|
7570
7709
|
function linearizeChannel(c255) {
|
|
7571
7710
|
const c = c255 / 255;
|
|
7572
7711
|
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
@@ -7738,8 +7877,8 @@ function registerContrastTools(server2) {
|
|
|
7738
7877
|
"stackwright_pro_check_contrast",
|
|
7739
7878
|
'Compute the exact WCAG 2.1 contrast ratio between a foreground and background color. Returns the ratio and AA/AAA pass/fail flags. Use this to verify any foreground/background color pair before writing it to the token set. Accepts #RGB, #RRGGBB, hsl(...), or shadcn HSL format ("H S% L%").',
|
|
7740
7879
|
{
|
|
7741
|
-
fg:
|
|
7742
|
-
bg:
|
|
7880
|
+
fg: z20.string().describe("Foreground (text) color \u2014 hex or HSL"),
|
|
7881
|
+
bg: z20.string().describe("Background color \u2014 hex or HSL")
|
|
7743
7882
|
},
|
|
7744
7883
|
async ({ fg, bg }) => {
|
|
7745
7884
|
let result;
|
|
@@ -7769,8 +7908,8 @@ function registerContrastTools(server2) {
|
|
|
7769
7908
|
"stackwright_pro_derive_accessible_palette",
|
|
7770
7909
|
'Given a seed color (treated as a background), derive the best accessible foreground (text) color that meets the target contrast ratio. Evaluates #ffffff and #000000 against the seed and picks whichever satisfies the target ratio with the highest contrast. Use this for every {name}-foreground token derivation instead of guessing white vs black. Accepts #RGB, #RRGGBB, hsl(...), or shadcn HSL format ("H S% L%").',
|
|
7771
7910
|
{
|
|
7772
|
-
seed:
|
|
7773
|
-
targetRatio: numCoerce(
|
|
7911
|
+
seed: z20.string().describe("Background (seed) color \u2014 hex or HSL"),
|
|
7912
|
+
targetRatio: numCoerce(z20.number().default(4.5)).describe(
|
|
7774
7913
|
"Minimum required contrast ratio (default 4.5 for WCAG AA)"
|
|
7775
7914
|
)
|
|
7776
7915
|
},
|
|
@@ -7805,7 +7944,7 @@ function registerContrastTools(server2) {
|
|
|
7805
7944
|
}
|
|
7806
7945
|
|
|
7807
7946
|
// src/tools/compile.ts
|
|
7808
|
-
import { z as
|
|
7947
|
+
import { z as z21 } from "zod";
|
|
7809
7948
|
import fs3 from "fs";
|
|
7810
7949
|
import path4 from "path";
|
|
7811
7950
|
import { compileCollections } from "@stackwright-pro/pulse/server";
|
|
@@ -7851,7 +7990,7 @@ async function tryOssCompile(fn, ctx, extra) {
|
|
|
7851
7990
|
}
|
|
7852
7991
|
}
|
|
7853
7992
|
function registerCompileTools(server2) {
|
|
7854
|
-
const projectRootArg =
|
|
7993
|
+
const projectRootArg = z21.string().optional().describe("Absolute path to project root (defaults to cwd)");
|
|
7855
7994
|
server2.tool(
|
|
7856
7995
|
"stackwright_pro_compile_collections",
|
|
7857
7996
|
"Compile stackwright.collections.yml \u2192 public/stackwright-content/_collections.json. Reads and validates the live-collections sidecar config file and emits the JSON sink consumed at runtime by PulseCollectionProvider.",
|
|
@@ -8027,7 +8166,7 @@ ${results.join("\n")}`;
|
|
|
8027
8166
|
"stackwright_pro_compile_page",
|
|
8028
8167
|
"Compile a single page by slug. Delegates to compilePage from @stackwright/build-scripts installed in the project.",
|
|
8029
8168
|
{
|
|
8030
|
-
slug:
|
|
8169
|
+
slug: z21.string().describe('Page slug (relative path under pages/, e.g. "dashboard")'),
|
|
8031
8170
|
projectRoot: projectRootArg
|
|
8032
8171
|
},
|
|
8033
8172
|
async ({ slug, projectRoot }) => {
|
|
@@ -8047,7 +8186,7 @@ ${results.join("\n")}`;
|
|
|
8047
8186
|
}
|
|
8048
8187
|
|
|
8049
8188
|
// src/tools/strip-legacy-integrations.ts
|
|
8050
|
-
import { z as
|
|
8189
|
+
import { z as z22 } from "zod";
|
|
8051
8190
|
import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
8052
8191
|
import { join as join14 } from "path";
|
|
8053
8192
|
function handleStripLegacyIntegrations(input) {
|
|
@@ -8110,7 +8249,7 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
8110
8249
|
"stackwright_pro_strip_legacy_integrations",
|
|
8111
8250
|
"Removes the deprecated top-level integrations[] block from stackwright.yml. The authoritative source for integration config is stackwright.integrations.yml \u2014 this tool cleans up legacy entries from older raft runs. Idempotent: no-op if no legacy block present. Preserves all other top-level keys, comments, and formatting.",
|
|
8112
8251
|
{
|
|
8113
|
-
projectRoot:
|
|
8252
|
+
projectRoot: z22.string().describe("Absolute path to the project root directory")
|
|
8114
8253
|
},
|
|
8115
8254
|
async ({ projectRoot }) => {
|
|
8116
8255
|
const result = handleStripLegacyIntegrations({ projectRoot });
|
|
@@ -8122,7 +8261,7 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
8122
8261
|
}
|
|
8123
8262
|
|
|
8124
8263
|
// src/tools/emitters.ts
|
|
8125
|
-
import { z as
|
|
8264
|
+
import { z as z23 } from "zod";
|
|
8126
8265
|
import { readFileSync as readFileSync14, existsSync as existsSync14, readdirSync as readdirSync8 } from "fs";
|
|
8127
8266
|
import { join as join15 } from "path";
|
|
8128
8267
|
import yaml from "js-yaml";
|
|
@@ -8181,7 +8320,7 @@ Call this tool, then write both files with stackwright_pro_safe_write:
|
|
|
8181
8320
|
|
|
8182
8321
|
Do NOT compose the contents by hand. This emitter is the source of truth. If an env var is missing, update integrations.yml or the emitter \u2014 not the file. This is the first concrete instance of the swp-zf9y deterministic emitter pattern. ${ENV_LOCAL_DESC}`,
|
|
8183
8322
|
{
|
|
8184
|
-
cwd:
|
|
8323
|
+
cwd: z23.string().describe(
|
|
8185
8324
|
"Absolute path to the project root containing stackwright.integrations.yml and optionally services/*.yml"
|
|
8186
8325
|
)
|
|
8187
8326
|
},
|
|
@@ -8228,8 +8367,8 @@ mapProvider options:
|
|
|
8228
8367
|
|
|
8229
8368
|
This is the second concrete instance of the swp-zf9y deterministic emitter pattern. Refs: swp-57ku, swp-2m89, swp-zf9y, drawer 957. ${PROVIDERS_DESC}`,
|
|
8230
8369
|
{
|
|
8231
|
-
cwd:
|
|
8232
|
-
mapProvider:
|
|
8370
|
+
cwd: z23.string().describe("Absolute path to the project root containing stackwright.integrations.yml"),
|
|
8371
|
+
mapProvider: z23.enum(["maplibre", "cesium", "none"]).optional().describe(
|
|
8233
8372
|
"Map provider to register (default: cesium for Pro \u2014 3D globe + terrain). Use maplibre for OSS-style 2D dashboards (explicit opt-out). Use none if pages have no map content_items."
|
|
8234
8373
|
)
|
|
8235
8374
|
},
|
|
@@ -8270,7 +8409,7 @@ Workflow:
|
|
|
8270
8409
|
|
|
8271
8410
|
This is the third concrete instance of the swp-zf9y deterministic emitter pattern. Refs: swp-phkh, swp-zf9y. ${PORTS_DESC}`,
|
|
8272
8411
|
{
|
|
8273
|
-
cwd:
|
|
8412
|
+
cwd: z23.string().describe("Absolute path to the project root containing stackwright.integrations.yml")
|
|
8274
8413
|
},
|
|
8275
8414
|
async ({ cwd }) => {
|
|
8276
8415
|
const integrations = readIntegrations(cwd);
|
|
@@ -8299,7 +8438,7 @@ This is the third concrete instance of the swp-zf9y deterministic emitter patter
|
|
|
8299
8438
|
}
|
|
8300
8439
|
|
|
8301
8440
|
// src/tools/list-specs.ts
|
|
8302
|
-
import { z as
|
|
8441
|
+
import { z as z24 } from "zod";
|
|
8303
8442
|
import { readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync2, existsSync as existsSync15 } from "fs";
|
|
8304
8443
|
import { join as join16, extname, basename as basename2 } from "path";
|
|
8305
8444
|
var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
@@ -8364,7 +8503,7 @@ function registerListSpecsTool(server2) {
|
|
|
8364
8503
|
"stackwright_pro_list_specs",
|
|
8365
8504
|
"Enumerate OpenAPI/AsyncAPI specs in the project's specs/ directory. Returns one entry per spec with integration name (derived from filename), detected format, and size. Used by the foreman to fan out the api phase with one api-otter invocation per spec.",
|
|
8366
8505
|
{
|
|
8367
|
-
projectRoot:
|
|
8506
|
+
projectRoot: z24.string().describe("Absolute path to the project root directory")
|
|
8368
8507
|
},
|
|
8369
8508
|
async ({ projectRoot }) => {
|
|
8370
8509
|
const result = handleListSpecs({ projectRoot });
|
|
@@ -8376,7 +8515,7 @@ function registerListSpecsTool(server2) {
|
|
|
8376
8515
|
}
|
|
8377
8516
|
|
|
8378
8517
|
// src/tools/consolidate-integrations.ts
|
|
8379
|
-
import { z as
|
|
8518
|
+
import { z as z25 } from "zod";
|
|
8380
8519
|
import { readdirSync as readdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync9 } from "fs";
|
|
8381
8520
|
import { lockSync as lockSync2 } from "proper-lockfile";
|
|
8382
8521
|
import { join as join17, basename as basename3 } from "path";
|
|
@@ -8497,7 +8636,7 @@ function registerConsolidateIntegrationsTool(server2) {
|
|
|
8497
8636
|
"no per-spec artifacts are found."
|
|
8498
8637
|
].join(" "),
|
|
8499
8638
|
{
|
|
8500
|
-
projectRoot:
|
|
8639
|
+
projectRoot: z25.string().describe("Absolute path to the project root directory")
|
|
8501
8640
|
},
|
|
8502
8641
|
async ({ projectRoot }) => {
|
|
8503
8642
|
try {
|
|
@@ -8555,7 +8694,7 @@ var package_default = {
|
|
|
8555
8694
|
"test:coverage": "vitest run --coverage"
|
|
8556
8695
|
},
|
|
8557
8696
|
name: "@stackwright-pro/mcp",
|
|
8558
|
-
version: "0.2.0-alpha.
|
|
8697
|
+
version: "0.2.0-alpha.112",
|
|
8559
8698
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
8560
8699
|
license: "SEE LICENSE IN LICENSE",
|
|
8561
8700
|
main: "./dist/server.js",
|
|
@@ -8624,6 +8763,7 @@ registerOrchestrationTools(server);
|
|
|
8624
8763
|
registerPipelineTools(server);
|
|
8625
8764
|
registerSafeWriteTools(server);
|
|
8626
8765
|
registerAuthTools(server);
|
|
8766
|
+
registerReadAuthManifestsTool(server);
|
|
8627
8767
|
registerIntegrityTools(server);
|
|
8628
8768
|
registerArtifactSigningTools(server);
|
|
8629
8769
|
registerDomainTools(server);
|