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

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.11";
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,7 @@ function audienceDistance(resolution, targetProjectId) {
29176
29211
 
29177
29212
  // src/server/landscape.ts
29178
29213
  var yamlLib = __toESM(require("js-yaml"));
29214
+ init_filenames();
29179
29215
  var PROJECT_ADMIN_CAPABILITY3 = "project:admin";
29180
29216
  var PROJECT_READ_CAPABILITY3 = "project:read";
29181
29217
  var PROJECT_WRITE_CAPABILITY3 = "project:write";
@@ -29662,7 +29698,7 @@ function getProjectSurfaceForMcp(cfg, credential, currentProjectId, targetProjec
29662
29698
  const result = runWithProjectRoot(record2.rootPath, () => hostSurfaces.exportBoundSurface(maxAudience, "native"));
29663
29699
  return { ...result.snapshot, origin: "exchanged" };
29664
29700
  }
29665
- function exportProjectSurface(cfg, credential, projectId, format, maxAudience) {
29701
+ function exportProjectSurface(cfg, credential, projectId, format, maxAudience, portalId) {
29666
29702
  const principal = requirePrincipal6(cfg, credential);
29667
29703
  if (!permitsCap(cfg, principal, PROJECT_ADMIN_CAPABILITY3, "project", projectId)) {
29668
29704
  throw new ForbiddenError(
@@ -29676,16 +29712,41 @@ function exportProjectSurface(cfg, credential, projectId, format, maxAudience) {
29676
29712
  if (!root) throw new Error(`Unknown project "${projectId}".`);
29677
29713
  const result = runWithProjectRoot(root, () => hostSurfaces.exportBoundSurface(maxAudience, format));
29678
29714
  if (format === "openapi") {
29715
+ const specs = result.renderedSet ?? [];
29716
+ if (portalId) {
29717
+ const hit = specs.find((s) => s.portalId === portalId);
29718
+ if (!hit) {
29719
+ throw new Error(
29720
+ `Unknown portal "${portalId}" in project "${projectId}" (published: ${specs.map((s) => s.portalId).join(", ") || "none"}).`
29721
+ );
29722
+ }
29723
+ return {
29724
+ body: hit.document,
29725
+ contentType: "application/json",
29726
+ filename: `${safeFilenamePart(projectId)}-${safeFilenamePart(portalId)}-surface.openapi.json`
29727
+ };
29728
+ }
29729
+ if (specs.length === 1) {
29730
+ return {
29731
+ body: specs[0].document,
29732
+ contentType: "application/json",
29733
+ filename: `${safeFilenamePart(projectId)}-surface.openapi.json`
29734
+ };
29735
+ }
29679
29736
  return {
29680
- body: result.rendered ?? "{}",
29737
+ body: JSON.stringify(
29738
+ { openapiIndex: true, specs: specs.map((s) => ({ portalId: s.portalId, name: s.name })) },
29739
+ null,
29740
+ 2
29741
+ ),
29681
29742
  contentType: "application/json",
29682
- filename: `${projectId}-surface.openapi.json`
29743
+ filename: `${safeFilenamePart(projectId)}-surface.openapi.index.json`
29683
29744
  };
29684
29745
  }
29685
29746
  return {
29686
29747
  body: yamlLib.dump(result.snapshot),
29687
29748
  contentType: "application/yaml",
29688
- filename: `${projectId}-surface.yaml`
29749
+ filename: `${safeFilenamePart(projectId)}-surface.yaml`
29689
29750
  };
29690
29751
  }
29691
29752
  function removeRelation(cfg, credential, id) {
@@ -29729,7 +29790,8 @@ function handleLandscapeRequest(cfg, credential, req, res, body, url) {
29729
29790
  credential,
29730
29791
  parts[2],
29731
29792
  url.searchParams.get("format") ?? "native",
29732
- url.searchParams.get("audience") ?? "instance"
29793
+ url.searchParams.get("audience") ?? "instance",
29794
+ url.searchParams.get("spec") ?? void 0
29733
29795
  );
29734
29796
  res.writeHead(200, {
29735
29797
  "content-type": artifact.contentType,
@@ -31804,6 +31866,26 @@ var ShareSnapshotRegistry = class {
31804
31866
  return stored;
31805
31867
  }
31806
31868
  };
31869
+ function openApiIndexDocument(specs) {
31870
+ return JSON.stringify(
31871
+ { openapiIndex: true, specs: specs.map((s) => ({ portalId: s.portalId, name: s.name })) },
31872
+ null,
31873
+ 2
31874
+ );
31875
+ }
31876
+ function selectCapturedOpenApi(snap, portalId) {
31877
+ const specs = snap.openapiSet;
31878
+ if (!specs || specs.length === 0) {
31879
+ if (portalId) return null;
31880
+ return snap.openapi ?? null;
31881
+ }
31882
+ if (portalId) {
31883
+ const hit = specs.find((s) => s.portalId === portalId);
31884
+ return hit ? hit.document : null;
31885
+ }
31886
+ if (specs.length === 1) return specs[0].document;
31887
+ return openApiIndexDocument(specs);
31888
+ }
31807
31889
  var ShareSnapshotIndex = class {
31808
31890
  constructor(store) {
31809
31891
  this.store = store;
@@ -31811,12 +31893,12 @@ var ShareSnapshotIndex = class {
31811
31893
  get(snapshotId) {
31812
31894
  return this.store.read(snapshotId);
31813
31895
  }
31814
- getArtifact(snapshotId, kind) {
31896
+ getArtifact(snapshotId, kind, portalId) {
31815
31897
  const snap = this.store.read(snapshotId);
31816
31898
  if (!snap) return null;
31817
31899
  if (kind === "canvas") return snap.canvasModel ?? null;
31818
31900
  if (kind === "html") return snap.html ?? null;
31819
- if (kind === "openapi") return snap.openapi ?? null;
31901
+ if (kind === "openapi") return selectCapturedOpenApi(snap, portalId);
31820
31902
  return null;
31821
31903
  }
31822
31904
  };
@@ -31826,8 +31908,8 @@ function putSnapshot(dataDir, snapshot) {
31826
31908
  function getSnapshot2(dataDir, snapshotId) {
31827
31909
  return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).get(snapshotId);
31828
31910
  }
31829
- function getSnapshotArtifact(dataDir, snapshotId, kind) {
31830
- return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).getArtifact(snapshotId, kind);
31911
+ function getSnapshotArtifact(dataDir, snapshotId, kind, portalId) {
31912
+ return new ShareSnapshotIndex(new ShareSnapshotStore(dataDir)).getArtifact(snapshotId, kind, portalId);
31831
31913
  }
31832
31914
  function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
31833
31915
  const root = resolveProjectRoot(dataDir, principal, projectId);
@@ -31846,7 +31928,11 @@ function captureSnapshot(dataDir, principal, projectId, view, artifacts) {
31846
31928
  }
31847
31929
  if (artifacts.includes("openapi")) {
31848
31930
  const result = hostSurfaces.exportBoundSurface("project", "openapi");
31849
- snapshot.openapi = result.rendered ?? "{}";
31931
+ const specs = result.renderedSet ?? [];
31932
+ if (specs.length) {
31933
+ snapshot.openapiSet = specs;
31934
+ if (specs.length === 1) snapshot.openapi = specs[0].document;
31935
+ }
31850
31936
  }
31851
31937
  return snapshot;
31852
31938
  });
@@ -32407,10 +32493,10 @@ function getWebProjectOpenApi(cfg, sessionId, projectId, portalId) {
32407
32493
  if (!root) throw new ForbiddenError("project not authorized or unknown");
32408
32494
  const result = runWithProjectRoot(root, () => hostSurfaces.exportBoundSurface("project", "openapi"));
32409
32495
  const specs = result.renderedSet ?? (result.rendered ? [{ portalId: "", name: projectId, document: result.rendered }] : []);
32410
- if (specs.length === 0) return swaggerUiPage("{}", projectId);
32496
+ if (specs.length === 0) return openApiIndexPage(projectId, []);
32411
32497
  if (portalId) {
32412
- const sel = specs.find((s) => s.portalId === portalId) ?? specs[0];
32413
- return swaggerUiPage(sel.document, sel.name);
32498
+ const sel = specs.find((s) => s.portalId === portalId);
32499
+ return sel ? swaggerUiPage(sel.document, sel.name) : openApiIndexPage(projectId, specs);
32414
32500
  }
32415
32501
  if (specs.length === 1) return swaggerUiPage(specs[0].document, specs[0].name);
32416
32502
  return openApiIndexPage(projectId, specs);
@@ -32418,7 +32504,7 @@ function getWebProjectOpenApi(cfg, sessionId, projectId, portalId) {
32418
32504
  function openApiIndexPage(projectId, specs) {
32419
32505
  const esc2 = (s) => s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
32420
32506
  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>`;
32507
+ 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
32508
  }
32423
32509
  function reshapeLandscapeGraph(model, level) {
32424
32510
  const unitNodeIds = new Set(model.nodes.filter((n) => n.nodeKind === "orgUnit").map((n) => n.id));
@@ -35544,7 +35630,7 @@ var CONTENT_TYPE = {
35544
35630
  openapi: "application/json",
35545
35631
  canvas: "application/json"
35546
35632
  };
35547
- function downloadArtifact(cfg, token, kind, meta) {
35633
+ function downloadArtifact(cfg, token, kind, meta, portalId) {
35548
35634
  const link = linkByTokenHash(cfg.dataDir, hashToken(token));
35549
35635
  const check = usable(link);
35550
35636
  if ("outcome" in check) {
@@ -35556,7 +35642,7 @@ function downloadArtifact(cfg, token, kind, meta) {
35556
35642
  record(cfg.dataDir, check.link.id, meta, "denied-download");
35557
35643
  return { found: false, outcome: "denied-download" };
35558
35644
  }
35559
- const content = getSnapshotArtifact(cfg.dataDir, check.link.snapshotId, kind);
35645
+ const content = getSnapshotArtifact(cfg.dataDir, check.link.snapshotId, kind, portalId);
35560
35646
  if (content === null) {
35561
35647
  record(cfg.dataDir, check.link.id, meta, "not-found");
35562
35648
  return { found: false, outcome: "not-found" };
@@ -35566,6 +35652,35 @@ function downloadArtifact(cfg, token, kind, meta) {
35566
35652
  }
35567
35653
 
35568
35654
  // src/server/sharehttp.ts
35655
+ init_filenames();
35656
+ function specParam(req) {
35657
+ const url = req.url ?? "";
35658
+ const q2 = url.indexOf("?");
35659
+ if (q2 < 0) return void 0;
35660
+ return new URLSearchParams(url.slice(q2 + 1)).get("spec") || void 0;
35661
+ }
35662
+ function asOpenApiIndex(payload) {
35663
+ try {
35664
+ const parsed = JSON.parse(payload);
35665
+ return parsed && parsed.openapiIndex === true && Array.isArray(parsed.specs) ? parsed : null;
35666
+ } catch {
35667
+ return null;
35668
+ }
35669
+ }
35670
+ function escapeHtml2(s) {
35671
+ return s.replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[c]);
35672
+ }
35673
+ function sharedOpenApiIndexPage(index) {
35674
+ const items = index.specs.map(
35675
+ (s) => `<li><a href="?spec=${encodeURIComponent(s.portalId)}">${escapeHtml2(s.name)}</a><code>${escapeHtml2(s.portalId)}</code></li>`
35676
+ ).join("");
35677
+ 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>`;
35678
+ }
35679
+ function downloadFilename(kind, portalId, payload) {
35680
+ if (kind !== "openapi") return `shared-canvas.${safeFilenamePart(kind)}`;
35681
+ if (portalId) return `shared-canvas.${safeFilenamePart(portalId)}.openapi.json`;
35682
+ return asOpenApiIndex(payload) ? "shared-canvas.openapi.index.json" : "shared-canvas.openapi.json";
35683
+ }
35569
35684
  function shareRequestMeta(req) {
35570
35685
  const fwd = req.headers["x-forwarded-for"];
35571
35686
  const ip = (Array.isArray(fwd) ? fwd[0] : fwd)?.split(",")[0].trim() || req.socket?.remoteAddress || "unknown";
@@ -35617,14 +35732,16 @@ function serveSharedModel(cfg, token, req, res) {
35617
35732
  );
35618
35733
  }
35619
35734
  function serveSharedOpenApi(cfg, token, req, res) {
35620
- const result = downloadArtifact(cfg, token, "openapi", shareRequestMeta(req));
35735
+ const result = downloadArtifact(cfg, token, "openapi", shareRequestMeta(req), specParam(req));
35621
35736
  if (!result.found || result.content === void 0) return notFoundPage(res);
35737
+ const index = asOpenApiIndex(result.content);
35622
35738
  harden(res, void 0, "text/html; charset=utf-8");
35623
35739
  res.statusCode = 200;
35624
- res.end(swaggerUiPage(result.content, "Shared API"));
35740
+ res.end(index ? sharedOpenApiIndexPage(index) : swaggerUiPage(result.content, "Shared API"));
35625
35741
  }
35626
35742
  function serveSharedDownload(cfg, token, kind, req, res) {
35627
- const result = downloadArtifact(cfg, token, kind, shareRequestMeta(req));
35743
+ const portalId = specParam(req);
35744
+ const result = downloadArtifact(cfg, token, kind, shareRequestMeta(req), portalId);
35628
35745
  if (!result.found || result.content === void 0) {
35629
35746
  if (result.outcome === "denied-download") {
35630
35747
  harden(res, void 0, "text/plain");
@@ -35634,9 +35751,11 @@ function serveSharedDownload(cfg, token, kind, req, res) {
35634
35751
  }
35635
35752
  return notFoundPage(res);
35636
35753
  }
35637
- const ext = kind === "html" ? "html" : kind === "openapi" ? "openapi.json" : kind;
35638
35754
  harden(res, void 0, result.contentType ?? "application/octet-stream");
35639
- res.setHeader("content-disposition", `attachment; filename="shared-canvas.${ext}"`);
35755
+ res.setHeader(
35756
+ "content-disposition",
35757
+ `attachment; filename="${downloadFilename(kind, portalId, result.content)}"`
35758
+ );
35640
35759
  res.statusCode = 200;
35641
35760
  res.end(result.content);
35642
35761
  }
@@ -37246,15 +37365,29 @@ async function runSurface(action, options = {}) {
37246
37365
  if (format !== "native" && format !== "openapi") {
37247
37366
  throw new WaironError(`Unknown format "${format}" (supported: native, openapi).`);
37248
37367
  }
37249
- const result = exportSurface(audience, format, options.out);
37368
+ if (options.portal && format !== "openapi") {
37369
+ throw new WaironError("`--portal` selects one OpenAPI document and only applies to `--format openapi`.");
37370
+ }
37371
+ const result = exportSurface(audience, format, options.out, options.portal);
37250
37372
  logger.success(
37251
37373
  `Projected surface of "${result.snapshot.projectName}": ${result.snapshot.interfaces.length} interface(s), ${result.snapshot.types.length} type(s) at audience \u2265 ${audience}.`
37252
37374
  );
37253
- if (result.writtenTo) {
37254
- logger.info(`Written to ${result.writtenTo}`);
37375
+ const written = result.writtenPaths ?? (result.writtenTo ? [result.writtenTo] : []);
37376
+ if (written.length === 1) {
37377
+ logger.info(`Written to ${written[0]}`);
37378
+ } else if (written.length > 1) {
37379
+ logger.info(`Written ${written.length} document(s) \u2014 one per portal:`);
37380
+ for (const p of written) logger.info(` ${p}`);
37255
37381
  } else if (result.rendered) {
37256
37382
  process.stdout.write(`${result.rendered}
37257
37383
  `);
37384
+ } else if (result.renderedSet && result.renderedSet.length > 1) {
37385
+ logger.info(
37386
+ `This project publishes ${result.renderedSet.length} portals \u2014 pick one with \`--portal <id>\` (or use --out to write them all):`
37387
+ );
37388
+ for (const spec of result.renderedSet) {
37389
+ logger.info(` ${import_chalk19.default.cyan(spec.portalId)} \u2014 ${spec.name}`);
37390
+ }
37258
37391
  } else {
37259
37392
  for (const entry of result.snapshot.interfaces) {
37260
37393
  logger.info(` ${import_chalk19.default.cyan(entry.id)} (${entry.type}, ${entry.audience}) \u2014 ${entry.methods.length} method(s)`);
@@ -37523,13 +37656,14 @@ mcpCmd.command("status").description("Show whether the wairon MCP server is regi
37523
37656
  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
37657
  await runProduce(target, { page: opts.page, token: opts.token });
37525
37658
  });
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) => {
37659
+ 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
37660
  await runSurface(action, {
37528
37661
  audience: opts.audience,
37529
37662
  format: opts.format,
37530
37663
  out: opts.out,
37531
37664
  source: opts.source,
37532
- origin: opts.origin
37665
+ origin: opts.origin,
37666
+ portal: opts.portal
37533
37667
  });
37534
37668
  });
37535
37669
  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) => {