@wairon/cli 5.0.2-dev.10 → 5.0.2-dev.12

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 CHANGED
@@ -65,7 +65,7 @@ var init_defaults = __esm({
65
65
  copilot: ".github/prompts",
66
66
  codex: ".codex/agents"
67
67
  };
68
- WAIRON_VERSION = "5.0.2-dev.10";
68
+ WAIRON_VERSION = "5.0.2-dev.12";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  ARCHITECT_AGENT_ID = "agent-architect";
71
71
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -6859,6 +6859,16 @@ var init_type_references = __esm({
6859
6859
  }
6860
6860
  });
6861
6861
 
6862
+ // src/utils/filenames.ts
6863
+ function safeFilenamePart(value) {
6864
+ return value.replace(/[^a-zA-Z0-9._-]/g, "-");
6865
+ }
6866
+ var init_filenames = __esm({
6867
+ "src/utils/filenames.ts"() {
6868
+ "use strict";
6869
+ }
6870
+ });
6871
+
6862
6872
  // src/core/statehash.ts
6863
6873
  function computeStateId() {
6864
6874
  const tree = {
@@ -7379,29 +7389,53 @@ function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
7379
7389
  function loadSurfaceSnapshots() {
7380
7390
  return listSnapshots();
7381
7391
  }
7382
- function renderOpenApiForms(snapshot) {
7383
- const renderedSet = toOpenApiSet(snapshot);
7384
- return { renderedSet, ...renderedSet.length === 1 ? { rendered: renderedSet[0].document } : {} };
7392
+ function selectPortalSpec(renderedSet, portalId) {
7393
+ const hit = renderedSet.find((spec) => spec.portalId === portalId);
7394
+ if (!hit) {
7395
+ const known = renderedSet.map((s) => s.portalId).join(", ");
7396
+ throw new Error(`Unknown portal "${portalId}" \u2014 this surface renders: ${known || "(no portals)"}.`);
7397
+ }
7398
+ return [hit];
7385
7399
  }
7386
- function writeSurfaceFile(outPath, body, snapshot) {
7400
+ function perPortalPath(resolvedOut, portalId) {
7401
+ const ext = path10.extname(resolvedOut);
7402
+ const stem2 = ext ? resolvedOut.slice(0, -ext.length) : resolvedOut;
7403
+ return `${stem2}.${safeFilenamePart(portalId)}${ext}`;
7404
+ }
7405
+ function writeSurfaceFile(outPath, snapshot, renderedSet) {
7387
7406
  const resolved = path10.resolve(outPath);
7388
7407
  fs9.mkdirSync(path10.dirname(resolved), { recursive: true });
7389
- if (body !== void 0) fs9.writeFileSync(resolved, body);
7390
- else writeYamlFile(resolved, snapshot);
7391
- return resolved;
7408
+ if (!renderedSet || renderedSet.length === 0) {
7409
+ writeYamlFile(resolved, snapshot);
7410
+ return [resolved];
7411
+ }
7412
+ if (renderedSet.length === 1) {
7413
+ fs9.writeFileSync(resolved, renderedSet[0].document);
7414
+ return [resolved];
7415
+ }
7416
+ return renderedSet.map((spec) => {
7417
+ const target = perPortalPath(resolved, spec.portalId);
7418
+ fs9.writeFileSync(target, spec.document);
7419
+ return target;
7420
+ });
7392
7421
  }
7393
- function exportSurface(maxAudience, format, outPath) {
7394
- const snapshot = projectOwnSurface(maxAudience);
7395
- const openapi = format === "openapi" ? renderOpenApiForms(snapshot) : void 0;
7396
- const body = openapi?.rendered ?? openapi?.renderedSet[0]?.document;
7397
- const writtenTo = outPath ? writeSurfaceFile(outPath, body, snapshot) : void 0;
7422
+ function exportResult(snapshot, renderedSet, writtenPaths) {
7423
+ const rendered = renderedSet?.length === 1 ? renderedSet[0].document : void 0;
7398
7424
  return {
7399
7425
  snapshot,
7400
- ...openapi?.rendered !== void 0 ? { rendered: openapi.rendered } : {},
7401
- ...openapi ? { renderedSet: openapi.renderedSet } : {},
7402
- ...writtenTo ? { writtenTo } : {}
7426
+ ...rendered !== void 0 ? { rendered } : {},
7427
+ ...renderedSet ? { renderedSet } : {},
7428
+ ...writtenPaths.length === 1 ? { writtenTo: writtenPaths[0] } : {},
7429
+ ...writtenPaths.length ? { writtenPaths } : {}
7403
7430
  };
7404
7431
  }
7432
+ function exportSurface(maxAudience, format, outPath, portalId) {
7433
+ const snapshot = projectOwnSurface(maxAudience);
7434
+ let renderedSet = format === "openapi" ? toOpenApiSet(snapshot) : void 0;
7435
+ if (renderedSet && portalId) renderedSet = selectPortalSpec(renderedSet, portalId);
7436
+ const writtenPaths = outPath ? writeSurfaceFile(outPath, snapshot, renderedSet) : [];
7437
+ return exportResult(snapshot, renderedSet, writtenPaths);
7438
+ }
7405
7439
  function importSurface(sourcePath, origin) {
7406
7440
  const resolved = path10.resolve(sourcePath);
7407
7441
  if (!fs9.existsSync(resolved)) {
@@ -7466,6 +7500,7 @@ var init_surfaces = __esm({
7466
7500
  path10 = __toESM(require("path"));
7467
7501
  init_fs();
7468
7502
  init_yaml();
7503
+ init_filenames();
7469
7504
  init_models();
7470
7505
  init_specs2();
7471
7506
  init_statehash();
@@ -29176,6 +29211,17 @@ function audienceDistance(resolution, targetProjectId) {
29176
29211
 
29177
29212
  // src/server/landscape.ts
29178
29213
  var yamlLib = __toESM(require("js-yaml"));
29214
+ init_filenames();
29215
+
29216
+ // src/server/openapiindex.ts
29217
+ function buildOpenApiSpecIndex(specs) {
29218
+ return { openapiIndex: true, specs: specs.map((s) => ({ portalId: s.portalId, name: s.name })) };
29219
+ }
29220
+ function openApiIndexDocument(specs) {
29221
+ return JSON.stringify(buildOpenApiSpecIndex(specs), null, 2);
29222
+ }
29223
+
29224
+ // src/server/landscape.ts
29179
29225
  var PROJECT_ADMIN_CAPABILITY3 = "project:admin";
29180
29226
  var PROJECT_READ_CAPABILITY3 = "project:read";
29181
29227
  var PROJECT_WRITE_CAPABILITY3 = "project:write";
@@ -29662,7 +29708,7 @@ function getProjectSurfaceForMcp(cfg, credential, currentProjectId, targetProjec
29662
29708
  const result = runWithProjectRoot(record2.rootPath, () => hostSurfaces.exportBoundSurface(maxAudience, "native"));
29663
29709
  return { ...result.snapshot, origin: "exchanged" };
29664
29710
  }
29665
- function exportProjectSurface(cfg, credential, projectId, format, maxAudience) {
29711
+ function exportProjectSurface(cfg, credential, projectId, format, maxAudience, portalId) {
29666
29712
  const principal = requirePrincipal6(cfg, credential);
29667
29713
  if (!permitsCap(cfg, principal, PROJECT_ADMIN_CAPABILITY3, "project", projectId)) {
29668
29714
  throw new ForbiddenError(
@@ -29676,16 +29722,37 @@ function exportProjectSurface(cfg, credential, projectId, format, maxAudience) {
29676
29722
  if (!root) throw new Error(`Unknown project "${projectId}".`);
29677
29723
  const result = runWithProjectRoot(root, () => hostSurfaces.exportBoundSurface(maxAudience, format));
29678
29724
  if (format === "openapi") {
29725
+ const specs = result.renderedSet ?? [];
29726
+ if (portalId) {
29727
+ const hit = specs.find((s) => s.portalId === portalId);
29728
+ if (!hit) {
29729
+ throw new Error(
29730
+ `Unknown portal "${portalId}" in project "${projectId}" (published: ${specs.map((s) => s.portalId).join(", ") || "none"}).`
29731
+ );
29732
+ }
29733
+ return {
29734
+ body: hit.document,
29735
+ contentType: "application/json",
29736
+ filename: `${safeFilenamePart(projectId)}-${safeFilenamePart(portalId)}-surface.openapi.json`
29737
+ };
29738
+ }
29739
+ if (specs.length === 1) {
29740
+ return {
29741
+ body: specs[0].document,
29742
+ contentType: "application/json",
29743
+ filename: `${safeFilenamePart(projectId)}-surface.openapi.json`
29744
+ };
29745
+ }
29679
29746
  return {
29680
- body: result.rendered ?? "{}",
29747
+ body: openApiIndexDocument(specs),
29681
29748
  contentType: "application/json",
29682
- filename: `${projectId}-surface.openapi.json`
29749
+ filename: `${safeFilenamePart(projectId)}-surface.openapi.index.json`
29683
29750
  };
29684
29751
  }
29685
29752
  return {
29686
29753
  body: yamlLib.dump(result.snapshot),
29687
29754
  contentType: "application/yaml",
29688
- filename: `${projectId}-surface.yaml`
29755
+ filename: `${safeFilenamePart(projectId)}-surface.yaml`
29689
29756
  };
29690
29757
  }
29691
29758
  function removeRelation(cfg, credential, id) {
@@ -29729,7 +29796,8 @@ function handleLandscapeRequest(cfg, credential, req, res, body, url) {
29729
29796
  credential,
29730
29797
  parts[2],
29731
29798
  url.searchParams.get("format") ?? "native",
29732
- url.searchParams.get("audience") ?? "instance"
29799
+ url.searchParams.get("audience") ?? "instance",
29800
+ url.searchParams.get("spec") ?? void 0
29733
29801
  );
29734
29802
  res.writeHead(200, {
29735
29803
  "content-type": artifact.contentType,
@@ -31804,6 +31872,19 @@ var ShareSnapshotRegistry = class {
31804
31872
  return stored;
31805
31873
  }
31806
31874
  };
31875
+ function selectCapturedOpenApi(snap, portalId) {
31876
+ const specs = snap.openapiSet;
31877
+ if (!specs || specs.length === 0) {
31878
+ if (portalId) return null;
31879
+ return snap.openapi ?? null;
31880
+ }
31881
+ if (portalId) {
31882
+ const hit = specs.find((s) => s.portalId === portalId);
31883
+ return hit ? hit.document : null;
31884
+ }
31885
+ if (specs.length === 1) return specs[0].document;
31886
+ return openApiIndexDocument(specs);
31887
+ }
31807
31888
  var ShareSnapshotIndex = class {
31808
31889
  constructor(store) {
31809
31890
  this.store = store;
@@ -31811,12 +31892,12 @@ var ShareSnapshotIndex = class {
31811
31892
  get(snapshotId) {
31812
31893
  return this.store.read(snapshotId);
31813
31894
  }
31814
- getArtifact(snapshotId, kind) {
31895
+ getArtifact(snapshotId, kind, portalId) {
31815
31896
  const snap = this.store.read(snapshotId);
31816
31897
  if (!snap) return null;
31817
31898
  if (kind === "canvas") return snap.canvasModel ?? null;
31818
31899
  if (kind === "html") return snap.html ?? null;
31819
- if (kind === "openapi") return snap.openapi ?? null;
31900
+ if (kind === "openapi") return selectCapturedOpenApi(snap, portalId);
31820
31901
  return null;
31821
31902
  }
31822
31903
  };
@@ -31826,8 +31907,8 @@ function putSnapshot(dataDir, snapshot) {
31826
31907
  function getSnapshot2(dataDir, snapshotId) {
31827
31908
  return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).get(snapshotId);
31828
31909
  }
31829
- function getSnapshotArtifact(dataDir, snapshotId, kind) {
31830
- return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).getArtifact(snapshotId, kind);
31910
+ function getSnapshotArtifact(dataDir, snapshotId, kind, portalId) {
31911
+ return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).getArtifact(snapshotId, kind, portalId);
31831
31912
  }
31832
31913
  function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
31833
31914
  const root = resolveProjectRoot(dataDir, principal, projectId);
@@ -31846,7 +31927,11 @@ function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
31846
31927
  }
31847
31928
  if (artifacts.includes("openapi")) {
31848
31929
  const result = hostSurfaces.exportBoundSurface("project", "openapi");
31849
- snapshot.openapi = result.rendered ?? "{}";
31930
+ const specs = result.renderedSet ?? [];
31931
+ if (specs.length) {
31932
+ snapshot.openapiSet = specs;
31933
+ if (specs.length === 1) snapshot.openapi = specs[0].document;
31934
+ }
31850
31935
  }
31851
31936
  return snapshot;
31852
31937
  });
@@ -32407,10 +32492,10 @@ function getWebProjectOpenApi(cfg, sessionId, projectId, portalId) {
32407
32492
  if (!root) throw new ForbiddenError("project not authorized or unknown");
32408
32493
  const result = runWithProjectRoot(root, () => hostSurfaces.exportBoundSurface("project", "openapi"));
32409
32494
  const specs = result.renderedSet ?? (result.rendered ? [{ portalId: "", name: projectId, document: result.rendered }] : []);
32410
- if (specs.length === 0) return swaggerUiPage("{}", projectId);
32495
+ if (specs.length === 0) return openApiIndexPage(projectId, []);
32411
32496
  if (portalId) {
32412
- const sel = specs.find((s) => s.portalId === portalId) ?? specs[0];
32413
- return swaggerUiPage(sel.document, sel.name);
32497
+ const sel = specs.find((s) => s.portalId === portalId);
32498
+ return sel ? swaggerUiPage(sel.document, sel.name) : openApiIndexPage(projectId, specs);
32414
32499
  }
32415
32500
  if (specs.length === 1) return swaggerUiPage(specs[0].document, specs[0].name);
32416
32501
  return openApiIndexPage(projectId, specs);
@@ -32418,7 +32503,7 @@ function getWebProjectOpenApi(cfg, sessionId, projectId, portalId) {
32418
32503
  function openApiIndexPage(projectId, specs) {
32419
32504
  const esc2 = (s) => s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
32420
32505
  const items = specs.map((s) => `<li><a href="/web/openapi?projectId=${encodeURIComponent(projectId)}&spec=${encodeURIComponent(s.portalId)}">${esc2(s.name)}</a> <code>${esc2(s.portalId)}</code></li>`).join("");
32421
- return `<!doctype html><html><head><meta charset="utf-8"><title>${esc2(projectId)} \u2014 API specs</title><style>body{font-family:system-ui,sans-serif;max-width:680px;margin:48px auto;padding:0 20px;color:#e6e6e6;background:#161616}h1{font-size:20px}a{color:#6ea8fe;text-decoration:none}a:hover{text-decoration:underline}li{margin:10px 0}code{color:#8a94a6;font-size:12px;margin-left:8px}</style></head><body><h1>${esc2(projectId)} \u2014 API specs</h1><p>This project exposes ${specs.length} separate public APIs, each with its own OpenAPI document and auth:</p><ul>${items}</ul></body></html>`;
32506
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${esc2(projectId)} \u2014 API specs</title><style>body{font-family:system-ui,sans-serif;max-width:680px;margin:48px auto;padding:0 20px;color:#e6e6e6;background:#161616}h1{font-size:20px}a{color:#6ea8fe;text-decoration:none}a:hover{text-decoration:underline}li{margin:10px 0}code{color:#8a94a6;font-size:12px;margin-left:8px}</style></head><body><h1>${esc2(projectId)} \u2014 API specs</h1>` + (specs.length ? `<p>This project exposes ${specs.length} separate public APIs, each with its own OpenAPI document and auth:</p><ul>${items}</ul>` : `<p>This project publishes no HTTP API \u2014 no public portal exposes one.</p>`) + `</body></html>`;
32422
32507
  }
32423
32508
  function reshapeLandscapeGraph(model, level) {
32424
32509
  const unitNodeIds = new Set(model.nodes.filter((n) => n.nodeKind === "orgUnit").map((n) => n.id));
@@ -35544,7 +35629,7 @@ var CONTENT_TYPE = {
35544
35629
  openapi: "application/json",
35545
35630
  canvas: "application/json"
35546
35631
  };
35547
- function downloadArtifact(cfg, token, kind, meta) {
35632
+ function downloadArtifact(cfg, token, kind, meta, portalId) {
35548
35633
  const link = linkByTokenHash(cfg.dataDir, hashToken(token));
35549
35634
  const check = usable(link);
35550
35635
  if ("outcome" in check) {
@@ -35556,7 +35641,7 @@ function downloadArtifact(cfg, token, kind, meta) {
35556
35641
  record(cfg.dataDir, check.link.id, meta, "denied-download");
35557
35642
  return { found: false, outcome: "denied-download" };
35558
35643
  }
35559
- const content = getSnapshotArtifact(cfg.dataDir, check.link.snapshotId, kind);
35644
+ const content = getSnapshotArtifact(cfg.dataDir, check.link.snapshotId, kind, portalId);
35560
35645
  if (content === null) {
35561
35646
  record(cfg.dataDir, check.link.id, meta, "not-found");
35562
35647
  return { found: false, outcome: "not-found" };
@@ -35566,6 +35651,35 @@ function downloadArtifact(cfg, token, kind, meta) {
35566
35651
  }
35567
35652
 
35568
35653
  // src/server/sharehttp.ts
35654
+ init_filenames();
35655
+ function specParam(req) {
35656
+ const url = req.url ?? "";
35657
+ const q2 = url.indexOf("?");
35658
+ if (q2 < 0) return void 0;
35659
+ return new URLSearchParams(url.slice(q2 + 1)).get("spec") || void 0;
35660
+ }
35661
+ function asOpenApiIndex(payload) {
35662
+ try {
35663
+ const parsed = JSON.parse(payload);
35664
+ return parsed && parsed.openapiIndex === true && Array.isArray(parsed.specs) ? parsed : null;
35665
+ } catch {
35666
+ return null;
35667
+ }
35668
+ }
35669
+ function escapeHtml2(s) {
35670
+ return s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
35671
+ }
35672
+ function sharedOpenApiIndexPage(index) {
35673
+ const items = index.specs.map(
35674
+ (s) => `<li><a href="?spec=${encodeURIComponent(s.portalId)}">${escapeHtml2(s.name)}</a><code>${escapeHtml2(s.portalId)}</code></li>`
35675
+ ).join("");
35676
+ return `<!doctype html><html><head><meta charset="utf-8"><title>Shared APIs</title><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{font:15px/1.6 system-ui,sans-serif;max-width:680px;margin:48px auto;padding:0 20px;background:#0b1120;color:#e8e8f0}h1{font-size:20px}a{color:#6ea8fe;text-decoration:none}a:hover{text-decoration:underline}li{margin:10px 0}code{color:#9a9aab;font-size:12px;margin-left:8px}p{color:#9a9aab}</style></head><body><h1>Shared APIs</h1><p>This share exposes ${index.specs.length} separate APIs, each with its own OpenAPI document and auth:</p><ul>${items}</ul></body></html>`;
35677
+ }
35678
+ function downloadFilename(kind, portalId, payload) {
35679
+ if (kind !== "openapi") return `shared-canvas.${safeFilenamePart(kind)}`;
35680
+ if (portalId) return `shared-canvas.${safeFilenamePart(portalId)}.openapi.json`;
35681
+ return asOpenApiIndex(payload) ? "shared-canvas.openapi.index.json" : "shared-canvas.openapi.json";
35682
+ }
35569
35683
  function shareRequestMeta(req) {
35570
35684
  const fwd = req.headers["x-forwarded-for"];
35571
35685
  const ip = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(",")[0].trim() || req.socket?.remoteAddress || "unknown";
@@ -35617,14 +35731,16 @@ function serveSharedModel(cfg, token, req, res) {
35617
35731
  );
35618
35732
  }
35619
35733
  function serveSharedOpenApi(cfg, token, req, res) {
35620
- const result = downloadArtifact(cfg, token, "openapi", shareRequestMeta(req));
35734
+ const result = downloadArtifact(cfg, token, "openapi", shareRequestMeta(req), specParam(req));
35621
35735
  if (!result.found || result.content === void 0) return notFoundPage(res);
35736
+ const index = asOpenApiIndex(result.content);
35622
35737
  harden(res, void 0, "text/html; charset=utf-8");
35623
35738
  res.statusCode = 200;
35624
- res.end(swaggerUiPage(result.content, "Shared API"));
35739
+ res.end(index ? sharedOpenApiIndexPage(index) : swaggerUiPage(result.content, "Shared API"));
35625
35740
  }
35626
35741
  function serveSharedDownload(cfg, token, kind, req, res) {
35627
- const result = downloadArtifact(cfg, token, kind, shareRequestMeta(req));
35742
+ const portalId = specParam(req);
35743
+ const result = downloadArtifact(cfg, token, kind, shareRequestMeta(req), portalId);
35628
35744
  if (!result.found || result.content === void 0) {
35629
35745
  if (result.outcome === "denied-download") {
35630
35746
  harden(res, void 0, "text/plain");
@@ -35634,9 +35750,11 @@ function serveSharedDownload(cfg, token, kind, req, res) {
35634
35750
  }
35635
35751
  return notFoundPage(res);
35636
35752
  }
35637
- const ext = kind === "html" ? "html" : kind === "openapi" ? "openapi.json" : kind;
35638
35753
  harden(res, void 0, result.contentType ?? "application/octet-stream");
35639
- res.setHeader("content-disposition", `attachment; filename="shared-canvas.${ext}"`);
35754
+ res.setHeader(
35755
+ "content-disposition",
35756
+ `attachment; filename="${downloadFilename(kind, portalId, result.content)}"`
35757
+ );
35640
35758
  res.statusCode = 200;
35641
35759
  res.end(result.content);
35642
35760
  }
@@ -37246,15 +37364,29 @@ async function runSurface(action, options = {}) {
37246
37364
  if (format !== "native" && format !== "openapi") {
37247
37365
  throw new WaironError(`Unknown format "${format}" (supported: native, openapi).`);
37248
37366
  }
37249
- const result = exportSurface(audience, format, options.out);
37367
+ if (options.portal && format !== "openapi") {
37368
+ throw new WaironError("`--portal` selects one OpenAPI document and only applies to `--format openapi`.");
37369
+ }
37370
+ const result = exportSurface(audience, format, options.out, options.portal);
37250
37371
  logger.success(
37251
37372
  `Projected surface of "${result.snapshot.projectName}": ${result.snapshot.interfaces.length} interface(s), ${result.snapshot.types.length} type(s) at audience \u2265 ${audience}.`
37252
37373
  );
37253
- if (result.writtenTo) {
37254
- logger.info(`Written to ${result.writtenTo}`);
37374
+ const written = result.writtenPaths ?? (result.writtenTo ? [result.writtenTo] : []);
37375
+ if (written.length === 1) {
37376
+ logger.info(`Written to ${written[0]}`);
37377
+ } else if (written.length > 1) {
37378
+ logger.info(`Written ${written.length} document(s) \u2014 one per portal:`);
37379
+ for (const p of written) logger.info(` ${p}`);
37255
37380
  } else if (result.rendered) {
37256
37381
  process.stdout.write(`${result.rendered}
37257
37382
  `);
37383
+ } else if (result.renderedSet && result.renderedSet.length > 1) {
37384
+ logger.info(
37385
+ `This project publishes ${result.renderedSet.length} portals \u2014 pick one with \`--portal <id>\` (or use --out to write them all):`
37386
+ );
37387
+ for (const spec of result.renderedSet) {
37388
+ logger.info(` ${import_chalk19.default.cyan(spec.portalId)} \u2014 ${spec.name}`);
37389
+ }
37258
37390
  } else {
37259
37391
  for (const entry of result.snapshot.interfaces) {
37260
37392
  logger.info(` ${import_chalk19.default.cyan(entry.id)} (${entry.type}, ${entry.audience}) \u2014 ${entry.methods.length} method(s)`);
@@ -37523,13 +37655,14 @@ mcpCmd.command("status").description("Show whether the wairon MCP server is regi
37523
37655
  program.command("produce <target>").description("project the local project's specs to a producer target (notion | miro)").option("--page <id>", "parent page/board id in the target").option("--token <token>", "integration token (else env, else interactive prompt)").action(async (target, opts) => {
37524
37656
  await runProduce(target, { page: opts.page, token: opts.token });
37525
37657
  });
37526
- program.command("surface <action>").description("public surface exchange: export | import | list | generate-children").option("--audience <level>", "export ceiling: project | department | instance | partner | external (default instance)").option("--format <fmt>", "export format: native | openapi (default native)").option("--out <path>", "export output path (else print)").option("--source <path>", "import: the surface document (native snapshot YAML or OpenAPI)").option("--origin <origin>", "import provenance: exchanged | authored (default authored)").action(async (action, opts) => {
37658
+ program.command("surface <action>").description("public surface exchange: export | import | list | generate-children").option("--audience <level>", "export ceiling: project | department | instance | partner | external (default instance)").option("--format <fmt>", "export format: native | openapi (default native)").option("--out <path>", "export output path (else print)").option("--portal <id>", "export: select one portal's OpenAPI spec (a multi-portal project renders one document per portal)").option("--source <path>", "import: the surface document (native snapshot YAML or OpenAPI)").option("--origin <origin>", "import provenance: exchanged | authored (default authored)").action(async (action, opts) => {
37527
37659
  await runSurface(action, {
37528
37660
  audience: opts.audience,
37529
37661
  format: opts.format,
37530
37662
  out: opts.out,
37531
37663
  source: opts.source,
37532
- origin: opts.origin
37664
+ origin: opts.origin,
37665
+ portal: opts.portal
37533
37666
  });
37534
37667
  });
37535
37668
  program.command("serve").description("Run the wairon hosting server: HTTP MCP for many isolated projects + admin API").option("--host <host>", "data-plane bind host (default 0.0.0.0)").option("--port <port>", "data-plane port (default 8080)").option("--admin-host <host>", "admin-plane bind host (default 127.0.0.1)").option("--admin-port <port>", "admin-plane port (default 8081)").option("--data-dir <path>", "data root holding projects/ and auth/ (default WAIRON_DATA_DIR or ~/.wairon/data)").option("--no-auth", "disable data-plane auth (trusted networks only)").action(async (opts) => {