@stackwright-pro/mcp 0.2.0-alpha.105 → 0.2.0-alpha.107
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 +1 -1
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +1 -1
- package/dist/integrity.mjs.map +1 -1
- package/dist/pipeline-constants.js +16 -1
- package/dist/pipeline-constants.js.map +1 -1
- package/dist/pipeline-constants.mjs +16 -1
- package/dist/pipeline-constants.mjs.map +1 -1
- package/dist/server.js +429 -214
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +371 -156
- package/dist/server.mjs.map +1 -1
- package/package.json +6 -6
package/dist/server.mjs
CHANGED
|
@@ -1999,9 +1999,9 @@ function registerOrchestrationTools(server2) {
|
|
|
1999
1999
|
|
|
2000
2000
|
// src/tools/pipeline.ts
|
|
2001
2001
|
import { z as z13 } from "zod";
|
|
2002
|
-
import { readFileSync as
|
|
2002
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4, lstatSync as lstatSync6 } from "fs";
|
|
2003
2003
|
import { lockSync } from "proper-lockfile";
|
|
2004
|
-
import { join as
|
|
2004
|
+
import { join as join6 } from "path";
|
|
2005
2005
|
import { createHash as createHash3 } from "crypto";
|
|
2006
2006
|
|
|
2007
2007
|
// src/artifact-signing.ts
|
|
@@ -2342,6 +2342,208 @@ function registerArtifactSigningTools(server2) {
|
|
|
2342
2342
|
);
|
|
2343
2343
|
}
|
|
2344
2344
|
|
|
2345
|
+
// src/tools/validate-pulse-content.ts
|
|
2346
|
+
import { load as yamlLoad } from "js-yaml";
|
|
2347
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync, existsSync as existsSync5 } from "fs";
|
|
2348
|
+
import { join as join4, relative } from "path";
|
|
2349
|
+
import { PULSE_CONTENT_SCHEMAS, ColumnSchema } from "@stackwright-pro/pulse";
|
|
2350
|
+
function getAllowedProps(schema) {
|
|
2351
|
+
let inner = schema;
|
|
2352
|
+
while (inner && typeof inner.unwrap === "function") {
|
|
2353
|
+
inner = inner.unwrap();
|
|
2354
|
+
}
|
|
2355
|
+
if (inner && typeof inner === "object" && inner.shape && typeof inner.shape === "object") {
|
|
2356
|
+
return Object.keys(inner.shape);
|
|
2357
|
+
}
|
|
2358
|
+
return [];
|
|
2359
|
+
}
|
|
2360
|
+
var COLUMN_ALLOWED_PROPS = getAllowedProps(ColumnSchema);
|
|
2361
|
+
function walk(node, path5, relativeFilePath, violations, counter) {
|
|
2362
|
+
if (node === null || node === void 0 || typeof node !== "object") return;
|
|
2363
|
+
if (Array.isArray(node)) {
|
|
2364
|
+
for (let i = 0; i < node.length; i++) {
|
|
2365
|
+
walk(node[i], `${path5}[${i}]`, relativeFilePath, violations, counter);
|
|
2366
|
+
}
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
const obj = node;
|
|
2370
|
+
const contentType = typeof obj["type"] === "string" ? obj["type"] : null;
|
|
2371
|
+
if (contentType !== null && contentType in PULSE_CONTENT_SCHEMAS) {
|
|
2372
|
+
counter.checked++;
|
|
2373
|
+
const schema = PULSE_CONTENT_SCHEMAS[contentType];
|
|
2374
|
+
const result = schema.safeParse(obj);
|
|
2375
|
+
if (!result.success) {
|
|
2376
|
+
violations.push({
|
|
2377
|
+
file: relativeFilePath,
|
|
2378
|
+
yamlPath: path5,
|
|
2379
|
+
contentType,
|
|
2380
|
+
issues: result.error.issues.map((issue) => ({
|
|
2381
|
+
path: issue.path.map(String),
|
|
2382
|
+
message: issue.message
|
|
2383
|
+
})),
|
|
2384
|
+
emittedProps: Object.keys(obj).filter((k) => k !== "type"),
|
|
2385
|
+
allowedProps: getAllowedProps(schema),
|
|
2386
|
+
rawItem: obj
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
2391
|
+
walk(child, `${path5}.${key}`, relativeFilePath, violations, counter);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
function findContentYamlFiles(dir) {
|
|
2395
|
+
const results = [];
|
|
2396
|
+
if (!existsSync5(dir)) return results;
|
|
2397
|
+
for (const entry of readdirSync2(dir)) {
|
|
2398
|
+
const fullPath = join4(dir, entry);
|
|
2399
|
+
const stat = statSync(fullPath);
|
|
2400
|
+
if (stat.isDirectory()) {
|
|
2401
|
+
results.push(...findContentYamlFiles(fullPath));
|
|
2402
|
+
} else if (entry === "content.yml") {
|
|
2403
|
+
results.push(fullPath);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return results;
|
|
2407
|
+
}
|
|
2408
|
+
function validatePulseContent(projectRoot) {
|
|
2409
|
+
const pagesDir = join4(projectRoot, "pages");
|
|
2410
|
+
const contentFiles = findContentYamlFiles(pagesDir);
|
|
2411
|
+
const violations = [];
|
|
2412
|
+
const counter = { checked: 0 };
|
|
2413
|
+
for (const absolutePath of contentFiles) {
|
|
2414
|
+
const relPath = relative(projectRoot, absolutePath);
|
|
2415
|
+
let parsed;
|
|
2416
|
+
try {
|
|
2417
|
+
const raw = readFileSync4(absolutePath, "utf-8");
|
|
2418
|
+
parsed = yamlLoad(raw);
|
|
2419
|
+
} catch {
|
|
2420
|
+
continue;
|
|
2421
|
+
}
|
|
2422
|
+
walk(parsed, "content", relPath, violations, counter);
|
|
2423
|
+
}
|
|
2424
|
+
return {
|
|
2425
|
+
valid: violations.length === 0,
|
|
2426
|
+
filesScanned: contentFiles.length,
|
|
2427
|
+
itemsChecked: counter.checked,
|
|
2428
|
+
violations
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
function unknownProps(violation) {
|
|
2432
|
+
const allowed = new Set(violation.allowedProps);
|
|
2433
|
+
return violation.emittedProps.filter((p) => !allowed.has(p));
|
|
2434
|
+
}
|
|
2435
|
+
function extractColumnViolations(violation) {
|
|
2436
|
+
if (violation.contentType !== "data_table_pulse") return [];
|
|
2437
|
+
const byColumn = /* @__PURE__ */ new Map();
|
|
2438
|
+
for (const issue of violation.issues) {
|
|
2439
|
+
if (issue.path[0] === "columns" && typeof issue.path[1] === "string") {
|
|
2440
|
+
const colIdx = parseInt(issue.path[1], 10);
|
|
2441
|
+
if (!isNaN(colIdx)) {
|
|
2442
|
+
const missing = issue.path[2] ?? issue.message;
|
|
2443
|
+
if (!byColumn.has(colIdx)) byColumn.set(colIdx, []);
|
|
2444
|
+
byColumn.get(colIdx).push(String(missing));
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
if (byColumn.size === 0) return [];
|
|
2449
|
+
const rawColumns = Array.isArray(violation.rawItem["columns"]) ? violation.rawItem["columns"] : [];
|
|
2450
|
+
const columnAllowed = new Set(COLUMN_ALLOWED_PROPS);
|
|
2451
|
+
const summaries = [];
|
|
2452
|
+
for (const [colIdx, missingProps] of byColumn.entries()) {
|
|
2453
|
+
const rawCol = rawColumns[colIdx] ?? {};
|
|
2454
|
+
const emitted = Object.keys(rawCol);
|
|
2455
|
+
const unknown = emitted.filter((p) => !columnAllowed.has(p));
|
|
2456
|
+
summaries.push({
|
|
2457
|
+
index: colIdx,
|
|
2458
|
+
emittedProps: emitted,
|
|
2459
|
+
unknownProps: unknown,
|
|
2460
|
+
missingProps: [...new Set(missingProps)]
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
2463
|
+
return summaries.sort((a, b) => a.index - b.index);
|
|
2464
|
+
}
|
|
2465
|
+
function swapSuggestion(unknownPropNames, missingPropNames, itemType) {
|
|
2466
|
+
if (unknownPropNames.length !== 1 || missingPropNames.length !== 1) return null;
|
|
2467
|
+
const level = itemType === "column" ? "column property" : "property";
|
|
2468
|
+
return ` Fix: rename ${level} \`${unknownPropNames[0]}\` \u2192 \`${missingPropNames[0]}\``;
|
|
2469
|
+
}
|
|
2470
|
+
function formatPulseRetryPrompt(result) {
|
|
2471
|
+
if (result.valid) return "";
|
|
2472
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
2473
|
+
for (const v of result.violations) {
|
|
2474
|
+
if (!byFile.has(v.file)) byFile.set(v.file, []);
|
|
2475
|
+
byFile.get(v.file).push(v);
|
|
2476
|
+
}
|
|
2477
|
+
const fileCount = byFile.size;
|
|
2478
|
+
const totalViolations = result.violations.length;
|
|
2479
|
+
const lines = [
|
|
2480
|
+
`Pulse schema validation failed: ${totalViolations} violation(s) across ${fileCount} content.yml file(s).`,
|
|
2481
|
+
"",
|
|
2482
|
+
"You MUST fix ALL violations and re-call stackwright_pro_validate_artifact.",
|
|
2483
|
+
'Do NOT invent new prop names. Only use props listed in "Schema allows".',
|
|
2484
|
+
""
|
|
2485
|
+
];
|
|
2486
|
+
for (const [file, violations] of byFile.entries()) {
|
|
2487
|
+
lines.push(`${file} \u2014 ${violations.length} violation(s):`);
|
|
2488
|
+
for (const v of violations) {
|
|
2489
|
+
lines.push(` ${v.yamlPath} (${v.contentType}):`);
|
|
2490
|
+
const unknown = unknownProps(v);
|
|
2491
|
+
const colViolations = extractColumnViolations(v);
|
|
2492
|
+
if (colViolations.length > 0) {
|
|
2493
|
+
for (const col of colViolations) {
|
|
2494
|
+
lines.push(` columns[${col.index}]:`);
|
|
2495
|
+
if (col.unknownProps.length > 0) {
|
|
2496
|
+
lines.push(
|
|
2497
|
+
` Unknown prop(s) used: ${col.unknownProps.map((p) => `\`${p}\``).join(", ")}`
|
|
2498
|
+
);
|
|
2499
|
+
}
|
|
2500
|
+
if (col.missingProps.length > 0) {
|
|
2501
|
+
lines.push(
|
|
2502
|
+
` Missing required prop(s): ${col.missingProps.map((p) => `\`${p}\``).join(", ")}`
|
|
2503
|
+
);
|
|
2504
|
+
}
|
|
2505
|
+
const suggestion = swapSuggestion(col.unknownProps, col.missingProps, "column");
|
|
2506
|
+
if (suggestion) lines.push(suggestion);
|
|
2507
|
+
}
|
|
2508
|
+
lines.push(
|
|
2509
|
+
` Schema allows ONLY these column props: ${COLUMN_ALLOWED_PROPS.join(", ")}.`
|
|
2510
|
+
);
|
|
2511
|
+
lines.push(
|
|
2512
|
+
` Do NOT use: \`label\` (use \`header\`), \`renderAs\` (use \`type\`), \`severityMap\` (use \`colorMap\`), \`width\` (not supported).`
|
|
2513
|
+
);
|
|
2514
|
+
} else if (unknown.length > 0 || v.issues.length > 0) {
|
|
2515
|
+
const missingFromIssues = v.issues.filter((i) => i.path.length === 1 && !i.path[0].match(/^\d+$/)).map((i) => i.path[0]).filter(Boolean);
|
|
2516
|
+
if (unknown.length > 0) {
|
|
2517
|
+
lines.push(` Unknown prop(s) used: ${unknown.map((p) => `\`${p}\``).join(", ")}`);
|
|
2518
|
+
}
|
|
2519
|
+
if (missingFromIssues.length > 0) {
|
|
2520
|
+
lines.push(
|
|
2521
|
+
` Missing required prop(s): ${[...new Set(missingFromIssues)].map((p) => `\`${p}\``).join(", ")}`
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2524
|
+
const suggestion = swapSuggestion(unknown, [...new Set(missingFromIssues)], "top");
|
|
2525
|
+
if (suggestion) lines.push(suggestion);
|
|
2526
|
+
lines.push(` Schema allows ONLY these props: ${v.allowedProps.join(", ")}.`);
|
|
2527
|
+
if (v.contentType === "metric_card_pulse") {
|
|
2528
|
+
lines.push(
|
|
2529
|
+
` Do NOT use: \`valueField\`, \`valuePath\`, \`dataField\`, \`value\`. The correct prop is \`field\` (dot-path into the collection).`
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
} else {
|
|
2533
|
+
for (const issue of v.issues) {
|
|
2534
|
+
lines.push(` ${issue.path.join(".")}: ${issue.message}`);
|
|
2535
|
+
}
|
|
2536
|
+
lines.push(` Schema allows ONLY these props: ${v.allowedProps.join(", ")}.`);
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
lines.push("");
|
|
2540
|
+
}
|
|
2541
|
+
lines.push(
|
|
2542
|
+
"After fixing ALL violations above, re-call stackwright_pro_validate_artifact with the corrected artifact."
|
|
2543
|
+
);
|
|
2544
|
+
return lines.join("\n");
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2345
2547
|
// src/tools/pipeline.ts
|
|
2346
2548
|
import { WorkflowFileSchema, authConfigSchema as authConfigSchema3 } from "@stackwright-pro/types";
|
|
2347
2549
|
|
|
@@ -2357,7 +2559,7 @@ import {
|
|
|
2357
2559
|
|
|
2358
2560
|
// src/tools/validate-yaml-fragment.ts
|
|
2359
2561
|
import { z as z11 } from "zod";
|
|
2360
|
-
import { load as
|
|
2562
|
+
import { load as yamlLoad2 } from "js-yaml";
|
|
2361
2563
|
import {
|
|
2362
2564
|
WorkflowStepSchema,
|
|
2363
2565
|
WorkflowDefinitionSchema,
|
|
@@ -2465,7 +2667,7 @@ function handleValidateYamlFragment(input) {
|
|
|
2465
2667
|
const name = schemaName;
|
|
2466
2668
|
let parsed;
|
|
2467
2669
|
try {
|
|
2468
|
-
parsed =
|
|
2670
|
+
parsed = yamlLoad2(yaml3);
|
|
2469
2671
|
} catch (err) {
|
|
2470
2672
|
return {
|
|
2471
2673
|
valid: false,
|
|
@@ -2692,8 +2894,8 @@ function registerGetSchemaTool(server2) {
|
|
|
2692
2894
|
}
|
|
2693
2895
|
|
|
2694
2896
|
// src/tools/pipeline-graph.ts
|
|
2695
|
-
import { readFileSync as
|
|
2696
|
-
import { join as
|
|
2897
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync3, lstatSync as lstatSync5 } from "fs";
|
|
2898
|
+
import { join as join5 } from "path";
|
|
2697
2899
|
var MANIFEST_NAME_TO_PHASE = {
|
|
2698
2900
|
"stackwright-pro-designer-otter": "designer",
|
|
2699
2901
|
"stackwright-pro-theme-otter": "theme",
|
|
@@ -2722,8 +2924,8 @@ var OTTER_SEARCH_PATHS = [
|
|
|
2722
2924
|
];
|
|
2723
2925
|
function resolveOtterDirFromCwd() {
|
|
2724
2926
|
const cwd = process.cwd();
|
|
2725
|
-
for (const
|
|
2726
|
-
const candidate =
|
|
2927
|
+
for (const relative2 of OTTER_SEARCH_PATHS) {
|
|
2928
|
+
const candidate = join5(cwd, relative2);
|
|
2727
2929
|
try {
|
|
2728
2930
|
lstatSync5(candidate);
|
|
2729
2931
|
return candidate;
|
|
@@ -2735,14 +2937,14 @@ function resolveOtterDirFromCwd() {
|
|
|
2735
2937
|
);
|
|
2736
2938
|
}
|
|
2737
2939
|
function loadOtterManifestsFromDir(otterDir) {
|
|
2738
|
-
const entries =
|
|
2940
|
+
const entries = readdirSync3(otterDir);
|
|
2739
2941
|
const manifests = [];
|
|
2740
2942
|
for (const filename of entries) {
|
|
2741
2943
|
if (!filename.endsWith("-otter.json")) continue;
|
|
2742
|
-
const filePath =
|
|
2944
|
+
const filePath = join5(otterDir, filename);
|
|
2743
2945
|
if (lstatSync5(filePath).isSymbolicLink()) continue;
|
|
2744
2946
|
try {
|
|
2745
|
-
const raw =
|
|
2947
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
2746
2948
|
const manifest = JSON.parse(raw);
|
|
2747
2949
|
manifests.push(manifest);
|
|
2748
2950
|
} catch (err) {
|
|
@@ -2847,19 +3049,19 @@ var DISTRIBUTED_NAMESPACES = ["@stackwright-pro", "@stackwright"];
|
|
|
2847
3049
|
function loadDistributedOtterManifests(cwd) {
|
|
2848
3050
|
const results = [];
|
|
2849
3051
|
for (const namespace of DISTRIBUTED_NAMESPACES) {
|
|
2850
|
-
const nsDir =
|
|
3052
|
+
const nsDir = join5(cwd, "node_modules", namespace);
|
|
2851
3053
|
let pkgDirNames;
|
|
2852
3054
|
try {
|
|
2853
|
-
pkgDirNames =
|
|
3055
|
+
pkgDirNames = readdirSync3(nsDir);
|
|
2854
3056
|
} catch {
|
|
2855
3057
|
continue;
|
|
2856
3058
|
}
|
|
2857
3059
|
for (const pkgDirName of pkgDirNames) {
|
|
2858
|
-
const pkgDir =
|
|
3060
|
+
const pkgDir = join5(nsDir, pkgDirName);
|
|
2859
3061
|
const packageName = `${namespace}/${pkgDirName}`;
|
|
2860
3062
|
let otterRelPaths;
|
|
2861
3063
|
try {
|
|
2862
|
-
const raw =
|
|
3064
|
+
const raw = readFileSync5(join5(pkgDir, "package.json"), "utf-8");
|
|
2863
3065
|
const pkgJson = JSON.parse(raw);
|
|
2864
3066
|
const declared = pkgJson.stackwright?.otters;
|
|
2865
3067
|
if (!Array.isArray(declared) || declared.length === 0) continue;
|
|
@@ -2868,9 +3070,9 @@ function loadDistributedOtterManifests(cwd) {
|
|
|
2868
3070
|
continue;
|
|
2869
3071
|
}
|
|
2870
3072
|
for (const relPath of otterRelPaths) {
|
|
2871
|
-
const otterFilePath =
|
|
3073
|
+
const otterFilePath = join5(pkgDir, relPath);
|
|
2872
3074
|
try {
|
|
2873
|
-
const raw =
|
|
3075
|
+
const raw = readFileSync5(otterFilePath, "utf-8");
|
|
2874
3076
|
const manifest = JSON.parse(raw);
|
|
2875
3077
|
results.push({ manifest, packageName });
|
|
2876
3078
|
} catch (err) {
|
|
@@ -2911,6 +3113,7 @@ function loadPipelineGraph() {
|
|
|
2911
3113
|
|
|
2912
3114
|
// src/tools/pipeline.ts
|
|
2913
3115
|
import { emit } from "@stackwright-pro/telemetry";
|
|
3116
|
+
var PULSE_CONTENT_PHASES = /* @__PURE__ */ new Set(["pages", "dashboard", "geo", "workflow"]);
|
|
2914
3117
|
var PHASE_ORDER = [
|
|
2915
3118
|
"designer",
|
|
2916
3119
|
"theme",
|
|
@@ -2987,11 +3190,11 @@ function createDefaultState() {
|
|
|
2987
3190
|
};
|
|
2988
3191
|
}
|
|
2989
3192
|
function statePath(cwd) {
|
|
2990
|
-
return
|
|
3193
|
+
return join6(cwd, ".stackwright", "pipeline-state.json");
|
|
2991
3194
|
}
|
|
2992
3195
|
function readState(cwd) {
|
|
2993
3196
|
const p = statePath(cwd);
|
|
2994
|
-
if (!
|
|
3197
|
+
if (!existsSync6(p)) return createDefaultState();
|
|
2995
3198
|
const raw = JSON.parse(safeReadSync(p));
|
|
2996
3199
|
if (typeof raw !== "object" || raw === null || raw.version !== "1.0") {
|
|
2997
3200
|
return createDefaultState();
|
|
@@ -2999,7 +3202,7 @@ function readState(cwd) {
|
|
|
2999
3202
|
return raw;
|
|
3000
3203
|
}
|
|
3001
3204
|
function safeWriteSync(filePath, content) {
|
|
3002
|
-
if (
|
|
3205
|
+
if (existsSync6(filePath)) {
|
|
3003
3206
|
const stat = lstatSync6(filePath);
|
|
3004
3207
|
if (stat.isSymbolicLink()) {
|
|
3005
3208
|
throw new Error(`Refusing to write to symlink: ${filePath}`);
|
|
@@ -3008,25 +3211,25 @@ function safeWriteSync(filePath, content) {
|
|
|
3008
3211
|
writeFileSync4(filePath, content);
|
|
3009
3212
|
}
|
|
3010
3213
|
function safeReadSync(filePath) {
|
|
3011
|
-
if (
|
|
3214
|
+
if (existsSync6(filePath)) {
|
|
3012
3215
|
const stat = lstatSync6(filePath);
|
|
3013
3216
|
if (stat.isSymbolicLink()) {
|
|
3014
3217
|
throw new Error(`Refusing to read symlink: ${filePath}`);
|
|
3015
3218
|
}
|
|
3016
3219
|
}
|
|
3017
|
-
return
|
|
3220
|
+
return readFileSync6(filePath, "utf-8");
|
|
3018
3221
|
}
|
|
3019
3222
|
function writeState(cwd, state) {
|
|
3020
|
-
const dir =
|
|
3223
|
+
const dir = join6(cwd, ".stackwright");
|
|
3021
3224
|
mkdirSync4(dir, { recursive: true });
|
|
3022
3225
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3023
3226
|
safeWriteSync(statePath(cwd), JSON.stringify(state, null, 2) + "\n");
|
|
3024
3227
|
}
|
|
3025
3228
|
function updateStateAtomic(cwd, mutator) {
|
|
3026
|
-
const dir =
|
|
3229
|
+
const dir = join6(cwd, ".stackwright");
|
|
3027
3230
|
mkdirSync4(dir, { recursive: true });
|
|
3028
3231
|
const path5 = statePath(cwd);
|
|
3029
|
-
if (!
|
|
3232
|
+
if (!existsSync6(path5)) {
|
|
3030
3233
|
safeWriteSync(path5, JSON.stringify(createDefaultState(), null, 2) + "\n");
|
|
3031
3234
|
}
|
|
3032
3235
|
const release = lockSync(path5, {
|
|
@@ -3060,8 +3263,8 @@ function handleGetPipelineState(_cwd) {
|
|
|
3060
3263
|
const cwd = _cwd ?? process.cwd();
|
|
3061
3264
|
try {
|
|
3062
3265
|
const state = readState(cwd);
|
|
3063
|
-
const keyPath =
|
|
3064
|
-
if (!
|
|
3266
|
+
const keyPath = join6(cwd, ".stackwright", "pipeline-keys.json");
|
|
3267
|
+
if (!existsSync6(keyPath)) {
|
|
3065
3268
|
try {
|
|
3066
3269
|
const { fingerprint } = initPipelineKeys(cwd);
|
|
3067
3270
|
state["signingKeyFingerprint"] = fingerprint;
|
|
@@ -3192,8 +3395,8 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3192
3395
|
isError: true
|
|
3193
3396
|
};
|
|
3194
3397
|
}
|
|
3195
|
-
const answerFile =
|
|
3196
|
-
if (!
|
|
3398
|
+
const answerFile = join6(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3399
|
+
if (!existsSync6(answerFile)) {
|
|
3197
3400
|
return {
|
|
3198
3401
|
text: JSON.stringify({ ready: false, phase, reason: "Answer file not found" }),
|
|
3199
3402
|
isError: false
|
|
@@ -3217,12 +3420,12 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3217
3420
|
}
|
|
3218
3421
|
}
|
|
3219
3422
|
try {
|
|
3220
|
-
const answersDir =
|
|
3423
|
+
const answersDir = join6(cwd, ".stackwright", "answers");
|
|
3221
3424
|
const answeredPhases = [];
|
|
3222
3425
|
const missingPhases = [];
|
|
3223
3426
|
for (const phase2 of PHASE_ORDER) {
|
|
3224
|
-
const answerFile =
|
|
3225
|
-
if (
|
|
3427
|
+
const answerFile = join6(answersDir, `${phase2}.json`);
|
|
3428
|
+
if (existsSync6(answerFile)) {
|
|
3226
3429
|
try {
|
|
3227
3430
|
const raw = safeReadSync(answerFile);
|
|
3228
3431
|
const parsed = JSON.parse(raw);
|
|
@@ -3291,7 +3494,7 @@ function handleGetReadyPhases(_cwd) {
|
|
|
3291
3494
|
function handleListArtifacts(_cwd) {
|
|
3292
3495
|
const cwd = _cwd ?? process.cwd();
|
|
3293
3496
|
try {
|
|
3294
|
-
const artifactsDir =
|
|
3497
|
+
const artifactsDir = join6(cwd, ".stackwright", "artifacts");
|
|
3295
3498
|
let manifest = null;
|
|
3296
3499
|
try {
|
|
3297
3500
|
manifest = loadSignatureManifest(cwd);
|
|
@@ -3301,8 +3504,8 @@ function handleListArtifacts(_cwd) {
|
|
|
3301
3504
|
let completedCount = 0;
|
|
3302
3505
|
for (const phase of PHASE_ORDER) {
|
|
3303
3506
|
const expectedFile = PHASE_ARTIFACT[phase];
|
|
3304
|
-
const fullPath =
|
|
3305
|
-
const exists =
|
|
3507
|
+
const fullPath = join6(artifactsDir, expectedFile);
|
|
3508
|
+
const exists = existsSync6(fullPath);
|
|
3306
3509
|
if (exists) completedCount++;
|
|
3307
3510
|
let signed = false;
|
|
3308
3511
|
let signatureValid = null;
|
|
@@ -3355,9 +3558,9 @@ function handleWritePhaseQuestions(input) {
|
|
|
3355
3558
|
}
|
|
3356
3559
|
} catch {
|
|
3357
3560
|
}
|
|
3358
|
-
const questionsDir =
|
|
3561
|
+
const questionsDir = join6(cwd, ".stackwright", "questions");
|
|
3359
3562
|
mkdirSync4(questionsDir, { recursive: true });
|
|
3360
|
-
const filePath =
|
|
3563
|
+
const filePath = join6(questionsDir, `${phase}.json`);
|
|
3361
3564
|
const payload = {
|
|
3362
3565
|
version: "1.0",
|
|
3363
3566
|
phase,
|
|
@@ -3397,14 +3600,14 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
3397
3600
|
};
|
|
3398
3601
|
}
|
|
3399
3602
|
try {
|
|
3400
|
-
const answersPath =
|
|
3603
|
+
const answersPath = join6(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3401
3604
|
let answers = {};
|
|
3402
|
-
if (
|
|
3605
|
+
if (existsSync6(answersPath)) {
|
|
3403
3606
|
answers = JSON.parse(safeReadSync(answersPath));
|
|
3404
3607
|
}
|
|
3405
3608
|
let buildContextText = "";
|
|
3406
|
-
const buildContextPath =
|
|
3407
|
-
if (
|
|
3609
|
+
const buildContextPath = join6(cwd, ".stackwright", "build-context.json");
|
|
3610
|
+
if (existsSync6(buildContextPath)) {
|
|
3408
3611
|
try {
|
|
3409
3612
|
const bcRaw = JSON.parse(safeReadSync(buildContextPath));
|
|
3410
3613
|
if (typeof bcRaw.buildContext === "string" && bcRaw.buildContext.trim().length > 0) {
|
|
@@ -3419,8 +3622,8 @@ function handleBuildSpecialistPrompt(input) {
|
|
|
3419
3622
|
const missingDependencies = [];
|
|
3420
3623
|
for (const dep of deps) {
|
|
3421
3624
|
const artifactFile = PHASE_ARTIFACT[dep];
|
|
3422
|
-
const artifactPath =
|
|
3423
|
-
if (
|
|
3625
|
+
const artifactPath = join6(cwd, ".stackwright", "artifacts", artifactFile);
|
|
3626
|
+
if (existsSync6(artifactPath)) {
|
|
3424
3627
|
const rawContent = safeReadSync(artifactPath);
|
|
3425
3628
|
const rawBytes = Buffer.from(rawContent, "utf-8");
|
|
3426
3629
|
const content = JSON.parse(rawContent);
|
|
@@ -3485,8 +3688,8 @@ ${JSON.stringify(content, null, 2)}`
|
|
|
3485
3688
|
parts.push("BUILD_CONTEXT:", buildContextText, "");
|
|
3486
3689
|
}
|
|
3487
3690
|
if (phase === "auth") {
|
|
3488
|
-
const initContextPath =
|
|
3489
|
-
if (
|
|
3691
|
+
const initContextPath = join6(cwd, ".stackwright", "init-context.json");
|
|
3692
|
+
if (existsSync6(initContextPath)) {
|
|
3490
3693
|
try {
|
|
3491
3694
|
const initContext = JSON.parse(safeReadSync(initContextPath));
|
|
3492
3695
|
if (initContext.devOnly === true || initContext.nonInteractive === true) {
|
|
@@ -3991,13 +4194,13 @@ function _validateArtifactInner(input) {
|
|
|
3991
4194
|
// to a flow or state-machine in this services-config artifact.
|
|
3992
4195
|
// Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
|
|
3993
4196
|
services: (artifact2) => {
|
|
3994
|
-
const workflowArtifactPath =
|
|
3995
|
-
if (!
|
|
4197
|
+
const workflowArtifactPath = join6(cwd, ".stackwright", "artifacts", "workflow-config.json");
|
|
4198
|
+
if (!existsSync6(workflowArtifactPath)) {
|
|
3996
4199
|
return { success: true };
|
|
3997
4200
|
}
|
|
3998
4201
|
let workflowArtifact;
|
|
3999
4202
|
try {
|
|
4000
|
-
workflowArtifact = JSON.parse(
|
|
4203
|
+
workflowArtifact = JSON.parse(readFileSync6(workflowArtifactPath, "utf-8"));
|
|
4001
4204
|
} catch {
|
|
4002
4205
|
return { success: true };
|
|
4003
4206
|
}
|
|
@@ -4067,11 +4270,23 @@ function _validateArtifactInner(input) {
|
|
|
4067
4270
|
return { text: JSON.stringify(result), isError: false };
|
|
4068
4271
|
}
|
|
4069
4272
|
}
|
|
4273
|
+
if (PULSE_CONTENT_PHASES.has(phase)) {
|
|
4274
|
+
const pulseResult = validatePulseContent(cwd);
|
|
4275
|
+
if (!pulseResult.valid) {
|
|
4276
|
+
const result = {
|
|
4277
|
+
valid: false,
|
|
4278
|
+
phase,
|
|
4279
|
+
violation: "schema-mismatch",
|
|
4280
|
+
retryPrompt: formatPulseRetryPrompt(pulseResult)
|
|
4281
|
+
};
|
|
4282
|
+
return { text: JSON.stringify(result), isError: false };
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4070
4285
|
try {
|
|
4071
|
-
const artifactsDir =
|
|
4286
|
+
const artifactsDir = join6(cwd, ".stackwright", "artifacts");
|
|
4072
4287
|
mkdirSync4(artifactsDir, { recursive: true });
|
|
4073
4288
|
const artifactFile = phase === "api" && input.integrationName ? `api-config-${input.integrationName}.json` : PHASE_ARTIFACT[phase];
|
|
4074
|
-
const artifactPath =
|
|
4289
|
+
const artifactPath = join6(artifactsDir, artifactFile);
|
|
4075
4290
|
const serialized = JSON.stringify(artifact, null, 2) + "\n";
|
|
4076
4291
|
const artifactBytes = Buffer.from(serialized, "utf-8");
|
|
4077
4292
|
safeWriteSync(artifactPath, serialized);
|
|
@@ -4290,9 +4505,9 @@ function registerPipelineTools(server2) {
|
|
|
4290
4505
|
isError: true
|
|
4291
4506
|
};
|
|
4292
4507
|
}
|
|
4293
|
-
const questionsDir =
|
|
4508
|
+
const questionsDir = join6(process.cwd(), ".stackwright", "questions");
|
|
4294
4509
|
mkdirSync4(questionsDir, { recursive: true });
|
|
4295
|
-
const outPath =
|
|
4510
|
+
const outPath = join6(questionsDir, `${phase}.json`);
|
|
4296
4511
|
writeFileSync4(
|
|
4297
4512
|
outPath,
|
|
4298
4513
|
JSON.stringify({ phase, questions, writtenAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
@@ -4380,8 +4595,8 @@ function registerPipelineTools(server2) {
|
|
|
4380
4595
|
|
|
4381
4596
|
// src/tools/safe-write.ts
|
|
4382
4597
|
import { z as z14 } from "zod";
|
|
4383
|
-
import { writeFileSync as writeFileSync5, readFileSync as
|
|
4384
|
-
import { normalize, isAbsolute, dirname, join as
|
|
4598
|
+
import { writeFileSync as writeFileSync5, readFileSync as readFileSync7, existsSync as existsSync7, mkdirSync as mkdirSync5, lstatSync as lstatSync7 } from "fs";
|
|
4599
|
+
import { normalize, isAbsolute, dirname, join as join7 } from "path";
|
|
4385
4600
|
import { emit as emit2 } from "@stackwright-pro/telemetry";
|
|
4386
4601
|
var OTTER_WRITE_ALLOWLISTS = {
|
|
4387
4602
|
"stackwright-pro-designer-otter": [
|
|
@@ -4563,21 +4778,21 @@ function extractContentTypes(yamlContent) {
|
|
|
4563
4778
|
return types.length > 0 ? types : ["unknown"];
|
|
4564
4779
|
}
|
|
4565
4780
|
function readPageRegistry(cwd) {
|
|
4566
|
-
const regPath =
|
|
4567
|
-
if (!
|
|
4781
|
+
const regPath = join7(cwd, PAGE_REGISTRY_FILE);
|
|
4782
|
+
if (!existsSync7(regPath)) return {};
|
|
4568
4783
|
try {
|
|
4569
4784
|
const stat = lstatSync7(regPath);
|
|
4570
4785
|
if (stat.isSymbolicLink()) return {};
|
|
4571
|
-
return JSON.parse(
|
|
4786
|
+
return JSON.parse(readFileSync7(regPath, "utf-8"));
|
|
4572
4787
|
} catch {
|
|
4573
4788
|
return {};
|
|
4574
4789
|
}
|
|
4575
4790
|
}
|
|
4576
4791
|
function writePageRegistry(cwd, registry) {
|
|
4577
|
-
const dir =
|
|
4792
|
+
const dir = join7(cwd, ".stackwright");
|
|
4578
4793
|
mkdirSync5(dir, { recursive: true });
|
|
4579
|
-
const regPath =
|
|
4580
|
-
if (
|
|
4794
|
+
const regPath = join7(cwd, PAGE_REGISTRY_FILE);
|
|
4795
|
+
if (existsSync7(regPath)) {
|
|
4581
4796
|
const stat = lstatSync7(regPath);
|
|
4582
4797
|
if (stat.isSymbolicLink()) {
|
|
4583
4798
|
throw new Error("Refusing to write page registry through symlink");
|
|
@@ -4749,8 +4964,8 @@ function _safeWriteInner(input) {
|
|
|
4749
4964
|
return { text: JSON.stringify(result), isError: true };
|
|
4750
4965
|
}
|
|
4751
4966
|
const normalized = normalize(filePath);
|
|
4752
|
-
const fullPath =
|
|
4753
|
-
if (
|
|
4967
|
+
const fullPath = join7(cwd, normalized);
|
|
4968
|
+
if (existsSync7(fullPath)) {
|
|
4754
4969
|
try {
|
|
4755
4970
|
const stat = lstatSync7(fullPath);
|
|
4756
4971
|
if (stat.isSymbolicLink()) {
|
|
@@ -4880,8 +5095,8 @@ function registerSafeWriteTools(server2) {
|
|
|
4880
5095
|
|
|
4881
5096
|
// src/tools/auth.ts
|
|
4882
5097
|
import { z as z15 } from "zod";
|
|
4883
|
-
import { readFileSync as
|
|
4884
|
-
import { join as
|
|
5098
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
5099
|
+
import { join as join8 } from "path";
|
|
4885
5100
|
function buildHierarchy(roles) {
|
|
4886
5101
|
const h = {};
|
|
4887
5102
|
for (let i = 0; i < roles.length - 1; i++) {
|
|
@@ -4908,9 +5123,9 @@ ${indent} requiredRole: ${r.requiredRole}`).join("\n");
|
|
|
4908
5123
|
}
|
|
4909
5124
|
function detectNextMajorVersion(cwd) {
|
|
4910
5125
|
try {
|
|
4911
|
-
const pkgPath =
|
|
4912
|
-
if (!
|
|
4913
|
-
const pkg = JSON.parse(
|
|
5126
|
+
const pkgPath = join8(cwd, "package.json");
|
|
5127
|
+
if (!existsSync8(pkgPath)) return null;
|
|
5128
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
4914
5129
|
const nextVersion = pkg.dependencies?.next ?? pkg.devDependencies?.next;
|
|
4915
5130
|
if (!nextVersion) return null;
|
|
4916
5131
|
const match = nextVersion.match(/(\d+)/);
|
|
@@ -5574,7 +5789,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5574
5789
|
auditRetentionDays,
|
|
5575
5790
|
normalizedRoutes
|
|
5576
5791
|
);
|
|
5577
|
-
writeFileSync6(
|
|
5792
|
+
writeFileSync6(join8(cwd, conventionFile), authFileContent, "utf8");
|
|
5578
5793
|
filesWritten.push(conventionFile);
|
|
5579
5794
|
} catch (err) {
|
|
5580
5795
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5594,9 +5809,9 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5594
5809
|
if (!devOnly) {
|
|
5595
5810
|
try {
|
|
5596
5811
|
const envBlock = generateEnvBlock(effectiveMethod, params);
|
|
5597
|
-
const envPath =
|
|
5598
|
-
if (
|
|
5599
|
-
const existing =
|
|
5812
|
+
const envPath = join8(cwd, ".env.example");
|
|
5813
|
+
if (existsSync8(envPath)) {
|
|
5814
|
+
const existing = readFileSync8(envPath, "utf8");
|
|
5600
5815
|
writeFileSync6(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
|
|
5601
5816
|
} else {
|
|
5602
5817
|
writeFileSync6(envPath, envBlock, "utf8");
|
|
@@ -5617,10 +5832,10 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5617
5832
|
}
|
|
5618
5833
|
if (devOnly) {
|
|
5619
5834
|
try {
|
|
5620
|
-
const mockAuthDir =
|
|
5835
|
+
const mockAuthDir = join8(cwd, "lib");
|
|
5621
5836
|
mkdirSync6(mockAuthDir, { recursive: true });
|
|
5622
5837
|
const mockAuthContent = generateMockAuthContent(roles, mockUsers);
|
|
5623
|
-
writeFileSync6(
|
|
5838
|
+
writeFileSync6(join8(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
|
|
5624
5839
|
filesWritten.push("lib/mock-auth.ts");
|
|
5625
5840
|
} catch (err) {
|
|
5626
5841
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5638,9 +5853,9 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5638
5853
|
};
|
|
5639
5854
|
}
|
|
5640
5855
|
try {
|
|
5641
|
-
const pkgPath =
|
|
5642
|
-
if (
|
|
5643
|
-
const existingPkg =
|
|
5856
|
+
const pkgPath = join8(cwd, "package.json");
|
|
5857
|
+
if (existsSync8(pkgPath)) {
|
|
5858
|
+
const existingPkg = readFileSync8(pkgPath, "utf8");
|
|
5644
5859
|
const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
|
|
5645
5860
|
writeFileSync6(pkgPath, updatedPkg, "utf8");
|
|
5646
5861
|
filesWritten.push("package.json");
|
|
@@ -5683,7 +5898,7 @@ async function configureAuthHandler(params, cwd) {
|
|
|
5683
5898
|
normalizedRoutes,
|
|
5684
5899
|
useProxy
|
|
5685
5900
|
);
|
|
5686
|
-
writeFileSync6(
|
|
5901
|
+
writeFileSync6(join8(cwd, "stackwright.auth.yml"), authYaml, "utf8");
|
|
5687
5902
|
filesWritten.push("stackwright.auth.yml");
|
|
5688
5903
|
} catch (err) {
|
|
5689
5904
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -5796,8 +6011,8 @@ function registerAuthTools(server2) {
|
|
|
5796
6011
|
|
|
5797
6012
|
// src/integrity.ts
|
|
5798
6013
|
import { createHash as createHash4, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
5799
|
-
import { readFileSync as
|
|
5800
|
-
import { join as
|
|
6014
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync4, lstatSync as lstatSync8 } from "fs";
|
|
6015
|
+
import { join as join9, basename } from "path";
|
|
5801
6016
|
var _checksums = /* @__PURE__ */ new Map([
|
|
5802
6017
|
[
|
|
5803
6018
|
"stackwright-pro-api-otter.json",
|
|
@@ -5813,7 +6028,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
5813
6028
|
],
|
|
5814
6029
|
[
|
|
5815
6030
|
"stackwright-pro-data-otter.json",
|
|
5816
|
-
"
|
|
6031
|
+
"be4e3d6f530ae2d6b79d6412e8d2d6defb3caffddedf5be4f928976a72206074"
|
|
5817
6032
|
],
|
|
5818
6033
|
[
|
|
5819
6034
|
"stackwright-pro-designer-otter.json",
|
|
@@ -5905,7 +6120,7 @@ function verifyOtterFile(filePath) {
|
|
|
5905
6120
|
}
|
|
5906
6121
|
let raw;
|
|
5907
6122
|
try {
|
|
5908
|
-
raw =
|
|
6123
|
+
raw = readFileSync9(filePath);
|
|
5909
6124
|
} catch (err) {
|
|
5910
6125
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5911
6126
|
return { verified: false, filename, error: `Cannot read file: ${msg}` };
|
|
@@ -5955,7 +6170,7 @@ function verifyAllOtters(otterDir) {
|
|
|
5955
6170
|
const unknown = [];
|
|
5956
6171
|
let entries;
|
|
5957
6172
|
try {
|
|
5958
|
-
entries =
|
|
6173
|
+
entries = readdirSync4(otterDir);
|
|
5959
6174
|
} catch (err) {
|
|
5960
6175
|
const msg = err instanceof Error ? err.message : String(err);
|
|
5961
6176
|
return {
|
|
@@ -5966,7 +6181,7 @@ function verifyAllOtters(otterDir) {
|
|
|
5966
6181
|
}
|
|
5967
6182
|
const otterFiles = entries.filter((f) => f.endsWith("-otter.json"));
|
|
5968
6183
|
for (const filename of otterFiles) {
|
|
5969
|
-
const filePath =
|
|
6184
|
+
const filePath = join9(otterDir, filename);
|
|
5970
6185
|
try {
|
|
5971
6186
|
if (lstatSync8(filePath).isSymbolicLink()) {
|
|
5972
6187
|
failed.push({ filename, error: "Skipped: symlink" });
|
|
@@ -5993,8 +6208,8 @@ function verifyAllOtters(otterDir) {
|
|
|
5993
6208
|
var DEFAULT_SEARCH_PATHS = ["node_modules/@stackwright-pro/otters/src/", "packages/otters/src/"];
|
|
5994
6209
|
function resolveOtterDir() {
|
|
5995
6210
|
const cwd = process.cwd();
|
|
5996
|
-
for (const
|
|
5997
|
-
const candidate =
|
|
6211
|
+
for (const relative2 of DEFAULT_SEARCH_PATHS) {
|
|
6212
|
+
const candidate = join9(cwd, relative2);
|
|
5998
6213
|
try {
|
|
5999
6214
|
lstatSync8(candidate);
|
|
6000
6215
|
return candidate;
|
|
@@ -6075,13 +6290,13 @@ function registerIntegrityTools(server2) {
|
|
|
6075
6290
|
|
|
6076
6291
|
// src/tools/domain.ts
|
|
6077
6292
|
import { z as z16 } from "zod";
|
|
6078
|
-
import { readFileSync as
|
|
6079
|
-
import { join as
|
|
6293
|
+
import { readFileSync as readFileSync10, existsSync as existsSync9 } from "fs";
|
|
6294
|
+
import { join as join10 } from "path";
|
|
6080
6295
|
function handleListCollections(input) {
|
|
6081
6296
|
const cwd = input._cwd ?? process.cwd();
|
|
6082
6297
|
const sources = [
|
|
6083
6298
|
{
|
|
6084
|
-
path:
|
|
6299
|
+
path: join10(cwd, ".stackwright", "artifacts", "data-config.json"),
|
|
6085
6300
|
source: "data-config.json",
|
|
6086
6301
|
parse: (raw) => {
|
|
6087
6302
|
const parsed = JSON.parse(raw);
|
|
@@ -6092,15 +6307,15 @@ function handleListCollections(input) {
|
|
|
6092
6307
|
}
|
|
6093
6308
|
},
|
|
6094
6309
|
{
|
|
6095
|
-
path:
|
|
6310
|
+
path: join10(cwd, "stackwright.yml"),
|
|
6096
6311
|
source: "stackwright.yml",
|
|
6097
6312
|
parse: extractCollectionsFromYaml
|
|
6098
6313
|
}
|
|
6099
6314
|
];
|
|
6100
6315
|
for (const { path: path5, source, parse } of sources) {
|
|
6101
|
-
if (!
|
|
6316
|
+
if (!existsSync9(path5)) continue;
|
|
6102
6317
|
try {
|
|
6103
|
-
const collections = parse(
|
|
6318
|
+
const collections = parse(readFileSync10(path5, "utf8"));
|
|
6104
6319
|
return {
|
|
6105
6320
|
text: JSON.stringify({ collections, source, collectionCount: collections.length }),
|
|
6106
6321
|
isError: false
|
|
@@ -6251,8 +6466,8 @@ function handleValidateWorkflow(input) {
|
|
|
6251
6466
|
if (input.workflow && Object.keys(input.workflow).length > 0) {
|
|
6252
6467
|
raw = input.workflow;
|
|
6253
6468
|
} else {
|
|
6254
|
-
const artifactPath =
|
|
6255
|
-
if (!
|
|
6469
|
+
const artifactPath = join10(cwd, ".stackwright", "artifacts", "workflow-config.json");
|
|
6470
|
+
if (!existsSync9(artifactPath)) {
|
|
6256
6471
|
return fail([
|
|
6257
6472
|
{
|
|
6258
6473
|
code: "NO_WORKFLOW",
|
|
@@ -6261,7 +6476,7 @@ function handleValidateWorkflow(input) {
|
|
|
6261
6476
|
]);
|
|
6262
6477
|
}
|
|
6263
6478
|
try {
|
|
6264
|
-
raw = JSON.parse(
|
|
6479
|
+
raw = JSON.parse(readFileSync10(artifactPath, "utf8"));
|
|
6265
6480
|
} catch (err) {
|
|
6266
6481
|
return fail([{ code: "INVALID_JSON", message: `Failed to parse workflow artifact: ${err}` }]);
|
|
6267
6482
|
}
|
|
@@ -6573,7 +6788,7 @@ import * as fs2 from "fs";
|
|
|
6573
6788
|
import * as os from "os";
|
|
6574
6789
|
import * as path3 from "path";
|
|
6575
6790
|
import { z as z18 } from "zod";
|
|
6576
|
-
import { load as
|
|
6791
|
+
import { load as yamlLoad3, dump as yamlDump } from "js-yaml";
|
|
6577
6792
|
import { writePage, resolvePagesDir } from "@stackwright/cli";
|
|
6578
6793
|
import { isProContentType, isOssTypeWithProExtension } from "@stackwright-pro/types";
|
|
6579
6794
|
var BCP47_RE = /^[a-z]{2}(-[A-Z]{2})?$/;
|
|
@@ -6627,7 +6842,7 @@ function handleProWritePage(input) {
|
|
|
6627
6842
|
}
|
|
6628
6843
|
let parsed;
|
|
6629
6844
|
try {
|
|
6630
|
-
parsed =
|
|
6845
|
+
parsed = yamlLoad3(content);
|
|
6631
6846
|
} catch (err) {
|
|
6632
6847
|
return {
|
|
6633
6848
|
isError: true,
|
|
@@ -6699,16 +6914,16 @@ function registerProPageTools(server2) {
|
|
|
6699
6914
|
|
|
6700
6915
|
// src/tools/scaffold-cleanup.ts
|
|
6701
6916
|
import {
|
|
6702
|
-
existsSync as
|
|
6917
|
+
existsSync as existsSync11,
|
|
6703
6918
|
lstatSync as lstatSync9,
|
|
6704
6919
|
unlinkSync as unlinkSync2,
|
|
6705
|
-
readFileSync as
|
|
6920
|
+
readFileSync as readFileSync11,
|
|
6706
6921
|
writeFileSync as writeFileSync8,
|
|
6707
|
-
readdirSync as
|
|
6922
|
+
readdirSync as readdirSync5,
|
|
6708
6923
|
rmdirSync,
|
|
6709
6924
|
mkdirSync as mkdirSync8
|
|
6710
6925
|
} from "fs";
|
|
6711
|
-
import { join as
|
|
6926
|
+
import { join as join12, dirname as dirname3 } from "path";
|
|
6712
6927
|
var SCAFFOLD_FILES_TO_DELETE = [
|
|
6713
6928
|
"content/posts/getting-started.yaml",
|
|
6714
6929
|
"content/posts/hello-world.yaml"
|
|
@@ -6733,12 +6948,12 @@ var FALLBACK_COLORS = {
|
|
|
6733
6948
|
mutedForeground: "#737373"
|
|
6734
6949
|
};
|
|
6735
6950
|
function readThemeColors(cwd) {
|
|
6736
|
-
const tokenPath =
|
|
6737
|
-
if (!
|
|
6951
|
+
const tokenPath = join12(cwd, ".stackwright", "artifacts", "theme-tokens.json");
|
|
6952
|
+
if (!existsSync11(tokenPath)) return { ...FALLBACK_COLORS };
|
|
6738
6953
|
const stat = lstatSync9(tokenPath);
|
|
6739
6954
|
if (stat.isSymbolicLink()) return { ...FALLBACK_COLORS };
|
|
6740
6955
|
try {
|
|
6741
|
-
const raw = JSON.parse(
|
|
6956
|
+
const raw = JSON.parse(readFileSync11(tokenPath, "utf-8"));
|
|
6742
6957
|
const css = raw?.cssVariables ?? {};
|
|
6743
6958
|
const toHsl = (hslValue) => {
|
|
6744
6959
|
if (!hslValue || typeof hslValue !== "string") return null;
|
|
@@ -6798,15 +7013,15 @@ function generateNotFoundPage(colors) {
|
|
|
6798
7013
|
`;
|
|
6799
7014
|
}
|
|
6800
7015
|
function isSafePath(fullPath) {
|
|
6801
|
-
if (!
|
|
7016
|
+
if (!existsSync11(fullPath)) return true;
|
|
6802
7017
|
const stat = lstatSync9(fullPath);
|
|
6803
7018
|
return !stat.isSymbolicLink();
|
|
6804
7019
|
}
|
|
6805
7020
|
function isDirEmpty(dirPath) {
|
|
6806
|
-
if (!
|
|
7021
|
+
if (!existsSync11(dirPath)) return false;
|
|
6807
7022
|
const stat = lstatSync9(dirPath);
|
|
6808
7023
|
if (!stat.isDirectory()) return false;
|
|
6809
|
-
return
|
|
7024
|
+
return readdirSync5(dirPath).length === 0;
|
|
6810
7025
|
}
|
|
6811
7026
|
function handleCleanupScaffold(_cwd) {
|
|
6812
7027
|
const cwd = _cwd ?? process.cwd();
|
|
@@ -6819,8 +7034,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6819
7034
|
cleanupWarnings: []
|
|
6820
7035
|
};
|
|
6821
7036
|
for (const relPath of SCAFFOLD_FILES_TO_DELETE) {
|
|
6822
|
-
const fullPath =
|
|
6823
|
-
if (!
|
|
7037
|
+
const fullPath = join12(cwd, relPath);
|
|
7038
|
+
if (!existsSync11(fullPath)) {
|
|
6824
7039
|
result.skipped.push(relPath);
|
|
6825
7040
|
continue;
|
|
6826
7041
|
}
|
|
@@ -6837,13 +7052,13 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6837
7052
|
}
|
|
6838
7053
|
{
|
|
6839
7054
|
const relPath = "pages/getting-started/content.yml";
|
|
6840
|
-
const fullPath =
|
|
6841
|
-
if (!
|
|
7055
|
+
const fullPath = join12(cwd, relPath);
|
|
7056
|
+
if (!existsSync11(fullPath)) {
|
|
6842
7057
|
result.skipped.push(relPath);
|
|
6843
7058
|
} else if (!isSafePath(fullPath)) {
|
|
6844
7059
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
6845
7060
|
} else {
|
|
6846
|
-
const content =
|
|
7061
|
+
const content = readFileSync11(fullPath, "utf-8");
|
|
6847
7062
|
const isScaffoldDefault = GETTING_STARTED_SCAFFOLD_FINGERPRINTS.some(
|
|
6848
7063
|
(marker) => content.includes(marker)
|
|
6849
7064
|
);
|
|
@@ -6863,16 +7078,16 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6863
7078
|
}
|
|
6864
7079
|
{
|
|
6865
7080
|
const relPath = `content/posts/${POSTS_COLLECTION_FILE}`;
|
|
6866
|
-
const fullPath =
|
|
6867
|
-
const postsDir =
|
|
6868
|
-
if (!
|
|
7081
|
+
const fullPath = join12(cwd, relPath);
|
|
7082
|
+
const postsDir = join12(cwd, "content/posts");
|
|
7083
|
+
if (!existsSync11(fullPath)) {
|
|
6869
7084
|
result.skipped.push(relPath);
|
|
6870
7085
|
} else if (!isSafePath(fullPath)) {
|
|
6871
7086
|
result.errors.push(`Refusing to delete symlink: ${relPath}`);
|
|
6872
|
-
} else if (!
|
|
7087
|
+
} else if (!existsSync11(postsDir)) {
|
|
6873
7088
|
result.skipped.push(relPath);
|
|
6874
7089
|
} else {
|
|
6875
|
-
const userPostFiles =
|
|
7090
|
+
const userPostFiles = readdirSync5(postsDir).filter(
|
|
6876
7091
|
(f) => f !== POSTS_COLLECTION_FILE && POST_FILE_EXTENSIONS.some((ext) => f.endsWith(ext))
|
|
6877
7092
|
);
|
|
6878
7093
|
if (userPostFiles.length === 0) {
|
|
@@ -6890,8 +7105,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6890
7105
|
}
|
|
6891
7106
|
}
|
|
6892
7107
|
for (const relDir of SCAFFOLD_DIRS_TO_PRUNE) {
|
|
6893
|
-
const fullDir =
|
|
6894
|
-
if (!
|
|
7108
|
+
const fullDir = join12(cwd, relDir);
|
|
7109
|
+
if (!existsSync11(fullDir)) continue;
|
|
6895
7110
|
if (!isSafePath(fullDir)) {
|
|
6896
7111
|
result.errors.push(`Refusing to prune symlink directory: ${relDir}`);
|
|
6897
7112
|
continue;
|
|
@@ -6906,8 +7121,8 @@ function handleCleanupScaffold(_cwd) {
|
|
|
6906
7121
|
}
|
|
6907
7122
|
}
|
|
6908
7123
|
{
|
|
6909
|
-
const fullPath =
|
|
6910
|
-
if (!
|
|
7124
|
+
const fullPath = join12(cwd, NOT_FOUND_PATH);
|
|
7125
|
+
if (!existsSync11(fullPath)) {
|
|
6911
7126
|
result.skipped.push(NOT_FOUND_PATH);
|
|
6912
7127
|
} else if (!isSafePath(fullPath)) {
|
|
6913
7128
|
result.errors.push(`Refusing to rewrite symlink: ${NOT_FOUND_PATH}`);
|
|
@@ -7428,14 +7643,14 @@ ${results.join("\n")}`;
|
|
|
7428
7643
|
|
|
7429
7644
|
// src/tools/strip-legacy-integrations.ts
|
|
7430
7645
|
import { z as z21 } from "zod";
|
|
7431
|
-
import { existsSync as
|
|
7432
|
-
import { join as
|
|
7646
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
|
|
7647
|
+
import { join as join13 } from "path";
|
|
7433
7648
|
function handleStripLegacyIntegrations(input) {
|
|
7434
7649
|
const { projectRoot } = input;
|
|
7435
|
-
const ymlPath =
|
|
7436
|
-
const yamlPath =
|
|
7437
|
-
if (!
|
|
7438
|
-
if (
|
|
7650
|
+
const ymlPath = join13(projectRoot, "stackwright.yml");
|
|
7651
|
+
const yamlPath = join13(projectRoot, "stackwright.yaml");
|
|
7652
|
+
if (!existsSync12(ymlPath)) {
|
|
7653
|
+
if (existsSync12(yamlPath)) {
|
|
7439
7654
|
return {
|
|
7440
7655
|
changed: false,
|
|
7441
7656
|
removedEntries: 0,
|
|
@@ -7448,7 +7663,7 @@ function handleStripLegacyIntegrations(input) {
|
|
|
7448
7663
|
message: "stackwright.yml not found \u2014 no-op."
|
|
7449
7664
|
};
|
|
7450
7665
|
}
|
|
7451
|
-
const raw =
|
|
7666
|
+
const raw = readFileSync12(ymlPath, "utf-8");
|
|
7452
7667
|
const isCRLF = raw.includes("\r\n");
|
|
7453
7668
|
const content = isCRLF ? raw.replace(/\r\n/g, "\n") : raw;
|
|
7454
7669
|
const lines = content.split("\n");
|
|
@@ -7503,15 +7718,15 @@ function registerStripLegacyIntegrationsTool(server2) {
|
|
|
7503
7718
|
|
|
7504
7719
|
// src/tools/emitters.ts
|
|
7505
7720
|
import { z as z22 } from "zod";
|
|
7506
|
-
import { readFileSync as
|
|
7507
|
-
import { join as
|
|
7721
|
+
import { readFileSync as readFileSync13, existsSync as existsSync13, readdirSync as readdirSync6 } from "fs";
|
|
7722
|
+
import { join as join14 } from "path";
|
|
7508
7723
|
import yaml from "js-yaml";
|
|
7509
7724
|
import { emitEnvLocal, emitProvidersFile, emitMockPorts } from "@stackwright-pro/emitters";
|
|
7510
7725
|
function readIntegrations(cwd) {
|
|
7511
|
-
const filePath =
|
|
7512
|
-
if (!
|
|
7726
|
+
const filePath = join14(cwd, "stackwright.integrations.yml");
|
|
7727
|
+
if (!existsSync13(filePath)) return [];
|
|
7513
7728
|
try {
|
|
7514
|
-
const raw = yaml.load(
|
|
7729
|
+
const raw = yaml.load(readFileSync13(filePath, "utf-8"));
|
|
7515
7730
|
if (!raw?.integrations || !Array.isArray(raw.integrations)) return [];
|
|
7516
7731
|
return raw.integrations.filter((i) => typeof i.name === "string").map((i) => ({
|
|
7517
7732
|
name: i.name,
|
|
@@ -7522,15 +7737,15 @@ function readIntegrations(cwd) {
|
|
|
7522
7737
|
}
|
|
7523
7738
|
}
|
|
7524
7739
|
function readServiceTokenRefs(cwd) {
|
|
7525
|
-
const servicesDir =
|
|
7526
|
-
if (!
|
|
7740
|
+
const servicesDir = join14(cwd, "services");
|
|
7741
|
+
if (!existsSync13(servicesDir)) return [];
|
|
7527
7742
|
try {
|
|
7528
|
-
const files =
|
|
7743
|
+
const files = readdirSync6(servicesDir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
7529
7744
|
const refs = [];
|
|
7530
7745
|
for (const file of files) {
|
|
7531
7746
|
try {
|
|
7532
7747
|
const raw = yaml.load(
|
|
7533
|
-
|
|
7748
|
+
readFileSync13(join14(servicesDir, file), "utf-8")
|
|
7534
7749
|
);
|
|
7535
7750
|
if (raw?.tokenEnv && typeof raw.tokenEnv === "string") {
|
|
7536
7751
|
refs.push({
|
|
@@ -7680,8 +7895,8 @@ This is the third concrete instance of the swp-zf9y deterministic emitter patter
|
|
|
7680
7895
|
|
|
7681
7896
|
// src/tools/list-specs.ts
|
|
7682
7897
|
import { z as z23 } from "zod";
|
|
7683
|
-
import { readdirSync as
|
|
7684
|
-
import { join as
|
|
7898
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync14, statSync as statSync2, existsSync as existsSync14 } from "fs";
|
|
7899
|
+
import { join as join15, extname, basename as basename2 } from "path";
|
|
7685
7900
|
var STRIP_SUFFIXES = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
7686
7901
|
var ALLOWED_EXTS = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
|
|
7687
7902
|
function deriveIntegrationName(fileName) {
|
|
@@ -7703,21 +7918,21 @@ function detectFormat(snippet) {
|
|
|
7703
7918
|
}
|
|
7704
7919
|
function handleListSpecs(input) {
|
|
7705
7920
|
const { projectRoot } = input;
|
|
7706
|
-
const specsDir =
|
|
7921
|
+
const specsDir = join15(projectRoot, "specs");
|
|
7707
7922
|
const empty = { projectRoot, specs: [], totalCount: 0 };
|
|
7708
|
-
if (!
|
|
7923
|
+
if (!existsSync14(specsDir)) return empty;
|
|
7709
7924
|
let entries;
|
|
7710
7925
|
try {
|
|
7711
|
-
entries =
|
|
7926
|
+
entries = readdirSync7(specsDir);
|
|
7712
7927
|
} catch {
|
|
7713
7928
|
return empty;
|
|
7714
7929
|
}
|
|
7715
7930
|
const specs = [];
|
|
7716
7931
|
for (const entry of entries) {
|
|
7717
|
-
const fullPath =
|
|
7932
|
+
const fullPath = join15(specsDir, entry);
|
|
7718
7933
|
let stat;
|
|
7719
7934
|
try {
|
|
7720
|
-
stat =
|
|
7935
|
+
stat = statSync2(fullPath);
|
|
7721
7936
|
} catch {
|
|
7722
7937
|
continue;
|
|
7723
7938
|
}
|
|
@@ -7725,7 +7940,7 @@ function handleListSpecs(input) {
|
|
|
7725
7940
|
if (!ALLOWED_EXTS.has(extname(entry).toLowerCase())) continue;
|
|
7726
7941
|
let snippet = "";
|
|
7727
7942
|
try {
|
|
7728
|
-
snippet =
|
|
7943
|
+
snippet = readFileSync14(fullPath, "utf-8").slice(0, 2048);
|
|
7729
7944
|
} catch {
|
|
7730
7945
|
}
|
|
7731
7946
|
specs.push({
|
|
@@ -7757,9 +7972,9 @@ function registerListSpecsTool(server2) {
|
|
|
7757
7972
|
|
|
7758
7973
|
// src/tools/consolidate-integrations.ts
|
|
7759
7974
|
import { z as z24 } from "zod";
|
|
7760
|
-
import { readdirSync as
|
|
7975
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
7761
7976
|
import { lockSync as lockSync2 } from "proper-lockfile";
|
|
7762
|
-
import { join as
|
|
7977
|
+
import { join as join16, basename as basename3 } from "path";
|
|
7763
7978
|
import yaml2 from "js-yaml";
|
|
7764
7979
|
var BASE_MOCK_PORT = 4011;
|
|
7765
7980
|
var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
@@ -7772,13 +7987,13 @@ function deriveNameFromFilename(filename) {
|
|
|
7772
7987
|
}
|
|
7773
7988
|
function handleConsolidateIntegrations(input) {
|
|
7774
7989
|
const { projectRoot } = input;
|
|
7775
|
-
const artifactsDir =
|
|
7776
|
-
if (!
|
|
7990
|
+
const artifactsDir = join16(projectRoot, ".stackwright", "artifacts");
|
|
7991
|
+
if (!existsSync15(artifactsDir)) {
|
|
7777
7992
|
return { written: false, integrationCount: 0, reason: "no per-spec artifacts found" };
|
|
7778
7993
|
}
|
|
7779
7994
|
let entries;
|
|
7780
7995
|
try {
|
|
7781
|
-
entries =
|
|
7996
|
+
entries = readdirSync8(artifactsDir).filter(
|
|
7782
7997
|
(f) => f.startsWith("api-config-") && f.endsWith(".json") && f !== "api-config.json"
|
|
7783
7998
|
);
|
|
7784
7999
|
} catch {
|
|
@@ -7792,7 +8007,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
7792
8007
|
entries.forEach((filename, index) => {
|
|
7793
8008
|
let artifact = {};
|
|
7794
8009
|
try {
|
|
7795
|
-
const raw =
|
|
8010
|
+
const raw = readFileSync15(join16(artifactsDir, filename), "utf-8");
|
|
7796
8011
|
artifact = JSON.parse(raw);
|
|
7797
8012
|
} catch {
|
|
7798
8013
|
}
|
|
@@ -7818,19 +8033,19 @@ function handleConsolidateIntegrations(input) {
|
|
|
7818
8033
|
});
|
|
7819
8034
|
const integrations = Array.from(integrationsByName.values());
|
|
7820
8035
|
const dedupedCount = entries.length - integrations.length;
|
|
7821
|
-
const integrationsYmlPath =
|
|
8036
|
+
const integrationsYmlPath = join16(projectRoot, "stackwright.integrations.yml");
|
|
7822
8037
|
let existing = {};
|
|
7823
|
-
if (
|
|
8038
|
+
if (existsSync15(integrationsYmlPath)) {
|
|
7824
8039
|
try {
|
|
7825
|
-
const raw =
|
|
8040
|
+
const raw = readFileSync15(integrationsYmlPath, "utf-8");
|
|
7826
8041
|
existing = yaml2.load(raw) ?? {};
|
|
7827
8042
|
} catch {
|
|
7828
8043
|
existing = {};
|
|
7829
8044
|
}
|
|
7830
8045
|
}
|
|
7831
8046
|
const merged = { ...existing, integrations };
|
|
7832
|
-
mkdirSync9(
|
|
7833
|
-
if (!
|
|
8047
|
+
mkdirSync9(join16(projectRoot), { recursive: true });
|
|
8048
|
+
if (!existsSync15(integrationsYmlPath)) {
|
|
7834
8049
|
writeFileSync10(integrationsYmlPath, "", "utf-8");
|
|
7835
8050
|
}
|
|
7836
8051
|
const release = lockSync2(integrationsYmlPath, {
|
|
@@ -7923,7 +8138,7 @@ var package_default = {
|
|
|
7923
8138
|
"test:coverage": "vitest run --coverage"
|
|
7924
8139
|
},
|
|
7925
8140
|
name: "@stackwright-pro/mcp",
|
|
7926
|
-
version: "0.2.0-alpha.
|
|
8141
|
+
version: "0.2.0-alpha.107",
|
|
7927
8142
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
7928
8143
|
license: "SEE LICENSE IN LICENSE",
|
|
7929
8144
|
main: "./dist/server.js",
|