@m8t-stack/cli 0.2.105 → 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.105";
1457
+ var CLI_VERSION = "0.2.106";
1458
1458
 
1459
1459
  // src/lib/render-error.ts
1460
1460
  init_errors();
@@ -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));
@@ -21421,6 +21474,414 @@ function voiceSecretParam(read, gatewayName) {
21421
21474
  };
21422
21475
  }
21423
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
+
21424
21885
  // src/lib/platform-converge.ts
21425
21886
  var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
21426
21887
  function seedSetRev(manifest) {
@@ -21484,8 +21945,10 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
21484
21945
  continue;
21485
21946
  }
21486
21947
  const from = cur?.tag ?? null;
21487
- if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag });
21488
- 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 });
21489
21952
  else skipped.push({ component, reason: "up-to-date" });
21490
21953
  }
21491
21954
  return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
@@ -21628,10 +22091,20 @@ function seedStamp(prior, manifest, plan) {
21628
22091
  };
21629
22092
  }
21630
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
+ }
21631
22105
  const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
21632
22106
  const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
21633
22107
  const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
21634
- const digest = ctx.manifest.components.gateway.digest;
21635
22108
  const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to, toDigest: digest });
21636
22109
  switch (plan.kind) {
21637
22110
  case "refuse-byoc":
@@ -22130,10 +22603,10 @@ async function resolveExecutorAgentName(args) {
22130
22603
  }
22131
22604
 
22132
22605
  // src/lib/platform-infra-params.ts
22133
- import { TableClient as TableClient3 } from "@azure/data-tables";
22606
+ import { TableClient as TableClient4 } from "@azure/data-tables";
22134
22607
  async function openInfraParamsTable(opts) {
22135
22608
  const { tableEndpoint } = await discoverStampStorage(opts);
22136
- return new TableClient3(tableEndpoint, "Metadata", opts.credential);
22609
+ return new TableClient4(tableEndpoint, "Metadata", opts.credential);
22137
22610
  }
22138
22611
  async function readInfraParams(client) {
22139
22612
  try {
@@ -23006,73 +23479,6 @@ function resolveHeadlessContextFromEnv(env) {
23006
23479
  };
23007
23480
  }
23008
23481
 
23009
- // src/lib/apply-request-store.ts
23010
- import { TableClient as TableClient4 } from "@azure/data-tables";
23011
- async function openApplyTable(opts) {
23012
- const { tableEndpoint } = await discoverStampStorage(opts);
23013
- return new TableClient4(tableEndpoint, "Metadata", opts.credential);
23014
- }
23015
- function hasStatus(e, n) {
23016
- return e.statusCode === n;
23017
- }
23018
- async function readApplyRequest(client) {
23019
- try {
23020
- const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
23021
- return { ...entityToApplyRequest(row), etag: row.etag };
23022
- } catch (e) {
23023
- if (hasStatus(e, 404)) return null;
23024
- throw e;
23025
- }
23026
- }
23027
- async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
23028
- const cur = await readApplyRequest(client);
23029
- if (!cur) return null;
23030
- const leaseExpired = isInFlight(cur.status) && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
23031
- const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
23032
- if (!claimable) return null;
23033
- const nowMs = Date.parse(nowIso);
23034
- const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
23035
- if (leaseExpired && cur.attempt >= MAX_LEASE_TAKEOVERS) {
23036
- const held = {
23037
- ...cur,
23038
- status: "held",
23039
- breaker: "held",
23040
- result: {
23041
- appliedVersion: cur.result?.appliedVersion ?? null,
23042
- 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`
23043
- },
23044
- updatedAt: nowIso
23045
- };
23046
- try {
23047
- await client.updateEntity({ ...applyRequestToEntity(held), etag: cur.etag }, "Merge", { etag: cur.etag });
23048
- } catch (e) {
23049
- if (!hasStatus(e, 412)) throw e;
23050
- }
23051
- return null;
23052
- }
23053
- const claimed = {
23054
- ...cur,
23055
- status: "claimed",
23056
- claimedBy: executionId,
23057
- leaseUntil,
23058
- ...leaseExpired ? { attempt: cur.attempt + 1 } : {},
23059
- updatedAt: nowIso
23060
- };
23061
- try {
23062
- await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
23063
- return claimed;
23064
- } catch (e) {
23065
- if (hasStatus(e, 412)) return null;
23066
- throw e;
23067
- }
23068
- }
23069
- async function patchApplyRequest(client, mutate) {
23070
- const cur = await readApplyRequest(client);
23071
- if (!cur) return;
23072
- const next = mutate(cur);
23073
- await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
23074
- }
23075
-
23076
23482
  // src/lib/rail-preconditions.ts
23077
23483
  init_errors();
23078
23484
  function assertBakedContentPresent(repoRoot) {
@@ -23233,7 +23639,7 @@ async function runGate(args, applied, budgetMs) {
23233
23639
  }
23234
23640
  async function rollbackOrHold(args, notify, error) {
23235
23641
  const target = args.plan.targetVersion;
23236
- const rollbackTarget = args.stamp.previousPlatformVersion;
23642
+ const rollbackTarget = args.preApplyVersion;
23237
23643
  const current = await readBreaker(args.client);
23238
23644
  const next = nextBreaker(current);
23239
23645
  if (next === "held") {
@@ -23497,6 +23903,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
23497
23903
  plan,
23498
23904
  ctx: applyCtx,
23499
23905
  stamp: stamp ?? seedStampFor(plan),
23906
+ preApplyVersion: stamp?.platformVersion ?? null,
23500
23907
  manifest,
23501
23908
  credential: ctx.credential,
23502
23909
  endpoint: ctx.endpoint,
@@ -23722,8 +24129,8 @@ var PlatformRequestUpdateCommand = class extends M8tCommand {
23722
24129
  description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
23723
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.",
23724
24131
  examples: [
23725
- ["Request an update", "m8t platform request-update --version 0.7.3 --resource-group rg-m8t --subscription <id>"],
23726
- ["Request it and wait", "m8t platform request-update --version 0.7.3 --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>"]
23727
24134
  ]
23728
24135
  });
23729
24136
  version = Option35.String("--version", { description: "The platform version to converge to." });
@@ -24021,9 +24428,87 @@ var PlatformSeedStampCommand = class extends M8tCommand {
24021
24428
  }
24022
24429
  };
24023
24430
 
24024
- // src/commands/platform/policy.ts
24431
+ // src/commands/platform/gateway-adopt.ts
24025
24432
  import { Command as Command40, Option as Option37 } from "clipanion";
24026
- 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";
24027
24512
  init_errors();
24028
24513
 
24029
24514
  // src/lib/platform-policy.ts
@@ -24078,16 +24563,16 @@ async function readPolicy(opts) {
24078
24563
  // src/commands/platform/policy.ts
24079
24564
  var PlatformPolicyCommand = class extends M8tCommand {
24080
24565
  static paths = [["platform", "policy"]];
24081
- static usage = Command40.Usage({
24566
+ static usage = Command41.Usage({
24082
24567
  description: "Show or set how this install handles available platform updates.",
24083
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.",
24084
24569
  examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
24085
24570
  });
24086
- set = Option37.String("--set", { description: "notify-only | auto-critical | auto-all" });
24087
- subscription = Option37.String("--subscription");
24088
- resourceGroup = Option37.String("--resource-group");
24089
- miClientId = Option37.String("--mi-client-id");
24090
- 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");
24091
24576
  async executeCommand() {
24092
24577
  const mode = resolveOutputMode(this.output, this.context.stdout);
24093
24578
  const need = (v, flag) => {
@@ -24100,7 +24585,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24100
24585
  this.context.stderr.write(`${m}
24101
24586
  `);
24102
24587
  };
24103
- 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();
24104
24589
  const ctx = {
24105
24590
  credential: credential2,
24106
24591
  subscriptionId: need(this.subscription, "--subscription"),
@@ -24133,7 +24618,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24133
24618
  };
24134
24619
 
24135
24620
  // src/commands/platform/enable-cost-report.ts
24136
- import { Command as Command41, Option as Option38 } from "clipanion";
24621
+ import { Command as Command42, Option as Option39 } from "clipanion";
24137
24622
 
24138
24623
  // src/lib/wire-gateway-acs.ts
24139
24624
  init_rbac();
@@ -24173,18 +24658,18 @@ async function wireGatewayForAcs(args) {
24173
24658
  init_errors();
24174
24659
  var PlatformEnableCostReportCommand = class extends M8tCommand {
24175
24660
  static paths = [["platform", "enable-cost-report"]];
24176
- static usage = Command41.Usage({
24661
+ static usage = Command42.Usage({
24177
24662
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
24178
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')."
24179
24664
  });
24180
- subscription = Option38.String("--subscription");
24181
- resourceGroup = Option38.String("--resource-group", {
24665
+ subscription = Option39.String("--subscription");
24666
+ resourceGroup = Option39.String("--resource-group", {
24182
24667
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24183
24668
  });
24184
- acsEndpoint = Option38.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
24185
- acsSender = Option38.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
24186
- acsResourceId = Option38.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
24187
- 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");
24188
24673
  async executeCommand() {
24189
24674
  const mode = resolveOutputMode(
24190
24675
  this.output,
@@ -24258,13 +24743,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
24258
24743
  };
24259
24744
 
24260
24745
  // src/commands/platform/email.ts
24261
- import { Command as Command42, Option as Option39 } from "clipanion";
24262
- 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";
24263
24748
  import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
24264
24749
  init_errors();
24265
24750
  var PlatformEmailCommand = class extends M8tCommand {
24266
24751
  static paths = [["platform", "email"]];
24267
- static usage = Command42.Usage({
24752
+ static usage = Command43.Usage({
24268
24753
  description: "Turn outbound email (advisor handoffs) on or off for this install.",
24269
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.",
24270
24755
  examples: [
@@ -24272,19 +24757,19 @@ var PlatformEmailCommand = class extends M8tCommand {
24272
24757
  ["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
24273
24758
  ]
24274
24759
  });
24275
- state = Option39.String({ required: true, name: "on|off" });
24276
- subscription = Option39.String("--subscription");
24277
- 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", {
24278
24763
  description: "m8t resource group, to disambiguate in a multi-deployment subscription."
24279
24764
  });
24280
- agent = Option39.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
24281
- 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", {
24282
24767
  description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
24283
24768
  });
24284
- endpoint = Option39.String("--endpoint", {
24769
+ endpoint = Option40.String("--endpoint", {
24285
24770
  description: "Foundry project endpoint, to disambiguate a subscription holding several."
24286
24771
  });
24287
- output = Option39.String("--output");
24772
+ output = Option40.String("--output");
24288
24773
  async executeCommand() {
24289
24774
  const wanted = this.state.trim().toLowerCase();
24290
24775
  if (wanted !== "on" && wanted !== "off") {
@@ -24308,7 +24793,7 @@ var PlatformEmailCommand = class extends M8tCommand {
24308
24793
  });
24309
24794
  const { resourceGroup } = parseContainerAppResourceId(ctx.containerAppResourceId);
24310
24795
  const subscriptionId = ctx.subscriptionId;
24311
- const credential2 = new DefaultAzureCredential22();
24796
+ const credential2 = new DefaultAzureCredential23();
24312
24797
  const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
24313
24798
  if (stamp === null) {
24314
24799
  throw new LocalCliError({
@@ -24434,45 +24919,45 @@ var PlatformEmailCommand = class extends M8tCommand {
24434
24919
  };
24435
24920
 
24436
24921
  // src/commands/platform/enable-auto-update.ts
24437
- import { Command as Command43, Option as Option40 } from "clipanion";
24922
+ import { Command as Command44, Option as Option41 } from "clipanion";
24438
24923
  import { confirm as confirm6 } from "@inquirer/prompts";
24439
- import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
24924
+ import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
24440
24925
  init_errors();
24441
24926
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24442
24927
  static paths = [["platform", "enable-auto-update"]];
24443
- static usage = Command43.Usage({
24928
+ static usage = Command44.Usage({
24444
24929
  category: "Platform",
24445
24930
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
24446
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."
24447
24932
  });
24448
- subscription = Option40.String("--subscription");
24449
- resourceGroup = Option40.String("--resource-group", {
24933
+ subscription = Option41.String("--subscription");
24934
+ resourceGroup = Option41.String("--resource-group", {
24450
24935
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24451
24936
  });
24452
- suffix = Option40.String("--suffix", {
24937
+ suffix = Option41.String("--suffix", {
24453
24938
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
24454
24939
  });
24455
- installerImage = Option40.String("--installer-image", {
24940
+ installerImage = Option41.String("--installer-image", {
24456
24941
  required: true,
24457
24942
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
24458
24943
  });
24459
- updateCron = Option40.String("--update-cron", {
24944
+ updateCron = Option41.String("--update-cron", {
24460
24945
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
24461
24946
  });
24462
- channelUrl = Option40.String("--channel-url", {
24947
+ channelUrl = Option41.String("--channel-url", {
24463
24948
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
24464
24949
  });
24465
- location = Option40.String("--location", {
24950
+ location = Option41.String("--location", {
24466
24951
  description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
24467
24952
  });
24468
- foundryTracing = Option40.String("--foundry-tracing", {
24953
+ foundryTracing = Option41.String("--foundry-tracing", {
24469
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)."
24470
24955
  });
24471
- endpoint = Option40.String("--endpoint", {
24956
+ endpoint = Option41.String("--endpoint", {
24472
24957
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
24473
24958
  });
24474
- yes = Option40.Boolean("--yes", false);
24475
- output = Option40.String("--output");
24959
+ yes = Option41.Boolean("--yes", false);
24960
+ output = Option41.String("--output");
24476
24961
  async executeCommand() {
24477
24962
  const mode = resolveOutputMode(
24478
24963
  this.output,
@@ -24491,7 +24976,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24491
24976
  resourceGroup: this.resourceGroup
24492
24977
  });
24493
24978
  const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
24494
- const credential2 = new DefaultAzureCredential23();
24979
+ const credential2 = new DefaultAzureCredential24();
24495
24980
  const account = await getAzAccount();
24496
24981
  const subscriptionId = this.subscription ?? account.subscriptionId;
24497
24982
  const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
@@ -24653,7 +25138,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24653
25138
  };
24654
25139
 
24655
25140
  // src/commands/deploy.ts
24656
- import { Command as Command44, Option as Option41 } from "clipanion";
25141
+ import { Command as Command45, Option as Option42 } from "clipanion";
24657
25142
 
24658
25143
  // src/lib/app-reg.ts
24659
25144
  init_esm();
@@ -25217,15 +25702,15 @@ function classifyWhatIf(changes) {
25217
25702
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
25218
25703
  var DeployCommand = class extends M8tCommand {
25219
25704
  static paths = [["deploy"]];
25220
- static usage = Command44.Usage({
25705
+ static usage = Command45.Usage({
25221
25706
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
25222
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."
25223
25708
  });
25224
- subscription = Option41.String("--subscription");
25225
- resourceGroup = Option41.String("--resource-group", "rg-m8t-stack");
25226
- location = Option41.String("--location", "eastus");
25227
- suffix = Option41.String("--suffix", "");
25228
- 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);
25229
25714
  // Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
25230
25715
  // from-zero case. It exists because the gateway and the voice relay do NOT
25231
25716
  // always run the same image: a converge preserves a BYOC gateway on its own
@@ -25233,28 +25718,28 @@ var DeployCommand = class extends M8tCommand {
25233
25718
  // (`--what-if`) that can only express one image therefore reports the other
25234
25719
  // app as drift on every single run, forever — which is exactly what the
25235
25720
  // infra-drift gate did from 2026-08-11.
25236
- gatewayImageRef = Option41.String("--gateway-image-ref", "");
25237
- acrPullIdentity = Option41.String("--acrpull-identity");
25238
- acrResourceId = Option41.String("--acr-resource-id");
25239
- foundryEndpoint = Option41.String("--foundry-endpoint");
25240
- foundryResourceId = Option41.String("--foundry-resource-id");
25241
- 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");
25242
25727
  // project | account | skip (bicep default: project)
25243
- clientId = Option41.String("--client-id");
25244
- whatIf = Option41.Boolean("--what-if", false);
25728
+ clientId = Option42.String("--client-id");
25729
+ whatIf = Option42.Boolean("--what-if", false);
25245
25730
  // Only meaningful with --what-if. Routes the comparison through the
25246
25731
  // value-free renderers (see ./lib/whatif-redact.js) instead of the default
25247
25732
  // before/after renderer. Defaults false so a local, interactive run keeps
25248
25733
  // showing values — that is the whole diagnostic point of --what-if.
25249
25734
  // Automation that forwards this output anywhere non-private (a CI log, an
25250
25735
  // issue) MUST pass --redact.
25251
- redact = Option41.Boolean("--redact", false);
25252
- output = Option41.String("--output");
25736
+ redact = Option42.Boolean("--redact", false);
25737
+ output = Option42.String("--output");
25253
25738
  // Subscription-scoped role assignments. Omitted ⇒ the template default (true).
25254
25739
  // Pass false when deploying as a principal scoped to the resource group only:
25255
25740
  // it cannot deploy at subscription scope, and those assignments persist
25256
25741
  // idempotently from the initial deployment anyway.
25257
- assignSubscriptionRoles = Option41.String("--assign-subscription-roles");
25742
+ assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
25258
25743
  /**
25259
25744
  * The installer image the updater Container-Apps Job runs.
25260
25745
  *
@@ -25265,23 +25750,23 @@ var DeployCommand = class extends M8tCommand {
25265
25750
  * than that — the comparison proposes REMOVING an updater job that exists and
25266
25751
  * should, and reports it as drift on every single run.
25267
25752
  */
25268
- installerImage = Option41.String("--installer-image");
25753
+ installerImage = Option42.String("--installer-image");
25269
25754
  // Referee — all optional, undefined by default ⇒ the bicep defaults
25270
25755
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
25271
25756
  // empty). Only pass these when explicitly enabling the referee exam stack.
25272
- gatewayCpu = Option41.String("--gateway-cpu");
25273
- gatewayMemory = Option41.String("--gateway-memory");
25274
- refereeEnabled = Option41.String("--referee-enabled");
25275
- refereeBrainRepos = Option41.String("--referee-brain-repos");
25276
- refereeFeedRepo = Option41.String("--referee-feed-repo");
25277
- refereeInstallationId = Option41.String("--referee-installation-id");
25278
- refereeWebhookHmacKvUri = Option41.String("--referee-webhook-hmac-kv-uri");
25279
- examKvUri = Option41.String("--exam-kv-uri");
25280
- examLaWorkspaceId = Option41.String("--exam-la-workspace-id");
25281
- brainEvalDeployment = Option41.String("--brain-eval-deployment");
25282
- brainAppLogin = Option41.String("--brain-app-login");
25283
- refereeCheckpointDir = Option41.String("--referee-checkpoint-dir");
25284
- 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");
25285
25770
  async executeCommand() {
25286
25771
  const mode = resolveOutputMode(
25287
25772
  this.output,
@@ -25465,7 +25950,7 @@ var DeployCommand = class extends M8tCommand {
25465
25950
 
25466
25951
  // src/commands/eval/skill.ts
25467
25952
  import { spawnSync as spawnSync4 } from "child_process";
25468
- import { Command as Command45, Option as Option42 } from "clipanion";
25953
+ import { Command as Command46, Option as Option43 } from "clipanion";
25469
25954
  init_errors();
25470
25955
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
25471
25956
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -25490,14 +25975,14 @@ function parseVerdict(stdout) {
25490
25975
  }
25491
25976
  var EvalSkillCommand = class extends M8tCommand {
25492
25977
  static paths = [["eval", "skill"]];
25493
- static usage = Command45.Usage({
25978
+ static usage = Command46.Usage({
25494
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)."
25495
25980
  });
25496
- candidate = Option42.String();
25497
- skillsDir = Option42.String("--skills-dir");
25498
- noJudge = Option42.Boolean("--no-judge", false);
25499
- deployment = Option42.String("--deployment");
25500
- 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");
25501
25986
  executeCommand() {
25502
25987
  return Promise.resolve(this._runCommand());
25503
25988
  }
@@ -25556,7 +26041,7 @@ var EvalSkillCommand = class extends M8tCommand {
25556
26041
  import { spawnSync as spawnSync5 } from "child_process";
25557
26042
  import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
25558
26043
  import { join as join30 } from "path";
25559
- import { Command as Command46, Option as Option43 } from "clipanion";
26044
+ import { Command as Command47, Option as Option44 } from "clipanion";
25560
26045
  init_errors();
25561
26046
  init_esm();
25562
26047
  function parseArmToken(tok, opts) {
@@ -25786,24 +26271,24 @@ function buildPlan(args) {
25786
26271
  }
25787
26272
  var EvalExamCommand = class extends M8tCommand {
25788
26273
  static paths = [["eval", "exam"]];
25789
- static usage = Command46.Usage({
26274
+ static usage = Command47.Usage({
25790
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."
25791
26276
  });
25792
- worker = Option43.String();
25793
- arms = Option43.String("--arms");
25794
- taskSet = Option43.String("--task-set");
25795
- examType = Option43.String("--exam-type");
25796
- skill = Option43.String("--skill");
25797
- reps = Option43.String("-n,--reps");
25798
- probes = Option43.String("--probes");
25799
- pool = Option43.String("--pool");
25800
- out = Option43.String("--out");
25801
- dryRun = Option43.Boolean("--dry-run", false);
25802
- keepArms = Option43.Boolean("--keep-arms", false);
25803
- allowStub = Option43.Boolean("--allow-stub", false);
25804
- deployment = Option43.String("--deployment");
25805
- output = Option43.String("--output");
25806
- 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");
25807
26292
  async executeCommand() {
25808
26293
  await Promise.resolve();
25809
26294
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -25918,10 +26403,10 @@ var EvalExamCommand = class extends M8tCommand {
25918
26403
  };
25919
26404
 
25920
26405
  // src/commands/version.ts
25921
- import { Command as Command47, Option as Option44 } from "clipanion";
26406
+ import { Command as Command48, Option as Option45 } from "clipanion";
25922
26407
  var VersionCommand = class extends M8tCommand {
25923
26408
  static paths = [["version"], ["--version"], ["-v"]];
25924
- static usage = Command47.Usage({
26409
+ static usage = Command48.Usage({
25925
26410
  description: "Print the CLI version.",
25926
26411
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
25927
26412
  examples: [
@@ -25929,8 +26414,8 @@ var VersionCommand = class extends M8tCommand {
25929
26414
  ["Print as JSON", "$0 version --output json"]
25930
26415
  ]
25931
26416
  });
25932
- output = Option44.String("--output", { description: "pretty | json | auto (default)" });
25933
- verbose = Option44.Boolean("--verbose", false);
26417
+ output = Option45.String("--output", { description: "pretty | json | auto (default)" });
26418
+ verbose = Option45.Boolean("--verbose", false);
25934
26419
  executeCommand() {
25935
26420
  const mode = resolveOutputMode(
25936
26421
  this.output ?? "auto",
@@ -25961,18 +26446,18 @@ var VersionCommand = class extends M8tCommand {
25961
26446
  };
25962
26447
 
25963
26448
  // src/commands/whoami.ts
25964
- import { Command as Command48, Option as Option45 } from "clipanion";
26449
+ import { Command as Command49, Option as Option46 } from "clipanion";
25965
26450
  var WhoamiCommand = class extends M8tCommand {
25966
26451
  static paths = [["whoami"]];
25967
- static usage = Command48.Usage({
26452
+ static usage = Command49.Usage({
25968
26453
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
25969
26454
  });
25970
- output = Option45.String("--output");
25971
- verbose = Option45.Boolean("--verbose", false);
25972
- subscription = Option45.String("--subscription", {
26455
+ output = Option46.String("--output");
26456
+ verbose = Option46.Boolean("--verbose", false);
26457
+ subscription = Option46.String("--subscription", {
25973
26458
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
25974
26459
  });
25975
- resourceGroup = Option45.String("--resource-group", {
26460
+ resourceGroup = Option46.String("--resource-group", {
25976
26461
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
25977
26462
  });
25978
26463
  async executeCommand() {
@@ -26037,7 +26522,7 @@ var WhoamiCommand = class extends M8tCommand {
26037
26522
  };
26038
26523
 
26039
26524
  // src/commands/status.ts
26040
- import { Command as Command49, Option as Option46 } from "clipanion";
26525
+ import { Command as Command50, Option as Option47 } from "clipanion";
26041
26526
 
26042
26527
  // src/lib/azd.ts
26043
26528
  init_errors();
@@ -26102,10 +26587,10 @@ async function resolveLocalContext() {
26102
26587
  // src/commands/status.ts
26103
26588
  var StatusCommand = class extends M8tCommand {
26104
26589
  static paths = [["status"]];
26105
- static usage = Command49.Usage({
26590
+ static usage = Command50.Usage({
26106
26591
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
26107
26592
  });
26108
- output = Option46.String("--output");
26593
+ output = Option47.String("--output");
26109
26594
  async executeCommand() {
26110
26595
  const mode = resolveOutputMode(
26111
26596
  this.output,
@@ -26143,8 +26628,8 @@ var StatusCommand = class extends M8tCommand {
26143
26628
  };
26144
26629
 
26145
26630
  // src/commands/doctor.ts
26146
- import { Command as Command50, Option as Option47 } from "clipanion";
26147
- 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";
26148
26633
  import * as fs30 from "fs";
26149
26634
  import * as os12 from "os";
26150
26635
  import * as path33 from "path";
@@ -26691,12 +27176,12 @@ function probeLegacyStateDir() {
26691
27176
  }
26692
27177
  var DoctorCommand = class extends M8tCommand {
26693
27178
  static paths = [["doctor"]];
26694
- static usage = Command50.Usage({
27179
+ static usage = Command51.Usage({
26695
27180
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
26696
27181
  });
26697
- output = Option47.String("--output");
26698
- agent = Option47.String("--agent");
26699
- resourceGroup = Option47.String("--resource-group", {
27182
+ output = Option48.String("--output");
27183
+ agent = Option48.String("--agent");
27184
+ resourceGroup = Option48.String("--resource-group", {
26700
27185
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
26701
27186
  });
26702
27187
  async executeCommand() {
@@ -26769,7 +27254,7 @@ var DoctorCommand = class extends M8tCommand {
26769
27254
  let kvStatus = 0;
26770
27255
  if (kv) {
26771
27256
  try {
26772
- const token = await new DefaultAzureCredential24().getToken("https://vault.azure.net/.default");
27257
+ const token = await new DefaultAzureCredential25().getToken("https://vault.azure.net/.default");
26773
27258
  const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
26774
27259
  headers: { Authorization: `Bearer ${token.token}` }
26775
27260
  });
@@ -26788,7 +27273,7 @@ var DoctorCommand = class extends M8tCommand {
26788
27273
  emit(checkModelQuota(deployments, usages));
26789
27274
  checking("outbound email");
26790
27275
  try {
26791
- const credential2 = new DefaultAzureCredential24();
27276
+ const credential2 = new DefaultAzureCredential25();
26792
27277
  const outcome = platformRg && platformSub ? await readStampOutcome({ credential: credential2, subscriptionId: platformSub, resourceGroup: platformRg }).catch(
26793
27278
  () => ({ source: "unreadable" })
26794
27279
  ) : { source: "unreadable" };
@@ -26843,7 +27328,7 @@ var DoctorCommand = class extends M8tCommand {
26843
27328
  if (typeof this.agent === "string" && this.agent) {
26844
27329
  checking(`delivery grant for ${this.agent}`);
26845
27330
  try {
26846
- const credential2 = new DefaultAzureCredential24();
27331
+ const credential2 = new DefaultAzureCredential25();
26847
27332
  const cur = await getAgentVersion({
26848
27333
  credential: credential2,
26849
27334
  projectEndpoint: foundry.projectEndpoint,
@@ -26886,11 +27371,11 @@ var DoctorCommand = class extends M8tCommand {
26886
27371
  };
26887
27372
 
26888
27373
  // src/commands/prereqs.ts
26889
- import { Command as Command51, Option as Option48 } from "clipanion";
27374
+ import { Command as Command52, Option as Option49 } from "clipanion";
26890
27375
  init_errors();
26891
27376
 
26892
27377
  // src/lib/prereq-deps.ts
26893
- import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
27378
+ import { DefaultAzureCredential as DefaultAzureCredential26 } from "@azure/identity";
26894
27379
 
26895
27380
  // src/lib/bootstrap-preflight.ts
26896
27381
  var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
@@ -27002,7 +27487,7 @@ function buildPrereqDeps(opts = {}) {
27002
27487
  probeRedirectUri,
27003
27488
  fixFoundryAccess,
27004
27489
  fixKeyVaultAccess,
27005
- credential: () => credentialSingleton2 ??= new DefaultAzureCredential25()
27490
+ credential: () => credentialSingleton2 ??= new DefaultAzureCredential26()
27006
27491
  };
27007
27492
  }
27008
27493
 
@@ -27592,7 +28077,7 @@ function renderVerdict(v) {
27592
28077
  }
27593
28078
  var PrereqsCommand = class extends M8tCommand {
27594
28079
  static paths = [["prereqs"]];
27595
- static usage = Command51.Usage({
28080
+ static usage = Command52.Usage({
27596
28081
  description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
27597
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.",
27598
28083
  examples: [
@@ -27603,15 +28088,15 @@ var PrereqsCommand = class extends M8tCommand {
27603
28088
  ["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
27604
28089
  ]
27605
28090
  });
27606
- fix = Option48.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
27607
- for_ = Option48.String("--for", { description: "UPN or object id of another person. Usage phase only." });
27608
- phase = Option48.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
27609
- 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)." });
27610
- model = Option48.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
27611
- clientId = Option48.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
27612
- subscription = Option48.String("--subscription");
27613
- resourceGroup = Option48.String("--resource-group");
27614
- 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");
27615
28100
  async executeCommand() {
27616
28101
  const mode = resolveOutputMode(this.output, this.context.stdout);
27617
28102
  const json = mode === "json";
@@ -27667,7 +28152,7 @@ var PrereqsCommand = class extends M8tCommand {
27667
28152
  };
27668
28153
 
27669
28154
  // src/commands/switch.ts
27670
- import { Command as Command52, Option as Option49 } from "clipanion";
28155
+ import { Command as Command53, Option as Option50 } from "clipanion";
27671
28156
 
27672
28157
  // src/lib/profiles.ts
27673
28158
  import * as fs31 from "fs/promises";
@@ -27807,14 +28292,14 @@ async function profileSwitch(name, asName) {
27807
28292
  init_errors();
27808
28293
  var SwitchCommand = class extends M8tCommand {
27809
28294
  static paths = [["switch"]];
27810
- static usage = Command52.Usage({
28295
+ static usage = Command53.Usage({
27811
28296
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
27812
28297
  });
27813
- profile = Option49.String({ required: false });
27814
- subscription = Option49.String("--subscription");
27815
- list = Option49.Boolean("--list", false);
27816
- as = Option49.String("--as");
27817
- 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");
27818
28303
  async executeCommand() {
27819
28304
  const mode = resolveOutputMode(
27820
28305
  this.output,
@@ -27871,7 +28356,7 @@ var SwitchCommand = class extends M8tCommand {
27871
28356
 
27872
28357
  // src/commands/open.ts
27873
28358
  import { spawn as spawn5 } from "child_process";
27874
- import { Command as Command53, Option as Option50 } from "clipanion";
28359
+ import { Command as Command54, Option as Option51 } from "clipanion";
27875
28360
 
27876
28361
  // src/lib/open-targets.ts
27877
28362
  init_errors();
@@ -27917,14 +28402,14 @@ function openUrl(url) {
27917
28402
  }
27918
28403
  var OpenCommand = class extends M8tCommand {
27919
28404
  static paths = [["open"]];
27920
- static usage = Command53.Usage({
28405
+ static usage = Command54.Usage({
27921
28406
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
27922
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)."
27923
28408
  });
27924
- target = Option50.String({ required: false });
27925
- print = Option50.Boolean("--print", false);
27926
- output = Option50.String("--output");
27927
- 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", {
27928
28413
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
27929
28414
  });
27930
28415
  async executeCommand() {
@@ -27968,7 +28453,7 @@ var OpenCommand = class extends M8tCommand {
27968
28453
  };
27969
28454
 
27970
28455
  // src/commands/dream/run.ts
27971
- import { Command as Command54, Option as Option51 } from "clipanion";
28456
+ import { Command as Command55, Option as Option52 } from "clipanion";
27972
28457
  import { AzureCliCredential } from "@azure/identity";
27973
28458
  import { TableClient as TableClient7 } from "@azure/data-tables";
27974
28459
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
@@ -30368,28 +30853,28 @@ function redactTranscripts(input) {
30368
30853
  }
30369
30854
  var DreamRunCommand = class extends M8tCommand {
30370
30855
  static paths = [["dream", "run"]];
30371
- static usage = Command54.Usage({
30856
+ static usage = Command55.Usage({
30372
30857
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
30373
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."
30374
30859
  });
30375
- worker = Option51.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
30860
+ worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
30376
30861
  // Opting IN to the side effects, rather than opting out of them. This command's
30377
30862
  // own help has always described a dry run, but the bare invocation used to take
30378
30863
  // the live branch — a real model call and a commit to the brain repo — so anyone
30379
30864
  // acting on `--help` got the opposite of what they read.
30380
- live = Option51.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
30381
- dryRun = Option51.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
30382
- since = Option51.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
30383
- reset = Option51.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
30384
- 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, {
30385
30870
  description: "Print transcript bodies (default: metadata only)."
30386
30871
  });
30387
- subscription = Option51.String("--subscription");
30388
- endpoint = Option51.String("--endpoint");
30389
- storageAccount = Option51.String("--storage-account", {
30872
+ subscription = Option52.String("--subscription");
30873
+ endpoint = Option52.String("--endpoint");
30874
+ storageAccount = Option52.String("--storage-account", {
30390
30875
  description: "Ledger storage account name (skips tag-based discovery)"
30391
30876
  });
30392
- output = Option51.String("--output");
30877
+ output = Option52.String("--output");
30393
30878
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
30394
30879
  deps;
30395
30880
  async executeCommand() {
@@ -30740,7 +31225,7 @@ function defaultDeps(overrides) {
30740
31225
 
30741
31226
  // src/commands/conversations/sweep.ts
30742
31227
  import { createHash as createHash5 } from "crypto";
30743
- import { Command as Command55, Option as Option52 } from "clipanion";
31228
+ import { Command as Command56, Option as Option53 } from "clipanion";
30744
31229
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
30745
31230
  import { TableClient as TableClient8 } from "@azure/data-tables";
30746
31231
  import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
@@ -30866,7 +31351,7 @@ function defaultDeps2() {
30866
31351
  }
30867
31352
  var ConversationsSweepCommand = class extends M8tCommand {
30868
31353
  static paths = [["conversations", "sweep"]];
30869
- static usage = Command55.Usage({
31354
+ static usage = Command56.Usage({
30870
31355
  category: "Conversations",
30871
31356
  description: "Delete expired public-visitor conversations (dry run by default)",
30872
31357
  details: `
@@ -30882,22 +31367,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
30882
31367
  ["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
30883
31368
  ]
30884
31369
  });
30885
- doDelete = Option52.Boolean("--delete", false, {
31370
+ doDelete = Option53.Boolean("--delete", false, {
30886
31371
  description: "Perform deletions (without this flag the command only reports)"
30887
31372
  });
30888
- principal = Option52.String("--principal", {
31373
+ principal = Option53.String("--principal", {
30889
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)"
30890
31375
  });
30891
- max = Option52.String("--max", "200", { description: "Maximum deletions per run" });
30892
- graceDays = Option52.String("--grace-days", "14", {
31376
+ max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
31377
+ graceDays = Option53.String("--grace-days", "14", {
30893
31378
  description: "Days past the 30-day key life before a conversation is eligible"
30894
31379
  });
30895
- lookbackDays = Option52.String("--lookback-days", "180", {
31380
+ lookbackDays = Option53.String("--lookback-days", "180", {
30896
31381
  description: "How far back to scan ledger activity for candidates"
30897
31382
  });
30898
- subscription = Option52.String("--subscription", { description: "Azure subscription id override" });
30899
- endpoint = Option52.String("--endpoint", { description: "Foundry project endpoint override" });
30900
- 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", {
30901
31386
  description: "Ledger storage account name (skips tag-based discovery)"
30902
31387
  });
30903
31388
  deps = defaultDeps2();
@@ -31025,7 +31510,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
31025
31510
  };
31026
31511
 
31027
31512
  // src/commands/foundry/create.ts
31028
- import { Command as Command56, Option as Option53 } from "clipanion";
31513
+ import { Command as Command57, Option as Option54 } from "clipanion";
31029
31514
 
31030
31515
  // src/lib/foundry-create.ts
31031
31516
  init_errors();
@@ -31263,7 +31748,7 @@ async function createFoundryProject(args) {
31263
31748
  init_errors();
31264
31749
  var FoundryCreateCommand = class extends M8tCommand {
31265
31750
  static paths = [["foundry", "create"]];
31266
- static usage = Command56.Usage({
31751
+ static usage = Command57.Usage({
31267
31752
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
31268
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).",
31269
31754
  examples: [
@@ -31272,16 +31757,16 @@ var FoundryCreateCommand = class extends M8tCommand {
31272
31757
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
31273
31758
  ]
31274
31759
  });
31275
- resourceGroup = Option53.String("--resource-group");
31276
- location = Option53.String("--location");
31277
- account = Option53.String("--account");
31278
- project = Option53.String("--project", "m8t");
31279
- model = Option53.String("--model", "gpt-4.1-mini");
31280
- modelVersion = Option53.String("--model-version", "2025-04-14");
31281
- capacity = Option53.String("--capacity", "50");
31282
- subscription = Option53.String("--subscription");
31283
- skipQuotaCheck = Option53.Boolean("--skip-quota-check", false);
31284
- 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");
31285
31770
  async executeCommand() {
31286
31771
  const mode = resolveOutputMode(
31287
31772
  this.output,
@@ -31353,22 +31838,22 @@ var FoundryCreateCommand = class extends M8tCommand {
31353
31838
  };
31354
31839
 
31355
31840
  // src/commands/foundry/await-ready.ts
31356
- import { Command as Command57, Option as Option54 } from "clipanion";
31841
+ import { Command as Command58, Option as Option55 } from "clipanion";
31357
31842
  import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
31358
31843
  init_errors();
31359
31844
  var FoundryAwaitReadyCommand = class extends M8tCommand {
31360
31845
  static paths = [["foundry", "await-ready"]];
31361
- static usage = Command57.Usage({
31846
+ static usage = Command58.Usage({
31362
31847
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
31363
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.",
31364
31849
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
31365
31850
  });
31366
- endpoint = Option54.String("--endpoint");
31367
- consecutive = Option54.String("--consecutive", "3");
31368
- attempts = Option54.String("--attempts", "60");
31369
- interval = Option54.String("--interval", "5");
31370
- subscription = Option54.String("--subscription");
31371
- 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");
31372
31857
  async executeCommand() {
31373
31858
  const mode = resolveOutputMode(this.output, this.context.stdout);
31374
31859
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -31402,7 +31887,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
31402
31887
  };
31403
31888
 
31404
31889
  // src/commands/bootstrap/preflight.ts
31405
- import { Command as Command58, Option as Option55 } from "clipanion";
31890
+ import { Command as Command59, Option as Option56 } from "clipanion";
31406
31891
 
31407
31892
  // ../../packages/telemetry-contract/artifact/tier-map.ts
31408
31893
  var EVENT_TIERS = {
@@ -31454,7 +31939,7 @@ function preflightRenderable(results) {
31454
31939
  }
31455
31940
  var BootstrapPreflightCommand = class extends M8tCommand {
31456
31941
  static paths = [["bootstrap", "preflight"]];
31457
- static usage = Command58.Usage({
31942
+ static usage = Command59.Usage({
31458
31943
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
31459
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.",
31460
31945
  examples: [
@@ -31463,9 +31948,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
31463
31948
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
31464
31949
  ]
31465
31950
  });
31466
- clientId = Option55.String("--client-id");
31467
- subscription = Option55.String("--subscription");
31468
- location = Option55.String("--location", {
31951
+ clientId = Option56.String("--client-id");
31952
+ subscription = Option56.String("--subscription");
31953
+ location = Option56.String("--location", {
31469
31954
  description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
31470
31955
  });
31471
31956
  async executeCommand() {
@@ -31565,7 +32050,7 @@ ${colors.error(" " + why)}
31565
32050
  import * as fs34 from "fs";
31566
32051
  import * as os15 from "os";
31567
32052
  import * as path37 from "path";
31568
- import { Command as Command59, Option as Option56 } from "clipanion";
32053
+ import { Command as Command60, Option as Option57 } from "clipanion";
31569
32054
  init_errors();
31570
32055
 
31571
32056
  // src/lib/bootstrap-mi.ts
@@ -31930,12 +32415,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
31930
32415
  // src/commands/bootstrap/launch.ts
31931
32416
  var DEFAULT_RG = "rg-m8t-stack";
31932
32417
  var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
31933
- var DEFAULT_INSTALLER_TAG = "v0.1.73";
32418
+ var DEFAULT_INSTALLER_TAG = "v0.1.74";
31934
32419
  var ACI_NAME = "m8t-installer";
31935
32420
  var MI_NAME = "m8t-installer-mi";
31936
32421
  var BootstrapLaunchCommand = class extends M8tCommand {
31937
32422
  static paths = [["bootstrap", "launch"]];
31938
- static usage = Command59.Usage({
32423
+ static usage = Command60.Usage({
31939
32424
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
31940
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.",
31941
32426
  examples: [
@@ -31946,31 +32431,31 @@ var BootstrapLaunchCommand = class extends M8tCommand {
31946
32431
  ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
31947
32432
  ]
31948
32433
  });
31949
- location = Option56.String("--location");
31950
- resourceGroup = Option56.String("--resource-group");
31951
- clientId = Option56.String("--client-id");
31952
- subscription = Option56.String("--subscription");
31953
- 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");
31954
32439
  // Full image ref override (registry + repo + tag) — an escape hatch when the
31955
32440
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
31956
32441
  // Wins over --installer-tag / the pinned default.
31957
- installerImage = Option56.String("--installer-image");
31958
- gatewayImageRef = Option56.String("--gateway-image-ref");
31959
- githubAppCreds = Option56.String("--github-app-creds");
31960
- contactEmail = Option56.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
31961
- 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." });
31962
32447
  // Value-carrying on purpose: a bare --force would be cargo-culted into
31963
32448
  // runbooks and harness prompts and erode the protection, whereas a faithful
31964
32449
  // paste can never accidentally carry the victim group's name. It AUTHORIZES
31965
32450
  // the target; --resource-group is what CHOOSES it.
31966
- 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." });
31967
- org = Option56.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
31968
- 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." });
31969
32454
  // The opt-out TELEMETRY.md names. Without it there is no way to decline at
31970
32455
  // install time — the ACI env is built entirely from these options, so a
31971
32456
  // founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
31972
32457
  // privacy notice that documents a control has to be a control that exists.
31973
- 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)." });
31974
32459
  async executeCommand() {
31975
32460
  const location = typeof this.location === "string" ? this.location : void 0;
31976
32461
  if (!location) {
@@ -32132,7 +32617,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32132
32617
  };
32133
32618
 
32134
32619
  // src/commands/bootstrap/status.ts
32135
- import { Command as Command61, Option as Option58 } from "clipanion";
32620
+ import { Command as Command62, Option as Option59 } from "clipanion";
32136
32621
  init_errors();
32137
32622
 
32138
32623
  // src/lib/bootstrap-aci-state.ts
@@ -34309,7 +34794,7 @@ async function uninstallCompanion(options) {
34309
34794
  // src/commands/companion/install.ts
34310
34795
  import * as os19 from "os";
34311
34796
  import * as path43 from "path";
34312
- import { Command as Command60, Option as Option57 } from "clipanion";
34797
+ import { Command as Command61, Option as Option58 } from "clipanion";
34313
34798
 
34314
34799
  // src/lib/companion-channel.ts
34315
34800
  async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
@@ -34440,7 +34925,7 @@ Version: ${state.version}
34440
34925
  }
34441
34926
  var CompanionInstallCommand = class extends M8tCommand {
34442
34927
  static paths = [["companion", "install"]];
34443
- static usage = Command60.Usage({
34928
+ static usage = Command61.Usage({
34444
34929
  description: "Install the desktop companions for this user from the release channel.",
34445
34930
  examples: [
34446
34931
  ["Install the released build", "$0 companion install"],
@@ -34450,10 +34935,10 @@ var CompanionInstallCommand = class extends M8tCommand {
34450
34935
  ]
34451
34936
  ]
34452
34937
  });
34453
- from = Option57.String("--from", {
34938
+ from = Option58.String("--from", {
34454
34939
  description: "A locally staged build directory instead of the released one."
34455
34940
  });
34456
- resourceGroup = Option57.String("--resource-group", {
34941
+ resourceGroup = Option58.String("--resource-group", {
34457
34942
  description: "Which deployment to bind to, when the subscription holds more than one."
34458
34943
  });
34459
34944
  async executeCommand() {
@@ -34668,7 +35153,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
34668
35153
  // runbooks and shakedown recipes that a founder may already be part-way
34669
35154
  // through — it prints a deprecation notice and does the right thing.
34670
35155
  static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
34671
- static usage = Command61.Usage({
35156
+ static usage = Command62.Usage({
34672
35157
  description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
34673
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.",
34674
35159
  examples: [
@@ -34677,12 +35162,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
34677
35162
  ["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
34678
35163
  ]
34679
35164
  });
34680
- watch = Option58.Boolean("--watch", false);
34681
- output = Option58.String("--output");
34682
- repoRoot = Option58.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
34683
- finalize = Option58.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
34684
- subscription = Option58.String("--subscription");
34685
- 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");
34686
35171
  async executeCommand() {
34687
35172
  const state = await readBootstrapState();
34688
35173
  if (!state) {
@@ -34815,7 +35300,7 @@ function formatStatus(d) {
34815
35300
  }
34816
35301
 
34817
35302
  // src/commands/bootstrap/reap.ts
34818
- import { Command as Command62, Option as Option59 } from "clipanion";
35303
+ import { Command as Command63, Option as Option60 } from "clipanion";
34819
35304
  init_errors();
34820
35305
 
34821
35306
  // src/lib/bootstrap-reap.ts
@@ -34909,7 +35394,7 @@ async function reapInstaller(opts) {
34909
35394
  // src/commands/bootstrap/reap.ts
34910
35395
  var BootstrapReapCommand = class extends M8tCommand {
34911
35396
  static paths = [["bootstrap", "reap"]];
34912
- static usage = Command62.Usage({
35397
+ static usage = Command63.Usage({
34913
35398
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
34914
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.",
34915
35400
  examples: [
@@ -34919,9 +35404,9 @@ var BootstrapReapCommand = class extends M8tCommand {
34919
35404
  ["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
34920
35405
  ]
34921
35406
  });
34922
- force = Option59.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
34923
- sweepOrphans = Option59.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
34924
- 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." });
34925
35410
  async executeCommand() {
34926
35411
  if (this.sweepOrphans === true) {
34927
35412
  const { subscriptionId: sub } = await getAzAccount();
@@ -35015,7 +35500,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
35015
35500
  };
35016
35501
 
35017
35502
  // src/commands/bootstrap/ui.ts
35018
- import { Command as Command63, Option as Option60 } from "clipanion";
35503
+ import { Command as Command64, Option as Option61 } from "clipanion";
35019
35504
 
35020
35505
  // src/lib/bootstrap-ui.ts
35021
35506
  import * as fs40 from "fs";
@@ -35090,7 +35575,7 @@ function renderDeprecationNotice() {
35090
35575
  }
35091
35576
  var BootstrapUiCommand = class extends M8tCommand {
35092
35577
  static paths = [["bootstrap", "ui"]];
35093
- static usage = Command63.Usage({
35578
+ static usage = Command64.Usage({
35094
35579
  description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
35095
35580
  details: [
35096
35581
  "The local onboarding chat has been retired. Your details are collected by",
@@ -35110,14 +35595,14 @@ var BootstrapUiCommand = class extends M8tCommand {
35110
35595
  // Accepted and ignored, deliberately: removing them would turn an old script's
35111
35596
  // harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
35112
35597
  // the whole surface when the rewrite lands.
35113
- repoRoot = Option60.String("--repo-root", { description: "Ignored (deprecated)." });
35114
- port = Option60.String("--port", "3000", { description: "Ignored (deprecated)." });
35115
- endpoint = Option60.String("--endpoint", { description: "Ignored (deprecated)." });
35116
- prepOnly = Option60.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
35117
- skipInstall = Option60.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
35118
- foreground = Option60.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
35119
- voice = Option60.Boolean("--voice", false, { description: "Ignored (deprecated)." });
35120
- 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, {
35121
35606
  description: "Shut down a local chat UI left running by an earlier version of this command."
35122
35607
  });
35123
35608
  // Not `async`: there is nothing left to await. Everything this command used to
@@ -35139,7 +35624,7 @@ var BootstrapUiCommand = class extends M8tCommand {
35139
35624
 
35140
35625
  // src/commands/bootstrap/profile.ts
35141
35626
  import * as readline3 from "readline/promises";
35142
- import { Command as Command64, Option as Option61 } from "clipanion";
35627
+ import { Command as Command65, Option as Option62 } from "clipanion";
35143
35628
 
35144
35629
  // src/lib/profile-collect.ts
35145
35630
  init_errors();
@@ -35308,7 +35793,7 @@ async function openChatInvite(deps = {}) {
35308
35793
  // src/commands/bootstrap/profile.ts
35309
35794
  var BootstrapProfileCommand = class extends M8tCommand {
35310
35795
  static paths = [["bootstrap", "profile"]];
35311
- static usage = Command64.Usage({
35796
+ static usage = Command65.Usage({
35312
35797
  description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
35313
35798
  details: [
35314
35799
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
@@ -35328,12 +35813,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
35328
35813
  ["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
35329
35814
  ]
35330
35815
  });
35331
- founderEmail = Option61.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
35332
- advisorName = Option61.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
35333
- advisorEmail = Option61.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
35334
- noAdvisor = Option61.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
35335
- noChat = Option61.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
35336
- 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)." });
35337
35822
  async executeCommand() {
35338
35823
  const stdin = this.context.stdin;
35339
35824
  const stdout = this.context.stdout;
@@ -35426,10 +35911,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
35426
35911
  };
35427
35912
 
35428
35913
  // src/commands/bootstrap/seed-profile.ts
35429
- import { Command as Command65, Option as Option62 } from "clipanion";
35914
+ import { Command as Command66, Option as Option63 } from "clipanion";
35430
35915
  var BootstrapSeedProfileCommand = class extends M8tCommand {
35431
35916
  static paths = [["bootstrap", "seed-profile"]];
35432
- static usage = Command65.Usage({
35917
+ static usage = Command66.Usage({
35433
35918
  description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
35434
35919
  details: [
35435
35920
  "Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
@@ -35446,11 +35931,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
35446
35931
  ["Seed now (idempotent)", "$0 bootstrap seed-profile"]
35447
35932
  ]
35448
35933
  });
35449
- endpoint = Option62.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
35450
- brain = Option62.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
35451
- watch = Option62.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
35452
- timeout = Option62.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
35453
- 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");
35454
35939
  async executeCommand() {
35455
35940
  const ctx = await resolveSeedContext({
35456
35941
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -35538,7 +36023,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
35538
36023
  import * as fs41 from "fs";
35539
36024
  import * as os22 from "os";
35540
36025
  import * as path46 from "path";
35541
- import { Command as Command66, Option as Option63 } from "clipanion";
36026
+ import { Command as Command67, Option as Option64 } from "clipanion";
35542
36027
  init_errors();
35543
36028
 
35544
36029
  // src/lib/telemetry-enroll.ts
@@ -35620,7 +36105,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
35620
36105
  }
35621
36106
  var TelemetryEnrollCommand = class extends M8tCommand {
35622
36107
  static paths = [["telemetry", "enroll"]];
35623
- static usage = Command66.Usage({
36108
+ static usage = Command67.Usage({
35624
36109
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
35625
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.",
35626
36111
  examples: [
@@ -35628,11 +36113,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
35628
36113
  ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
35629
36114
  ]
35630
36115
  });
35631
- company = Option63.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
35632
- contactEmail = Option63.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
35633
- subscription = Option63.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
35634
- resourceGroup = Option63.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
35635
- 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." });
35636
36121
  async executeCommand() {
35637
36122
  const account = await getAzAccount();
35638
36123
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
@@ -35682,7 +36167,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
35682
36167
  };
35683
36168
 
35684
36169
  // src/commands/companion/bridge.ts
35685
- import { Command as Command67, Option as Option64 } from "clipanion";
36170
+ import { Command as Command68, Option as Option65 } from "clipanion";
35686
36171
 
35687
36172
  // ../../packages/companion-bridge-contract/src/index.ts
35688
36173
  var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
@@ -37149,14 +37634,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
37149
37634
  return 3;
37150
37635
  }
37151
37636
  }
37152
- var CompanionBridgeCommand = class extends Command67 {
37637
+ var CompanionBridgeCommand = class extends Command68 {
37153
37638
  static paths = [["companion", "_bridge"]];
37154
37639
  /**
37155
37640
  * One process serving many requests instead of one per request, so the
37156
37641
  * session keeps its authenticated context between them. A CLI predating the
37157
37642
  * flag rejects it outright, which is how the app knows to fall back.
37158
37643
  */
37159
- serve = Option64.Boolean("--serve", false);
37644
+ serve = Option65.Boolean("--serve", false);
37160
37645
  async execute() {
37161
37646
  if (this.serve) {
37162
37647
  return runCompanionBridgeServe(
@@ -37174,7 +37659,7 @@ var CompanionBridgeCommand = class extends Command67 {
37174
37659
  };
37175
37660
 
37176
37661
  // src/commands/companion/status.ts
37177
- import { Command as Command68 } from "clipanion";
37662
+ import { Command as Command69 } from "clipanion";
37178
37663
  async function withTimeout(work, ms) {
37179
37664
  let timer;
37180
37665
  try {
@@ -37246,7 +37731,7 @@ Run: m8t companion install
37246
37731
  }
37247
37732
  var CompanionStatusCommand = class extends M8tCommand {
37248
37733
  static paths = [["companion", "status"]];
37249
- static usage = Command68.Usage({
37734
+ static usage = Command69.Usage({
37250
37735
  description: "Verify the installed desktop companion without launching it."
37251
37736
  });
37252
37737
  async executeCommand() {
@@ -37260,7 +37745,7 @@ var CompanionStatusCommand = class extends M8tCommand {
37260
37745
  };
37261
37746
 
37262
37747
  // src/commands/companion/repair.ts
37263
- import { Command as Command69, Option as Option65 } from "clipanion";
37748
+ import { Command as Command70, Option as Option66 } from "clipanion";
37264
37749
  async function runCompanionRepairCommand(stdout, repair) {
37265
37750
  const state = await repair();
37266
37751
  if (state.state === "not-released") {
@@ -37279,10 +37764,10 @@ async function runCompanionRepairCommand(stdout, repair) {
37279
37764
  }
37280
37765
  var CompanionRepairCommand = class extends M8tCommand {
37281
37766
  static paths = [["companion", "repair"]];
37282
- static usage = Command69.Usage({
37767
+ static usage = Command70.Usage({
37283
37768
  description: "Restore the desktop companions and start-at-login state."
37284
37769
  });
37285
- resourceGroup = Option65.String("--resource-group", {
37770
+ resourceGroup = Option66.String("--resource-group", {
37286
37771
  description: "Which deployment to bind to, when the subscription holds more than one."
37287
37772
  });
37288
37773
  async executeCommand() {
@@ -37300,7 +37785,7 @@ var CompanionRepairCommand = class extends M8tCommand {
37300
37785
  };
37301
37786
 
37302
37787
  // src/commands/companion/uninstall.ts
37303
- import { Command as Command70 } from "clipanion";
37788
+ import { Command as Command71 } from "clipanion";
37304
37789
  async function runCompanionUninstallCommand(stdout, uninstall) {
37305
37790
  const state = await uninstall();
37306
37791
  if (state.state !== "not-installed") {
@@ -37312,7 +37797,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
37312
37797
  }
37313
37798
  var CompanionUninstallCommand = class extends M8tCommand {
37314
37799
  static paths = [["companion", "uninstall"]];
37315
- static usage = Command70.Usage({
37800
+ static usage = Command71.Usage({
37316
37801
  description: "Remove only this user's desktop companion installation."
37317
37802
  });
37318
37803
  async executeCommand() {
@@ -37373,6 +37858,7 @@ cli.register(PlatformConvergeCommand);
37373
37858
  cli.register(PlatformClearIntentCommand);
37374
37859
  cli.register(PlatformRequestUpdateCommand);
37375
37860
  cli.register(PlatformSeedStampCommand);
37861
+ cli.register(PlatformGatewayAdoptCommand);
37376
37862
  cli.register(PlatformPolicyCommand);
37377
37863
  cli.register(PlatformEnableCostReportCommand);
37378
37864
  cli.register(PlatformEmailCommand);