@m8t-stack/cli 0.2.103 → 0.2.106

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.js CHANGED
@@ -1454,7 +1454,7 @@ function installFoundryDnsShim() {
1454
1454
  }
1455
1455
 
1456
1456
  // src/lib/package-version.ts
1457
- var CLI_VERSION = "0.2.103";
1457
+ var CLI_VERSION = "0.2.106";
1458
1458
 
1459
1459
  // src/lib/render-error.ts
1460
1460
  init_errors();
@@ -19479,7 +19479,7 @@ var SIZE_PRESETS = {
19479
19479
  };
19480
19480
  var DEFAULT_REGISTRY = "ghcr.io/m8t-labs";
19481
19481
  var DEFAULT_IMAGE = "m8t-coding-agent";
19482
- var DEFAULT_TAG = "v0.1.3";
19482
+ var DEFAULT_TAG = "v0.1.4";
19483
19483
  var DEFAULT_MODEL = "gpt-4.1-mini";
19484
19484
  var NAME_RE = /^[a-z0-9-]+$/;
19485
19485
  var CoderDeployCommand = class extends M8tCommand {
@@ -20286,6 +20286,59 @@ async function makeSharedKeyClient(opts) {
20286
20286
  if (!key2) return null;
20287
20287
  return new TableClient(opts.tableEndpoint, TABLE, new AzureNamedKeyCredential(opts.accountName, key2));
20288
20288
  }
20289
+ async function makeSharedKeyTransactionClient(opts) {
20290
+ return await makeSharedKeyClient(opts);
20291
+ }
20292
+ async function openMetadataTransactionClient(opts) {
20293
+ const discover = opts.discoverImpl ?? discoverStampStorage;
20294
+ const makeAad = opts.aadClientImpl ?? ((endpoint, credential2) => new TableClient(endpoint, TABLE, credential2));
20295
+ const makeShared = opts.sharedClientImpl ?? makeSharedKeyTransactionClient;
20296
+ const nap = opts.sleepImpl ?? sleep2;
20297
+ const storage = await discover({ credential: opts.credential, subscriptionId: opts.subscriptionId, resourceGroup: opts.resourceGroup });
20298
+ const probe = async (client) => {
20299
+ try {
20300
+ await client.getEntity("system", "platform");
20301
+ return "ok";
20302
+ } catch (e) {
20303
+ if (is404(e)) return "ok";
20304
+ if (is403(e)) return "forbidden";
20305
+ throw e;
20306
+ }
20307
+ };
20308
+ const aad = makeAad(storage.tableEndpoint, opts.credential);
20309
+ if (await probe(aad) === "ok") return aad;
20310
+ opts.onProgress?.(`opening the ${opts.label} with the account key \u2014 your identity lacks the Storage Table data role.`);
20311
+ const shared = await makeShared(storage);
20312
+ if (shared) {
20313
+ if (await probe(shared) === "ok") return shared;
20314
+ }
20315
+ opts.onProgress?.("shared-key access is disabled \u2014 granting Storage Table Data Contributor to your identity (one-time)\u2026");
20316
+ try {
20317
+ const oid = await getCallerObjectId();
20318
+ await grantStorageTableDataContributor({
20319
+ credential: opts.credential,
20320
+ subscriptionId: opts.subscriptionId,
20321
+ scope: storage.accountResourceId,
20322
+ principalId: oid
20323
+ });
20324
+ } catch (e) {
20325
+ throw new LocalCliError({
20326
+ code: opts.errorCode,
20327
+ message: `Cannot open the ${opts.label}: AAD lacks the Storage Table data role, shared-key access is disabled, and self-granting the role failed.`,
20328
+ hint: "Grant yourself 'Storage Table Data Contributor' on the storage account and retry.",
20329
+ cause: e
20330
+ });
20331
+ }
20332
+ for (let i = 0; i < 6; i += 1) {
20333
+ await nap(1e4);
20334
+ if (await probe(aad) === "ok") return aad;
20335
+ }
20336
+ throw new LocalCliError({
20337
+ code: opts.errorCode,
20338
+ message: `Cannot open the ${opts.label}: the granted Storage Table data role has not propagated yet.`,
20339
+ hint: opts.retryHint
20340
+ });
20341
+ }
20289
20342
  async function upsertMetadataEntity(opts) {
20290
20343
  const discover = opts.discoverImpl ?? discoverStampStorage;
20291
20344
  const makeAad = opts.aadClientImpl ?? ((endpoint, credential2) => new TableClient(endpoint, TABLE, credential2));
@@ -21164,6 +21217,9 @@ function buildBicepParams(p) {
21164
21217
  ];
21165
21218
  if (p.gatewayImageRef) params.push(`gatewayImageRef=${p.gatewayImageRef}`);
21166
21219
  if (p.foundryTracingMode) params.push(`foundryTracingMode=${p.foundryTracingMode}`);
21220
+ if (p.deployFoundryTracingConnection !== void 0) {
21221
+ params.push(`deployFoundryTracingConnection=${String(p.deployFoundryTracingConnection)}`);
21222
+ }
21167
21223
  if (p.assignSubscriptionRoles !== void 0) params.push(`assignSubscriptionRoles=${String(p.assignSubscriptionRoles)}`);
21168
21224
  if (p.provisionUpdater !== void 0) params.push(`provisionUpdater=${String(p.provisionUpdater)}`);
21169
21225
  if (p.installerImage !== void 0) params.push(`installerImage=${p.installerImage}`);
@@ -21418,6 +21474,414 @@ function voiceSecretParam(read, gatewayName) {
21418
21474
  };
21419
21475
  }
21420
21476
 
21477
+ // src/lib/gateway-adoption.ts
21478
+ init_errors();
21479
+
21480
+ // src/lib/apply-request-store.ts
21481
+ import { TableClient as TableClient3 } from "@azure/data-tables";
21482
+ async function openApplyTable(opts) {
21483
+ const { tableEndpoint } = await discoverStampStorage(opts);
21484
+ return new TableClient3(tableEndpoint, "Metadata", opts.credential);
21485
+ }
21486
+ function hasStatus(e, n) {
21487
+ return e.statusCode === n;
21488
+ }
21489
+ async function readApplyRequest(client) {
21490
+ try {
21491
+ const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
21492
+ return { ...entityToApplyRequest(row), etag: row.etag };
21493
+ } catch (e) {
21494
+ if (hasStatus(e, 404)) return null;
21495
+ throw e;
21496
+ }
21497
+ }
21498
+ async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
21499
+ const cur = await readApplyRequest(client);
21500
+ if (!cur) return null;
21501
+ const leaseExpired = isInFlight(cur.status) && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
21502
+ const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
21503
+ if (!claimable) return null;
21504
+ const nowMs = Date.parse(nowIso);
21505
+ const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
21506
+ if (leaseExpired && cur.attempt >= MAX_LEASE_TAKEOVERS) {
21507
+ const held = {
21508
+ ...cur,
21509
+ status: "held",
21510
+ breaker: "held",
21511
+ result: {
21512
+ appliedVersion: cur.result?.appliedVersion ?? null,
21513
+ error: `the converge was abandoned mid-apply ${(cur.attempt + 1).toString()} times (last owner ${cur.claimedBy ?? "(unknown)"}, phase "${cur.phase ?? "(none)"}"); the updater stopped retrying to avoid re-rolling infra behind a converge that cannot finish`
21514
+ },
21515
+ updatedAt: nowIso
21516
+ };
21517
+ try {
21518
+ await client.updateEntity({ ...applyRequestToEntity(held), etag: cur.etag }, "Merge", { etag: cur.etag });
21519
+ } catch (e) {
21520
+ if (!hasStatus(e, 412)) throw e;
21521
+ }
21522
+ return null;
21523
+ }
21524
+ const claimed = {
21525
+ ...cur,
21526
+ status: "claimed",
21527
+ claimedBy: executionId,
21528
+ leaseUntil,
21529
+ ...leaseExpired ? { attempt: cur.attempt + 1 } : {},
21530
+ updatedAt: nowIso
21531
+ };
21532
+ try {
21533
+ await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
21534
+ return claimed;
21535
+ } catch (e) {
21536
+ if (hasStatus(e, 412)) return null;
21537
+ throw e;
21538
+ }
21539
+ }
21540
+ async function patchApplyRequest(client, mutate) {
21541
+ const cur = await readApplyRequest(client);
21542
+ if (!cur) return;
21543
+ const next = mutate(cur);
21544
+ await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
21545
+ }
21546
+
21547
+ // src/lib/gateway-adoption.ts
21548
+ function lowerId(v) {
21549
+ return v.replace(/\/$/, "").toLowerCase();
21550
+ }
21551
+ function recordOfStrings(v) {
21552
+ if (!v || typeof v !== "object" || Array.isArray(v)) return {};
21553
+ const out = {};
21554
+ for (const [k, value] of Object.entries(v)) if (typeof value === "string") out[k] = value;
21555
+ return out;
21556
+ }
21557
+ function parseLiveGatewayDeployment(raw) {
21558
+ const w = raw ?? {};
21559
+ const containersRaw = w.properties?.template?.containers;
21560
+ const containers = Array.isArray(containersRaw) ? containersRaw : [];
21561
+ const gateway = containers.find((c) => c.name === "gateway");
21562
+ const uas = w.identity?.userAssignedIdentities;
21563
+ const userAssignedIdentityIds = uas && typeof uas === "object" && !Array.isArray(uas) ? Object.keys(uas) : [];
21564
+ const registriesRaw = w.properties?.configuration?.registries;
21565
+ const regs = Array.isArray(registriesRaw) ? registriesRaw : [];
21566
+ return {
21567
+ name: typeof w.name === "string" ? w.name : "",
21568
+ tags: recordOfStrings(w.tags),
21569
+ image: typeof gateway?.image === "string" ? gateway.image : "",
21570
+ userAssignedIdentityIds,
21571
+ registries: regs.map((r) => {
21572
+ const x = r ?? {};
21573
+ return {
21574
+ server: typeof x.server === "string" ? x.server : "",
21575
+ identity: typeof x.identity === "string" ? x.identity : "",
21576
+ username: typeof x.username === "string" ? x.username : "",
21577
+ passwordSecretRef: typeof x.passwordSecretRef === "string" ? x.passwordSecretRef : ""
21578
+ };
21579
+ }),
21580
+ provisioningState: typeof w.properties?.provisioningState === "string" ? w.properties.provisioningState : "",
21581
+ latestRevisionName: typeof w.properties?.latestRevisionName === "string" ? w.properties.latestRevisionName : "",
21582
+ latestReadyRevisionName: typeof w.properties?.latestReadyRevisionName === "string" ? w.properties.latestReadyRevisionName : ""
21583
+ };
21584
+ }
21585
+ function legacyGatewayTopologyError(live, gatewayResourceId, expectedSubscriptionId) {
21586
+ const { subscriptionId, resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
21587
+ if (expectedSubscriptionId && subscriptionId.toLowerCase() !== expectedSubscriptionId.toLowerCase()) return "gateway subscription does not match";
21588
+ const prefix = "m8t-gateway-";
21589
+ if (!name.startsWith(prefix) || name.length === prefix.length || live.name !== name) return "gateway name is not the generated m8t name";
21590
+ const suffix = name.slice(prefix.length);
21591
+ if (live.tags.managedBy !== "m8t" || live.tags.m8t !== "gateway") return "gateway ownership tags do not match";
21592
+ const parsed = parseImageRef2(live.image);
21593
+ const parts = parsed.repo.split("/");
21594
+ const host = parts.shift() ?? "";
21595
+ const repo = parts.join("/");
21596
+ if (!host.endsWith(".azurecr.io") || repo !== "m8t-web" || !parsed.tag || !/^v\d{8}-[0-9a-f]{7,40}$/.test(parsed.tag)) {
21597
+ return "image is not the legacy m8t ACR image shape";
21598
+ }
21599
+ const expectedIdentity = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/m8t-acrpull-${suffix}`;
21600
+ if (live.userAssignedIdentityIds.length !== 1 || lowerId(live.userAssignedIdentityIds[0]) !== lowerId(expectedIdentity)) {
21601
+ return "gateway does not have exactly the generated ACR pull identity";
21602
+ }
21603
+ if (live.registries.length !== 1) return "gateway does not have exactly one registry credential";
21604
+ const registry = live.registries[0];
21605
+ if (registry.server.toLowerCase() !== host.toLowerCase() || lowerId(registry.identity) !== lowerId(live.userAssignedIdentityIds[0]) || registry.username !== "" || registry.passwordSecretRef !== "") {
21606
+ return "registry credential does not match the generated identity and image host";
21607
+ }
21608
+ return null;
21609
+ }
21610
+ async function readLiveGatewayDeployment(gatewayResourceId) {
21611
+ const { subscriptionId, resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
21612
+ let raw;
21613
+ try {
21614
+ raw = await runAz(["containerapp", "show", "--subscription", subscriptionId, "-g", resourceGroup, "-n", name, "-o", "json"]);
21615
+ } catch (e) {
21616
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_UNREADABLE", message: `Could not read gateway '${name}'.`, cause: e });
21617
+ }
21618
+ try {
21619
+ return parseLiveGatewayDeployment(JSON.parse(raw));
21620
+ } catch (e) {
21621
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_UNREADABLE", message: `Gateway '${name}' returned invalid JSON.`, cause: e });
21622
+ }
21623
+ }
21624
+ async function resolveLegacyAcrDigest(live, subscriptionId) {
21625
+ const parsed = parseImageRef2(live.image);
21626
+ const [host, ...repoParts] = parsed.repo.split("/");
21627
+ const acrName = host.split(".")[0];
21628
+ const repo = repoParts.join("/");
21629
+ if (!acrName || !repo || !parsed.tag) {
21630
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The live gateway image is not a tagged ACR image." });
21631
+ }
21632
+ const command = ["acr", "repository", "show"];
21633
+ if (subscriptionId) command.push("--subscription", subscriptionId);
21634
+ command.push("-n", acrName, "--image", `${repo}:${parsed.tag}`, "--query", "digest", "-o", "tsv");
21635
+ const digest = (await runAz(command)).trim();
21636
+ if (!/^sha256:[0-9a-f]{64}$/.test(digest)) {
21637
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The live ACR image did not resolve to a sha256 digest." });
21638
+ }
21639
+ return digest;
21640
+ }
21641
+ function adoptedAcrTopologyError(live, adoption) {
21642
+ const source = parseImageRef2(adoption.expectedSourceRef);
21643
+ const current = parseImageRef2(live.image);
21644
+ const sourceParts = source.repo.split("/");
21645
+ const currentParts = current.repo.split("/");
21646
+ if (sourceParts[0]?.toLowerCase() !== currentParts[0]?.toLowerCase() || currentParts.slice(1).join("/") !== "m8t-web") {
21647
+ return "live image left the explicitly adopted ACR repository";
21648
+ }
21649
+ if (live.userAssignedIdentityIds.length !== 1 || lowerId(live.userAssignedIdentityIds[0]) !== lowerId(adoption.expectedAcrPullIdentityResourceId)) {
21650
+ return "live pull identity differs from the adopted identity";
21651
+ }
21652
+ if (live.registries.length !== 1) return "live registry credential count differs from the adopted topology";
21653
+ const reg = live.registries[0];
21654
+ if (reg.server.toLowerCase() !== sourceParts[0]?.toLowerCase() || lowerId(reg.identity) !== lowerId(adoption.expectedAcrPullIdentityResourceId)) {
21655
+ return "live registry credential differs from the adopted topology";
21656
+ }
21657
+ return null;
21658
+ }
21659
+ async function readAcrDigest(subscriptionId, acrName, image) {
21660
+ try {
21661
+ const value = (await runAz(["acr", "repository", "show", "--subscription", subscriptionId, "-n", acrName, "--image", image, "--query", "digest", "-o", "tsv"])).trim();
21662
+ return /^sha256:[0-9a-f]{64}$/.test(value) ? value : null;
21663
+ } catch {
21664
+ return null;
21665
+ }
21666
+ }
21667
+ async function rollAdoptedGateway(args) {
21668
+ if (!/^sha256:[0-9a-f]{64}$/.test(args.targetDigest)) {
21669
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_DIGEST_INVALID", message: "The manifest gateway digest is not a lowercase sha256 digest." });
21670
+ }
21671
+ const { subscriptionId, resourceGroup, name } = parseContainerAppResourceId(args.gatewayResourceId);
21672
+ let live = await readLiveGatewayDeployment(args.gatewayResourceId);
21673
+ const topologyError = adoptedAcrTopologyError(live, args.adoption);
21674
+ if (topologyError) throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_DRIFT", message: `Adopted gateway held: ${topologyError}.` });
21675
+ const acrName = args.adoption.expectedAcrResourceId.split("/").filter(Boolean).at(-1) ?? "";
21676
+ const host = parseImageRef2(args.adoption.expectedSourceRef).repo.split("/")[0];
21677
+ if (!acrName || !host) throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The adoption record lacks a usable ACR identity." });
21678
+ if (args.adoption.status === "pending") {
21679
+ if (args.targetDigest !== args.adoption.expectedForwardDigest && args.targetDigest !== args.adoption.expectedRollbackDigest && args.targetDigest !== args.adoption.expectedSourceDigest) {
21680
+ throw new LocalCliError({
21681
+ code: "PLATFORM_GATEWAY_ADOPTION_TARGET_CHANGED",
21682
+ message: "The pending adoption may move only to its recorded forward digest or back to its recorded source digest."
21683
+ });
21684
+ }
21685
+ const current = parseImageRef2(live.image);
21686
+ const currentDigest = current.digest ?? (current.tag ? await readAcrDigest(subscriptionId, acrName, `m8t-web:${current.tag}`) : null);
21687
+ const atSource = current.repo === `${host}/m8t-web` && currentDigest === args.adoption.expectedSourceDigest;
21688
+ const atAuthorizedForward = current.repo === `${host}/m8t-web` && currentDigest === args.adoption.expectedForwardDigest;
21689
+ const atTarget = current.repo === `${host}/m8t-web` && currentDigest === args.targetDigest;
21690
+ if (!atSource && !atAuthorizedForward && !atTarget) {
21691
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: "The adopted gateway changed after authorization; refusing to overwrite it." });
21692
+ }
21693
+ }
21694
+ const targetImage = `m8t-web:${args.targetTag}`;
21695
+ const existing = await readAcrDigest(subscriptionId, acrName, targetImage);
21696
+ if (existing && existing !== args.targetDigest) {
21697
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_TAG_CONFLICT", message: `ACR tag ${targetImage} already exists with a different digest; refusing to overwrite it.` });
21698
+ }
21699
+ if (!existing) {
21700
+ await runAz([
21701
+ "acr",
21702
+ "import",
21703
+ "--subscription",
21704
+ subscriptionId,
21705
+ "-n",
21706
+ acrName,
21707
+ "--source",
21708
+ `${DEFAULT_IMAGE_REPO}@${args.targetDigest}`,
21709
+ "--image",
21710
+ targetImage
21711
+ ]);
21712
+ }
21713
+ const imported = await readAcrDigest(subscriptionId, acrName, targetImage);
21714
+ if (imported !== args.targetDigest) {
21715
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_IMPORT_MISMATCH", message: "The mirrored ACR tag does not match the official manifest digest." });
21716
+ }
21717
+ const attempts = args.pollAttempts ?? 12;
21718
+ const delayMs = args.pollDelayMs ?? 5e3;
21719
+ const waitReady = async (digest) => {
21720
+ for (let n = 0; n < attempts; n += 1) {
21721
+ live = await readLiveGatewayDeployment(args.gatewayResourceId);
21722
+ const actual = parseImageRef2(live.image);
21723
+ if (actual.repo === `${host}/m8t-web` && actual.digest === digest && live.provisioningState === "Succeeded" && live.latestRevisionName !== "" && live.latestRevisionName === live.latestReadyRevisionName) return true;
21724
+ if (n + 1 < attempts) await new Promise((resolve6) => setTimeout(resolve6, delayMs));
21725
+ }
21726
+ return false;
21727
+ };
21728
+ const updateTo = async (digest) => {
21729
+ const desired = `${host}/m8t-web@${digest}`;
21730
+ const actual = parseImageRef2(live.image);
21731
+ if (actual.repo !== `${host}/m8t-web` || actual.digest !== digest) {
21732
+ await runAz(["containerapp", "update", "--subscription", subscriptionId, "-g", resourceGroup, "-n", name, "--image", desired]);
21733
+ }
21734
+ };
21735
+ try {
21736
+ await updateTo(args.targetDigest);
21737
+ if (!await waitReady(args.targetDigest)) {
21738
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_NOT_READY", message: `Gateway '${name}' did not become ready on the manifest digest.` });
21739
+ }
21740
+ } catch (e) {
21741
+ if (args.adoption.status === "pending") {
21742
+ try {
21743
+ live = await readLiveGatewayDeployment(args.gatewayResourceId);
21744
+ await updateTo(args.adoption.expectedSourceDigest);
21745
+ if (!await waitReady(args.adoption.expectedSourceDigest)) throw new Error("source revision did not become ready");
21746
+ } catch (restoreError) {
21747
+ throw new LocalCliError({
21748
+ code: "PLATFORM_GATEWAY_ADOPTION_RESTORE_FAILED",
21749
+ message: `Gateway adoption failed and restoring the authorized source also failed: ${restoreError.message}`,
21750
+ cause: e
21751
+ });
21752
+ }
21753
+ }
21754
+ throw e;
21755
+ }
21756
+ args.onProgress?.(`gateway ready at ${args.targetTag} (${args.targetDigest}).`);
21757
+ return { ...args.adoption, status: "complete", completedAt: (/* @__PURE__ */ new Date()).toISOString() };
21758
+ }
21759
+ function isSamePendingAdoption(existing, liveImage, digest, acrResourceId, gatewayState, forwardTag, forwardDigest, rollbackTag, rollbackDigest) {
21760
+ if (!existing) return false;
21761
+ return existing.expectedSourceRef === liveImage && existing.expectedSourceDigest === digest && existing.expectedAcrResourceId.toLowerCase() === acrResourceId.toLowerCase() && existing.expectedForwardTag === forwardTag && existing.expectedForwardDigest === forwardDigest && existing.expectedRollbackTag === rollbackTag && existing.expectedRollbackDigest === rollbackDigest && gatewayState === "managed" && existing.status === "pending";
21762
+ }
21763
+ async function adoptLegacyGateway(args) {
21764
+ if (!/^sha256:[0-9a-f]{64}$/.test(args.expectedDigest)) {
21765
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-digest must be a lowercase sha256 digest." });
21766
+ }
21767
+ const parsedResource = parseContainerAppResourceId(args.gatewayResourceId);
21768
+ if (parsedResource.subscriptionId.toLowerCase() !== args.subscriptionId.toLowerCase() || parsedResource.resourceGroup.toLowerCase() !== args.resourceGroup.toLowerCase()) {
21769
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_SCOPE", message: "The discovered gateway does not belong to the requested subscription and resource group." });
21770
+ }
21771
+ if (!/^sha256:[0-9a-f]{64}$/.test(args.targetGatewayDigest) || args.targetGatewayTag.trim() === "" || !/^sha256:[0-9a-f]{64}$/.test(args.rollbackGatewayDigest) || args.rollbackGatewayTag.trim() === "") {
21772
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "The selected target lacks a valid gateway tag and digest." });
21773
+ }
21774
+ const client = await openMetadataTransactionClient({
21775
+ credential: args.credential,
21776
+ subscriptionId: args.subscriptionId,
21777
+ resourceGroup: args.resourceGroup,
21778
+ label: "gateway adoption transaction",
21779
+ errorCode: "PLATFORM_GATEWAY_ADOPTION_NO_ACCESS",
21780
+ retryHint: "The role assignment persists \u2014 retry the gateway adoption command in a few minutes.",
21781
+ onProgress: args.onProgress
21782
+ });
21783
+ const stampRow = await client.getEntity("system", "platform").catch((e) => {
21784
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_NO_STAMP", message: "The installed platform stamp is absent or unreadable; refusing to record gateway ownership.", cause: e });
21785
+ });
21786
+ const stamp = entityToStamp(stampRow);
21787
+ const stampEtag = stampRow.etag;
21788
+ if (!stamp || typeof stampEtag !== "string" || !stampEtag) {
21789
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_NO_STAMP", message: "The installed platform stamp is malformed or lacks an ETag." });
21790
+ }
21791
+ const existingRequest = await readApplyRequest(client);
21792
+ const live = await readLiveGatewayDeployment(args.gatewayResourceId);
21793
+ const topologyError = legacyGatewayTopologyError(live, args.gatewayResourceId, args.subscriptionId);
21794
+ if (topologyError) {
21795
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_REFUSED", message: `Gateway adoption refused: ${topologyError}.` });
21796
+ }
21797
+ if (live.image !== args.expectedImage) {
21798
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway image changed; expected '${args.expectedImage}', found '${live.image}'.` });
21799
+ }
21800
+ const digest = await resolveLegacyAcrDigest(live, args.subscriptionId);
21801
+ if (digest !== args.expectedDigest) {
21802
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway digest changed; expected '${args.expectedDigest}', found '${digest}'.` });
21803
+ }
21804
+ const expectedIdentity = live.userAssignedIdentityIds[0];
21805
+ if (args.expectedAcrResourceId.trim() === "" || typeof expectedIdentity !== "string") {
21806
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id and the generated pull identity are required." });
21807
+ }
21808
+ const imageHost = parseImageRef2(live.image).repo.split("/")[0].split(".")[0];
21809
+ const acrName = args.expectedAcrResourceId.split("/").filter(Boolean).at(-1) ?? "";
21810
+ const expectedAcrId = `/subscriptions/${args.subscriptionId}/resourceGroups/${args.resourceGroup}/providers/Microsoft.ContainerRegistry/registries/${imageHost}`;
21811
+ if (imageHost.toLowerCase() !== acrName.toLowerCase() || lowerId(args.expectedAcrResourceId) !== lowerId(expectedAcrId)) {
21812
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id does not match the live image registry." });
21813
+ }
21814
+ const existing = stamp.components.gateway.adoption;
21815
+ const samePending = isSamePendingAdoption(
21816
+ existing,
21817
+ live.image,
21818
+ digest,
21819
+ args.expectedAcrResourceId,
21820
+ stamp.components.gateway.state,
21821
+ args.targetGatewayTag,
21822
+ args.targetGatewayDigest,
21823
+ args.rollbackGatewayTag,
21824
+ args.rollbackGatewayDigest
21825
+ );
21826
+ if (existing && !samePending) {
21827
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CONFLICT", message: "The gateway already carries a different adoption record." });
21828
+ }
21829
+ if (existingRequest && blocksNewIntent(existingRequest.status)) {
21830
+ if (samePending && existingRequest.status === "pending" && existingRequest.target === args.target) {
21831
+ return { outcome: "already-adopted", image: live.image, digest };
21832
+ }
21833
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_IN_PROGRESS", message: `An update request for ${existingRequest.target} is already ${existingRequest.status}.` });
21834
+ }
21835
+ const now = args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString();
21836
+ const nextStamp = {
21837
+ ...stamp,
21838
+ updatedAt: now,
21839
+ components: {
21840
+ ...stamp.components,
21841
+ gateway: {
21842
+ ...stamp.components.gateway,
21843
+ tag: parseImageRef2(live.image).tag ?? stamp.components.gateway.tag,
21844
+ digest,
21845
+ state: "managed",
21846
+ adoption: existing ?? {
21847
+ version: 1,
21848
+ status: "pending",
21849
+ expectedSourceRef: live.image,
21850
+ expectedSourceDigest: digest,
21851
+ expectedForwardDigest: args.targetGatewayDigest,
21852
+ expectedForwardTag: args.targetGatewayTag,
21853
+ expectedRollbackDigest: args.rollbackGatewayDigest,
21854
+ expectedRollbackTag: args.rollbackGatewayTag,
21855
+ expectedAcrResourceId: args.expectedAcrResourceId,
21856
+ expectedAcrPullIdentityResourceId: expectedIdentity,
21857
+ authorizedAt: now
21858
+ }
21859
+ }
21860
+ }
21861
+ };
21862
+ const intent = newIntent(args.target, { source: "policy", identity: args.operatorIdentity, at: now }, now);
21863
+ const stampWire = stampToEntity(nextStamp);
21864
+ const stampEntity = {
21865
+ ...stampWire,
21866
+ partitionKey: String(stampWire.partitionKey),
21867
+ rowKey: String(stampWire.rowKey)
21868
+ };
21869
+ const applyEntity = applyRequestToEntity(intent);
21870
+ const stampAction = ["update", stampEntity, "Replace", { etag: stampEtag }];
21871
+ const requestAction = existingRequest ? ["update", applyEntity, "Replace", { etag: existingRequest.etag }] : ["create", applyEntity];
21872
+ try {
21873
+ await client.submitTransaction([stampAction, requestAction]);
21874
+ } catch (e) {
21875
+ const status = e.statusCode;
21876
+ if (status === 409 || status === 412) {
21877
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_RACE", message: "Gateway adoption lost a concurrent metadata update; no adoption was recorded.", hint: "Re-read the live image and digest, then retry the command.", cause: e });
21878
+ }
21879
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_WRITE_FAILED", message: "Could not atomically record gateway adoption and its update request.", cause: e });
21880
+ }
21881
+ args.onProgress?.(`recorded digest-bound gateway adoption and queued update to ${args.target}.`);
21882
+ return { outcome: samePending ? "already-adopted" : "adopted", image: live.image, digest };
21883
+ }
21884
+
21421
21885
  // src/lib/platform-converge.ts
21422
21886
  var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
21423
21887
  function seedSetRev(manifest) {
@@ -21481,8 +21945,10 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
21481
21945
  continue;
21482
21946
  }
21483
21947
  const from = cur?.tag ?? null;
21484
- if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag });
21485
- else if (from !== img.tag) actions.push({ component, reason: "changed", from, to: img.tag });
21948
+ const adoption = component === "gateway" ? cur?.adoption : void 0;
21949
+ const withAdoption = adoption ? { gatewayAdoption: adoption } : {};
21950
+ if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag, ...withAdoption });
21951
+ else if (from !== img.tag || adoption?.status === "pending") actions.push({ component, reason: "changed", from, to: img.tag, ...withAdoption });
21486
21952
  else skipped.push({ component, reason: "up-to-date" });
21487
21953
  }
21488
21954
  return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
@@ -21625,10 +22091,20 @@ function seedStamp(prior, manifest, plan) {
21625
22091
  };
21626
22092
  }
21627
22093
  async function applyGatewayImage(a, ctx, gatewayResourceId) {
22094
+ const digest = ctx.manifest.components.gateway.digest;
22095
+ if (a.gatewayAdoption) {
22096
+ const adoption = await rollAdoptedGateway({
22097
+ gatewayResourceId,
22098
+ adoption: a.gatewayAdoption,
22099
+ targetTag: a.to,
22100
+ targetDigest: assertGatewayDigest(ctx.manifest.components.gateway),
22101
+ onProgress: ctx.onProgress
22102
+ });
22103
+ return { tag: a.to, digest, state: "managed", adoption };
22104
+ }
21628
22105
  const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
21629
22106
  const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
21630
22107
  const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
21631
- const digest = ctx.manifest.components.gateway.digest;
21632
22108
  const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to, toDigest: digest });
21633
22109
  switch (plan.kind) {
21634
22110
  case "refuse-byoc":
@@ -21711,6 +22187,7 @@ async function resolveBicepParamsForConverge(ctx, opts = {}) {
21711
22187
  acrPullIdentityResourceId,
21712
22188
  acrResourceId,
21713
22189
  foundryTracingMode: opts.foundryTracingMode,
22190
+ ...opts.deployFoundryTracingConnection !== void 0 ? { deployFoundryTracingConnection: opts.deployFoundryTracingConnection } : {},
21714
22191
  ...opts.assignSubscriptionRoles !== void 0 ? { assignSubscriptionRoles: opts.assignSubscriptionRoles } : {},
21715
22192
  ...opts.voiceInternalSecret ? { voiceInternalSecret: opts.voiceInternalSecret } : {},
21716
22193
  ...opts.channelUrl ? { channelUrl: opts.channelUrl } : {}
@@ -22126,10 +22603,10 @@ async function resolveExecutorAgentName(args) {
22126
22603
  }
22127
22604
 
22128
22605
  // src/lib/platform-infra-params.ts
22129
- import { TableClient as TableClient3 } from "@azure/data-tables";
22606
+ import { TableClient as TableClient4 } from "@azure/data-tables";
22130
22607
  async function openInfraParamsTable(opts) {
22131
22608
  const { tableEndpoint } = await discoverStampStorage(opts);
22132
- return new TableClient3(tableEndpoint, "Metadata", opts.credential);
22609
+ return new TableClient4(tableEndpoint, "Metadata", opts.credential);
22133
22610
  }
22134
22611
  async function readInfraParams(client) {
22135
22612
  try {
@@ -22645,7 +23122,7 @@ async function buildConvergeDeps(args) {
22645
23122
  hint: "Run `m8t platform enable-auto-update` against this deployment (it captures the full bicep parameter set), then let the next tick retry."
22646
23123
  });
22647
23124
  }
22648
- args.onProgress?.("subscription-scoped modules skipped: updater is RG-scoped; sub-level changes require a founder-run CLI converge.");
23125
+ args.onProgress?.("cross-scope modules skipped: updater is RG-scoped; subscription roles and the existing Foundry tracing connection are preserved.");
22649
23126
  const gatewayName = args.suffix ? `m8t-gateway-${args.suffix}` : void 0;
22650
23127
  const secret = gatewayName ? voiceSecretParam(
22651
23128
  await readLiveVoiceInternalSecret({
@@ -22660,6 +23137,7 @@ async function buildConvergeDeps(args) {
22660
23137
  const opts = {
22661
23138
  suffix: args.suffix,
22662
23139
  assignSubscriptionRoles: false,
23140
+ deployFoundryTracingConnection: false,
22663
23141
  // Unconditional: the guard above has already refused an absent row. The
22664
23142
  // ternary that used to be here read as "these are optional" — which is
22665
23143
  // how the empty-endpoint death got written in the first place.
@@ -23001,73 +23479,6 @@ function resolveHeadlessContextFromEnv(env) {
23001
23479
  };
23002
23480
  }
23003
23481
 
23004
- // src/lib/apply-request-store.ts
23005
- import { TableClient as TableClient4 } from "@azure/data-tables";
23006
- async function openApplyTable(opts) {
23007
- const { tableEndpoint } = await discoverStampStorage(opts);
23008
- return new TableClient4(tableEndpoint, "Metadata", opts.credential);
23009
- }
23010
- function hasStatus(e, n) {
23011
- return e.statusCode === n;
23012
- }
23013
- async function readApplyRequest(client) {
23014
- try {
23015
- const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
23016
- return { ...entityToApplyRequest(row), etag: row.etag };
23017
- } catch (e) {
23018
- if (hasStatus(e, 404)) return null;
23019
- throw e;
23020
- }
23021
- }
23022
- async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
23023
- const cur = await readApplyRequest(client);
23024
- if (!cur) return null;
23025
- const leaseExpired = isInFlight(cur.status) && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
23026
- const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
23027
- if (!claimable) return null;
23028
- const nowMs = Date.parse(nowIso);
23029
- const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
23030
- if (leaseExpired && cur.attempt >= MAX_LEASE_TAKEOVERS) {
23031
- const held = {
23032
- ...cur,
23033
- status: "held",
23034
- breaker: "held",
23035
- result: {
23036
- appliedVersion: cur.result?.appliedVersion ?? null,
23037
- error: `the converge was abandoned mid-apply ${(cur.attempt + 1).toString()} times (last owner ${cur.claimedBy ?? "(unknown)"}, phase "${cur.phase ?? "(none)"}"); the updater stopped retrying to avoid re-rolling infra behind a converge that cannot finish`
23038
- },
23039
- updatedAt: nowIso
23040
- };
23041
- try {
23042
- await client.updateEntity({ ...applyRequestToEntity(held), etag: cur.etag }, "Merge", { etag: cur.etag });
23043
- } catch (e) {
23044
- if (!hasStatus(e, 412)) throw e;
23045
- }
23046
- return null;
23047
- }
23048
- const claimed = {
23049
- ...cur,
23050
- status: "claimed",
23051
- claimedBy: executionId,
23052
- leaseUntil,
23053
- ...leaseExpired ? { attempt: cur.attempt + 1 } : {},
23054
- updatedAt: nowIso
23055
- };
23056
- try {
23057
- await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
23058
- return claimed;
23059
- } catch (e) {
23060
- if (hasStatus(e, 412)) return null;
23061
- throw e;
23062
- }
23063
- }
23064
- async function patchApplyRequest(client, mutate) {
23065
- const cur = await readApplyRequest(client);
23066
- if (!cur) return;
23067
- const next = mutate(cur);
23068
- await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
23069
- }
23070
-
23071
23482
  // src/lib/rail-preconditions.ts
23072
23483
  init_errors();
23073
23484
  function assertBakedContentPresent(repoRoot) {
@@ -23228,7 +23639,7 @@ async function runGate(args, applied, budgetMs) {
23228
23639
  }
23229
23640
  async function rollbackOrHold(args, notify, error) {
23230
23641
  const target = args.plan.targetVersion;
23231
- const rollbackTarget = args.stamp.previousPlatformVersion;
23642
+ const rollbackTarget = args.preApplyVersion;
23232
23643
  const current = await readBreaker(args.client);
23233
23644
  const next = nextBreaker(current);
23234
23645
  if (next === "held") {
@@ -23492,6 +23903,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
23492
23903
  plan,
23493
23904
  ctx: applyCtx,
23494
23905
  stamp: stamp ?? seedStampFor(plan),
23906
+ preApplyVersion: stamp?.platformVersion ?? null,
23495
23907
  manifest,
23496
23908
  credential: ctx.credential,
23497
23909
  endpoint: ctx.endpoint,
@@ -23717,8 +24129,8 @@ var PlatformRequestUpdateCommand = class extends M8tCommand {
23717
24129
  description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
23718
24130
  details: "Writes the update request the updater job claims on its next tick. This is the ONLY privilege the requester holds \u2014 it does not deploy anything itself; the install's own updater fetches the release, applies it, health-gates it and rolls back on failure. With --wait, follows the request to a terminal state and exits non-zero if it did not succeed.",
23719
24131
  examples: [
23720
- ["Request an update", "m8t platform request-update --version 0.7.2 --resource-group rg-m8t --subscription <id>"],
23721
- ["Request it and wait", "m8t platform request-update --version 0.7.2 --wait --resource-group rg-m8t --subscription <id>"]
24132
+ ["Request an update", "m8t platform request-update --version 0.7.4 --resource-group rg-m8t --subscription <id>"],
24133
+ ["Request it and wait", "m8t platform request-update --version 0.7.4 --wait --resource-group rg-m8t --subscription <id>"]
23722
24134
  ]
23723
24135
  });
23724
24136
  version = Option35.String("--version", { description: "The platform version to converge to." });
@@ -24016,9 +24428,87 @@ var PlatformSeedStampCommand = class extends M8tCommand {
24016
24428
  }
24017
24429
  };
24018
24430
 
24019
- // src/commands/platform/policy.ts
24431
+ // src/commands/platform/gateway-adopt.ts
24020
24432
  import { Command as Command40, Option as Option37 } from "clipanion";
24021
- import { DefaultAzureCredential as DefaultAzureCredential21, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
24433
+ import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
24434
+ init_errors();
24435
+ var PlatformGatewayAdoptCommand = class extends M8tCommand {
24436
+ static paths = [["platform", "gateway", "adopt"]];
24437
+ static usage = Command40.Usage({
24438
+ description: "Explicitly authorize one legacy m8t ACR gateway for managed, digest-pinned updates.",
24439
+ details: "Fail-closed ownership transition. Reads the live gateway and ACR digest, requires the operator to repeat both exactly, verifies the generated legacy topology, and atomically writes the adoption marker with a pending platform update. Unadopted BYOC remains external."
24440
+ });
24441
+ subscription = Option37.String("--subscription");
24442
+ resourceGroup = Option37.String("--resource-group");
24443
+ target = Option37.String("--target", { description: "Published platform target whose installer contains adoption support." });
24444
+ expectedImage = Option37.String("--expected-image", { description: "Exact live legacy ACR image ref." });
24445
+ expectedDigest = Option37.String("--expected-digest", { description: "Exact sha256 digest resolved from that ACR image." });
24446
+ expectedAcrResourceId = Option37.String("--expected-acr-resource-id", { description: "Exact ARM id of the legacy ACR." });
24447
+ output = Option37.String("--output");
24448
+ async executeCommand() {
24449
+ const need = (v, flag) => {
24450
+ if (typeof v !== "string" || v.trim() === "") throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_ARG_MISSING", message: `${flag} is required.` });
24451
+ return v.trim();
24452
+ };
24453
+ const mode = resolveOutputMode(this.output, this.context.stdout);
24454
+ const account = await getAzAccount();
24455
+ const subscriptionId = need(this.subscription ?? account.subscriptionId, "--subscription");
24456
+ const gw = await resolveGatewayContext({ subscriptionId, resourceGroup: this.resourceGroup, interactive: mode !== "json" });
24457
+ const parsed = parseContainerAppResourceId(gw.containerAppResourceId);
24458
+ const resourceGroup = need(this.resourceGroup ?? parsed.resourceGroup, "--resource-group");
24459
+ const target = need(this.target, "--target");
24460
+ const manifest = await fetchManifest({ channel: true, version: target });
24461
+ const assertTarget = (requested, actual) => {
24462
+ const normalized = requested.replace(/^platform-v/, "");
24463
+ if (actual.platform.version.replace(/^platform-v/, "") !== normalized || actual.platform.tag !== platformTag(normalized)) {
24464
+ throw new LocalCliError({
24465
+ code: "PLATFORM_GATEWAY_ADOPTION_TARGET_MISMATCH",
24466
+ message: `Requested target ${requested}, but the release service returned ${actual.platform.version}.`
24467
+ });
24468
+ }
24469
+ };
24470
+ assertTarget(target, manifest);
24471
+ if (compareSemver(`v${manifest.components.installer.version}`, "v0.1.74") < 0 || compareSemver(`v${manifest.components.cli.recommended}`, "v0.2.106") < 0) {
24472
+ throw new LocalCliError({
24473
+ code: "PLATFORM_GATEWAY_ADOPTION_TARGET_TOO_OLD",
24474
+ message: `Platform target ${target} predates digest-bound legacy gateway adoption support.`,
24475
+ hint: "Choose a published platform target whose installer is at least 0.1.74 and recommended CLI is at least 0.2.106."
24476
+ });
24477
+ }
24478
+ if (!manifest.platform.previousVersion) {
24479
+ throw new LocalCliError({
24480
+ code: "PLATFORM_GATEWAY_ADOPTION_NO_ROLLBACK",
24481
+ message: `Platform target ${target} has no recorded previous release; refusing a gateway ownership transition without a rollback target.`
24482
+ });
24483
+ }
24484
+ const rollbackManifest = await fetchManifest({ channel: true, version: manifest.platform.previousVersion });
24485
+ assertTarget(manifest.platform.previousVersion, rollbackManifest);
24486
+ const result2 = await adoptLegacyGateway({
24487
+ credential: new DefaultAzureCredential21(),
24488
+ subscriptionId,
24489
+ resourceGroup,
24490
+ gatewayResourceId: gw.containerAppResourceId,
24491
+ target,
24492
+ expectedImage: need(this.expectedImage, "--expected-image"),
24493
+ expectedDigest: need(this.expectedDigest, "--expected-digest"),
24494
+ expectedAcrResourceId: need(this.expectedAcrResourceId, "--expected-acr-resource-id"),
24495
+ targetGatewayTag: manifest.components.gateway.tag,
24496
+ targetGatewayDigest: manifest.components.gateway.digest,
24497
+ rollbackGatewayTag: rollbackManifest.components.gateway.tag,
24498
+ rollbackGatewayDigest: rollbackManifest.components.gateway.digest,
24499
+ operatorIdentity: await getCallerObjectId(),
24500
+ onProgress: (m) => this.context.stderr.write(`${m}
24501
+ `)
24502
+ });
24503
+ this.context.stdout.write(mode === "json" ? renderJson(result2) + "\n" : `${result2.outcome}: ${result2.image}@${result2.digest}
24504
+ `);
24505
+ return 0;
24506
+ }
24507
+ };
24508
+
24509
+ // src/commands/platform/policy.ts
24510
+ import { Command as Command41, Option as Option38 } from "clipanion";
24511
+ import { DefaultAzureCredential as DefaultAzureCredential22, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
24022
24512
  init_errors();
24023
24513
 
24024
24514
  // src/lib/platform-policy.ts
@@ -24073,16 +24563,16 @@ async function readPolicy(opts) {
24073
24563
  // src/commands/platform/policy.ts
24074
24564
  var PlatformPolicyCommand = class extends M8tCommand {
24075
24565
  static paths = [["platform", "policy"]];
24076
- static usage = Command40.Usage({
24566
+ static usage = Command41.Usage({
24077
24567
  description: "Show or set how this install handles available platform updates.",
24078
24568
  details: "Modes: 'notify-only' shows updates but never applies them; 'auto-critical' (the default when unset) applies critical releases without asking; 'auto-all' applies every release. Use 'notify-only' where something else already controls what is deployed.",
24079
24569
  examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
24080
24570
  });
24081
- set = Option37.String("--set", { description: "notify-only | auto-critical | auto-all" });
24082
- subscription = Option37.String("--subscription");
24083
- resourceGroup = Option37.String("--resource-group");
24084
- miClientId = Option37.String("--mi-client-id");
24085
- output = Option37.String("--output");
24571
+ set = Option38.String("--set", { description: "notify-only | auto-critical | auto-all" });
24572
+ subscription = Option38.String("--subscription");
24573
+ resourceGroup = Option38.String("--resource-group");
24574
+ miClientId = Option38.String("--mi-client-id");
24575
+ output = Option38.String("--output");
24086
24576
  async executeCommand() {
24087
24577
  const mode = resolveOutputMode(this.output, this.context.stdout);
24088
24578
  const need = (v, flag) => {
@@ -24095,7 +24585,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24095
24585
  this.context.stderr.write(`${m}
24096
24586
  `);
24097
24587
  };
24098
- const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential21();
24588
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential22();
24099
24589
  const ctx = {
24100
24590
  credential: credential2,
24101
24591
  subscriptionId: need(this.subscription, "--subscription"),
@@ -24128,7 +24618,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24128
24618
  };
24129
24619
 
24130
24620
  // src/commands/platform/enable-cost-report.ts
24131
- import { Command as Command41, Option as Option38 } from "clipanion";
24621
+ import { Command as Command42, Option as Option39 } from "clipanion";
24132
24622
 
24133
24623
  // src/lib/wire-gateway-acs.ts
24134
24624
  init_rbac();
@@ -24168,18 +24658,18 @@ async function wireGatewayForAcs(args) {
24168
24658
  init_errors();
24169
24659
  var PlatformEnableCostReportCommand = class extends M8tCommand {
24170
24660
  static paths = [["platform", "enable-cost-report"]];
24171
- static usage = Command41.Usage({
24661
+ static usage = Command42.Usage({
24172
24662
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
24173
24663
  details: "Discovers the live gateway Container App, grants its managed identity Contributor at the ACS resource scope (authorising the ACS Email send), and sets M8T_ACS_ENDPOINT / M8T_ACS_SENDER / M8T_ENABLE_COST_REPORTER=1 on the gateway. Idempotent \u2014 safely re-runnable. ACS is created during the executor deploy; pass its endpoint, sender, and resource id (from that deploy or 'az communication list')."
24174
24664
  });
24175
- subscription = Option38.String("--subscription");
24176
- resourceGroup = Option38.String("--resource-group", {
24665
+ subscription = Option39.String("--subscription");
24666
+ resourceGroup = Option39.String("--resource-group", {
24177
24667
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24178
24668
  });
24179
- acsEndpoint = Option38.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
24180
- acsSender = Option38.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
24181
- acsResourceId = Option38.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
24182
- output = Option38.String("--output");
24669
+ acsEndpoint = Option39.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
24670
+ acsSender = Option39.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
24671
+ acsResourceId = Option39.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
24672
+ output = Option39.String("--output");
24183
24673
  async executeCommand() {
24184
24674
  const mode = resolveOutputMode(
24185
24675
  this.output,
@@ -24253,13 +24743,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
24253
24743
  };
24254
24744
 
24255
24745
  // src/commands/platform/email.ts
24256
- import { Command as Command42, Option as Option39 } from "clipanion";
24257
- import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
24746
+ import { Command as Command43, Option as Option40 } from "clipanion";
24747
+ import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
24258
24748
  import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
24259
24749
  init_errors();
24260
24750
  var PlatformEmailCommand = class extends M8tCommand {
24261
24751
  static paths = [["platform", "email"]];
24262
- static usage = Command42.Usage({
24752
+ static usage = Command43.Usage({
24263
24753
  description: "Turn outbound email (advisor handoffs) on or off for this install.",
24264
24754
  details: "`on` resolves the ACS sender (reusing an existing one, provisioning only if there is none), records the decision in the platform stamp, and reconciles the executor's environment. `off` records the decision and strips the executor's email wiring, leaving the ACS resource in place so turning it back on is a flag flip. Idempotent \u2014 re-running either direction is safe, and no new agent version is posted when nothing changes.",
24265
24755
  examples: [
@@ -24267,19 +24757,19 @@ var PlatformEmailCommand = class extends M8tCommand {
24267
24757
  ["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
24268
24758
  ]
24269
24759
  });
24270
- state = Option39.String({ required: true, name: "on|off" });
24271
- subscription = Option39.String("--subscription");
24272
- resourceGroup = Option39.String("--resource-group", {
24760
+ state = Option40.String({ required: true, name: "on|off" });
24761
+ subscription = Option40.String("--subscription");
24762
+ resourceGroup = Option40.String("--resource-group", {
24273
24763
  description: "m8t resource group, to disambiguate in a multi-deployment subscription."
24274
24764
  });
24275
- agent = Option39.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
24276
- kvUri = Option39.String("--kv-uri", {
24765
+ agent = Option40.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
24766
+ kvUri = Option40.String("--kv-uri", {
24277
24767
  description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
24278
24768
  });
24279
- endpoint = Option39.String("--endpoint", {
24769
+ endpoint = Option40.String("--endpoint", {
24280
24770
  description: "Foundry project endpoint, to disambiguate a subscription holding several."
24281
24771
  });
24282
- output = Option39.String("--output");
24772
+ output = Option40.String("--output");
24283
24773
  async executeCommand() {
24284
24774
  const wanted = this.state.trim().toLowerCase();
24285
24775
  if (wanted !== "on" && wanted !== "off") {
@@ -24303,7 +24793,7 @@ var PlatformEmailCommand = class extends M8tCommand {
24303
24793
  });
24304
24794
  const { resourceGroup } = parseContainerAppResourceId(ctx.containerAppResourceId);
24305
24795
  const subscriptionId = ctx.subscriptionId;
24306
- const credential2 = new DefaultAzureCredential22();
24796
+ const credential2 = new DefaultAzureCredential23();
24307
24797
  const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
24308
24798
  if (stamp === null) {
24309
24799
  throw new LocalCliError({
@@ -24429,45 +24919,45 @@ var PlatformEmailCommand = class extends M8tCommand {
24429
24919
  };
24430
24920
 
24431
24921
  // src/commands/platform/enable-auto-update.ts
24432
- import { Command as Command43, Option as Option40 } from "clipanion";
24922
+ import { Command as Command44, Option as Option41 } from "clipanion";
24433
24923
  import { confirm as confirm6 } from "@inquirer/prompts";
24434
- import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
24924
+ import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
24435
24925
  init_errors();
24436
24926
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24437
24927
  static paths = [["platform", "enable-auto-update"]];
24438
- static usage = Command43.Usage({
24928
+ static usage = Command44.Usage({
24439
24929
  category: "Platform",
24440
24930
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
24441
24931
  details: "For an install that predates the Platform Update Framework: recovers the deployment's resource-name suffix (from the stamped system/infra-params row, or by verified derivation from the live gateway), recovers the other bicep params from live state (WITHOUT changing the deployed gateway image), and re-runs deploy/main.bicep with provisionUpdater=true \u2014 which provisions the updater managed identity, the cron Container Apps Job, and every role assignment (Owner@RG + Foundry User@account + Storage Table + Storage Blob + AcrPull), all scoped to the resource group. On success, backfills system/infra-params so future converges never have to re-derive the suffix. Idempotent \u2014 safe to re-run."
24442
24932
  });
24443
- subscription = Option40.String("--subscription");
24444
- resourceGroup = Option40.String("--resource-group", {
24933
+ subscription = Option41.String("--subscription");
24934
+ resourceGroup = Option41.String("--resource-group", {
24445
24935
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24446
24936
  });
24447
- suffix = Option40.String("--suffix", {
24937
+ suffix = Option41.String("--suffix", {
24448
24938
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
24449
24939
  });
24450
- installerImage = Option40.String("--installer-image", {
24940
+ installerImage = Option41.String("--installer-image", {
24451
24941
  required: true,
24452
24942
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
24453
24943
  });
24454
- updateCron = Option40.String("--update-cron", {
24944
+ updateCron = Option41.String("--update-cron", {
24455
24945
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
24456
24946
  });
24457
- channelUrl = Option40.String("--channel-url", {
24947
+ channelUrl = Option41.String("--channel-url", {
24458
24948
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
24459
24949
  });
24460
- location = Option40.String("--location", {
24950
+ location = Option41.String("--location", {
24461
24951
  description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
24462
24952
  });
24463
- foundryTracing = Option40.String("--foundry-tracing", {
24953
+ foundryTracing = Option41.String("--foundry-tracing", {
24464
24954
  description: "project | account | skip. Omitting it keeps the value already stamped on this install; only an install with nothing stamped falls through to the default (project)."
24465
24955
  });
24466
- endpoint = Option40.String("--endpoint", {
24956
+ endpoint = Option41.String("--endpoint", {
24467
24957
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
24468
24958
  });
24469
- yes = Option40.Boolean("--yes", false);
24470
- output = Option40.String("--output");
24959
+ yes = Option41.Boolean("--yes", false);
24960
+ output = Option41.String("--output");
24471
24961
  async executeCommand() {
24472
24962
  const mode = resolveOutputMode(
24473
24963
  this.output,
@@ -24486,7 +24976,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24486
24976
  resourceGroup: this.resourceGroup
24487
24977
  });
24488
24978
  const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
24489
- const credential2 = new DefaultAzureCredential23();
24979
+ const credential2 = new DefaultAzureCredential24();
24490
24980
  const account = await getAzAccount();
24491
24981
  const subscriptionId = this.subscription ?? account.subscriptionId;
24492
24982
  const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
@@ -24648,7 +25138,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24648
25138
  };
24649
25139
 
24650
25140
  // src/commands/deploy.ts
24651
- import { Command as Command44, Option as Option41 } from "clipanion";
25141
+ import { Command as Command45, Option as Option42 } from "clipanion";
24652
25142
 
24653
25143
  // src/lib/app-reg.ts
24654
25144
  init_esm();
@@ -25212,15 +25702,15 @@ function classifyWhatIf(changes) {
25212
25702
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
25213
25703
  var DeployCommand = class extends M8tCommand {
25214
25704
  static paths = [["deploy"]];
25215
- static usage = Command44.Usage({
25705
+ static usage = Command45.Usage({
25216
25706
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
25217
25707
  details: "Ensures the Entra app reg (or pass --client-id to reuse an existing one \u2014 required if you can't create app regs), writes ~/.m8t/config.yaml, ensures the resource group, and runs deploy/main.bicep. The repo is located via ~/.m8t/repo-root."
25218
25708
  });
25219
- subscription = Option41.String("--subscription");
25220
- resourceGroup = Option41.String("--resource-group", "rg-m8t-stack");
25221
- location = Option41.String("--location", "eastus");
25222
- suffix = Option41.String("--suffix", "");
25223
- imageRef = Option41.String("--image-ref", DEFAULT_IMAGE_REF);
25709
+ subscription = Option42.String("--subscription");
25710
+ resourceGroup = Option42.String("--resource-group", "rg-m8t-stack");
25711
+ location = Option42.String("--location", "eastus");
25712
+ suffix = Option42.String("--suffix", "");
25713
+ imageRef = Option42.String("--image-ref", DEFAULT_IMAGE_REF);
25224
25714
  // Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
25225
25715
  // from-zero case. It exists because the gateway and the voice relay do NOT
25226
25716
  // always run the same image: a converge preserves a BYOC gateway on its own
@@ -25228,28 +25718,28 @@ var DeployCommand = class extends M8tCommand {
25228
25718
  // (`--what-if`) that can only express one image therefore reports the other
25229
25719
  // app as drift on every single run, forever — which is exactly what the
25230
25720
  // infra-drift gate did from 2026-08-11.
25231
- gatewayImageRef = Option41.String("--gateway-image-ref", "");
25232
- acrPullIdentity = Option41.String("--acrpull-identity");
25233
- acrResourceId = Option41.String("--acr-resource-id");
25234
- foundryEndpoint = Option41.String("--foundry-endpoint");
25235
- foundryResourceId = Option41.String("--foundry-resource-id");
25236
- foundryTracing = Option41.String("--foundry-tracing");
25721
+ gatewayImageRef = Option42.String("--gateway-image-ref", "");
25722
+ acrPullIdentity = Option42.String("--acrpull-identity");
25723
+ acrResourceId = Option42.String("--acr-resource-id");
25724
+ foundryEndpoint = Option42.String("--foundry-endpoint");
25725
+ foundryResourceId = Option42.String("--foundry-resource-id");
25726
+ foundryTracing = Option42.String("--foundry-tracing");
25237
25727
  // project | account | skip (bicep default: project)
25238
- clientId = Option41.String("--client-id");
25239
- whatIf = Option41.Boolean("--what-if", false);
25728
+ clientId = Option42.String("--client-id");
25729
+ whatIf = Option42.Boolean("--what-if", false);
25240
25730
  // Only meaningful with --what-if. Routes the comparison through the
25241
25731
  // value-free renderers (see ./lib/whatif-redact.js) instead of the default
25242
25732
  // before/after renderer. Defaults false so a local, interactive run keeps
25243
25733
  // showing values — that is the whole diagnostic point of --what-if.
25244
25734
  // Automation that forwards this output anywhere non-private (a CI log, an
25245
25735
  // issue) MUST pass --redact.
25246
- redact = Option41.Boolean("--redact", false);
25247
- output = Option41.String("--output");
25736
+ redact = Option42.Boolean("--redact", false);
25737
+ output = Option42.String("--output");
25248
25738
  // Subscription-scoped role assignments. Omitted ⇒ the template default (true).
25249
25739
  // Pass false when deploying as a principal scoped to the resource group only:
25250
25740
  // it cannot deploy at subscription scope, and those assignments persist
25251
25741
  // idempotently from the initial deployment anyway.
25252
- assignSubscriptionRoles = Option41.String("--assign-subscription-roles");
25742
+ assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
25253
25743
  /**
25254
25744
  * The installer image the updater Container-Apps Job runs.
25255
25745
  *
@@ -25260,23 +25750,23 @@ var DeployCommand = class extends M8tCommand {
25260
25750
  * than that — the comparison proposes REMOVING an updater job that exists and
25261
25751
  * should, and reports it as drift on every single run.
25262
25752
  */
25263
- installerImage = Option41.String("--installer-image");
25753
+ installerImage = Option42.String("--installer-image");
25264
25754
  // Referee — all optional, undefined by default ⇒ the bicep defaults
25265
25755
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
25266
25756
  // empty). Only pass these when explicitly enabling the referee exam stack.
25267
- gatewayCpu = Option41.String("--gateway-cpu");
25268
- gatewayMemory = Option41.String("--gateway-memory");
25269
- refereeEnabled = Option41.String("--referee-enabled");
25270
- refereeBrainRepos = Option41.String("--referee-brain-repos");
25271
- refereeFeedRepo = Option41.String("--referee-feed-repo");
25272
- refereeInstallationId = Option41.String("--referee-installation-id");
25273
- refereeWebhookHmacKvUri = Option41.String("--referee-webhook-hmac-kv-uri");
25274
- examKvUri = Option41.String("--exam-kv-uri");
25275
- examLaWorkspaceId = Option41.String("--exam-la-workspace-id");
25276
- brainEvalDeployment = Option41.String("--brain-eval-deployment");
25277
- brainAppLogin = Option41.String("--brain-app-login");
25278
- refereeCheckpointDir = Option41.String("--referee-checkpoint-dir");
25279
- examApiBase = Option41.String("--exam-api-base");
25757
+ gatewayCpu = Option42.String("--gateway-cpu");
25758
+ gatewayMemory = Option42.String("--gateway-memory");
25759
+ refereeEnabled = Option42.String("--referee-enabled");
25760
+ refereeBrainRepos = Option42.String("--referee-brain-repos");
25761
+ refereeFeedRepo = Option42.String("--referee-feed-repo");
25762
+ refereeInstallationId = Option42.String("--referee-installation-id");
25763
+ refereeWebhookHmacKvUri = Option42.String("--referee-webhook-hmac-kv-uri");
25764
+ examKvUri = Option42.String("--exam-kv-uri");
25765
+ examLaWorkspaceId = Option42.String("--exam-la-workspace-id");
25766
+ brainEvalDeployment = Option42.String("--brain-eval-deployment");
25767
+ brainAppLogin = Option42.String("--brain-app-login");
25768
+ refereeCheckpointDir = Option42.String("--referee-checkpoint-dir");
25769
+ examApiBase = Option42.String("--exam-api-base");
25280
25770
  async executeCommand() {
25281
25771
  const mode = resolveOutputMode(
25282
25772
  this.output,
@@ -25460,7 +25950,7 @@ var DeployCommand = class extends M8tCommand {
25460
25950
 
25461
25951
  // src/commands/eval/skill.ts
25462
25952
  import { spawnSync as spawnSync4 } from "child_process";
25463
- import { Command as Command45, Option as Option42 } from "clipanion";
25953
+ import { Command as Command46, Option as Option43 } from "clipanion";
25464
25954
  init_errors();
25465
25955
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
25466
25956
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -25485,14 +25975,14 @@ function parseVerdict(stdout) {
25485
25975
  }
25486
25976
  var EvalSkillCommand = class extends M8tCommand {
25487
25977
  static paths = [["eval", "skill"]];
25488
- static usage = Command45.Usage({
25978
+ static usage = Command46.Usage({
25489
25979
  description: "Vet one inbox skill candidate: promote / reject / needs_review. Shells out to the Python `brain-eval` core (override its path with $BRAIN_EVAL_BIN)."
25490
25980
  });
25491
- candidate = Option42.String();
25492
- skillsDir = Option42.String("--skills-dir");
25493
- noJudge = Option42.Boolean("--no-judge", false);
25494
- deployment = Option42.String("--deployment");
25495
- output = Option42.String("--output");
25981
+ candidate = Option43.String();
25982
+ skillsDir = Option43.String("--skills-dir");
25983
+ noJudge = Option43.Boolean("--no-judge", false);
25984
+ deployment = Option43.String("--deployment");
25985
+ output = Option43.String("--output");
25496
25986
  executeCommand() {
25497
25987
  return Promise.resolve(this._runCommand());
25498
25988
  }
@@ -25551,7 +26041,7 @@ var EvalSkillCommand = class extends M8tCommand {
25551
26041
  import { spawnSync as spawnSync5 } from "child_process";
25552
26042
  import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
25553
26043
  import { join as join30 } from "path";
25554
- import { Command as Command46, Option as Option43 } from "clipanion";
26044
+ import { Command as Command47, Option as Option44 } from "clipanion";
25555
26045
  init_errors();
25556
26046
  init_esm();
25557
26047
  function parseArmToken(tok, opts) {
@@ -25781,24 +26271,24 @@ function buildPlan(args) {
25781
26271
  }
25782
26272
  var EvalExamCommand = class extends M8tCommand {
25783
26273
  static paths = [["eval", "exam"]];
25784
- static usage = Command46.Usage({
26274
+ static usage = Command47.Usage({
25785
26275
  description: "Run a brain exam: impact A/B + dream-delta. Resolves the plan, then shells out to the Python `brain-exam` orchestrator (override its path with $BRAIN_EXAM_BIN). Renders an ExamVerdict: three-valued verdict + power note + per-task flips."
25786
26276
  });
25787
- worker = Option43.String();
25788
- arms = Option43.String("--arms");
25789
- taskSet = Option43.String("--task-set");
25790
- examType = Option43.String("--exam-type");
25791
- skill = Option43.String("--skill");
25792
- reps = Option43.String("-n,--reps");
25793
- probes = Option43.String("--probes");
25794
- pool = Option43.String("--pool");
25795
- out = Option43.String("--out");
25796
- dryRun = Option43.Boolean("--dry-run", false);
25797
- keepArms = Option43.Boolean("--keep-arms", false);
25798
- allowStub = Option43.Boolean("--allow-stub", false);
25799
- deployment = Option43.String("--deployment");
25800
- output = Option43.String("--output");
25801
- observeWaitS = Option43.String("--observe-wait-s");
26277
+ worker = Option44.String();
26278
+ arms = Option44.String("--arms");
26279
+ taskSet = Option44.String("--task-set");
26280
+ examType = Option44.String("--exam-type");
26281
+ skill = Option44.String("--skill");
26282
+ reps = Option44.String("-n,--reps");
26283
+ probes = Option44.String("--probes");
26284
+ pool = Option44.String("--pool");
26285
+ out = Option44.String("--out");
26286
+ dryRun = Option44.Boolean("--dry-run", false);
26287
+ keepArms = Option44.Boolean("--keep-arms", false);
26288
+ allowStub = Option44.Boolean("--allow-stub", false);
26289
+ deployment = Option44.String("--deployment");
26290
+ output = Option44.String("--output");
26291
+ observeWaitS = Option44.String("--observe-wait-s");
25802
26292
  async executeCommand() {
25803
26293
  await Promise.resolve();
25804
26294
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -25913,10 +26403,10 @@ var EvalExamCommand = class extends M8tCommand {
25913
26403
  };
25914
26404
 
25915
26405
  // src/commands/version.ts
25916
- import { Command as Command47, Option as Option44 } from "clipanion";
26406
+ import { Command as Command48, Option as Option45 } from "clipanion";
25917
26407
  var VersionCommand = class extends M8tCommand {
25918
26408
  static paths = [["version"], ["--version"], ["-v"]];
25919
- static usage = Command47.Usage({
26409
+ static usage = Command48.Usage({
25920
26410
  description: "Print the CLI version.",
25921
26411
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
25922
26412
  examples: [
@@ -25924,8 +26414,8 @@ var VersionCommand = class extends M8tCommand {
25924
26414
  ["Print as JSON", "$0 version --output json"]
25925
26415
  ]
25926
26416
  });
25927
- output = Option44.String("--output", { description: "pretty | json | auto (default)" });
25928
- verbose = Option44.Boolean("--verbose", false);
26417
+ output = Option45.String("--output", { description: "pretty | json | auto (default)" });
26418
+ verbose = Option45.Boolean("--verbose", false);
25929
26419
  executeCommand() {
25930
26420
  const mode = resolveOutputMode(
25931
26421
  this.output ?? "auto",
@@ -25956,18 +26446,18 @@ var VersionCommand = class extends M8tCommand {
25956
26446
  };
25957
26447
 
25958
26448
  // src/commands/whoami.ts
25959
- import { Command as Command48, Option as Option45 } from "clipanion";
26449
+ import { Command as Command49, Option as Option46 } from "clipanion";
25960
26450
  var WhoamiCommand = class extends M8tCommand {
25961
26451
  static paths = [["whoami"]];
25962
- static usage = Command48.Usage({
26452
+ static usage = Command49.Usage({
25963
26453
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
25964
26454
  });
25965
- output = Option45.String("--output");
25966
- verbose = Option45.Boolean("--verbose", false);
25967
- subscription = Option45.String("--subscription", {
26455
+ output = Option46.String("--output");
26456
+ verbose = Option46.Boolean("--verbose", false);
26457
+ subscription = Option46.String("--subscription", {
25968
26458
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
25969
26459
  });
25970
- resourceGroup = Option45.String("--resource-group", {
26460
+ resourceGroup = Option46.String("--resource-group", {
25971
26461
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
25972
26462
  });
25973
26463
  async executeCommand() {
@@ -26032,7 +26522,7 @@ var WhoamiCommand = class extends M8tCommand {
26032
26522
  };
26033
26523
 
26034
26524
  // src/commands/status.ts
26035
- import { Command as Command49, Option as Option46 } from "clipanion";
26525
+ import { Command as Command50, Option as Option47 } from "clipanion";
26036
26526
 
26037
26527
  // src/lib/azd.ts
26038
26528
  init_errors();
@@ -26097,10 +26587,10 @@ async function resolveLocalContext() {
26097
26587
  // src/commands/status.ts
26098
26588
  var StatusCommand = class extends M8tCommand {
26099
26589
  static paths = [["status"]];
26100
- static usage = Command49.Usage({
26590
+ static usage = Command50.Usage({
26101
26591
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
26102
26592
  });
26103
- output = Option46.String("--output");
26593
+ output = Option47.String("--output");
26104
26594
  async executeCommand() {
26105
26595
  const mode = resolveOutputMode(
26106
26596
  this.output,
@@ -26138,8 +26628,8 @@ var StatusCommand = class extends M8tCommand {
26138
26628
  };
26139
26629
 
26140
26630
  // src/commands/doctor.ts
26141
- import { Command as Command50, Option as Option47 } from "clipanion";
26142
- import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
26631
+ import { Command as Command51, Option as Option48 } from "clipanion";
26632
+ import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
26143
26633
  import * as fs30 from "fs";
26144
26634
  import * as os12 from "os";
26145
26635
  import * as path33 from "path";
@@ -26686,12 +27176,12 @@ function probeLegacyStateDir() {
26686
27176
  }
26687
27177
  var DoctorCommand = class extends M8tCommand {
26688
27178
  static paths = [["doctor"]];
26689
- static usage = Command50.Usage({
27179
+ static usage = Command51.Usage({
26690
27180
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
26691
27181
  });
26692
- output = Option47.String("--output");
26693
- agent = Option47.String("--agent");
26694
- resourceGroup = Option47.String("--resource-group", {
27182
+ output = Option48.String("--output");
27183
+ agent = Option48.String("--agent");
27184
+ resourceGroup = Option48.String("--resource-group", {
26695
27185
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
26696
27186
  });
26697
27187
  async executeCommand() {
@@ -26764,7 +27254,7 @@ var DoctorCommand = class extends M8tCommand {
26764
27254
  let kvStatus = 0;
26765
27255
  if (kv) {
26766
27256
  try {
26767
- const token = await new DefaultAzureCredential24().getToken("https://vault.azure.net/.default");
27257
+ const token = await new DefaultAzureCredential25().getToken("https://vault.azure.net/.default");
26768
27258
  const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
26769
27259
  headers: { Authorization: `Bearer ${token.token}` }
26770
27260
  });
@@ -26783,7 +27273,7 @@ var DoctorCommand = class extends M8tCommand {
26783
27273
  emit(checkModelQuota(deployments, usages));
26784
27274
  checking("outbound email");
26785
27275
  try {
26786
- const credential2 = new DefaultAzureCredential24();
27276
+ const credential2 = new DefaultAzureCredential25();
26787
27277
  const outcome = platformRg && platformSub ? await readStampOutcome({ credential: credential2, subscriptionId: platformSub, resourceGroup: platformRg }).catch(
26788
27278
  () => ({ source: "unreadable" })
26789
27279
  ) : { source: "unreadable" };
@@ -26838,7 +27328,7 @@ var DoctorCommand = class extends M8tCommand {
26838
27328
  if (typeof this.agent === "string" && this.agent) {
26839
27329
  checking(`delivery grant for ${this.agent}`);
26840
27330
  try {
26841
- const credential2 = new DefaultAzureCredential24();
27331
+ const credential2 = new DefaultAzureCredential25();
26842
27332
  const cur = await getAgentVersion({
26843
27333
  credential: credential2,
26844
27334
  projectEndpoint: foundry.projectEndpoint,
@@ -26881,11 +27371,11 @@ var DoctorCommand = class extends M8tCommand {
26881
27371
  };
26882
27372
 
26883
27373
  // src/commands/prereqs.ts
26884
- import { Command as Command51, Option as Option48 } from "clipanion";
27374
+ import { Command as Command52, Option as Option49 } from "clipanion";
26885
27375
  init_errors();
26886
27376
 
26887
27377
  // src/lib/prereq-deps.ts
26888
- import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
27378
+ import { DefaultAzureCredential as DefaultAzureCredential26 } from "@azure/identity";
26889
27379
 
26890
27380
  // src/lib/bootstrap-preflight.ts
26891
27381
  var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
@@ -26997,7 +27487,7 @@ function buildPrereqDeps(opts = {}) {
26997
27487
  probeRedirectUri,
26998
27488
  fixFoundryAccess,
26999
27489
  fixKeyVaultAccess,
27000
- credential: () => credentialSingleton2 ??= new DefaultAzureCredential25()
27490
+ credential: () => credentialSingleton2 ??= new DefaultAzureCredential26()
27001
27491
  };
27002
27492
  }
27003
27493
 
@@ -27587,7 +28077,7 @@ function renderVerdict(v) {
27587
28077
  }
27588
28078
  var PrereqsCommand = class extends M8tCommand {
27589
28079
  static paths = [["prereqs"]];
27590
- static usage = Command51.Usage({
28080
+ static usage = Command52.Usage({
27591
28081
  description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
27592
28082
  details: "Runs one of two phases. With no platform discoverable it checks INSTALL prerequisites: your Azure sign-in, subscription and directory rights, resource-provider registrations, soft-deleted Cognitive Services accounts (they hold their model quota until purged), and model quota. With a platform live it checks USAGE prerequisites for the signed-in account: the sign-in redirect URI, Foundry data-plane access, and Key Vault secrets access. Pass --fix to repair what is repairable, and --fix --for <upn> to set a teammate up (usage phase only). Read-only without --fix.",
27593
28083
  examples: [
@@ -27598,15 +28088,15 @@ var PrereqsCommand = class extends M8tCommand {
27598
28088
  ["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
27599
28089
  ]
27600
28090
  });
27601
- fix = Option48.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
27602
- for_ = Option48.String("--for", { description: "UPN or object id of another person. Usage phase only." });
27603
- phase = Option48.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
27604
- region = Option48.String("--region", { description: "Target region. Enables the install-phase quota check, and judges soft-deleted accounts against the region you will install into (without it, they only warn)." });
27605
- model = Option48.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
27606
- clientId = Option48.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
27607
- subscription = Option48.String("--subscription");
27608
- resourceGroup = Option48.String("--resource-group");
27609
- output = Option48.String("--output");
28091
+ fix = Option49.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
28092
+ for_ = Option49.String("--for", { description: "UPN or object id of another person. Usage phase only." });
28093
+ phase = Option49.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
28094
+ region = Option49.String("--region", { description: "Target region. Enables the install-phase quota check, and judges soft-deleted accounts against the region you will install into (without it, they only warn)." });
28095
+ model = Option49.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
28096
+ clientId = Option49.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
28097
+ subscription = Option49.String("--subscription");
28098
+ resourceGroup = Option49.String("--resource-group");
28099
+ output = Option49.String("--output");
27610
28100
  async executeCommand() {
27611
28101
  const mode = resolveOutputMode(this.output, this.context.stdout);
27612
28102
  const json = mode === "json";
@@ -27662,7 +28152,7 @@ var PrereqsCommand = class extends M8tCommand {
27662
28152
  };
27663
28153
 
27664
28154
  // src/commands/switch.ts
27665
- import { Command as Command52, Option as Option49 } from "clipanion";
28155
+ import { Command as Command53, Option as Option50 } from "clipanion";
27666
28156
 
27667
28157
  // src/lib/profiles.ts
27668
28158
  import * as fs31 from "fs/promises";
@@ -27802,14 +28292,14 @@ async function profileSwitch(name, asName) {
27802
28292
  init_errors();
27803
28293
  var SwitchCommand = class extends M8tCommand {
27804
28294
  static paths = [["switch"]];
27805
- static usage = Command52.Usage({
28295
+ static usage = Command53.Usage({
27806
28296
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
27807
28297
  });
27808
- profile = Option49.String({ required: false });
27809
- subscription = Option49.String("--subscription");
27810
- list = Option49.Boolean("--list", false);
27811
- as = Option49.String("--as");
27812
- output = Option49.String("--output");
28298
+ profile = Option50.String({ required: false });
28299
+ subscription = Option50.String("--subscription");
28300
+ list = Option50.Boolean("--list", false);
28301
+ as = Option50.String("--as");
28302
+ output = Option50.String("--output");
27813
28303
  async executeCommand() {
27814
28304
  const mode = resolveOutputMode(
27815
28305
  this.output,
@@ -27866,7 +28356,7 @@ var SwitchCommand = class extends M8tCommand {
27866
28356
 
27867
28357
  // src/commands/open.ts
27868
28358
  import { spawn as spawn5 } from "child_process";
27869
- import { Command as Command53, Option as Option50 } from "clipanion";
28359
+ import { Command as Command54, Option as Option51 } from "clipanion";
27870
28360
 
27871
28361
  // src/lib/open-targets.ts
27872
28362
  init_errors();
@@ -27912,14 +28402,14 @@ function openUrl(url) {
27912
28402
  }
27913
28403
  var OpenCommand = class extends M8tCommand {
27914
28404
  static paths = [["open"]];
27915
- static usage = Command53.Usage({
28405
+ static usage = Command54.Usage({
27916
28406
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
27917
28407
  details: "Targets: webapp (deployed app, default) | foundry (ai.azure.com) | portal (resource group in the Azure portal). Pass --print to emit the URL instead of launching a browser (also the default when stdout isn't a TTY)."
27918
28408
  });
27919
- target = Option50.String({ required: false });
27920
- print = Option50.Boolean("--print", false);
27921
- output = Option50.String("--output");
27922
- resourceGroup = Option50.String("--resource-group", {
28409
+ target = Option51.String({ required: false });
28410
+ print = Option51.Boolean("--print", false);
28411
+ output = Option51.String("--output");
28412
+ resourceGroup = Option51.String("--resource-group", {
27923
28413
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
27924
28414
  });
27925
28415
  async executeCommand() {
@@ -27963,7 +28453,7 @@ var OpenCommand = class extends M8tCommand {
27963
28453
  };
27964
28454
 
27965
28455
  // src/commands/dream/run.ts
27966
- import { Command as Command54, Option as Option51 } from "clipanion";
28456
+ import { Command as Command55, Option as Option52 } from "clipanion";
27967
28457
  import { AzureCliCredential } from "@azure/identity";
27968
28458
  import { TableClient as TableClient7 } from "@azure/data-tables";
27969
28459
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
@@ -30363,28 +30853,28 @@ function redactTranscripts(input) {
30363
30853
  }
30364
30854
  var DreamRunCommand = class extends M8tCommand {
30365
30855
  static paths = [["dream", "run"]];
30366
- static usage = Command54.Usage({
30856
+ static usage = Command55.Usage({
30367
30857
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
30368
30858
  details: "Builds AzureCliCredential + resolves the Foundry project, ledger table, and Log Analytics workspace, runs the consumption pipeline, and prints the skip-ledger, the partition invariant, and harvest stats. Transcripts are metadata-only unless --show-transcripts is passed.\n\nThe bare command is READ-ONLY. --live takes the other branch: a real model call and a commit to the worker's brain repo through a minted GitHub App token."
30369
30859
  });
30370
- worker = Option51.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
30860
+ worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
30371
30861
  // Opting IN to the side effects, rather than opting out of them. This command's
30372
30862
  // own help has always described a dry run, but the bare invocation used to take
30373
30863
  // the live branch — a real model call and a commit to the brain repo — so anyone
30374
30864
  // acting on `--help` got the opposite of what they read.
30375
- live = Option51.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
30376
- dryRun = Option51.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
30377
- since = Option51.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
30378
- reset = Option51.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
30379
- showTranscripts = Option51.Boolean("--show-transcripts", false, {
30865
+ live = Option52.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
30866
+ dryRun = Option52.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
30867
+ since = Option52.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
30868
+ reset = Option52.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
30869
+ showTranscripts = Option52.Boolean("--show-transcripts", false, {
30380
30870
  description: "Print transcript bodies (default: metadata only)."
30381
30871
  });
30382
- subscription = Option51.String("--subscription");
30383
- endpoint = Option51.String("--endpoint");
30384
- storageAccount = Option51.String("--storage-account", {
30872
+ subscription = Option52.String("--subscription");
30873
+ endpoint = Option52.String("--endpoint");
30874
+ storageAccount = Option52.String("--storage-account", {
30385
30875
  description: "Ledger storage account name (skips tag-based discovery)"
30386
30876
  });
30387
- output = Option51.String("--output");
30877
+ output = Option52.String("--output");
30388
30878
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
30389
30879
  deps;
30390
30880
  async executeCommand() {
@@ -30735,7 +31225,7 @@ function defaultDeps(overrides) {
30735
31225
 
30736
31226
  // src/commands/conversations/sweep.ts
30737
31227
  import { createHash as createHash5 } from "crypto";
30738
- import { Command as Command55, Option as Option52 } from "clipanion";
31228
+ import { Command as Command56, Option as Option53 } from "clipanion";
30739
31229
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
30740
31230
  import { TableClient as TableClient8 } from "@azure/data-tables";
30741
31231
  import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
@@ -30861,7 +31351,7 @@ function defaultDeps2() {
30861
31351
  }
30862
31352
  var ConversationsSweepCommand = class extends M8tCommand {
30863
31353
  static paths = [["conversations", "sweep"]];
30864
- static usage = Command55.Usage({
31354
+ static usage = Command56.Usage({
30865
31355
  category: "Conversations",
30866
31356
  description: "Delete expired public-visitor conversations (dry run by default)",
30867
31357
  details: `
@@ -30877,22 +31367,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
30877
31367
  ["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
30878
31368
  ]
30879
31369
  });
30880
- doDelete = Option52.Boolean("--delete", false, {
31370
+ doDelete = Option53.Boolean("--delete", false, {
30881
31371
  description: "Perform deletions (without this flag the command only reports)"
30882
31372
  });
30883
- principal = Option52.String("--principal", {
31373
+ principal = Option53.String("--principal", {
30884
31374
  description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
30885
31375
  });
30886
- max = Option52.String("--max", "200", { description: "Maximum deletions per run" });
30887
- graceDays = Option52.String("--grace-days", "14", {
31376
+ max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
31377
+ graceDays = Option53.String("--grace-days", "14", {
30888
31378
  description: "Days past the 30-day key life before a conversation is eligible"
30889
31379
  });
30890
- lookbackDays = Option52.String("--lookback-days", "180", {
31380
+ lookbackDays = Option53.String("--lookback-days", "180", {
30891
31381
  description: "How far back to scan ledger activity for candidates"
30892
31382
  });
30893
- subscription = Option52.String("--subscription", { description: "Azure subscription id override" });
30894
- endpoint = Option52.String("--endpoint", { description: "Foundry project endpoint override" });
30895
- storageAccount = Option52.String("--storage-account", {
31383
+ subscription = Option53.String("--subscription", { description: "Azure subscription id override" });
31384
+ endpoint = Option53.String("--endpoint", { description: "Foundry project endpoint override" });
31385
+ storageAccount = Option53.String("--storage-account", {
30896
31386
  description: "Ledger storage account name (skips tag-based discovery)"
30897
31387
  });
30898
31388
  deps = defaultDeps2();
@@ -31020,7 +31510,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
31020
31510
  };
31021
31511
 
31022
31512
  // src/commands/foundry/create.ts
31023
- import { Command as Command56, Option as Option53 } from "clipanion";
31513
+ import { Command as Command57, Option as Option54 } from "clipanion";
31024
31514
 
31025
31515
  // src/lib/foundry-create.ts
31026
31516
  init_errors();
@@ -31258,7 +31748,7 @@ async function createFoundryProject(args) {
31258
31748
  init_errors();
31259
31749
  var FoundryCreateCommand = class extends M8tCommand {
31260
31750
  static paths = [["foundry", "create"]];
31261
- static usage = Command56.Usage({
31751
+ static usage = Command57.Usage({
31262
31752
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
31263
31753
  details: "Non-interactive and idempotent. Creates the AIServices account (custom subdomain + project management), a project, and a model deployment (default gpt-4.1-mini @ capacity 50). Region must be hosted-agent-eligible. Emits the project endpoint as structured output. Re-run is a clean no-op (account/project skipped if present; deployment capacity converges UP, never down).",
31264
31754
  examples: [
@@ -31267,16 +31757,16 @@ var FoundryCreateCommand = class extends M8tCommand {
31267
31757
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
31268
31758
  ]
31269
31759
  });
31270
- resourceGroup = Option53.String("--resource-group");
31271
- location = Option53.String("--location");
31272
- account = Option53.String("--account");
31273
- project = Option53.String("--project", "m8t");
31274
- model = Option53.String("--model", "gpt-4.1-mini");
31275
- modelVersion = Option53.String("--model-version", "2025-04-14");
31276
- capacity = Option53.String("--capacity", "50");
31277
- subscription = Option53.String("--subscription");
31278
- skipQuotaCheck = Option53.Boolean("--skip-quota-check", false);
31279
- output = Option53.String("--output");
31760
+ resourceGroup = Option54.String("--resource-group");
31761
+ location = Option54.String("--location");
31762
+ account = Option54.String("--account");
31763
+ project = Option54.String("--project", "m8t");
31764
+ model = Option54.String("--model", "gpt-4.1-mini");
31765
+ modelVersion = Option54.String("--model-version", "2025-04-14");
31766
+ capacity = Option54.String("--capacity", "50");
31767
+ subscription = Option54.String("--subscription");
31768
+ skipQuotaCheck = Option54.Boolean("--skip-quota-check", false);
31769
+ output = Option54.String("--output");
31280
31770
  async executeCommand() {
31281
31771
  const mode = resolveOutputMode(
31282
31772
  this.output,
@@ -31348,22 +31838,22 @@ var FoundryCreateCommand = class extends M8tCommand {
31348
31838
  };
31349
31839
 
31350
31840
  // src/commands/foundry/await-ready.ts
31351
- import { Command as Command57, Option as Option54 } from "clipanion";
31841
+ import { Command as Command58, Option as Option55 } from "clipanion";
31352
31842
  import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
31353
31843
  init_errors();
31354
31844
  var FoundryAwaitReadyCommand = class extends M8tCommand {
31355
31845
  static paths = [["foundry", "await-ready"]];
31356
- static usage = Command57.Usage({
31846
+ static usage = Command58.Usage({
31357
31847
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
31358
31848
  details: "Probes the project (GET /agents) until it returns 200 on a few consecutive tries, or fails clearly after a bounded budget. A newly-created account can serve intermittent 404 'Project not found' for minutes; run this after 'foundry create' and before deploying agents so the worker phase doesn't catch the unstable window.",
31359
31849
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
31360
31850
  });
31361
- endpoint = Option54.String("--endpoint");
31362
- consecutive = Option54.String("--consecutive", "3");
31363
- attempts = Option54.String("--attempts", "60");
31364
- interval = Option54.String("--interval", "5");
31365
- subscription = Option54.String("--subscription");
31366
- output = Option54.String("--output");
31851
+ endpoint = Option55.String("--endpoint");
31852
+ consecutive = Option55.String("--consecutive", "3");
31853
+ attempts = Option55.String("--attempts", "60");
31854
+ interval = Option55.String("--interval", "5");
31855
+ subscription = Option55.String("--subscription");
31856
+ output = Option55.String("--output");
31367
31857
  async executeCommand() {
31368
31858
  const mode = resolveOutputMode(this.output, this.context.stdout);
31369
31859
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -31397,7 +31887,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
31397
31887
  };
31398
31888
 
31399
31889
  // src/commands/bootstrap/preflight.ts
31400
- import { Command as Command58, Option as Option55 } from "clipanion";
31890
+ import { Command as Command59, Option as Option56 } from "clipanion";
31401
31891
 
31402
31892
  // ../../packages/telemetry-contract/artifact/tier-map.ts
31403
31893
  var EVENT_TIERS = {
@@ -31449,7 +31939,7 @@ function preflightRenderable(results) {
31449
31939
  }
31450
31940
  var BootstrapPreflightCommand = class extends M8tCommand {
31451
31941
  static paths = [["bootstrap", "preflight"]];
31452
- static usage = Command58.Usage({
31942
+ static usage = Command59.Usage({
31453
31943
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
31454
31944
  details: "Step 1 of `m8t bootstrap`. Prints an unmissable admin-credentials notice, then checks: Owner or User Access Administrator at subscription scope (hard requirement), directory-admin capability to register the app (or pass --client-id), every Azure resource provider the install uses (registering any that are missing), soft-deleted Cognitive Services accounts (they keep their model quota until purged, so quota can read free and the model deployment still fail), and \u2014 with --location \u2014 model quota in the target region. Exits non-zero with the exact failing check + remedy. `m8t prereqs` runs the same substrate checks on their own.",
31455
31945
  examples: [
@@ -31458,9 +31948,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
31458
31948
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
31459
31949
  ]
31460
31950
  });
31461
- clientId = Option55.String("--client-id");
31462
- subscription = Option55.String("--subscription");
31463
- location = Option55.String("--location", {
31951
+ clientId = Option56.String("--client-id");
31952
+ subscription = Option56.String("--subscription");
31953
+ location = Option56.String("--location", {
31464
31954
  description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
31465
31955
  });
31466
31956
  async executeCommand() {
@@ -31560,7 +32050,7 @@ ${colors.error(" " + why)}
31560
32050
  import * as fs34 from "fs";
31561
32051
  import * as os15 from "os";
31562
32052
  import * as path37 from "path";
31563
- import { Command as Command59, Option as Option56 } from "clipanion";
32053
+ import { Command as Command60, Option as Option57 } from "clipanion";
31564
32054
  init_errors();
31565
32055
 
31566
32056
  // src/lib/bootstrap-mi.ts
@@ -31925,12 +32415,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
31925
32415
  // src/commands/bootstrap/launch.ts
31926
32416
  var DEFAULT_RG = "rg-m8t-stack";
31927
32417
  var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
31928
- var DEFAULT_INSTALLER_TAG = "v0.1.71";
32418
+ var DEFAULT_INSTALLER_TAG = "v0.1.74";
31929
32419
  var ACI_NAME = "m8t-installer";
31930
32420
  var MI_NAME = "m8t-installer-mi";
31931
32421
  var BootstrapLaunchCommand = class extends M8tCommand {
31932
32422
  static paths = [["bootstrap", "launch"]];
31933
- static usage = Command59.Usage({
32423
+ static usage = Command60.Usage({
31934
32424
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
31935
32425
  details: "Step 2 of `m8t bootstrap` (run after `preflight`). Ensures the resource group, creates the m8t app registration (or uses --client-id), creates a user-assigned managed identity granted Owner at subscription scope, and launches the published m8t-installer image as an ACI run-to-completion job under that identity. Writes ~/.m8t/bootstrap.json for `status` and `reap`, and threads your Azure object id so the installer can grant you the platform's data-plane roles itself.",
31936
32426
  examples: [
@@ -31941,31 +32431,31 @@ var BootstrapLaunchCommand = class extends M8tCommand {
31941
32431
  ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
31942
32432
  ]
31943
32433
  });
31944
- location = Option56.String("--location");
31945
- resourceGroup = Option56.String("--resource-group");
31946
- clientId = Option56.String("--client-id");
31947
- subscription = Option56.String("--subscription");
31948
- installerTag = Option56.String("--installer-tag");
32434
+ location = Option57.String("--location");
32435
+ resourceGroup = Option57.String("--resource-group");
32436
+ clientId = Option57.String("--client-id");
32437
+ subscription = Option57.String("--subscription");
32438
+ installerTag = Option57.String("--installer-tag");
31949
32439
  // Full image ref override (registry + repo + tag) — an escape hatch when the
31950
32440
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
31951
32441
  // Wins over --installer-tag / the pinned default.
31952
- installerImage = Option56.String("--installer-image");
31953
- gatewayImageRef = Option56.String("--gateway-image-ref");
31954
- githubAppCreds = Option56.String("--github-app-creds");
31955
- contactEmail = Option56.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
31956
- company = Option56.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
32442
+ installerImage = Option57.String("--installer-image");
32443
+ gatewayImageRef = Option57.String("--gateway-image-ref");
32444
+ githubAppCreds = Option57.String("--github-app-creds");
32445
+ contactEmail = Option57.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
32446
+ company = Option57.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
31957
32447
  // Value-carrying on purpose: a bare --force would be cargo-culted into
31958
32448
  // runbooks and harness prompts and erode the protection, whereas a faithful
31959
32449
  // paste can never accidentally carry the victim group's name. It AUTHORIZES
31960
32450
  // the target; --resource-group is what CHOOSES it.
31961
- reinstallInto = Option56.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
31962
- org = Option56.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
31963
- noBrains = Option56.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
32451
+ reinstallInto = Option57.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
32452
+ org = Option57.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
32453
+ noBrains = Option57.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
31964
32454
  // The opt-out TELEMETRY.md names. Without it there is no way to decline at
31965
32455
  // install time — the ACI env is built entirely from these options, so a
31966
32456
  // founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
31967
32457
  // privacy notice that documents a control has to be a control that exists.
31968
- foundryTracing = Option56.String("--foundry-tracing", { description: "Where agent traces go: project (default, your own App Insights) | account (legacy shared) | skip (no tracing \u2014 you lose the record of what your agents did)." });
32458
+ foundryTracing = Option57.String("--foundry-tracing", { description: "Where agent traces go: project (default, your own App Insights) | account (legacy shared) | skip (no tracing \u2014 you lose the record of what your agents did)." });
31969
32459
  async executeCommand() {
31970
32460
  const location = typeof this.location === "string" ? this.location : void 0;
31971
32461
  if (!location) {
@@ -32127,7 +32617,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32127
32617
  };
32128
32618
 
32129
32619
  // src/commands/bootstrap/status.ts
32130
- import { Command as Command61, Option as Option58 } from "clipanion";
32620
+ import { Command as Command62, Option as Option59 } from "clipanion";
32131
32621
  init_errors();
32132
32622
 
32133
32623
  // src/lib/bootstrap-aci-state.ts
@@ -34304,7 +34794,7 @@ async function uninstallCompanion(options) {
34304
34794
  // src/commands/companion/install.ts
34305
34795
  import * as os19 from "os";
34306
34796
  import * as path43 from "path";
34307
- import { Command as Command60, Option as Option57 } from "clipanion";
34797
+ import { Command as Command61, Option as Option58 } from "clipanion";
34308
34798
 
34309
34799
  // src/lib/companion-channel.ts
34310
34800
  async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
@@ -34435,7 +34925,7 @@ Version: ${state.version}
34435
34925
  }
34436
34926
  var CompanionInstallCommand = class extends M8tCommand {
34437
34927
  static paths = [["companion", "install"]];
34438
- static usage = Command60.Usage({
34928
+ static usage = Command61.Usage({
34439
34929
  description: "Install the desktop companions for this user from the release channel.",
34440
34930
  examples: [
34441
34931
  ["Install the released build", "$0 companion install"],
@@ -34445,10 +34935,10 @@ var CompanionInstallCommand = class extends M8tCommand {
34445
34935
  ]
34446
34936
  ]
34447
34937
  });
34448
- from = Option57.String("--from", {
34938
+ from = Option58.String("--from", {
34449
34939
  description: "A locally staged build directory instead of the released one."
34450
34940
  });
34451
- resourceGroup = Option57.String("--resource-group", {
34941
+ resourceGroup = Option58.String("--resource-group", {
34452
34942
  description: "Which deployment to bind to, when the subscription holds more than one."
34453
34943
  });
34454
34944
  async executeCommand() {
@@ -34663,7 +35153,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
34663
35153
  // runbooks and shakedown recipes that a founder may already be part-way
34664
35154
  // through — it prints a deprecation notice and does the right thing.
34665
35155
  static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
34666
- static usage = Command61.Usage({
35156
+ static usage = Command62.Usage({
34667
35157
  description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
34668
35158
  details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.\n\nOn reaching done under --watch this also completes the local half of the install, which nothing else in the bootstrap path can do: it registers the webapp's sign-in redirect URI (the installer runs as a managed identity with no directory role, and at launch time the gateway FQDN did not exist yet), writes ~/.m8t/repo-root and the gateway discovery cache, and seeds your advisors' brains from the onboarding intake. Re-runnable \u2014 `m8t bootstrap finish` is a deprecated alias that does exactly this on an already-done install.",
34669
35159
  examples: [
@@ -34672,12 +35162,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
34672
35162
  ["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
34673
35163
  ]
34674
35164
  });
34675
- watch = Option58.Boolean("--watch", false);
34676
- output = Option58.String("--output");
34677
- repoRoot = Option58.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
34678
- finalize = Option58.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
34679
- subscription = Option58.String("--subscription");
34680
- resourceGroup = Option58.String("--resource-group");
35165
+ watch = Option59.Boolean("--watch", false);
35166
+ output = Option59.String("--output");
35167
+ repoRoot = Option59.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
35168
+ finalize = Option59.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
35169
+ subscription = Option59.String("--subscription");
35170
+ resourceGroup = Option59.String("--resource-group");
34681
35171
  async executeCommand() {
34682
35172
  const state = await readBootstrapState();
34683
35173
  if (!state) {
@@ -34810,7 +35300,7 @@ function formatStatus(d) {
34810
35300
  }
34811
35301
 
34812
35302
  // src/commands/bootstrap/reap.ts
34813
- import { Command as Command62, Option as Option59 } from "clipanion";
35303
+ import { Command as Command63, Option as Option60 } from "clipanion";
34814
35304
  init_errors();
34815
35305
 
34816
35306
  // src/lib/bootstrap-reap.ts
@@ -34904,7 +35394,7 @@ async function reapInstaller(opts) {
34904
35394
  // src/commands/bootstrap/reap.ts
34905
35395
  var BootstrapReapCommand = class extends M8tCommand {
34906
35396
  static paths = [["bootstrap", "reap"]];
34907
- static usage = Command62.Usage({
35397
+ static usage = Command63.Usage({
34908
35398
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
34909
35399
  details: "Runs locally on the 'done' signal (the installer can't delete its own identity). The platform RG and the gateway's own assignments persist. A failed install is left intact for diagnosis unless --force.\n\n--sweep-orphans is a DIFFERENT and much broader mode: instead of reaping this install, it scans the WHOLE SUBSCRIPTION for m8t role assignments left behind by installs whose resources are gone \u2014 orphaned installer Owner-at-subscription-scope grants and orphaned gateway subscription-scope roles. It is a dry run that only lists what it found unless you also pass --yes, which deletes them.",
34910
35400
  examples: [
@@ -34914,9 +35404,9 @@ var BootstrapReapCommand = class extends M8tCommand {
34914
35404
  ["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
34915
35405
  ]
34916
35406
  });
34917
- force = Option59.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
34918
- sweepOrphans = Option59.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
34919
- yes = Option59.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
35407
+ force = Option60.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
35408
+ sweepOrphans = Option60.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
35409
+ yes = Option60.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
34920
35410
  async executeCommand() {
34921
35411
  if (this.sweepOrphans === true) {
34922
35412
  const { subscriptionId: sub } = await getAzAccount();
@@ -35010,7 +35500,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
35010
35500
  };
35011
35501
 
35012
35502
  // src/commands/bootstrap/ui.ts
35013
- import { Command as Command63, Option as Option60 } from "clipanion";
35503
+ import { Command as Command64, Option as Option61 } from "clipanion";
35014
35504
 
35015
35505
  // src/lib/bootstrap-ui.ts
35016
35506
  import * as fs40 from "fs";
@@ -35085,7 +35575,7 @@ function renderDeprecationNotice() {
35085
35575
  }
35086
35576
  var BootstrapUiCommand = class extends M8tCommand {
35087
35577
  static paths = [["bootstrap", "ui"]];
35088
- static usage = Command63.Usage({
35578
+ static usage = Command64.Usage({
35089
35579
  description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
35090
35580
  details: [
35091
35581
  "The local onboarding chat has been retired. Your details are collected by",
@@ -35105,14 +35595,14 @@ var BootstrapUiCommand = class extends M8tCommand {
35105
35595
  // Accepted and ignored, deliberately: removing them would turn an old script's
35106
35596
  // harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
35107
35597
  // the whole surface when the rewrite lands.
35108
- repoRoot = Option60.String("--repo-root", { description: "Ignored (deprecated)." });
35109
- port = Option60.String("--port", "3000", { description: "Ignored (deprecated)." });
35110
- endpoint = Option60.String("--endpoint", { description: "Ignored (deprecated)." });
35111
- prepOnly = Option60.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
35112
- skipInstall = Option60.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
35113
- foreground = Option60.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
35114
- voice = Option60.Boolean("--voice", false, { description: "Ignored (deprecated)." });
35115
- stop = Option60.Boolean("--stop", false, {
35598
+ repoRoot = Option61.String("--repo-root", { description: "Ignored (deprecated)." });
35599
+ port = Option61.String("--port", "3000", { description: "Ignored (deprecated)." });
35600
+ endpoint = Option61.String("--endpoint", { description: "Ignored (deprecated)." });
35601
+ prepOnly = Option61.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
35602
+ skipInstall = Option61.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
35603
+ foreground = Option61.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
35604
+ voice = Option61.Boolean("--voice", false, { description: "Ignored (deprecated)." });
35605
+ stop = Option61.Boolean("--stop", false, {
35116
35606
  description: "Shut down a local chat UI left running by an earlier version of this command."
35117
35607
  });
35118
35608
  // Not `async`: there is nothing left to await. Everything this command used to
@@ -35134,7 +35624,7 @@ var BootstrapUiCommand = class extends M8tCommand {
35134
35624
 
35135
35625
  // src/commands/bootstrap/profile.ts
35136
35626
  import * as readline3 from "readline/promises";
35137
- import { Command as Command64, Option as Option61 } from "clipanion";
35627
+ import { Command as Command65, Option as Option62 } from "clipanion";
35138
35628
 
35139
35629
  // src/lib/profile-collect.ts
35140
35630
  init_errors();
@@ -35303,7 +35793,7 @@ async function openChatInvite(deps = {}) {
35303
35793
  // src/commands/bootstrap/profile.ts
35304
35794
  var BootstrapProfileCommand = class extends M8tCommand {
35305
35795
  static paths = [["bootstrap", "profile"]];
35306
- static usage = Command64.Usage({
35796
+ static usage = Command65.Usage({
35307
35797
  description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
35308
35798
  details: [
35309
35799
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
@@ -35323,12 +35813,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
35323
35813
  ["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
35324
35814
  ]
35325
35815
  });
35326
- founderEmail = Option61.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
35327
- advisorName = Option61.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
35328
- advisorEmail = Option61.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
35329
- noAdvisor = Option61.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
35330
- noChat = Option61.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
35331
- print = Option61.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
35816
+ founderEmail = Option62.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
35817
+ advisorName = Option62.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
35818
+ advisorEmail = Option62.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
35819
+ noAdvisor = Option62.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
35820
+ noChat = Option62.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
35821
+ print = Option62.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
35332
35822
  async executeCommand() {
35333
35823
  const stdin = this.context.stdin;
35334
35824
  const stdout = this.context.stdout;
@@ -35421,10 +35911,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
35421
35911
  };
35422
35912
 
35423
35913
  // src/commands/bootstrap/seed-profile.ts
35424
- import { Command as Command65, Option as Option62 } from "clipanion";
35914
+ import { Command as Command66, Option as Option63 } from "clipanion";
35425
35915
  var BootstrapSeedProfileCommand = class extends M8tCommand {
35426
35916
  static paths = [["bootstrap", "seed-profile"]];
35427
- static usage = Command65.Usage({
35917
+ static usage = Command66.Usage({
35428
35918
  description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
35429
35919
  details: [
35430
35920
  "Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
@@ -35441,11 +35931,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
35441
35931
  ["Seed now (idempotent)", "$0 bootstrap seed-profile"]
35442
35932
  ]
35443
35933
  });
35444
- endpoint = Option62.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
35445
- brain = Option62.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
35446
- watch = Option62.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
35447
- timeout = Option62.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
35448
- githubAppCreds = Option62.String("--github-app-creds");
35934
+ endpoint = Option63.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
35935
+ brain = Option63.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
35936
+ watch = Option63.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
35937
+ timeout = Option63.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
35938
+ githubAppCreds = Option63.String("--github-app-creds");
35449
35939
  async executeCommand() {
35450
35940
  const ctx = await resolveSeedContext({
35451
35941
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -35533,7 +36023,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
35533
36023
  import * as fs41 from "fs";
35534
36024
  import * as os22 from "os";
35535
36025
  import * as path46 from "path";
35536
- import { Command as Command66, Option as Option63 } from "clipanion";
36026
+ import { Command as Command67, Option as Option64 } from "clipanion";
35537
36027
  init_errors();
35538
36028
 
35539
36029
  // src/lib/telemetry-enroll.ts
@@ -35615,7 +36105,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
35615
36105
  }
35616
36106
  var TelemetryEnrollCommand = class extends M8tCommand {
35617
36107
  static paths = [["telemetry", "enroll"]];
35618
- static usage = Command66.Usage({
36108
+ static usage = Command67.Usage({
35619
36109
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
35620
36110
  details: "Generates an instance id + ingest key from the m8t telemetry ingest and stores the key in your platform Key Vault, the same place the installer writes it on a fresh install. Your installation is identified only by that random instance id \u2014 no contact, company, or subscription details are sent unless you pass them explicitly. Idempotent: refuses if a key already exists.",
35621
36111
  examples: [
@@ -35623,11 +36113,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
35623
36113
  ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
35624
36114
  ]
35625
36115
  });
35626
- company = Option63.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
35627
- contactEmail = Option63.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
35628
- subscription = Option63.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
35629
- resourceGroup = Option63.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
35630
- force = Option63.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
36116
+ company = Option64.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
36117
+ contactEmail = Option64.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
36118
+ subscription = Option64.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
36119
+ resourceGroup = Option64.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
36120
+ force = Option64.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
35631
36121
  async executeCommand() {
35632
36122
  const account = await getAzAccount();
35633
36123
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
@@ -35677,7 +36167,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
35677
36167
  };
35678
36168
 
35679
36169
  // src/commands/companion/bridge.ts
35680
- import { Command as Command67, Option as Option64 } from "clipanion";
36170
+ import { Command as Command68, Option as Option65 } from "clipanion";
35681
36171
 
35682
36172
  // ../../packages/companion-bridge-contract/src/index.ts
35683
36173
  var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
@@ -37144,14 +37634,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
37144
37634
  return 3;
37145
37635
  }
37146
37636
  }
37147
- var CompanionBridgeCommand = class extends Command67 {
37637
+ var CompanionBridgeCommand = class extends Command68 {
37148
37638
  static paths = [["companion", "_bridge"]];
37149
37639
  /**
37150
37640
  * One process serving many requests instead of one per request, so the
37151
37641
  * session keeps its authenticated context between them. A CLI predating the
37152
37642
  * flag rejects it outright, which is how the app knows to fall back.
37153
37643
  */
37154
- serve = Option64.Boolean("--serve", false);
37644
+ serve = Option65.Boolean("--serve", false);
37155
37645
  async execute() {
37156
37646
  if (this.serve) {
37157
37647
  return runCompanionBridgeServe(
@@ -37169,7 +37659,7 @@ var CompanionBridgeCommand = class extends Command67 {
37169
37659
  };
37170
37660
 
37171
37661
  // src/commands/companion/status.ts
37172
- import { Command as Command68 } from "clipanion";
37662
+ import { Command as Command69 } from "clipanion";
37173
37663
  async function withTimeout(work, ms) {
37174
37664
  let timer;
37175
37665
  try {
@@ -37241,7 +37731,7 @@ Run: m8t companion install
37241
37731
  }
37242
37732
  var CompanionStatusCommand = class extends M8tCommand {
37243
37733
  static paths = [["companion", "status"]];
37244
- static usage = Command68.Usage({
37734
+ static usage = Command69.Usage({
37245
37735
  description: "Verify the installed desktop companion without launching it."
37246
37736
  });
37247
37737
  async executeCommand() {
@@ -37255,7 +37745,7 @@ var CompanionStatusCommand = class extends M8tCommand {
37255
37745
  };
37256
37746
 
37257
37747
  // src/commands/companion/repair.ts
37258
- import { Command as Command69, Option as Option65 } from "clipanion";
37748
+ import { Command as Command70, Option as Option66 } from "clipanion";
37259
37749
  async function runCompanionRepairCommand(stdout, repair) {
37260
37750
  const state = await repair();
37261
37751
  if (state.state === "not-released") {
@@ -37274,10 +37764,10 @@ async function runCompanionRepairCommand(stdout, repair) {
37274
37764
  }
37275
37765
  var CompanionRepairCommand = class extends M8tCommand {
37276
37766
  static paths = [["companion", "repair"]];
37277
- static usage = Command69.Usage({
37767
+ static usage = Command70.Usage({
37278
37768
  description: "Restore the desktop companions and start-at-login state."
37279
37769
  });
37280
- resourceGroup = Option65.String("--resource-group", {
37770
+ resourceGroup = Option66.String("--resource-group", {
37281
37771
  description: "Which deployment to bind to, when the subscription holds more than one."
37282
37772
  });
37283
37773
  async executeCommand() {
@@ -37295,7 +37785,7 @@ var CompanionRepairCommand = class extends M8tCommand {
37295
37785
  };
37296
37786
 
37297
37787
  // src/commands/companion/uninstall.ts
37298
- import { Command as Command70 } from "clipanion";
37788
+ import { Command as Command71 } from "clipanion";
37299
37789
  async function runCompanionUninstallCommand(stdout, uninstall) {
37300
37790
  const state = await uninstall();
37301
37791
  if (state.state !== "not-installed") {
@@ -37307,7 +37797,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
37307
37797
  }
37308
37798
  var CompanionUninstallCommand = class extends M8tCommand {
37309
37799
  static paths = [["companion", "uninstall"]];
37310
- static usage = Command70.Usage({
37800
+ static usage = Command71.Usage({
37311
37801
  description: "Remove only this user's desktop companion installation."
37312
37802
  });
37313
37803
  async executeCommand() {
@@ -37368,6 +37858,7 @@ cli.register(PlatformConvergeCommand);
37368
37858
  cli.register(PlatformClearIntentCommand);
37369
37859
  cli.register(PlatformRequestUpdateCommand);
37370
37860
  cli.register(PlatformSeedStampCommand);
37861
+ cli.register(PlatformGatewayAdoptCommand);
37371
37862
  cli.register(PlatformPolicyCommand);
37372
37863
  cli.register(PlatformEnableCostReportCommand);
37373
37864
  cli.register(PlatformEmailCommand);