@stackwright-pro/mcp 0.2.0-alpha.90 → 0.2.0-alpha.95
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 +16 -16
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +16 -16
- package/dist/integrity.mjs.map +1 -1
- package/dist/server.js +845 -338
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +794 -287
- package/dist/server.mjs.map +1 -1
- package/dist/tools/type-schemas.js +1 -1
- package/dist/tools/type-schemas.js.map +1 -1
- package/dist/tools/type-schemas.mjs +1 -1
- package/dist/tools/type-schemas.mjs.map +1 -1
- package/package.json +6 -3
package/dist/server.js
CHANGED
|
@@ -144,8 +144,8 @@ function registerDataExplorerTools(server2) {
|
|
|
144
144
|
lines.push(" endpoints:");
|
|
145
145
|
if (result.filter.include && result.filter.include.length > 0) {
|
|
146
146
|
lines.push(" include:");
|
|
147
|
-
for (const
|
|
148
|
-
lines.push(` - ${
|
|
147
|
+
for (const path4 of result.filter.include) {
|
|
148
|
+
lines.push(` - ${path4}`);
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
if (result.filter.exclude && result.filter.exclude.length > 0) {
|
|
@@ -1619,6 +1619,9 @@ var OTTER_NAME_TO_PHASE = [
|
|
|
1619
1619
|
["dashboard", "dashboard"],
|
|
1620
1620
|
["data", "data"],
|
|
1621
1621
|
["page", "pages"],
|
|
1622
|
+
// swp-4071: workflow-otter renamed to form-wizard-otter; keep 'workflow' for
|
|
1623
|
+
// any in-flight session that still uses the old name (belt-and-suspenders)
|
|
1624
|
+
["form-wizard", "workflow"],
|
|
1622
1625
|
["workflow", "workflow"],
|
|
1623
1626
|
["services", "services"],
|
|
1624
1627
|
["polish", "polish"],
|
|
@@ -1633,7 +1636,7 @@ var PHASE_TO_OTTER = {
|
|
|
1633
1636
|
pages: "stackwright-pro-page-otter",
|
|
1634
1637
|
dashboard: "stackwright-pro-dashboard-otter",
|
|
1635
1638
|
data: "stackwright-pro-data-otter",
|
|
1636
|
-
workflow: "stackwright-pro-
|
|
1639
|
+
workflow: "stackwright-pro-form-wizard-otter",
|
|
1637
1640
|
services: "stackwright-services-otter",
|
|
1638
1641
|
polish: "stackwright-pro-polish-otter",
|
|
1639
1642
|
geo: "stackwright-pro-geo-otter"
|
|
@@ -1891,8 +1894,8 @@ function registerOrchestrationTools(server2) {
|
|
|
1891
1894
|
{
|
|
1892
1895
|
buildContext: import_zod9.z.string().describe("Free-text description of what the user wants to build")
|
|
1893
1896
|
},
|
|
1894
|
-
async ({ buildContext }) => {
|
|
1895
|
-
const { text, isError } = handleSaveBuildContext({ buildContext });
|
|
1897
|
+
async ({ buildContext: buildContext2 }) => {
|
|
1898
|
+
const { text, isError } = handleSaveBuildContext({ buildContext: buildContext2 });
|
|
1896
1899
|
return {
|
|
1897
1900
|
content: [{ type: "text", text }],
|
|
1898
1901
|
isError
|
|
@@ -2020,8 +2023,8 @@ function registerOrchestrationTools(server2) {
|
|
|
2020
2023
|
|
|
2021
2024
|
// src/tools/pipeline.ts
|
|
2022
2025
|
var import_zod13 = require("zod");
|
|
2023
|
-
var
|
|
2024
|
-
var
|
|
2026
|
+
var import_fs6 = require("fs");
|
|
2027
|
+
var import_path6 = require("path");
|
|
2025
2028
|
var import_crypto3 = require("crypto");
|
|
2026
2029
|
|
|
2027
2030
|
// src/artifact-signing.ts
|
|
@@ -2683,6 +2686,223 @@ function registerGetSchemaTool(server2) {
|
|
|
2683
2686
|
);
|
|
2684
2687
|
}
|
|
2685
2688
|
|
|
2689
|
+
// src/tools/pipeline-graph.ts
|
|
2690
|
+
var import_fs5 = require("fs");
|
|
2691
|
+
var import_path5 = require("path");
|
|
2692
|
+
var MANIFEST_NAME_TO_PHASE = {
|
|
2693
|
+
"stackwright-pro-designer-otter": "designer",
|
|
2694
|
+
"stackwright-pro-theme-otter": "theme",
|
|
2695
|
+
"stackwright-pro-scaffold-otter": "scaffold",
|
|
2696
|
+
"stackwright-pro-api-otter": "api",
|
|
2697
|
+
"stackwright-pro-auth-otter": "auth",
|
|
2698
|
+
"stackwright-pro-data-otter": "data",
|
|
2699
|
+
"stackwright-pro-page-otter": "pages",
|
|
2700
|
+
"stackwright-pro-dashboard-otter": "dashboard",
|
|
2701
|
+
"stackwright-pro-form-wizard-otter": "workflow",
|
|
2702
|
+
"stackwright-pro-geo-otter": "geo",
|
|
2703
|
+
"stackwright-pro-polish-otter": "polish",
|
|
2704
|
+
"stackwright-services-otter": "services"
|
|
2705
|
+
};
|
|
2706
|
+
function manifestNameToPhase(name) {
|
|
2707
|
+
return MANIFEST_NAME_TO_PHASE[name] ?? null;
|
|
2708
|
+
}
|
|
2709
|
+
var OTTER_SEARCH_PATHS = [
|
|
2710
|
+
"node_modules/@stackwright-pro/otters/src/",
|
|
2711
|
+
// production: installed package
|
|
2712
|
+
"packages/otters/src/",
|
|
2713
|
+
// monorepo: run from repo root
|
|
2714
|
+
"../otters/src/"
|
|
2715
|
+
// monorepo: run from packages/mcp/
|
|
2716
|
+
];
|
|
2717
|
+
function resolveOtterDirFromCwd() {
|
|
2718
|
+
const cwd = process.cwd();
|
|
2719
|
+
for (const relative of OTTER_SEARCH_PATHS) {
|
|
2720
|
+
const candidate = (0, import_path5.join)(cwd, relative);
|
|
2721
|
+
try {
|
|
2722
|
+
(0, import_fs5.lstatSync)(candidate);
|
|
2723
|
+
return candidate;
|
|
2724
|
+
} catch {
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
throw new Error(
|
|
2728
|
+
`Cannot locate otter directory. Searched: ${OTTER_SEARCH_PATHS.join(", ")} (relative to ${cwd}). Make sure @stackwright-pro/otters is installed.`
|
|
2729
|
+
);
|
|
2730
|
+
}
|
|
2731
|
+
function loadOtterManifestsFromDir(otterDir) {
|
|
2732
|
+
const entries = (0, import_fs5.readdirSync)(otterDir);
|
|
2733
|
+
const manifests = [];
|
|
2734
|
+
for (const filename of entries) {
|
|
2735
|
+
if (!filename.endsWith("-otter.json")) continue;
|
|
2736
|
+
const filePath = (0, import_path5.join)(otterDir, filename);
|
|
2737
|
+
if ((0, import_fs5.lstatSync)(filePath).isSymbolicLink()) continue;
|
|
2738
|
+
try {
|
|
2739
|
+
const raw = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
2740
|
+
const manifest = JSON.parse(raw);
|
|
2741
|
+
manifests.push(manifest);
|
|
2742
|
+
} catch (err) {
|
|
2743
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2744
|
+
throw new Error(`Failed to load otter manifest "${filename}": ${msg}`, { cause: err });
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
return manifests;
|
|
2748
|
+
}
|
|
2749
|
+
function topologicalSort(dependencies) {
|
|
2750
|
+
const phases = Object.keys(dependencies);
|
|
2751
|
+
const inDegree = {};
|
|
2752
|
+
const adjList = {};
|
|
2753
|
+
for (const phase of phases) {
|
|
2754
|
+
inDegree[phase] ??= 0;
|
|
2755
|
+
adjList[phase] ??= [];
|
|
2756
|
+
}
|
|
2757
|
+
for (const phase of phases) {
|
|
2758
|
+
for (const dep of dependencies[phase] ?? []) {
|
|
2759
|
+
inDegree[phase] = (inDegree[phase] ?? 0) + 1;
|
|
2760
|
+
adjList[dep] ??= [];
|
|
2761
|
+
adjList[dep].push(phase);
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
const queue = phases.filter((p) => (inDegree[p] ?? 0) === 0);
|
|
2765
|
+
const result = [];
|
|
2766
|
+
while (queue.length > 0) {
|
|
2767
|
+
const node = queue.shift();
|
|
2768
|
+
result.push(node);
|
|
2769
|
+
for (const dependent of adjList[node] ?? []) {
|
|
2770
|
+
inDegree[dependent] -= 1;
|
|
2771
|
+
if (inDegree[dependent] === 0) {
|
|
2772
|
+
queue.push(dependent);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
if (result.length !== phases.length) {
|
|
2777
|
+
const cycleNodes = phases.filter((p) => (inDegree[p] ?? 0) > 0);
|
|
2778
|
+
throw new Error(
|
|
2779
|
+
`Pipeline dependency cycle detected involving: [${cycleNodes.join(", ")}]. Check otter pipeline declarations for circular dependencies.`
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2782
|
+
return result;
|
|
2783
|
+
}
|
|
2784
|
+
function buildPipelineGraph(manifests) {
|
|
2785
|
+
const sinkProducers = {};
|
|
2786
|
+
const artifactProducers = {};
|
|
2787
|
+
for (const m of manifests) {
|
|
2788
|
+
const phase = manifestNameToPhase(m.name);
|
|
2789
|
+
if (!phase) continue;
|
|
2790
|
+
const out = m.pipeline?.outputs;
|
|
2791
|
+
if (!out) continue;
|
|
2792
|
+
for (const sink of out.sinks ?? []) {
|
|
2793
|
+
if (sinkProducers[sink]) {
|
|
2794
|
+
throw new Error(
|
|
2795
|
+
`Sink "${sink}" is produced by both "${sinkProducers[sink]}" and "${phase}". Each sink must have exactly one producer.`
|
|
2796
|
+
);
|
|
2797
|
+
}
|
|
2798
|
+
sinkProducers[sink] = phase;
|
|
2799
|
+
}
|
|
2800
|
+
if (out.artifact) {
|
|
2801
|
+
if (artifactProducers[out.artifact]) {
|
|
2802
|
+
throw new Error(
|
|
2803
|
+
`Artifact "${out.artifact}" is produced by both "${artifactProducers[out.artifact]}" and "${phase}". Each artifact must have exactly one producer.`
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2806
|
+
artifactProducers[out.artifact] = phase;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
const dependencies = {};
|
|
2810
|
+
for (const m of manifests) {
|
|
2811
|
+
const phase = manifestNameToPhase(m.name);
|
|
2812
|
+
if (!phase) continue;
|
|
2813
|
+
const deps = /* @__PURE__ */ new Set();
|
|
2814
|
+
const inp = m.pipeline?.inputs;
|
|
2815
|
+
if (inp) {
|
|
2816
|
+
for (const sink of inp.sinks ?? []) {
|
|
2817
|
+
const producer = sinkProducers[sink];
|
|
2818
|
+
if (!producer) {
|
|
2819
|
+
throw new Error(
|
|
2820
|
+
`Phase "${phase}" declares sink input "${sink}" but no otter produces it. Add an otter with outputs.sinks including "${sink}".`
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
if (producer !== phase) deps.add(producer);
|
|
2824
|
+
}
|
|
2825
|
+
for (const artifact of inp.artifacts ?? []) {
|
|
2826
|
+
const producer = artifactProducers[artifact];
|
|
2827
|
+
if (!producer) {
|
|
2828
|
+
throw new Error(
|
|
2829
|
+
`Phase "${phase}" declares artifact input "${artifact}" but no otter produces it. Add an otter with outputs.artifact = "${artifact}".`
|
|
2830
|
+
);
|
|
2831
|
+
}
|
|
2832
|
+
if (producer !== phase) deps.add(producer);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
dependencies[phase] = Array.from(deps);
|
|
2836
|
+
}
|
|
2837
|
+
const order = topologicalSort(dependencies);
|
|
2838
|
+
return { dependencies, order, sinkProducers, artifactProducers };
|
|
2839
|
+
}
|
|
2840
|
+
var DISTRIBUTED_NAMESPACES = ["@stackwright-pro", "@stackwright"];
|
|
2841
|
+
function loadDistributedOtterManifests(cwd) {
|
|
2842
|
+
const results = [];
|
|
2843
|
+
for (const namespace of DISTRIBUTED_NAMESPACES) {
|
|
2844
|
+
const nsDir = (0, import_path5.join)(cwd, "node_modules", namespace);
|
|
2845
|
+
let pkgDirNames;
|
|
2846
|
+
try {
|
|
2847
|
+
pkgDirNames = (0, import_fs5.readdirSync)(nsDir);
|
|
2848
|
+
} catch {
|
|
2849
|
+
continue;
|
|
2850
|
+
}
|
|
2851
|
+
for (const pkgDirName of pkgDirNames) {
|
|
2852
|
+
const pkgDir = (0, import_path5.join)(nsDir, pkgDirName);
|
|
2853
|
+
const packageName = `${namespace}/${pkgDirName}`;
|
|
2854
|
+
let otterRelPaths;
|
|
2855
|
+
try {
|
|
2856
|
+
const raw = (0, import_fs5.readFileSync)((0, import_path5.join)(pkgDir, "package.json"), "utf-8");
|
|
2857
|
+
const pkgJson = JSON.parse(raw);
|
|
2858
|
+
const declared = pkgJson.stackwright?.otters;
|
|
2859
|
+
if (!Array.isArray(declared) || declared.length === 0) continue;
|
|
2860
|
+
otterRelPaths = declared;
|
|
2861
|
+
} catch {
|
|
2862
|
+
continue;
|
|
2863
|
+
}
|
|
2864
|
+
for (const relPath of otterRelPaths) {
|
|
2865
|
+
const otterFilePath = (0, import_path5.join)(pkgDir, relPath);
|
|
2866
|
+
try {
|
|
2867
|
+
const raw = (0, import_fs5.readFileSync)(otterFilePath, "utf-8");
|
|
2868
|
+
const manifest = JSON.parse(raw);
|
|
2869
|
+
results.push({ manifest, packageName });
|
|
2870
|
+
} catch (err) {
|
|
2871
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2872
|
+
console.warn(
|
|
2873
|
+
`[pipeline-graph] Failed to load distributed otter at "${otterFilePath}" from "${packageName}": ${msg}`
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
return results;
|
|
2880
|
+
}
|
|
2881
|
+
function _mergeManifestPools(central, distributed) {
|
|
2882
|
+
const centralNames = new Set(central.map((m) => m.name));
|
|
2883
|
+
const merged = [...central];
|
|
2884
|
+
for (const { manifest, packageName } of distributed) {
|
|
2885
|
+
if (centralNames.has(manifest.name)) {
|
|
2886
|
+
console.warn(
|
|
2887
|
+
`[pipeline-graph] Otter "${manifest.name}" declared by both @stackwright-pro/otters and ${packageName} \u2014 using @stackwright-pro/otters version.`
|
|
2888
|
+
);
|
|
2889
|
+
} else {
|
|
2890
|
+
merged.push(manifest);
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
return merged;
|
|
2894
|
+
}
|
|
2895
|
+
var _cachedGraph = null;
|
|
2896
|
+
function loadPipelineGraph() {
|
|
2897
|
+
if (_cachedGraph) return _cachedGraph;
|
|
2898
|
+
const otterDir = resolveOtterDirFromCwd();
|
|
2899
|
+
const central = loadOtterManifestsFromDir(otterDir);
|
|
2900
|
+
const distributed = loadDistributedOtterManifests(process.cwd());
|
|
2901
|
+
const manifests = _mergeManifestPools(central, distributed);
|
|
2902
|
+
_cachedGraph = buildPipelineGraph(manifests);
|
|
2903
|
+
return _cachedGraph;
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2686
2906
|
// src/tools/pipeline.ts
|
|
2687
2907
|
var PHASE_ORDER = [
|
|
2688
2908
|
"designer",
|
|
@@ -2691,45 +2911,15 @@ var PHASE_ORDER = [
|
|
|
2691
2911
|
// generates app/ directory from config
|
|
2692
2912
|
"api",
|
|
2693
2913
|
"data",
|
|
2914
|
+
"auth",
|
|
2915
|
+
// moved earlier — only depends on design-language.json (designer)
|
|
2694
2916
|
"geo",
|
|
2695
2917
|
"workflow",
|
|
2696
2918
|
"services",
|
|
2697
2919
|
"pages",
|
|
2698
2920
|
"dashboard",
|
|
2699
|
-
"auth",
|
|
2700
2921
|
"polish"
|
|
2701
2922
|
];
|
|
2702
|
-
var PHASE_DEPENDENCIES = {
|
|
2703
|
-
designer: [],
|
|
2704
|
-
theme: ["designer"],
|
|
2705
|
-
scaffold: ["designer", "theme"],
|
|
2706
|
-
// needs stackwright.yml + theme-tokens
|
|
2707
|
-
api: [],
|
|
2708
|
-
data: ["api"],
|
|
2709
|
-
geo: ["data"],
|
|
2710
|
-
// workflow: no hard deps — uses service: references with Prism mock fallback
|
|
2711
|
-
// when API artifacts aren't available; roles come from user answers
|
|
2712
|
-
workflow: [],
|
|
2713
|
-
// services: needs API endpoints to know what to wire, and optionally
|
|
2714
|
-
// workflow states as trigger sources. Produces OpenAPI specs consumed
|
|
2715
|
-
// by pages and dashboard for typed data wiring.
|
|
2716
|
-
services: ["api", "workflow"],
|
|
2717
|
-
// pages/dashboard: depend on services for typed endpoint wiring (OpenAPI
|
|
2718
|
-
// specs) and on geo so the specialist prompt includes geo-manifest.json
|
|
2719
|
-
// — page/dashboard otters see which page slugs are already claimed and
|
|
2720
|
-
// won't attempt to overwrite them (swp-73c). 'api' is still transitive
|
|
2721
|
-
// through 'data'; auth removed — page-otter has graceful fallback.
|
|
2722
|
-
pages: ["designer", "theme", "data", "services", "geo", "scaffold"],
|
|
2723
|
-
dashboard: ["designer", "theme", "data", "services", "geo", "scaffold"],
|
|
2724
|
-
// auth is the penultimate phase — runs after all content-producing phases
|
|
2725
|
-
// so it can read pages, dashboard, workflow, and geo artifacts for route
|
|
2726
|
-
// protection and RBAC wiring. Skipped upstream phases still satisfy deps
|
|
2727
|
-
// (the foreman marks them executed=true), so auth always runs.
|
|
2728
|
-
auth: ["pages", "dashboard", "workflow", "geo"],
|
|
2729
|
-
// polish is the terminal phase — runs after auth so the landing page and
|
|
2730
|
-
// nav reflect the final set of protected routes and all generated pages.
|
|
2731
|
-
polish: ["auth"]
|
|
2732
|
-
};
|
|
2733
2923
|
var PHASE_ARTIFACT = {
|
|
2734
2924
|
designer: "design-language.json",
|
|
2735
2925
|
theme: "theme-tokens.json",
|
|
@@ -2753,7 +2943,7 @@ var PHASE_TO_OTTER2 = {
|
|
|
2753
2943
|
data: "stackwright-pro-data-otter",
|
|
2754
2944
|
pages: "stackwright-pro-page-otter",
|
|
2755
2945
|
dashboard: "stackwright-pro-dashboard-otter",
|
|
2756
|
-
workflow: "stackwright-pro-
|
|
2946
|
+
workflow: "stackwright-pro-form-wizard-otter",
|
|
2757
2947
|
services: "stackwright-services-otter",
|
|
2758
2948
|
polish: "stackwright-pro-polish-otter",
|
|
2759
2949
|
geo: "stackwright-pro-geo-otter"
|
|
@@ -2786,11 +2976,11 @@ function createDefaultState() {
|
|
|
2786
2976
|
};
|
|
2787
2977
|
}
|
|
2788
2978
|
function statePath(cwd) {
|
|
2789
|
-
return (0,
|
|
2979
|
+
return (0, import_path6.join)(cwd, ".stackwright", "pipeline-state.json");
|
|
2790
2980
|
}
|
|
2791
2981
|
function readState(cwd) {
|
|
2792
2982
|
const p = statePath(cwd);
|
|
2793
|
-
if (!(0,
|
|
2983
|
+
if (!(0, import_fs6.existsSync)(p)) return createDefaultState();
|
|
2794
2984
|
const raw = JSON.parse(safeReadSync(p));
|
|
2795
2985
|
if (typeof raw !== "object" || raw === null || raw.version !== "1.0") {
|
|
2796
2986
|
return createDefaultState();
|
|
@@ -2798,26 +2988,26 @@ function readState(cwd) {
|
|
|
2798
2988
|
return raw;
|
|
2799
2989
|
}
|
|
2800
2990
|
function safeWriteSync(filePath, content) {
|
|
2801
|
-
if ((0,
|
|
2802
|
-
const stat = (0,
|
|
2991
|
+
if ((0, import_fs6.existsSync)(filePath)) {
|
|
2992
|
+
const stat = (0, import_fs6.lstatSync)(filePath);
|
|
2803
2993
|
if (stat.isSymbolicLink()) {
|
|
2804
2994
|
throw new Error(`Refusing to write to symlink: ${filePath}`);
|
|
2805
2995
|
}
|
|
2806
2996
|
}
|
|
2807
|
-
(0,
|
|
2997
|
+
(0, import_fs6.writeFileSync)(filePath, content);
|
|
2808
2998
|
}
|
|
2809
2999
|
function safeReadSync(filePath) {
|
|
2810
|
-
if ((0,
|
|
2811
|
-
const stat = (0,
|
|
3000
|
+
if ((0, import_fs6.existsSync)(filePath)) {
|
|
3001
|
+
const stat = (0, import_fs6.lstatSync)(filePath);
|
|
2812
3002
|
if (stat.isSymbolicLink()) {
|
|
2813
3003
|
throw new Error(`Refusing to read symlink: ${filePath}`);
|
|
2814
3004
|
}
|
|
2815
3005
|
}
|
|
2816
|
-
return (0,
|
|
3006
|
+
return (0, import_fs6.readFileSync)(filePath, "utf-8");
|
|
2817
3007
|
}
|
|
2818
3008
|
function writeState(cwd, state) {
|
|
2819
|
-
const dir = (0,
|
|
2820
|
-
(0,
|
|
3009
|
+
const dir = (0, import_path6.join)(cwd, ".stackwright");
|
|
3010
|
+
(0, import_fs6.mkdirSync)(dir, { recursive: true });
|
|
2821
3011
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2822
3012
|
safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
|
|
2823
3013
|
}
|
|
@@ -2838,8 +3028,8 @@ function handleGetPipelineState(_cwd) {
|
|
|
2838
3028
|
const cwd = _cwd ?? process.cwd();
|
|
2839
3029
|
try {
|
|
2840
3030
|
const state = readState(cwd);
|
|
2841
|
-
const keyPath = (0,
|
|
2842
|
-
if (!(0,
|
|
3031
|
+
const keyPath = (0, import_path6.join)(cwd, ".stackwright", "pipeline-keys.json");
|
|
3032
|
+
if (!(0, import_fs6.existsSync)(keyPath)) {
|
|
2843
3033
|
try {
|
|
2844
3034
|
const { fingerprint } = initPipelineKeys(cwd);
|
|
2845
3035
|
state["signingKeyFingerprint"] = fingerprint;
|
|
@@ -2942,8 +3132,8 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
2942
3132
|
isError: true
|
|
2943
3133
|
};
|
|
2944
3134
|
}
|
|
2945
|
-
const answerFile = (0,
|
|
2946
|
-
if (!(0,
|
|
3135
|
+
const answerFile = (0, import_path6.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3136
|
+
if (!(0, import_fs6.existsSync)(answerFile)) {
|
|
2947
3137
|
return {
|
|
2948
3138
|
text: JSON.stringify({ ready: false, phase, reason: "Answer file not found" }),
|
|
2949
3139
|
isError: false
|
|
@@ -2967,12 +3157,12 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
2967
3157
|
}
|
|
2968
3158
|
}
|
|
2969
3159
|
try {
|
|
2970
|
-
const answersDir = (0,
|
|
3160
|
+
const answersDir = (0, import_path6.join)(cwd, ".stackwright", "answers");
|
|
2971
3161
|
const answeredPhases = [];
|
|
2972
3162
|
const missingPhases = [];
|
|
2973
3163
|
for (const phase2 of PHASE_ORDER) {
|
|
2974
|
-
const answerFile = (0,
|
|
2975
|
-
if ((0,
|
|
3164
|
+
const answerFile = (0, import_path6.join)(answersDir, `${phase2}.json`);
|
|
3165
|
+
if ((0, import_fs6.existsSync)(answerFile)) {
|
|
2976
3166
|
try {
|
|
2977
3167
|
const raw = safeReadSync(answerFile);
|
|
2978
3168
|
const parsed = JSON.parse(raw);
|
|
@@ -3005,6 +3195,7 @@ function handleGetReadyPhases(_cwd) {
|
|
|
3005
3195
|
const cwd = _cwd ?? process.cwd();
|
|
3006
3196
|
try {
|
|
3007
3197
|
const state = readState(cwd);
|
|
3198
|
+
const graph = loadPipelineGraph();
|
|
3008
3199
|
const completed = [];
|
|
3009
3200
|
const ready = [];
|
|
3010
3201
|
const blocked = [];
|
|
@@ -3014,7 +3205,7 @@ function handleGetReadyPhases(_cwd) {
|
|
|
3014
3205
|
completed.push(phase);
|
|
3015
3206
|
continue;
|
|
3016
3207
|
}
|
|
3017
|
-
const deps =
|
|
3208
|
+
const deps = graph.dependencies[phase] ?? [];
|
|
3018
3209
|
const unmetDeps = deps.filter((dep) => !state.phases[dep]?.executed);
|
|
3019
3210
|
if (unmetDeps.length === 0) {
|
|
3020
3211
|
ready.push(phase);
|
|
@@ -3040,7 +3231,7 @@ function handleGetReadyPhases(_cwd) {
|
|
|
3040
3231
|
function handleListArtifacts(_cwd) {
|
|
3041
3232
|
const cwd = _cwd ?? process.cwd();
|
|
3042
3233
|
try {
|
|
3043
|
-
const artifactsDir = (0,
|
|
3234
|
+
const artifactsDir = (0, import_path6.join)(cwd, ".stackwright", "artifacts");
|
|
3044
3235
|
let manifest = null;
|
|
3045
3236
|
try {
|
|
3046
3237
|
manifest = loadSignatureManifest(cwd);
|
|
@@ -3050,8 +3241,8 @@ function handleListArtifacts(_cwd) {
|
|
|
3050
3241
|
let completedCount = 0;
|
|
3051
3242
|
for (const phase of PHASE_ORDER) {
|
|
3052
3243
|
const expectedFile = PHASE_ARTIFACT[phase];
|
|
3053
|
-
const fullPath = (0,
|
|
3054
|
-
const exists = (0,
|
|
3244
|
+
const fullPath = (0, import_path6.join)(artifactsDir, expectedFile);
|
|
3245
|
+
const exists = (0, import_fs6.existsSync)(fullPath);
|
|
3055
3246
|
if (exists) completedCount++;
|
|
3056
3247
|
let signed = false;
|
|
3057
3248
|
let signatureValid = null;
|
|
@@ -3104,9 +3295,9 @@ function handleWritePhaseQuestions(input) {
|
|
|
3104
3295
|
}
|
|
3105
3296
|
} catch {
|
|
3106
3297
|
}
|
|
3107
|
-
const questionsDir = (0,
|
|
3108
|
-
(0,
|
|
3109
|
-
const filePath = (0,
|
|
3298
|
+
const questionsDir = (0, import_path6.join)(cwd, ".stackwright", "questions");
|
|
3299
|
+
(0, import_fs6.mkdirSync)(questionsDir, { recursive: true });
|
|
3300
|
+
const filePath = (0, import_path6.join)(questionsDir, `${phase}.json`);
|
|
3110
3301
|
const payload = {
|
|
3111
3302
|
version: "1.0",
|
|
3112
3303
|
phase,
|
|
@@ -3146,14 +3337,14 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
3146
3337
|
};
|
|
3147
3338
|
}
|
|
3148
3339
|
try {
|
|
3149
|
-
const answersPath = (0,
|
|
3340
|
+
const answersPath = (0, import_path6.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3150
3341
|
let answers = {};
|
|
3151
|
-
if ((0,
|
|
3342
|
+
if ((0, import_fs6.existsSync)(answersPath)) {
|
|
3152
3343
|
answers = JSON.parse(safeReadSync(answersPath));
|
|
3153
3344
|
}
|
|
3154
3345
|
let buildContextText = "";
|
|
3155
|
-
const buildContextPath = (0,
|
|
3156
|
-
if ((0,
|
|
3346
|
+
const buildContextPath = (0, import_path6.join)(cwd, ".stackwright", "build-context.json");
|
|
3347
|
+
if ((0, import_fs6.existsSync)(buildContextPath)) {
|
|
3157
3348
|
try {
|
|
3158
3349
|
const bcRaw = JSON.parse(safeReadSync(buildContextPath));
|
|
3159
3350
|
if (typeof bcRaw.buildContext === "string" && bcRaw.buildContext.trim().length > 0) {
|
|
@@ -3162,13 +3353,14 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
3162
3353
|
} catch {
|
|
3163
3354
|
}
|
|
3164
3355
|
}
|
|
3165
|
-
const
|
|
3356
|
+
const graph = loadPipelineGraph();
|
|
3357
|
+
const deps = graph.dependencies[phase] ?? [];
|
|
3166
3358
|
const artifactSections = [];
|
|
3167
3359
|
const missingDependencies = [];
|
|
3168
3360
|
for (const dep of deps) {
|
|
3169
3361
|
const artifactFile = PHASE_ARTIFACT[dep];
|
|
3170
|
-
const artifactPath = (0,
|
|
3171
|
-
if ((0,
|
|
3362
|
+
const artifactPath = (0, import_path6.join)(cwd, ".stackwright", "artifacts", artifactFile);
|
|
3363
|
+
if ((0, import_fs6.existsSync)(artifactPath)) {
|
|
3172
3364
|
const rawContent = safeReadSync(artifactPath);
|
|
3173
3365
|
const rawBytes = Buffer.from(rawContent, "utf-8");
|
|
3174
3366
|
const content = JSON.parse(rawContent);
|
|
@@ -3233,8 +3425,8 @@ ${JSON.stringify(content, null, 2)}`
|
|
|
3233
3425
|
parts.push("BUILD_CONTEXT:", buildContextText, "");
|
|
3234
3426
|
}
|
|
3235
3427
|
if (phase === "auth") {
|
|
3236
|
-
const initContextPath = (0,
|
|
3237
|
-
if ((0,
|
|
3428
|
+
const initContextPath = (0, import_path6.join)(cwd, ".stackwright", "init-context.json");
|
|
3429
|
+
if ((0, import_fs6.existsSync)(initContextPath)) {
|
|
3238
3430
|
try {
|
|
3239
3431
|
const initContext = JSON.parse(safeReadSync(initContextPath));
|
|
3240
3432
|
if (initContext.devOnly === true || initContext.nonInteractive === true) {
|
|
@@ -3466,7 +3658,7 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
3466
3658
|
workflow: JSON.stringify(
|
|
3467
3659
|
{
|
|
3468
3660
|
version: "1.0",
|
|
3469
|
-
generatedBy: "stackwright-pro-
|
|
3661
|
+
generatedBy: "stackwright-pro-form-wizard-otter",
|
|
3470
3662
|
// Root key is `workflow` — NOT `workflowConfig` (see swp-k7cl)
|
|
3471
3663
|
workflow: {
|
|
3472
3664
|
id: "procurement-approval",
|
|
@@ -3676,6 +3868,57 @@ function handleValidateArtifact(input) {
|
|
|
3676
3868
|
const issues = result.error.issues.slice(0, 3).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
3677
3869
|
return { success: false, error: { message: issues } };
|
|
3678
3870
|
}
|
|
3871
|
+
const rbacRoles = authConfig["rbacRoles"];
|
|
3872
|
+
const rbacDefaultRole = authConfig["rbacDefaultRole"];
|
|
3873
|
+
const protectedRoutes = authConfig["protectedRoutes"];
|
|
3874
|
+
if (Array.isArray(rbacRoles) && rbacRoles.length > 2 && Array.isArray(protectedRoutes) && protectedRoutes.length > 0 && typeof rbacDefaultRole === "string") {
|
|
3875
|
+
const uniqueRoles = new Set(
|
|
3876
|
+
protectedRoutes.map(
|
|
3877
|
+
(r) => typeof r === "string" ? rbacDefaultRole : r.requiredRole ?? rbacDefaultRole
|
|
3878
|
+
)
|
|
3879
|
+
);
|
|
3880
|
+
if (uniqueRoles.size === 1 && uniqueRoles.has(rbacDefaultRole)) {
|
|
3881
|
+
return {
|
|
3882
|
+
success: false,
|
|
3883
|
+
error: {
|
|
3884
|
+
message: `RBAC theatre detected: all ${protectedRoutes.length} protectedRoutes require '${rbacDefaultRole}' (the defaultRole), but ${rbacRoles.length} roles are defined. This defeats the RBAC hierarchy. Each route MUST have the minimum role required for its function. See auth-otter PER-ROUTE RBAC GRANULARITY section.`
|
|
3885
|
+
}
|
|
3886
|
+
};
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
return { success: true };
|
|
3890
|
+
},
|
|
3891
|
+
// swp-4071 — opt-in cross-reference validator for the services phase.
|
|
3892
|
+
// If workflow-config exists and declares serviceHooks, every hook ref must resolve
|
|
3893
|
+
// to a flow or state-machine in this services-config artifact.
|
|
3894
|
+
// Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
|
|
3895
|
+
services: (artifact2) => {
|
|
3896
|
+
const workflowArtifactPath = (0, import_path6.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
|
|
3897
|
+
if (!(0, import_fs6.existsSync)(workflowArtifactPath)) {
|
|
3898
|
+
return { success: true };
|
|
3899
|
+
}
|
|
3900
|
+
let workflowArtifact;
|
|
3901
|
+
try {
|
|
3902
|
+
workflowArtifact = JSON.parse((0, import_fs6.readFileSync)(workflowArtifactPath, "utf-8"));
|
|
3903
|
+
} catch {
|
|
3904
|
+
return { success: true };
|
|
3905
|
+
}
|
|
3906
|
+
const declaredHooks = workflowArtifact.serviceHooks ?? [];
|
|
3907
|
+
if (declaredHooks.length === 0) {
|
|
3908
|
+
return { success: true };
|
|
3909
|
+
}
|
|
3910
|
+
const flows = artifact2["flows"] ?? [];
|
|
3911
|
+
const workflows = artifact2["workflows"] ?? [];
|
|
3912
|
+
const flowNames = /* @__PURE__ */ new Set([...flows.map((f) => f.name), ...workflows.map((w) => w.name)]);
|
|
3913
|
+
const unresolved = declaredHooks.filter((h) => !flowNames.has(h.ref));
|
|
3914
|
+
if (unresolved.length > 0) {
|
|
3915
|
+
return {
|
|
3916
|
+
success: false,
|
|
3917
|
+
error: {
|
|
3918
|
+
message: `Workflow declared ${declaredHooks.length} service hook(s) but ${unresolved.length} unresolved by services-config: ` + unresolved.map((h) => `${h.ref} (${h.kind ?? "unknown"}: ${h.purpose ?? "no purpose"})`).join("; ") + ". Either add matching flows to services-config OR remove the hooks from the workflow YAML."
|
|
3919
|
+
}
|
|
3920
|
+
};
|
|
3921
|
+
}
|
|
3679
3922
|
return { success: true };
|
|
3680
3923
|
}
|
|
3681
3924
|
};
|
|
@@ -3693,10 +3936,10 @@ function handleValidateArtifact(input) {
|
|
|
3693
3936
|
}
|
|
3694
3937
|
}
|
|
3695
3938
|
try {
|
|
3696
|
-
const artifactsDir = (0,
|
|
3697
|
-
(0,
|
|
3939
|
+
const artifactsDir = (0, import_path6.join)(cwd, ".stackwright", "artifacts");
|
|
3940
|
+
(0, import_fs6.mkdirSync)(artifactsDir, { recursive: true });
|
|
3698
3941
|
const artifactFile = PHASE_ARTIFACT[phase];
|
|
3699
|
-
const artifactPath = (0,
|
|
3942
|
+
const artifactPath = (0, import_path6.join)(artifactsDir, artifactFile);
|
|
3700
3943
|
const serialized = JSON.stringify(artifact, null, 2) + "\n";
|
|
3701
3944
|
const artifactBytes = Buffer.from(serialized, "utf-8");
|
|
3702
3945
|
safeWriteSync(artifactPath, serialized);
|
|
@@ -3813,10 +4056,10 @@ function registerPipelineTools(server2) {
|
|
|
3813
4056
|
isError: true
|
|
3814
4057
|
};
|
|
3815
4058
|
}
|
|
3816
|
-
const questionsDir = (0,
|
|
3817
|
-
(0,
|
|
3818
|
-
const outPath = (0,
|
|
3819
|
-
(0,
|
|
4059
|
+
const questionsDir = (0, import_path6.join)(process.cwd(), ".stackwright", "questions");
|
|
4060
|
+
(0, import_fs6.mkdirSync)(questionsDir, { recursive: true });
|
|
4061
|
+
const outPath = (0, import_path6.join)(questionsDir, `${phase}.json`);
|
|
4062
|
+
(0, import_fs6.writeFileSync)(
|
|
3820
4063
|
outPath,
|
|
3821
4064
|
JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
3822
4065
|
);
|
|
@@ -3867,8 +4110,8 @@ function registerPipelineTools(server2) {
|
|
|
3867
4110
|
|
|
3868
4111
|
// src/tools/safe-write.ts
|
|
3869
4112
|
var import_zod14 = require("zod");
|
|
3870
|
-
var
|
|
3871
|
-
var
|
|
4113
|
+
var import_fs7 = require("fs");
|
|
4114
|
+
var import_path7 = require("path");
|
|
3872
4115
|
var OTTER_WRITE_ALLOWLISTS = {
|
|
3873
4116
|
"stackwright-pro-designer-otter": [
|
|
3874
4117
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Design language artifact" }
|
|
@@ -3883,8 +4126,11 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3883
4126
|
],
|
|
3884
4127
|
"stackwright-pro-auth-otter": [
|
|
3885
4128
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Auth config artifact" },
|
|
3886
|
-
{
|
|
3887
|
-
|
|
4129
|
+
{
|
|
4130
|
+
prefix: "stackwright.auth.",
|
|
4131
|
+
suffix: ".yml",
|
|
4132
|
+
description: "Auth config YAML (stackwright.auth.yml \u2014 compiled to _auth.json)"
|
|
4133
|
+
},
|
|
3888
4134
|
{
|
|
3889
4135
|
prefix: ".env",
|
|
3890
4136
|
suffix: "",
|
|
@@ -3903,7 +4149,11 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3903
4149
|
],
|
|
3904
4150
|
"stackwright-pro-data-otter": [
|
|
3905
4151
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Data config artifact" },
|
|
3906
|
-
{
|
|
4152
|
+
{
|
|
4153
|
+
prefix: "stackwright.collections.",
|
|
4154
|
+
suffix: ".yml",
|
|
4155
|
+
description: "Collections config YAML (stackwright.collections.yml \u2014 compiled to _collections.json)"
|
|
4156
|
+
}
|
|
3907
4157
|
],
|
|
3908
4158
|
"stackwright-pro-page-otter": [
|
|
3909
4159
|
{ prefix: "pages/", suffix: "/content.yml", description: "Page content YAML" },
|
|
@@ -3915,7 +4165,7 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3915
4165
|
{ prefix: "pages/", suffix: "/content.yaml", description: "Dashboard content YAML" },
|
|
3916
4166
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Dashboard manifest" }
|
|
3917
4167
|
],
|
|
3918
|
-
"stackwright-pro-
|
|
4168
|
+
"stackwright-pro-form-wizard-otter": [
|
|
3919
4169
|
{ prefix: "workflows/", suffix: ".yml", description: "Workflow definition" },
|
|
3920
4170
|
{ prefix: "workflows/", suffix: ".yaml", description: "Workflow definition" },
|
|
3921
4171
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Workflow config" }
|
|
@@ -3929,7 +4179,12 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3929
4179
|
}
|
|
3930
4180
|
],
|
|
3931
4181
|
"stackwright-pro-api-otter": [
|
|
3932
|
-
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "API config artifact" }
|
|
4182
|
+
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "API config artifact" },
|
|
4183
|
+
{
|
|
4184
|
+
prefix: "stackwright.integrations.",
|
|
4185
|
+
suffix: ".yml",
|
|
4186
|
+
description: "Integrations config YAML (stackwright.integrations.yml \u2014 compiled to _integrations.json)"
|
|
4187
|
+
}
|
|
3933
4188
|
],
|
|
3934
4189
|
"stackwright-pro-geo-otter": [
|
|
3935
4190
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Geo manifest artifact" },
|
|
@@ -3992,7 +4247,7 @@ function getMaxBytesForPath(filePath) {
|
|
|
3992
4247
|
}
|
|
3993
4248
|
var PAGE_REGISTRY_FILE = ".stackwright/page-registry.json";
|
|
3994
4249
|
function extractPageSlug(filePath) {
|
|
3995
|
-
const match = (0,
|
|
4250
|
+
const match = (0, import_path7.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
|
|
3996
4251
|
return match?.[1] ?? null;
|
|
3997
4252
|
}
|
|
3998
4253
|
function extractContentTypes(yamlContent) {
|
|
@@ -4008,27 +4263,27 @@ function extractContentTypes(yamlContent) {
|
|
|
4008
4263
|
return types.length > 0 ? types : ["unknown"];
|
|
4009
4264
|
}
|
|
4010
4265
|
function readPageRegistry(cwd) {
|
|
4011
|
-
const regPath = (0,
|
|
4012
|
-
if (!(0,
|
|
4266
|
+
const regPath = (0, import_path7.join)(cwd, PAGE_REGISTRY_FILE);
|
|
4267
|
+
if (!(0, import_fs7.existsSync)(regPath)) return {};
|
|
4013
4268
|
try {
|
|
4014
|
-
const stat = (0,
|
|
4269
|
+
const stat = (0, import_fs7.lstatSync)(regPath);
|
|
4015
4270
|
if (stat.isSymbolicLink()) return {};
|
|
4016
|
-
return JSON.parse((0,
|
|
4271
|
+
return JSON.parse((0, import_fs7.readFileSync)(regPath, "utf-8"));
|
|
4017
4272
|
} catch {
|
|
4018
4273
|
return {};
|
|
4019
4274
|
}
|
|
4020
4275
|
}
|
|
4021
4276
|
function writePageRegistry(cwd, registry) {
|
|
4022
|
-
const dir = (0,
|
|
4023
|
-
(0,
|
|
4024
|
-
const regPath = (0,
|
|
4025
|
-
if ((0,
|
|
4026
|
-
const stat = (0,
|
|
4277
|
+
const dir = (0, import_path7.join)(cwd, ".stackwright");
|
|
4278
|
+
(0, import_fs7.mkdirSync)(dir, { recursive: true });
|
|
4279
|
+
const regPath = (0, import_path7.join)(cwd, PAGE_REGISTRY_FILE);
|
|
4280
|
+
if ((0, import_fs7.existsSync)(regPath)) {
|
|
4281
|
+
const stat = (0, import_fs7.lstatSync)(regPath);
|
|
4027
4282
|
if (stat.isSymbolicLink()) {
|
|
4028
4283
|
throw new Error("Refusing to write page registry through symlink");
|
|
4029
4284
|
}
|
|
4030
4285
|
}
|
|
4031
|
-
(0,
|
|
4286
|
+
(0, import_fs7.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
|
|
4032
4287
|
}
|
|
4033
4288
|
function checkPageOwnership(cwd, slug, callerOtter) {
|
|
4034
4289
|
const registry = readPageRegistry(cwd);
|
|
@@ -4043,15 +4298,15 @@ function checkPageOwnership(cwd, slug, callerOtter) {
|
|
|
4043
4298
|
};
|
|
4044
4299
|
}
|
|
4045
4300
|
var NEXTJS_BRACKET_SEGMENT_RE = /\[\[\.{3}[a-zA-Z_][a-zA-Z0-9_]*\]\]|\[\.{3}[a-zA-Z_][a-zA-Z0-9_]*\]|\[[a-zA-Z_][a-zA-Z0-9_]*\]/g;
|
|
4046
|
-
function stripNextjsBracketSegments(
|
|
4047
|
-
return
|
|
4301
|
+
function stripNextjsBracketSegments(path4) {
|
|
4302
|
+
return path4.replace(NEXTJS_BRACKET_SEGMENT_RE, "");
|
|
4048
4303
|
}
|
|
4049
4304
|
function checkPathAllowed(callerOtter, filePath) {
|
|
4050
|
-
const normalized = (0,
|
|
4305
|
+
const normalized = (0, import_path7.normalize)(filePath);
|
|
4051
4306
|
if (stripNextjsBracketSegments(normalized).includes("..")) {
|
|
4052
4307
|
return { allowed: false, error: 'Path traversal detected: ".." segments are not allowed' };
|
|
4053
4308
|
}
|
|
4054
|
-
if ((0,
|
|
4309
|
+
if ((0, import_path7.isAbsolute)(normalized)) {
|
|
4055
4310
|
return {
|
|
4056
4311
|
allowed: false,
|
|
4057
4312
|
error: "Absolute paths are not allowed \u2014 use paths relative to project root"
|
|
@@ -4193,11 +4448,11 @@ function handleSafeWrite(input) {
|
|
|
4193
4448
|
};
|
|
4194
4449
|
return { text: JSON.stringify(result), isError: true };
|
|
4195
4450
|
}
|
|
4196
|
-
const normalized = (0,
|
|
4197
|
-
const fullPath = (0,
|
|
4198
|
-
if ((0,
|
|
4451
|
+
const normalized = (0, import_path7.normalize)(filePath);
|
|
4452
|
+
const fullPath = (0, import_path7.join)(cwd, normalized);
|
|
4453
|
+
if ((0, import_fs7.existsSync)(fullPath)) {
|
|
4199
4454
|
try {
|
|
4200
|
-
const stat = (0,
|
|
4455
|
+
const stat = (0, import_fs7.lstatSync)(fullPath);
|
|
4201
4456
|
if (stat.isSymbolicLink()) {
|
|
4202
4457
|
const result = {
|
|
4203
4458
|
success: false,
|
|
@@ -4238,9 +4493,9 @@ function handleSafeWrite(input) {
|
|
|
4238
4493
|
}
|
|
4239
4494
|
try {
|
|
4240
4495
|
if (createDirectories) {
|
|
4241
|
-
(0,
|
|
4496
|
+
(0, import_fs7.mkdirSync)((0, import_path7.dirname)(fullPath), { recursive: true });
|
|
4242
4497
|
}
|
|
4243
|
-
(0,
|
|
4498
|
+
(0, import_fs7.writeFileSync)(fullPath, content, { encoding: "utf-8" });
|
|
4244
4499
|
if (pageSlug) {
|
|
4245
4500
|
try {
|
|
4246
4501
|
const registry = readPageRegistry(cwd);
|
|
@@ -4300,8 +4555,8 @@ function registerSafeWriteTools(server2) {
|
|
|
4300
4555
|
|
|
4301
4556
|
// src/tools/auth.ts
|
|
4302
4557
|
var import_zod15 = require("zod");
|
|
4303
|
-
var
|
|
4304
|
-
var
|
|
4558
|
+
var import_fs8 = require("fs");
|
|
4559
|
+
var import_path8 = require("path");
|
|
4305
4560
|
function buildHierarchy(roles) {
|
|
4306
4561
|
const h = {};
|
|
4307
4562
|
for (let i = 0; i < roles.length - 1; i++) {
|
|
@@ -4315,17 +4570,22 @@ function hierarchyToYaml(hierarchy, indent) {
|
|
|
4315
4570
|
return entries.map(([role, subs]) => `${indent}${role}: [${subs.join(", ")}]`).join("\n");
|
|
4316
4571
|
}
|
|
4317
4572
|
function rolesToYaml(roles, indent) {
|
|
4318
|
-
return roles.map((r) => `${indent}- ${r}`).join("\n");
|
|
4573
|
+
return roles.map((r) => `${indent}- name: ${r}`).join("\n");
|
|
4319
4574
|
}
|
|
4320
|
-
function
|
|
4321
|
-
return routes.map(
|
|
4322
|
-
|
|
4575
|
+
function normalizeRoutes(routes, defaultRole) {
|
|
4576
|
+
return routes.map(
|
|
4577
|
+
(r) => typeof r === "string" ? { pattern: r, requiredRole: defaultRole } : { pattern: r.pattern, requiredRole: r.requiredRole ?? defaultRole }
|
|
4578
|
+
);
|
|
4579
|
+
}
|
|
4580
|
+
function routesToYaml(routes, indent) {
|
|
4581
|
+
return routes.map((r) => `${indent}- pattern: ${r.pattern}
|
|
4582
|
+
${indent} requiredRole: ${r.requiredRole}`).join("\n");
|
|
4323
4583
|
}
|
|
4324
4584
|
function detectNextMajorVersion(cwd) {
|
|
4325
4585
|
try {
|
|
4326
|
-
const pkgPath = (0,
|
|
4327
|
-
if (!(0,
|
|
4328
|
-
const pkg = JSON.parse((0,
|
|
4586
|
+
const pkgPath = (0, import_path8.join)(cwd, "package.json");
|
|
4587
|
+
if (!(0, import_fs8.existsSync)(pkgPath)) return null;
|
|
4588
|
+
const pkg = JSON.parse((0, import_fs8.readFileSync)(pkgPath, "utf8"));
|
|
4329
4589
|
const nextVersion = pkg.dependencies?.next ?? pkg.devDependencies?.next;
|
|
4330
4590
|
if (!nextVersion) return null;
|
|
4331
4591
|
const match = nextVersion.match(/(\d+)/);
|
|
@@ -4334,25 +4594,6 @@ function detectNextMajorVersion(cwd) {
|
|
|
4334
4594
|
return null;
|
|
4335
4595
|
}
|
|
4336
4596
|
}
|
|
4337
|
-
function upsertAuthBlock(existing, authYaml) {
|
|
4338
|
-
const lines = existing.split("\n");
|
|
4339
|
-
let authStart = -1;
|
|
4340
|
-
let authEnd = lines.length;
|
|
4341
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4342
|
-
if (/^auth:/.test(lines[i])) {
|
|
4343
|
-
authStart = i;
|
|
4344
|
-
} else if (authStart >= 0 && i > authStart && /^\S/.test(lines[i]) && lines[i].trim() !== "") {
|
|
4345
|
-
authEnd = i;
|
|
4346
|
-
break;
|
|
4347
|
-
}
|
|
4348
|
-
}
|
|
4349
|
-
if (authStart < 0) {
|
|
4350
|
-
return existing.trimEnd() + "\n" + authYaml + "\n";
|
|
4351
|
-
}
|
|
4352
|
-
const before = lines.slice(0, authStart);
|
|
4353
|
-
const after = lines.slice(authEnd);
|
|
4354
|
-
return [...before, ...authYaml.trimEnd().split("\n"), ...after.length ? after : []].join("\n");
|
|
4355
|
-
}
|
|
4356
4597
|
function generateMiddlewareContent(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes) {
|
|
4357
4598
|
const rbacBlock = ` rbac: {
|
|
4358
4599
|
roles: ${JSON.stringify(roles)},
|
|
@@ -4363,9 +4604,10 @@ function generateMiddlewareContent(method, params, roles, defaultRole, hierarchy
|
|
|
4363
4604
|
enabled: ${auditEnabled},
|
|
4364
4605
|
retentionDays: ${auditRetentionDays},
|
|
4365
4606
|
},`;
|
|
4366
|
-
const
|
|
4607
|
+
const patterns = protectedRoutes.map((r) => r.pattern);
|
|
4608
|
+
const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
|
|
4367
4609
|
const configBlock = `export const config = {
|
|
4368
|
-
matcher: ${JSON.stringify(
|
|
4610
|
+
matcher: ${JSON.stringify(patterns)},
|
|
4369
4611
|
};`;
|
|
4370
4612
|
if (method === "cac") {
|
|
4371
4613
|
const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
|
|
@@ -4448,9 +4690,10 @@ function generateProxyContent(method, params, roles, defaultRole, hierarchy, aud
|
|
|
4448
4690
|
enabled: ${auditEnabled},
|
|
4449
4691
|
retentionDays: ${auditRetentionDays},
|
|
4450
4692
|
},`;
|
|
4451
|
-
const
|
|
4693
|
+
const patterns = protectedRoutes.map((r) => r.pattern);
|
|
4694
|
+
const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
|
|
4452
4695
|
const configBlock = `export const config = {
|
|
4453
|
-
matcher: ${JSON.stringify(
|
|
4696
|
+
matcher: ${JSON.stringify(patterns)},
|
|
4454
4697
|
};`;
|
|
4455
4698
|
if (method === "cac") {
|
|
4456
4699
|
const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
|
|
@@ -4550,36 +4793,33 @@ OAUTH2_CLIENT_SECRET=your-client-secret
|
|
|
4550
4793
|
`;
|
|
4551
4794
|
}
|
|
4552
4795
|
function generateYamlBlock(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes, useProxy) {
|
|
4553
|
-
const rbacSection = `
|
|
4554
|
-
|
|
4555
|
-
${rolesToYaml(roles, "
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
${hierarchyToYaml(hierarchy, "
|
|
4559
|
-
const auditSection = `
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
const
|
|
4563
|
-
${
|
|
4564
|
-
const routeLines = protectedRoutes.map((r) => ` - pattern: ${r}
|
|
4565
|
-
requiredRole: ${defaultRole}`).join("\n");
|
|
4566
|
-
const providerLine = params.provider ? ` provider: ${params.provider}
|
|
4796
|
+
const rbacSection = `rbac:
|
|
4797
|
+
roles:
|
|
4798
|
+
${rolesToYaml(roles, " ")}
|
|
4799
|
+
defaultRole: ${defaultRole}
|
|
4800
|
+
hierarchy:
|
|
4801
|
+
${hierarchyToYaml(hierarchy, " ")}`;
|
|
4802
|
+
const auditSection = `audit:
|
|
4803
|
+
enabled: ${auditEnabled}
|
|
4804
|
+
retentionDays: ${auditRetentionDays}`;
|
|
4805
|
+
const routeLines = routesToYaml(protectedRoutes, " ");
|
|
4806
|
+
const providerLine = params.provider ? `provider: ${params.provider}
|
|
4567
4807
|
` : "";
|
|
4568
4808
|
if (method === "cac") {
|
|
4569
4809
|
const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
|
|
4570
4810
|
const edipiLookup = params.cacEdipiLookup ?? "./config/edipi-lookup.json";
|
|
4571
4811
|
const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
|
|
4572
4812
|
const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
|
|
4573
|
-
return
|
|
4574
|
-
|
|
4575
|
-
${providerLine}
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4813
|
+
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
4814
|
+
method: cac
|
|
4815
|
+
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
4816
|
+
cac:
|
|
4817
|
+
caBundle: \${CAC_CA_BUNDLE}
|
|
4818
|
+
edipiLookup: ${edipiLookup}
|
|
4819
|
+
ocspEndpoint: \${CAC_OCSP_ENDPOINT}
|
|
4820
|
+
certHeader: ${certHeader}
|
|
4581
4821
|
${rbacSection}
|
|
4582
|
-
|
|
4822
|
+
protectedRoutes:
|
|
4583
4823
|
${routeLines}
|
|
4584
4824
|
${auditSection}
|
|
4585
4825
|
`;
|
|
@@ -4587,33 +4827,33 @@ ${auditSection}
|
|
|
4587
4827
|
if (method === "oidc") {
|
|
4588
4828
|
const scopes2 = params.oidcScopes ?? "openid profile email";
|
|
4589
4829
|
const roleClaim = params.oidcRoleClaim ?? "roles";
|
|
4590
|
-
return
|
|
4591
|
-
|
|
4592
|
-
${providerLine}
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4830
|
+
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
4831
|
+
method: oidc
|
|
4832
|
+
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
4833
|
+
oidc:
|
|
4834
|
+
discoveryUrl: \${OIDC_DISCOVERY_URL}
|
|
4835
|
+
clientId: \${OIDC_CLIENT_ID}
|
|
4836
|
+
clientSecret: \${OIDC_CLIENT_SECRET}
|
|
4837
|
+
scopes: ${scopes2}
|
|
4838
|
+
roleClaim: ${roleClaim}
|
|
4599
4839
|
${rbacSection}
|
|
4600
|
-
|
|
4840
|
+
protectedRoutes:
|
|
4601
4841
|
${routeLines}
|
|
4602
4842
|
${auditSection}
|
|
4603
4843
|
`;
|
|
4604
4844
|
}
|
|
4605
4845
|
const scopes = params.oauth2Scopes ?? "read write";
|
|
4606
|
-
return
|
|
4607
|
-
|
|
4608
|
-
${providerLine}
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4846
|
+
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
4847
|
+
method: oauth2
|
|
4848
|
+
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
4849
|
+
oauth2:
|
|
4850
|
+
authorizationUrl: \${OAUTH2_AUTH_URL}
|
|
4851
|
+
tokenUrl: \${OAUTH2_TOKEN_URL}
|
|
4852
|
+
clientId: \${OAUTH2_CLIENT_ID}
|
|
4853
|
+
clientSecret: \${OAUTH2_CLIENT_SECRET}
|
|
4854
|
+
scopes: ${scopes}
|
|
4615
4855
|
${rbacSection}
|
|
4616
|
-
|
|
4856
|
+
protectedRoutes:
|
|
4617
4857
|
${routeLines}
|
|
4618
4858
|
${auditSection}
|
|
4619
4859
|
`;
|
|
@@ -4628,9 +4868,10 @@ function generateDevOnlyMiddlewareContent(method, params, roles, defaultRole, hi
|
|
|
4628
4868
|
enabled: ${auditEnabled},
|
|
4629
4869
|
retentionDays: ${auditRetentionDays},
|
|
4630
4870
|
},`;
|
|
4631
|
-
const
|
|
4871
|
+
const patterns = protectedRoutes.map((r) => r.pattern);
|
|
4872
|
+
const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
|
|
4632
4873
|
const configBlock = `export const config = {
|
|
4633
|
-
matcher: ${JSON.stringify(
|
|
4874
|
+
matcher: ${JSON.stringify(patterns)},
|
|
4634
4875
|
};`;
|
|
4635
4876
|
const devHeader = [
|
|
4636
4877
|
"// middleware.ts \u2014 generated by @stackwright-pro/auth-nextjs (dev-only mock)",
|
|
@@ -4717,9 +4958,10 @@ function generateDevOnlyProxyContent(method, params, roles, defaultRole, hierarc
|
|
|
4717
4958
|
enabled: ${auditEnabled},
|
|
4718
4959
|
retentionDays: ${auditRetentionDays},
|
|
4719
4960
|
},`;
|
|
4720
|
-
const
|
|
4961
|
+
const patterns = protectedRoutes.map((r) => r.pattern);
|
|
4962
|
+
const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
|
|
4721
4963
|
const configBlock = `export const config = {
|
|
4722
|
-
matcher: ${JSON.stringify(
|
|
4964
|
+
matcher: ${JSON.stringify(patterns)},
|
|
4723
4965
|
};`;
|
|
4724
4966
|
const devHeader = [
|
|
4725
4967
|
"// proxy.ts -- generated by @stackwright-pro/auth (Next.js >=16, dev-only mock)",
|
|
@@ -4797,35 +5039,35 @@ ${configBlock}
|
|
|
4797
5039
|
`;
|
|
4798
5040
|
}
|
|
4799
5041
|
function generateDevOnlyYamlBlock(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes, useProxy) {
|
|
4800
|
-
const rbacSection = `
|
|
4801
|
-
|
|
4802
|
-
${rolesToYaml(roles, "
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
${hierarchyToYaml(hierarchy, "
|
|
4806
|
-
const auditSection = `
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
const routeLines = protectedRoutes.map((r) => `
|
|
4810
|
-
|
|
4811
|
-
const providerLine = params.provider ? `
|
|
5042
|
+
const rbacSection = `rbac:
|
|
5043
|
+
roles:
|
|
5044
|
+
${rolesToYaml(roles, " ")}
|
|
5045
|
+
defaultRole: ${defaultRole}
|
|
5046
|
+
hierarchy:
|
|
5047
|
+
${hierarchyToYaml(hierarchy, " ")}`;
|
|
5048
|
+
const auditSection = `audit:
|
|
5049
|
+
enabled: ${auditEnabled}
|
|
5050
|
+
retentionDays: ${auditRetentionDays}`;
|
|
5051
|
+
const routeLines = protectedRoutes.map((r) => ` - pattern: ${r.pattern}
|
|
5052
|
+
requiredRole: ${r.requiredRole}`).join("\n");
|
|
5053
|
+
const providerLine = params.provider ? `provider: ${params.provider}
|
|
4812
5054
|
` : "";
|
|
4813
5055
|
if (method === "cac") {
|
|
4814
5056
|
const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
|
|
4815
5057
|
const edipiLookup = params.cacEdipiLookup ?? "./config/edipi-lookup.json";
|
|
4816
5058
|
const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
|
|
4817
5059
|
const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
|
|
4818
|
-
return
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
${providerLine}
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
5060
|
+
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
5061
|
+
method: cac
|
|
5062
|
+
devOnly: true
|
|
5063
|
+
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
5064
|
+
cac:
|
|
5065
|
+
caBundle: ${caBundle}
|
|
5066
|
+
edipiLookup: ${edipiLookup}
|
|
5067
|
+
ocspEndpoint: ${ocspEndpoint}
|
|
5068
|
+
certHeader: ${certHeader}
|
|
4827
5069
|
${rbacSection}
|
|
4828
|
-
|
|
5070
|
+
protectedRoutes:
|
|
4829
5071
|
${routeLines}
|
|
4830
5072
|
${auditSection}
|
|
4831
5073
|
`;
|
|
@@ -4833,35 +5075,35 @@ ${auditSection}
|
|
|
4833
5075
|
if (method === "oidc") {
|
|
4834
5076
|
const scopes2 = params.oidcScopes ?? "openid profile email";
|
|
4835
5077
|
const roleClaim = params.oidcRoleClaim ?? "roles";
|
|
4836
|
-
return
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
${providerLine}
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
5078
|
+
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
5079
|
+
method: oidc
|
|
5080
|
+
devOnly: true
|
|
5081
|
+
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
5082
|
+
oidc:
|
|
5083
|
+
discoveryUrl: https://dev-mock-oidc/.well-known/openid-configuration
|
|
5084
|
+
clientId: dev-mock-client
|
|
5085
|
+
clientSecret: dev-mock-secret
|
|
5086
|
+
scopes: ${scopes2}
|
|
5087
|
+
roleClaim: ${roleClaim}
|
|
4846
5088
|
${rbacSection}
|
|
4847
|
-
|
|
5089
|
+
protectedRoutes:
|
|
4848
5090
|
${routeLines}
|
|
4849
5091
|
${auditSection}
|
|
4850
5092
|
`;
|
|
4851
5093
|
}
|
|
4852
5094
|
const scopes = params.oauth2Scopes ?? "read write";
|
|
4853
|
-
return
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
${providerLine}
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
5095
|
+
return `# stackwright.auth.yml \u2014 generated by Auth Otter
|
|
5096
|
+
method: oauth2
|
|
5097
|
+
devOnly: true
|
|
5098
|
+
${providerLine}${useProxy ? "proxy" : "middleware"}: ./${useProxy ? "proxy" : "middleware"}.ts
|
|
5099
|
+
oauth2:
|
|
5100
|
+
authorizationUrl: https://dev-mock-oauth2/authorize
|
|
5101
|
+
tokenUrl: https://dev-mock-oauth2/token
|
|
5102
|
+
clientId: dev-mock-client
|
|
5103
|
+
clientSecret: dev-mock-secret
|
|
5104
|
+
scopes: ${scopes}
|
|
4863
5105
|
${rbacSection}
|
|
4864
|
-
|
|
5106
|
+
protectedRoutes:
|
|
4865
5107
|
${routeLines}
|
|
4866
5108
|
${auditSection}
|
|
4867
5109
|
`;
|
|
@@ -4940,6 +5182,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
4940
5182
|
} = params;
|
|
4941
5183
|
const roles = rbacRoles;
|
|
4942
5184
|
const defaultRole = params.rbacDefaultRole ?? roles[roles.length - 1];
|
|
5185
|
+
const normalizedRoutes = normalizeRoutes(protectedRoutes, defaultRole);
|
|
4943
5186
|
const hierarchy = buildHierarchy(roles);
|
|
4944
5187
|
const detectedVersion = params.nextMajorVersion ?? detectNextMajorVersion(cwd);
|
|
4945
5188
|
const useProxy = (detectedVersion ?? 0) >= 16;
|
|
@@ -4976,7 +5219,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
4976
5219
|
hierarchy,
|
|
4977
5220
|
auditEnabled,
|
|
4978
5221
|
auditRetentionDays,
|
|
4979
|
-
|
|
5222
|
+
normalizedRoutes
|
|
4980
5223
|
) : generateDevOnlyMiddlewareContent(
|
|
4981
5224
|
effectiveMethod,
|
|
4982
5225
|
params,
|
|
@@ -4985,7 +5228,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
4985
5228
|
hierarchy,
|
|
4986
5229
|
auditEnabled,
|
|
4987
5230
|
auditRetentionDays,
|
|
4988
|
-
|
|
5231
|
+
normalizedRoutes
|
|
4989
5232
|
) : useProxy ? generateProxyContent(
|
|
4990
5233
|
effectiveMethod,
|
|
4991
5234
|
params,
|
|
@@ -4994,7 +5237,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
4994
5237
|
hierarchy,
|
|
4995
5238
|
auditEnabled,
|
|
4996
5239
|
auditRetentionDays,
|
|
4997
|
-
|
|
5240
|
+
normalizedRoutes
|
|
4998
5241
|
) : generateMiddlewareContent(
|
|
4999
5242
|
effectiveMethod,
|
|
5000
5243
|
params,
|
|
@@ -5003,9 +5246,9 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5003
5246
|
hierarchy,
|
|
5004
5247
|
auditEnabled,
|
|
5005
5248
|
auditRetentionDays,
|
|
5006
|
-
|
|
5249
|
+
normalizedRoutes
|
|
5007
5250
|
);
|
|
5008
|
-
(0,
|
|
5251
|
+
(0, import_fs8.writeFileSync)((0, import_path8.join)(cwd, conventionFile), authFileContent, "utf8");
|
|
5009
5252
|
filesWritten.push(conventionFile);
|
|
5010
5253
|
} catch (err) {
|
|
5011
5254
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5025,12 +5268,12 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5025
5268
|
if (!devOnly) {
|
|
5026
5269
|
try {
|
|
5027
5270
|
const envBlock = generateEnvBlock(effectiveMethod, params);
|
|
5028
|
-
const envPath = (0,
|
|
5029
|
-
if ((0,
|
|
5030
|
-
const existing = (0,
|
|
5031
|
-
(0,
|
|
5271
|
+
const envPath = (0, import_path8.join)(cwd, ".env.example");
|
|
5272
|
+
if ((0, import_fs8.existsSync)(envPath)) {
|
|
5273
|
+
const existing = (0, import_fs8.readFileSync)(envPath, "utf8");
|
|
5274
|
+
(0, import_fs8.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
|
|
5032
5275
|
} else {
|
|
5033
|
-
(0,
|
|
5276
|
+
(0, import_fs8.writeFileSync)(envPath, envBlock, "utf8");
|
|
5034
5277
|
}
|
|
5035
5278
|
filesWritten.push(".env.example");
|
|
5036
5279
|
} catch (err) {
|
|
@@ -5048,10 +5291,10 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5048
5291
|
}
|
|
5049
5292
|
if (devOnly) {
|
|
5050
5293
|
try {
|
|
5051
|
-
const mockAuthDir = (0,
|
|
5052
|
-
(0,
|
|
5294
|
+
const mockAuthDir = (0, import_path8.join)(cwd, "lib");
|
|
5295
|
+
(0, import_fs8.mkdirSync)(mockAuthDir, { recursive: true });
|
|
5053
5296
|
const mockAuthContent = generateMockAuthContent(roles, mockUsers);
|
|
5054
|
-
(0,
|
|
5297
|
+
(0, import_fs8.writeFileSync)((0, import_path8.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
|
|
5055
5298
|
filesWritten.push("lib/mock-auth.ts");
|
|
5056
5299
|
} catch (err) {
|
|
5057
5300
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5069,11 +5312,11 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5069
5312
|
};
|
|
5070
5313
|
}
|
|
5071
5314
|
try {
|
|
5072
|
-
const pkgPath = (0,
|
|
5073
|
-
if ((0,
|
|
5074
|
-
const existingPkg = (0,
|
|
5315
|
+
const pkgPath = (0, import_path8.join)(cwd, "package.json");
|
|
5316
|
+
if ((0, import_fs8.existsSync)(pkgPath)) {
|
|
5317
|
+
const existingPkg = (0, import_fs8.readFileSync)(pkgPath, "utf8");
|
|
5075
5318
|
const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
|
|
5076
|
-
(0,
|
|
5319
|
+
(0, import_fs8.writeFileSync)(pkgPath, updatedPkg, "utf8");
|
|
5077
5320
|
filesWritten.push("package.json");
|
|
5078
5321
|
}
|
|
5079
5322
|
} catch (err) {
|
|
@@ -5101,7 +5344,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5101
5344
|
hierarchy,
|
|
5102
5345
|
auditEnabled,
|
|
5103
5346
|
auditRetentionDays,
|
|
5104
|
-
|
|
5347
|
+
normalizedRoutes,
|
|
5105
5348
|
useProxy
|
|
5106
5349
|
) : generateYamlBlock(
|
|
5107
5350
|
effectiveMethod,
|
|
@@ -5111,24 +5354,21 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5111
5354
|
hierarchy,
|
|
5112
5355
|
auditEnabled,
|
|
5113
5356
|
auditRetentionDays,
|
|
5114
|
-
|
|
5357
|
+
normalizedRoutes,
|
|
5115
5358
|
useProxy
|
|
5116
5359
|
);
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
(0, import_fs7.writeFileSync)(ymlPath, authYaml, "utf8");
|
|
5120
|
-
} else {
|
|
5121
|
-
const existing = (0, import_fs7.readFileSync)(ymlPath, "utf8");
|
|
5122
|
-
(0, import_fs7.writeFileSync)(ymlPath, upsertAuthBlock(existing, authYaml), "utf8");
|
|
5123
|
-
}
|
|
5124
|
-
filesWritten.push("stackwright.yml");
|
|
5360
|
+
(0, import_fs8.writeFileSync)((0, import_path8.join)(cwd, "stackwright.auth.yml"), authYaml, "utf8");
|
|
5361
|
+
filesWritten.push("stackwright.auth.yml");
|
|
5125
5362
|
} catch (err) {
|
|
5126
5363
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5127
5364
|
return {
|
|
5128
5365
|
content: [
|
|
5129
5366
|
{
|
|
5130
5367
|
type: "text",
|
|
5131
|
-
text: JSON.stringify({
|
|
5368
|
+
text: JSON.stringify({
|
|
5369
|
+
success: false,
|
|
5370
|
+
error: `Failed writing stackwright.auth.yml: ${msg}`
|
|
5371
|
+
})
|
|
5132
5372
|
}
|
|
5133
5373
|
],
|
|
5134
5374
|
isError: true
|
|
@@ -5173,7 +5413,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5173
5413
|
function registerAuthTools(server2) {
|
|
5174
5414
|
server2.tool(
|
|
5175
5415
|
"stackwright_pro_configure_auth",
|
|
5176
|
-
"Generate authentication middleware and configuration for a Next.js Stackwright application. Writes `middleware.ts` from a secure template,
|
|
5416
|
+
"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.",
|
|
5177
5417
|
{
|
|
5178
5418
|
method: import_zod15.z.enum(["cac", "oidc", "oauth2", "none"]),
|
|
5179
5419
|
provider: import_zod15.z.enum(["azure-ad", "okta", "ping", "cognito", "custom"]).optional(),
|
|
@@ -5200,8 +5440,15 @@ function registerAuthTools(server2) {
|
|
|
5200
5440
|
// Audit
|
|
5201
5441
|
auditEnabled: boolCoerce(import_zod15.z.boolean().optional()),
|
|
5202
5442
|
auditRetentionDays: numCoerce(import_zod15.z.number().int().positive().optional()),
|
|
5203
|
-
// Routes
|
|
5204
|
-
protectedRoutes: jsonCoerce(
|
|
5443
|
+
// Routes — accepts bare strings (backward-compat) or per-route objects (swp-owu0)
|
|
5444
|
+
protectedRoutes: jsonCoerce(
|
|
5445
|
+
import_zod15.z.array(
|
|
5446
|
+
import_zod15.z.union([
|
|
5447
|
+
import_zod15.z.string(),
|
|
5448
|
+
import_zod15.z.object({ pattern: import_zod15.z.string(), requiredRole: import_zod15.z.string().optional() })
|
|
5449
|
+
])
|
|
5450
|
+
).optional()
|
|
5451
|
+
),
|
|
5205
5452
|
// Injection for tests
|
|
5206
5453
|
_cwd: import_zod15.z.string().optional(),
|
|
5207
5454
|
devOnly: boolCoerce(import_zod15.z.boolean().optional()),
|
|
@@ -5217,28 +5464,28 @@ function registerAuthTools(server2) {
|
|
|
5217
5464
|
|
|
5218
5465
|
// src/integrity.ts
|
|
5219
5466
|
var import_crypto4 = require("crypto");
|
|
5220
|
-
var
|
|
5221
|
-
var
|
|
5467
|
+
var import_fs9 = require("fs");
|
|
5468
|
+
var import_path9 = require("path");
|
|
5222
5469
|
var _checksums = /* @__PURE__ */ new Map([
|
|
5223
5470
|
[
|
|
5224
5471
|
"stackwright-pro-api-otter.json",
|
|
5225
|
-
"
|
|
5472
|
+
"822b35d7a330ed8ac0b42a63b0f70a41885aa9b5ea23cc5b8b998b549da4c05c"
|
|
5226
5473
|
],
|
|
5227
5474
|
[
|
|
5228
5475
|
"stackwright-pro-auth-otter.json",
|
|
5229
|
-
"
|
|
5476
|
+
"d6cd5732667018d99456be77d5955e454da7a0999a0330b5f03a483aa1b9b33f"
|
|
5230
5477
|
],
|
|
5231
5478
|
[
|
|
5232
5479
|
"stackwright-pro-dashboard-otter.json",
|
|
5233
|
-
"
|
|
5480
|
+
"e3b82555fcffbd77285bd304828814e9f9f7257130797c9de3785de97527668c"
|
|
5234
5481
|
],
|
|
5235
5482
|
[
|
|
5236
5483
|
"stackwright-pro-data-otter.json",
|
|
5237
|
-
"
|
|
5484
|
+
"bb66eaab31c6872536dca4d3ff145724a46356946dd0665cc4f0346714d3bacb"
|
|
5238
5485
|
],
|
|
5239
5486
|
[
|
|
5240
5487
|
"stackwright-pro-designer-otter.json",
|
|
5241
|
-
"
|
|
5488
|
+
"b54121c6a2ab5b1ad68c1262c58b2e04e6315feacd6bb8de8e78eb36f2fa6be6"
|
|
5242
5489
|
],
|
|
5243
5490
|
[
|
|
5244
5491
|
"stackwright-pro-domain-expert-otter.json",
|
|
@@ -5246,35 +5493,35 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5246
5493
|
],
|
|
5247
5494
|
[
|
|
5248
5495
|
"stackwright-pro-foreman-otter.json",
|
|
5249
|
-
"
|
|
5496
|
+
"abb1cc5e40a8c5ff98057273f43a9e90e2791a15951090cd4d98407c0c3618e3"
|
|
5497
|
+
],
|
|
5498
|
+
[
|
|
5499
|
+
"stackwright-pro-form-wizard-otter.json",
|
|
5500
|
+
"975ad68e8fb6fe5a10373be278946fa4d9d7ec2238a0b2bd9e4a412e5b1fdd6d"
|
|
5250
5501
|
],
|
|
5251
5502
|
[
|
|
5252
5503
|
"stackwright-pro-geo-otter.json",
|
|
5253
|
-
"
|
|
5504
|
+
"ac440da786d8c5ab1feddbf861449c3be3a433a04370578e1b2d35bf6ae7a601"
|
|
5254
5505
|
],
|
|
5255
5506
|
[
|
|
5256
5507
|
"stackwright-pro-page-otter.json",
|
|
5257
|
-
"
|
|
5508
|
+
"b70bd6e5d2a40230f7c24ff4a6113585a1614e5b6a215ece2bd47ff053c30f58"
|
|
5258
5509
|
],
|
|
5259
5510
|
[
|
|
5260
5511
|
"stackwright-pro-polish-otter.json",
|
|
5261
|
-
"
|
|
5512
|
+
"48ef3cf7f29ac6b5e8ab2004d4d41e2fb899e781689178d3905993241c84095a"
|
|
5262
5513
|
],
|
|
5263
5514
|
[
|
|
5264
5515
|
"stackwright-pro-scaffold-otter.json",
|
|
5265
|
-
"
|
|
5516
|
+
"e8662b63bbc9ce38851d94f9656bbebefdb7843d038862adb9fde2c23f9e77c2"
|
|
5266
5517
|
],
|
|
5267
5518
|
[
|
|
5268
5519
|
"stackwright-pro-theme-otter.json",
|
|
5269
|
-
"
|
|
5270
|
-
],
|
|
5271
|
-
[
|
|
5272
|
-
"stackwright-pro-workflow-otter.json",
|
|
5273
|
-
"5f6209fadc1355580e2455a16386f06bd7d5814f047b2dd5b33800abf6a48ff8"
|
|
5520
|
+
"fb62e56642f7f94c50ce173f3e1ce978b3f20ab1567e87403b901d6dd991a7db"
|
|
5274
5521
|
],
|
|
5275
5522
|
[
|
|
5276
5523
|
"stackwright-services-otter.json",
|
|
5277
|
-
"
|
|
5524
|
+
"0a5dac670482871c718099767b30bf23d8ec57e942bd40a00ce295926d82c6bd"
|
|
5278
5525
|
]
|
|
5279
5526
|
]);
|
|
5280
5527
|
Object.freeze(_checksums);
|
|
@@ -5296,14 +5543,14 @@ function safeEqual(a, b) {
|
|
|
5296
5543
|
return (0, import_crypto4.timingSafeEqual)(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
|
|
5297
5544
|
}
|
|
5298
5545
|
function verifyOtterFile(filePath) {
|
|
5299
|
-
const filename = (0,
|
|
5546
|
+
const filename = (0, import_path9.basename)(filePath);
|
|
5300
5547
|
const expected = CANONICAL_CHECKSUMS.get(filename);
|
|
5301
5548
|
if (expected === void 0) {
|
|
5302
5549
|
return { verified: false, filename, error: `Unknown otter file: not in canonical set` };
|
|
5303
5550
|
}
|
|
5304
5551
|
let stat;
|
|
5305
5552
|
try {
|
|
5306
|
-
stat = (0,
|
|
5553
|
+
stat = (0, import_fs9.lstatSync)(filePath);
|
|
5307
5554
|
} catch (err) {
|
|
5308
5555
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5309
5556
|
return { verified: false, filename, error: `Cannot stat file: ${msg}` };
|
|
@@ -5321,7 +5568,7 @@ function verifyOtterFile(filePath) {
|
|
|
5321
5568
|
}
|
|
5322
5569
|
let raw;
|
|
5323
5570
|
try {
|
|
5324
|
-
raw = (0,
|
|
5571
|
+
raw = (0, import_fs9.readFileSync)(filePath);
|
|
5325
5572
|
} catch (err) {
|
|
5326
5573
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5327
5574
|
return { verified: false, filename, error: `Cannot read file: ${msg}` };
|
|
@@ -5371,7 +5618,7 @@ function verifyAllOtters(otterDir) {
|
|
|
5371
5618
|
const unknown = [];
|
|
5372
5619
|
let entries;
|
|
5373
5620
|
try {
|
|
5374
|
-
entries = (0,
|
|
5621
|
+
entries = (0, import_fs9.readdirSync)(otterDir);
|
|
5375
5622
|
} catch (err) {
|
|
5376
5623
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5377
5624
|
return {
|
|
@@ -5382,9 +5629,9 @@ function verifyAllOtters(otterDir) {
|
|
|
5382
5629
|
}
|
|
5383
5630
|
const otterFiles = entries.filter((f) => f.endsWith("-otter.json"));
|
|
5384
5631
|
for (const filename of otterFiles) {
|
|
5385
|
-
const filePath = (0,
|
|
5632
|
+
const filePath = (0, import_path9.join)(otterDir, filename);
|
|
5386
5633
|
try {
|
|
5387
|
-
if ((0,
|
|
5634
|
+
if ((0, import_fs9.lstatSync)(filePath).isSymbolicLink()) {
|
|
5388
5635
|
failed.push({ filename, error: "Skipped: symlink" });
|
|
5389
5636
|
continue;
|
|
5390
5637
|
}
|
|
@@ -5410,9 +5657,9 @@ var DEFAULT_SEARCH_PATHS = ["node_modules/@stackwright-pro/otters/src/", "packag
|
|
|
5410
5657
|
function resolveOtterDir() {
|
|
5411
5658
|
const cwd = process.cwd();
|
|
5412
5659
|
for (const relative of DEFAULT_SEARCH_PATHS) {
|
|
5413
|
-
const candidate = (0,
|
|
5660
|
+
const candidate = (0, import_path9.join)(cwd, relative);
|
|
5414
5661
|
try {
|
|
5415
|
-
(0,
|
|
5662
|
+
(0, import_fs9.lstatSync)(candidate);
|
|
5416
5663
|
return candidate;
|
|
5417
5664
|
} catch {
|
|
5418
5665
|
}
|
|
@@ -5491,13 +5738,13 @@ function registerIntegrityTools(server2) {
|
|
|
5491
5738
|
|
|
5492
5739
|
// src/tools/domain.ts
|
|
5493
5740
|
var import_zod16 = require("zod");
|
|
5494
|
-
var
|
|
5495
|
-
var
|
|
5741
|
+
var import_fs10 = require("fs");
|
|
5742
|
+
var import_path10 = require("path");
|
|
5496
5743
|
function handleListCollections(input) {
|
|
5497
5744
|
const cwd = input._cwd ?? process.cwd();
|
|
5498
5745
|
const sources = [
|
|
5499
5746
|
{
|
|
5500
|
-
path: (0,
|
|
5747
|
+
path: (0, import_path10.join)(cwd, ".stackwright", "artifacts", "data-config.json"),
|
|
5501
5748
|
source: "data-config.json",
|
|
5502
5749
|
parse: (raw) => {
|
|
5503
5750
|
const parsed = JSON.parse(raw);
|
|
@@ -5508,15 +5755,15 @@ function handleListCollections(input) {
|
|
|
5508
5755
|
}
|
|
5509
5756
|
},
|
|
5510
5757
|
{
|
|
5511
|
-
path: (0,
|
|
5758
|
+
path: (0, import_path10.join)(cwd, "stackwright.yml"),
|
|
5512
5759
|
source: "stackwright.yml",
|
|
5513
5760
|
parse: extractCollectionsFromYaml
|
|
5514
5761
|
}
|
|
5515
5762
|
];
|
|
5516
|
-
for (const { path:
|
|
5517
|
-
if (!(0,
|
|
5763
|
+
for (const { path: path4, source, parse } of sources) {
|
|
5764
|
+
if (!(0, import_fs10.existsSync)(path4)) continue;
|
|
5518
5765
|
try {
|
|
5519
|
-
const collections = parse((0,
|
|
5766
|
+
const collections = parse((0, import_fs10.readFileSync)(path4, "utf8"));
|
|
5520
5767
|
return {
|
|
5521
5768
|
text: JSON.stringify({ collections, source, collectionCount: collections.length }),
|
|
5522
5769
|
isError: false
|
|
@@ -5667,8 +5914,8 @@ function handleValidateWorkflow(input) {
|
|
|
5667
5914
|
if (input.workflow && Object.keys(input.workflow).length > 0) {
|
|
5668
5915
|
raw = input.workflow;
|
|
5669
5916
|
} else {
|
|
5670
|
-
const artifactPath = (0,
|
|
5671
|
-
if (!(0,
|
|
5917
|
+
const artifactPath = (0, import_path10.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
|
|
5918
|
+
if (!(0, import_fs10.existsSync)(artifactPath)) {
|
|
5672
5919
|
return fail([
|
|
5673
5920
|
{
|
|
5674
5921
|
code: "NO_WORKFLOW",
|
|
@@ -5677,7 +5924,7 @@ function handleValidateWorkflow(input) {
|
|
|
5677
5924
|
]);
|
|
5678
5925
|
}
|
|
5679
5926
|
try {
|
|
5680
|
-
raw = JSON.parse((0,
|
|
5927
|
+
raw = JSON.parse((0, import_fs10.readFileSync)(artifactPath, "utf8"));
|
|
5681
5928
|
} catch (err) {
|
|
5682
5929
|
return fail([{ code: "INVALID_JSON", message: `Failed to parse workflow artifact: ${err}` }]);
|
|
5683
5930
|
}
|
|
@@ -5789,12 +6036,12 @@ function handleValidateWorkflow(input) {
|
|
|
5789
6036
|
return { text: JSON.stringify({ valid: errors.length === 0, errors, warnings }), isError: false };
|
|
5790
6037
|
}
|
|
5791
6038
|
function validateTransitionTargets(step, stepId, stepIds, errors) {
|
|
5792
|
-
const check = (target,
|
|
6039
|
+
const check = (target, path4) => {
|
|
5793
6040
|
if (typeof target === "string" && !stepIds.has(target)) {
|
|
5794
6041
|
errors.push({
|
|
5795
6042
|
code: "ORPHANED_TRANSITION",
|
|
5796
6043
|
message: `Step "${stepId}" transitions to "${target}" which does not exist`,
|
|
5797
|
-
path:
|
|
6044
|
+
path: path4
|
|
5798
6045
|
});
|
|
5799
6046
|
}
|
|
5800
6047
|
};
|
|
@@ -5833,11 +6080,11 @@ function validateTransitionTargets(step, stepId, stepIds, errors) {
|
|
|
5833
6080
|
}
|
|
5834
6081
|
function collectServiceWarnings(step, stepId, warnings) {
|
|
5835
6082
|
const isService = (val) => typeof val === "string" && val.startsWith("service:");
|
|
5836
|
-
const warn = (
|
|
6083
|
+
const warn = (path4) => {
|
|
5837
6084
|
warnings.push({
|
|
5838
6085
|
code: "WARN_SERVICE_REFERENCE",
|
|
5839
|
-
message: `service: reference at "${
|
|
5840
|
-
path:
|
|
6086
|
+
message: `service: reference at "${path4}" requires @stackwright-pro/services. Prism mock fallback will be used until services layer is configured.`,
|
|
6087
|
+
path: path4
|
|
5841
6088
|
});
|
|
5842
6089
|
};
|
|
5843
6090
|
const onSubmit = step.on_submit;
|
|
@@ -5915,7 +6162,7 @@ function buildTypeSchemaSummary() {
|
|
|
5915
6162
|
"TransitionConditionSchema",
|
|
5916
6163
|
"PersistenceSchema"
|
|
5917
6164
|
],
|
|
5918
|
-
otter: "stackwright-pro-
|
|
6165
|
+
otter: "stackwright-pro-form-wizard-otter",
|
|
5919
6166
|
artifactKey: "workflowConfig"
|
|
5920
6167
|
},
|
|
5921
6168
|
auth: {
|
|
@@ -5985,8 +6232,8 @@ function registerTypeSchemasTool(server2) {
|
|
|
5985
6232
|
}
|
|
5986
6233
|
|
|
5987
6234
|
// src/tools/scaffold-cleanup.ts
|
|
5988
|
-
var
|
|
5989
|
-
var
|
|
6235
|
+
var import_fs11 = require("fs");
|
|
6236
|
+
var import_path11 = require("path");
|
|
5990
6237
|
var SCAFFOLD_FILES_TO_DELETE = [
|
|
5991
6238
|
"content/posts/getting-started.yaml",
|
|
5992
6239
|
"content/posts/hello-world.yaml"
|
|
@@ -6011,12 +6258,12 @@ var FALLBACK_COLORS = {
|
|
|
6011
6258
|
mutedForeground: "#737373"
|
|
6012
6259
|
};
|
|
6013
6260
|
function readThemeColors(cwd) {
|
|
6014
|
-
const tokenPath = (0,
|
|
6015
|
-
if (!(0,
|
|
6016
|
-
const stat = (0,
|
|
6261
|
+
const tokenPath = (0, import_path11.join)(cwd, ".stackwright", "artifacts", "theme-tokens.json");
|
|
6262
|
+
if (!(0, import_fs11.existsSync)(tokenPath)) return { ...FALLBACK_COLORS };
|
|
6263
|
+
const stat = (0, import_fs11.lstatSync)(tokenPath);
|
|
6017
6264
|
if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
|
|
6018
6265
|
try {
|
|
6019
|
-
const raw = JSON.parse((0,
|
|
6266
|
+
const raw = JSON.parse((0, import_fs11.readFileSync)(tokenPath, "utf-8"));
|
|
6020
6267
|
const css = raw?.cssVariables ?? {};
|
|
6021
6268
|
const toHsl = (hslValue) => {
|
|
6022
6269
|
if (!hslValue || typeof hslValue !== "string") return null;
|
|
@@ -6076,15 +6323,15 @@ function generateNotFoundPage(colors) {
|
|
|
6076
6323
|
`;
|
|
6077
6324
|
}
|
|
6078
6325
|
function isSafePath(fullPath) {
|
|
6079
|
-
if (!(0,
|
|
6080
|
-
const stat = (0,
|
|
6326
|
+
if (!(0, import_fs11.existsSync)(fullPath)) return true;
|
|
6327
|
+
const stat = (0, import_fs11.lstatSync)(fullPath);
|
|
6081
6328
|
return !stat.isSymbolicLink();
|
|
6082
6329
|
}
|
|
6083
6330
|
function isDirEmpty(dirPath) {
|
|
6084
|
-
if (!(0,
|
|
6085
|
-
const stat = (0,
|
|
6331
|
+
if (!(0, import_fs11.existsSync)(dirPath)) return false;
|
|
6332
|
+
const stat = (0, import_fs11.lstatSync)(dirPath);
|
|
6086
6333
|
if (!stat.isDirectory()) return false;
|
|
6087
|
-
return (0,
|
|
6334
|
+
return (0, import_fs11.readdirSync)(dirPath).length === 0;
|
|
6088
6335
|
}
|
|
6089
6336
|
function handleCleanupScaffold(_cwd) {
|
|
6090
6337
|
const cwd = _cwd ?? process.cwd();
|
|
@@ -6097,8 +6344,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6097
6344
|
cleanupWarnings: []
|
|
6098
6345
|
};
|
|
6099
6346
|
for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
|
|
6100
|
-
const fullPath = (0,
|
|
6101
|
-
if (!(0,
|
|
6347
|
+
const fullPath = (0, import_path11.join)(cwd, relPath);
|
|
6348
|
+
if (!(0, import_fs11.existsSync)(fullPath)) {
|
|
6102
6349
|
result.skipped.push(relPath);
|
|
6103
6350
|
continue;
|
|
6104
6351
|
}
|
|
@@ -6107,7 +6354,7 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6107
6354
|
continue;
|
|
6108
6355
|
}
|
|
6109
6356
|
try {
|
|
6110
|
-
(0,
|
|
6357
|
+
(0, import_fs11.unlinkSync)(fullPath);
|
|
6111
6358
|
result.deleted.push(relPath);
|
|
6112
6359
|
} catch (err) {
|
|
6113
6360
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -6115,19 +6362,19 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6115
6362
|
}
|
|
6116
6363
|
{
|
|
6117
6364
|
const relPath = "pages/getting-started/content.yml";
|
|
6118
|
-
const fullPath = (0,
|
|
6119
|
-
if (!(0,
|
|
6365
|
+
const fullPath = (0, import_path11.join)(cwd, relPath);
|
|
6366
|
+
if (!(0, import_fs11.existsSync)(fullPath)) {
|
|
6120
6367
|
result.skipped.push(relPath);
|
|
6121
6368
|
} else if (!isSafePath(fullPath)) {
|
|
6122
6369
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
6123
6370
|
} else {
|
|
6124
|
-
const content = (0,
|
|
6371
|
+
const content = (0, import_fs11.readFileSync)(fullPath, "utf-8");
|
|
6125
6372
|
const isScaffoldDefault = GETTING_STARTED_SCAFFOLD_FINGERPRINTS.some(
|
|
6126
6373
|
(marker) => content.includes(marker)
|
|
6127
6374
|
);
|
|
6128
6375
|
if (isScaffoldDefault) {
|
|
6129
6376
|
try {
|
|
6130
|
-
(0,
|
|
6377
|
+
(0, import_fs11.unlinkSync)(fullPath);
|
|
6131
6378
|
result.deleted.push(relPath);
|
|
6132
6379
|
} catch (err) {
|
|
6133
6380
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -6141,21 +6388,21 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6141
6388
|
}
|
|
6142
6389
|
{
|
|
6143
6390
|
const relPath = `content/posts/${POSTS_COLLECTION_FILE}`;
|
|
6144
|
-
const fullPath = (0,
|
|
6145
|
-
const postsDir = (0,
|
|
6146
|
-
if (!(0,
|
|
6391
|
+
const fullPath = (0, import_path11.join)(cwd, relPath);
|
|
6392
|
+
const postsDir = (0, import_path11.join)(cwd, "content/posts");
|
|
6393
|
+
if (!(0, import_fs11.existsSync)(fullPath)) {
|
|
6147
6394
|
result.skipped.push(relPath);
|
|
6148
6395
|
} else if (!isSafePath(fullPath)) {
|
|
6149
6396
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
6150
|
-
} else if (!(0,
|
|
6397
|
+
} else if (!(0, import_fs11.existsSync)(postsDir)) {
|
|
6151
6398
|
result.skipped.push(relPath);
|
|
6152
6399
|
} else {
|
|
6153
|
-
const userPostFiles = (0,
|
|
6400
|
+
const userPostFiles = (0, import_fs11.readdirSync)(postsDir).filter(
|
|
6154
6401
|
(f) => f !== POSTS_COLLECTION_FILE && POST_FILE_EXTENSIONS.some((ext) => f.endsWith(ext))
|
|
6155
6402
|
);
|
|
6156
6403
|
if (userPostFiles.length === 0) {
|
|
6157
6404
|
try {
|
|
6158
|
-
(0,
|
|
6405
|
+
(0, import_fs11.unlinkSync)(fullPath);
|
|
6159
6406
|
result.deleted.push(relPath);
|
|
6160
6407
|
} catch (err) {
|
|
6161
6408
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -6168,15 +6415,15 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6168
6415
|
}
|
|
6169
6416
|
}
|
|
6170
6417
|
for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
|
|
6171
|
-
const fullDir = (0,
|
|
6172
|
-
if (!(0,
|
|
6418
|
+
const fullDir = (0, import_path11.join)(cwd, relDir);
|
|
6419
|
+
if (!(0, import_fs11.existsSync)(fullDir)) continue;
|
|
6173
6420
|
if (!isSafePath(fullDir)) {
|
|
6174
6421
|
result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
|
|
6175
6422
|
continue;
|
|
6176
6423
|
}
|
|
6177
6424
|
if (isDirEmpty(fullDir)) {
|
|
6178
6425
|
try {
|
|
6179
|
-
(0,
|
|
6426
|
+
(0, import_fs11.rmdirSync)(fullDir);
|
|
6180
6427
|
result.prunedDirs.push(relDir);
|
|
6181
6428
|
} catch (err) {
|
|
6182
6429
|
result.errors.push(`Failed to prune directory ${relDir}: ${String(err)}`);
|
|
@@ -6184,8 +6431,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6184
6431
|
}
|
|
6185
6432
|
}
|
|
6186
6433
|
{
|
|
6187
|
-
const fullPath = (0,
|
|
6188
|
-
if (!(0,
|
|
6434
|
+
const fullPath = (0, import_path11.join)(cwd, NOT_FOUND_PATH);
|
|
6435
|
+
if (!(0, import_fs11.existsSync)(fullPath)) {
|
|
6189
6436
|
result.skipped.push(NOT_FOUND_PATH);
|
|
6190
6437
|
} else if (!isSafePath(fullPath)) {
|
|
6191
6438
|
result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
|
|
@@ -6193,9 +6440,9 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6193
6440
|
try {
|
|
6194
6441
|
const colors = readThemeColors(cwd);
|
|
6195
6442
|
const content = generateNotFoundPage(colors);
|
|
6196
|
-
const dir = (0,
|
|
6197
|
-
(0,
|
|
6198
|
-
(0,
|
|
6443
|
+
const dir = (0, import_path11.dirname)(fullPath);
|
|
6444
|
+
(0, import_fs11.mkdirSync)(dir, { recursive: true });
|
|
6445
|
+
(0, import_fs11.writeFileSync)(fullPath, content, "utf-8");
|
|
6199
6446
|
result.rewritten.push(NOT_FOUND_PATH);
|
|
6200
6447
|
} catch (err) {
|
|
6201
6448
|
result.errors.push(`Failed to rewrite ${NOT_FOUND_PATH}: ${String(err)}`);
|
|
@@ -6462,11 +6709,256 @@ function registerContrastTools(server2) {
|
|
|
6462
6709
|
);
|
|
6463
6710
|
}
|
|
6464
6711
|
|
|
6712
|
+
// src/tools/compile.ts
|
|
6713
|
+
var import_zod19 = require("zod");
|
|
6714
|
+
var import_fs12 = __toESM(require("fs"));
|
|
6715
|
+
var import_path12 = __toESM(require("path"));
|
|
6716
|
+
var import_server = require("@stackwright-pro/pulse/server");
|
|
6717
|
+
var import_server2 = require("@stackwright-pro/auth-nextjs/server");
|
|
6718
|
+
var import_server3 = require("@stackwright-pro/openapi/server");
|
|
6719
|
+
function buildContext(projectRoot) {
|
|
6720
|
+
return {
|
|
6721
|
+
projectRoot,
|
|
6722
|
+
contentOutDir: import_path12.default.join(projectRoot, "public", "stackwright-content"),
|
|
6723
|
+
imagesDir: import_path12.default.join(projectRoot, "public", "images"),
|
|
6724
|
+
siteConfig: loadSiteConfig(projectRoot)
|
|
6725
|
+
};
|
|
6726
|
+
}
|
|
6727
|
+
function loadSiteConfig(projectRoot) {
|
|
6728
|
+
try {
|
|
6729
|
+
const sitePath = import_path12.default.join(projectRoot, "public", "stackwright-content", "_site.json");
|
|
6730
|
+
return JSON.parse(import_fs12.default.readFileSync(sitePath, "utf8"));
|
|
6731
|
+
} catch {
|
|
6732
|
+
return {};
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6735
|
+
function formatDuration(startMs) {
|
|
6736
|
+
const ms = Date.now() - startMs;
|
|
6737
|
+
return ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(2)}s`;
|
|
6738
|
+
}
|
|
6739
|
+
async function tryOssCompile(fn, ctx, extra) {
|
|
6740
|
+
try {
|
|
6741
|
+
const bsPath = import_path12.default.join(ctx.projectRoot, "node_modules", "@stackwright", "build-scripts");
|
|
6742
|
+
if (!import_fs12.default.existsSync(bsPath)) {
|
|
6743
|
+
return {
|
|
6744
|
+
ok: false,
|
|
6745
|
+
error: `@stackwright/build-scripts not found at ${bsPath}. Is it installed in your project?`
|
|
6746
|
+
};
|
|
6747
|
+
}
|
|
6748
|
+
const bs = await import(bsPath);
|
|
6749
|
+
if (typeof bs[fn] !== "function") {
|
|
6750
|
+
return { ok: false, error: `${fn} not exported from @stackwright/build-scripts` };
|
|
6751
|
+
}
|
|
6752
|
+
await bs[fn](ctx, ...extra ? [extra] : []);
|
|
6753
|
+
return { ok: true };
|
|
6754
|
+
} catch (err) {
|
|
6755
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
6756
|
+
}
|
|
6757
|
+
}
|
|
6758
|
+
function registerCompileTools(server2) {
|
|
6759
|
+
const projectRootArg = import_zod19.z.string().optional().describe("Absolute path to project root (defaults to cwd)");
|
|
6760
|
+
server2.tool(
|
|
6761
|
+
"stackwright_pro_compile_collections",
|
|
6762
|
+
"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.",
|
|
6763
|
+
{ projectRoot: projectRootArg },
|
|
6764
|
+
async ({ projectRoot }) => {
|
|
6765
|
+
const start = Date.now();
|
|
6766
|
+
const root = projectRoot ?? process.cwd();
|
|
6767
|
+
try {
|
|
6768
|
+
await (0, import_server.compileCollections)(buildContext(root));
|
|
6769
|
+
return {
|
|
6770
|
+
content: [
|
|
6771
|
+
{
|
|
6772
|
+
type: "text",
|
|
6773
|
+
text: ` _collections.json written (${formatDuration(start)})`
|
|
6774
|
+
}
|
|
6775
|
+
]
|
|
6776
|
+
};
|
|
6777
|
+
} catch (err) {
|
|
6778
|
+
return {
|
|
6779
|
+
content: [
|
|
6780
|
+
{
|
|
6781
|
+
type: "text",
|
|
6782
|
+
text: ` compile_collections failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6783
|
+
}
|
|
6784
|
+
]
|
|
6785
|
+
};
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6788
|
+
);
|
|
6789
|
+
server2.tool(
|
|
6790
|
+
"stackwright_pro_compile_auth",
|
|
6791
|
+
"Compile stackwright.auth.yml \u2192 public/stackwright-content/_auth.json. Reads and validates the auth sidecar config file and emits the JSON sink consumed at app startup to configure auth middleware.",
|
|
6792
|
+
{ projectRoot: projectRootArg },
|
|
6793
|
+
async ({ projectRoot }) => {
|
|
6794
|
+
const start = Date.now();
|
|
6795
|
+
const root = projectRoot ?? process.cwd();
|
|
6796
|
+
try {
|
|
6797
|
+
await (0, import_server2.compileAuth)(buildContext(root));
|
|
6798
|
+
return {
|
|
6799
|
+
content: [
|
|
6800
|
+
{
|
|
6801
|
+
type: "text",
|
|
6802
|
+
text: ` _auth.json written (${formatDuration(start)})`
|
|
6803
|
+
}
|
|
6804
|
+
]
|
|
6805
|
+
};
|
|
6806
|
+
} catch (err) {
|
|
6807
|
+
return {
|
|
6808
|
+
content: [
|
|
6809
|
+
{
|
|
6810
|
+
type: "text",
|
|
6811
|
+
text: ` compile_auth failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6812
|
+
}
|
|
6813
|
+
]
|
|
6814
|
+
};
|
|
6815
|
+
}
|
|
6816
|
+
}
|
|
6817
|
+
);
|
|
6818
|
+
server2.tool(
|
|
6819
|
+
"stackwright_pro_compile_integrations",
|
|
6820
|
+
"Compile stackwright.integrations.yml \u2192 public/stackwright-content/_integrations.json. If no yml file exists, falls back to extracting integration metadata from _site.json (collections data is excluded \u2014 it lives in _collections.json).",
|
|
6821
|
+
{ projectRoot: projectRootArg },
|
|
6822
|
+
async ({ projectRoot }) => {
|
|
6823
|
+
const start = Date.now();
|
|
6824
|
+
const root = projectRoot ?? process.cwd();
|
|
6825
|
+
try {
|
|
6826
|
+
await (0, import_server3.compileIntegrations)(buildContext(root));
|
|
6827
|
+
return {
|
|
6828
|
+
content: [
|
|
6829
|
+
{
|
|
6830
|
+
type: "text",
|
|
6831
|
+
text: ` _integrations.json written (${formatDuration(start)})`
|
|
6832
|
+
}
|
|
6833
|
+
]
|
|
6834
|
+
};
|
|
6835
|
+
} catch (err) {
|
|
6836
|
+
return {
|
|
6837
|
+
content: [
|
|
6838
|
+
{
|
|
6839
|
+
type: "text",
|
|
6840
|
+
text: ` compile_integrations failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6841
|
+
}
|
|
6842
|
+
]
|
|
6843
|
+
};
|
|
6844
|
+
}
|
|
6845
|
+
}
|
|
6846
|
+
);
|
|
6847
|
+
server2.tool(
|
|
6848
|
+
"stackwright_pro_compile_all",
|
|
6849
|
+
"Run all Pro compile sinks (collections + auth + integrations) in sequence. Equivalent to running compile_collections, compile_auth, and compile_integrations individually. Use this for a full Pro prebuild pass.",
|
|
6850
|
+
{ projectRoot: projectRootArg },
|
|
6851
|
+
async ({ projectRoot }) => {
|
|
6852
|
+
const start = Date.now();
|
|
6853
|
+
const root = projectRoot ?? process.cwd();
|
|
6854
|
+
const ctx = buildContext(root);
|
|
6855
|
+
const results = [];
|
|
6856
|
+
const errors = [];
|
|
6857
|
+
for (const [label, fn] of [
|
|
6858
|
+
["collections", () => (0, import_server.compileCollections)(ctx)],
|
|
6859
|
+
["auth", () => (0, import_server2.compileAuth)(ctx)],
|
|
6860
|
+
["integrations", () => (0, import_server3.compileIntegrations)(ctx)]
|
|
6861
|
+
]) {
|
|
6862
|
+
try {
|
|
6863
|
+
await fn();
|
|
6864
|
+
results.push(` _${label}.json`);
|
|
6865
|
+
} catch (err) {
|
|
6866
|
+
errors.push(` ${label}: ${err instanceof Error ? err.message : String(err)}`);
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
const summary = errors.length > 0 ? ` compile_all finished with errors (${formatDuration(start)}):
|
|
6870
|
+
${results.join("\n")}
|
|
6871
|
+
Errors:
|
|
6872
|
+
${errors.join("\n")}` : ` compile_all complete (${formatDuration(start)}):
|
|
6873
|
+
${results.join("\n")}`;
|
|
6874
|
+
return { content: [{ type: "text", text: summary }] };
|
|
6875
|
+
}
|
|
6876
|
+
);
|
|
6877
|
+
server2.tool(
|
|
6878
|
+
"stackwright_pro_compile_theme",
|
|
6879
|
+
"Compile stackwright.yml theme config \u2192 _theme.json. Delegates to compileTheme from @stackwright/build-scripts installed in the project.",
|
|
6880
|
+
{ projectRoot: projectRootArg },
|
|
6881
|
+
async ({ projectRoot }) => {
|
|
6882
|
+
const start = Date.now();
|
|
6883
|
+
const root = projectRoot ?? process.cwd();
|
|
6884
|
+
const { ok, error } = await tryOssCompile("compileTheme", buildContext(root));
|
|
6885
|
+
return {
|
|
6886
|
+
content: [
|
|
6887
|
+
{
|
|
6888
|
+
type: "text",
|
|
6889
|
+
text: ok ? ` _theme.json written (${formatDuration(start)})` : ` compile_theme failed: ${error}`
|
|
6890
|
+
}
|
|
6891
|
+
]
|
|
6892
|
+
};
|
|
6893
|
+
}
|
|
6894
|
+
);
|
|
6895
|
+
server2.tool(
|
|
6896
|
+
"stackwright_pro_compile_site",
|
|
6897
|
+
"Compile stackwright.yml \u2192 _site.json. Delegates to compileSite from @stackwright/build-scripts installed in the project.",
|
|
6898
|
+
{ projectRoot: projectRootArg },
|
|
6899
|
+
async ({ projectRoot }) => {
|
|
6900
|
+
const start = Date.now();
|
|
6901
|
+
const root = projectRoot ?? process.cwd();
|
|
6902
|
+
const { ok, error } = await tryOssCompile("compileSite", buildContext(root));
|
|
6903
|
+
return {
|
|
6904
|
+
content: [
|
|
6905
|
+
{
|
|
6906
|
+
type: "text",
|
|
6907
|
+
text: ok ? ` _site.json written (${formatDuration(start)})` : ` compile_site failed: ${error}`
|
|
6908
|
+
}
|
|
6909
|
+
]
|
|
6910
|
+
};
|
|
6911
|
+
}
|
|
6912
|
+
);
|
|
6913
|
+
server2.tool(
|
|
6914
|
+
"stackwright_pro_compile_pages",
|
|
6915
|
+
"Compile all pages content.yml files \u2192 page JSON files. Delegates to compilePages from @stackwright/build-scripts installed in the project.",
|
|
6916
|
+
{ projectRoot: projectRootArg },
|
|
6917
|
+
async ({ projectRoot }) => {
|
|
6918
|
+
const start = Date.now();
|
|
6919
|
+
const root = projectRoot ?? process.cwd();
|
|
6920
|
+
const { ok, error } = await tryOssCompile("compilePages", buildContext(root));
|
|
6921
|
+
return {
|
|
6922
|
+
content: [
|
|
6923
|
+
{
|
|
6924
|
+
type: "text",
|
|
6925
|
+
text: ok ? ` pages compiled (${formatDuration(start)})` : ` compile_pages failed: ${error}`
|
|
6926
|
+
}
|
|
6927
|
+
]
|
|
6928
|
+
};
|
|
6929
|
+
}
|
|
6930
|
+
);
|
|
6931
|
+
server2.tool(
|
|
6932
|
+
"stackwright_pro_compile_page",
|
|
6933
|
+
"Compile a single page by slug. Delegates to compilePage from @stackwright/build-scripts installed in the project.",
|
|
6934
|
+
{
|
|
6935
|
+
slug: import_zod19.z.string().describe('Page slug (relative path under pages/, e.g. "dashboard")'),
|
|
6936
|
+
projectRoot: projectRootArg
|
|
6937
|
+
},
|
|
6938
|
+
async ({ slug, projectRoot }) => {
|
|
6939
|
+
const start = Date.now();
|
|
6940
|
+
const root = projectRoot ?? process.cwd();
|
|
6941
|
+
const { ok, error } = await tryOssCompile("compilePage", buildContext(root), { slug });
|
|
6942
|
+
return {
|
|
6943
|
+
content: [
|
|
6944
|
+
{
|
|
6945
|
+
type: "text",
|
|
6946
|
+
text: ok ? ` page "${slug}" compiled (${formatDuration(start)})` : ` compile_page failed: ${error}`
|
|
6947
|
+
}
|
|
6948
|
+
]
|
|
6949
|
+
};
|
|
6950
|
+
}
|
|
6951
|
+
);
|
|
6952
|
+
}
|
|
6953
|
+
|
|
6465
6954
|
// package.json
|
|
6466
6955
|
var package_default = {
|
|
6467
6956
|
dependencies: {
|
|
6468
6957
|
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
6958
|
+
"@stackwright-pro/auth-nextjs": "workspace:*",
|
|
6469
6959
|
"@stackwright-pro/cli-data-explorer": "workspace:*",
|
|
6960
|
+
"@stackwright-pro/openapi": "workspace:*",
|
|
6961
|
+
"@stackwright-pro/pulse": "workspace:*",
|
|
6470
6962
|
"@stackwright-pro/types": "workspace:*",
|
|
6471
6963
|
"@types/js-yaml": "^4.0.9",
|
|
6472
6964
|
"js-yaml": "^4.2.0",
|
|
@@ -6487,7 +6979,7 @@ var package_default = {
|
|
|
6487
6979
|
"test:coverage": "vitest run --coverage"
|
|
6488
6980
|
},
|
|
6489
6981
|
name: "@stackwright-pro/mcp",
|
|
6490
|
-
version: "0.2.0-alpha.
|
|
6982
|
+
version: "0.2.0-alpha.95",
|
|
6491
6983
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
6492
6984
|
license: "SEE LICENSE IN LICENSE",
|
|
6493
6985
|
main: "./dist/server.js",
|
|
@@ -6545,7 +7037,22 @@ registerValidateYamlFragmentTool(server);
|
|
|
6545
7037
|
registerGetSchemaTool(server);
|
|
6546
7038
|
registerScaffoldCleanupTools(server);
|
|
6547
7039
|
registerContrastTools(server);
|
|
7040
|
+
registerCompileTools(server);
|
|
6548
7041
|
async function main() {
|
|
7042
|
+
const pipelineGraph = loadPipelineGraph();
|
|
7043
|
+
const phasePosition = new Map(PHASE_ORDER.map((p, i) => [p, i]));
|
|
7044
|
+
for (const [phase, deps] of Object.entries(pipelineGraph.dependencies)) {
|
|
7045
|
+
const phaseIdx = phasePosition.get(phase);
|
|
7046
|
+
if (phaseIdx === void 0) continue;
|
|
7047
|
+
for (const dep of deps) {
|
|
7048
|
+
const depIdx = phasePosition.get(dep);
|
|
7049
|
+
if (depIdx !== void 0 && depIdx >= phaseIdx) {
|
|
7050
|
+
throw new Error(
|
|
7051
|
+
`PHASE_ORDER sanity check failed: "${dep}" (position ${depIdx}) must precede "${phase}" (position ${phaseIdx}) but it doesn't. Update PHASE_ORDER in pipeline.ts to reflect the new dependency structure.`
|
|
7052
|
+
);
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
}
|
|
6549
7056
|
const transport = new import_stdio.StdioServerTransport();
|
|
6550
7057
|
try {
|
|
6551
7058
|
const servicesRegisterPkg = "@stackwright-services/mcp/register";
|