@stackwright-pro/mcp 0.2.0-alpha.60 → 0.2.0-alpha.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.js +9 -9
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +9 -9
- package/dist/integrity.mjs.map +1 -1
- package/dist/server.js +467 -42
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +475 -50
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1702,6 +1702,14 @@ function handleSavePhaseAnswers(input) {
|
|
|
1702
1702
|
let answers;
|
|
1703
1703
|
if (input.questions && input.questions.length > 0) {
|
|
1704
1704
|
answers = answersToManifestFormat(input.rawAnswers, input.questions);
|
|
1705
|
+
if (Object.keys(answers).length === 0 && input.rawAnswers.length > 0) {
|
|
1706
|
+
answers = Object.fromEntries(
|
|
1707
|
+
input.rawAnswers.map((a) => [
|
|
1708
|
+
a.question_header,
|
|
1709
|
+
a.selected_options.length > 1 ? a.selected_options : a.selected_options[0] ?? ""
|
|
1710
|
+
])
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1705
1713
|
} else {
|
|
1706
1714
|
answers = Object.fromEntries(
|
|
1707
1715
|
input.rawAnswers.map((a) => [a.question_header, a.selected_options[0] ?? ""])
|
|
@@ -2285,12 +2293,13 @@ var PHASE_DEPENDENCIES = {
|
|
|
2285
2293
|
// workflow states as trigger sources. Produces OpenAPI specs consumed
|
|
2286
2294
|
// by pages and dashboard for typed data wiring.
|
|
2287
2295
|
services: ["api", "workflow"],
|
|
2288
|
-
// pages/dashboard:
|
|
2289
|
-
//
|
|
2290
|
-
//
|
|
2291
|
-
//
|
|
2292
|
-
|
|
2293
|
-
|
|
2296
|
+
// pages/dashboard: depend on services for typed endpoint wiring (OpenAPI
|
|
2297
|
+
// specs) and on geo so the specialist prompt includes geo-manifest.json
|
|
2298
|
+
// — page/dashboard otters see which page slugs are already claimed and
|
|
2299
|
+
// won't attempt to overwrite them (swp-73c). 'api' is still transitive
|
|
2300
|
+
// through 'data'; auth removed — page-otter has graceful fallback.
|
|
2301
|
+
pages: ["designer", "theme", "data", "services", "geo"],
|
|
2302
|
+
dashboard: ["designer", "theme", "data", "services", "geo"],
|
|
2294
2303
|
// auth is the penultimate phase — runs after all content-producing phases
|
|
2295
2304
|
// so it can read pages, dashboard, workflow, and geo artifacts for route
|
|
2296
2305
|
// protection and RBAC wiring. Skipped upstream phases still satisfy deps
|
|
@@ -3032,13 +3041,19 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
3032
3041
|
null,
|
|
3033
3042
|
2
|
|
3034
3043
|
),
|
|
3044
|
+
// type: 'pki' = CAC/DoD certificate auth | 'oidc' = enterprise SSO
|
|
3045
|
+
// For dev-only mock auth: use type: 'oidc' with devOnly: true (Zod strips devOnly — it's a convention only)
|
|
3035
3046
|
auth: JSON.stringify(
|
|
3036
3047
|
{
|
|
3037
3048
|
version: "1.0",
|
|
3038
3049
|
generatedBy: "stackwright-pro-auth-otter",
|
|
3039
3050
|
authConfig: {
|
|
3040
|
-
|
|
3041
|
-
|
|
3051
|
+
type: "<pki|oidc>",
|
|
3052
|
+
// OIDC-only fields (omit for pki):
|
|
3053
|
+
provider: "<azure_ad|okta|cognito|auth0|authentik|keycloak|custom>",
|
|
3054
|
+
discoveryUrl: "<IdP OIDC discovery URL>",
|
|
3055
|
+
clientId: "<OIDC client ID>",
|
|
3056
|
+
clientSecret: "<OIDC client secret>",
|
|
3042
3057
|
rbacRoles: ["ADMIN", "ANALYST"],
|
|
3043
3058
|
rbacDefaultRole: "ANALYST",
|
|
3044
3059
|
protectedRoutes: ["/dashboard/:path*", "/procurement/:path*"],
|
|
@@ -3341,6 +3356,16 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3341
3356
|
prefix: ".env",
|
|
3342
3357
|
suffix: "",
|
|
3343
3358
|
description: "Dotenv files (.env, .env.local, .env.production, etc.)"
|
|
3359
|
+
},
|
|
3360
|
+
{
|
|
3361
|
+
prefix: "lib/mock-auth",
|
|
3362
|
+
suffix: ".ts",
|
|
3363
|
+
description: "Mock auth module (DEV_ONLY_MODE role updates)"
|
|
3364
|
+
},
|
|
3365
|
+
{
|
|
3366
|
+
prefix: "package.json",
|
|
3367
|
+
suffix: ".json",
|
|
3368
|
+
description: "Project package.json (DEV_ONLY_MODE script cleanup)"
|
|
3344
3369
|
}
|
|
3345
3370
|
],
|
|
3346
3371
|
"stackwright-pro-data-otter": [
|
|
@@ -3379,6 +3404,16 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
3379
3404
|
{ prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
|
|
3380
3405
|
{ prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
|
|
3381
3406
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" }
|
|
3407
|
+
],
|
|
3408
|
+
"stackwright-services-otter": [
|
|
3409
|
+
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Services config artifact" },
|
|
3410
|
+
{ prefix: "services/", suffix: ".ts", description: "Service implementation files" },
|
|
3411
|
+
{ prefix: "services/", suffix: ".yaml", description: "Service flow definitions" },
|
|
3412
|
+
{ prefix: "services/", suffix: ".yml", description: "Service flow definitions" },
|
|
3413
|
+
{ prefix: "lib/seeds/", suffix: ".ts", description: "Seed data files" },
|
|
3414
|
+
{ prefix: "specs/", suffix: ".json", description: "Generated OpenAPI specs" },
|
|
3415
|
+
{ prefix: "specs/", suffix: ".yaml", description: "Generated OpenAPI specs" },
|
|
3416
|
+
{ prefix: "stackwright-generated/", suffix: ".json", description: "Services manifest" }
|
|
3382
3417
|
]
|
|
3383
3418
|
};
|
|
3384
3419
|
var PROTECTED_PATH_PREFIXES = [
|
|
@@ -3388,7 +3423,9 @@ var PROTECTED_PATH_PREFIXES = [
|
|
|
3388
3423
|
".stackwright/artifacts/signatures.json",
|
|
3389
3424
|
// artifact signature manifest
|
|
3390
3425
|
".stackwright/questions/",
|
|
3391
|
-
".stackwright/answers/"
|
|
3426
|
+
".stackwright/answers/",
|
|
3427
|
+
".stackwright/page-registry.json"
|
|
3428
|
+
// page ownership — managed by safe_write internally
|
|
3392
3429
|
];
|
|
3393
3430
|
var MAX_SAFE_WRITE_BYTES_JSON = 512 * 1024;
|
|
3394
3431
|
var MAX_SAFE_WRITE_BYTES_YAML = 256 * 1024;
|
|
@@ -3402,6 +3439,58 @@ function getMaxBytesForPath(filePath) {
|
|
|
3402
3439
|
return { limit: MAX_SAFE_WRITE_BYTES_ENV, label: "env" };
|
|
3403
3440
|
return { limit: MAX_SAFE_WRITE_BYTES_DEFAULT, label: "default" };
|
|
3404
3441
|
}
|
|
3442
|
+
var PAGE_REGISTRY_FILE = ".stackwright/page-registry.json";
|
|
3443
|
+
function extractPageSlug(filePath) {
|
|
3444
|
+
const match = (0, import_path6.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
|
|
3445
|
+
return match?.[1] ?? null;
|
|
3446
|
+
}
|
|
3447
|
+
function extractContentTypes(yamlContent) {
|
|
3448
|
+
const typePattern = /^\s*-?\s*type:\s*(\S+)/gm;
|
|
3449
|
+
const types = [];
|
|
3450
|
+
let m;
|
|
3451
|
+
while ((m = typePattern.exec(yamlContent)) !== null) {
|
|
3452
|
+
const typeName = m[1];
|
|
3453
|
+
if (typeName && !types.includes(typeName)) {
|
|
3454
|
+
types.push(typeName);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
return types.length > 0 ? types : ["unknown"];
|
|
3458
|
+
}
|
|
3459
|
+
function readPageRegistry(cwd) {
|
|
3460
|
+
const regPath = (0, import_path6.join)(cwd, PAGE_REGISTRY_FILE);
|
|
3461
|
+
if (!(0, import_fs6.existsSync)(regPath)) return {};
|
|
3462
|
+
try {
|
|
3463
|
+
const stat = (0, import_fs6.lstatSync)(regPath);
|
|
3464
|
+
if (stat.isSymbolicLink()) return {};
|
|
3465
|
+
return JSON.parse((0, import_fs6.readFileSync)(regPath, "utf-8"));
|
|
3466
|
+
} catch {
|
|
3467
|
+
return {};
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
function writePageRegistry(cwd, registry) {
|
|
3471
|
+
const dir = (0, import_path6.join)(cwd, ".stackwright");
|
|
3472
|
+
(0, import_fs6.mkdirSync)(dir, { recursive: true });
|
|
3473
|
+
const regPath = (0, import_path6.join)(cwd, PAGE_REGISTRY_FILE);
|
|
3474
|
+
if ((0, import_fs6.existsSync)(regPath)) {
|
|
3475
|
+
const stat = (0, import_fs6.lstatSync)(regPath);
|
|
3476
|
+
if (stat.isSymbolicLink()) {
|
|
3477
|
+
throw new Error("Refusing to write page registry through symlink");
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
(0, import_fs6.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
|
|
3481
|
+
}
|
|
3482
|
+
function checkPageOwnership(cwd, slug, callerOtter) {
|
|
3483
|
+
const registry = readPageRegistry(cwd);
|
|
3484
|
+
const existing = registry[slug];
|
|
3485
|
+
if (!existing || existing.claimedBy === callerOtter) {
|
|
3486
|
+
return { allowed: true };
|
|
3487
|
+
}
|
|
3488
|
+
return {
|
|
3489
|
+
allowed: false,
|
|
3490
|
+
owner: existing.claimedBy,
|
|
3491
|
+
contentTypes: existing.contentTypes
|
|
3492
|
+
};
|
|
3493
|
+
}
|
|
3405
3494
|
function checkPathAllowed(callerOtter, filePath) {
|
|
3406
3495
|
const normalized = (0, import_path6.normalize)(filePath);
|
|
3407
3496
|
if (normalized.includes("..")) {
|
|
@@ -3443,6 +3532,16 @@ function checkPathAllowed(callerOtter, filePath) {
|
|
|
3443
3532
|
continue;
|
|
3444
3533
|
}
|
|
3445
3534
|
}
|
|
3535
|
+
if (rule.prefix === "lib/mock-auth" && rule.suffix === ".ts") {
|
|
3536
|
+
if (normalized !== "lib/mock-auth.ts") {
|
|
3537
|
+
continue;
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
if (rule.prefix === "package.json" && rule.suffix === ".json") {
|
|
3541
|
+
if (normalized !== "package.json") {
|
|
3542
|
+
continue;
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3446
3545
|
return { allowed: true, rule: rule.description };
|
|
3447
3546
|
}
|
|
3448
3547
|
}
|
|
@@ -3568,11 +3667,38 @@ function handleSafeWrite(input) {
|
|
|
3568
3667
|
};
|
|
3569
3668
|
return { text: JSON.stringify(result), isError: true };
|
|
3570
3669
|
}
|
|
3670
|
+
const pageSlug = extractPageSlug(normalized);
|
|
3671
|
+
if (pageSlug) {
|
|
3672
|
+
const ownership = checkPageOwnership(cwd, pageSlug, callerOtter);
|
|
3673
|
+
if (!ownership.allowed) {
|
|
3674
|
+
const result = {
|
|
3675
|
+
success: false,
|
|
3676
|
+
error: `Page slug "${pageSlug}" is already claimed by ${ownership.owner} (content types: ${ownership.contentTypes.join(", ")}). Use a different slug or coordinate with the owning otter.`,
|
|
3677
|
+
callerOtter,
|
|
3678
|
+
attemptedPath: filePath,
|
|
3679
|
+
allowedPaths: []
|
|
3680
|
+
};
|
|
3681
|
+
return { text: JSON.stringify(result), isError: true };
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3571
3684
|
try {
|
|
3572
3685
|
if (createDirectories) {
|
|
3573
3686
|
(0, import_fs6.mkdirSync)((0, import_path6.dirname)(fullPath), { recursive: true });
|
|
3574
3687
|
}
|
|
3575
3688
|
(0, import_fs6.writeFileSync)(fullPath, content, { encoding: "utf-8" });
|
|
3689
|
+
if (pageSlug) {
|
|
3690
|
+
try {
|
|
3691
|
+
const registry = readPageRegistry(cwd);
|
|
3692
|
+
registry[pageSlug] = {
|
|
3693
|
+
slug: pageSlug,
|
|
3694
|
+
claimedBy: callerOtter,
|
|
3695
|
+
contentTypes: extractContentTypes(content),
|
|
3696
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3697
|
+
};
|
|
3698
|
+
writePageRegistry(cwd, registry);
|
|
3699
|
+
} catch {
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3576
3702
|
const result = {
|
|
3577
3703
|
success: true,
|
|
3578
3704
|
path: normalized,
|
|
@@ -3839,10 +3965,232 @@ ${routeLines}
|
|
|
3839
3965
|
${auditSection}
|
|
3840
3966
|
`;
|
|
3841
3967
|
}
|
|
3968
|
+
function generateDevOnlyMiddlewareContent(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes) {
|
|
3969
|
+
const rbacBlock = ` rbac: {
|
|
3970
|
+
roles: ${JSON.stringify(roles)},
|
|
3971
|
+
defaultRole: '${defaultRole}',
|
|
3972
|
+
hierarchy: ${JSON.stringify(hierarchy, null, 4)},
|
|
3973
|
+
},`;
|
|
3974
|
+
const auditBlock = ` audit: {
|
|
3975
|
+
enabled: ${auditEnabled},
|
|
3976
|
+
retentionDays: ${auditRetentionDays},
|
|
3977
|
+
},`;
|
|
3978
|
+
const routesBlock = ` protectedRoutes: ${JSON.stringify(protectedRoutes)},`;
|
|
3979
|
+
const configBlock = `export const config = {
|
|
3980
|
+
matcher: ${JSON.stringify(protectedRoutes)},
|
|
3981
|
+
};`;
|
|
3982
|
+
const devHeader = [
|
|
3983
|
+
"// middleware.ts \u2014 generated by @stackwright-pro/auth-nextjs (dev-only mock)",
|
|
3984
|
+
"// DEV ONLY \u2014 uses mock authentication from lib/mock-auth.ts",
|
|
3985
|
+
"// Do NOT deploy to production.",
|
|
3986
|
+
"import { createProMiddleware } from '@stackwright-pro/auth-nextjs';",
|
|
3987
|
+
"import { mockAuthProvider } from './lib/mock-auth';"
|
|
3988
|
+
].join("\n");
|
|
3989
|
+
if (method === "cac") {
|
|
3990
|
+
const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
|
|
3991
|
+
const edipiLookup = params.cacEdipiLookup ?? "./config/edipi-lookup.json";
|
|
3992
|
+
const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
|
|
3993
|
+
const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
|
|
3994
|
+
return `${devHeader}
|
|
3995
|
+
|
|
3996
|
+
export const middleware = createProMiddleware({
|
|
3997
|
+
method: 'cac',
|
|
3998
|
+
cac: {
|
|
3999
|
+
caBundle: '${caBundle}',
|
|
4000
|
+
edipiLookup: '${edipiLookup}',
|
|
4001
|
+
ocspEndpoint: '${ocspEndpoint}',
|
|
4002
|
+
certHeader: '${certHeader}',
|
|
4003
|
+
provider: mockAuthProvider,
|
|
4004
|
+
},
|
|
4005
|
+
${rbacBlock}
|
|
4006
|
+
${auditBlock}
|
|
4007
|
+
${routesBlock}
|
|
4008
|
+
});
|
|
4009
|
+
|
|
4010
|
+
${configBlock}
|
|
4011
|
+
`;
|
|
4012
|
+
}
|
|
4013
|
+
if (method === "oidc") {
|
|
4014
|
+
const scopes2 = params.oidcScopes ?? "openid profile email";
|
|
4015
|
+
const roleClaim = params.oidcRoleClaim ?? "roles";
|
|
4016
|
+
return `${devHeader}
|
|
4017
|
+
|
|
4018
|
+
export const middleware = createProMiddleware({
|
|
4019
|
+
method: 'oidc',
|
|
4020
|
+
oidc: {
|
|
4021
|
+
discoveryUrl: 'https://dev-mock-oidc/.well-known/openid-configuration',
|
|
4022
|
+
clientId: 'dev-mock-client',
|
|
4023
|
+
clientSecret: 'dev-mock-secret',
|
|
4024
|
+
scopes: '${scopes2}',
|
|
4025
|
+
roleClaim: '${roleClaim}',
|
|
4026
|
+
provider: mockAuthProvider,
|
|
4027
|
+
},
|
|
4028
|
+
${rbacBlock}
|
|
4029
|
+
${auditBlock}
|
|
4030
|
+
${routesBlock}
|
|
4031
|
+
});
|
|
4032
|
+
|
|
4033
|
+
${configBlock}
|
|
4034
|
+
`;
|
|
4035
|
+
}
|
|
4036
|
+
const scopes = params.oauth2Scopes ?? "read write";
|
|
4037
|
+
return `${devHeader}
|
|
4038
|
+
|
|
4039
|
+
export const middleware = createProMiddleware({
|
|
4040
|
+
method: 'oauth2',
|
|
4041
|
+
oauth2: {
|
|
4042
|
+
authorizationUrl: 'https://dev-mock-oauth2/authorize',
|
|
4043
|
+
tokenUrl: 'https://dev-mock-oauth2/token',
|
|
4044
|
+
clientId: 'dev-mock-client',
|
|
4045
|
+
clientSecret: 'dev-mock-secret',
|
|
4046
|
+
scopes: '${scopes}',
|
|
4047
|
+
provider: mockAuthProvider,
|
|
4048
|
+
},
|
|
4049
|
+
${rbacBlock}
|
|
4050
|
+
${auditBlock}
|
|
4051
|
+
${routesBlock}
|
|
4052
|
+
});
|
|
4053
|
+
|
|
4054
|
+
${configBlock}
|
|
4055
|
+
`;
|
|
4056
|
+
}
|
|
4057
|
+
function generateDevOnlyYamlBlock(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes) {
|
|
4058
|
+
const rbacSection = ` rbac:
|
|
4059
|
+
roles:
|
|
4060
|
+
${rolesToYaml(roles, " ")}
|
|
4061
|
+
defaultRole: ${defaultRole}
|
|
4062
|
+
hierarchy:
|
|
4063
|
+
${hierarchyToYaml(hierarchy, " ")}`;
|
|
4064
|
+
const auditSection = ` audit:
|
|
4065
|
+
enabled: ${auditEnabled}
|
|
4066
|
+
retentionDays: ${auditRetentionDays}`;
|
|
4067
|
+
const routeLines = protectedRoutes.map((r) => ` - pattern: ${r}
|
|
4068
|
+
requiredRole: ${defaultRole}`).join("\n");
|
|
4069
|
+
const providerLine = params.provider ? ` provider: ${params.provider}
|
|
4070
|
+
` : "";
|
|
4071
|
+
if (method === "cac") {
|
|
4072
|
+
const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
|
|
4073
|
+
const edipiLookup = params.cacEdipiLookup ?? "./config/edipi-lookup.json";
|
|
4074
|
+
const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
|
|
4075
|
+
const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
|
|
4076
|
+
return `auth:
|
|
4077
|
+
method: cac
|
|
4078
|
+
devOnly: true
|
|
4079
|
+
${providerLine} middleware: ./middleware.ts
|
|
4080
|
+
cac:
|
|
4081
|
+
caBundle: ${caBundle}
|
|
4082
|
+
edipiLookup: ${edipiLookup}
|
|
4083
|
+
ocspEndpoint: ${ocspEndpoint}
|
|
4084
|
+
certHeader: ${certHeader}
|
|
4085
|
+
${rbacSection}
|
|
4086
|
+
protectedRoutes:
|
|
4087
|
+
${routeLines}
|
|
4088
|
+
${auditSection}
|
|
4089
|
+
`;
|
|
4090
|
+
}
|
|
4091
|
+
if (method === "oidc") {
|
|
4092
|
+
const scopes2 = params.oidcScopes ?? "openid profile email";
|
|
4093
|
+
const roleClaim = params.oidcRoleClaim ?? "roles";
|
|
4094
|
+
return `auth:
|
|
4095
|
+
method: oidc
|
|
4096
|
+
devOnly: true
|
|
4097
|
+
${providerLine} middleware: ./middleware.ts
|
|
4098
|
+
oidc:
|
|
4099
|
+
discoveryUrl: https://dev-mock-oidc/.well-known/openid-configuration
|
|
4100
|
+
clientId: dev-mock-client
|
|
4101
|
+
clientSecret: dev-mock-secret
|
|
4102
|
+
scopes: ${scopes2}
|
|
4103
|
+
roleClaim: ${roleClaim}
|
|
4104
|
+
${rbacSection}
|
|
4105
|
+
protectedRoutes:
|
|
4106
|
+
${routeLines}
|
|
4107
|
+
${auditSection}
|
|
4108
|
+
`;
|
|
4109
|
+
}
|
|
4110
|
+
const scopes = params.oauth2Scopes ?? "read write";
|
|
4111
|
+
return `auth:
|
|
4112
|
+
method: oauth2
|
|
4113
|
+
devOnly: true
|
|
4114
|
+
${providerLine} middleware: ./middleware.ts
|
|
4115
|
+
oauth2:
|
|
4116
|
+
authorizationUrl: https://dev-mock-oauth2/authorize
|
|
4117
|
+
tokenUrl: https://dev-mock-oauth2/token
|
|
4118
|
+
clientId: dev-mock-client
|
|
4119
|
+
clientSecret: dev-mock-secret
|
|
4120
|
+
scopes: ${scopes}
|
|
4121
|
+
${rbacSection}
|
|
4122
|
+
protectedRoutes:
|
|
4123
|
+
${routeLines}
|
|
4124
|
+
${auditSection}
|
|
4125
|
+
`;
|
|
4126
|
+
}
|
|
4127
|
+
function deriveDevKey(roleName) {
|
|
4128
|
+
const segment = roleName.split("_")[0];
|
|
4129
|
+
return (segment ?? roleName).toLowerCase();
|
|
4130
|
+
}
|
|
4131
|
+
function generateMockAuthContent(roles, mockUsers) {
|
|
4132
|
+
const entries = [];
|
|
4133
|
+
const scriptLines = [];
|
|
4134
|
+
for (let i = 0; i < roles.length; i++) {
|
|
4135
|
+
const role = roles[i];
|
|
4136
|
+
const devKey = deriveDevKey(role);
|
|
4137
|
+
const persona = mockUsers?.[i];
|
|
4138
|
+
const name = persona?.name ?? `Dev ${role}`;
|
|
4139
|
+
const email = persona?.email ?? `dev-${devKey}@example.mil`;
|
|
4140
|
+
const edipi = String(i + 1).padStart(10, "0");
|
|
4141
|
+
entries.push(` ${devKey}: {
|
|
4142
|
+
id: 'mock-${devKey}-${String(i + 1).padStart(3, "0")}',
|
|
4143
|
+
name: '${name}',
|
|
4144
|
+
email: '${email}',
|
|
4145
|
+
roles: ['${role}'],
|
|
4146
|
+
edipi: '${edipi}',
|
|
4147
|
+
}`);
|
|
4148
|
+
scriptLines.push(` * pnpm dev:${devKey} \u2014 ${name}`);
|
|
4149
|
+
}
|
|
4150
|
+
return `/**
|
|
4151
|
+
* Mock authentication for development mode.
|
|
4152
|
+
*
|
|
4153
|
+
* DEV_ONLY_MODE \u2014 no real auth provider. Select a persona via MOCK_USER env var
|
|
4154
|
+
* or use the convenience scripts in package.json:
|
|
4155
|
+
${scriptLines.join("\n")}
|
|
4156
|
+
*/
|
|
4157
|
+
|
|
4158
|
+
export const MOCK_USERS = {
|
|
4159
|
+
${entries.join(",\n")}
|
|
4160
|
+
} as const;
|
|
4161
|
+
|
|
4162
|
+
export type MockRole = keyof typeof MOCK_USERS;
|
|
4163
|
+
|
|
4164
|
+
export function getMockUser(role?: string) {
|
|
4165
|
+
const key = (role ?? process.env.MOCK_USER) as MockRole | undefined;
|
|
4166
|
+
if (!key) return null;
|
|
4167
|
+
return MOCK_USERS[key] ?? null;
|
|
4168
|
+
}
|
|
4169
|
+
|
|
4170
|
+
export function mockAuthProvider() {
|
|
4171
|
+
return getMockUser();
|
|
4172
|
+
}
|
|
4173
|
+
`;
|
|
4174
|
+
}
|
|
4175
|
+
function updatePackageJsonScripts(existingJson, roles, mockUsers) {
|
|
4176
|
+
const pkg = JSON.parse(existingJson);
|
|
4177
|
+
const scripts = pkg.scripts ?? {};
|
|
4178
|
+
delete scripts["dev:admin"];
|
|
4179
|
+
delete scripts["dev:analyst"];
|
|
4180
|
+
delete scripts["dev:viewer"];
|
|
4181
|
+
for (let i = 0; i < roles.length; i++) {
|
|
4182
|
+
const devKey = deriveDevKey(roles[i]);
|
|
4183
|
+
scripts[`dev:${devKey}`] = `MOCK_USER=${devKey} next dev`;
|
|
4184
|
+
}
|
|
4185
|
+
pkg.scripts = scripts;
|
|
4186
|
+
return JSON.stringify(pkg, null, 2) + "\n";
|
|
4187
|
+
}
|
|
3842
4188
|
async function configureAuthHandler(params, cwd) {
|
|
3843
4189
|
const {
|
|
3844
4190
|
method,
|
|
3845
4191
|
provider,
|
|
4192
|
+
devOnly = false,
|
|
4193
|
+
mockUsers,
|
|
3846
4194
|
rbacRoles = ["SUPER_ADMIN", "ADMIN", "ANALYST"],
|
|
3847
4195
|
auditEnabled = true,
|
|
3848
4196
|
auditRetentionDays = 90,
|
|
@@ -3872,7 +4220,16 @@ async function configureAuthHandler(params, cwd) {
|
|
|
3872
4220
|
}
|
|
3873
4221
|
const filesWritten = [];
|
|
3874
4222
|
try {
|
|
3875
|
-
const middlewareContent =
|
|
4223
|
+
const middlewareContent = devOnly ? generateDevOnlyMiddlewareContent(
|
|
4224
|
+
method,
|
|
4225
|
+
params,
|
|
4226
|
+
roles,
|
|
4227
|
+
defaultRole,
|
|
4228
|
+
hierarchy,
|
|
4229
|
+
auditEnabled,
|
|
4230
|
+
auditRetentionDays,
|
|
4231
|
+
protectedRoutes
|
|
4232
|
+
) : generateMiddlewareContent(
|
|
3876
4233
|
method,
|
|
3877
4234
|
params,
|
|
3878
4235
|
roles,
|
|
@@ -3896,30 +4253,87 @@ async function configureAuthHandler(params, cwd) {
|
|
|
3896
4253
|
isError: true
|
|
3897
4254
|
};
|
|
3898
4255
|
}
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
4256
|
+
if (!devOnly) {
|
|
4257
|
+
try {
|
|
4258
|
+
const envBlock = generateEnvBlock(method, params);
|
|
4259
|
+
const envPath = (0, import_path7.join)(cwd, ".env.example");
|
|
4260
|
+
if ((0, import_fs7.existsSync)(envPath)) {
|
|
4261
|
+
const existing = (0, import_fs7.readFileSync)(envPath, "utf8");
|
|
4262
|
+
(0, import_fs7.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
|
|
4263
|
+
} else {
|
|
4264
|
+
(0, import_fs7.writeFileSync)(envPath, envBlock, "utf8");
|
|
4265
|
+
}
|
|
4266
|
+
filesWritten.push(".env.example");
|
|
4267
|
+
} catch (err) {
|
|
4268
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4269
|
+
return {
|
|
4270
|
+
content: [
|
|
4271
|
+
{
|
|
4272
|
+
type: "text",
|
|
4273
|
+
text: JSON.stringify({ success: false, error: `Failed writing .env.example: ${msg}` })
|
|
4274
|
+
}
|
|
4275
|
+
],
|
|
4276
|
+
isError: true
|
|
4277
|
+
};
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
if (devOnly) {
|
|
4281
|
+
try {
|
|
4282
|
+
const mockAuthDir = (0, import_path7.join)(cwd, "lib");
|
|
4283
|
+
(0, import_fs7.mkdirSync)(mockAuthDir, { recursive: true });
|
|
4284
|
+
const mockAuthContent = generateMockAuthContent(roles, mockUsers);
|
|
4285
|
+
(0, import_fs7.writeFileSync)((0, import_path7.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
|
|
4286
|
+
filesWritten.push("lib/mock-auth.ts");
|
|
4287
|
+
} catch (err) {
|
|
4288
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4289
|
+
return {
|
|
4290
|
+
content: [
|
|
4291
|
+
{
|
|
4292
|
+
type: "text",
|
|
4293
|
+
text: JSON.stringify({
|
|
4294
|
+
success: false,
|
|
4295
|
+
error: `Failed writing lib/mock-auth.ts: ${msg}`
|
|
4296
|
+
})
|
|
4297
|
+
}
|
|
4298
|
+
],
|
|
4299
|
+
isError: true
|
|
4300
|
+
};
|
|
4301
|
+
}
|
|
4302
|
+
try {
|
|
4303
|
+
const pkgPath = (0, import_path7.join)(cwd, "package.json");
|
|
4304
|
+
if ((0, import_fs7.existsSync)(pkgPath)) {
|
|
4305
|
+
const existingPkg = (0, import_fs7.readFileSync)(pkgPath, "utf8");
|
|
4306
|
+
const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
|
|
4307
|
+
(0, import_fs7.writeFileSync)(pkgPath, updatedPkg, "utf8");
|
|
4308
|
+
filesWritten.push("package.json");
|
|
4309
|
+
}
|
|
4310
|
+
} catch (err) {
|
|
4311
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4312
|
+
return {
|
|
4313
|
+
content: [
|
|
4314
|
+
{
|
|
4315
|
+
type: "text",
|
|
4316
|
+
text: JSON.stringify({
|
|
4317
|
+
success: false,
|
|
4318
|
+
error: `Failed updating package.json: ${msg}`
|
|
4319
|
+
})
|
|
4320
|
+
}
|
|
4321
|
+
],
|
|
4322
|
+
isError: true
|
|
4323
|
+
};
|
|
3907
4324
|
}
|
|
3908
|
-
filesWritten.push(".env.example");
|
|
3909
|
-
} catch (err) {
|
|
3910
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3911
|
-
return {
|
|
3912
|
-
content: [
|
|
3913
|
-
{
|
|
3914
|
-
type: "text",
|
|
3915
|
-
text: JSON.stringify({ success: false, error: `Failed writing .env.example: ${msg}` })
|
|
3916
|
-
}
|
|
3917
|
-
],
|
|
3918
|
-
isError: true
|
|
3919
|
-
};
|
|
3920
4325
|
}
|
|
3921
4326
|
try {
|
|
3922
|
-
const authYaml =
|
|
4327
|
+
const authYaml = devOnly ? generateDevOnlyYamlBlock(
|
|
4328
|
+
method,
|
|
4329
|
+
params,
|
|
4330
|
+
roles,
|
|
4331
|
+
defaultRole,
|
|
4332
|
+
hierarchy,
|
|
4333
|
+
auditEnabled,
|
|
4334
|
+
auditRetentionDays,
|
|
4335
|
+
protectedRoutes
|
|
4336
|
+
) : generateYamlBlock(
|
|
3923
4337
|
method,
|
|
3924
4338
|
params,
|
|
3925
4339
|
roles,
|
|
@@ -4001,7 +4415,9 @@ function registerAuthTools(server2) {
|
|
|
4001
4415
|
// Routes
|
|
4002
4416
|
protectedRoutes: jsonCoerce(import_zod13.z.array(import_zod13.z.string()).optional()),
|
|
4003
4417
|
// Injection for tests
|
|
4004
|
-
_cwd: import_zod13.z.string().optional()
|
|
4418
|
+
_cwd: import_zod13.z.string().optional(),
|
|
4419
|
+
devOnly: boolCoerce(import_zod13.z.boolean().optional()),
|
|
4420
|
+
mockUsers: jsonCoerce(import_zod13.z.array(import_zod13.z.object({ name: import_zod13.z.string(), email: import_zod13.z.string() })).optional())
|
|
4005
4421
|
},
|
|
4006
4422
|
async (params) => {
|
|
4007
4423
|
const cwd = params._cwd ?? process.cwd();
|
|
@@ -4021,15 +4437,15 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
4021
4437
|
],
|
|
4022
4438
|
[
|
|
4023
4439
|
"stackwright-pro-auth-otter.json",
|
|
4024
|
-
"
|
|
4440
|
+
"4bf6beba7150d08c74c5f6fbbeb20e988aba52a2029ff2892615e71f6ab12ed1"
|
|
4025
4441
|
],
|
|
4026
4442
|
[
|
|
4027
4443
|
"stackwright-pro-dashboard-otter.json",
|
|
4028
|
-
"
|
|
4444
|
+
"9c319d311801730e8dc9bc142eebb8fc5a7f48da48fa0b8d8c3b7431652447be"
|
|
4029
4445
|
],
|
|
4030
4446
|
[
|
|
4031
4447
|
"stackwright-pro-data-otter.json",
|
|
4032
|
-
"
|
|
4448
|
+
"4d9369277685a4acc484116920c9622ad8a1838012a493fcfe42a6ae5abe53cf"
|
|
4033
4449
|
],
|
|
4034
4450
|
[
|
|
4035
4451
|
"stackwright-pro-designer-otter.json",
|
|
@@ -4037,23 +4453,23 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
4037
4453
|
],
|
|
4038
4454
|
[
|
|
4039
4455
|
"stackwright-pro-domain-expert-otter.json",
|
|
4040
|
-
"
|
|
4456
|
+
"6055a2efc78f54a8393f628839e2a2563bf0c6de3ad32de00c82779a53381efd"
|
|
4041
4457
|
],
|
|
4042
4458
|
[
|
|
4043
4459
|
"stackwright-pro-foreman-otter.json",
|
|
4044
|
-
"
|
|
4460
|
+
"ab38ef53b95ec610a38b2866d78a135cbec16d257a9b35d7e46e2fee2d4de235"
|
|
4045
4461
|
],
|
|
4046
4462
|
[
|
|
4047
4463
|
"stackwright-pro-geo-otter.json",
|
|
4048
|
-
"
|
|
4464
|
+
"9e09aaf2bb10197c6d1c05d0fd5f5f9380acc0cb697a410fcae839ffba648561"
|
|
4049
4465
|
],
|
|
4050
4466
|
[
|
|
4051
4467
|
"stackwright-pro-page-otter.json",
|
|
4052
|
-
"
|
|
4468
|
+
"532bb7e9a25a5c832edd1ff1ea0886dd4453905d86e6f9331eb957ae5e121833"
|
|
4053
4469
|
],
|
|
4054
4470
|
[
|
|
4055
4471
|
"stackwright-pro-polish-otter.json",
|
|
4056
|
-
"
|
|
4472
|
+
"8f284d4d6a204137cd786824fc584d5bddac1bc757204769b99ca5412cf2cea2"
|
|
4057
4473
|
],
|
|
4058
4474
|
[
|
|
4059
4475
|
"stackwright-pro-theme-otter.json",
|
|
@@ -4065,7 +4481,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
4065
4481
|
],
|
|
4066
4482
|
[
|
|
4067
4483
|
"stackwright-services-otter.json",
|
|
4068
|
-
"
|
|
4484
|
+
"4893a596d187110124f78336ee91184a51b3c8d980c455382fe481adb9b487b5"
|
|
4069
4485
|
]
|
|
4070
4486
|
]);
|
|
4071
4487
|
Object.freeze(_checksums);
|
|
@@ -4798,7 +5214,7 @@ var package_default = {
|
|
|
4798
5214
|
"test:coverage": "vitest run --coverage"
|
|
4799
5215
|
},
|
|
4800
5216
|
name: "@stackwright-pro/mcp",
|
|
4801
|
-
version: "0.2.0-alpha.
|
|
5217
|
+
version: "0.2.0-alpha.61",
|
|
4802
5218
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
4803
5219
|
license: "SEE LICENSE IN LICENSE",
|
|
4804
5220
|
main: "./dist/server.js",
|
|
@@ -4854,6 +5270,15 @@ registerDomainTools(server);
|
|
|
4854
5270
|
registerTypeSchemasTool(server);
|
|
4855
5271
|
async function main() {
|
|
4856
5272
|
const transport = new import_stdio.StdioServerTransport();
|
|
5273
|
+
try {
|
|
5274
|
+
const servicesRegisterPkg = "@stackwright-services/mcp/register";
|
|
5275
|
+
const mod = await import(servicesRegisterPkg);
|
|
5276
|
+
if (typeof mod.registerServicesTools === "function") {
|
|
5277
|
+
mod.registerServicesTools(server);
|
|
5278
|
+
console.error("Stackwright Services tools registered on pro MCP");
|
|
5279
|
+
}
|
|
5280
|
+
} catch {
|
|
5281
|
+
}
|
|
4857
5282
|
await server.connect(transport);
|
|
4858
5283
|
console.error("Stackwright Pro MCP server running on stdio");
|
|
4859
5284
|
}
|