@wairon/cli 5.0.2-dev.7 → 5.0.2-dev.9
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/cli/index.js +247 -29
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +242 -26
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-architect.md +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -393,7 +393,7 @@ var init_template = __esm({
|
|
|
393
393
|
});
|
|
394
394
|
|
|
395
395
|
// src/models/specs.ts
|
|
396
|
-
var import_zod6, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, ComponentSpecSchema, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, MethodSignatureSchema, InterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, InvariantSchema, TypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, GroupSpecSchema;
|
|
396
|
+
var import_zod6, SpecIdSchema, SpecStatusSchema, BoundaryItemSchema, RequirementItemSchema, DatabaseSpecSchema, DiagramConfigSchema, SURFACE_AUDIENCES, SurfaceAudienceSchema, SystemPublicInterfaceSchema, SystemSpecSchema, PublicInterfaceTypeSchema, PublicInterfaceSchema, TrustedLinkSchema, LintAllowSchema, LintConfigSchema, ExtDataSchema, LifecycleEntrypointSchema, SubsystemSpecSchema, ComponentTypeSchema, PATTERN_TYPES, PortalTypeSchema, DispatchBindingSchema, DurabilitySchema, PatternRefSchema, EventBindingSchema, ExternalLinkTypeSchema, ExternalLinkSchema, PortalAuthSchemeSchema, PortalAuthSchema, ComponentSpecSchema, HttpMethodSchema, TransportSchema, EndpointSchema, SEMANTIC_GUARANTEES, GuaranteeSchema, MethodParamSchema, MethodSignatureSchema, InterfaceSpecSchema, NarrativeStepTypeSchema, LoopKindSchema, SwitchCaseSchema, CatchClauseSchema, ParallelBranchSchema, NarrativeStepSchema, NarrativeDetailSchema, ConformanceTierSchema, MethodImplementationSchema, ImplementationSpecSchema, TypeKindSchema, TypeFieldSchema, TypeMethodSchema, InvariantSchema, TypeSpecSchema, SurfaceOriginSchema, SurfaceTypeDefSchema, SurfaceContractEntrySchema, SurfaceSnapshotSchema, NamedOpenApiSpecSchema, GroupSpecSchema;
|
|
397
397
|
var init_specs = __esm({
|
|
398
398
|
"src/models/specs.ts"() {
|
|
399
399
|
"use strict";
|
|
@@ -604,6 +604,21 @@ var init_specs = __esm({
|
|
|
604
604
|
type: ExternalLinkTypeSchema.default("informative"),
|
|
605
605
|
label: import_zod6.z.string().optional()
|
|
606
606
|
});
|
|
607
|
+
PortalAuthSchemeSchema = import_zod6.z.enum(["none", "apiKey", "bearer", "basic", "oauth2", "openIdConnect", "custom"]);
|
|
608
|
+
PortalAuthSchema = import_zod6.z.object({
|
|
609
|
+
scheme: PortalAuthSchemeSchema,
|
|
610
|
+
in: import_zod6.z.enum(["header", "query", "cookie"]).optional(),
|
|
611
|
+
name: import_zod6.z.string().optional(),
|
|
612
|
+
bearerFormat: import_zod6.z.string().optional(),
|
|
613
|
+
authorizationUrl: import_zod6.z.string().optional(),
|
|
614
|
+
tokenUrl: import_zod6.z.string().optional(),
|
|
615
|
+
refreshUrl: import_zod6.z.string().optional(),
|
|
616
|
+
scopes: import_zod6.z.array(import_zod6.z.object({ name: import_zod6.z.string(), description: import_zod6.z.string() })).optional(),
|
|
617
|
+
flow: import_zod6.z.enum(["authorizationCode", "clientCredentials", "implicit", "password"]).optional(),
|
|
618
|
+
openIdConnectUrl: import_zod6.z.string().optional(),
|
|
619
|
+
description: import_zod6.z.string().optional(),
|
|
620
|
+
example: import_zod6.z.string().optional()
|
|
621
|
+
});
|
|
607
622
|
ComponentSpecSchema = import_zod6.z.object({
|
|
608
623
|
id: SpecIdSchema,
|
|
609
624
|
name: import_zod6.z.string(),
|
|
@@ -617,6 +632,10 @@ var init_specs = __esm({
|
|
|
617
632
|
dependsOn: import_zod6.z.array(import_zod6.z.string()).default([]),
|
|
618
633
|
portalType: PortalTypeSchema.optional(),
|
|
619
634
|
basePath: import_zod6.z.string().optional(),
|
|
635
|
+
/** Portal-only: the API's authentication scheme (see PortalAuthSchema) — projected
|
|
636
|
+
* into the generated OpenAPI's securitySchemes/security. Portals with different auth
|
|
637
|
+
* must be separate components (one auth per portal ⇒ one OpenAPI spec per portal). */
|
|
638
|
+
auth: PortalAuthSchema.optional(),
|
|
620
639
|
/** Portal-only: capability → component.method dispatch table (see DispatchBindingSchema). */
|
|
621
640
|
dispatch: import_zod6.z.array(DispatchBindingSchema).optional(),
|
|
622
641
|
/** Store-only: whether held state survives restart (see DurabilitySchema). */
|
|
@@ -766,6 +785,14 @@ var init_specs = __esm({
|
|
|
766
785
|
// Required if type is 'call', references Method name on target interface
|
|
767
786
|
capability: import_zod6.z.string().optional(),
|
|
768
787
|
// Required if type is 'dispatch': the capability routed through the target Portal's dispatch table
|
|
788
|
+
/**
|
|
789
|
+
* call/dispatch only: the credential this step presents to an authed callee
|
|
790
|
+
* Portal, and WHERE it is loaded from (`from` — a secret-store component id,
|
|
791
|
+
* `env:API_KEY`, a config key, a vault ref, …). A declared DESIGN NOTE — wairon
|
|
792
|
+
* never fetches it — but its absence on a call into a Portal whose `auth ≠ none`
|
|
793
|
+
* warns (PORTAL_AUTH_UNMET), so credential loading is never overlooked.
|
|
794
|
+
*/
|
|
795
|
+
auth: import_zod6.z.object({ from: import_zod6.z.string(), note: import_zod6.z.string().optional() }).optional(),
|
|
769
796
|
assertsGuarantees: import_zod6.z.array(GuaranteeSchema).optional(),
|
|
770
797
|
/**
|
|
771
798
|
* Declared entity invariants this step upholds, as "<type-id>.<invariant-id>"
|
|
@@ -982,7 +1009,12 @@ var init_specs = __esm({
|
|
|
982
1009
|
dispatch: import_zod6.z.array(DispatchBindingSchema).optional(),
|
|
983
1010
|
details: import_zod6.z.string().default(""),
|
|
984
1011
|
version: import_zod6.z.string().optional(),
|
|
985
|
-
stability: import_zod6.z.string().optional()
|
|
1012
|
+
stability: import_zod6.z.string().optional(),
|
|
1013
|
+
/** Projected copy of the backing Portal's auth (see PortalAuthSchema) — the codec
|
|
1014
|
+
* emits it as OpenAPI securitySchemes/security. */
|
|
1015
|
+
auth: PortalAuthSchema.optional(),
|
|
1016
|
+
/** The backing Portal's basePath — becomes the per-portal OpenAPI `servers` url. */
|
|
1017
|
+
basePath: import_zod6.z.string().optional()
|
|
986
1018
|
});
|
|
987
1019
|
SurfaceSnapshotSchema = import_zod6.z.object({
|
|
988
1020
|
/** Producing project/system name — the snapshot's resolution identity. */
|
|
@@ -997,6 +1029,11 @@ var init_specs = __esm({
|
|
|
997
1029
|
/** Transitive type closure of every exported signature — self-contained. */
|
|
998
1030
|
types: import_zod6.z.array(SurfaceTypeDefSchema).default([])
|
|
999
1031
|
});
|
|
1032
|
+
NamedOpenApiSpecSchema = import_zod6.z.object({
|
|
1033
|
+
portalId: import_zod6.z.string(),
|
|
1034
|
+
name: import_zod6.z.string(),
|
|
1035
|
+
document: import_zod6.z.string()
|
|
1036
|
+
});
|
|
1000
1037
|
GroupSpecSchema = import_zod6.z.object({
|
|
1001
1038
|
kind: import_zod6.z.literal("group"),
|
|
1002
1039
|
id: SpecIdSchema,
|
|
@@ -5461,7 +5498,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
5461
5498
|
+ (scopeFocus ? staticChip('current view') : '')
|
|
5462
5499
|
+ chip(c.subsystem, 'subsystem', c.subsystem)
|
|
5463
5500
|
+ (scopeFocus ? '' : openViewButton('component', c.id, c.owns.length > 0))
|
|
5464
|
-
+ openApiButton(componentExposesApi(c), c.
|
|
5501
|
+
+ openApiButton(componentExposesApi(c), c.id)
|
|
5465
5502
|
+ openSpecButton('component', c.id);
|
|
5466
5503
|
|
|
5467
5504
|
var linkedTypes = MODEL.types.filter(function (t) { return t.componentClass === c.id; });
|
|
@@ -6269,11 +6306,63 @@ function operationFor(method, closureIds) {
|
|
|
6269
6306
|
}
|
|
6270
6307
|
return op;
|
|
6271
6308
|
}
|
|
6272
|
-
function
|
|
6273
|
-
|
|
6274
|
-
const
|
|
6309
|
+
function securitySchemeObject(auth) {
|
|
6310
|
+
if (auth.scheme === "none") return null;
|
|
6311
|
+
const desc = auth.description ? { description: auth.description } : {};
|
|
6312
|
+
switch (auth.scheme) {
|
|
6313
|
+
case "apiKey":
|
|
6314
|
+
return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "X-API-Key", ...desc };
|
|
6315
|
+
case "bearer":
|
|
6316
|
+
return { type: "http", scheme: "bearer", ...auth.bearerFormat ? { bearerFormat: auth.bearerFormat } : {}, ...desc };
|
|
6317
|
+
case "basic":
|
|
6318
|
+
return { type: "http", scheme: "basic", ...desc };
|
|
6319
|
+
case "oauth2": {
|
|
6320
|
+
const flow = { scopes: Object.fromEntries((auth.scopes ?? []).map((s) => [s.name, s.description])) };
|
|
6321
|
+
if (auth.authorizationUrl) flow.authorizationUrl = auth.authorizationUrl;
|
|
6322
|
+
if (auth.tokenUrl) flow.tokenUrl = auth.tokenUrl;
|
|
6323
|
+
if (auth.refreshUrl) flow.refreshUrl = auth.refreshUrl;
|
|
6324
|
+
return { type: "oauth2", flows: { [auth.flow ?? "authorizationCode"]: flow }, ...desc };
|
|
6325
|
+
}
|
|
6326
|
+
case "openIdConnect":
|
|
6327
|
+
return { type: "openIdConnect", openIdConnectUrl: auth.openIdConnectUrl ?? "", ...desc };
|
|
6328
|
+
case "custom":
|
|
6329
|
+
return { type: "apiKey", in: auth.in ?? "header", name: auth.name ?? "Authorization", description: auth.description ?? auth.example ?? "Custom authentication scheme." };
|
|
6330
|
+
default:
|
|
6331
|
+
return null;
|
|
6332
|
+
}
|
|
6333
|
+
}
|
|
6334
|
+
function schemeBaseName(scheme) {
|
|
6335
|
+
return { apiKey: "ApiKeyAuth", bearer: "BearerAuth", basic: "BasicAuth", oauth2: "OAuth2", openIdConnect: "OpenIdConnect", custom: "CustomAuth" }[scheme] ?? "Auth";
|
|
6336
|
+
}
|
|
6337
|
+
function buildSecurity(entries) {
|
|
6338
|
+
const schemes = {};
|
|
6339
|
+
const nameByContent = /* @__PURE__ */ new Map();
|
|
6340
|
+
const securityByEntry = /* @__PURE__ */ new Map();
|
|
6341
|
+
for (const entry of entries) {
|
|
6342
|
+
if (!entry.auth) continue;
|
|
6343
|
+
const obj = securitySchemeObject(entry.auth);
|
|
6344
|
+
if (!obj) continue;
|
|
6345
|
+
const content = JSON.stringify(obj);
|
|
6346
|
+
let name = nameByContent.get(content);
|
|
6347
|
+
if (!name) {
|
|
6348
|
+
name = schemeBaseName(entry.auth.scheme);
|
|
6349
|
+
for (let n = 2; schemes[name]; n++) name = schemeBaseName(entry.auth.scheme) + n;
|
|
6350
|
+
schemes[name] = obj;
|
|
6351
|
+
nameByContent.set(content, name);
|
|
6352
|
+
}
|
|
6353
|
+
const scopeNames = entry.auth.scheme === "oauth2" ? (entry.auth.scopes ?? []).map((s) => s.name) : [];
|
|
6354
|
+
securityByEntry.set(entry.id, { [name]: scopeNames });
|
|
6355
|
+
}
|
|
6356
|
+
return { schemes, securityByEntry };
|
|
6357
|
+
}
|
|
6358
|
+
function httpEntriesOf(snapshot) {
|
|
6359
|
+
return snapshot.interfaces.filter((e) => e.type === "REST" || e.methods.some((m) => m.endpoint?.transport === "HTTP"));
|
|
6360
|
+
}
|
|
6361
|
+
function renderDoc(snapshot, entries, closureIds, opts = {}) {
|
|
6362
|
+
const { schemes, securityByEntry } = buildSecurity(entries);
|
|
6275
6363
|
const paths = {};
|
|
6276
|
-
for (const entry of
|
|
6364
|
+
for (const entry of entries) {
|
|
6365
|
+
const security = securityByEntry.get(entry.id);
|
|
6277
6366
|
for (const method of entry.methods) {
|
|
6278
6367
|
const endpoint = method.endpoint;
|
|
6279
6368
|
if (!endpoint || endpoint.transport !== "HTTP") continue;
|
|
@@ -6281,7 +6370,8 @@ function toOpenApi(snapshot) {
|
|
|
6281
6370
|
paths[p] = paths[p] ?? {};
|
|
6282
6371
|
paths[p][endpoint.method.toLowerCase()] = {
|
|
6283
6372
|
tags: [entry.id],
|
|
6284
|
-
...operationFor(method, closureIds)
|
|
6373
|
+
...operationFor(method, closureIds),
|
|
6374
|
+
...security ? { security: [security] } : {}
|
|
6285
6375
|
};
|
|
6286
6376
|
}
|
|
6287
6377
|
}
|
|
@@ -6294,19 +6384,45 @@ function toOpenApi(snapshot) {
|
|
|
6294
6384
|
required: t.fields.filter((f) => !f.optional).map((f) => f.name)
|
|
6295
6385
|
};
|
|
6296
6386
|
}
|
|
6297
|
-
const
|
|
6387
|
+
const components = {};
|
|
6388
|
+
if (Object.keys(schemas).length) components.schemas = schemas;
|
|
6389
|
+
if (Object.keys(schemes).length) components.securitySchemes = schemes;
|
|
6390
|
+
const basePaths = [...new Set(entries.map((e) => e.basePath).filter((b) => !!b))];
|
|
6391
|
+
const servers = opts.servers && basePaths.length === 1 ? [{ url: basePaths[0] }] : void 0;
|
|
6392
|
+
return {
|
|
6298
6393
|
openapi: "3.1.0",
|
|
6299
6394
|
info: {
|
|
6300
|
-
title: snapshot.projectName,
|
|
6395
|
+
title: opts.title ?? snapshot.projectName,
|
|
6301
6396
|
version: snapshot.version ?? "0.0.0",
|
|
6302
6397
|
...snapshot.stateId ? { "x-wairon-state-id": snapshot.stateId } : {},
|
|
6303
6398
|
"x-wairon-origin": snapshot.origin,
|
|
6304
6399
|
"x-wairon-generated-at": snapshot.generatedAt
|
|
6305
6400
|
},
|
|
6401
|
+
...servers ? { servers } : {},
|
|
6306
6402
|
paths,
|
|
6307
|
-
...Object.keys(
|
|
6403
|
+
...Object.keys(components).length ? { components } : {}
|
|
6308
6404
|
};
|
|
6309
|
-
|
|
6405
|
+
}
|
|
6406
|
+
function toOpenApi(snapshot) {
|
|
6407
|
+
const closureIds = new Set(snapshot.types.map((t) => t.id));
|
|
6408
|
+
return JSON.stringify(renderDoc(snapshot, httpEntriesOf(snapshot), closureIds), null, 2);
|
|
6409
|
+
}
|
|
6410
|
+
function toOpenApiSet(snapshot) {
|
|
6411
|
+
const closureIds = new Set(snapshot.types.map((t) => t.id));
|
|
6412
|
+
const byPortal = /* @__PURE__ */ new Map();
|
|
6413
|
+
const order = [];
|
|
6414
|
+
for (const entry of httpEntriesOf(snapshot)) {
|
|
6415
|
+
if (!byPortal.has(entry.component)) {
|
|
6416
|
+
byPortal.set(entry.component, []);
|
|
6417
|
+
order.push(entry.component);
|
|
6418
|
+
}
|
|
6419
|
+
byPortal.get(entry.component).push(entry);
|
|
6420
|
+
}
|
|
6421
|
+
return order.map((portalId) => {
|
|
6422
|
+
const entries = byPortal.get(portalId);
|
|
6423
|
+
const name = entries[0]?.name ?? portalId;
|
|
6424
|
+
return { portalId, name, document: JSON.stringify(renderDoc(snapshot, entries, closureIds, { title: name, servers: true }), null, 2) };
|
|
6425
|
+
});
|
|
6310
6426
|
}
|
|
6311
6427
|
function isOpenApiDocument(body) {
|
|
6312
6428
|
try {
|
|
@@ -6328,6 +6444,39 @@ function typeRefFromSchema(schema) {
|
|
|
6328
6444
|
if (typeof t === "string" && t !== "object") return t;
|
|
6329
6445
|
return "json";
|
|
6330
6446
|
}
|
|
6447
|
+
function authFromSecurityScheme(scheme) {
|
|
6448
|
+
const desc = typeof scheme.description === "string" ? { description: scheme.description } : {};
|
|
6449
|
+
if (scheme.type === "apiKey") {
|
|
6450
|
+
return {
|
|
6451
|
+
scheme: "apiKey",
|
|
6452
|
+
...scheme.in === "header" || scheme.in === "query" || scheme.in === "cookie" ? { in: scheme.in } : {},
|
|
6453
|
+
...typeof scheme.name === "string" ? { name: scheme.name } : {},
|
|
6454
|
+
...desc
|
|
6455
|
+
};
|
|
6456
|
+
}
|
|
6457
|
+
if (scheme.type === "http") {
|
|
6458
|
+
if (scheme.scheme === "bearer") return { scheme: "bearer", ...typeof scheme.bearerFormat === "string" ? { bearerFormat: scheme.bearerFormat } : {}, ...desc };
|
|
6459
|
+
if (scheme.scheme === "basic") return { scheme: "basic", ...desc };
|
|
6460
|
+
}
|
|
6461
|
+
if (scheme.type === "oauth2") {
|
|
6462
|
+
const flows = scheme.flows ?? {};
|
|
6463
|
+
const flowKey = Object.keys(flows)[0];
|
|
6464
|
+
const f = flows[flowKey] ?? {};
|
|
6465
|
+
return {
|
|
6466
|
+
scheme: "oauth2",
|
|
6467
|
+
...flowKey === "authorizationCode" || flowKey === "clientCredentials" || flowKey === "implicit" || flowKey === "password" ? { flow: flowKey } : {},
|
|
6468
|
+
...typeof f.authorizationUrl === "string" ? { authorizationUrl: f.authorizationUrl } : {},
|
|
6469
|
+
...typeof f.tokenUrl === "string" ? { tokenUrl: f.tokenUrl } : {},
|
|
6470
|
+
...typeof f.refreshUrl === "string" ? { refreshUrl: f.refreshUrl } : {},
|
|
6471
|
+
scopes: Object.entries(f.scopes ?? {}).map(([name, description]) => ({ name, description })),
|
|
6472
|
+
...desc
|
|
6473
|
+
};
|
|
6474
|
+
}
|
|
6475
|
+
if (scheme.type === "openIdConnect") {
|
|
6476
|
+
return { scheme: "openIdConnect", openIdConnectUrl: typeof scheme.openIdConnectUrl === "string" ? scheme.openIdConnectUrl : "", ...desc };
|
|
6477
|
+
}
|
|
6478
|
+
return void 0;
|
|
6479
|
+
}
|
|
6331
6480
|
function fromOpenApi(document, projectName) {
|
|
6332
6481
|
let parsed;
|
|
6333
6482
|
try {
|
|
@@ -6405,6 +6554,9 @@ function fromOpenApi(document, projectName) {
|
|
|
6405
6554
|
}))
|
|
6406
6555
|
});
|
|
6407
6556
|
}
|
|
6557
|
+
const securitySchemes = parsed.components?.securitySchemes ?? {};
|
|
6558
|
+
const firstScheme = Object.values(securitySchemes)[0];
|
|
6559
|
+
const importedAuth = firstScheme ? authFromSecurityScheme(firstScheme) : void 0;
|
|
6408
6560
|
const entry = {
|
|
6409
6561
|
id: `${projectName}-api`,
|
|
6410
6562
|
name: typeof info.title === "string" ? info.title : projectName,
|
|
@@ -6413,7 +6565,8 @@ function fromOpenApi(document, projectName) {
|
|
|
6413
6565
|
component: `${projectName}-api`,
|
|
6414
6566
|
methods,
|
|
6415
6567
|
details: typeof info.description === "string" ? info.description : `Imported OpenAPI surface of ${projectName}.`,
|
|
6416
|
-
...typeof info.version === "string" ? { version: info.version } : {}
|
|
6568
|
+
...typeof info.version === "string" ? { version: info.version } : {},
|
|
6569
|
+
...importedAuth ? { auth: importedAuth } : {}
|
|
6417
6570
|
};
|
|
6418
6571
|
return SurfaceSnapshotSchema.parse({
|
|
6419
6572
|
projectName,
|
|
@@ -6528,6 +6681,10 @@ function projectOwnSurface(maxAudience) {
|
|
|
6528
6681
|
component: comp.id,
|
|
6529
6682
|
methods,
|
|
6530
6683
|
...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
|
|
6684
|
+
// Project the backing Portal's auth + basePath so the codec can emit
|
|
6685
|
+
// OpenAPI security + per-portal servers self-contained from the snapshot.
|
|
6686
|
+
...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
|
|
6687
|
+
...comp.basePath ? { basePath: comp.basePath } : {},
|
|
6531
6688
|
details: raw.details ?? "",
|
|
6532
6689
|
...raw.version ? { version: raw.version } : {},
|
|
6533
6690
|
...raw.stability ? { stability: raw.stability } : {}
|
|
@@ -6577,20 +6734,28 @@ function removeSnapshot(projectName, rootDir = getProjectRoot()) {
|
|
|
6577
6734
|
function loadSurfaceSnapshots() {
|
|
6578
6735
|
return listSnapshots();
|
|
6579
6736
|
}
|
|
6737
|
+
function renderOpenApiForms(snapshot) {
|
|
6738
|
+
const renderedSet = toOpenApiSet(snapshot);
|
|
6739
|
+
return { renderedSet, ...renderedSet.length === 1 ? { rendered: renderedSet[0].document } : {} };
|
|
6740
|
+
}
|
|
6741
|
+
function writeSurfaceFile(outPath, body, snapshot) {
|
|
6742
|
+
const resolved = path5.resolve(outPath);
|
|
6743
|
+
fs4.mkdirSync(path5.dirname(resolved), { recursive: true });
|
|
6744
|
+
if (body !== void 0) fs4.writeFileSync(resolved, body);
|
|
6745
|
+
else writeYamlFile(resolved, snapshot);
|
|
6746
|
+
return resolved;
|
|
6747
|
+
}
|
|
6580
6748
|
function exportSurface(maxAudience, format, outPath) {
|
|
6581
6749
|
const snapshot = projectOwnSurface(maxAudience);
|
|
6582
|
-
const
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
}
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
writtenTo = path5.resolve(outPath);
|
|
6592
|
-
}
|
|
6593
|
-
return { snapshot, ...rendered !== void 0 ? { rendered } : {}, ...writtenTo ? { writtenTo } : {} };
|
|
6750
|
+
const openapi = format === "openapi" ? renderOpenApiForms(snapshot) : void 0;
|
|
6751
|
+
const body = openapi?.rendered ?? openapi?.renderedSet[0]?.document;
|
|
6752
|
+
const writtenTo = outPath ? writeSurfaceFile(outPath, body, snapshot) : void 0;
|
|
6753
|
+
return {
|
|
6754
|
+
snapshot,
|
|
6755
|
+
...openapi?.rendered !== void 0 ? { rendered: openapi.rendered } : {},
|
|
6756
|
+
...openapi ? { renderedSet: openapi.renderedSet } : {},
|
|
6757
|
+
...writtenTo ? { writtenTo } : {}
|
|
6758
|
+
};
|
|
6594
6759
|
}
|
|
6595
6760
|
function importSurface(sourcePath, origin) {
|
|
6596
6761
|
const resolved = path5.resolve(sourcePath);
|
|
@@ -11389,6 +11554,44 @@ var init_lint_allows = __esm({
|
|
|
11389
11554
|
}
|
|
11390
11555
|
});
|
|
11391
11556
|
|
|
11557
|
+
// src/core/rules/portal-call-auth.ts
|
|
11558
|
+
var portalCallAuthRule;
|
|
11559
|
+
var init_portal_call_auth = __esm({
|
|
11560
|
+
"src/core/rules/portal-call-auth.ts"() {
|
|
11561
|
+
"use strict";
|
|
11562
|
+
portalCallAuthRule = {
|
|
11563
|
+
name: "portal-call-auth",
|
|
11564
|
+
description: "An OUTBOUND narrative `call` into ANOTHER component's Portal whose auth is not `none` must declare the credential source it presents \u2014 the step's `auth.from`, a design note naming where the secret loads from (a secret-store component, env var, config key, vault ref). Its absence warns (PORTAL_AUTH_UNMET) so credential loading is never overlooked; the actual secret is never stored in the spec. Dispatch steps (a portal's OWN inbound routing) and self-calls are not cross-service calls and are excluded.",
|
|
11565
|
+
codes: [
|
|
11566
|
+
{ code: "PORTAL_AUTH_UNMET", defaultSeverity: "warning", summary: "A narrative call into another component's authed Portal does not declare where its credential loads from" }
|
|
11567
|
+
],
|
|
11568
|
+
check(ctx) {
|
|
11569
|
+
for (const impl of ctx.implementations) {
|
|
11570
|
+
const ownComponent = ctx.interfaceMap.get(impl.contract)?.component;
|
|
11571
|
+
const draft = ctx.isImplementationDraft(impl);
|
|
11572
|
+
for (const method of impl.methods ?? []) {
|
|
11573
|
+
for (const step of method.narrative ?? []) {
|
|
11574
|
+
if (step.type !== "call") continue;
|
|
11575
|
+
if (!step.targetComponent || step.targetComponent === ownComponent) continue;
|
|
11576
|
+
const target = ctx.componentMap.get(step.targetComponent);
|
|
11577
|
+
if (!target || target.componentType !== "Portal") continue;
|
|
11578
|
+
if (!target.auth || target.auth.scheme === "none") continue;
|
|
11579
|
+
if (step.auth?.from) continue;
|
|
11580
|
+
ctx.addIssue(
|
|
11581
|
+
"warning",
|
|
11582
|
+
"PORTAL_AUTH_UNMET",
|
|
11583
|
+
`Narrative step ${step.stepNumber} of "${method.name}" in "${impl.id}" calls the authenticated portal "${target.id}" (auth scheme: ${target.auth.scheme}) but does not declare where its credential is loaded from. Add the credential source (the step's auth.from \u2014 a secret-store component, env var, config key, or vault ref), or drop the portal's auth if it needs none.`,
|
|
11584
|
+
impl.id,
|
|
11585
|
+
draft
|
|
11586
|
+
);
|
|
11587
|
+
}
|
|
11588
|
+
}
|
|
11589
|
+
}
|
|
11590
|
+
}
|
|
11591
|
+
};
|
|
11592
|
+
}
|
|
11593
|
+
});
|
|
11594
|
+
|
|
11392
11595
|
// src/core/rules/index.ts
|
|
11393
11596
|
function composeRuleSequence(extraRules = []) {
|
|
11394
11597
|
const base = SDD_RULES.filter((r) => r !== lintAllowsRule);
|
|
@@ -11638,6 +11841,7 @@ var init_rules = __esm({
|
|
|
11638
11841
|
init_hidden_state();
|
|
11639
11842
|
init_dependency_conformance();
|
|
11640
11843
|
init_lint_allows();
|
|
11844
|
+
init_portal_call_auth();
|
|
11641
11845
|
init_source_analysis();
|
|
11642
11846
|
init_types();
|
|
11643
11847
|
init_type_analysis();
|
|
@@ -11659,6 +11863,9 @@ var init_rules = __esm({
|
|
|
11659
11863
|
narrativeAntipatternsRule,
|
|
11660
11864
|
narrativeDetailRule,
|
|
11661
11865
|
portalsRule,
|
|
11866
|
+
// Cross-call auth: a narrative call into an authed Portal must name its
|
|
11867
|
+
// credential source (rides with the portal family).
|
|
11868
|
+
portalCallAuthRule,
|
|
11662
11869
|
stereotypeDepsRule,
|
|
11663
11870
|
patternsRule,
|
|
11664
11871
|
// Facade shape rides with pattern ownership: same §7 doctrine, narrative side.
|
|
@@ -11721,6 +11928,7 @@ var init_rules = __esm({
|
|
|
11721
11928
|
// L4 expectations: implementations and their code linkage.
|
|
11722
11929
|
MISSING_IMPLEMENTATION_METHOD: "implementations",
|
|
11723
11930
|
MISSING_SOURCE_PATH: "implementations",
|
|
11931
|
+
PORTAL_AUTH_UNMET: "implementations",
|
|
11724
11932
|
MISSING_SOURCE_FILE: "implementations",
|
|
11725
11933
|
SOURCE_PATH_ESCAPES_ROOT: "implementations",
|
|
11726
11934
|
UNREALIZED_METHOD: "implementations",
|
|
@@ -15146,6 +15354,7 @@ __export(src_exports, {
|
|
|
15146
15354
|
MethodImplementationSchema: () => MethodImplementationSchema,
|
|
15147
15355
|
MethodParamSchema: () => MethodParamSchema,
|
|
15148
15356
|
MethodSignatureSchema: () => MethodSignatureSchema,
|
|
15357
|
+
NamedOpenApiSpecSchema: () => NamedOpenApiSpecSchema,
|
|
15149
15358
|
NamingRuleConfigSchema: () => NamingRuleConfigSchema,
|
|
15150
15359
|
NarrativeDetailSchema: () => NarrativeDetailSchema,
|
|
15151
15360
|
NarrativeStepSchema: () => NarrativeStepSchema,
|
|
@@ -15159,6 +15368,8 @@ __export(src_exports, {
|
|
|
15159
15368
|
PathsConfigSchema: () => PathsConfigSchema,
|
|
15160
15369
|
PatternDefSchema: () => PatternDefSchema,
|
|
15161
15370
|
PatternRefSchema: () => PatternRefSchema,
|
|
15371
|
+
PortalAuthSchema: () => PortalAuthSchema,
|
|
15372
|
+
PortalAuthSchemeSchema: () => PortalAuthSchemeSchema,
|
|
15162
15373
|
PortalTypeSchema: () => PortalTypeSchema,
|
|
15163
15374
|
ProfileDefSchema: () => ProfileDefSchema,
|
|
15164
15375
|
ProjectConfigSchema: () => ProjectConfigSchema,
|
|
@@ -15378,6 +15589,7 @@ __export(src_exports, {
|
|
|
15378
15589
|
syncContextFiles: () => syncContextFiles,
|
|
15379
15590
|
toMarkdown: () => toMarkdown,
|
|
15380
15591
|
toOpenApi: () => toOpenApi,
|
|
15592
|
+
toOpenApiSet: () => toOpenApiSet,
|
|
15381
15593
|
updateSpec: () => updateSpec,
|
|
15382
15594
|
validateAsComplete: () => validateAsComplete,
|
|
15383
15595
|
validateProjectConfig: () => validateProjectConfig,
|
|
@@ -15413,7 +15625,7 @@ function defaultTargetConfig(type) {
|
|
|
15413
15625
|
enabled: true
|
|
15414
15626
|
};
|
|
15415
15627
|
}
|
|
15416
|
-
var WAIRON_VERSION = "5.0.2-dev.
|
|
15628
|
+
var WAIRON_VERSION = "5.0.2-dev.9";
|
|
15417
15629
|
var GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
15418
15630
|
var ARCHITECT_AGENT_ID = "agent-architect";
|
|
15419
15631
|
var ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -16729,6 +16941,7 @@ init_yaml();
|
|
|
16729
16941
|
MethodImplementationSchema,
|
|
16730
16942
|
MethodParamSchema,
|
|
16731
16943
|
MethodSignatureSchema,
|
|
16944
|
+
NamedOpenApiSpecSchema,
|
|
16732
16945
|
NamingRuleConfigSchema,
|
|
16733
16946
|
NarrativeDetailSchema,
|
|
16734
16947
|
NarrativeStepSchema,
|
|
@@ -16742,6 +16955,8 @@ init_yaml();
|
|
|
16742
16955
|
PathsConfigSchema,
|
|
16743
16956
|
PatternDefSchema,
|
|
16744
16957
|
PatternRefSchema,
|
|
16958
|
+
PortalAuthSchema,
|
|
16959
|
+
PortalAuthSchemeSchema,
|
|
16745
16960
|
PortalTypeSchema,
|
|
16746
16961
|
ProfileDefSchema,
|
|
16747
16962
|
ProjectConfigSchema,
|
|
@@ -16961,6 +17176,7 @@ init_yaml();
|
|
|
16961
17176
|
syncContextFiles,
|
|
16962
17177
|
toMarkdown,
|
|
16963
17178
|
toOpenApi,
|
|
17179
|
+
toOpenApiSet,
|
|
16964
17180
|
updateSpec,
|
|
16965
17181
|
validateAsComplete,
|
|
16966
17182
|
validateProjectConfig,
|