@stackwright-pro/mcp 0.2.0-alpha.90 → 0.2.0-alpha.92

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/server.mjs CHANGED
@@ -1595,6 +1595,9 @@ var OTTER_NAME_TO_PHASE = [
1595
1595
  ["dashboard", "dashboard"],
1596
1596
  ["data", "data"],
1597
1597
  ["page", "pages"],
1598
+ // swp-4071: workflow-otter renamed to form-wizard-otter; keep 'workflow' for
1599
+ // any in-flight session that still uses the old name (belt-and-suspenders)
1600
+ ["form-wizard", "workflow"],
1598
1601
  ["workflow", "workflow"],
1599
1602
  ["services", "services"],
1600
1603
  ["polish", "polish"],
@@ -1609,7 +1612,7 @@ var PHASE_TO_OTTER = {
1609
1612
  pages: "stackwright-pro-page-otter",
1610
1613
  dashboard: "stackwright-pro-dashboard-otter",
1611
1614
  data: "stackwright-pro-data-otter",
1612
- workflow: "stackwright-pro-workflow-otter",
1615
+ workflow: "stackwright-pro-form-wizard-otter",
1613
1616
  services: "stackwright-services-otter",
1614
1617
  polish: "stackwright-pro-polish-otter",
1615
1618
  geo: "stackwright-pro-geo-otter"
@@ -2757,7 +2760,7 @@ var PHASE_TO_OTTER2 = {
2757
2760
  data: "stackwright-pro-data-otter",
2758
2761
  pages: "stackwright-pro-page-otter",
2759
2762
  dashboard: "stackwright-pro-dashboard-otter",
2760
- workflow: "stackwright-pro-workflow-otter",
2763
+ workflow: "stackwright-pro-form-wizard-otter",
2761
2764
  services: "stackwright-services-otter",
2762
2765
  polish: "stackwright-pro-polish-otter",
2763
2766
  geo: "stackwright-pro-geo-otter"
@@ -3470,7 +3473,7 @@ var PHASE_ARTIFACT_SCHEMA = {
3470
3473
  workflow: JSON.stringify(
3471
3474
  {
3472
3475
  version: "1.0",
3473
- generatedBy: "stackwright-pro-workflow-otter",
3476
+ generatedBy: "stackwright-pro-form-wizard-otter",
3474
3477
  // Root key is `workflow` — NOT `workflowConfig` (see swp-k7cl)
3475
3478
  workflow: {
3476
3479
  id: "procurement-approval",
@@ -3680,6 +3683,57 @@ function handleValidateArtifact(input) {
3680
3683
  const issues = result.error.issues.slice(0, 3).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
3681
3684
  return { success: false, error: { message: issues } };
3682
3685
  }
3686
+ const rbacRoles = authConfig["rbacRoles"];
3687
+ const rbacDefaultRole = authConfig["rbacDefaultRole"];
3688
+ const protectedRoutes = authConfig["protectedRoutes"];
3689
+ if (Array.isArray(rbacRoles) && rbacRoles.length > 2 && Array.isArray(protectedRoutes) && protectedRoutes.length > 0 && typeof rbacDefaultRole === "string") {
3690
+ const uniqueRoles = new Set(
3691
+ protectedRoutes.map(
3692
+ (r) => typeof r === "string" ? rbacDefaultRole : r.requiredRole ?? rbacDefaultRole
3693
+ )
3694
+ );
3695
+ if (uniqueRoles.size === 1 && uniqueRoles.has(rbacDefaultRole)) {
3696
+ return {
3697
+ success: false,
3698
+ error: {
3699
+ message: `RBAC theatre detected: all ${protectedRoutes.length} protectedRoutes require '${rbacDefaultRole}' (the defaultRole), but ${rbacRoles.length} roles are defined. This defeats the RBAC hierarchy. Each route MUST have the minimum role required for its function. See auth-otter PER-ROUTE RBAC GRANULARITY section.`
3700
+ }
3701
+ };
3702
+ }
3703
+ }
3704
+ return { success: true };
3705
+ },
3706
+ // swp-4071 — opt-in cross-reference validator for the services phase.
3707
+ // If workflow-config exists and declares serviceHooks, every hook ref must resolve
3708
+ // to a flow or state-machine in this services-config artifact.
3709
+ // Skips gracefully when wizard-otter didn't run (Pro-only install) or declared no hooks.
3710
+ services: (artifact2) => {
3711
+ const workflowArtifactPath = join4(cwd, ".stackwright", "artifacts", "workflow-config.json");
3712
+ if (!existsSync5(workflowArtifactPath)) {
3713
+ return { success: true };
3714
+ }
3715
+ let workflowArtifact;
3716
+ try {
3717
+ workflowArtifact = JSON.parse(readFileSync4(workflowArtifactPath, "utf-8"));
3718
+ } catch {
3719
+ return { success: true };
3720
+ }
3721
+ const declaredHooks = workflowArtifact.serviceHooks ?? [];
3722
+ if (declaredHooks.length === 0) {
3723
+ return { success: true };
3724
+ }
3725
+ const flows = artifact2["flows"] ?? [];
3726
+ const workflows = artifact2["workflows"] ?? [];
3727
+ const flowNames = /* @__PURE__ */ new Set([...flows.map((f) => f.name), ...workflows.map((w) => w.name)]);
3728
+ const unresolved = declaredHooks.filter((h) => !flowNames.has(h.ref));
3729
+ if (unresolved.length > 0) {
3730
+ return {
3731
+ success: false,
3732
+ error: {
3733
+ message: `Workflow declared ${declaredHooks.length} service hook(s) but ${unresolved.length} unresolved by services-config: ` + unresolved.map((h) => `${h.ref} (${h.kind ?? "unknown"}: ${h.purpose ?? "no purpose"})`).join("; ") + ". Either add matching flows to services-config OR remove the hooks from the workflow YAML."
3734
+ }
3735
+ };
3736
+ }
3683
3737
  return { success: true };
3684
3738
  }
3685
3739
  };
@@ -3919,7 +3973,7 @@ var OTTER_WRITE_ALLOWLISTS = {
3919
3973
  { prefix: "pages/", suffix: "/content.yaml", description: "Dashboard content YAML" },
3920
3974
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Dashboard manifest" }
3921
3975
  ],
3922
- "stackwright-pro-workflow-otter": [
3976
+ "stackwright-pro-form-wizard-otter": [
3923
3977
  { prefix: "workflows/", suffix: ".yml", description: "Workflow definition" },
3924
3978
  { prefix: "workflows/", suffix: ".yaml", description: "Workflow definition" },
3925
3979
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Workflow config" }
@@ -4321,9 +4375,14 @@ function hierarchyToYaml(hierarchy, indent) {
4321
4375
  function rolesToYaml(roles, indent) {
4322
4376
  return roles.map((r) => `${indent}- ${r}`).join("\n");
4323
4377
  }
4324
- function routesToYaml(routes, defaultRole, indent) {
4325
- return routes.map((r) => `${indent}- pattern: ${r}
4326
- ${indent} requiredRole: ${defaultRole}`).join("\n");
4378
+ function normalizeRoutes(routes, defaultRole) {
4379
+ return routes.map(
4380
+ (r) => typeof r === "string" ? { pattern: r, requiredRole: defaultRole } : { pattern: r.pattern, requiredRole: r.requiredRole ?? defaultRole }
4381
+ );
4382
+ }
4383
+ function routesToYaml(routes, indent) {
4384
+ return routes.map((r) => `${indent}- pattern: ${r.pattern}
4385
+ ${indent} requiredRole: ${r.requiredRole}`).join("\n");
4327
4386
  }
4328
4387
  function detectNextMajorVersion(cwd) {
4329
4388
  try {
@@ -4367,9 +4426,10 @@ function generateMiddlewareContent(method, params, roles, defaultRole, hierarchy
4367
4426
  enabled: ${auditEnabled},
4368
4427
  retentionDays: ${auditRetentionDays},
4369
4428
  },`;
4370
- const routesBlock = ` protectedRoutes: ${JSON.stringify(protectedRoutes)},`;
4429
+ const patterns = protectedRoutes.map((r) => r.pattern);
4430
+ const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
4371
4431
  const configBlock = `export const config = {
4372
- matcher: ${JSON.stringify(protectedRoutes)},
4432
+ matcher: ${JSON.stringify(patterns)},
4373
4433
  };`;
4374
4434
  if (method === "cac") {
4375
4435
  const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
@@ -4452,9 +4512,10 @@ function generateProxyContent(method, params, roles, defaultRole, hierarchy, aud
4452
4512
  enabled: ${auditEnabled},
4453
4513
  retentionDays: ${auditRetentionDays},
4454
4514
  },`;
4455
- const routesBlock = ` protectedRoutes: ${JSON.stringify(protectedRoutes)},`;
4515
+ const patterns = protectedRoutes.map((r) => r.pattern);
4516
+ const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
4456
4517
  const configBlock = `export const config = {
4457
- matcher: ${JSON.stringify(protectedRoutes)},
4518
+ matcher: ${JSON.stringify(patterns)},
4458
4519
  };`;
4459
4520
  if (method === "cac") {
4460
4521
  const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
@@ -4563,10 +4624,7 @@ ${hierarchyToYaml(hierarchy, " ")}`;
4563
4624
  const auditSection = ` audit:
4564
4625
  enabled: ${auditEnabled}
4565
4626
  retentionDays: ${auditRetentionDays}`;
4566
- const routesSection = ` protectedRoutes:
4567
- ${routesToYaml(protectedRoutes, " ", defaultRole)}`.replace(/\n\s+,/g, "");
4568
- const routeLines = protectedRoutes.map((r) => ` - pattern: ${r}
4569
- requiredRole: ${defaultRole}`).join("\n");
4627
+ const routeLines = routesToYaml(protectedRoutes, " ");
4570
4628
  const providerLine = params.provider ? ` provider: ${params.provider}
4571
4629
  ` : "";
4572
4630
  if (method === "cac") {
@@ -4632,9 +4690,10 @@ function generateDevOnlyMiddlewareContent(method, params, roles, defaultRole, hi
4632
4690
  enabled: ${auditEnabled},
4633
4691
  retentionDays: ${auditRetentionDays},
4634
4692
  },`;
4635
- const routesBlock = ` protectedRoutes: ${JSON.stringify(protectedRoutes)},`;
4693
+ const patterns = protectedRoutes.map((r) => r.pattern);
4694
+ const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
4636
4695
  const configBlock = `export const config = {
4637
- matcher: ${JSON.stringify(protectedRoutes)},
4696
+ matcher: ${JSON.stringify(patterns)},
4638
4697
  };`;
4639
4698
  const devHeader = [
4640
4699
  "// middleware.ts \u2014 generated by @stackwright-pro/auth-nextjs (dev-only mock)",
@@ -4721,9 +4780,10 @@ function generateDevOnlyProxyContent(method, params, roles, defaultRole, hierarc
4721
4780
  enabled: ${auditEnabled},
4722
4781
  retentionDays: ${auditRetentionDays},
4723
4782
  },`;
4724
- const routesBlock = ` protectedRoutes: ${JSON.stringify(protectedRoutes)},`;
4783
+ const patterns = protectedRoutes.map((r) => r.pattern);
4784
+ const routesBlock = ` protectedRoutes: ${JSON.stringify(patterns)},`;
4725
4785
  const configBlock = `export const config = {
4726
- matcher: ${JSON.stringify(protectedRoutes)},
4786
+ matcher: ${JSON.stringify(patterns)},
4727
4787
  };`;
4728
4788
  const devHeader = [
4729
4789
  "// proxy.ts -- generated by @stackwright-pro/auth (Next.js >=16, dev-only mock)",
@@ -4810,8 +4870,8 @@ ${hierarchyToYaml(hierarchy, " ")}`;
4810
4870
  const auditSection = ` audit:
4811
4871
  enabled: ${auditEnabled}
4812
4872
  retentionDays: ${auditRetentionDays}`;
4813
- const routeLines = protectedRoutes.map((r) => ` - pattern: ${r}
4814
- requiredRole: ${defaultRole}`).join("\n");
4873
+ const routeLines = protectedRoutes.map((r) => ` - pattern: ${r.pattern}
4874
+ requiredRole: ${r.requiredRole}`).join("\n");
4815
4875
  const providerLine = params.provider ? ` provider: ${params.provider}
4816
4876
  ` : "";
4817
4877
  if (method === "cac") {
@@ -4944,6 +5004,7 @@ async function configureAuthHandler(params, cwd) {
4944
5004
  } = params;
4945
5005
  const roles = rbacRoles;
4946
5006
  const defaultRole = params.rbacDefaultRole ?? roles[roles.length - 1];
5007
+ const normalizedRoutes = normalizeRoutes(protectedRoutes, defaultRole);
4947
5008
  const hierarchy = buildHierarchy(roles);
4948
5009
  const detectedVersion = params.nextMajorVersion ?? detectNextMajorVersion(cwd);
4949
5010
  const useProxy = (detectedVersion ?? 0) >= 16;
@@ -4980,7 +5041,7 @@ async function configureAuthHandler(params, cwd) {
4980
5041
  hierarchy,
4981
5042
  auditEnabled,
4982
5043
  auditRetentionDays,
4983
- protectedRoutes
5044
+ normalizedRoutes
4984
5045
  ) : generateDevOnlyMiddlewareContent(
4985
5046
  effectiveMethod,
4986
5047
  params,
@@ -4989,7 +5050,7 @@ async function configureAuthHandler(params, cwd) {
4989
5050
  hierarchy,
4990
5051
  auditEnabled,
4991
5052
  auditRetentionDays,
4992
- protectedRoutes
5053
+ normalizedRoutes
4993
5054
  ) : useProxy ? generateProxyContent(
4994
5055
  effectiveMethod,
4995
5056
  params,
@@ -4998,7 +5059,7 @@ async function configureAuthHandler(params, cwd) {
4998
5059
  hierarchy,
4999
5060
  auditEnabled,
5000
5061
  auditRetentionDays,
5001
- protectedRoutes
5062
+ normalizedRoutes
5002
5063
  ) : generateMiddlewareContent(
5003
5064
  effectiveMethod,
5004
5065
  params,
@@ -5007,7 +5068,7 @@ async function configureAuthHandler(params, cwd) {
5007
5068
  hierarchy,
5008
5069
  auditEnabled,
5009
5070
  auditRetentionDays,
5010
- protectedRoutes
5071
+ normalizedRoutes
5011
5072
  );
5012
5073
  writeFileSync6(join6(cwd, conventionFile), authFileContent, "utf8");
5013
5074
  filesWritten.push(conventionFile);
@@ -5105,7 +5166,7 @@ async function configureAuthHandler(params, cwd) {
5105
5166
  hierarchy,
5106
5167
  auditEnabled,
5107
5168
  auditRetentionDays,
5108
- protectedRoutes,
5169
+ normalizedRoutes,
5109
5170
  useProxy
5110
5171
  ) : generateYamlBlock(
5111
5172
  effectiveMethod,
@@ -5115,7 +5176,7 @@ async function configureAuthHandler(params, cwd) {
5115
5176
  hierarchy,
5116
5177
  auditEnabled,
5117
5178
  auditRetentionDays,
5118
- protectedRoutes,
5179
+ normalizedRoutes,
5119
5180
  useProxy
5120
5181
  );
5121
5182
  const ymlPath = join6(cwd, "stackwright.yml");
@@ -5204,8 +5265,15 @@ function registerAuthTools(server2) {
5204
5265
  // Audit
5205
5266
  auditEnabled: boolCoerce(z15.boolean().optional()),
5206
5267
  auditRetentionDays: numCoerce(z15.number().int().positive().optional()),
5207
- // Routes
5208
- protectedRoutes: jsonCoerce(z15.array(z15.string()).optional()),
5268
+ // Routes — accepts bare strings (backward-compat) or per-route objects (swp-owu0)
5269
+ protectedRoutes: jsonCoerce(
5270
+ z15.array(
5271
+ z15.union([
5272
+ z15.string(),
5273
+ z15.object({ pattern: z15.string(), requiredRole: z15.string().optional() })
5274
+ ])
5275
+ ).optional()
5276
+ ),
5209
5277
  // Injection for tests
5210
5278
  _cwd: z15.string().optional(),
5211
5279
  devOnly: boolCoerce(z15.boolean().optional()),
@@ -5230,7 +5298,7 @@ var _checksums = /* @__PURE__ */ new Map([
5230
5298
  ],
5231
5299
  [
5232
5300
  "stackwright-pro-auth-otter.json",
5233
- "07134125ab4f8c82ee015f27587e4d84bdeefca5ec211d1563bcb25b8aa61eb3"
5301
+ "643344b88e42992e0467845fb0184d3932e115b85130fbc6a47a3175759678c8"
5234
5302
  ],
5235
5303
  [
5236
5304
  "stackwright-pro-dashboard-otter.json",
@@ -5252,6 +5320,10 @@ var _checksums = /* @__PURE__ */ new Map([
5252
5320
  "stackwright-pro-foreman-otter.json",
5253
5321
  "438b03d2c35f64536055c68b3a9044fe3b5e4f2889acde4500dd801fad724be1"
5254
5322
  ],
5323
+ [
5324
+ "stackwright-pro-form-wizard-otter.json",
5325
+ "3dffa3ef2d2ef057578391f784cbea779d768e87d98321894ea5bcd9e3ab7e60"
5326
+ ],
5255
5327
  [
5256
5328
  "stackwright-pro-geo-otter.json",
5257
5329
  "2ec83c26a08c413d9553ff8b8f0f0f643c1a8da043741b356e27106cad78f05f"
@@ -5272,10 +5344,6 @@ var _checksums = /* @__PURE__ */ new Map([
5272
5344
  "stackwright-pro-theme-otter.json",
5273
5345
  "fe10108e3ba67106751ea662f512bb5f4eb58f7abda26880ef4cf6b0f9feb944"
5274
5346
  ],
5275
- [
5276
- "stackwright-pro-workflow-otter.json",
5277
- "5f6209fadc1355580e2455a16386f06bd7d5814f047b2dd5b33800abf6a48ff8"
5278
- ],
5279
5347
  [
5280
5348
  "stackwright-services-otter.json",
5281
5349
  "c013d7fc2475e62d0af54d57bc988182feee7766bac0edf841cbfad5c73e9261"
@@ -5919,7 +5987,7 @@ function buildTypeSchemaSummary() {
5919
5987
  "TransitionConditionSchema",
5920
5988
  "PersistenceSchema"
5921
5989
  ],
5922
- otter: "stackwright-pro-workflow-otter",
5990
+ otter: "stackwright-pro-form-wizard-otter",
5923
5991
  artifactKey: "workflowConfig"
5924
5992
  },
5925
5993
  auth: {
@@ -6500,7 +6568,7 @@ var package_default = {
6500
6568
  "test:coverage": "vitest run --coverage"
6501
6569
  },
6502
6570
  name: "@stackwright-pro/mcp",
6503
- version: "0.2.0-alpha.90",
6571
+ version: "0.2.0-alpha.92",
6504
6572
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
6505
6573
  license: "SEE LICENSE IN LICENSE",
6506
6574
  main: "./dist/server.js",