@stackwright-pro/mcp 0.2.0-alpha.107 → 0.2.0-alpha.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.js +8 -8
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +8 -8
- 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 +618 -226
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +565 -173
- 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,6 +4368,61 @@ 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 = {
|
|
4140
4427
|
workflow: (artifact2) => {
|
|
4141
4428
|
const workflowData = artifact2["workflow"];
|
|
@@ -4186,33 +4473,60 @@ function _validateArtifactInner(input) {
|
|
|
4186
4473
|
return { success: true };
|
|
4187
4474
|
},
|
|
4188
4475
|
// swp-4071 — opt-in cross-reference validator for the services phase.
|
|
4189
|
-
//
|
|
4190
|
-
//
|
|
4476
|
+
// Collects serviceHooks from ALL workflow-config-*.json artifacts (swp-x8sm fan-out)
|
|
4477
|
+
// plus the legacy single-file workflow-config.json (backward compat).
|
|
4478
|
+
// If any workflow declared serviceHooks, every ref must resolve to a flow or
|
|
4479
|
+
// state-machine in this services-config artifact.
|
|
4191
4480
|
// Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
|
|
4192
4481
|
services: (artifact2) => {
|
|
4193
|
-
const
|
|
4194
|
-
|
|
4195
|
-
|
|
4482
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
4483
|
+
const workflowArtifactFiles = [];
|
|
4484
|
+
if ((0, import_fs8.existsSync)(artifactsDir)) {
|
|
4485
|
+
try {
|
|
4486
|
+
const entries = (0, import_fs8.readdirSync)(artifactsDir);
|
|
4487
|
+
for (const entry of entries) {
|
|
4488
|
+
if (entry.startsWith("workflow-config-") && entry.endsWith(".json")) {
|
|
4489
|
+
workflowArtifactFiles.push((0, import_path8.join)(artifactsDir, entry));
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
} catch {
|
|
4493
|
+
}
|
|
4196
4494
|
}
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
}
|
|
4495
|
+
const legacyPath = (0, import_path8.join)(artifactsDir, "workflow-config.json");
|
|
4496
|
+
if ((0, import_fs8.existsSync)(legacyPath)) {
|
|
4497
|
+
workflowArtifactFiles.push(legacyPath);
|
|
4498
|
+
}
|
|
4499
|
+
if (workflowArtifactFiles.length === 0) {
|
|
4201
4500
|
return { success: true };
|
|
4202
4501
|
}
|
|
4203
|
-
const declaredHooks =
|
|
4204
|
-
|
|
4502
|
+
const declaredHooks = [];
|
|
4503
|
+
for (const filePath of workflowArtifactFiles) {
|
|
4504
|
+
try {
|
|
4505
|
+
const parsed = JSON.parse((0, import_fs8.readFileSync)(filePath, "utf-8"));
|
|
4506
|
+
if (Array.isArray(parsed.serviceHooks)) {
|
|
4507
|
+
declaredHooks.push(...parsed.serviceHooks);
|
|
4508
|
+
}
|
|
4509
|
+
} catch {
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
const seenRefs = /* @__PURE__ */ new Set();
|
|
4513
|
+
const uniqueHooks = declaredHooks.filter((h) => {
|
|
4514
|
+
if (seenRefs.has(h.ref)) return false;
|
|
4515
|
+
seenRefs.add(h.ref);
|
|
4516
|
+
return true;
|
|
4517
|
+
});
|
|
4518
|
+
if (uniqueHooks.length === 0) {
|
|
4205
4519
|
return { success: true };
|
|
4206
4520
|
}
|
|
4207
4521
|
const flows = artifact2["flows"] ?? [];
|
|
4208
4522
|
const workflows = artifact2["workflows"] ?? [];
|
|
4209
4523
|
const flowNames = /* @__PURE__ */ new Set([...flows.map((f) => f.name), ...workflows.map((w) => w.name)]);
|
|
4210
|
-
const unresolved =
|
|
4524
|
+
const unresolved = uniqueHooks.filter((h) => !flowNames.has(h.ref));
|
|
4211
4525
|
if (unresolved.length > 0) {
|
|
4212
4526
|
return {
|
|
4213
4527
|
success: false,
|
|
4214
4528
|
error: {
|
|
4215
|
-
message: `Workflow declared ${
|
|
4529
|
+
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
4530
|
}
|
|
4217
4531
|
};
|
|
4218
4532
|
}
|
|
@@ -4278,11 +4592,55 @@ function _validateArtifactInner(input) {
|
|
|
4278
4592
|
return { text: JSON.stringify(result), isError: false };
|
|
4279
4593
|
}
|
|
4280
4594
|
}
|
|
4595
|
+
if (phase === "api") {
|
|
4596
|
+
const apiAuthResult = validateApiIntegration(artifact, input.integrationName);
|
|
4597
|
+
if (!apiAuthResult.valid) {
|
|
4598
|
+
const result = {
|
|
4599
|
+
valid: false,
|
|
4600
|
+
phase,
|
|
4601
|
+
violation: "schema-mismatch",
|
|
4602
|
+
retryPrompt: apiAuthResult.retryPrompt
|
|
4603
|
+
};
|
|
4604
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
if (phase === "workflow" && input.workflowName !== void 0) {
|
|
4608
|
+
if (!isValidSlug(input.workflowName)) {
|
|
4609
|
+
const result = {
|
|
4610
|
+
valid: false,
|
|
4611
|
+
phase,
|
|
4612
|
+
violation: "schema-mismatch",
|
|
4613
|
+
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".`
|
|
4614
|
+
};
|
|
4615
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
let authOrphanWarning;
|
|
4619
|
+
if (phase === "auth") {
|
|
4620
|
+
const authCfg = artifact["authConfig"];
|
|
4621
|
+
const routes = authCfg?.["protectedRoutes"];
|
|
4622
|
+
if (Array.isArray(routes) && routes.length > 0) {
|
|
4623
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
4624
|
+
const knownSlugs = extractManifestSlugs(artifactsDir);
|
|
4625
|
+
const orphanPatterns = [];
|
|
4626
|
+
for (const route of routes) {
|
|
4627
|
+
const pattern = typeof route === "string" ? route : route.pattern ?? "";
|
|
4628
|
+
if (!pattern) continue;
|
|
4629
|
+
const baseSlug = extractPatternBaseSlug(pattern);
|
|
4630
|
+
if (baseSlug && !knownSlugs.has(baseSlug)) {
|
|
4631
|
+
orphanPatterns.push(pattern);
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
if (orphanPatterns.length > 0) {
|
|
4635
|
+
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.`;
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4281
4639
|
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,
|
|
4640
|
+
const artifactsDir = (0, import_path8.join)(cwd, ".stackwright", "artifacts");
|
|
4641
|
+
(0, import_fs8.mkdirSync)(artifactsDir, { recursive: true });
|
|
4642
|
+
const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : phase === "workflow" && input.workflowName ? `workflow-config-${input.workflowName}.json` : PHASE_ARTIFACT[phase];
|
|
4643
|
+
const artifactPath = (0, import_path8.join)(artifactsDir, artifactFile);
|
|
4286
4644
|
const serialized = JSON.stringify(artifact, null, 2) + "\n";
|
|
4287
4645
|
const artifactBytes = Buffer.from(serialized, "utf-8");
|
|
4288
4646
|
safeWriteSync(artifactPath, serialized);
|
|
@@ -4305,7 +4663,8 @@ function _validateArtifactInner(input) {
|
|
|
4305
4663
|
valid: true,
|
|
4306
4664
|
phase,
|
|
4307
4665
|
artifactPath,
|
|
4308
|
-
summary: `Wrote ${artifactFile} (keys: ${topKeys}${Object.keys(artifact).length > 5 ? ", ..." : ""})${signed ? " [signed]" : ""}
|
|
4666
|
+
summary: `Wrote ${artifactFile} (keys: ${topKeys}${Object.keys(artifact).length > 5 ? ", ..." : ""})${signed ? " [signed]" : ""}`,
|
|
4667
|
+
...authOrphanWarning ? { retryPrompt: authOrphanWarning } : {}
|
|
4309
4668
|
};
|
|
4310
4669
|
return { text: JSON.stringify(result), isError: false };
|
|
4311
4670
|
} catch (err) {
|
|
@@ -4501,10 +4860,10 @@ function registerPipelineTools(server2) {
|
|
|
4501
4860
|
isError: true
|
|
4502
4861
|
};
|
|
4503
4862
|
}
|
|
4504
|
-
const questionsDir = (0,
|
|
4505
|
-
(0,
|
|
4506
|
-
const outPath = (0,
|
|
4507
|
-
(0,
|
|
4863
|
+
const questionsDir = (0, import_path8.join)(process.cwd(), ".stackwright", "questions");
|
|
4864
|
+
(0, import_fs8.mkdirSync)(questionsDir, { recursive: true });
|
|
4865
|
+
const outPath = (0, import_path8.join)(questionsDir, `${phase}.json`);
|
|
4866
|
+
(0, import_fs8.writeFileSync)(
|
|
4508
4867
|
outPath,
|
|
4509
4868
|
JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
4510
4869
|
);
|
|
@@ -4543,15 +4902,19 @@ function registerPipelineTools(server2) {
|
|
|
4543
4902
|
),
|
|
4544
4903
|
integrationName: import_zod13.z.string().optional().describe(
|
|
4545
4904
|
'When phase="api": write to api-config-<integrationName>.json instead of api-config.json. Enables concurrent per-spec fan-out without collision (swp-530y).'
|
|
4905
|
+
),
|
|
4906
|
+
workflowName: import_zod13.z.string().optional().describe(
|
|
4907
|
+
`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
4908
|
)
|
|
4547
4909
|
},
|
|
4548
|
-
async ({ phase, responseText, artifact, integrationName }) => {
|
|
4910
|
+
async ({ phase, responseText, artifact, integrationName, workflowName }) => {
|
|
4549
4911
|
if (artifact) {
|
|
4550
4912
|
return res(
|
|
4551
4913
|
handleValidateArtifact({
|
|
4552
4914
|
phase,
|
|
4553
4915
|
artifact,
|
|
4554
|
-
...integrationName ? { integrationName } : {}
|
|
4916
|
+
...integrationName ? { integrationName } : {},
|
|
4917
|
+
...workflowName ? { workflowName } : {}
|
|
4555
4918
|
})
|
|
4556
4919
|
);
|
|
4557
4920
|
}
|
|
@@ -4559,7 +4922,8 @@ function registerPipelineTools(server2) {
|
|
|
4559
4922
|
handleValidateArtifact({
|
|
4560
4923
|
phase,
|
|
4561
4924
|
responseText: responseText ?? "",
|
|
4562
|
-
...integrationName ? { integrationName } : {}
|
|
4925
|
+
...integrationName ? { integrationName } : {},
|
|
4926
|
+
...workflowName ? { workflowName } : {}
|
|
4563
4927
|
})
|
|
4564
4928
|
);
|
|
4565
4929
|
}
|
|
@@ -4591,8 +4955,8 @@ function registerPipelineTools(server2) {
|
|
|
4591
4955
|
|
|
4592
4956
|
// src/tools/safe-write.ts
|
|
4593
4957
|
var import_zod14 = require("zod");
|
|
4594
|
-
var
|
|
4595
|
-
var
|
|
4958
|
+
var import_fs9 = require("fs");
|
|
4959
|
+
var import_path9 = require("path");
|
|
4596
4960
|
var import_telemetry2 = require("@stackwright-pro/telemetry");
|
|
4597
4961
|
var OTTER_WRITE_ALLOWLISTS = {
|
|
4598
4962
|
"stackwright-pro-designer-otter": [
|
|
@@ -4604,6 +4968,11 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
4604
4968
|
prefix: "stackwright.theme.",
|
|
4605
4969
|
suffix: ".yml",
|
|
4606
4970
|
description: "Theme config YAML (merged at build time)"
|
|
4971
|
+
},
|
|
4972
|
+
{
|
|
4973
|
+
prefix: "stackwright.yml",
|
|
4974
|
+
suffix: "",
|
|
4975
|
+
description: "Stackwright config (themeName + customTheme writeback)"
|
|
4607
4976
|
}
|
|
4608
4977
|
],
|
|
4609
4978
|
"stackwright-pro-auth-otter": [
|
|
@@ -4658,6 +5027,17 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
4658
5027
|
prefix: ".stackwright/artifacts/",
|
|
4659
5028
|
suffix: ".json",
|
|
4660
5029
|
description: "Scaffold manifest artifact"
|
|
5030
|
+
},
|
|
5031
|
+
// swp-89w5: scaffold-otter initialises mock backend wiring via stackwright_pro_emit_env_local
|
|
5032
|
+
{
|
|
5033
|
+
prefix: ".env.local",
|
|
5034
|
+
suffix: "",
|
|
5035
|
+
description: "Dotenv runtime config (mock backend wiring: NEXT_PUBLIC_MOCK_BACKEND + per-integration mock keys)"
|
|
5036
|
+
},
|
|
5037
|
+
{
|
|
5038
|
+
prefix: ".env.local.example",
|
|
5039
|
+
suffix: "",
|
|
5040
|
+
description: "Dotenv template (schema for real production values)"
|
|
4661
5041
|
}
|
|
4662
5042
|
],
|
|
4663
5043
|
"stackwright-pro-api-otter": [
|
|
@@ -4758,7 +5138,7 @@ function getMaxBytesForPath(filePath) {
|
|
|
4758
5138
|
}
|
|
4759
5139
|
var PAGE_REGISTRY_FILE = ".stackwright/page-registry.json";
|
|
4760
5140
|
function extractPageSlug(filePath) {
|
|
4761
|
-
const match = (0,
|
|
5141
|
+
const match = (0, import_path9.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
|
|
4762
5142
|
return match?.[1] ?? null;
|
|
4763
5143
|
}
|
|
4764
5144
|
function extractContentTypes(yamlContent) {
|
|
@@ -4774,27 +5154,27 @@ function extractContentTypes(yamlContent) {
|
|
|
4774
5154
|
return types.length > 0 ? types : ["unknown"];
|
|
4775
5155
|
}
|
|
4776
5156
|
function readPageRegistry(cwd) {
|
|
4777
|
-
const regPath = (0,
|
|
4778
|
-
if (!(0,
|
|
5157
|
+
const regPath = (0, import_path9.join)(cwd, PAGE_REGISTRY_FILE);
|
|
5158
|
+
if (!(0, import_fs9.existsSync)(regPath)) return {};
|
|
4779
5159
|
try {
|
|
4780
|
-
const stat = (0,
|
|
5160
|
+
const stat = (0, import_fs9.lstatSync)(regPath);
|
|
4781
5161
|
if (stat.isSymbolicLink()) return {};
|
|
4782
|
-
return JSON.parse((0,
|
|
5162
|
+
return JSON.parse((0, import_fs9.readFileSync)(regPath, "utf-8"));
|
|
4783
5163
|
} catch {
|
|
4784
5164
|
return {};
|
|
4785
5165
|
}
|
|
4786
5166
|
}
|
|
4787
5167
|
function writePageRegistry(cwd, registry) {
|
|
4788
|
-
const dir = (0,
|
|
4789
|
-
(0,
|
|
4790
|
-
const regPath = (0,
|
|
4791
|
-
if ((0,
|
|
4792
|
-
const stat = (0,
|
|
5168
|
+
const dir = (0, import_path9.join)(cwd, ".stackwright");
|
|
5169
|
+
(0, import_fs9.mkdirSync)(dir, { recursive: true });
|
|
5170
|
+
const regPath = (0, import_path9.join)(cwd, PAGE_REGISTRY_FILE);
|
|
5171
|
+
if ((0, import_fs9.existsSync)(regPath)) {
|
|
5172
|
+
const stat = (0, import_fs9.lstatSync)(regPath);
|
|
4793
5173
|
if (stat.isSymbolicLink()) {
|
|
4794
5174
|
throw new Error("Refusing to write page registry through symlink");
|
|
4795
5175
|
}
|
|
4796
5176
|
}
|
|
4797
|
-
(0,
|
|
5177
|
+
(0, import_fs9.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
|
|
4798
5178
|
}
|
|
4799
5179
|
function checkPageOwnership(cwd, slug, callerOtter) {
|
|
4800
5180
|
const registry = readPageRegistry(cwd);
|
|
@@ -4813,11 +5193,11 @@ function stripNextjsBracketSegments(path5) {
|
|
|
4813
5193
|
return path5.replace(NEXTJS_BRACKET_SEGMENT_RE, "");
|
|
4814
5194
|
}
|
|
4815
5195
|
function checkPathAllowed(callerOtter, filePath) {
|
|
4816
|
-
const normalized = (0,
|
|
5196
|
+
const normalized = (0, import_path9.normalize)(filePath);
|
|
4817
5197
|
if (stripNextjsBracketSegments(normalized).includes("..")) {
|
|
4818
5198
|
return { allowed: false, error: 'Path traversal detected: ".." segments are not allowed' };
|
|
4819
5199
|
}
|
|
4820
|
-
if ((0,
|
|
5200
|
+
if ((0, import_path9.isAbsolute)(normalized)) {
|
|
4821
5201
|
return {
|
|
4822
5202
|
allowed: false,
|
|
4823
5203
|
error: "Absolute paths are not allowed \u2014 use paths relative to project root"
|
|
@@ -4959,11 +5339,11 @@ function _safeWriteInner(input) {
|
|
|
4959
5339
|
};
|
|
4960
5340
|
return { text: JSON.stringify(result), isError: true };
|
|
4961
5341
|
}
|
|
4962
|
-
const normalized = (0,
|
|
4963
|
-
const fullPath = (0,
|
|
4964
|
-
if ((0,
|
|
5342
|
+
const normalized = (0, import_path9.normalize)(filePath);
|
|
5343
|
+
const fullPath = (0, import_path9.join)(cwd, normalized);
|
|
5344
|
+
if ((0, import_fs9.existsSync)(fullPath)) {
|
|
4965
5345
|
try {
|
|
4966
|
-
const stat = (0,
|
|
5346
|
+
const stat = (0, import_fs9.lstatSync)(fullPath);
|
|
4967
5347
|
if (stat.isSymbolicLink()) {
|
|
4968
5348
|
const result = {
|
|
4969
5349
|
success: false,
|
|
@@ -5004,9 +5384,9 @@ function _safeWriteInner(input) {
|
|
|
5004
5384
|
}
|
|
5005
5385
|
try {
|
|
5006
5386
|
if (createDirectories) {
|
|
5007
|
-
(0,
|
|
5387
|
+
(0, import_fs9.mkdirSync)((0, import_path9.dirname)(fullPath), { recursive: true });
|
|
5008
5388
|
}
|
|
5009
|
-
(0,
|
|
5389
|
+
(0, import_fs9.writeFileSync)(fullPath, content, { encoding: "utf-8" });
|
|
5010
5390
|
if (pageSlug) {
|
|
5011
5391
|
try {
|
|
5012
5392
|
const registry = readPageRegistry(cwd);
|
|
@@ -5091,8 +5471,8 @@ function registerSafeWriteTools(server2) {
|
|
|
5091
5471
|
|
|
5092
5472
|
// src/tools/auth.ts
|
|
5093
5473
|
var import_zod15 = require("zod");
|
|
5094
|
-
var
|
|
5095
|
-
var
|
|
5474
|
+
var import_fs10 = require("fs");
|
|
5475
|
+
var import_path10 = require("path");
|
|
5096
5476
|
function buildHierarchy(roles) {
|
|
5097
5477
|
const h = {};
|
|
5098
5478
|
for (let i = 0; i < roles.length - 1; i++) {
|
|
@@ -5119,9 +5499,9 @@ ${indent} requiredRole: ${r.requiredRole}`).join("\n");
|
|
|
5119
5499
|
}
|
|
5120
5500
|
function detectNextMajorVersion(cwd) {
|
|
5121
5501
|
try {
|
|
5122
|
-
const pkgPath = (0,
|
|
5123
|
-
if (!(0,
|
|
5124
|
-
const pkg = JSON.parse((0,
|
|
5502
|
+
const pkgPath = (0, import_path10.join)(cwd, "package.json");
|
|
5503
|
+
if (!(0, import_fs10.existsSync)(pkgPath)) return null;
|
|
5504
|
+
const pkg = JSON.parse((0, import_fs10.readFileSync)(pkgPath, "utf8"));
|
|
5125
5505
|
const nextVersion = pkg.dependencies?.next ?? pkg.devDependencies?.next;
|
|
5126
5506
|
if (!nextVersion) return null;
|
|
5127
5507
|
const match = nextVersion.match(/(\d+)/);
|
|
@@ -5785,7 +6165,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5785
6165
|
auditRetentionDays,
|
|
5786
6166
|
normalizedRoutes
|
|
5787
6167
|
);
|
|
5788
|
-
(0,
|
|
6168
|
+
(0, import_fs10.writeFileSync)((0, import_path10.join)(cwd, conventionFile), authFileContent, "utf8");
|
|
5789
6169
|
filesWritten.push(conventionFile);
|
|
5790
6170
|
} catch (err) {
|
|
5791
6171
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5805,12 +6185,12 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5805
6185
|
if (!devOnly) {
|
|
5806
6186
|
try {
|
|
5807
6187
|
const envBlock = generateEnvBlock(effectiveMethod, params);
|
|
5808
|
-
const envPath = (0,
|
|
5809
|
-
if ((0,
|
|
5810
|
-
const existing = (0,
|
|
5811
|
-
(0,
|
|
6188
|
+
const envPath = (0, import_path10.join)(cwd, ".env.example");
|
|
6189
|
+
if ((0, import_fs10.existsSync)(envPath)) {
|
|
6190
|
+
const existing = (0, import_fs10.readFileSync)(envPath, "utf8");
|
|
6191
|
+
(0, import_fs10.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
|
|
5812
6192
|
} else {
|
|
5813
|
-
(0,
|
|
6193
|
+
(0, import_fs10.writeFileSync)(envPath, envBlock, "utf8");
|
|
5814
6194
|
}
|
|
5815
6195
|
filesWritten.push(".env.example");
|
|
5816
6196
|
} catch (err) {
|
|
@@ -5828,10 +6208,10 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5828
6208
|
}
|
|
5829
6209
|
if (devOnly) {
|
|
5830
6210
|
try {
|
|
5831
|
-
const mockAuthDir = (0,
|
|
5832
|
-
(0,
|
|
6211
|
+
const mockAuthDir = (0, import_path10.join)(cwd, "lib");
|
|
6212
|
+
(0, import_fs10.mkdirSync)(mockAuthDir, { recursive: true });
|
|
5833
6213
|
const mockAuthContent = generateMockAuthContent(roles, mockUsers);
|
|
5834
|
-
(0,
|
|
6214
|
+
(0, import_fs10.writeFileSync)((0, import_path10.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
|
|
5835
6215
|
filesWritten.push("lib/mock-auth.ts");
|
|
5836
6216
|
} catch (err) {
|
|
5837
6217
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5849,11 +6229,11 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5849
6229
|
};
|
|
5850
6230
|
}
|
|
5851
6231
|
try {
|
|
5852
|
-
const pkgPath = (0,
|
|
5853
|
-
if ((0,
|
|
5854
|
-
const existingPkg = (0,
|
|
6232
|
+
const pkgPath = (0, import_path10.join)(cwd, "package.json");
|
|
6233
|
+
if ((0, import_fs10.existsSync)(pkgPath)) {
|
|
6234
|
+
const existingPkg = (0, import_fs10.readFileSync)(pkgPath, "utf8");
|
|
5855
6235
|
const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
|
|
5856
|
-
(0,
|
|
6236
|
+
(0, import_fs10.writeFileSync)(pkgPath, updatedPkg, "utf8");
|
|
5857
6237
|
filesWritten.push("package.json");
|
|
5858
6238
|
}
|
|
5859
6239
|
} catch (err) {
|
|
@@ -5894,7 +6274,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5894
6274
|
normalizedRoutes,
|
|
5895
6275
|
useProxy
|
|
5896
6276
|
);
|
|
5897
|
-
(0,
|
|
6277
|
+
(0, import_fs10.writeFileSync)((0, import_path10.join)(cwd, "stackwright.auth.yml"), authYaml, "utf8");
|
|
5898
6278
|
filesWritten.push("stackwright.auth.yml");
|
|
5899
6279
|
} catch (err) {
|
|
5900
6280
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -6007,8 +6387,8 @@ function registerAuthTools(server2) {
|
|
|
6007
6387
|
|
|
6008
6388
|
// src/integrity.ts
|
|
6009
6389
|
var import_crypto4 = require("crypto");
|
|
6010
|
-
var
|
|
6011
|
-
var
|
|
6390
|
+
var import_fs11 = require("fs");
|
|
6391
|
+
var import_path11 = require("path");
|
|
6012
6392
|
var _checksums = /* @__PURE__ */ new Map([
|
|
6013
6393
|
[
|
|
6014
6394
|
"stackwright-pro-api-otter.json",
|
|
@@ -6016,11 +6396,11 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6016
6396
|
],
|
|
6017
6397
|
[
|
|
6018
6398
|
"stackwright-pro-auth-otter.json",
|
|
6019
|
-
"
|
|
6399
|
+
"69fa03c5532ef617c86fe7af41e8174c168d282baf602b1b269a7e933ea86b7c"
|
|
6020
6400
|
],
|
|
6021
6401
|
[
|
|
6022
6402
|
"stackwright-pro-dashboard-otter.json",
|
|
6023
|
-
"
|
|
6403
|
+
"f55081bf4d6545e5435128919f6f9233956ba37671fc9f85300f240ad2497ab6"
|
|
6024
6404
|
],
|
|
6025
6405
|
[
|
|
6026
6406
|
"stackwright-pro-data-otter.json",
|
|
@@ -6036,19 +6416,19 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6036
6416
|
],
|
|
6037
6417
|
[
|
|
6038
6418
|
"stackwright-pro-foreman-otter.json",
|
|
6039
|
-
"
|
|
6419
|
+
"88c4101154d44a7c5470ded13562402a39323ee570ed3a90a73b8d85d7fd2817"
|
|
6040
6420
|
],
|
|
6041
6421
|
[
|
|
6042
6422
|
"stackwright-pro-form-wizard-otter.json",
|
|
6043
|
-
"
|
|
6423
|
+
"cb350297a0068ead2424e703e8c775d91c0b87249ea6904f43b903baf8a84b79"
|
|
6044
6424
|
],
|
|
6045
6425
|
[
|
|
6046
6426
|
"stackwright-pro-geo-otter.json",
|
|
6047
|
-
"
|
|
6427
|
+
"3032fbd27de36c0cdc0e19e46a32d771208d8e86714482f5a368d46342c8317a"
|
|
6048
6428
|
],
|
|
6049
6429
|
[
|
|
6050
6430
|
"stackwright-pro-page-otter.json",
|
|
6051
|
-
"
|
|
6431
|
+
"cc0db24b00f0130f8b4ec8d385c73ea1bcf5d57ba45aca6b986bcbd5da328930"
|
|
6052
6432
|
],
|
|
6053
6433
|
[
|
|
6054
6434
|
"stackwright-pro-polish-otter.json",
|
|
@@ -6060,7 +6440,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6060
6440
|
],
|
|
6061
6441
|
[
|
|
6062
6442
|
"stackwright-pro-scaffold-otter.json",
|
|
6063
|
-
"
|
|
6443
|
+
"90272bab51bd8ed9e25c0fb6481cdd4252386850f64192edfe1e2eff44166e2d"
|
|
6064
6444
|
],
|
|
6065
6445
|
[
|
|
6066
6446
|
"stackwright-pro-theme-otter.json",
|
|
@@ -6068,7 +6448,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6068
6448
|
],
|
|
6069
6449
|
[
|
|
6070
6450
|
"stackwright-services-otter.json",
|
|
6071
|
-
"
|
|
6451
|
+
"4351348806aeb98404a2bacb044209ca411be5e35e2f2c3d03eb4d28a07fde78"
|
|
6072
6452
|
]
|
|
6073
6453
|
]);
|
|
6074
6454
|
Object.freeze(_checksums);
|
|
@@ -6091,14 +6471,14 @@ function safeEqual(a, b) {
|
|
|
6091
6471
|
return (0, import_crypto4.timingSafeEqual)(Buffer.from(a, "utf8"), Buffer.from(b, "utf8"));
|
|
6092
6472
|
}
|
|
6093
6473
|
function verifyOtterFile(filePath) {
|
|
6094
|
-
const filename = (0,
|
|
6474
|
+
const filename = (0, import_path11.basename)(filePath);
|
|
6095
6475
|
const expected = CANONICAL_CHECKSUMS.get(filename);
|
|
6096
6476
|
if (expected === void 0) {
|
|
6097
6477
|
return { verified: false, filename, error: `Unknown otter file: not in canonical set` };
|
|
6098
6478
|
}
|
|
6099
6479
|
let stat;
|
|
6100
6480
|
try {
|
|
6101
|
-
stat = (0,
|
|
6481
|
+
stat = (0, import_fs11.lstatSync)(filePath);
|
|
6102
6482
|
} catch (err) {
|
|
6103
6483
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6104
6484
|
return { verified: false, filename, error: `Cannot stat file: ${msg}` };
|
|
@@ -6116,7 +6496,7 @@ function verifyOtterFile(filePath) {
|
|
|
6116
6496
|
}
|
|
6117
6497
|
let raw;
|
|
6118
6498
|
try {
|
|
6119
|
-
raw = (0,
|
|
6499
|
+
raw = (0, import_fs11.readFileSync)(filePath);
|
|
6120
6500
|
} catch (err) {
|
|
6121
6501
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6122
6502
|
return { verified: false, filename, error: `Cannot read file: ${msg}` };
|
|
@@ -6166,7 +6546,7 @@ function verifyAllOtters(otterDir) {
|
|
|
6166
6546
|
const unknown = [];
|
|
6167
6547
|
let entries;
|
|
6168
6548
|
try {
|
|
6169
|
-
entries = (0,
|
|
6549
|
+
entries = (0, import_fs11.readdirSync)(otterDir);
|
|
6170
6550
|
} catch (err) {
|
|
6171
6551
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6172
6552
|
return {
|
|
@@ -6177,9 +6557,9 @@ function verifyAllOtters(otterDir) {
|
|
|
6177
6557
|
}
|
|
6178
6558
|
const otterFiles = entries.filter((f) => f.endsWith("-otter.json"));
|
|
6179
6559
|
for (const filename of otterFiles) {
|
|
6180
|
-
const filePath = (0,
|
|
6560
|
+
const filePath = (0, import_path11.join)(otterDir, filename);
|
|
6181
6561
|
try {
|
|
6182
|
-
if ((0,
|
|
6562
|
+
if ((0, import_fs11.lstatSync)(filePath).isSymbolicLink()) {
|
|
6183
6563
|
failed.push({ filename, error: "Skipped: symlink" });
|
|
6184
6564
|
continue;
|
|
6185
6565
|
}
|
|
@@ -6205,9 +6585,9 @@ var DEFAULT_SEARCH_PATHS = ["node_modules/@stackwright-pro/otters/src/", "packag
|
|
|
6205
6585
|
function resolveOtterDir() {
|
|
6206
6586
|
const cwd = process.cwd();
|
|
6207
6587
|
for (const relative2 of DEFAULT_SEARCH_PATHS) {
|
|
6208
|
-
const candidate = (0,
|
|
6588
|
+
const candidate = (0, import_path11.join)(cwd, relative2);
|
|
6209
6589
|
try {
|
|
6210
|
-
(0,
|
|
6590
|
+
(0, import_fs11.lstatSync)(candidate);
|
|
6211
6591
|
return candidate;
|
|
6212
6592
|
} catch {
|
|
6213
6593
|
}
|
|
@@ -6286,13 +6666,13 @@ function registerIntegrityTools(server2) {
|
|
|
6286
6666
|
|
|
6287
6667
|
// src/tools/domain.ts
|
|
6288
6668
|
var import_zod16 = require("zod");
|
|
6289
|
-
var
|
|
6290
|
-
var
|
|
6669
|
+
var import_fs12 = require("fs");
|
|
6670
|
+
var import_path12 = require("path");
|
|
6291
6671
|
function handleListCollections(input) {
|
|
6292
6672
|
const cwd = input._cwd ?? process.cwd();
|
|
6293
6673
|
const sources = [
|
|
6294
6674
|
{
|
|
6295
|
-
path: (0,
|
|
6675
|
+
path: (0, import_path12.join)(cwd, ".stackwright", "artifacts", "data-config.json"),
|
|
6296
6676
|
source: "data-config.json",
|
|
6297
6677
|
parse: (raw) => {
|
|
6298
6678
|
const parsed = JSON.parse(raw);
|
|
@@ -6303,15 +6683,15 @@ function handleListCollections(input) {
|
|
|
6303
6683
|
}
|
|
6304
6684
|
},
|
|
6305
6685
|
{
|
|
6306
|
-
path: (0,
|
|
6686
|
+
path: (0, import_path12.join)(cwd, "stackwright.yml"),
|
|
6307
6687
|
source: "stackwright.yml",
|
|
6308
6688
|
parse: extractCollectionsFromYaml
|
|
6309
6689
|
}
|
|
6310
6690
|
];
|
|
6311
6691
|
for (const { path: path5, source, parse } of sources) {
|
|
6312
|
-
if (!(0,
|
|
6692
|
+
if (!(0, import_fs12.existsSync)(path5)) continue;
|
|
6313
6693
|
try {
|
|
6314
|
-
const collections = parse((0,
|
|
6694
|
+
const collections = parse((0, import_fs12.readFileSync)(path5, "utf8"));
|
|
6315
6695
|
return {
|
|
6316
6696
|
text: JSON.stringify({ collections, source, collectionCount: collections.length }),
|
|
6317
6697
|
isError: false
|
|
@@ -6462,8 +6842,8 @@ function handleValidateWorkflow(input) {
|
|
|
6462
6842
|
if (input.workflow && Object.keys(input.workflow).length > 0) {
|
|
6463
6843
|
raw = input.workflow;
|
|
6464
6844
|
} else {
|
|
6465
|
-
const artifactPath = (0,
|
|
6466
|
-
if (!(0,
|
|
6845
|
+
const artifactPath = (0, import_path12.join)(cwd, ".stackwright", "artifacts", "workflow-config.json");
|
|
6846
|
+
if (!(0, import_fs12.existsSync)(artifactPath)) {
|
|
6467
6847
|
return fail([
|
|
6468
6848
|
{
|
|
6469
6849
|
code: "NO_WORKFLOW",
|
|
@@ -6472,7 +6852,7 @@ function handleValidateWorkflow(input) {
|
|
|
6472
6852
|
]);
|
|
6473
6853
|
}
|
|
6474
6854
|
try {
|
|
6475
|
-
raw = JSON.parse((0,
|
|
6855
|
+
raw = JSON.parse((0, import_fs12.readFileSync)(artifactPath, "utf8"));
|
|
6476
6856
|
} catch (err) {
|
|
6477
6857
|
return fail([{ code: "INVALID_JSON", message: `Failed to parse workflow artifact: ${err}` }]);
|
|
6478
6858
|
}
|
|
@@ -6909,8 +7289,8 @@ function registerProPageTools(server2) {
|
|
|
6909
7289
|
}
|
|
6910
7290
|
|
|
6911
7291
|
// src/tools/scaffold-cleanup.ts
|
|
6912
|
-
var
|
|
6913
|
-
var
|
|
7292
|
+
var import_fs13 = require("fs");
|
|
7293
|
+
var import_path13 = require("path");
|
|
6914
7294
|
var SCAFFOLD_FILES_TO_DELETE = [
|
|
6915
7295
|
"content/posts/getting-started.yaml",
|
|
6916
7296
|
"content/posts/hello-world.yaml"
|
|
@@ -6935,12 +7315,12 @@ var FALLBACK_COLORS = {
|
|
|
6935
7315
|
mutedForeground: "#737373"
|
|
6936
7316
|
};
|
|
6937
7317
|
function readThemeColors(cwd) {
|
|
6938
|
-
const tokenPath = (0,
|
|
6939
|
-
if (!(0,
|
|
6940
|
-
const stat = (0,
|
|
7318
|
+
const tokenPath = (0, import_path13.join)(cwd, ".stackwright", "artifacts", "theme-tokens.json");
|
|
7319
|
+
if (!(0, import_fs13.existsSync)(tokenPath)) return { ...FALLBACK_COLORS };
|
|
7320
|
+
const stat = (0, import_fs13.lstatSync)(tokenPath);
|
|
6941
7321
|
if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
|
|
6942
7322
|
try {
|
|
6943
|
-
const raw = JSON.parse((0,
|
|
7323
|
+
const raw = JSON.parse((0, import_fs13.readFileSync)(tokenPath, "utf-8"));
|
|
6944
7324
|
const css = raw?.cssVariables ?? {};
|
|
6945
7325
|
const toHsl = (hslValue) => {
|
|
6946
7326
|
if (!hslValue || typeof hslValue !== "string") return null;
|
|
@@ -7000,15 +7380,15 @@ function generateNotFoundPage(colors) {
|
|
|
7000
7380
|
`;
|
|
7001
7381
|
}
|
|
7002
7382
|
function isSafePath(fullPath) {
|
|
7003
|
-
if (!(0,
|
|
7004
|
-
const stat = (0,
|
|
7383
|
+
if (!(0, import_fs13.existsSync)(fullPath)) return true;
|
|
7384
|
+
const stat = (0, import_fs13.lstatSync)(fullPath);
|
|
7005
7385
|
return !stat.isSymbolicLink();
|
|
7006
7386
|
}
|
|
7007
7387
|
function isDirEmpty(dirPath) {
|
|
7008
|
-
if (!(0,
|
|
7009
|
-
const stat = (0,
|
|
7388
|
+
if (!(0, import_fs13.existsSync)(dirPath)) return false;
|
|
7389
|
+
const stat = (0, import_fs13.lstatSync)(dirPath);
|
|
7010
7390
|
if (!stat.isDirectory()) return false;
|
|
7011
|
-
return (0,
|
|
7391
|
+
return (0, import_fs13.readdirSync)(dirPath).length === 0;
|
|
7012
7392
|
}
|
|
7013
7393
|
function handleCleanupScaffold(_cwd) {
|
|
7014
7394
|
const cwd = _cwd ?? process.cwd();
|
|
@@ -7021,8 +7401,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7021
7401
|
cleanupWarnings: []
|
|
7022
7402
|
};
|
|
7023
7403
|
for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
|
|
7024
|
-
const fullPath = (0,
|
|
7025
|
-
if (!(0,
|
|
7404
|
+
const fullPath = (0, import_path13.join)(cwd, relPath);
|
|
7405
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7026
7406
|
result.skipped.push(relPath);
|
|
7027
7407
|
continue;
|
|
7028
7408
|
}
|
|
@@ -7031,7 +7411,7 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7031
7411
|
continue;
|
|
7032
7412
|
}
|
|
7033
7413
|
try {
|
|
7034
|
-
(0,
|
|
7414
|
+
(0, import_fs13.unlinkSync)(fullPath);
|
|
7035
7415
|
result.deleted.push(relPath);
|
|
7036
7416
|
} catch (err) {
|
|
7037
7417
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -7039,19 +7419,19 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7039
7419
|
}
|
|
7040
7420
|
{
|
|
7041
7421
|
const relPath = "pages/getting-started/content.yml";
|
|
7042
|
-
const fullPath = (0,
|
|
7043
|
-
if (!(0,
|
|
7422
|
+
const fullPath = (0, import_path13.join)(cwd, relPath);
|
|
7423
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7044
7424
|
result.skipped.push(relPath);
|
|
7045
7425
|
} else if (!isSafePath(fullPath)) {
|
|
7046
7426
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
7047
7427
|
} else {
|
|
7048
|
-
const content = (0,
|
|
7428
|
+
const content = (0, import_fs13.readFileSync)(fullPath, "utf-8");
|
|
7049
7429
|
const isScaffoldDefault = GETTING_STARTED_SCAFFOLD_FINGERPRINTS.some(
|
|
7050
7430
|
(marker) => content.includes(marker)
|
|
7051
7431
|
);
|
|
7052
7432
|
if (isScaffoldDefault) {
|
|
7053
7433
|
try {
|
|
7054
|
-
(0,
|
|
7434
|
+
(0, import_fs13.unlinkSync)(fullPath);
|
|
7055
7435
|
result.deleted.push(relPath);
|
|
7056
7436
|
} catch (err) {
|
|
7057
7437
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -7065,21 +7445,21 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7065
7445
|
}
|
|
7066
7446
|
{
|
|
7067
7447
|
const relPath = `content/posts/${POSTS_COLLECTION_FILE}`;
|
|
7068
|
-
const fullPath = (0,
|
|
7069
|
-
const postsDir = (0,
|
|
7070
|
-
if (!(0,
|
|
7448
|
+
const fullPath = (0, import_path13.join)(cwd, relPath);
|
|
7449
|
+
const postsDir = (0, import_path13.join)(cwd, "content/posts");
|
|
7450
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7071
7451
|
result.skipped.push(relPath);
|
|
7072
7452
|
} else if (!isSafePath(fullPath)) {
|
|
7073
7453
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
7074
|
-
} else if (!(0,
|
|
7454
|
+
} else if (!(0, import_fs13.existsSync)(postsDir)) {
|
|
7075
7455
|
result.skipped.push(relPath);
|
|
7076
7456
|
} else {
|
|
7077
|
-
const userPostFiles = (0,
|
|
7457
|
+
const userPostFiles = (0, import_fs13.readdirSync)(postsDir).filter(
|
|
7078
7458
|
(f) => f !== POSTS_COLLECTION_FILE && POST_FILE_EXTENSIONS.some((ext) => f.endsWith(ext))
|
|
7079
7459
|
);
|
|
7080
7460
|
if (userPostFiles.length === 0) {
|
|
7081
7461
|
try {
|
|
7082
|
-
(0,
|
|
7462
|
+
(0, import_fs13.unlinkSync)(fullPath);
|
|
7083
7463
|
result.deleted.push(relPath);
|
|
7084
7464
|
} catch (err) {
|
|
7085
7465
|
result.errors.push(`Failed to delete ${relPath}: ${String(err)}`);
|
|
@@ -7092,15 +7472,15 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7092
7472
|
}
|
|
7093
7473
|
}
|
|
7094
7474
|
for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
|
|
7095
|
-
const fullDir = (0,
|
|
7096
|
-
if (!(0,
|
|
7475
|
+
const fullDir = (0, import_path13.join)(cwd, relDir);
|
|
7476
|
+
if (!(0, import_fs13.existsSync)(fullDir)) continue;
|
|
7097
7477
|
if (!isSafePath(fullDir)) {
|
|
7098
7478
|
result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
|
|
7099
7479
|
continue;
|
|
7100
7480
|
}
|
|
7101
7481
|
if (isDirEmpty(fullDir)) {
|
|
7102
7482
|
try {
|
|
7103
|
-
(0,
|
|
7483
|
+
(0, import_fs13.rmdirSync)(fullDir);
|
|
7104
7484
|
result.prunedDirs.push(relDir);
|
|
7105
7485
|
} catch (err) {
|
|
7106
7486
|
result.errors.push(`Failed to prune directory ${relDir}: ${String(err)}`);
|
|
@@ -7108,8 +7488,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7108
7488
|
}
|
|
7109
7489
|
}
|
|
7110
7490
|
{
|
|
7111
|
-
const fullPath = (0,
|
|
7112
|
-
if (!(0,
|
|
7491
|
+
const fullPath = (0, import_path13.join)(cwd, NOT_FOUND_PATH);
|
|
7492
|
+
if (!(0, import_fs13.existsSync)(fullPath)) {
|
|
7113
7493
|
result.skipped.push(NOT_FOUND_PATH);
|
|
7114
7494
|
} else if (!isSafePath(fullPath)) {
|
|
7115
7495
|
result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
|
|
@@ -7117,9 +7497,9 @@ function handleCleanupScaffold(_cwd) {
|
|
|
7117
7497
|
try {
|
|
7118
7498
|
const colors = readThemeColors(cwd);
|
|
7119
7499
|
const content = generateNotFoundPage(colors);
|
|
7120
|
-
const dir = (0,
|
|
7121
|
-
(0,
|
|
7122
|
-
(0,
|
|
7500
|
+
const dir = (0, import_path13.dirname)(fullPath);
|
|
7501
|
+
(0, import_fs13.mkdirSync)(dir, { recursive: true });
|
|
7502
|
+
(0, import_fs13.writeFileSync)(fullPath, content, "utf-8");
|
|
7123
7503
|
result.rewritten.push(NOT_FOUND_PATH);
|
|
7124
7504
|
} catch (err) {
|
|
7125
7505
|
result.errors.push(`Failed to rewrite ${NOT_FOUND_PATH}: ${String(err)}`);
|
|
@@ -7388,23 +7768,23 @@ function registerContrastTools(server2) {
|
|
|
7388
7768
|
|
|
7389
7769
|
// src/tools/compile.ts
|
|
7390
7770
|
var import_zod20 = require("zod");
|
|
7391
|
-
var
|
|
7392
|
-
var
|
|
7771
|
+
var import_fs14 = __toESM(require("fs"));
|
|
7772
|
+
var import_path14 = __toESM(require("path"));
|
|
7393
7773
|
var import_server = require("@stackwright-pro/pulse/server");
|
|
7394
7774
|
var import_server2 = require("@stackwright-pro/auth-nextjs/server");
|
|
7395
7775
|
var import_server3 = require("@stackwright-pro/openapi/server");
|
|
7396
7776
|
function buildContext(projectRoot) {
|
|
7397
7777
|
return {
|
|
7398
7778
|
projectRoot,
|
|
7399
|
-
contentOutDir:
|
|
7400
|
-
imagesDir:
|
|
7779
|
+
contentOutDir: import_path14.default.join(projectRoot, "public", "stackwright-content"),
|
|
7780
|
+
imagesDir: import_path14.default.join(projectRoot, "public", "images"),
|
|
7401
7781
|
siteConfig: loadSiteConfig(projectRoot)
|
|
7402
7782
|
};
|
|
7403
7783
|
}
|
|
7404
7784
|
function loadSiteConfig(projectRoot) {
|
|
7405
7785
|
try {
|
|
7406
|
-
const sitePath =
|
|
7407
|
-
return JSON.parse(
|
|
7786
|
+
const sitePath = import_path14.default.join(projectRoot, "public", "stackwright-content", "_site.json");
|
|
7787
|
+
return JSON.parse(import_fs14.default.readFileSync(sitePath, "utf8"));
|
|
7408
7788
|
} catch {
|
|
7409
7789
|
return {};
|
|
7410
7790
|
}
|
|
@@ -7415,8 +7795,8 @@ function formatDuration(startMs) {
|
|
|
7415
7795
|
}
|
|
7416
7796
|
async function tryOssCompile(fn, ctx, extra) {
|
|
7417
7797
|
try {
|
|
7418
|
-
const bsPath =
|
|
7419
|
-
if (!
|
|
7798
|
+
const bsPath = import_path14.default.join(ctx.projectRoot, "node_modules", "@stackwright", "build-scripts");
|
|
7799
|
+
if (!import_fs14.default.existsSync(bsPath)) {
|
|
7420
7800
|
return {
|
|
7421
7801
|
ok: false,
|
|
7422
7802
|
error: `@stackwright/build-scripts not found at ${bsPath}. Is it installed in your project?`
|
|
@@ -7630,14 +8010,14 @@ ${results.join("\n")}`;
|
|
|
7630
8010
|
|
|
7631
8011
|
// src/tools/strip-legacy-integrations.ts
|
|
7632
8012
|
var import_zod21 = require("zod");
|
|
7633
|
-
var
|
|
7634
|
-
var
|
|
8013
|
+
var import_fs15 = require("fs");
|
|
8014
|
+
var import_path15 = require("path");
|
|
7635
8015
|
function handleStripLegacyIntegrations(input) {
|
|
7636
8016
|
const { projectRoot } = input;
|
|
7637
|
-
const ymlPath = (0,
|
|
7638
|
-
const yamlPath = (0,
|
|
7639
|
-
if (!(0,
|
|
7640
|
-
if ((0,
|
|
8017
|
+
const ymlPath = (0, import_path15.join)(projectRoot, "stackwright.yml");
|
|
8018
|
+
const yamlPath = (0, import_path15.join)(projectRoot, "stackwright.yaml");
|
|
8019
|
+
if (!(0, import_fs15.existsSync)(ymlPath)) {
|
|
8020
|
+
if ((0, import_fs15.existsSync)(yamlPath)) {
|
|
7641
8021
|
return {
|
|
7642
8022
|
changed: false,
|
|
7643
8023
|
removedEntries: 0,
|
|
@@ -7650,7 +8030,7 @@ function handleStripLegacyIntegrations(input) {
|
|
|
7650
8030
|
message: "stackwright.yml not found \u2014 no-op."
|
|
7651
8031
|
};
|
|
7652
8032
|
}
|
|
7653
|
-
const raw = (0,
|
|
8033
|
+
const raw = (0, import_fs15.readFileSync)(ymlPath, "utf-8");
|
|
7654
8034
|
const isCRLF = raw.includes("\r\n");
|
|
7655
8035
|
const content = isCRLF ? raw.replace(/\r\n/g, "\n") : raw;
|
|
7656
8036
|
const lines = content.split("\n");
|
|
@@ -7680,7 +8060,7 @@ function handleStripLegacyIntegrations(input) {
|
|
|
7680
8060
|
outputContent += "\n";
|
|
7681
8061
|
}
|
|
7682
8062
|
const outputRaw = isCRLF ? outputContent.replace(/\n/g, "\r\n") : outputContent;
|
|
7683
|
-
(0,
|
|
8063
|
+
(0, import_fs15.writeFileSync)(ymlPath, outputRaw, "utf-8");
|
|
7684
8064
|
return {
|
|
7685
8065
|
changed: true,
|
|
7686
8066
|
removedEntries,
|
|
@@ -7705,15 +8085,15 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
7705
8085
|
|
|
7706
8086
|
// src/tools/emitters.ts
|
|
7707
8087
|
var import_zod22 = require("zod");
|
|
7708
|
-
var
|
|
7709
|
-
var
|
|
8088
|
+
var import_fs16 = require("fs");
|
|
8089
|
+
var import_path16 = require("path");
|
|
7710
8090
|
var import_js_yaml4 = __toESM(require("js-yaml"));
|
|
7711
8091
|
var import_emitters = require("@stackwright-pro/emitters");
|
|
7712
8092
|
function readIntegrations(cwd) {
|
|
7713
|
-
const filePath = (0,
|
|
7714
|
-
if (!(0,
|
|
8093
|
+
const filePath = (0, import_path16.join)(cwd, "stackwright.integrations.yml");
|
|
8094
|
+
if (!(0, import_fs16.existsSync)(filePath)) return [];
|
|
7715
8095
|
try {
|
|
7716
|
-
const raw = import_js_yaml4.default.load((0,
|
|
8096
|
+
const raw = import_js_yaml4.default.load((0, import_fs16.readFileSync)(filePath, "utf-8"));
|
|
7717
8097
|
if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
|
|
7718
8098
|
return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
|
|
7719
8099
|
name: i.name,
|
|
@@ -7724,15 +8104,15 @@ function readIntegrations(cwd) {
|
|
|
7724
8104
|
}
|
|
7725
8105
|
}
|
|
7726
8106
|
function readServiceTokenRefs(cwd) {
|
|
7727
|
-
const servicesDir = (0,
|
|
7728
|
-
if (!(0,
|
|
8107
|
+
const servicesDir = (0, import_path16.join)(cwd, "services");
|
|
8108
|
+
if (!(0, import_fs16.existsSync)(servicesDir)) return [];
|
|
7729
8109
|
try {
|
|
7730
|
-
const files = (0,
|
|
8110
|
+
const files = (0, import_fs16.readdirSync)(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
7731
8111
|
const refs = [];
|
|
7732
8112
|
for (const file of files) {
|
|
7733
8113
|
try {
|
|
7734
8114
|
const raw = import_js_yaml4.default.load(
|
|
7735
|
-
(0,
|
|
8115
|
+
(0, import_fs16.readFileSync)((0, import_path16.join)(servicesDir, file), "utf-8")
|
|
7736
8116
|
);
|
|
7737
8117
|
if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
|
|
7738
8118
|
refs.push({
|
|
@@ -7882,12 +8262,12 @@ This is the third concrete instance of the swp-zf9y deterministic emitter patter
|
|
|
7882
8262
|
|
|
7883
8263
|
// src/tools/list-specs.ts
|
|
7884
8264
|
var import_zod23 = require("zod");
|
|
7885
|
-
var
|
|
7886
|
-
var
|
|
8265
|
+
var import_fs17 = require("fs");
|
|
8266
|
+
var import_path17 = require("path");
|
|
7887
8267
|
var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7888
8268
|
var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
|
|
7889
8269
|
function deriveIntegrationName(fileName) {
|
|
7890
|
-
let name = (0,
|
|
8270
|
+
let name = (0, import_path17.basename)(fileName, (0, import_path17.extname)(fileName));
|
|
7891
8271
|
for (const suffix of STRIP_SUFFIXES) {
|
|
7892
8272
|
if (name.endsWith(suffix)) {
|
|
7893
8273
|
name = name.slice(0, -suffix.length);
|
|
@@ -7905,29 +8285,29 @@ function detectFormat(snippet) {
|
|
|
7905
8285
|
}
|
|
7906
8286
|
function handleListSpecs(input) {
|
|
7907
8287
|
const { projectRoot } = input;
|
|
7908
|
-
const specsDir = (0,
|
|
8288
|
+
const specsDir = (0, import_path17.join)(projectRoot, "specs");
|
|
7909
8289
|
const empty = { projectRoot, specs: [], totalCount: 0 };
|
|
7910
|
-
if (!(0,
|
|
8290
|
+
if (!(0, import_fs17.existsSync)(specsDir)) return empty;
|
|
7911
8291
|
let entries;
|
|
7912
8292
|
try {
|
|
7913
|
-
entries = (0,
|
|
8293
|
+
entries = (0, import_fs17.readdirSync)(specsDir);
|
|
7914
8294
|
} catch {
|
|
7915
8295
|
return empty;
|
|
7916
8296
|
}
|
|
7917
8297
|
const specs = [];
|
|
7918
8298
|
for (const entry of entries) {
|
|
7919
|
-
const fullPath = (0,
|
|
8299
|
+
const fullPath = (0, import_path17.join)(specsDir, entry);
|
|
7920
8300
|
let stat;
|
|
7921
8301
|
try {
|
|
7922
|
-
stat = (0,
|
|
8302
|
+
stat = (0, import_fs17.statSync)(fullPath);
|
|
7923
8303
|
} catch {
|
|
7924
8304
|
continue;
|
|
7925
8305
|
}
|
|
7926
8306
|
if (!stat.isFile()) continue;
|
|
7927
|
-
if (!ALLOWED_EXTS.has((0,
|
|
8307
|
+
if (!ALLOWED_EXTS.has((0, import_path17.extname)(entry).toLowerCase())) continue;
|
|
7928
8308
|
let snippet = "";
|
|
7929
8309
|
try {
|
|
7930
|
-
snippet = (0,
|
|
8310
|
+
snippet = (0, import_fs17.readFileSync)(fullPath, "utf-8").slice(0, 2048);
|
|
7931
8311
|
} catch {
|
|
7932
8312
|
}
|
|
7933
8313
|
specs.push({
|
|
@@ -7959,14 +8339,14 @@ function registerListSpecsTool(server2) {
|
|
|
7959
8339
|
|
|
7960
8340
|
// src/tools/consolidate-integrations.ts
|
|
7961
8341
|
var import_zod24 = require("zod");
|
|
7962
|
-
var
|
|
8342
|
+
var import_fs18 = require("fs");
|
|
7963
8343
|
var import_proper_lockfile2 = require("proper-lockfile");
|
|
7964
|
-
var
|
|
8344
|
+
var import_path18 = require("path");
|
|
7965
8345
|
var import_js_yaml5 = __toESM(require("js-yaml"));
|
|
7966
8346
|
var BASE_MOCK_PORT = 4011;
|
|
7967
8347
|
var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7968
8348
|
function deriveNameFromFilename(filename) {
|
|
7969
|
-
const base = (0,
|
|
8349
|
+
const base = (0, import_path18.basename)(filename, ".json").replace(/^api-config-/, "");
|
|
7970
8350
|
for (const suffix of STRIP_SUFFIXES2) {
|
|
7971
8351
|
if (base.endsWith(suffix)) return base.slice(0, -suffix.length);
|
|
7972
8352
|
}
|
|
@@ -7974,13 +8354,13 @@ function deriveNameFromFilename(filename) {
|
|
|
7974
8354
|
}
|
|
7975
8355
|
function handleConsolidateIntegrations(input) {
|
|
7976
8356
|
const { projectRoot } = input;
|
|
7977
|
-
const artifactsDir = (0,
|
|
7978
|
-
if (!(0,
|
|
8357
|
+
const artifactsDir = (0, import_path18.join)(projectRoot, ".stackwright", "artifacts");
|
|
8358
|
+
if (!(0, import_fs18.existsSync)(artifactsDir)) {
|
|
7979
8359
|
return { written: false, integrationCount: 0, reason: "no per-spec artifacts found" };
|
|
7980
8360
|
}
|
|
7981
8361
|
let entries;
|
|
7982
8362
|
try {
|
|
7983
|
-
entries = (0,
|
|
8363
|
+
entries = (0, import_fs18.readdirSync)(artifactsDir).filter(
|
|
7984
8364
|
(f) => f.startsWith("api-config-") && f.endsWith(".json") && f !== "api-config.json"
|
|
7985
8365
|
);
|
|
7986
8366
|
} catch {
|
|
@@ -7991,14 +8371,22 @@ function handleConsolidateIntegrations(input) {
|
|
|
7991
8371
|
}
|
|
7992
8372
|
entries.sort();
|
|
7993
8373
|
const integrationsByName = /* @__PURE__ */ new Map();
|
|
8374
|
+
const skippedIntegrationsList = [];
|
|
7994
8375
|
entries.forEach((filename, index) => {
|
|
7995
8376
|
let artifact = {};
|
|
7996
8377
|
try {
|
|
7997
|
-
const raw = (0,
|
|
8378
|
+
const raw = (0, import_fs18.readFileSync)((0, import_path18.join)(artifactsDir, filename), "utf-8");
|
|
7998
8379
|
artifact = JSON.parse(raw);
|
|
7999
8380
|
} catch {
|
|
8000
8381
|
}
|
|
8001
8382
|
const name = artifact.integrationName ?? deriveNameFromFilename(filename);
|
|
8383
|
+
if (artifact.skipped && artifact.skipped.length > 0) {
|
|
8384
|
+
skippedIntegrationsList.push({
|
|
8385
|
+
name,
|
|
8386
|
+
reason: artifact.skipped[0]?.reason ?? "explicitly skipped by api-otter"
|
|
8387
|
+
});
|
|
8388
|
+
return;
|
|
8389
|
+
}
|
|
8002
8390
|
const spec = artifact.integrationSpec ?? artifact.specPath ?? `./specs/${name}.yaml`;
|
|
8003
8391
|
const mockUrl = artifact.mockUrl ?? `http://localhost:${BASE_MOCK_PORT + index}`;
|
|
8004
8392
|
const baseUrl = artifact.baseUrl ?? "";
|
|
@@ -8019,21 +8407,21 @@ function handleConsolidateIntegrations(input) {
|
|
|
8019
8407
|
integrationsByName.set(name, entry);
|
|
8020
8408
|
});
|
|
8021
8409
|
const integrations = Array.from(integrationsByName.values());
|
|
8022
|
-
const dedupedCount = entries.length - integrations.length;
|
|
8023
|
-
const integrationsYmlPath = (0,
|
|
8410
|
+
const dedupedCount = entries.length - skippedIntegrationsList.length - integrations.length;
|
|
8411
|
+
const integrationsYmlPath = (0, import_path18.join)(projectRoot, "stackwright.integrations.yml");
|
|
8024
8412
|
let existing = {};
|
|
8025
|
-
if ((0,
|
|
8413
|
+
if ((0, import_fs18.existsSync)(integrationsYmlPath)) {
|
|
8026
8414
|
try {
|
|
8027
|
-
const raw = (0,
|
|
8415
|
+
const raw = (0, import_fs18.readFileSync)(integrationsYmlPath, "utf-8");
|
|
8028
8416
|
existing = import_js_yaml5.default.load(raw) ?? {};
|
|
8029
8417
|
} catch {
|
|
8030
8418
|
existing = {};
|
|
8031
8419
|
}
|
|
8032
8420
|
}
|
|
8033
8421
|
const merged = { ...existing, integrations };
|
|
8034
|
-
(0,
|
|
8035
|
-
if (!(0,
|
|
8036
|
-
(0,
|
|
8422
|
+
(0, import_fs18.mkdirSync)((0, import_path18.join)(projectRoot), { recursive: true });
|
|
8423
|
+
if (!(0, import_fs18.existsSync)(integrationsYmlPath)) {
|
|
8424
|
+
(0, import_fs18.writeFileSync)(integrationsYmlPath, "", "utf-8");
|
|
8037
8425
|
}
|
|
8038
8426
|
const release = (0, import_proper_lockfile2.lockSync)(integrationsYmlPath, {
|
|
8039
8427
|
realpath: false,
|
|
@@ -8043,7 +8431,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8043
8431
|
const header = `# stackwright.integrations.yml \u2014 Auto-generated by stackwright_pro_consolidate_integrations
|
|
8044
8432
|
`;
|
|
8045
8433
|
const body = import_js_yaml5.default.dump(merged, { lineWidth: 120, noRefs: true });
|
|
8046
|
-
(0,
|
|
8434
|
+
(0, import_fs18.writeFileSync)(integrationsYmlPath, header + body, "utf-8");
|
|
8047
8435
|
} finally {
|
|
8048
8436
|
release();
|
|
8049
8437
|
}
|
|
@@ -8051,7 +8439,11 @@ function handleConsolidateIntegrations(input) {
|
|
|
8051
8439
|
written: true,
|
|
8052
8440
|
integrationCount: integrations.length,
|
|
8053
8441
|
path: "stackwright.integrations.yml",
|
|
8054
|
-
...dedupedCount > 0 ? { dedupedCount } : {}
|
|
8442
|
+
...dedupedCount > 0 ? { dedupedCount } : {},
|
|
8443
|
+
...skippedIntegrationsList.length > 0 ? {
|
|
8444
|
+
skippedCount: skippedIntegrationsList.length,
|
|
8445
|
+
skippedIntegrations: skippedIntegrationsList
|
|
8446
|
+
} : {}
|
|
8055
8447
|
};
|
|
8056
8448
|
}
|
|
8057
8449
|
function registerConsolidateIntegrationsTool(server2) {
|
|
@@ -8125,7 +8517,7 @@ var package_default = {
|
|
|
8125
8517
|
"test:coverage": "vitest run --coverage"
|
|
8126
8518
|
},
|
|
8127
8519
|
name: "@stackwright-pro/mcp",
|
|
8128
|
-
version: "0.2.0-alpha.
|
|
8520
|
+
version: "0.2.0-alpha.108",
|
|
8129
8521
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
8130
8522
|
license: "SEE LICENSE IN LICENSE",
|
|
8131
8523
|
main: "./dist/server.js",
|