@wairon/cli 5.0.2-dev.15 → 5.0.2-dev.16

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.15";
68
+ WAIRON_VERSION = "5.0.2-dev.16";
69
69
  GITHUB_REPO = "SYW-Apps/Waffle-AIron";
70
70
  ARCHITECT_AGENT_ID = "agent-architect";
71
71
  ARCHITECT_TEMPLATE_ID = "architect";
@@ -8012,11 +8012,15 @@ function projectSubsystemSurface(subsystemId) {
8012
8012
  const interfaces = loadInterfaceSpecs();
8013
8013
  const types = loadTypeSpecs();
8014
8014
  const entries = [];
8015
+ const unprojectable = [];
8015
8016
  for (const pub of target.publicInterfaces ?? []) {
8016
8017
  if (!pub.component) continue;
8017
8018
  const comp = components.find((c) => c.id === pub.component || c.id === `${subsystemId}::${pub.component}`);
8018
8019
  if (!comp) continue;
8019
- if (comp.componentType !== "Portal") continue;
8020
+ if (!CROSS_BOUNDARY_TARGETS.has(comp.componentType)) {
8021
+ unprojectable.push({ component: pub.component, componentType: comp.componentType });
8022
+ continue;
8023
+ }
8020
8024
  const compInterfaces = interfaces.filter((i) => i.component === comp.id && (!pub.interface || i.id === pub.interface || i.id === `${subsystemId}::${pub.interface}`));
8021
8025
  const methods = compInterfaces.flatMap((i) => i.methods);
8022
8026
  entries.push({
@@ -8030,13 +8034,18 @@ function projectSubsystemSurface(subsystemId) {
8030
8034
  component: localName(comp.id),
8031
8035
  methods,
8032
8036
  ...comp.dispatch && comp.dispatch.length ? { dispatch: comp.dispatch } : {},
8033
- // Project the backing Portal's auth + basePath so the codec can emit
8037
+ // Project the backing component's auth + basePath so the codec can emit
8034
8038
  // OpenAPI security + per-portal servers self-contained from the snapshot.
8035
8039
  ...comp.auth && comp.auth.scheme !== "none" ? { auth: comp.auth } : {},
8036
8040
  ...comp.basePath ? { basePath: comp.basePath } : {},
8037
8041
  details: pub.details ?? ""
8038
8042
  });
8039
8043
  }
8044
+ for (const skipped of unprojectable) {
8045
+ console.error(
8046
+ `[surfaces] skipped "${subsystemId}::${skipped.component}": a published ${skipped.componentType} can never serve a cross-boundary caller, so it stays out of every chained child's sibling surface \u2014 publish this surface through a Portal, a Gateway, or an Observer (for events).`
8047
+ );
8048
+ }
8040
8049
  return SurfaceSnapshotSchema.parse({
8041
8050
  projectName: `${system.name}::${subsystemId}`,
8042
8051
  origin: "generated",
@@ -8215,7 +8224,7 @@ function checkChildSurfaceFreshness(rootDir = getProjectRoot()) {
8215
8224
  }
8216
8225
  return issues;
8217
8226
  }
8218
- var fs9, path10, SURFACES_DIRNAME;
8227
+ var fs9, path10, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
8219
8228
  var init_surfaces = __esm({
8220
8229
  "src/core/surfaces.ts"() {
8221
8230
  "use strict";
@@ -8230,6 +8239,7 @@ var init_surfaces = __esm({
8230
8239
  init_type_analysis();
8231
8240
  init_openapi();
8232
8241
  SURFACES_DIRNAME = "surfaces";
8242
+ CROSS_BOUNDARY_TARGETS = /* @__PURE__ */ new Set(["Portal", "Gateway", "Observer"]);
8233
8243
  }
8234
8244
  });
8235
8245
 
@@ -27490,9 +27500,14 @@ function lockProject(cfg, credential, project2) {
27490
27500
  }
27491
27501
  return executeApprovedLock(cfg, project2);
27492
27502
  }
27493
- function executeApprovedLock(cfg, projectId) {
27503
+ function boundLifecycleRoot(cfg, projectId, subproject) {
27494
27504
  const root = existingProjectRoot(cfg.dataDir, projectId);
27495
27505
  if (!root) throw new Error(`Unknown project "${projectId}".`);
27506
+ if (!subproject) return root;
27507
+ return resolveSubprojectMounts(projectId, root, subproject.split(SUBPROJECT_SEPARATOR));
27508
+ }
27509
+ function executeApprovedLock(cfg, projectId, subproject) {
27510
+ const root = boundLifecycleRoot(cfg, projectId, subproject);
27496
27511
  return runWithProjectRoot(root, () => {
27497
27512
  hostGit.sync();
27498
27513
  const result = validateProjectAsComplete();
@@ -27664,9 +27679,8 @@ function promoteProject(cfg, credential, project2) {
27664
27679
  }
27665
27680
  return executeApprovedPromote(cfg, project2);
27666
27681
  }
27667
- function executeApprovedPromote(cfg, projectId) {
27668
- const root = existingProjectRoot(cfg.dataDir, projectId);
27669
- if (!root) throw new Error(`Unknown project "${projectId}".`);
27682
+ function executeApprovedPromote(cfg, projectId, subproject) {
27683
+ const root = boundLifecycleRoot(cfg, projectId, subproject);
27670
27684
  return runWithProjectRoot(root, () => {
27671
27685
  const lock = hostCore.readLockRecord();
27672
27686
  if (!lock) {
@@ -31551,38 +31565,58 @@ function initializeProject(cfg, credential, request) {
31551
31565
  }
31552
31566
  }
31553
31567
  }
31554
- function lockProject2(cfg, credential, projectId) {
31568
+ function lockProject2(cfg, credential, projectId, subproject) {
31555
31569
  return lifecycleAction(cfg, credential, projectId, {
31556
31570
  action: "project:lock",
31557
31571
  verb: "Lock",
31558
31572
  noun: "lock",
31573
+ subproject,
31559
31574
  execute: () => {
31560
- const lock = executeApprovedLock(cfg, projectId);
31575
+ const lock = executeApprovedLock(cfg, projectId, subproject);
31561
31576
  return {
31562
31577
  status: "completed",
31563
31578
  action: "project:lock",
31564
- summary: `Locked project "${projectId}" (status: ${lock.status}).`,
31579
+ summary: `Locked project "${projectId}"${subprojectSuffix(subproject)} (status: ${lock.status}).`,
31565
31580
  lock
31566
31581
  };
31567
31582
  }
31568
31583
  });
31569
31584
  }
31570
- function promoteProject2(cfg, credential, projectId) {
31585
+ function promoteProject2(cfg, credential, projectId, subproject) {
31571
31586
  return lifecycleAction(cfg, credential, projectId, {
31572
31587
  action: "project:promote",
31573
31588
  verb: "Promote",
31574
31589
  noun: "promotion",
31590
+ subproject,
31575
31591
  execute: () => {
31576
- const promo = executeApprovedPromote(cfg, projectId);
31592
+ const promo = executeApprovedPromote(cfg, projectId, subproject);
31577
31593
  return {
31578
31594
  status: "completed",
31579
31595
  action: "project:promote",
31580
- summary: `Promotion of project "${projectId}": ${promo.status} \u2014 ${promo.message}`,
31596
+ summary: `Promotion of project "${projectId}"${subprojectSuffix(subproject)}: ${promo.status} \u2014 ${promo.message}`,
31581
31597
  promote: promo
31582
31598
  };
31583
31599
  }
31584
31600
  });
31585
31601
  }
31602
+ function subprojectSuffix(subproject) {
31603
+ return subproject ? ` subproject "${subproject}"` : "";
31604
+ }
31605
+ var SUBPROJECT_SCOPE_PAYLOAD = "SubprojectScope";
31606
+ function subprojectScopePayload(subproject) {
31607
+ if (!subproject) return {};
31608
+ return { payloadType: SUBPROJECT_SCOPE_PAYLOAD, payload: JSON.stringify({ subproject }) };
31609
+ }
31610
+ function readSubprojectScope(req) {
31611
+ if (req.payloadType !== SUBPROJECT_SCOPE_PAYLOAD || !req.payload) return void 0;
31612
+ const parsed = JSON.parse(req.payload);
31613
+ if (typeof parsed.subproject !== "string" || !parsed.subproject) {
31614
+ throw new Error(
31615
+ `Approved ${req.kind} request "${req.id}" carries a ${SUBPROJECT_SCOPE_PAYLOAD} payload with no usable subproject qualifier \u2014 refusing to execute, because falling back to the whole project would widen the scope the requester was confined to.`
31616
+ );
31617
+ }
31618
+ return parsed.subproject;
31619
+ }
31586
31620
  function lifecycleAction(cfg, credential, projectId, opts) {
31587
31621
  const principal = requirePrincipal7(cfg, credential);
31588
31622
  const effective = authorize(cfg.dataDir, principal, PROJECT_WRITE_CAPABILITY4, "project", projectId);
@@ -31608,8 +31642,9 @@ function lifecycleAction(cfg, credential, projectId, opts) {
31608
31642
  const pending = buildPendingRequest(
31609
31643
  principal,
31610
31644
  opts.action,
31611
- `${opts.verb} project ${projectId}`,
31612
- projectId
31645
+ `${opts.verb} project ${projectId}${subprojectSuffix(opts.subproject)}`,
31646
+ projectId,
31647
+ subprojectScopePayload(opts.subproject)
31613
31648
  );
31614
31649
  return createPendingOutcome(cfg, principal, pending, opts.action);
31615
31650
  }
@@ -31683,12 +31718,14 @@ function executeApproved(cfg, req) {
31683
31718
  return `Initialized project "${rec.id}" under the active pack policy.`;
31684
31719
  }
31685
31720
  case "project:lock": {
31686
- const lock = executeApprovedLock(cfg, req.projectId ?? "");
31687
- return `Locked project "${req.projectId}" (status: ${lock.status}).`;
31721
+ const scope = readSubprojectScope(req);
31722
+ const lock = executeApprovedLock(cfg, req.projectId ?? "", scope);
31723
+ return `Locked project "${req.projectId}"${subprojectSuffix(scope)} (status: ${lock.status}).`;
31688
31724
  }
31689
31725
  case "project:promote": {
31690
- const promo = executeApprovedPromote(cfg, req.projectId ?? "");
31691
- return `Promotion of project "${req.projectId}": ${promo.status} \u2014 ${promo.message}`;
31726
+ const scope = readSubprojectScope(req);
31727
+ const promo = executeApprovedPromote(cfg, req.projectId ?? "", scope);
31728
+ return `Promotion of project "${req.projectId}"${subprojectSuffix(scope)}: ${promo.status} \u2014 ${promo.message}`;
31692
31729
  }
31693
31730
  default:
31694
31731
  throw new Error(`Unsupported approval kind "${req.kind}".`);
@@ -36421,6 +36458,12 @@ var PROJECT_OPS_TOOLS = /* @__PURE__ */ new Set([
36421
36458
  "sdd_host_produce",
36422
36459
  "sdd_host_commit_project"
36423
36460
  ]);
36461
+ var PROJECT_RECORD_TOOLS = /* @__PURE__ */ new Set([
36462
+ "sdd_host_initialize_project",
36463
+ "sdd_host_get_approval_status",
36464
+ "sdd_host_await_approval",
36465
+ ...PROJECT_OPS_TOOLS
36466
+ ]);
36424
36467
  function jsonRpcRequests(body) {
36425
36468
  const arr = Array.isArray(body) ? body : [body];
36426
36469
  return arr.filter((m) => !!m && typeof m === "object" && "method" in m);
@@ -36486,7 +36529,27 @@ function dataPlanePermissionError(cfg, principal, projectId, body) {
36486
36529
  }
36487
36530
  };
36488
36531
  }
36489
- async function dispatchProjectLifecycleTool(cfg, credential, projectId, body) {
36532
+ function subprojectConfinementError(projectId, subproject, body) {
36533
+ if (!subproject) return void 0;
36534
+ const msg = jsonRpcRequest(body);
36535
+ if (!msg || msg.method !== "tools/call") return void 0;
36536
+ const name = msg.params?.name;
36537
+ if (typeof name !== "string" || !PROJECT_RECORD_TOOLS.has(name)) return void 0;
36538
+ return {
36539
+ jsonrpc: "2.0",
36540
+ id: msg.id ?? null,
36541
+ result: {
36542
+ content: [
36543
+ {
36544
+ type: "text",
36545
+ text: `Refused \u2014 ${name} acts on the whole project "${projectId}", but this credential is bound to subproject "${projectId}${SUBPROJECT_SEPARATOR}${subproject}". An unqualified credential for "${projectId}" is required to call ${name}.`
36546
+ }
36547
+ ],
36548
+ isError: true
36549
+ }
36550
+ };
36551
+ }
36552
+ async function dispatchProjectLifecycleTool(cfg, credential, projectId, body, subproject) {
36490
36553
  const msg = jsonRpcRequest(body);
36491
36554
  if (!msg || msg.method !== "tools/call") return void 0;
36492
36555
  const name = msg.params?.name;
@@ -36503,10 +36566,10 @@ async function dispatchProjectLifecycleTool(cfg, credential, projectId, body) {
36503
36566
  value = initializeProject(cfg, credential, args);
36504
36567
  break;
36505
36568
  case "sdd_host_lock_project":
36506
- value = lockProject2(cfg, credential, projectId);
36569
+ value = lockProject2(cfg, credential, projectId, subproject);
36507
36570
  break;
36508
36571
  case "sdd_host_promote_project":
36509
- value = promoteProject2(cfg, credential, projectId);
36572
+ value = promoteProject2(cfg, credential, projectId, subproject);
36510
36573
  break;
36511
36574
  case "sdd_host_await_approval":
36512
36575
  value = await awaitApproval(
@@ -36613,7 +36676,13 @@ async function handleMcpRequest(cfg, req, res, body, credential) {
36613
36676
  await runWithProjectRoot(binding.rootPath, async () => {
36614
36677
  const projectId = binding.projectId;
36615
36678
  const subproject = binding.subproject;
36616
- const dispatchedResponse = await dispatchProjectLifecycleTool(cfg, cred, projectId, body);
36679
+ const confinementError = subprojectConfinementError(projectId, subproject, body);
36680
+ if (confinementError !== void 0) {
36681
+ sendJson(res, 200, confinementError);
36682
+ auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(confinementError), subproject);
36683
+ return;
36684
+ }
36685
+ const dispatchedResponse = await dispatchProjectLifecycleTool(cfg, cred, projectId, body, subproject);
36617
36686
  if (dispatchedResponse !== void 0) {
36618
36687
  sendJson(res, 200, dispatchedResponse);
36619
36688
  auditToolCall(cfg.dataDir, principal, projectId, body, deriveMcpOutcome(dispatchedResponse), subproject);