@stackwright-pro/mcp 0.2.0-alpha.107 → 0.2.0-alpha.109
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 +9 -9
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +9 -9
- package/dist/integrity.mjs.map +1 -1
- package/dist/pipeline-constants.js +5 -0
- package/dist/pipeline-constants.js.map +1 -1
- package/dist/pipeline-constants.mjs +5 -0
- package/dist/pipeline-constants.mjs.map +1 -1
- package/dist/server.js +644 -227
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +591 -174
- package/dist/server.mjs.map +1 -1
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -1334,6 +1334,10 @@ function answersToManifestFormat(answers, questions) {
|
|
|
1334
1334
|
// src/validation.ts
|
|
1335
1335
|
var SAFE_PHASE = /^[a-z][a-z0-9-]{0,30}$/;
|
|
1336
1336
|
var SAFE_QUESTION_ID = /^[a-z0-9][a-z0-9-]{0,63}$/i;
|
|
1337
|
+
var SAFE_SLUG = /^[a-z0-9][a-z0-9-]{0,79}$/;
|
|
1338
|
+
function isValidSlug(slug) {
|
|
1339
|
+
return SAFE_SLUG.test(slug);
|
|
1340
|
+
}
|
|
1337
1341
|
function isValidPhase(phase) {
|
|
1338
1342
|
return SAFE_PHASE.test(phase);
|
|
1339
1343
|
}
|
|
@@ -2023,9 +2027,9 @@ function registerOrchestrationTools(server2) {
|
|
|
2023
2027
|
|
|
2024
2028
|
// src/tools/pipeline.ts
|
|
2025
2029
|
var import_zod13 = require("zod");
|
|
2026
|
-
var
|
|
2030
|
+
var import_fs8 = require("fs");
|
|
2027
2031
|
var import_proper_lockfile = require("proper-lockfile");
|
|
2028
|
-
var
|
|
2032
|
+
var import_path8 = require("path");
|
|
2029
2033
|
var import_crypto3 = require("crypto");
|
|
2030
2034
|
|
|
2031
2035
|
// src/artifact-signing.ts
|
|
@@ -2552,6 +2556,234 @@ function formatPulseRetryPrompt(result) {
|
|
|
2552
2556
|
return lines.join("\n");
|
|
2553
2557
|
}
|
|
2554
2558
|
|
|
2559
|
+
// src/tools/validate-api-integration.ts
|
|
2560
|
+
var CANONICAL_API_AUTH_TYPES = ["apiKey", "bearer", "oauth2", "oidc", "none"];
|
|
2561
|
+
var CANONICAL_SET = new Set(CANONICAL_API_AUTH_TYPES);
|
|
2562
|
+
var VALID_SET_DISPLAY = CANONICAL_API_AUTH_TYPES.map((t) => `\`${t}\``).join(", ");
|
|
2563
|
+
var RENAME_MAP = {
|
|
2564
|
+
// apiKey variants
|
|
2565
|
+
"api-key": "apiKey",
|
|
2566
|
+
api_key: "apiKey",
|
|
2567
|
+
apikey: "apiKey",
|
|
2568
|
+
// lowercase of 'apiKey' / 'APIKEY'
|
|
2569
|
+
// oauth2 variants
|
|
2570
|
+
"oauth-2": "oauth2",
|
|
2571
|
+
oauth_2: "oauth2",
|
|
2572
|
+
// oidc variants
|
|
2573
|
+
"open-id-connect": "oidc",
|
|
2574
|
+
"openid-connect": "oidc",
|
|
2575
|
+
openidconnect: "oidc",
|
|
2576
|
+
// lowercase of 'openIdConnect'
|
|
2577
|
+
openidconnect_: "oidc",
|
|
2578
|
+
// belt-and-suspenders
|
|
2579
|
+
openIdConnect: "oidc",
|
|
2580
|
+
// exact camelCase as emitted by some tooling
|
|
2581
|
+
// bearer variants
|
|
2582
|
+
"bearer-token": "bearer",
|
|
2583
|
+
bearer_token: "bearer"
|
|
2584
|
+
};
|
|
2585
|
+
function renameSuggestion(badLiteral) {
|
|
2586
|
+
return RENAME_MAP[badLiteral] ?? RENAME_MAP[badLiteral.toLowerCase()];
|
|
2587
|
+
}
|
|
2588
|
+
function validateAuthType(authType, yamlPath, artifactPath) {
|
|
2589
|
+
if (authType === void 0 || authType === null) return null;
|
|
2590
|
+
if (typeof authType !== "string") {
|
|
2591
|
+
return [
|
|
2592
|
+
`${artifactPath}: \`${yamlPath}\` must be a string, got \`${typeof authType}\`.`,
|
|
2593
|
+
`Valid values: ${VALID_SET_DISPLAY}.`,
|
|
2594
|
+
"Fix this value and re-call stackwright_pro_validate_artifact."
|
|
2595
|
+
].join("\n");
|
|
2596
|
+
}
|
|
2597
|
+
if (CANONICAL_SET.has(authType)) return null;
|
|
2598
|
+
const suggestion = renameSuggestion(authType);
|
|
2599
|
+
const lines = [
|
|
2600
|
+
`${artifactPath}: \`${yamlPath}\` has non-canonical value \`"${authType}"\`.`,
|
|
2601
|
+
`Valid values: ${VALID_SET_DISPLAY}.`
|
|
2602
|
+
];
|
|
2603
|
+
if (suggestion) {
|
|
2604
|
+
lines.push(`Did you mean: \`${suggestion}\`?`);
|
|
2605
|
+
}
|
|
2606
|
+
lines.push("Fix this value and re-call stackwright_pro_validate_artifact.");
|
|
2607
|
+
return lines.join("\n");
|
|
2608
|
+
}
|
|
2609
|
+
function validateApiIntegration(artifact, integrationName) {
|
|
2610
|
+
const artifactFile = integrationName ? `api-config-${integrationName}.json` : "api-config.json";
|
|
2611
|
+
const artifactPath = `.stackwright/artifacts/${artifactFile}`;
|
|
2612
|
+
if (artifact === null || typeof artifact !== "object") {
|
|
2613
|
+
return { valid: true };
|
|
2614
|
+
}
|
|
2615
|
+
const obj = artifact;
|
|
2616
|
+
const errors = [];
|
|
2617
|
+
if ("auth" in obj && obj["auth"] !== null && typeof obj["auth"] === "object") {
|
|
2618
|
+
const auth = obj["auth"];
|
|
2619
|
+
const err = validateAuthType(auth["type"], "auth.type", artifactPath);
|
|
2620
|
+
if (err) errors.push(err);
|
|
2621
|
+
}
|
|
2622
|
+
if ("securitySchemes" in obj && obj["securitySchemes"] !== null && typeof obj["securitySchemes"] === "object" && !Array.isArray(obj["securitySchemes"])) {
|
|
2623
|
+
const schemes = obj["securitySchemes"];
|
|
2624
|
+
for (const [schemeName, scheme] of Object.entries(schemes)) {
|
|
2625
|
+
if (scheme !== null && typeof scheme === "object") {
|
|
2626
|
+
const s = scheme;
|
|
2627
|
+
const err = validateAuthType(s["type"], `securitySchemes.${schemeName}.type`, artifactPath);
|
|
2628
|
+
if (err) errors.push(err);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
if (errors.length === 0) return { valid: true };
|
|
2633
|
+
const retryPrompt = [
|
|
2634
|
+
`API integration auth validation failed: ${errors.length} violation(s) in ${artifactPath}.`,
|
|
2635
|
+
"",
|
|
2636
|
+
"You MUST fix ALL violations and re-call stackwright_pro_validate_artifact.",
|
|
2637
|
+
"",
|
|
2638
|
+
...errors.flatMap((e) => [e, ""]),
|
|
2639
|
+
`Canonical auth.type values: ${VALID_SET_DISPLAY}.`,
|
|
2640
|
+
"Do NOT use hyphenated or underscored variants (api-key, oauth-2, bearer-token, etc.)."
|
|
2641
|
+
].join("\n");
|
|
2642
|
+
return { valid: false, retryPrompt };
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
// src/tools/validate-auth-config.ts
|
|
2646
|
+
var KNOWN_NESTED_STRATEGIES = ["oidc", "pki"];
|
|
2647
|
+
var NESTED_STRATEGY_FIELD_EXAMPLES = {
|
|
2648
|
+
oidc: ["discoveryUrl", "clientId", "clientSecret"],
|
|
2649
|
+
pki: ["profile", "source", "headerPrefix"]
|
|
2650
|
+
};
|
|
2651
|
+
function nestedBlockError(strategy, nestedObj, artifactPath) {
|
|
2652
|
+
const exampleFields = NESTED_STRATEGY_FIELD_EXAMPLES[strategy];
|
|
2653
|
+
const actualKeys = Object.keys(nestedObj).slice(0, 2);
|
|
2654
|
+
const showField0 = actualKeys[0] ?? exampleFields[0];
|
|
2655
|
+
const showField1 = actualKeys[1] ?? exampleFields[1];
|
|
2656
|
+
return [
|
|
2657
|
+
`${artifactPath}: \`authConfig.${strategy}\` must not be a nested provider block.`,
|
|
2658
|
+
`Hoist \`authConfig.${strategy}.*\` fields to \`authConfig.*\`. The canonical AuthConfig`,
|
|
2659
|
+
`schema does not nest provider-specific fields under a named sub-object.`,
|
|
2660
|
+
``,
|
|
2661
|
+
`Bad (nested block):`,
|
|
2662
|
+
` "authConfig": {`,
|
|
2663
|
+
` "type": "${strategy}",`,
|
|
2664
|
+
` "${strategy}": {`,
|
|
2665
|
+
` "${showField0}": "...",`,
|
|
2666
|
+
` "${showField1}": "...",`,
|
|
2667
|
+
` ...`,
|
|
2668
|
+
` }`,
|
|
2669
|
+
` }`,
|
|
2670
|
+
``,
|
|
2671
|
+
`Correct (hoisted to root of authConfig):`,
|
|
2672
|
+
` "authConfig": {`,
|
|
2673
|
+
` "type": "${strategy}",`,
|
|
2674
|
+
` "${exampleFields[0]}": "...",`,
|
|
2675
|
+
` "${exampleFields[1]}": "...",`,
|
|
2676
|
+
` "${exampleFields[2]}": "...",`,
|
|
2677
|
+
` ...`,
|
|
2678
|
+
` }`,
|
|
2679
|
+
``,
|
|
2680
|
+
`Move every key from \`authConfig.${strategy}\` up to \`authConfig\` and remove the \`"${strategy}"\` sub-object.`
|
|
2681
|
+
].join("\n");
|
|
2682
|
+
}
|
|
2683
|
+
function discriminatorKeyError(methodVal, artifactPath) {
|
|
2684
|
+
return [
|
|
2685
|
+
`${artifactPath}: \`authConfig.method\` is not a valid key in the canonical AuthConfig schema.`,
|
|
2686
|
+
`Rename \`authConfig.method\` \u2192 \`authConfig.type\`. The canonical AuthConfig schema uses`,
|
|
2687
|
+
`\`type\` as the auth-strategy discriminator, matching the api-config \`auth.type\` convention`,
|
|
2688
|
+
`shipped in swp-fimq.`,
|
|
2689
|
+
``,
|
|
2690
|
+
`Bad:`,
|
|
2691
|
+
` "authConfig": { "method": "${methodVal}", ... }`,
|
|
2692
|
+
``,
|
|
2693
|
+
`Correct:`,
|
|
2694
|
+
` "authConfig": { "type": "${methodVal}", ... }`
|
|
2695
|
+
].join("\n");
|
|
2696
|
+
}
|
|
2697
|
+
function ambiguousDiscriminatorError(methodVal, typeVal, artifactPath) {
|
|
2698
|
+
return [
|
|
2699
|
+
`${artifactPath}: \`authConfig\` has both \`method: "${methodVal}"\` and \`type: "${typeVal}"\` \u2014 ambiguous discriminator.`,
|
|
2700
|
+
`Remove \`authConfig.method\` and keep only \`authConfig.type\`. The canonical AuthConfig`,
|
|
2701
|
+
`schema uses \`type\` as the sole auth-strategy discriminator.`
|
|
2702
|
+
].join("\n");
|
|
2703
|
+
}
|
|
2704
|
+
function validateAuthConfig(artifact, artifactPath) {
|
|
2705
|
+
if (artifact === null || typeof artifact !== "object") {
|
|
2706
|
+
return { valid: true };
|
|
2707
|
+
}
|
|
2708
|
+
const obj = artifact;
|
|
2709
|
+
if (!("authConfig" in obj) || obj["authConfig"] === void 0 || obj["authConfig"] === null) {
|
|
2710
|
+
return { valid: true };
|
|
2711
|
+
}
|
|
2712
|
+
if (typeof obj["authConfig"] !== "object" || Array.isArray(obj["authConfig"])) {
|
|
2713
|
+
return { valid: true };
|
|
2714
|
+
}
|
|
2715
|
+
const authConfig = obj["authConfig"];
|
|
2716
|
+
const errors = [];
|
|
2717
|
+
const hasMethod = "method" in authConfig && authConfig["method"] !== void 0;
|
|
2718
|
+
const hasType = "type" in authConfig && authConfig["type"] !== void 0;
|
|
2719
|
+
if (hasMethod && hasType) {
|
|
2720
|
+
errors.push(
|
|
2721
|
+
ambiguousDiscriminatorError(
|
|
2722
|
+
String(authConfig["method"]),
|
|
2723
|
+
String(authConfig["type"]),
|
|
2724
|
+
artifactPath
|
|
2725
|
+
)
|
|
2726
|
+
);
|
|
2727
|
+
} else if (hasMethod && !hasType) {
|
|
2728
|
+
errors.push(discriminatorKeyError(String(authConfig["method"]), artifactPath));
|
|
2729
|
+
}
|
|
2730
|
+
for (const strategy of KNOWN_NESTED_STRATEGIES) {
|
|
2731
|
+
const nested = authConfig[strategy];
|
|
2732
|
+
if (nested !== null && nested !== void 0 && typeof nested === "object" && !Array.isArray(nested)) {
|
|
2733
|
+
errors.push(nestedBlockError(strategy, nested, artifactPath));
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
if (errors.length === 0) return { valid: true };
|
|
2737
|
+
const retryPrompt = [
|
|
2738
|
+
`Auth config validation failed: ${errors.length} violation(s) in ${artifactPath}.`,
|
|
2739
|
+
``,
|
|
2740
|
+
`You MUST fix ALL violations and re-call stackwright_pro_validate_artifact.`,
|
|
2741
|
+
``,
|
|
2742
|
+
...errors.flatMap((e) => [e, ""]),
|
|
2743
|
+
`The canonical authConfig shape uses \`type\` (not \`method\`) as the discriminator,`,
|
|
2744
|
+
`with all provider-specific fields hoisted to the root of \`authConfig\`.`,
|
|
2745
|
+
`AuthConfig is a discriminated union on \`type: 'oidc' | 'pki'\`.`
|
|
2746
|
+
].join("\n");
|
|
2747
|
+
return { valid: false, retryPrompt };
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
// src/tools/auth-manifest-aggregator.ts
|
|
2751
|
+
var import_fs6 = require("fs");
|
|
2752
|
+
var import_path6 = require("path");
|
|
2753
|
+
function extractPatternBaseSlug(pattern) {
|
|
2754
|
+
const stripped = pattern.replace(/^\//, "");
|
|
2755
|
+
if (!stripped) return null;
|
|
2756
|
+
return stripped.split("/")[0] ?? null;
|
|
2757
|
+
}
|
|
2758
|
+
function extractManifestSlugs(artifactsDir) {
|
|
2759
|
+
const slugs = /* @__PURE__ */ new Set();
|
|
2760
|
+
if (!(0, import_fs6.existsSync)(artifactsDir)) return slugs;
|
|
2761
|
+
let entries;
|
|
2762
|
+
try {
|
|
2763
|
+
entries = (0, import_fs6.readdirSync)(artifactsDir);
|
|
2764
|
+
} catch {
|
|
2765
|
+
return slugs;
|
|
2766
|
+
}
|
|
2767
|
+
for (const entry of entries) {
|
|
2768
|
+
if (!entry.endsWith("-manifest.json")) continue;
|
|
2769
|
+
try {
|
|
2770
|
+
const content = JSON.parse((0, import_fs6.readFileSync)((0, import_path6.join)(artifactsDir, entry), "utf-8"));
|
|
2771
|
+
const pages = content.pages;
|
|
2772
|
+
if (!Array.isArray(pages)) continue;
|
|
2773
|
+
for (const page of pages) {
|
|
2774
|
+
if (typeof page.slug !== "string" || !page.slug) continue;
|
|
2775
|
+
const firstSeg = page.slug.split("/")[0];
|
|
2776
|
+
if (firstSeg) slugs.add(firstSeg);
|
|
2777
|
+
if (!page.slug.includes("[") && !page.slug.includes(":")) {
|
|
2778
|
+
slugs.add(page.slug);
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
} catch {
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
return slugs;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2555
2787
|
// src/tools/pipeline.ts
|
|
2556
2788
|
var import_types3 = require("@stackwright-pro/types");
|
|
2557
2789
|
|
|
@@ -2890,8 +3122,8 @@ function registerGetSchemaTool(server2) {
|
|
|
2890
3122
|
}
|
|
2891
3123
|
|
|
2892
3124
|
// src/tools/pipeline-graph.ts
|
|
2893
|
-
var
|
|
2894
|
-
var
|
|
3125
|
+
var import_fs7 = require("fs");
|
|
3126
|
+
var import_path7 = require("path");
|
|
2895
3127
|
var MANIFEST_NAME_TO_PHASE = {
|
|
2896
3128
|
"stackwright-pro-designer-otter": "designer",
|
|
2897
3129
|
"stackwright-pro-theme-otter": "theme",
|
|
@@ -2921,9 +3153,9 @@ var OTTER_SEARCH_PATHS = [
|
|
|
2921
3153
|
function resolveOtterDirFromCwd() {
|
|
2922
3154
|
const cwd = process.cwd();
|
|
2923
3155
|
for (const relative2 of OTTER_SEARCH_PATHS) {
|
|
2924
|
-
const candidate = (0,
|
|
3156
|
+
const candidate = (0, import_path7.join)(cwd, relative2);
|
|
2925
3157
|
try {
|
|
2926
|
-
(0,
|
|
3158
|
+
(0, import_fs7.lstatSync)(candidate);
|
|
2927
3159
|
return candidate;
|
|
2928
3160
|
} catch {
|
|
2929
3161
|
}
|
|
@@ -2933,14 +3165,14 @@ function resolveOtterDirFromCwd() {
|
|
|
2933
3165
|
);
|
|
2934
3166
|
}
|
|
2935
3167
|
function loadOtterManifestsFromDir(otterDir) {
|
|
2936
|
-
const entries = (0,
|
|
3168
|
+
const entries = (0, import_fs7.readdirSync)(otterDir);
|
|
2937
3169
|
const manifests = [];
|
|
2938
3170
|
for (const filename of entries) {
|
|
2939
3171
|
if (!filename.endsWith("-otter.json")) continue;
|
|
2940
|
-
const filePath = (0,
|
|
2941
|
-
if ((0,
|
|
3172
|
+
const filePath = (0, import_path7.join)(otterDir, filename);
|
|
3173
|
+
if ((0, import_fs7.lstatSync)(filePath).isSymbolicLink()) continue;
|
|
2942
3174
|
try {
|
|
2943
|
-
const raw = (0,
|
|
3175
|
+
const raw = (0, import_fs7.readFileSync)(filePath, "utf-8");
|
|
2944
3176
|
const manifest = JSON.parse(raw);
|
|
2945
3177
|
manifests.push(manifest);
|
|
2946
3178
|
} catch (err) {
|
|
@@ -3045,19 +3277,19 @@ var DISTRIBUTED_NAMESPACES = ["@stackwright-pro", "@stackwright"];
|
|
|
3045
3277
|
function loadDistributedOtterManifests(cwd) {
|
|
3046
3278
|
const results = [];
|
|
3047
3279
|
for (const namespace of DISTRIBUTED_NAMESPACES) {
|
|
3048
|
-
const nsDir = (0,
|
|
3280
|
+
const nsDir = (0, import_path7.join)(cwd, "node_modules", namespace);
|
|
3049
3281
|
let pkgDirNames;
|
|
3050
3282
|
try {
|
|
3051
|
-
pkgDirNames = (0,
|
|
3283
|
+
pkgDirNames = (0, import_fs7.readdirSync)(nsDir);
|
|
3052
3284
|
} catch {
|
|
3053
3285
|
continue;
|
|
3054
3286
|
}
|
|
3055
3287
|
for (const pkgDirName of pkgDirNames) {
|
|
3056
|
-
const pkgDir = (0,
|
|
3288
|
+
const pkgDir = (0, import_path7.join)(nsDir, pkgDirName);
|
|
3057
3289
|
const packageName = `${namespace}/${pkgDirName}`;
|
|
3058
3290
|
let otterRelPaths;
|
|
3059
3291
|
try {
|
|
3060
|
-
const raw = (0,
|
|
3292
|
+
const raw = (0, import_fs7.readFileSync)((0, import_path7.join)(pkgDir, "package.json"), "utf-8");
|
|
3061
3293
|
const pkgJson = JSON.parse(raw);
|
|
3062
3294
|
const declared = pkgJson.stackwright?.otters;
|
|
3063
3295
|
if (!Array.isArray(declared) || declared.length === 0) continue;
|
|
@@ -3066,9 +3298,9 @@ function loadDistributedOtterManifests(cwd) {
|
|
|
3066
3298
|
continue;
|
|
3067
3299
|
}
|
|
3068
3300
|
for (const relPath of otterRelPaths) {
|
|
3069
|
-
const otterFilePath = (0,
|
|
3301
|
+
const otterFilePath = (0, import_path7.join)(pkgDir, relPath);
|
|
3070
3302
|
try {
|
|
3071
|
-
const raw = (0,
|
|
3303
|
+
const raw = (0, import_fs7.readFileSync)(otterFilePath, "utf-8");
|
|
3072
3304
|
const manifest = JSON.parse(raw);
|
|
3073
3305
|
results.push({ manifest, packageName });
|
|
3074
3306
|
} catch (err) {
|
|
@@ -3186,11 +3418,11 @@ function createDefaultState() {
|
|
|
3186
3418
|
};
|
|
3187
3419
|
}
|
|
3188
3420
|
function statePath(cwd) {
|
|
3189
|
-
return (0,
|
|
3421
|
+
return (0, import_path8.join)(cwd, ".stackwright", "pipeline-state.json");
|
|
3190
3422
|
}
|
|
3191
3423
|
function readState(cwd) {
|
|
3192
3424
|
const p = statePath(cwd);
|
|
3193
|
-
if (!(0,
|
|
3425
|
+
if (!(0, import_fs8.existsSync)(p)) return createDefaultState();
|
|
3194
3426
|
const raw = JSON.parse(safeReadSync(p));
|
|
3195
3427
|
if (typeof raw !== "object" || raw === null || raw.version !== "1.0") {
|
|
3196
3428
|
return createDefaultState();
|
|
@@ -3198,34 +3430,34 @@ function readState(cwd) {
|
|
|
3198
3430
|
return raw;
|
|
3199
3431
|
}
|
|
3200
3432
|
function safeWriteSync(filePath, content) {
|
|
3201
|
-
if ((0,
|
|
3202
|
-
const stat = (0,
|
|
3433
|
+
if ((0, import_fs8.existsSync)(filePath)) {
|
|
3434
|
+
const stat = (0, import_fs8.lstatSync)(filePath);
|
|
3203
3435
|
if (stat.isSymbolicLink()) {
|
|
3204
3436
|
throw new Error(`Refusing to write to symlink: ${filePath}`);
|
|
3205
3437
|
}
|
|
3206
3438
|
}
|
|
3207
|
-
(0,
|
|
3439
|
+
(0, import_fs8.writeFileSync)(filePath, content);
|
|
3208
3440
|
}
|
|
3209
3441
|
function safeReadSync(filePath) {
|
|
3210
|
-
if ((0,
|
|
3211
|
-
const stat = (0,
|
|
3442
|
+
if ((0, import_fs8.existsSync)(filePath)) {
|
|
3443
|
+
const stat = (0, import_fs8.lstatSync)(filePath);
|
|
3212
3444
|
if (stat.isSymbolicLink()) {
|
|
3213
3445
|
throw new Error(`Refusing to read symlink: ${filePath}`);
|
|
3214
3446
|
}
|
|
3215
3447
|
}
|
|
3216
|
-
return (0,
|
|
3448
|
+
return (0, import_fs8.readFileSync)(filePath, "utf-8");
|
|
3217
3449
|
}
|
|
3218
3450
|
function writeState(cwd, state) {
|
|
3219
|
-
const dir = (0,
|
|
3220
|
-
(0,
|
|
3451
|
+
const dir = (0, import_path8.join)(cwd, ".stackwright");
|
|
3452
|
+
(0, import_fs8.mkdirSync)(dir, { recursive: true });
|
|
3221
3453
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3222
3454
|
safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
|
|
3223
3455
|
}
|
|
3224
3456
|
function updateStateAtomic(cwd, mutator) {
|
|
3225
|
-
const dir = (0,
|
|
3226
|
-
(0,
|
|
3457
|
+
const dir = (0, import_path8.join)(cwd, ".stackwright");
|
|
3458
|
+
(0, import_fs8.mkdirSync)(dir, { recursive: true });
|
|
3227
3459
|
const path5 = statePath(cwd);
|
|
3228
|
-
if (!(0,
|
|
3460
|
+
if (!(0, import_fs8.existsSync)(path5)) {
|
|
3229
3461
|
safeWriteSync(path5, JSON.stringify(createDefaultState(), null, 2) + "\n");
|
|
3230
3462
|
}
|
|
3231
3463
|
const release = (0, import_proper_lockfile.lockSync)(path5, {
|
|
@@ -3259,8 +3491,8 @@ function handleGetPipelineState(_cwd) {
|
|
|
3259
3491
|
const cwd = _cwd ?? process.cwd();
|
|
3260
3492
|
try {
|
|
3261
3493
|
const state = readState(cwd);
|
|
3262
|
-
const keyPath = (0,
|
|
3263
|
-
if (!(0,
|
|
3494
|
+
const keyPath = (0, import_path8.join)(cwd, ".stackwright", "pipeline-keys.json");
|
|
3495
|
+
if (!(0, import_fs8.existsSync)(keyPath)) {
|
|
3264
3496
|
try {
|
|
3265
3497
|
const { fingerprint } = initPipelineKeys(cwd);
|
|
3266
3498
|
state["signingKeyFingerprint"] = fingerprint;
|
|
@@ -3391,8 +3623,8 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3391
3623
|
isError: true
|
|
3392
3624
|
};
|
|
3393
3625
|
}
|
|
3394
|
-
const answerFile = (0,
|
|
3395
|
-
if (!(0,
|
|
3626
|
+
const answerFile = (0, import_path8.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3627
|
+
if (!(0, import_fs8.existsSync)(answerFile)) {
|
|
3396
3628
|
return {
|
|
3397
3629
|
text: JSON.stringify({ ready: false, phase, reason: "Answer file not found" }),
|
|
3398
3630
|
isError: false
|
|
@@ -3416,12 +3648,12 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3416
3648
|
}
|
|
3417
3649
|
}
|
|
3418
3650
|
try {
|
|
3419
|
-
const answersDir = (0,
|
|
3651
|
+
const answersDir = (0, import_path8.join)(cwd, ".stackwright", "answers");
|
|
3420
3652
|
const answeredPhases = [];
|
|
3421
3653
|
const missingPhases = [];
|
|
3422
3654
|
for (const phase2 of PHASE_ORDER) {
|
|
3423
|
-
const answerFile = (0,
|
|
3424
|
-
if ((0,
|
|
3655
|
+
const answerFile = (0, import_path8.join)(answersDir, `${phase2}.json`);
|
|
3656
|
+
if ((0, import_fs8.existsSync)(answerFile)) {
|
|
3425
3657
|
try {
|
|
3426
3658
|
const raw = safeReadSync(answerFile);
|
|
3427
3659
|
const parsed = JSON.parse(raw);
|
|
@@ -3490,7 +3722,7 @@ function handleGetReadyPhases(_cwd) {
|
|
|
3490
3722
|
function handleListArtifacts(_cwd) {
|
|
3491
3723
|
const cwd = _cwd ?? process.cwd();
|
|
3492
3724
|
try {
|
|
3493
|
-
const artifactsDir = (0,
|
|
3725
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
3494
3726
|
let manifest = null;
|
|
3495
3727
|
try {
|
|
3496
3728
|
manifest = loadSignatureManifest(cwd);
|
|
@@ -3500,8 +3732,8 @@ function handleListArtifacts(_cwd) {
|
|
|
3500
3732
|
let completedCount = 0;
|
|
3501
3733
|
for (const phase of PHASE_ORDER) {
|
|
3502
3734
|
const expectedFile = PHASE_ARTIFACT[phase];
|
|
3503
|
-
const fullPath = (0,
|
|
3504
|
-
const exists = (0,
|
|
3735
|
+
const fullPath = (0, import_path8.join)(artifactsDir, expectedFile);
|
|
3736
|
+
const exists = (0, import_fs8.existsSync)(fullPath);
|
|
3505
3737
|
if (exists) completedCount++;
|
|
3506
3738
|
let signed = false;
|
|
3507
3739
|
let signatureValid = null;
|
|
@@ -3554,9 +3786,9 @@ function handleWritePhaseQuestions(input) {
|
|
|
3554
3786
|
}
|
|
3555
3787
|
} catch {
|
|
3556
3788
|
}
|
|
3557
|
-
const questionsDir = (0,
|
|
3558
|
-
(0,
|
|
3559
|
-
const filePath = (0,
|
|
3789
|
+
const questionsDir = (0, import_path8.join)(cwd, ".stackwright", "questions");
|
|
3790
|
+
(0, import_fs8.mkdirSync)(questionsDir, { recursive: true });
|
|
3791
|
+
const filePath = (0, import_path8.join)(questionsDir, `${phase}.json`);
|
|
3560
3792
|
const payload = {
|
|
3561
3793
|
version: "1.0",
|
|
3562
3794
|
phase,
|
|
@@ -3596,14 +3828,14 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
3596
3828
|
};
|
|
3597
3829
|
}
|
|
3598
3830
|
try {
|
|
3599
|
-
const answersPath = (0,
|
|
3831
|
+
const answersPath = (0, import_path8.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3600
3832
|
let answers = {};
|
|
3601
|
-
if ((0,
|
|
3833
|
+
if ((0, import_fs8.existsSync)(answersPath)) {
|
|
3602
3834
|
answers = JSON.parse(safeReadSync(answersPath));
|
|
3603
3835
|
}
|
|
3604
3836
|
let buildContextText = "";
|
|
3605
|
-
const buildContextPath = (0,
|
|
3606
|
-
if ((0,
|
|
3837
|
+
const buildContextPath = (0, import_path8.join)(cwd, ".stackwright", "build-context.json");
|
|
3838
|
+
if ((0, import_fs8.existsSync)(buildContextPath)) {
|
|
3607
3839
|
try {
|
|
3608
3840
|
const bcRaw = JSON.parse(safeReadSync(buildContextPath));
|
|
3609
3841
|
if (typeof bcRaw.buildContext === "string" && bcRaw.buildContext.trim().length > 0) {
|
|
@@ -3618,8 +3850,8 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
3618
3850
|
const missingDependencies = [];
|
|
3619
3851
|
for (const dep of deps) {
|
|
3620
3852
|
const artifactFile = PHASE_ARTIFACT[dep];
|
|
3621
|
-
const artifactPath = (0,
|
|
3622
|
-
if ((0,
|
|
3853
|
+
const artifactPath = (0, import_path8.join)(cwd, ".stackwright", "artifacts", artifactFile);
|
|
3854
|
+
if ((0, import_fs8.existsSync)(artifactPath)) {
|
|
3623
3855
|
const rawContent = safeReadSync(artifactPath);
|
|
3624
3856
|
const rawBytes = Buffer.from(rawContent, "utf-8");
|
|
3625
3857
|
const content = JSON.parse(rawContent);
|
|
@@ -3684,8 +3916,8 @@ ${JSON.stringify(content, null, 2)}`
|
|
|
3684
3916
|
parts.push("BUILD_CONTEXT:", buildContextText, "");
|
|
3685
3917
|
}
|
|
3686
3918
|
if (phase === "auth") {
|
|
3687
|
-
const initContextPath = (0,
|
|
3688
|
-
if ((0,
|
|
3919
|
+
const initContextPath = (0, import_path8.join)(cwd, ".stackwright", "init-context.json");
|
|
3920
|
+
if ((0, import_fs8.existsSync)(initContextPath)) {
|
|
3689
3921
|
try {
|
|
3690
3922
|
const initContext = JSON.parse(safeReadSync(initContextPath));
|
|
3691
3923
|
if (initContext.devOnly === true || initContext.nonInteractive === true) {
|
|
@@ -4136,7 +4368,87 @@ function _validateArtifactInner(input) {
|
|
|
4136
4368
|
};
|
|
4137
4369
|
return { text: JSON.stringify(result), isError: false };
|
|
4138
4370
|
}
|
|
4371
|
+
if (phase === "auth") {
|
|
4372
|
+
const authArtifactPath = `.stackwright/artifacts/${PHASE_ARTIFACT["auth"]}`;
|
|
4373
|
+
const authShapeResult = validateAuthConfig(artifact, authArtifactPath);
|
|
4374
|
+
if (!authShapeResult.valid) {
|
|
4375
|
+
const result = {
|
|
4376
|
+
valid: false,
|
|
4377
|
+
phase,
|
|
4378
|
+
violation: "schema-mismatch",
|
|
4379
|
+
retryPrompt: authShapeResult.retryPrompt
|
|
4380
|
+
};
|
|
4381
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
if (phase === "auth") {
|
|
4385
|
+
const rawUncovered = artifact["uncoveredSlugs"];
|
|
4386
|
+
if (Array.isArray(rawUncovered) && rawUncovered.length > 0) {
|
|
4387
|
+
const otterFromSource = (source) => {
|
|
4388
|
+
if (source === "geo-manifest.json") return "geo-otter";
|
|
4389
|
+
if (source === "dashboard-manifest.json") return "dashboard-otter";
|
|
4390
|
+
if (source === "pages-manifest.json") return "page-otter";
|
|
4391
|
+
return source || "(unknown)";
|
|
4392
|
+
};
|
|
4393
|
+
const slugLines = rawUncovered.map((entry) => {
|
|
4394
|
+
const e = entry ?? {};
|
|
4395
|
+
const source = typeof e["source"] === "string" ? e["source"] : "(unknown)";
|
|
4396
|
+
const slug = typeof e["slug"] === "string" ? e["slug"] : "(unknown)";
|
|
4397
|
+
return ` ${source} \u2192 ${slug} (emitted by ${otterFromSource(source)})`;
|
|
4398
|
+
});
|
|
4399
|
+
const retryPrompt = [
|
|
4400
|
+
"Auth-config cannot be finalized: the following page slugs have no auth declaration in their emitting otter's manifest:",
|
|
4401
|
+
"",
|
|
4402
|
+
...slugLines,
|
|
4403
|
+
"",
|
|
4404
|
+
"This is a HARD build-gate failure \u2014 auth-otter cannot self-correct because the root cause is upstream.",
|
|
4405
|
+
"",
|
|
4406
|
+
"Fix: update the offending emitter otter to declare `authRequired: true` + `requiredRoles: [...]`",
|
|
4407
|
+
"(and optionally `tier`, `authRationale`) in the manifest entry for each listed slug.",
|
|
4408
|
+
"",
|
|
4409
|
+
"For map pages: geo-otter (see swp-1114).",
|
|
4410
|
+
"For dashboard pages: dashboard-otter (see swp-52lk).",
|
|
4411
|
+
"For page pages: page-otter (already does this \u2014 check for regressions).",
|
|
4412
|
+
"For workflow pages: form-wizard-otter emits routes but auth is derived from the pages-manifest",
|
|
4413
|
+
"workflow-wrapper entry \u2014 check page-otter didn't skip a workflow-wrapper.",
|
|
4414
|
+
"",
|
|
4415
|
+
"After updating the offending manifest, re-run the pipeline from the affected phase."
|
|
4416
|
+
].join("\n");
|
|
4417
|
+
const result = {
|
|
4418
|
+
valid: false,
|
|
4419
|
+
phase,
|
|
4420
|
+
violation: "uncovered-slugs",
|
|
4421
|
+
retryPrompt
|
|
4422
|
+
};
|
|
4423
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4139
4426
|
const PHASE_ZOD_VALIDATORS = {
|
|
4427
|
+
// 5.0 Theme defaultColorMode hygiene (swp-0ryn / swp-mw97)
|
|
4428
|
+
//
|
|
4429
|
+
// DHL raft runs produced `defaultColorMode: "auto"` (not a valid value) and
|
|
4430
|
+
// `defaultColorMode: "dark"` when the designer said "respect OS preference".
|
|
4431
|
+
// This validator catches the `auto` literal at write-time so the otter
|
|
4432
|
+
// self-corrects before the artifact lands on disk.
|
|
4433
|
+
//
|
|
4434
|
+
// Valid values: 'light' | 'dark' | 'system'
|
|
4435
|
+
// Omitting the key entirely is also valid (app shell honors prefers-color-scheme).
|
|
4436
|
+
theme: (artifact2) => {
|
|
4437
|
+
const dcm = artifact2["defaultColorMode"];
|
|
4438
|
+
if (dcm === void 0 || dcm === null) {
|
|
4439
|
+
return { success: true };
|
|
4440
|
+
}
|
|
4441
|
+
const VALID_COLOR_MODES = ["light", "dark", "system"];
|
|
4442
|
+
if (!VALID_COLOR_MODES.includes(dcm)) {
|
|
4443
|
+
return {
|
|
4444
|
+
success: false,
|
|
4445
|
+
error: {
|
|
4446
|
+
message: `theme-tokens.json: \`defaultColorMode: "${String(dcm)}"\` is not a valid value. Valid values are \`light\`, \`dark\`, or \`system\`. Use \`system\` for OS-following behavior (respects \`prefers-color-scheme\`). Alternatively, OMIT the \`defaultColorMode\` key entirely when the designer has specified in design-language.json operationalNotes that OS preference should be respected (trigger phrases: "respect OS preference", "no forced default").`
|
|
4447
|
+
}
|
|
4448
|
+
};
|
|
4449
|
+
}
|
|
4450
|
+
return { success: true };
|
|
4451
|
+
},
|
|
4140
4452
|
workflow: (artifact2) => {
|
|
4141
4453
|
const workflowData = artifact2["workflow"];
|
|
4142
4454
|
if (!workflowData) {
|
|
@@ -4186,33 +4498,60 @@ function _validateArtifactInner(input) {
|
|
|
4186
4498
|
return { success: true };
|
|
4187
4499
|
},
|
|
4188
4500
|
// swp-4071 — opt-in cross-reference validator for the services phase.
|
|
4189
|
-
//
|
|
4190
|
-
//
|
|
4501
|
+
// Collects serviceHooks from ALL workflow-config-*.json artifacts (swp-x8sm fan-out)
|
|
4502
|
+
// plus the legacy single-file workflow-config.json (backward compat).
|
|
4503
|
+
// If any workflow declared serviceHooks, every ref must resolve to a flow or
|
|
4504
|
+
// state-machine in this services-config artifact.
|
|
4191
4505
|
// Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
|
|
4192
4506
|
services: (artifact2) => {
|
|
4193
|
-
const
|
|
4194
|
-
|
|
4195
|
-
|
|
4507
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
4508
|
+
const workflowArtifactFiles = [];
|
|
4509
|
+
if ((0, import_fs8.existsSync)(artifactsDir)) {
|
|
4510
|
+
try {
|
|
4511
|
+
const entries = (0, import_fs8.readdirSync)(artifactsDir);
|
|
4512
|
+
for (const entry of entries) {
|
|
4513
|
+
if (entry.startsWith("workflow-config-") && entry.endsWith(".json")) {
|
|
4514
|
+
workflowArtifactFiles.push((0, import_path8.join)(artifactsDir, entry));
|
|
4515
|
+
}
|
|
4516
|
+
}
|
|
4517
|
+
} catch {
|
|
4518
|
+
}
|
|
4196
4519
|
}
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
}
|
|
4520
|
+
const legacyPath = (0, import_path8.join)(artifactsDir, "workflow-config.json");
|
|
4521
|
+
if ((0, import_fs8.existsSync)(legacyPath)) {
|
|
4522
|
+
workflowArtifactFiles.push(legacyPath);
|
|
4523
|
+
}
|
|
4524
|
+
if (workflowArtifactFiles.length === 0) {
|
|
4201
4525
|
return { success: true };
|
|
4202
4526
|
}
|
|
4203
|
-
const declaredHooks =
|
|
4204
|
-
|
|
4527
|
+
const declaredHooks = [];
|
|
4528
|
+
for (const filePath of workflowArtifactFiles) {
|
|
4529
|
+
try {
|
|
4530
|
+
const parsed = JSON.parse((0, import_fs8.readFileSync)(filePath, "utf-8"));
|
|
4531
|
+
if (Array.isArray(parsed.serviceHooks)) {
|
|
4532
|
+
declaredHooks.push(...parsed.serviceHooks);
|
|
4533
|
+
}
|
|
4534
|
+
} catch {
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
const seenRefs = /* @__PURE__ */ new Set();
|
|
4538
|
+
const uniqueHooks = declaredHooks.filter((h) => {
|
|
4539
|
+
if (seenRefs.has(h.ref)) return false;
|
|
4540
|
+
seenRefs.add(h.ref);
|
|
4541
|
+
return true;
|
|
4542
|
+
});
|
|
4543
|
+
if (uniqueHooks.length === 0) {
|
|
4205
4544
|
return { success: true };
|
|
4206
4545
|
}
|
|
4207
4546
|
const flows = artifact2["flows"] ?? [];
|
|
4208
4547
|
const workflows = artifact2["workflows"] ?? [];
|
|
4209
4548
|
const flowNames = /* @__PURE__ */ new Set([...flows.map((f) => f.name), ...workflows.map((w) => w.name)]);
|
|
4210
|
-
const unresolved =
|
|
4549
|
+
const unresolved = uniqueHooks.filter((h) => !flowNames.has(h.ref));
|
|
4211
4550
|
if (unresolved.length > 0) {
|
|
4212
4551
|
return {
|
|
4213
4552
|
success: false,
|
|
4214
4553
|
error: {
|
|
4215
|
-
message: `Workflow declared ${
|
|
4554
|
+
message: `Workflow(s) declared ${uniqueHooks.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."
|
|
4216
4555
|
}
|
|
4217
4556
|
};
|
|
4218
4557
|
}
|
|
@@ -4278,11 +4617,55 @@ function _validateArtifactInner(input) {
|
|
|
4278
4617
|
return { text: JSON.stringify(result), isError: false };
|
|
4279
4618
|
}
|
|
4280
4619
|
}
|
|
4620
|
+
if (phase === "api") {
|
|
4621
|
+
const apiAuthResult = validateApiIntegration(artifact, input.integrationName);
|
|
4622
|
+
if (!apiAuthResult.valid) {
|
|
4623
|
+
const result = {
|
|
4624
|
+
valid: false,
|
|
4625
|
+
phase,
|
|
4626
|
+
violation: "schema-mismatch",
|
|
4627
|
+
retryPrompt: apiAuthResult.retryPrompt
|
|
4628
|
+
};
|
|
4629
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
if (phase === "workflow" && input.workflowName !== void 0) {
|
|
4633
|
+
if (!isValidSlug(input.workflowName)) {
|
|
4634
|
+
const result = {
|
|
4635
|
+
valid: false,
|
|
4636
|
+
phase,
|
|
4637
|
+
violation: "schema-mismatch",
|
|
4638
|
+
retryPrompt: `workflowName "${input.workflowName}" is not a valid slug. Use lowercase kebab-case (a-z, 0-9, hyphens only, no spaces or path separators). Example: "patient-evacuation", "ambulance-crew-dispatch".`
|
|
4639
|
+
};
|
|
4640
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
let authOrphanWarning;
|
|
4644
|
+
if (phase === "auth") {
|
|
4645
|
+
const authCfg = artifact["authConfig"];
|
|
4646
|
+
const routes = authCfg?.["protectedRoutes"];
|
|
4647
|
+
if (Array.isArray(routes) && routes.length > 0) {
|
|
4648
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
4649
|
+
const knownSlugs = extractManifestSlugs(artifactsDir);
|
|
4650
|
+
const orphanPatterns = [];
|
|
4651
|
+
for (const route of routes) {
|
|
4652
|
+
const pattern = typeof route === "string" ? route : route.pattern ?? "";
|
|
4653
|
+
if (!pattern) continue;
|
|
4654
|
+
const baseSlug = extractPatternBaseSlug(pattern);
|
|
4655
|
+
if (baseSlug && !knownSlugs.has(baseSlug)) {
|
|
4656
|
+
orphanPatterns.push(pattern);
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
if (orphanPatterns.length > 0) {
|
|
4660
|
+
authOrphanWarning = `The following protectedRoute patterns don't match any known page slug: ${orphanPatterns.map((p) => `"${p}"`).join(", ")}. If these are intentional cross-cutting rules (e.g., /admin/:path*, /audit/:path*), keep them; otherwise remove.`;
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4281
4664
|
try {
|
|
4282
|
-
const artifactsDir = (0,
|
|
4283
|
-
(0,
|
|
4284
|
-
const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : PHASE_ARTIFACT[phase];
|
|
4285
|
-
const artifactPath = (0,
|
|
4665
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
4666
|
+
(0, import_fs8.mkdirSync)(artifactsDir, { recursive: true });
|
|
4667
|
+
const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : phase === "workflow" && input.workflowName ? `workflow-config-${input.workflowName}.json` : PHASE_ARTIFACT[phase];
|
|
4668
|
+
const artifactPath = (0, import_path8.join)(artifactsDir, artifactFile);
|
|
4286
4669
|
const serialized = JSON.stringify(artifact, null, 2) + "\n";
|
|
4287
4670
|
const artifactBytes = Buffer.from(serialized, "utf-8");
|
|
4288
4671
|
safeWriteSync(artifactPath, serialized);
|
|
@@ -4305,7 +4688,8 @@ function _validateArtifactInner(input) {
|
|
|
4305
4688
|
valid: true,
|
|
4306
4689
|
phase,
|
|
4307
4690
|
artifactPath,
|
|
4308
|
-
summary: `Wrote ${artifactFile} (keys: ${topKeys}${Object.keys(artifact).length > 5 ? ", ..." : ""})${signed ? " [signed]" : ""}
|
|
4691
|
+
summary: `Wrote ${artifactFile} (keys: ${topKeys}${Object.keys(artifact).length > 5 ? ", ..." : ""})${signed ? " [signed]" : ""}`,
|
|
4692
|
+
...authOrphanWarning ? { retryPrompt: authOrphanWarning } : {}
|
|
4309
4693
|
};
|
|
4310
4694
|
return { text: JSON.stringify(result), isError: false };
|
|
4311
4695
|
} catch (err) {
|
|
@@ -4501,10 +4885,10 @@ function registerPipelineTools(server2) {
|
|
|
4501
4885
|
isError: true
|
|
4502
4886
|
};
|
|
4503
4887
|
}
|
|
4504
|
-
const questionsDir = (0,
|
|
4505
|
-
(0,
|
|
4506
|
-
const outPath = (0,
|
|
4507
|
-
(0,
|
|
4888
|
+
const questionsDir = (0, import_path8.join)(process.cwd(), ".stackwright", "questions");
|
|
4889
|
+
(0, import_fs8.mkdirSync)(questionsDir, { recursive: true });
|
|
4890
|
+
const outPath = (0, import_path8.join)(questionsDir, `${phase}.json`);
|
|
4891
|
+
(0, import_fs8.writeFileSync)(
|
|
4508
4892
|
outPath,
|
|
4509
4893
|
JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
4510
4894
|
);
|
|
@@ -4543,15 +4927,19 @@ function registerPipelineTools(server2) {
|
|
|
4543
4927
|
),
|
|
4544
4928
|
integrationName: import_zod13.z.string().optional().describe(
|
|
4545
4929
|
'When phase="api": write to api-config-<integrationName>.json instead of api-config.json. Enables concurrent per-spec fan-out without collision (swp-530y).'
|
|
4930
|
+
),
|
|
4931
|
+
workflowName: import_zod13.z.string().optional().describe(
|
|
4932
|
+
`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.`
|
|
4546
4933
|
)
|
|
4547
4934
|
},
|
|
4548
|
-
async ({ phase, responseText, artifact, integrationName }) => {
|
|
4935
|
+
async ({ phase, responseText, artifact, integrationName, workflowName }) => {
|
|
4549
4936
|
if (artifact) {
|
|
4550
4937
|
return res(
|
|
4551
4938
|
handleValidateArtifact({
|
|
4552
4939
|
phase,
|
|
4553
4940
|
artifact,
|
|
4554
|
-
...integrationName ? { integrationName } : {}
|
|
4941
|
+
...integrationName ? { integrationName } : {},
|
|
4942
|
+
...workflowName ? { workflowName } : {}
|
|
4555
4943
|
})
|
|
4556
4944
|
);
|
|
4557
4945
|
}
|
|
@@ -4559,7 +4947,8 @@ function registerPipelineTools(server2) {
|
|
|
4559
4947
|
handleValidateArtifact({
|
|
4560
4948
|
phase,
|
|
4561
4949
|
responseText: responseText ?? "",
|
|
4562
|
-
...integrationName ? { integrationName } : {}
|
|
4950
|
+
...integrationName ? { integrationName } : {},
|
|
4951
|
+
...workflowName ? { workflowName } : {}
|
|
4563
4952
|
})
|
|
4564
4953
|
);
|
|
4565
4954
|
}
|
|
@@ -4591,8 +4980,8 @@ function registerPipelineTools(server2) {
|
|
|
4591
4980
|
|
|
4592
4981
|
// src/tools/safe-write.ts
|
|
4593
4982
|
var import_zod14 = require("zod");
|
|
4594
|
-
var
|
|
4595
|
-
var
|
|
4983
|
+
var import_fs9 = require("fs");
|
|
4984
|
+
var import_path9 = require("path");
|
|
4596
4985
|
var import_telemetry2 = require("@stackwright-pro/telemetry");
|
|
4597
4986
|
var OTTER_WRITE_ALLOWLISTS = {
|
|
4598
4987
|
"stackwright-pro-designer-otter": [
|
|
@@ -4604,6 +4993,11 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
4604
4993
|
prefix: "stackwright.theme.",
|
|
4605
4994
|
suffix: ".yml",
|
|
4606
4995
|
description: "Theme config YAML (merged at build time)"
|
|
4996
|
+
},
|
|
4997
|
+
{
|
|
4998
|
+
prefix: "stackwright.yml",
|
|
4999
|
+
suffix: "",
|
|
5000
|
+
description: "Stackwright config (themeName + customTheme writeback)"
|
|
4607
5001
|
}
|
|
4608
5002
|
],
|
|
4609
5003
|
"stackwright-pro-auth-otter": [
|
|
@@ -4658,6 +5052,17 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
4658
5052
|
prefix: ".stackwright/artifacts/",
|
|
4659
5053
|
suffix: ".json",
|
|
4660
5054
|
description: "Scaffold manifest artifact"
|
|
5055
|
+
},
|
|
5056
|
+
// swp-89w5: scaffold-otter initialises mock backend wiring via stackwright_pro_emit_env_local
|
|
5057
|
+
{
|
|
5058
|
+
prefix: ".env.local",
|
|
5059
|
+
suffix: "",
|
|
5060
|
+
description: "Dotenv runtime config (mock backend wiring: NEXT_PUBLIC_MOCK_BACKEND + per-integration mock keys)"
|
|
5061
|
+
},
|
|
5062
|
+
{
|
|
5063
|
+
prefix: ".env.local.example",
|
|
5064
|
+
suffix: "",
|
|
5065
|
+
description: "Dotenv template (schema for real production values)"
|
|
4661
5066
|
}
|
|
4662
5067
|
],
|
|
4663
5068
|
"stackwright-pro-api-otter": [
|
|
@@ -4758,7 +5163,7 @@ function getMaxBytesForPath(filePath) {
|
|
|
4758
5163
|
}
|
|
4759
5164
|
var PAGE_REGISTRY_FILE = ".stackwright/page-registry.json";
|
|
4760
5165
|
function extractPageSlug(filePath) {
|
|
4761
|
-
const match = (0,
|
|
5166
|
+
const match = (0, import_path9.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
|
|
4762
5167
|
return match?.[1] ?? null;
|
|
4763
5168
|
}
|
|
4764
5169
|
function extractContentTypes(yamlContent) {
|
|
@@ -4774,27 +5179,27 @@ function extractContentTypes(yamlContent) {
|
|
|
4774
5179
|
return types.length > 0 ? types : ["unknown"];
|
|
4775
5180
|
}
|
|
4776
5181
|
function readPageRegistry(cwd) {
|
|
4777
|
-
const regPath = (0,
|
|
4778
|
-
if (!(0,
|
|
5182
|
+
const regPath = (0, import_path9.join)(cwd, PAGE_REGISTRY_FILE);
|
|
5183
|
+
if (!(0, import_fs9.existsSync)(regPath)) return {};
|
|
4779
5184
|
try {
|
|
4780
|
-
const stat = (0,
|
|
5185
|
+
const stat = (0, import_fs9.lstatSync)(regPath);
|
|
4781
5186
|
if (stat.isSymbolicLink()) return {};
|
|
4782
|
-
return JSON.parse((0,
|
|
5187
|
+
return JSON.parse((0, import_fs9.readFileSync)(regPath, "utf-8"));
|
|
4783
5188
|
} catch {
|
|
4784
5189
|
return {};
|
|
4785
5190
|
}
|
|
4786
5191
|
}
|
|
4787
5192
|
function writePageRegistry(cwd, registry) {
|
|
4788
|
-
const dir = (0,
|
|
4789
|
-
(0,
|
|
4790
|
-
const regPath = (0,
|
|
4791
|
-
if ((0,
|
|
4792
|
-
const stat = (0,
|
|
5193
|
+
const dir = (0, import_path9.join)(cwd, ".stackwright");
|
|
5194
|
+
(0, import_fs9.mkdirSync)(dir, { recursive: true });
|
|
5195
|
+
const regPath = (0, import_path9.join)(cwd, PAGE_REGISTRY_FILE);
|
|
5196
|
+
if ((0, import_fs9.existsSync)(regPath)) {
|
|
5197
|
+
const stat = (0, import_fs9.lstatSync)(regPath);
|
|
4793
5198
|
if (stat.isSymbolicLink()) {
|
|
4794
5199
|
throw new Error("Refusing to write page registry through symlink");
|
|
4795
5200
|
}
|
|
4796
5201
|
}
|
|
4797
|
-
(0,
|
|
5202
|
+
(0, import_fs9.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
|
|
4798
5203
|
}
|
|
4799
5204
|
function checkPageOwnership(cwd, slug, callerOtter) {
|
|
4800
5205
|
const registry = readPageRegistry(cwd);
|
|
@@ -4813,11 +5218,11 @@ function stripNextjsBracketSegments(path5) {
|
|
|
4813
5218
|
return path5.replace(NEXTJS_BRACKET_SEGMENT_RE, "");
|
|
4814
5219
|
}
|
|
4815
5220
|
function checkPathAllowed(callerOtter, filePath) {
|
|
4816
|
-
const normalized = (0,
|
|
5221
|
+
const normalized = (0, import_path9.normalize)(filePath);
|
|
4817
5222
|
if (stripNextjsBracketSegments(normalized).includes("..")) {
|
|
4818
5223
|
return { allowed: false, error: 'Path traversal detected: ".." segments are not allowed' };
|
|
4819
5224
|
}
|
|
4820
|
-
if ((0,
|
|
5225
|
+
if ((0, import_path9.isAbsolute)(normalized)) {
|
|
4821
5226
|
return {
|
|
4822
5227
|
allowed: false,
|
|
4823
5228
|
error: "Absolute paths are not allowed \u2014 use paths relative to project root"
|
|
@@ -4959,11 +5364,11 @@ function _safeWriteInner(input) {
|
|
|
4959
5364
|
};
|
|
4960
5365
|
return { text: JSON.stringify(result), isError: true };
|
|
4961
5366
|
}
|
|
4962
|
-
const normalized = (0,
|
|
4963
|
-
const fullPath = (0,
|
|
4964
|
-
if ((0,
|
|
5367
|
+
const normalized = (0, import_path9.normalize)(filePath);
|
|
5368
|
+
const fullPath = (0, import_path9.join)(cwd, normalized);
|
|
5369
|
+
if ((0, import_fs9.existsSync)(fullPath)) {
|
|
4965
5370
|
try {
|
|
4966
|
-
const stat = (0,
|
|
5371
|
+
const stat = (0, import_fs9.lstatSync)(fullPath);
|
|
4967
5372
|
if (stat.isSymbolicLink()) {
|
|
4968
5373
|
const result = {
|
|
4969
5374
|
success: false,
|
|
@@ -5004,9 +5409,9 @@ function _safeWriteInner(input) {
|
|
|
5004
5409
|
}
|
|
5005
5410
|
try {
|
|
5006
5411
|
if (createDirectories) {
|
|
5007
|
-
(0,
|
|
5412
|
+
(0, import_fs9.mkdirSync)((0, import_path9.dirname)(fullPath), { recursive: true });
|
|
5008
5413
|
}
|
|
5009
|
-
(0,
|
|
5414
|
+
(0, import_fs9.writeFileSync)(fullPath, content, { encoding: "utf-8" });
|
|
5010
5415
|
if (pageSlug) {
|
|
5011
5416
|
try {
|
|
5012
5417
|
const registry = readPageRegistry(cwd);
|
|
@@ -5091,8 +5496,8 @@ function registerSafeWriteTools(server2) {
|
|
|
5091
5496
|
|
|
5092
5497
|
// src/tools/auth.ts
|
|
5093
5498
|
var import_zod15 = require("zod");
|
|
5094
|
-
var
|
|
5095
|
-
var
|
|
5499
|
+
var import_fs10 = require("fs");
|
|
5500
|
+
var import_path10 = require("path");
|
|
5096
5501
|
function buildHierarchy(roles) {
|
|
5097
5502
|
const h = {};
|
|
5098
5503
|
for (let i = 0; i < roles.length - 1; i++) {
|
|
@@ -5119,9 +5524,9 @@ ${indent} requiredRole: ${r.requiredRole}`).join("\n");
|
|
|
5119
5524
|
}
|
|
5120
5525
|
function detectNextMajorVersion(cwd) {
|
|
5121
5526
|
try {
|
|
5122
|
-
const pkgPath = (0,
|
|
5123
|
-
if (!(0,
|
|
5124
|
-
const pkg = JSON.parse((0,
|
|
5527
|
+
const pkgPath = (0, import_path10.join)(cwd, "package.json");
|
|
5528
|
+
if (!(0, import_fs10.existsSync)(pkgPath)) return null;
|
|
5529
|
+
const pkg = JSON.parse((0, import_fs10.readFileSync)(pkgPath, "utf8"));
|
|
5125
5530
|
const nextVersion = pkg.dependencies?.next ?? pkg.devDependencies?.next;
|
|
5126
5531
|
if (!nextVersion) return null;
|
|
5127
5532
|
const match = nextVersion.match(/(\d+)/);
|
|
@@ -5785,7 +6190,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5785
6190
|
auditRetentionDays,
|
|
5786
6191
|
normalizedRoutes
|
|
5787
6192
|
);
|
|
5788
|
-
(0,
|
|
6193
|
+
(0, import_fs10.writeFileSync)((0, import_path10.join)(cwd, conventionFile), authFileContent, "utf8");
|
|
5789
6194
|
filesWritten.push(conventionFile);
|
|
5790
6195
|
} catch (err) {
|
|
5791
6196
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5805,12 +6210,12 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5805
6210
|
if (!devOnly) {
|
|
5806
6211
|
try {
|
|
5807
6212
|
const envBlock = generateEnvBlock(effectiveMethod, params);
|
|
5808
|
-
const envPath = (0,
|
|
5809
|
-
if ((0,
|
|
5810
|
-
const existing = (0,
|
|
5811
|
-
(0,
|
|
6213
|
+
const envPath = (0, import_path10.join)(cwd, ".env.example");
|
|
6214
|
+
if ((0, import_fs10.existsSync)(envPath)) {
|
|
6215
|
+
const existing = (0, import_fs10.readFileSync)(envPath, "utf8");
|
|
6216
|
+
(0, import_fs10.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
|
|
5812
6217
|
} else {
|
|
5813
|
-
(0,
|
|
6218
|
+
(0, import_fs10.writeFileSync)(envPath, envBlock, "utf8");
|
|
5814
6219
|
}
|
|
5815
6220
|
filesWritten.push(".env.example");
|
|
5816
6221
|
} catch (err) {
|
|
@@ -5828,10 +6233,10 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5828
6233
|
}
|
|
5829
6234
|
if (devOnly) {
|
|
5830
6235
|
try {
|
|
5831
|
-
const mockAuthDir = (0,
|
|
5832
|
-
(0,
|
|
6236
|
+
const mockAuthDir = (0, import_path10.join)(cwd, "lib");
|
|
6237
|
+
(0, import_fs10.mkdirSync)(mockAuthDir, { recursive: true });
|
|
5833
6238
|
const mockAuthContent = generateMockAuthContent(roles, mockUsers);
|
|
5834
|
-
(0,
|
|
6239
|
+
(0, import_fs10.writeFileSync)((0, import_path10.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
|
|
5835
6240
|
filesWritten.push("lib/mock-auth.ts");
|
|
5836
6241
|
} catch (err) {
|
|
5837
6242
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5849,11 +6254,11 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5849
6254
|
};
|
|
5850
6255
|
}
|
|
5851
6256
|
try {
|
|
5852
|
-
const pkgPath = (0,
|
|
5853
|
-
if ((0,
|
|
5854
|
-
const existingPkg = (0,
|
|
6257
|
+
const pkgPath = (0, import_path10.join)(cwd, "package.json");
|
|
6258
|
+
if ((0, import_fs10.existsSync)(pkgPath)) {
|
|
6259
|
+
const existingPkg = (0, import_fs10.readFileSync)(pkgPath, "utf8");
|
|
5855
6260
|
const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
|
|
5856
|
-
(0,
|
|
6261
|
+
(0, import_fs10.writeFileSync)(pkgPath, updatedPkg, "utf8");
|
|
5857
6262
|
filesWritten.push("package.json");
|
|
5858
6263
|
}
|
|
5859
6264
|
} catch (err) {
|
|
@@ -5894,7 +6299,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5894
6299
|
normalizedRoutes,
|
|
5895
6300
|
useProxy
|
|
5896
6301
|
);
|
|
5897
|
-
(0,
|
|
6302
|
+
(0, import_fs10.writeFileSync)((0, import_path10.join)(cwd, "stackwright.auth.yml"), authYaml, "utf8");
|
|
5898
6303
|
filesWritten.push("stackwright.auth.yml");
|
|
5899
6304
|
} catch (err) {
|
|
5900
6305
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6007,8 +6412,8 @@ function registerAuthTools(server2) {
|
|
|
6007
6412
|
|
|
6008
6413
|
// src/integrity.ts
|
|
6009
6414
|
var import_crypto4 = require("crypto");
|
|
6010
|
-
var
|
|
6011
|
-
var
|
|
6415
|
+
var import_fs11 = require("fs");
|
|
6416
|
+
var import_path11 = require("path");
|
|
6012
6417
|
var _checksums = /* @__PURE__ */ new Map([
|
|
6013
6418
|
[
|
|
6014
6419
|
"stackwright-pro-api-otter.json",
|
|
@@ -6016,11 +6421,11 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6016
6421
|
],
|
|
6017
6422
|
[
|
|
6018
6423
|
"stackwright-pro-auth-otter.json",
|
|
6019
|
-
"
|
|
6424
|
+
"69fa03c5532ef617c86fe7af41e8174c168d282baf602b1b269a7e933ea86b7c"
|
|
6020
6425
|
],
|
|
6021
6426
|
[
|
|
6022
6427
|
"stackwright-pro-dashboard-otter.json",
|
|
6023
|
-
"
|
|
6428
|
+
"f55081bf4d6545e5435128919f6f9233956ba37671fc9f85300f240ad2497ab6"
|
|
6024
6429
|
],
|
|
6025
6430
|
[
|
|
6026
6431
|
"stackwright-pro-data-otter.json",
|
|
@@ -6036,19 +6441,19 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6036
6441
|
],
|
|
6037
6442
|
[
|
|
6038
6443
|
"stackwright-pro-foreman-otter.json",
|
|
6039
|
-
"
|
|
6444
|
+
"88c4101154d44a7c5470ded13562402a39323ee570ed3a90a73b8d85d7fd2817"
|
|
6040
6445
|
],
|
|
6041
6446
|
[
|
|
6042
6447
|
"stackwright-pro-form-wizard-otter.json",
|
|
6043
|
-
"
|
|
6448
|
+
"cb350297a0068ead2424e703e8c775d91c0b87249ea6904f43b903baf8a84b79"
|
|
6044
6449
|
],
|
|
6045
6450
|
[
|
|
6046
6451
|
"stackwright-pro-geo-otter.json",
|
|
6047
|
-
"
|
|
6452
|
+
"3032fbd27de36c0cdc0e19e46a32d771208d8e86714482f5a368d46342c8317a"
|
|
6048
6453
|
],
|
|
6049
6454
|
[
|
|
6050
6455
|
"stackwright-pro-page-otter.json",
|
|
6051
|
-
"
|
|
6456
|
+
"cc0db24b00f0130f8b4ec8d385c73ea1bcf5d57ba45aca6b986bcbd5da328930"
|
|
6052
6457
|
],
|
|
6053
6458
|
[
|
|
6054
6459
|
"stackwright-pro-polish-otter.json",
|
|
@@ -6060,15 +6465,15 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6060
6465
|
],
|
|
6061
6466
|
[
|
|
6062
6467
|
"stackwright-pro-scaffold-otter.json",
|
|
6063
|
-
"
|
|
6468
|
+
"90272bab51bd8ed9e25c0fb6481cdd4252386850f64192edfe1e2eff44166e2d"
|
|
6064
6469
|
],
|
|
6065
6470
|
[
|
|
6066
6471
|
"stackwright-pro-theme-otter.json",
|
|
6067
|
-
"
|
|
6472
|
+
"afb47f8381907145c0e098bdcaae4dd9f5c4ff825a8e88d00a218d2f6858820b"
|
|
6068
6473
|
],
|
|
6069
6474
|
[
|
|
6070
6475
|
"stackwright-services-otter.json",
|
|
6071
|
-
"
|
|
6476
|
+
"4351348806aeb98404a2bacb044209ca411be5e35e2f2c3d03eb4d28a07fde78"
|
|
6072
6477
|
]
|
|
6073
6478
|
]);
|
|
6074
6479
|
Object.freeze(_checksums);
|
|
@@ -6091,14 +6496,14 @@ function safeEqual(a, b) {
|
|
|
6091
6496
|
return (0, import_crypto4.timingSafeEqual)(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
|
|
6092
6497
|
}
|
|
6093
6498
|
function verifyOtterFile(filePath) {
|
|
6094
|
-
const filename = (0,
|
|
6499
|
+
const filename = (0, import_path11.basename)(filePath);
|
|
6095
6500
|
const expected = CANONICAL_CHECKSUMS.get(filename);
|
|
6096
6501
|
if (expected === void 0) {
|
|
6097
6502
|
return { verified: false, filename, error: `Unknown otter file: not in canonical set` };
|
|
6098
6503
|
}
|
|
6099
6504
|
let stat;
|
|
6100
6505
|
try {
|
|
6101
|
-
stat = (0,
|
|
6506
|
+
stat = (0, import_fs11.lstatSync)(filePath);
|
|
6102
6507
|
} catch (err) {
|
|
6103
6508
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6104
6509
|
return { verified: false, filename, error: `Cannot stat file: ${msg}` };
|
|
@@ -6116,7 +6521,7 @@ function verifyOtterFile(filePath) {
|
|
|
6116
6521
|
}
|
|
6117
6522
|
let raw;
|
|
6118
6523
|
try {
|
|
6119
|
-
raw = (0,
|
|
6524
|
+
raw = (0, import_fs11.readFileSync)(filePath);
|
|
6120
6525
|
} catch (err) {
|
|
6121
6526
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6122
6527
|
return { verified: false, filename, error: `Cannot read file: ${msg}` };
|
|
@@ -6166,7 +6571,7 @@ function verifyAllOtters(otterDir) {
|
|
|
6166
6571
|
const unknown = [];
|
|
6167
6572
|
let entries;
|
|
6168
6573
|
try {
|
|
6169
|
-
entries = (0,
|
|
6574
|
+
entries = (0, import_fs11.readdirSync)(otterDir);
|
|
6170
6575
|
} catch (err) {
|
|
6171
6576
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6172
6577
|
return {
|
|
@@ -6177,9 +6582,9 @@ function verifyAllOtters(otterDir) {
|
|
|
6177
6582
|
}
|
|
6178
6583
|
const otterFiles = entries.filter((f) => f.endsWith("-otter.json"));
|
|
6179
6584
|
for (const filename of otterFiles) {
|
|
6180
|
-
const filePath = (0,
|
|
6585
|
+
const filePath = (0, import_path11.join)(otterDir, filename);
|
|
6181
6586
|
try {
|
|
6182
|
-
if ((0,
|
|
6587
|
+
if ((0, import_fs11.lstatSync)(filePath).isSymbolicLink()) {
|
|
6183
6588
|
failed.push({ filename, error: "Skipped: symlink" });
|
|
6184
6589
|
continue;
|
|
6185
6590
|
}
|
|
@@ -6205,9 +6610,9 @@ var DEFAULT_SEARCH_PATHS = ["node_modules/@stackwright-pro/otters/src/", "packag
|
|
|
6205
6610
|
function resolveOtterDir() {
|
|
6206
6611
|
const cwd = process.cwd();
|
|
6207
6612
|
for (const relative2 of DEFAULT_SEARCH_PATHS) {
|
|
6208
|
-
const candidate = (0,
|
|
6613
|
+
const candidate = (0, import_path11.join)(cwd, relative2);
|
|
6209
6614
|
try {
|
|
6210
|
-
(0,
|
|
6615
|
+
(0, import_fs11.lstatSync)(candidate);
|
|
6211
6616
|
return candidate;
|
|
6212
6617
|
} catch {
|
|
6213
6618
|
}
|
|
@@ -6286,13 +6691,13 @@ function registerIntegrityTools(server2) {
|
|
|
6286
6691
|
|
|
6287
6692
|
// src/tools/domain.ts
|
|
6288
6693
|
var import_zod16 = require("zod");
|
|
6289
|
-
var
|
|
6290
|
-
var
|
|
6694
|
+
var import_fs12 = require("fs");
|
|
6695
|
+
var import_path12 = require("path");
|
|
6291
6696
|
function handleListCollections(input) {
|
|
6292
6697
|
const cwd = input._cwd ?? process.cwd();
|
|
6293
6698
|
const sources = [
|
|
6294
6699
|
{
|
|
6295
|
-
path: (0,
|
|
6700
|
+
path: (0, import_path12.join)(cwd, ".stackwright", "artifacts", "data-config.json"),
|
|
6296
6701
|
source: "data-config.json",
|
|
6297
6702
|
parse: (raw) => {
|
|
6298
6703
|
const parsed = JSON.parse(raw);
|
|
@@ -6303,15 +6708,15 @@ function handleListCollections(input) {
|
|
|
6303
6708
|
}
|
|
6304
6709
|
},
|
|
6305
6710
|
{
|
|
6306
|
-
path: (0,
|
|
6711
|
+
path: (0, import_path12.join)(cwd, "stackwright.yml"),
|
|
6307
6712
|
source: "stackwright.yml",
|
|
6308
6713
|
parse: extractCollectionsFromYaml
|
|
6309
6714
|
}
|
|
6310
6715
|
];
|
|
6311
6716
|
for (const { path: path5, source, parse } of sources) {
|
|
6312
|
-
if (!(0,
|
|
6717
|
+
if (!(0, import_fs12.existsSync)(path5)) continue;
|
|
6313
6718
|
try {
|
|
6314
|
-
const collections = parse((0,
|
|
6719
|
+
const collections = parse((0, import_fs12.readFileSync)(path5, "utf8"));
|
|
6315
6720
|
return {
|
|
6316
6721
|
text: JSON.stringify({ collections, source, collectionCount: collections.length }),
|
|
6317
6722
|
isError: false
|
|
@@ -6462,8 +6867,8 @@ function handleValidateWorkflow(input) {
|
|
|
6462
6867
|
if (input.workflow && Object.keys(input.workflow).length > 0) {
|
|
6463
6868
|
raw = input.workflow;
|
|
6464
6869
|
} else {
|
|
6465
|
-
const artifactPath = (0,
|
|
6466
|
-
if (!(0,
|
|
6870
|
+
const artifactPath = (0, import_path12.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
|
|
6871
|
+
if (!(0, import_fs12.existsSync)(artifactPath)) {
|
|
6467
6872
|
return fail([
|
|
6468
6873
|
{
|
|
6469
6874
|
code: "NO_WORKFLOW",
|
|
@@ -6472,7 +6877,7 @@ function handleValidateWorkflow(input) {
|
|
|
6472
6877
|
]);
|
|
6473
6878
|
}
|
|
6474
6879
|
try {
|
|
6475
|
-
raw = JSON.parse((0,
|
|
6880
|
+
raw = JSON.parse((0, import_fs12.readFileSync)(artifactPath, "utf8"));
|
|
6476
6881
|
} catch (err) {
|
|
6477
6882
|
return fail([{ code: "INVALID_JSON", message: `Failed to parse workflow artifact: ${err}` }]);
|
|
6478
6883
|
}
|
|
@@ -6909,8 +7314,8 @@ function registerProPageTools(server2) {
|
|
|
6909
7314
|
}
|
|
6910
7315
|
|
|
6911
7316
|
// src/tools/scaffold-cleanup.ts
|
|
6912
|
-
var
|
|
6913
|
-
var
|
|
7317
|
+
var import_fs13 = require("fs");
|
|
7318
|
+
var import_path13 = require("path");
|
|
6914
7319
|
var SCAFFOLD_FILES_TO_DELETE = [
|
|
6915
7320
|
"content/posts/getting-started.yaml",
|
|
6916
7321
|
"content/posts/hello-world.yaml"
|
|
@@ -6935,12 +7340,12 @@ var FALLBACK_COLORS = {
|
|
|
6935
7340
|
mutedForeground: "#737373"
|
|
6936
7341
|
};
|
|
6937
7342
|
function readThemeColors(cwd) {
|
|
6938
|
-
const tokenPath = (0,
|
|
6939
|
-
if (!(0,
|
|
6940
|
-
const stat = (0,
|
|
7343
|
+
const tokenPath = (0, import_path13.join)(cwd, ".stackwright", "artifacts", "theme-tokens.json");
|
|
7344
|
+
if (!(0, import_fs13.existsSync)(tokenPath)) return { ...FALLBACK_COLORS };
|
|
7345
|
+
const stat = (0, import_fs13.lstatSync)(tokenPath);
|
|
6941
7346
|
if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
|
|
6942
7347
|
try {
|
|
6943
|
-
const raw = JSON.parse((0,
|
|
7348
|
+
const raw = JSON.parse((0, import_fs13.readFileSync)(tokenPath, "utf-8"));
|
|
6944
7349
|
const css = raw?.cssVariables ?? {};
|
|
6945
7350
|
const toHsl = (hslValue) => {
|
|
6946
7351
|
if (!hslValue || typeof hslValue !== "string") return null;
|
|
@@ -7000,15 +7405,15 @@ function generateNotFoundPage(colors) {
|
|
|
7000
7405
|
`;
|
|
7001
7406
|
}
|
|
7002
7407
|
function isSafePath(fullPath) {
|
|
7003
|
-
if (!(0,
|
|
7004
|
-
const stat = (0,
|
|
7408
|
+
if (!(0, import_fs13.existsSync)(fullPath)) return true;
|
|
7409
|
+
const stat = (0, import_fs13.lstatSync)(fullPath);
|
|
7005
7410
|
return !stat.isSymbolicLink();
|
|
7006
7411
|
}
|
|
7007
7412
|
function isDirEmpty(dirPath) {
|
|
7008
|
-
if (!(0,
|
|
7009
|
-
const stat = (0,
|
|
7413
|
+
if (!(0, import_fs13.existsSync)(dirPath)) return false;
|
|
7414
|
+
const stat = (0, import_fs13.lstatSync)(dirPath);
|
|
7010
7415
|
if (!stat.isDirectory()) return false;
|
|
7011
|
-
return (0,
|
|
7416
|
+
return (0, import_fs13.readdirSync)(dirPath).length === 0;
|
|
7012
7417
|
}
|
|
7013
7418
|
function handleCleanupScaffold(_cwd) {
|
|
7014
7419
|
const cwd = _cwd ?? process.cwd();
|
|
@@ -7021,8 +7426,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7021
7426
|
cleanupWarnings: []
|
|
7022
7427
|
};
|
|
7023
7428
|
for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
|
|
7024
|
-
const fullPath = (0,
|
|
7025
|
-
if (!(0,
|
|
7429
|
+
const fullPath = (0, import_path13.join)(cwd, relPath);
|
|
7430
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7026
7431
|
result.skipped.push(relPath);
|
|
7027
7432
|
continue;
|
|
7028
7433
|
}
|
|
@@ -7031,7 +7436,7 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7031
7436
|
continue;
|
|
7032
7437
|
}
|
|
7033
7438
|
try {
|
|
7034
|
-
(0,
|
|
7439
|
+
(0, import_fs13.unlinkSync)(fullPath);
|
|
7035
7440
|
result.deleted.push(relPath);
|
|
7036
7441
|
} catch (err) {
|
|
7037
7442
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -7039,19 +7444,19 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7039
7444
|
}
|
|
7040
7445
|
{
|
|
7041
7446
|
const relPath = "pages/getting-started/content.yml";
|
|
7042
|
-
const fullPath = (0,
|
|
7043
|
-
if (!(0,
|
|
7447
|
+
const fullPath = (0, import_path13.join)(cwd, relPath);
|
|
7448
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7044
7449
|
result.skipped.push(relPath);
|
|
7045
7450
|
} else if (!isSafePath(fullPath)) {
|
|
7046
7451
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
7047
7452
|
} else {
|
|
7048
|
-
const content = (0,
|
|
7453
|
+
const content = (0, import_fs13.readFileSync)(fullPath, "utf-8");
|
|
7049
7454
|
const isScaffoldDefault = GETTING_STARTED_SCAFFOLD_FINGERPRINTS.some(
|
|
7050
7455
|
(marker) => content.includes(marker)
|
|
7051
7456
|
);
|
|
7052
7457
|
if (isScaffoldDefault) {
|
|
7053
7458
|
try {
|
|
7054
|
-
(0,
|
|
7459
|
+
(0, import_fs13.unlinkSync)(fullPath);
|
|
7055
7460
|
result.deleted.push(relPath);
|
|
7056
7461
|
} catch (err) {
|
|
7057
7462
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -7065,21 +7470,21 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7065
7470
|
}
|
|
7066
7471
|
{
|
|
7067
7472
|
const relPath = `content/posts/${POSTS_COLLECTION_FILE}`;
|
|
7068
|
-
const fullPath = (0,
|
|
7069
|
-
const postsDir = (0,
|
|
7070
|
-
if (!(0,
|
|
7473
|
+
const fullPath = (0, import_path13.join)(cwd, relPath);
|
|
7474
|
+
const postsDir = (0, import_path13.join)(cwd, "content/posts");
|
|
7475
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7071
7476
|
result.skipped.push(relPath);
|
|
7072
7477
|
} else if (!isSafePath(fullPath)) {
|
|
7073
7478
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
7074
|
-
} else if (!(0,
|
|
7479
|
+
} else if (!(0, import_fs13.existsSync)(postsDir)) {
|
|
7075
7480
|
result.skipped.push(relPath);
|
|
7076
7481
|
} else {
|
|
7077
|
-
const userPostFiles = (0,
|
|
7482
|
+
const userPostFiles = (0, import_fs13.readdirSync)(postsDir).filter(
|
|
7078
7483
|
(f) => f !== POSTS_COLLECTION_FILE && POST_FILE_EXTENSIONS.some((ext) => f.endsWith(ext))
|
|
7079
7484
|
);
|
|
7080
7485
|
if (userPostFiles.length === 0) {
|
|
7081
7486
|
try {
|
|
7082
|
-
(0,
|
|
7487
|
+
(0, import_fs13.unlinkSync)(fullPath);
|
|
7083
7488
|
result.deleted.push(relPath);
|
|
7084
7489
|
} catch (err) {
|
|
7085
7490
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -7092,15 +7497,15 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7092
7497
|
}
|
|
7093
7498
|
}
|
|
7094
7499
|
for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
|
|
7095
|
-
const fullDir = (0,
|
|
7096
|
-
if (!(0,
|
|
7500
|
+
const fullDir = (0, import_path13.join)(cwd, relDir);
|
|
7501
|
+
if (!(0, import_fs13.existsSync)(fullDir)) continue;
|
|
7097
7502
|
if (!isSafePath(fullDir)) {
|
|
7098
7503
|
result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
|
|
7099
7504
|
continue;
|
|
7100
7505
|
}
|
|
7101
7506
|
if (isDirEmpty(fullDir)) {
|
|
7102
7507
|
try {
|
|
7103
|
-
(0,
|
|
7508
|
+
(0, import_fs13.rmdirSync)(fullDir);
|
|
7104
7509
|
result.prunedDirs.push(relDir);
|
|
7105
7510
|
} catch (err) {
|
|
7106
7511
|
result.errors.push(`Failed to prune directory ${relDir}: ${String(err)}`);
|
|
@@ -7108,8 +7513,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7108
7513
|
}
|
|
7109
7514
|
}
|
|
7110
7515
|
{
|
|
7111
|
-
const fullPath = (0,
|
|
7112
|
-
if (!(0,
|
|
7516
|
+
const fullPath = (0, import_path13.join)(cwd, NOT_FOUND_PATH);
|
|
7517
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7113
7518
|
result.skipped.push(NOT_FOUND_PATH);
|
|
7114
7519
|
} else if (!isSafePath(fullPath)) {
|
|
7115
7520
|
result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
|
|
@@ -7117,9 +7522,9 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7117
7522
|
try {
|
|
7118
7523
|
const colors = readThemeColors(cwd);
|
|
7119
7524
|
const content = generateNotFoundPage(colors);
|
|
7120
|
-
const dir = (0,
|
|
7121
|
-
(0,
|
|
7122
|
-
(0,
|
|
7525
|
+
const dir = (0, import_path13.dirname)(fullPath);
|
|
7526
|
+
(0, import_fs13.mkdirSync)(dir, { recursive: true });
|
|
7527
|
+
(0, import_fs13.writeFileSync)(fullPath, content, "utf-8");
|
|
7123
7528
|
result.rewritten.push(NOT_FOUND_PATH);
|
|
7124
7529
|
} catch (err) {
|
|
7125
7530
|
result.errors.push(`Failed to rewrite ${NOT_FOUND_PATH}: ${String(err)}`);
|
|
@@ -7388,23 +7793,23 @@ function registerContrastTools(server2) {
|
|
|
7388
7793
|
|
|
7389
7794
|
// src/tools/compile.ts
|
|
7390
7795
|
var import_zod20 = require("zod");
|
|
7391
|
-
var
|
|
7392
|
-
var
|
|
7796
|
+
var import_fs14 = __toESM(require("fs"));
|
|
7797
|
+
var import_path14 = __toESM(require("path"));
|
|
7393
7798
|
var import_server = require("@stackwright-pro/pulse/server");
|
|
7394
7799
|
var import_server2 = require("@stackwright-pro/auth-nextjs/server");
|
|
7395
7800
|
var import_server3 = require("@stackwright-pro/openapi/server");
|
|
7396
7801
|
function buildContext(projectRoot) {
|
|
7397
7802
|
return {
|
|
7398
7803
|
projectRoot,
|
|
7399
|
-
contentOutDir:
|
|
7400
|
-
imagesDir:
|
|
7804
|
+
contentOutDir: import_path14.default.join(projectRoot, "public", "stackwright-content"),
|
|
7805
|
+
imagesDir: import_path14.default.join(projectRoot, "public", "images"),
|
|
7401
7806
|
siteConfig: loadSiteConfig(projectRoot)
|
|
7402
7807
|
};
|
|
7403
7808
|
}
|
|
7404
7809
|
function loadSiteConfig(projectRoot) {
|
|
7405
7810
|
try {
|
|
7406
|
-
const sitePath =
|
|
7407
|
-
return JSON.parse(
|
|
7811
|
+
const sitePath = import_path14.default.join(projectRoot, "public", "stackwright-content", "_site.json");
|
|
7812
|
+
return JSON.parse(import_fs14.default.readFileSync(sitePath, "utf8"));
|
|
7408
7813
|
} catch {
|
|
7409
7814
|
return {};
|
|
7410
7815
|
}
|
|
@@ -7415,8 +7820,8 @@ function formatDuration(startMs) {
|
|
|
7415
7820
|
}
|
|
7416
7821
|
async function tryOssCompile(fn, ctx, extra) {
|
|
7417
7822
|
try {
|
|
7418
|
-
const bsPath =
|
|
7419
|
-
if (!
|
|
7823
|
+
const bsPath = import_path14.default.join(ctx.projectRoot, "node_modules", "@stackwright", "build-scripts");
|
|
7824
|
+
if (!import_fs14.default.existsSync(bsPath)) {
|
|
7420
7825
|
return {
|
|
7421
7826
|
ok: false,
|
|
7422
7827
|
error: `@stackwright/build-scripts not found at ${bsPath}. Is it installed in your project?`
|
|
@@ -7630,14 +8035,14 @@ ${results.join("\n")}`;
|
|
|
7630
8035
|
|
|
7631
8036
|
// src/tools/strip-legacy-integrations.ts
|
|
7632
8037
|
var import_zod21 = require("zod");
|
|
7633
|
-
var
|
|
7634
|
-
var
|
|
8038
|
+
var import_fs15 = require("fs");
|
|
8039
|
+
var import_path15 = require("path");
|
|
7635
8040
|
function handleStripLegacyIntegrations(input) {
|
|
7636
8041
|
const { projectRoot } = input;
|
|
7637
|
-
const ymlPath = (0,
|
|
7638
|
-
const yamlPath = (0,
|
|
7639
|
-
if (!(0,
|
|
7640
|
-
if ((0,
|
|
8042
|
+
const ymlPath = (0, import_path15.join)(projectRoot, "stackwright.yml");
|
|
8043
|
+
const yamlPath = (0, import_path15.join)(projectRoot, "stackwright.yaml");
|
|
8044
|
+
if (!(0, import_fs15.existsSync)(ymlPath)) {
|
|
8045
|
+
if ((0, import_fs15.existsSync)(yamlPath)) {
|
|
7641
8046
|
return {
|
|
7642
8047
|
changed: false,
|
|
7643
8048
|
removedEntries: 0,
|
|
@@ -7650,7 +8055,7 @@ function handleStripLegacyIntegrations(input) {
|
|
|
7650
8055
|
message: "stackwright.yml not found \u2014 no-op."
|
|
7651
8056
|
};
|
|
7652
8057
|
}
|
|
7653
|
-
const raw = (0,
|
|
8058
|
+
const raw = (0, import_fs15.readFileSync)(ymlPath, "utf-8");
|
|
7654
8059
|
const isCRLF = raw.includes("\r\n");
|
|
7655
8060
|
const content = isCRLF ? raw.replace(/\r\n/g, "\n") : raw;
|
|
7656
8061
|
const lines = content.split("\n");
|
|
@@ -7680,7 +8085,7 @@ function handleStripLegacyIntegrations(input) {
|
|
|
7680
8085
|
outputContent += "\n";
|
|
7681
8086
|
}
|
|
7682
8087
|
const outputRaw = isCRLF ? outputContent.replace(/\n/g, "\r\n") : outputContent;
|
|
7683
|
-
(0,
|
|
8088
|
+
(0, import_fs15.writeFileSync)(ymlPath, outputRaw, "utf-8");
|
|
7684
8089
|
return {
|
|
7685
8090
|
changed: true,
|
|
7686
8091
|
removedEntries,
|
|
@@ -7705,15 +8110,15 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
7705
8110
|
|
|
7706
8111
|
// src/tools/emitters.ts
|
|
7707
8112
|
var import_zod22 = require("zod");
|
|
7708
|
-
var
|
|
7709
|
-
var
|
|
8113
|
+
var import_fs16 = require("fs");
|
|
8114
|
+
var import_path16 = require("path");
|
|
7710
8115
|
var import_js_yaml4 = __toESM(require("js-yaml"));
|
|
7711
8116
|
var import_emitters = require("@stackwright-pro/emitters");
|
|
7712
8117
|
function readIntegrations(cwd) {
|
|
7713
|
-
const filePath = (0,
|
|
7714
|
-
if (!(0,
|
|
8118
|
+
const filePath = (0, import_path16.join)(cwd, "stackwright.integrations.yml");
|
|
8119
|
+
if (!(0, import_fs16.existsSync)(filePath)) return [];
|
|
7715
8120
|
try {
|
|
7716
|
-
const raw = import_js_yaml4.default.load((0,
|
|
8121
|
+
const raw = import_js_yaml4.default.load((0, import_fs16.readFileSync)(filePath, "utf-8"));
|
|
7717
8122
|
if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
|
|
7718
8123
|
return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
|
|
7719
8124
|
name: i.name,
|
|
@@ -7724,15 +8129,15 @@ function readIntegrations(cwd) {
|
|
|
7724
8129
|
}
|
|
7725
8130
|
}
|
|
7726
8131
|
function readServiceTokenRefs(cwd) {
|
|
7727
|
-
const servicesDir = (0,
|
|
7728
|
-
if (!(0,
|
|
8132
|
+
const servicesDir = (0, import_path16.join)(cwd, "services");
|
|
8133
|
+
if (!(0, import_fs16.existsSync)(servicesDir)) return [];
|
|
7729
8134
|
try {
|
|
7730
|
-
const files = (0,
|
|
8135
|
+
const files = (0, import_fs16.readdirSync)(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
7731
8136
|
const refs = [];
|
|
7732
8137
|
for (const file of files) {
|
|
7733
8138
|
try {
|
|
7734
8139
|
const raw = import_js_yaml4.default.load(
|
|
7735
|
-
(0,
|
|
8140
|
+
(0, import_fs16.readFileSync)((0, import_path16.join)(servicesDir, file), "utf-8")
|
|
7736
8141
|
);
|
|
7737
8142
|
if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
|
|
7738
8143
|
refs.push({
|
|
@@ -7882,12 +8287,12 @@ This is the third concrete instance of the swp-zf9y deterministic emitter patter
|
|
|
7882
8287
|
|
|
7883
8288
|
// src/tools/list-specs.ts
|
|
7884
8289
|
var import_zod23 = require("zod");
|
|
7885
|
-
var
|
|
7886
|
-
var
|
|
8290
|
+
var import_fs17 = require("fs");
|
|
8291
|
+
var import_path17 = require("path");
|
|
7887
8292
|
var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7888
8293
|
var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
|
|
7889
8294
|
function deriveIntegrationName(fileName) {
|
|
7890
|
-
let name = (0,
|
|
8295
|
+
let name = (0, import_path17.basename)(fileName, (0, import_path17.extname)(fileName));
|
|
7891
8296
|
for (const suffix of STRIP_SUFFIXES) {
|
|
7892
8297
|
if (name.endsWith(suffix)) {
|
|
7893
8298
|
name = name.slice(0, -suffix.length);
|
|
@@ -7905,29 +8310,29 @@ function detectFormat(snippet) {
|
|
|
7905
8310
|
}
|
|
7906
8311
|
function handleListSpecs(input) {
|
|
7907
8312
|
const { projectRoot } = input;
|
|
7908
|
-
const specsDir = (0,
|
|
8313
|
+
const specsDir = (0, import_path17.join)(projectRoot, "specs");
|
|
7909
8314
|
const empty = { projectRoot, specs: [], totalCount: 0 };
|
|
7910
|
-
if (!(0,
|
|
8315
|
+
if (!(0, import_fs17.existsSync)(specsDir)) return empty;
|
|
7911
8316
|
let entries;
|
|
7912
8317
|
try {
|
|
7913
|
-
entries = (0,
|
|
8318
|
+
entries = (0, import_fs17.readdirSync)(specsDir);
|
|
7914
8319
|
} catch {
|
|
7915
8320
|
return empty;
|
|
7916
8321
|
}
|
|
7917
8322
|
const specs = [];
|
|
7918
8323
|
for (const entry of entries) {
|
|
7919
|
-
const fullPath = (0,
|
|
8324
|
+
const fullPath = (0, import_path17.join)(specsDir, entry);
|
|
7920
8325
|
let stat;
|
|
7921
8326
|
try {
|
|
7922
|
-
stat = (0,
|
|
8327
|
+
stat = (0, import_fs17.statSync)(fullPath);
|
|
7923
8328
|
} catch {
|
|
7924
8329
|
continue;
|
|
7925
8330
|
}
|
|
7926
8331
|
if (!stat.isFile()) continue;
|
|
7927
|
-
if (!ALLOWED_EXTS.has((0,
|
|
8332
|
+
if (!ALLOWED_EXTS.has((0, import_path17.extname)(entry).toLowerCase())) continue;
|
|
7928
8333
|
let snippet = "";
|
|
7929
8334
|
try {
|
|
7930
|
-
snippet = (0,
|
|
8335
|
+
snippet = (0, import_fs17.readFileSync)(fullPath, "utf-8").slice(0, 2048);
|
|
7931
8336
|
} catch {
|
|
7932
8337
|
}
|
|
7933
8338
|
specs.push({
|
|
@@ -7959,14 +8364,14 @@ function registerListSpecsTool(server2) {
|
|
|
7959
8364
|
|
|
7960
8365
|
// src/tools/consolidate-integrations.ts
|
|
7961
8366
|
var import_zod24 = require("zod");
|
|
7962
|
-
var
|
|
8367
|
+
var import_fs18 = require("fs");
|
|
7963
8368
|
var import_proper_lockfile2 = require("proper-lockfile");
|
|
7964
|
-
var
|
|
8369
|
+
var import_path18 = require("path");
|
|
7965
8370
|
var import_js_yaml5 = __toESM(require("js-yaml"));
|
|
7966
8371
|
var BASE_MOCK_PORT = 4011;
|
|
7967
8372
|
var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7968
8373
|
function deriveNameFromFilename(filename) {
|
|
7969
|
-
const base = (0,
|
|
8374
|
+
const base = (0, import_path18.basename)(filename, ".json").replace(/^api-config-/, "");
|
|
7970
8375
|
for (const suffix of STRIP_SUFFIXES2) {
|
|
7971
8376
|
if (base.endsWith(suffix)) return base.slice(0, -suffix.length);
|
|
7972
8377
|
}
|
|
@@ -7974,13 +8379,13 @@ function deriveNameFromFilename(filename) {
|
|
|
7974
8379
|
}
|
|
7975
8380
|
function handleConsolidateIntegrations(input) {
|
|
7976
8381
|
const { projectRoot } = input;
|
|
7977
|
-
const artifactsDir = (0,
|
|
7978
|
-
if (!(0,
|
|
8382
|
+
const artifactsDir = (0, import_path18.join)(projectRoot, ".stackwright", "artifacts");
|
|
8383
|
+
if (!(0, import_fs18.existsSync)(artifactsDir)) {
|
|
7979
8384
|
return { written: false, integrationCount: 0, reason: "no per-spec artifacts found" };
|
|
7980
8385
|
}
|
|
7981
8386
|
let entries;
|
|
7982
8387
|
try {
|
|
7983
|
-
entries = (0,
|
|
8388
|
+
entries = (0, import_fs18.readdirSync)(artifactsDir).filter(
|
|
7984
8389
|
(f) => f.startsWith("api-config-") && f.endsWith(".json") && f !== "api-config.json"
|
|
7985
8390
|
);
|
|
7986
8391
|
} catch {
|
|
@@ -7991,14 +8396,22 @@ function handleConsolidateIntegrations(input) {
|
|
|
7991
8396
|
}
|
|
7992
8397
|
entries.sort();
|
|
7993
8398
|
const integrationsByName = /* @__PURE__ */ new Map();
|
|
8399
|
+
const skippedIntegrationsList = [];
|
|
7994
8400
|
entries.forEach((filename, index) => {
|
|
7995
8401
|
let artifact = {};
|
|
7996
8402
|
try {
|
|
7997
|
-
const raw = (0,
|
|
8403
|
+
const raw = (0, import_fs18.readFileSync)((0, import_path18.join)(artifactsDir, filename), "utf-8");
|
|
7998
8404
|
artifact = JSON.parse(raw);
|
|
7999
8405
|
} catch {
|
|
8000
8406
|
}
|
|
8001
8407
|
const name = artifact.integrationName ?? deriveNameFromFilename(filename);
|
|
8408
|
+
if (artifact.skipped && artifact.skipped.length > 0) {
|
|
8409
|
+
skippedIntegrationsList.push({
|
|
8410
|
+
name,
|
|
8411
|
+
reason: artifact.skipped[0]?.reason ?? "explicitly skipped by api-otter"
|
|
8412
|
+
});
|
|
8413
|
+
return;
|
|
8414
|
+
}
|
|
8002
8415
|
const spec = artifact.integrationSpec ?? artifact.specPath ?? `./specs/${name}.yaml`;
|
|
8003
8416
|
const mockUrl = artifact.mockUrl ?? `http://localhost:${BASE_MOCK_PORT + index}`;
|
|
8004
8417
|
const baseUrl = artifact.baseUrl ?? "";
|
|
@@ -8019,21 +8432,21 @@ function handleConsolidateIntegrations(input) {
|
|
|
8019
8432
|
integrationsByName.set(name, entry);
|
|
8020
8433
|
});
|
|
8021
8434
|
const integrations = Array.from(integrationsByName.values());
|
|
8022
|
-
const dedupedCount = entries.length - integrations.length;
|
|
8023
|
-
const integrationsYmlPath = (0,
|
|
8435
|
+
const dedupedCount = entries.length - skippedIntegrationsList.length - integrations.length;
|
|
8436
|
+
const integrationsYmlPath = (0, import_path18.join)(projectRoot, "stackwright.integrations.yml");
|
|
8024
8437
|
let existing = {};
|
|
8025
|
-
if ((0,
|
|
8438
|
+
if ((0, import_fs18.existsSync)(integrationsYmlPath)) {
|
|
8026
8439
|
try {
|
|
8027
|
-
const raw = (0,
|
|
8440
|
+
const raw = (0, import_fs18.readFileSync)(integrationsYmlPath, "utf-8");
|
|
8028
8441
|
existing = import_js_yaml5.default.load(raw) ?? {};
|
|
8029
8442
|
} catch {
|
|
8030
8443
|
existing = {};
|
|
8031
8444
|
}
|
|
8032
8445
|
}
|
|
8033
8446
|
const merged = { ...existing, integrations };
|
|
8034
|
-
(0,
|
|
8035
|
-
if (!(0,
|
|
8036
|
-
(0,
|
|
8447
|
+
(0, import_fs18.mkdirSync)((0, import_path18.join)(projectRoot), { recursive: true });
|
|
8448
|
+
if (!(0, import_fs18.existsSync)(integrationsYmlPath)) {
|
|
8449
|
+
(0, import_fs18.writeFileSync)(integrationsYmlPath, "", "utf-8");
|
|
8037
8450
|
}
|
|
8038
8451
|
const release = (0, import_proper_lockfile2.lockSync)(integrationsYmlPath, {
|
|
8039
8452
|
realpath: false,
|
|
@@ -8043,7 +8456,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8043
8456
|
const header = `# stackwright.integrations.yml \u2014 Auto-generated by stackwright_pro_consolidate_integrations
|
|
8044
8457
|
`;
|
|
8045
8458
|
const body = import_js_yaml5.default.dump(merged, { lineWidth: 120, noRefs: true });
|
|
8046
|
-
(0,
|
|
8459
|
+
(0, import_fs18.writeFileSync)(integrationsYmlPath, header + body, "utf-8");
|
|
8047
8460
|
} finally {
|
|
8048
8461
|
release();
|
|
8049
8462
|
}
|
|
@@ -8051,7 +8464,11 @@ function handleConsolidateIntegrations(input) {
|
|
|
8051
8464
|
written: true,
|
|
8052
8465
|
integrationCount: integrations.length,
|
|
8053
8466
|
path: "stackwright.integrations.yml",
|
|
8054
|
-
...dedupedCount > 0 ? { dedupedCount } : {}
|
|
8467
|
+
...dedupedCount > 0 ? { dedupedCount } : {},
|
|
8468
|
+
...skippedIntegrationsList.length > 0 ? {
|
|
8469
|
+
skippedCount: skippedIntegrationsList.length,
|
|
8470
|
+
skippedIntegrations: skippedIntegrationsList
|
|
8471
|
+
} : {}
|
|
8055
8472
|
};
|
|
8056
8473
|
}
|
|
8057
8474
|
function registerConsolidateIntegrationsTool(server2) {
|
|
@@ -8125,7 +8542,7 @@ var package_default = {
|
|
|
8125
8542
|
"test:coverage": "vitest run --coverage"
|
|
8126
8543
|
},
|
|
8127
8544
|
name: "@stackwright-pro/mcp",
|
|
8128
|
-
version: "0.2.0-alpha.
|
|
8545
|
+
version: "0.2.0-alpha.109",
|
|
8129
8546
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
8130
8547
|
license: "SEE LICENSE IN LICENSE",
|
|
8131
8548
|
main: "./dist/server.js",
|