@m8t-stack/cli 0.2.105 → 0.2.107

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.107";
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,420 @@ 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
+ if (stamp.platformVersion !== args.rollbackPlatformVersion) {
21792
+ throw new LocalCliError({
21793
+ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED",
21794
+ message: `The installed platform changed after preflight; expected rollback ${args.rollbackPlatformVersion}, found ${stamp.platformVersion}.`
21795
+ });
21796
+ }
21797
+ const existingRequest = await readApplyRequest(client);
21798
+ const live = await readLiveGatewayDeployment(args.gatewayResourceId);
21799
+ const topologyError = legacyGatewayTopologyError(live, args.gatewayResourceId, args.subscriptionId);
21800
+ if (topologyError) {
21801
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_REFUSED", message: `Gateway adoption refused: ${topologyError}.` });
21802
+ }
21803
+ if (live.image !== args.expectedImage) {
21804
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway image changed; expected '${args.expectedImage}', found '${live.image}'.` });
21805
+ }
21806
+ const digest = await resolveLegacyAcrDigest(live, args.subscriptionId);
21807
+ if (digest !== args.expectedDigest) {
21808
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CHANGED", message: `The live gateway digest changed; expected '${args.expectedDigest}', found '${digest}'.` });
21809
+ }
21810
+ const expectedIdentity = live.userAssignedIdentityIds[0];
21811
+ if (args.expectedAcrResourceId.trim() === "" || typeof expectedIdentity !== "string") {
21812
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id and the generated pull identity are required." });
21813
+ }
21814
+ const imageHost = parseImageRef2(live.image).repo.split("/")[0].split(".")[0];
21815
+ const acrName = args.expectedAcrResourceId.split("/").filter(Boolean).at(-1) ?? "";
21816
+ const expectedAcrId = `/subscriptions/${args.subscriptionId}/resourceGroups/${args.resourceGroup}/providers/Microsoft.ContainerRegistry/registries/${imageHost}`;
21817
+ if (imageHost.toLowerCase() !== acrName.toLowerCase() || lowerId(args.expectedAcrResourceId) !== lowerId(expectedAcrId)) {
21818
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_INVALID", message: "--expected-acr-resource-id does not match the live image registry." });
21819
+ }
21820
+ const existing = stamp.components.gateway.adoption;
21821
+ const samePending = isSamePendingAdoption(
21822
+ existing,
21823
+ live.image,
21824
+ digest,
21825
+ args.expectedAcrResourceId,
21826
+ stamp.components.gateway.state,
21827
+ args.targetGatewayTag,
21828
+ args.targetGatewayDigest,
21829
+ args.rollbackGatewayTag,
21830
+ args.rollbackGatewayDigest
21831
+ );
21832
+ if (existing && !samePending) {
21833
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_CONFLICT", message: "The gateway already carries a different adoption record." });
21834
+ }
21835
+ if (existingRequest && blocksNewIntent(existingRequest.status)) {
21836
+ if (samePending && existingRequest.status === "pending" && existingRequest.target === args.target) {
21837
+ return { outcome: "already-adopted", image: live.image, digest };
21838
+ }
21839
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_IN_PROGRESS", message: `An update request for ${existingRequest.target} is already ${existingRequest.status}.` });
21840
+ }
21841
+ const now = args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString();
21842
+ const nextStamp = {
21843
+ ...stamp,
21844
+ updatedAt: now,
21845
+ components: {
21846
+ ...stamp.components,
21847
+ gateway: {
21848
+ ...stamp.components.gateway,
21849
+ tag: parseImageRef2(live.image).tag ?? stamp.components.gateway.tag,
21850
+ digest,
21851
+ state: "managed",
21852
+ adoption: existing ?? {
21853
+ version: 1,
21854
+ status: "pending",
21855
+ expectedSourceRef: live.image,
21856
+ expectedSourceDigest: digest,
21857
+ expectedForwardDigest: args.targetGatewayDigest,
21858
+ expectedForwardTag: args.targetGatewayTag,
21859
+ expectedRollbackDigest: args.rollbackGatewayDigest,
21860
+ expectedRollbackTag: args.rollbackGatewayTag,
21861
+ expectedAcrResourceId: args.expectedAcrResourceId,
21862
+ expectedAcrPullIdentityResourceId: expectedIdentity,
21863
+ authorizedAt: now
21864
+ }
21865
+ }
21866
+ }
21867
+ };
21868
+ const intent = newIntent(args.target, { source: "policy", identity: args.operatorIdentity, at: now }, now);
21869
+ const stampWire = stampToEntity(nextStamp);
21870
+ const stampEntity = {
21871
+ ...stampWire,
21872
+ partitionKey: String(stampWire.partitionKey),
21873
+ rowKey: String(stampWire.rowKey)
21874
+ };
21875
+ const applyEntity = applyRequestToEntity(intent);
21876
+ const stampAction = ["update", stampEntity, "Replace", { etag: stampEtag }];
21877
+ const requestAction = existingRequest ? ["update", applyEntity, "Replace", { etag: existingRequest.etag }] : ["create", applyEntity];
21878
+ try {
21879
+ await client.submitTransaction([stampAction, requestAction]);
21880
+ } catch (e) {
21881
+ const status = e.statusCode;
21882
+ if (status === 409 || status === 412) {
21883
+ 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 });
21884
+ }
21885
+ throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_WRITE_FAILED", message: "Could not atomically record gateway adoption and its update request.", cause: e });
21886
+ }
21887
+ args.onProgress?.(`recorded digest-bound gateway adoption and queued update to ${args.target}.`);
21888
+ return { outcome: samePending ? "already-adopted" : "adopted", image: live.image, digest };
21889
+ }
21890
+
21424
21891
  // src/lib/platform-converge.ts
21425
21892
  var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas", "brainSeeds"];
21426
21893
  function seedSetRev(manifest) {
@@ -21484,8 +21951,10 @@ function diffPlan(manifest, stamp, tree, opts = {}) {
21484
21951
  continue;
21485
21952
  }
21486
21953
  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 });
21954
+ const adoption = component === "gateway" ? cur?.adoption : void 0;
21955
+ const withAdoption = adoption ? { gatewayAdoption: adoption } : {};
21956
+ if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag, ...withAdoption });
21957
+ else if (from !== img.tag || adoption?.status === "pending") actions.push({ component, reason: "changed", from, to: img.tag, ...withAdoption });
21489
21958
  else skipped.push({ component, reason: "up-to-date" });
21490
21959
  }
21491
21960
  return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
@@ -21628,10 +22097,20 @@ function seedStamp(prior, manifest, plan) {
21628
22097
  };
21629
22098
  }
21630
22099
  async function applyGatewayImage(a, ctx, gatewayResourceId) {
22100
+ const digest = ctx.manifest.components.gateway.digest;
22101
+ if (a.gatewayAdoption) {
22102
+ const adoption = await rollAdoptedGateway({
22103
+ gatewayResourceId,
22104
+ adoption: a.gatewayAdoption,
22105
+ targetTag: a.to,
22106
+ targetDigest: assertGatewayDigest(ctx.manifest.components.gateway),
22107
+ onProgress: ctx.onProgress
22108
+ });
22109
+ return { tag: a.to, digest, state: "managed", adoption };
22110
+ }
21631
22111
  const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
21632
22112
  const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
21633
22113
  const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
21634
- const digest = ctx.manifest.components.gateway.digest;
21635
22114
  const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to, toDigest: digest });
21636
22115
  switch (plan.kind) {
21637
22116
  case "refuse-byoc":
@@ -22130,10 +22609,10 @@ async function resolveExecutorAgentName(args) {
22130
22609
  }
22131
22610
 
22132
22611
  // src/lib/platform-infra-params.ts
22133
- import { TableClient as TableClient3 } from "@azure/data-tables";
22612
+ import { TableClient as TableClient4 } from "@azure/data-tables";
22134
22613
  async function openInfraParamsTable(opts) {
22135
22614
  const { tableEndpoint } = await discoverStampStorage(opts);
22136
- return new TableClient3(tableEndpoint, "Metadata", opts.credential);
22615
+ return new TableClient4(tableEndpoint, "Metadata", opts.credential);
22137
22616
  }
22138
22617
  async function readInfraParams(client) {
22139
22618
  try {
@@ -23006,73 +23485,6 @@ function resolveHeadlessContextFromEnv(env) {
23006
23485
  };
23007
23486
  }
23008
23487
 
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
23488
  // src/lib/rail-preconditions.ts
23077
23489
  init_errors();
23078
23490
  function assertBakedContentPresent(repoRoot) {
@@ -23233,7 +23645,7 @@ async function runGate(args, applied, budgetMs) {
23233
23645
  }
23234
23646
  async function rollbackOrHold(args, notify, error) {
23235
23647
  const target = args.plan.targetVersion;
23236
- const rollbackTarget = args.stamp.previousPlatformVersion;
23648
+ const rollbackTarget = args.preApplyVersion;
23237
23649
  const current = await readBreaker(args.client);
23238
23650
  const next = nextBreaker(current);
23239
23651
  if (next === "held") {
@@ -23497,6 +23909,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
23497
23909
  plan,
23498
23910
  ctx: applyCtx,
23499
23911
  stamp: stamp ?? seedStampFor(plan),
23912
+ preApplyVersion: stamp?.platformVersion ?? null,
23500
23913
  manifest,
23501
23914
  credential: ctx.credential,
23502
23915
  endpoint: ctx.endpoint,
@@ -23722,8 +24135,8 @@ var PlatformRequestUpdateCommand = class extends M8tCommand {
23722
24135
  description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
23723
24136
  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
24137
  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>"]
24138
+ ["Request an update", "m8t platform request-update --version 0.7.4 --resource-group rg-m8t --subscription <id>"],
24139
+ ["Request it and wait", "m8t platform request-update --version 0.7.4 --wait --resource-group rg-m8t --subscription <id>"]
23727
24140
  ]
23728
24141
  });
23729
24142
  version = Option35.String("--version", { description: "The platform version to converge to." });
@@ -24021,9 +24434,91 @@ var PlatformSeedStampCommand = class extends M8tCommand {
24021
24434
  }
24022
24435
  };
24023
24436
 
24024
- // src/commands/platform/policy.ts
24437
+ // src/commands/platform/gateway-adopt.ts
24025
24438
  import { Command as Command40, Option as Option37 } from "clipanion";
24026
- import { DefaultAzureCredential as DefaultAzureCredential21, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
24439
+ import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
24440
+ init_errors();
24441
+ var PlatformGatewayAdoptCommand = class extends M8tCommand {
24442
+ static paths = [["platform", "gateway", "adopt"]];
24443
+ static usage = Command40.Usage({
24444
+ description: "Explicitly authorize one legacy m8t ACR gateway for managed, digest-pinned updates.",
24445
+ 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."
24446
+ });
24447
+ subscription = Option37.String("--subscription");
24448
+ resourceGroup = Option37.String("--resource-group");
24449
+ target = Option37.String("--target", { description: "Published platform target whose installer contains adoption support." });
24450
+ expectedImage = Option37.String("--expected-image", { description: "Exact live legacy ACR image ref." });
24451
+ expectedDigest = Option37.String("--expected-digest", { description: "Exact sha256 digest resolved from that ACR image." });
24452
+ expectedAcrResourceId = Option37.String("--expected-acr-resource-id", { description: "Exact ARM id of the legacy ACR." });
24453
+ output = Option37.String("--output");
24454
+ async executeCommand() {
24455
+ const need = (v, flag) => {
24456
+ if (typeof v !== "string" || v.trim() === "") throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_ARG_MISSING", message: `${flag} is required.` });
24457
+ return v.trim();
24458
+ };
24459
+ const mode = resolveOutputMode(this.output, this.context.stdout);
24460
+ const account = await getAzAccount();
24461
+ const subscriptionId = need(this.subscription ?? account.subscriptionId, "--subscription");
24462
+ const gw = await resolveGatewayContext({ subscriptionId, resourceGroup: this.resourceGroup, interactive: mode !== "json" });
24463
+ const parsed = parseContainerAppResourceId(gw.containerAppResourceId);
24464
+ const resourceGroup = need(this.resourceGroup ?? parsed.resourceGroup, "--resource-group");
24465
+ const target = need(this.target, "--target");
24466
+ const manifest = await fetchManifest({ channel: true, version: target });
24467
+ const assertTarget = (requested, actual) => {
24468
+ const normalized = requested.replace(/^platform-v/, "");
24469
+ if (actual.platform.version.replace(/^platform-v/, "") !== normalized || actual.platform.tag !== platformTag(normalized)) {
24470
+ throw new LocalCliError({
24471
+ code: "PLATFORM_GATEWAY_ADOPTION_TARGET_MISMATCH",
24472
+ message: `Requested target ${requested}, but the release service returned ${actual.platform.version}.`
24473
+ });
24474
+ }
24475
+ };
24476
+ assertTarget(target, manifest);
24477
+ if (compareSemver(`v${manifest.components.installer.version}`, "v0.1.74") < 0 || compareSemver(`v${manifest.components.cli.recommended}`, "v0.2.107") < 0) {
24478
+ throw new LocalCliError({
24479
+ code: "PLATFORM_GATEWAY_ADOPTION_TARGET_TOO_OLD",
24480
+ message: `Platform target ${target} predates digest-bound legacy gateway adoption support.`,
24481
+ hint: "Choose a published platform target whose installer is at least 0.1.74 and recommended CLI is at least 0.2.107."
24482
+ });
24483
+ }
24484
+ const credential2 = new DefaultAzureCredential21();
24485
+ const installed2 = await readStampOutcome({ credential: credential2, subscriptionId, resourceGroup });
24486
+ if (installed2.source !== "explicit") {
24487
+ throw new LocalCliError({
24488
+ code: "PLATFORM_GATEWAY_ADOPTION_NO_ROLLBACK",
24489
+ message: "The installed platform stamp is absent or unreadable; refusing a gateway ownership transition without its exact rollback target."
24490
+ });
24491
+ }
24492
+ const rollbackVersion = installed2.stamp.platformVersion;
24493
+ const rollbackManifest = await fetchManifest({ channel: true, version: rollbackVersion });
24494
+ assertTarget(rollbackVersion, rollbackManifest);
24495
+ const result2 = await adoptLegacyGateway({
24496
+ credential: credential2,
24497
+ subscriptionId,
24498
+ resourceGroup,
24499
+ gatewayResourceId: gw.containerAppResourceId,
24500
+ target,
24501
+ expectedImage: need(this.expectedImage, "--expected-image"),
24502
+ expectedDigest: need(this.expectedDigest, "--expected-digest"),
24503
+ expectedAcrResourceId: need(this.expectedAcrResourceId, "--expected-acr-resource-id"),
24504
+ targetGatewayTag: manifest.components.gateway.tag,
24505
+ targetGatewayDigest: manifest.components.gateway.digest,
24506
+ rollbackGatewayTag: rollbackManifest.components.gateway.tag,
24507
+ rollbackGatewayDigest: rollbackManifest.components.gateway.digest,
24508
+ rollbackPlatformVersion: rollbackVersion,
24509
+ operatorIdentity: await getCallerObjectId(),
24510
+ onProgress: (m) => this.context.stderr.write(`${m}
24511
+ `)
24512
+ });
24513
+ this.context.stdout.write(mode === "json" ? renderJson(result2) + "\n" : `${result2.outcome}: ${result2.image}@${result2.digest}
24514
+ `);
24515
+ return 0;
24516
+ }
24517
+ };
24518
+
24519
+ // src/commands/platform/policy.ts
24520
+ import { Command as Command41, Option as Option38 } from "clipanion";
24521
+ import { DefaultAzureCredential as DefaultAzureCredential22, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
24027
24522
  init_errors();
24028
24523
 
24029
24524
  // src/lib/platform-policy.ts
@@ -24078,16 +24573,16 @@ async function readPolicy(opts) {
24078
24573
  // src/commands/platform/policy.ts
24079
24574
  var PlatformPolicyCommand = class extends M8tCommand {
24080
24575
  static paths = [["platform", "policy"]];
24081
- static usage = Command40.Usage({
24576
+ static usage = Command41.Usage({
24082
24577
  description: "Show or set how this install handles available platform updates.",
24083
24578
  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
24579
  examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
24085
24580
  });
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");
24581
+ set = Option38.String("--set", { description: "notify-only | auto-critical | auto-all" });
24582
+ subscription = Option38.String("--subscription");
24583
+ resourceGroup = Option38.String("--resource-group");
24584
+ miClientId = Option38.String("--mi-client-id");
24585
+ output = Option38.String("--output");
24091
24586
  async executeCommand() {
24092
24587
  const mode = resolveOutputMode(this.output, this.context.stdout);
24093
24588
  const need = (v, flag) => {
@@ -24100,7 +24595,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24100
24595
  this.context.stderr.write(`${m}
24101
24596
  `);
24102
24597
  };
24103
- const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential21();
24598
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential22();
24104
24599
  const ctx = {
24105
24600
  credential: credential2,
24106
24601
  subscriptionId: need(this.subscription, "--subscription"),
@@ -24133,7 +24628,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24133
24628
  };
24134
24629
 
24135
24630
  // src/commands/platform/enable-cost-report.ts
24136
- import { Command as Command41, Option as Option38 } from "clipanion";
24631
+ import { Command as Command42, Option as Option39 } from "clipanion";
24137
24632
 
24138
24633
  // src/lib/wire-gateway-acs.ts
24139
24634
  init_rbac();
@@ -24173,18 +24668,18 @@ async function wireGatewayForAcs(args) {
24173
24668
  init_errors();
24174
24669
  var PlatformEnableCostReportCommand = class extends M8tCommand {
24175
24670
  static paths = [["platform", "enable-cost-report"]];
24176
- static usage = Command41.Usage({
24671
+ static usage = Command42.Usage({
24177
24672
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
24178
24673
  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
24674
  });
24180
- subscription = Option38.String("--subscription");
24181
- resourceGroup = Option38.String("--resource-group", {
24675
+ subscription = Option39.String("--subscription");
24676
+ resourceGroup = Option39.String("--resource-group", {
24182
24677
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24183
24678
  });
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");
24679
+ acsEndpoint = Option39.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
24680
+ acsSender = Option39.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
24681
+ acsResourceId = Option39.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
24682
+ output = Option39.String("--output");
24188
24683
  async executeCommand() {
24189
24684
  const mode = resolveOutputMode(
24190
24685
  this.output,
@@ -24258,13 +24753,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
24258
24753
  };
24259
24754
 
24260
24755
  // src/commands/platform/email.ts
24261
- import { Command as Command42, Option as Option39 } from "clipanion";
24262
- import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
24756
+ import { Command as Command43, Option as Option40 } from "clipanion";
24757
+ import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
24263
24758
  import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
24264
24759
  init_errors();
24265
24760
  var PlatformEmailCommand = class extends M8tCommand {
24266
24761
  static paths = [["platform", "email"]];
24267
- static usage = Command42.Usage({
24762
+ static usage = Command43.Usage({
24268
24763
  description: "Turn outbound email (advisor handoffs) on or off for this install.",
24269
24764
  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
24765
  examples: [
@@ -24272,19 +24767,19 @@ var PlatformEmailCommand = class extends M8tCommand {
24272
24767
  ["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
24273
24768
  ]
24274
24769
  });
24275
- state = Option39.String({ required: true, name: "on|off" });
24276
- subscription = Option39.String("--subscription");
24277
- resourceGroup = Option39.String("--resource-group", {
24770
+ state = Option40.String({ required: true, name: "on|off" });
24771
+ subscription = Option40.String("--subscription");
24772
+ resourceGroup = Option40.String("--resource-group", {
24278
24773
  description: "m8t resource group, to disambiguate in a multi-deployment subscription."
24279
24774
  });
24280
- agent = Option39.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
24281
- kvUri = Option39.String("--kv-uri", {
24775
+ agent = Option40.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
24776
+ kvUri = Option40.String("--kv-uri", {
24282
24777
  description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
24283
24778
  });
24284
- endpoint = Option39.String("--endpoint", {
24779
+ endpoint = Option40.String("--endpoint", {
24285
24780
  description: "Foundry project endpoint, to disambiguate a subscription holding several."
24286
24781
  });
24287
- output = Option39.String("--output");
24782
+ output = Option40.String("--output");
24288
24783
  async executeCommand() {
24289
24784
  const wanted = this.state.trim().toLowerCase();
24290
24785
  if (wanted !== "on" && wanted !== "off") {
@@ -24308,7 +24803,7 @@ var PlatformEmailCommand = class extends M8tCommand {
24308
24803
  });
24309
24804
  const { resourceGroup } = parseContainerAppResourceId(ctx.containerAppResourceId);
24310
24805
  const subscriptionId = ctx.subscriptionId;
24311
- const credential2 = new DefaultAzureCredential22();
24806
+ const credential2 = new DefaultAzureCredential23();
24312
24807
  const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
24313
24808
  if (stamp === null) {
24314
24809
  throw new LocalCliError({
@@ -24434,45 +24929,45 @@ var PlatformEmailCommand = class extends M8tCommand {
24434
24929
  };
24435
24930
 
24436
24931
  // src/commands/platform/enable-auto-update.ts
24437
- import { Command as Command43, Option as Option40 } from "clipanion";
24932
+ import { Command as Command44, Option as Option41 } from "clipanion";
24438
24933
  import { confirm as confirm6 } from "@inquirer/prompts";
24439
- import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
24934
+ import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
24440
24935
  init_errors();
24441
24936
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24442
24937
  static paths = [["platform", "enable-auto-update"]];
24443
- static usage = Command43.Usage({
24938
+ static usage = Command44.Usage({
24444
24939
  category: "Platform",
24445
24940
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
24446
24941
  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
24942
  });
24448
- subscription = Option40.String("--subscription");
24449
- resourceGroup = Option40.String("--resource-group", {
24943
+ subscription = Option41.String("--subscription");
24944
+ resourceGroup = Option41.String("--resource-group", {
24450
24945
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24451
24946
  });
24452
- suffix = Option40.String("--suffix", {
24947
+ suffix = Option41.String("--suffix", {
24453
24948
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
24454
24949
  });
24455
- installerImage = Option40.String("--installer-image", {
24950
+ installerImage = Option41.String("--installer-image", {
24456
24951
  required: true,
24457
24952
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
24458
24953
  });
24459
- updateCron = Option40.String("--update-cron", {
24954
+ updateCron = Option41.String("--update-cron", {
24460
24955
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
24461
24956
  });
24462
- channelUrl = Option40.String("--channel-url", {
24957
+ channelUrl = Option41.String("--channel-url", {
24463
24958
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
24464
24959
  });
24465
- location = Option40.String("--location", {
24960
+ location = Option41.String("--location", {
24466
24961
  description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
24467
24962
  });
24468
- foundryTracing = Option40.String("--foundry-tracing", {
24963
+ foundryTracing = Option41.String("--foundry-tracing", {
24469
24964
  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
24965
  });
24471
- endpoint = Option40.String("--endpoint", {
24966
+ endpoint = Option41.String("--endpoint", {
24472
24967
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
24473
24968
  });
24474
- yes = Option40.Boolean("--yes", false);
24475
- output = Option40.String("--output");
24969
+ yes = Option41.Boolean("--yes", false);
24970
+ output = Option41.String("--output");
24476
24971
  async executeCommand() {
24477
24972
  const mode = resolveOutputMode(
24478
24973
  this.output,
@@ -24491,7 +24986,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24491
24986
  resourceGroup: this.resourceGroup
24492
24987
  });
24493
24988
  const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
24494
- const credential2 = new DefaultAzureCredential23();
24989
+ const credential2 = new DefaultAzureCredential24();
24495
24990
  const account = await getAzAccount();
24496
24991
  const subscriptionId = this.subscription ?? account.subscriptionId;
24497
24992
  const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
@@ -24653,7 +25148,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
24653
25148
  };
24654
25149
 
24655
25150
  // src/commands/deploy.ts
24656
- import { Command as Command44, Option as Option41 } from "clipanion";
25151
+ import { Command as Command45, Option as Option42 } from "clipanion";
24657
25152
 
24658
25153
  // src/lib/app-reg.ts
24659
25154
  init_esm();
@@ -25217,15 +25712,15 @@ function classifyWhatIf(changes) {
25217
25712
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
25218
25713
  var DeployCommand = class extends M8tCommand {
25219
25714
  static paths = [["deploy"]];
25220
- static usage = Command44.Usage({
25715
+ static usage = Command45.Usage({
25221
25716
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
25222
25717
  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
25718
  });
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);
25719
+ subscription = Option42.String("--subscription");
25720
+ resourceGroup = Option42.String("--resource-group", "rg-m8t-stack");
25721
+ location = Option42.String("--location", "eastus");
25722
+ suffix = Option42.String("--suffix", "");
25723
+ imageRef = Option42.String("--image-ref", DEFAULT_IMAGE_REF);
25229
25724
  // Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
25230
25725
  // from-zero case. It exists because the gateway and the voice relay do NOT
25231
25726
  // always run the same image: a converge preserves a BYOC gateway on its own
@@ -25233,28 +25728,28 @@ var DeployCommand = class extends M8tCommand {
25233
25728
  // (`--what-if`) that can only express one image therefore reports the other
25234
25729
  // app as drift on every single run, forever — which is exactly what the
25235
25730
  // 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");
25731
+ gatewayImageRef = Option42.String("--gateway-image-ref", "");
25732
+ acrPullIdentity = Option42.String("--acrpull-identity");
25733
+ acrResourceId = Option42.String("--acr-resource-id");
25734
+ foundryEndpoint = Option42.String("--foundry-endpoint");
25735
+ foundryResourceId = Option42.String("--foundry-resource-id");
25736
+ foundryTracing = Option42.String("--foundry-tracing");
25242
25737
  // project | account | skip (bicep default: project)
25243
- clientId = Option41.String("--client-id");
25244
- whatIf = Option41.Boolean("--what-if", false);
25738
+ clientId = Option42.String("--client-id");
25739
+ whatIf = Option42.Boolean("--what-if", false);
25245
25740
  // Only meaningful with --what-if. Routes the comparison through the
25246
25741
  // value-free renderers (see ./lib/whatif-redact.js) instead of the default
25247
25742
  // before/after renderer. Defaults false so a local, interactive run keeps
25248
25743
  // showing values — that is the whole diagnostic point of --what-if.
25249
25744
  // Automation that forwards this output anywhere non-private (a CI log, an
25250
25745
  // issue) MUST pass --redact.
25251
- redact = Option41.Boolean("--redact", false);
25252
- output = Option41.String("--output");
25746
+ redact = Option42.Boolean("--redact", false);
25747
+ output = Option42.String("--output");
25253
25748
  // Subscription-scoped role assignments. Omitted ⇒ the template default (true).
25254
25749
  // Pass false when deploying as a principal scoped to the resource group only:
25255
25750
  // it cannot deploy at subscription scope, and those assignments persist
25256
25751
  // idempotently from the initial deployment anyway.
25257
- assignSubscriptionRoles = Option41.String("--assign-subscription-roles");
25752
+ assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
25258
25753
  /**
25259
25754
  * The installer image the updater Container-Apps Job runs.
25260
25755
  *
@@ -25265,23 +25760,23 @@ var DeployCommand = class extends M8tCommand {
25265
25760
  * than that — the comparison proposes REMOVING an updater job that exists and
25266
25761
  * should, and reports it as drift on every single run.
25267
25762
  */
25268
- installerImage = Option41.String("--installer-image");
25763
+ installerImage = Option42.String("--installer-image");
25269
25764
  // Referee — all optional, undefined by default ⇒ the bicep defaults
25270
25765
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
25271
25766
  // 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");
25767
+ gatewayCpu = Option42.String("--gateway-cpu");
25768
+ gatewayMemory = Option42.String("--gateway-memory");
25769
+ refereeEnabled = Option42.String("--referee-enabled");
25770
+ refereeBrainRepos = Option42.String("--referee-brain-repos");
25771
+ refereeFeedRepo = Option42.String("--referee-feed-repo");
25772
+ refereeInstallationId = Option42.String("--referee-installation-id");
25773
+ refereeWebhookHmacKvUri = Option42.String("--referee-webhook-hmac-kv-uri");
25774
+ examKvUri = Option42.String("--exam-kv-uri");
25775
+ examLaWorkspaceId = Option42.String("--exam-la-workspace-id");
25776
+ brainEvalDeployment = Option42.String("--brain-eval-deployment");
25777
+ brainAppLogin = Option42.String("--brain-app-login");
25778
+ refereeCheckpointDir = Option42.String("--referee-checkpoint-dir");
25779
+ examApiBase = Option42.String("--exam-api-base");
25285
25780
  async executeCommand() {
25286
25781
  const mode = resolveOutputMode(
25287
25782
  this.output,
@@ -25465,7 +25960,7 @@ var DeployCommand = class extends M8tCommand {
25465
25960
 
25466
25961
  // src/commands/eval/skill.ts
25467
25962
  import { spawnSync as spawnSync4 } from "child_process";
25468
- import { Command as Command45, Option as Option42 } from "clipanion";
25963
+ import { Command as Command46, Option as Option43 } from "clipanion";
25469
25964
  init_errors();
25470
25965
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
25471
25966
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -25490,14 +25985,14 @@ function parseVerdict(stdout) {
25490
25985
  }
25491
25986
  var EvalSkillCommand = class extends M8tCommand {
25492
25987
  static paths = [["eval", "skill"]];
25493
- static usage = Command45.Usage({
25988
+ static usage = Command46.Usage({
25494
25989
  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
25990
  });
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");
25991
+ candidate = Option43.String();
25992
+ skillsDir = Option43.String("--skills-dir");
25993
+ noJudge = Option43.Boolean("--no-judge", false);
25994
+ deployment = Option43.String("--deployment");
25995
+ output = Option43.String("--output");
25501
25996
  executeCommand() {
25502
25997
  return Promise.resolve(this._runCommand());
25503
25998
  }
@@ -25556,7 +26051,7 @@ var EvalSkillCommand = class extends M8tCommand {
25556
26051
  import { spawnSync as spawnSync5 } from "child_process";
25557
26052
  import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
25558
26053
  import { join as join30 } from "path";
25559
- import { Command as Command46, Option as Option43 } from "clipanion";
26054
+ import { Command as Command47, Option as Option44 } from "clipanion";
25560
26055
  init_errors();
25561
26056
  init_esm();
25562
26057
  function parseArmToken(tok, opts) {
@@ -25786,24 +26281,24 @@ function buildPlan(args) {
25786
26281
  }
25787
26282
  var EvalExamCommand = class extends M8tCommand {
25788
26283
  static paths = [["eval", "exam"]];
25789
- static usage = Command46.Usage({
26284
+ static usage = Command47.Usage({
25790
26285
  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
26286
  });
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");
26287
+ worker = Option44.String();
26288
+ arms = Option44.String("--arms");
26289
+ taskSet = Option44.String("--task-set");
26290
+ examType = Option44.String("--exam-type");
26291
+ skill = Option44.String("--skill");
26292
+ reps = Option44.String("-n,--reps");
26293
+ probes = Option44.String("--probes");
26294
+ pool = Option44.String("--pool");
26295
+ out = Option44.String("--out");
26296
+ dryRun = Option44.Boolean("--dry-run", false);
26297
+ keepArms = Option44.Boolean("--keep-arms", false);
26298
+ allowStub = Option44.Boolean("--allow-stub", false);
26299
+ deployment = Option44.String("--deployment");
26300
+ output = Option44.String("--output");
26301
+ observeWaitS = Option44.String("--observe-wait-s");
25807
26302
  async executeCommand() {
25808
26303
  await Promise.resolve();
25809
26304
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -25918,10 +26413,10 @@ var EvalExamCommand = class extends M8tCommand {
25918
26413
  };
25919
26414
 
25920
26415
  // src/commands/version.ts
25921
- import { Command as Command47, Option as Option44 } from "clipanion";
26416
+ import { Command as Command48, Option as Option45 } from "clipanion";
25922
26417
  var VersionCommand = class extends M8tCommand {
25923
26418
  static paths = [["version"], ["--version"], ["-v"]];
25924
- static usage = Command47.Usage({
26419
+ static usage = Command48.Usage({
25925
26420
  description: "Print the CLI version.",
25926
26421
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
25927
26422
  examples: [
@@ -25929,8 +26424,8 @@ var VersionCommand = class extends M8tCommand {
25929
26424
  ["Print as JSON", "$0 version --output json"]
25930
26425
  ]
25931
26426
  });
25932
- output = Option44.String("--output", { description: "pretty | json | auto (default)" });
25933
- verbose = Option44.Boolean("--verbose", false);
26427
+ output = Option45.String("--output", { description: "pretty | json | auto (default)" });
26428
+ verbose = Option45.Boolean("--verbose", false);
25934
26429
  executeCommand() {
25935
26430
  const mode = resolveOutputMode(
25936
26431
  this.output ?? "auto",
@@ -25961,18 +26456,18 @@ var VersionCommand = class extends M8tCommand {
25961
26456
  };
25962
26457
 
25963
26458
  // src/commands/whoami.ts
25964
- import { Command as Command48, Option as Option45 } from "clipanion";
26459
+ import { Command as Command49, Option as Option46 } from "clipanion";
25965
26460
  var WhoamiCommand = class extends M8tCommand {
25966
26461
  static paths = [["whoami"]];
25967
- static usage = Command48.Usage({
26462
+ static usage = Command49.Usage({
25968
26463
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
25969
26464
  });
25970
- output = Option45.String("--output");
25971
- verbose = Option45.Boolean("--verbose", false);
25972
- subscription = Option45.String("--subscription", {
26465
+ output = Option46.String("--output");
26466
+ verbose = Option46.Boolean("--verbose", false);
26467
+ subscription = Option46.String("--subscription", {
25973
26468
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
25974
26469
  });
25975
- resourceGroup = Option45.String("--resource-group", {
26470
+ resourceGroup = Option46.String("--resource-group", {
25976
26471
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
25977
26472
  });
25978
26473
  async executeCommand() {
@@ -26037,7 +26532,7 @@ var WhoamiCommand = class extends M8tCommand {
26037
26532
  };
26038
26533
 
26039
26534
  // src/commands/status.ts
26040
- import { Command as Command49, Option as Option46 } from "clipanion";
26535
+ import { Command as Command50, Option as Option47 } from "clipanion";
26041
26536
 
26042
26537
  // src/lib/azd.ts
26043
26538
  init_errors();
@@ -26102,10 +26597,10 @@ async function resolveLocalContext() {
26102
26597
  // src/commands/status.ts
26103
26598
  var StatusCommand = class extends M8tCommand {
26104
26599
  static paths = [["status"]];
26105
- static usage = Command49.Usage({
26600
+ static usage = Command50.Usage({
26106
26601
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
26107
26602
  });
26108
- output = Option46.String("--output");
26603
+ output = Option47.String("--output");
26109
26604
  async executeCommand() {
26110
26605
  const mode = resolveOutputMode(
26111
26606
  this.output,
@@ -26143,8 +26638,8 @@ var StatusCommand = class extends M8tCommand {
26143
26638
  };
26144
26639
 
26145
26640
  // src/commands/doctor.ts
26146
- import { Command as Command50, Option as Option47 } from "clipanion";
26147
- import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
26641
+ import { Command as Command51, Option as Option48 } from "clipanion";
26642
+ import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
26148
26643
  import * as fs30 from "fs";
26149
26644
  import * as os12 from "os";
26150
26645
  import * as path33 from "path";
@@ -26691,12 +27186,12 @@ function probeLegacyStateDir() {
26691
27186
  }
26692
27187
  var DoctorCommand = class extends M8tCommand {
26693
27188
  static paths = [["doctor"]];
26694
- static usage = Command50.Usage({
27189
+ static usage = Command51.Usage({
26695
27190
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
26696
27191
  });
26697
- output = Option47.String("--output");
26698
- agent = Option47.String("--agent");
26699
- resourceGroup = Option47.String("--resource-group", {
27192
+ output = Option48.String("--output");
27193
+ agent = Option48.String("--agent");
27194
+ resourceGroup = Option48.String("--resource-group", {
26700
27195
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
26701
27196
  });
26702
27197
  async executeCommand() {
@@ -26769,7 +27264,7 @@ var DoctorCommand = class extends M8tCommand {
26769
27264
  let kvStatus = 0;
26770
27265
  if (kv) {
26771
27266
  try {
26772
- const token = await new DefaultAzureCredential24().getToken("https://vault.azure.net/.default");
27267
+ const token = await new DefaultAzureCredential25().getToken("https://vault.azure.net/.default");
26773
27268
  const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
26774
27269
  headers: { Authorization: `Bearer ${token.token}` }
26775
27270
  });
@@ -26788,7 +27283,7 @@ var DoctorCommand = class extends M8tCommand {
26788
27283
  emit(checkModelQuota(deployments, usages));
26789
27284
  checking("outbound email");
26790
27285
  try {
26791
- const credential2 = new DefaultAzureCredential24();
27286
+ const credential2 = new DefaultAzureCredential25();
26792
27287
  const outcome = platformRg && platformSub ? await readStampOutcome({ credential: credential2, subscriptionId: platformSub, resourceGroup: platformRg }).catch(
26793
27288
  () => ({ source: "unreadable" })
26794
27289
  ) : { source: "unreadable" };
@@ -26843,7 +27338,7 @@ var DoctorCommand = class extends M8tCommand {
26843
27338
  if (typeof this.agent === "string" && this.agent) {
26844
27339
  checking(`delivery grant for ${this.agent}`);
26845
27340
  try {
26846
- const credential2 = new DefaultAzureCredential24();
27341
+ const credential2 = new DefaultAzureCredential25();
26847
27342
  const cur = await getAgentVersion({
26848
27343
  credential: credential2,
26849
27344
  projectEndpoint: foundry.projectEndpoint,
@@ -26886,11 +27381,11 @@ var DoctorCommand = class extends M8tCommand {
26886
27381
  };
26887
27382
 
26888
27383
  // src/commands/prereqs.ts
26889
- import { Command as Command51, Option as Option48 } from "clipanion";
27384
+ import { Command as Command52, Option as Option49 } from "clipanion";
26890
27385
  init_errors();
26891
27386
 
26892
27387
  // src/lib/prereq-deps.ts
26893
- import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
27388
+ import { DefaultAzureCredential as DefaultAzureCredential26 } from "@azure/identity";
26894
27389
 
26895
27390
  // src/lib/bootstrap-preflight.ts
26896
27391
  var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
@@ -27002,7 +27497,7 @@ function buildPrereqDeps(opts = {}) {
27002
27497
  probeRedirectUri,
27003
27498
  fixFoundryAccess,
27004
27499
  fixKeyVaultAccess,
27005
- credential: () => credentialSingleton2 ??= new DefaultAzureCredential25()
27500
+ credential: () => credentialSingleton2 ??= new DefaultAzureCredential26()
27006
27501
  };
27007
27502
  }
27008
27503
 
@@ -27592,7 +28087,7 @@ function renderVerdict(v) {
27592
28087
  }
27593
28088
  var PrereqsCommand = class extends M8tCommand {
27594
28089
  static paths = [["prereqs"]];
27595
- static usage = Command51.Usage({
28090
+ static usage = Command52.Usage({
27596
28091
  description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
27597
28092
  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
28093
  examples: [
@@ -27603,15 +28098,15 @@ var PrereqsCommand = class extends M8tCommand {
27603
28098
  ["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
27604
28099
  ]
27605
28100
  });
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");
28101
+ fix = Option49.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
28102
+ for_ = Option49.String("--for", { description: "UPN or object id of another person. Usage phase only." });
28103
+ phase = Option49.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
28104
+ 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)." });
28105
+ model = Option49.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
28106
+ clientId = Option49.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
28107
+ subscription = Option49.String("--subscription");
28108
+ resourceGroup = Option49.String("--resource-group");
28109
+ output = Option49.String("--output");
27615
28110
  async executeCommand() {
27616
28111
  const mode = resolveOutputMode(this.output, this.context.stdout);
27617
28112
  const json = mode === "json";
@@ -27667,7 +28162,7 @@ var PrereqsCommand = class extends M8tCommand {
27667
28162
  };
27668
28163
 
27669
28164
  // src/commands/switch.ts
27670
- import { Command as Command52, Option as Option49 } from "clipanion";
28165
+ import { Command as Command53, Option as Option50 } from "clipanion";
27671
28166
 
27672
28167
  // src/lib/profiles.ts
27673
28168
  import * as fs31 from "fs/promises";
@@ -27807,14 +28302,14 @@ async function profileSwitch(name, asName) {
27807
28302
  init_errors();
27808
28303
  var SwitchCommand = class extends M8tCommand {
27809
28304
  static paths = [["switch"]];
27810
- static usage = Command52.Usage({
28305
+ static usage = Command53.Usage({
27811
28306
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
27812
28307
  });
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");
28308
+ profile = Option50.String({ required: false });
28309
+ subscription = Option50.String("--subscription");
28310
+ list = Option50.Boolean("--list", false);
28311
+ as = Option50.String("--as");
28312
+ output = Option50.String("--output");
27818
28313
  async executeCommand() {
27819
28314
  const mode = resolveOutputMode(
27820
28315
  this.output,
@@ -27871,7 +28366,7 @@ var SwitchCommand = class extends M8tCommand {
27871
28366
 
27872
28367
  // src/commands/open.ts
27873
28368
  import { spawn as spawn5 } from "child_process";
27874
- import { Command as Command53, Option as Option50 } from "clipanion";
28369
+ import { Command as Command54, Option as Option51 } from "clipanion";
27875
28370
 
27876
28371
  // src/lib/open-targets.ts
27877
28372
  init_errors();
@@ -27917,14 +28412,14 @@ function openUrl(url) {
27917
28412
  }
27918
28413
  var OpenCommand = class extends M8tCommand {
27919
28414
  static paths = [["open"]];
27920
- static usage = Command53.Usage({
28415
+ static usage = Command54.Usage({
27921
28416
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
27922
28417
  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
28418
  });
27924
- target = Option50.String({ required: false });
27925
- print = Option50.Boolean("--print", false);
27926
- output = Option50.String("--output");
27927
- resourceGroup = Option50.String("--resource-group", {
28419
+ target = Option51.String({ required: false });
28420
+ print = Option51.Boolean("--print", false);
28421
+ output = Option51.String("--output");
28422
+ resourceGroup = Option51.String("--resource-group", {
27928
28423
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
27929
28424
  });
27930
28425
  async executeCommand() {
@@ -27968,7 +28463,7 @@ var OpenCommand = class extends M8tCommand {
27968
28463
  };
27969
28464
 
27970
28465
  // src/commands/dream/run.ts
27971
- import { Command as Command54, Option as Option51 } from "clipanion";
28466
+ import { Command as Command55, Option as Option52 } from "clipanion";
27972
28467
  import { AzureCliCredential } from "@azure/identity";
27973
28468
  import { TableClient as TableClient7 } from "@azure/data-tables";
27974
28469
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
@@ -30368,28 +30863,28 @@ function redactTranscripts(input) {
30368
30863
  }
30369
30864
  var DreamRunCommand = class extends M8tCommand {
30370
30865
  static paths = [["dream", "run"]];
30371
- static usage = Command54.Usage({
30866
+ static usage = Command55.Usage({
30372
30867
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
30373
30868
  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
30869
  });
30375
- worker = Option51.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
30870
+ worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
30376
30871
  // Opting IN to the side effects, rather than opting out of them. This command's
30377
30872
  // own help has always described a dry run, but the bare invocation used to take
30378
30873
  // the live branch — a real model call and a commit to the brain repo — so anyone
30379
30874
  // 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, {
30875
+ live = Option52.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
30876
+ dryRun = Option52.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
30877
+ since = Option52.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
30878
+ reset = Option52.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
30879
+ showTranscripts = Option52.Boolean("--show-transcripts", false, {
30385
30880
  description: "Print transcript bodies (default: metadata only)."
30386
30881
  });
30387
- subscription = Option51.String("--subscription");
30388
- endpoint = Option51.String("--endpoint");
30389
- storageAccount = Option51.String("--storage-account", {
30882
+ subscription = Option52.String("--subscription");
30883
+ endpoint = Option52.String("--endpoint");
30884
+ storageAccount = Option52.String("--storage-account", {
30390
30885
  description: "Ledger storage account name (skips tag-based discovery)"
30391
30886
  });
30392
- output = Option51.String("--output");
30887
+ output = Option52.String("--output");
30393
30888
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
30394
30889
  deps;
30395
30890
  async executeCommand() {
@@ -30740,7 +31235,7 @@ function defaultDeps(overrides) {
30740
31235
 
30741
31236
  // src/commands/conversations/sweep.ts
30742
31237
  import { createHash as createHash5 } from "crypto";
30743
- import { Command as Command55, Option as Option52 } from "clipanion";
31238
+ import { Command as Command56, Option as Option53 } from "clipanion";
30744
31239
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
30745
31240
  import { TableClient as TableClient8 } from "@azure/data-tables";
30746
31241
  import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
@@ -30866,7 +31361,7 @@ function defaultDeps2() {
30866
31361
  }
30867
31362
  var ConversationsSweepCommand = class extends M8tCommand {
30868
31363
  static paths = [["conversations", "sweep"]];
30869
- static usage = Command55.Usage({
31364
+ static usage = Command56.Usage({
30870
31365
  category: "Conversations",
30871
31366
  description: "Delete expired public-visitor conversations (dry run by default)",
30872
31367
  details: `
@@ -30882,22 +31377,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
30882
31377
  ["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
30883
31378
  ]
30884
31379
  });
30885
- doDelete = Option52.Boolean("--delete", false, {
31380
+ doDelete = Option53.Boolean("--delete", false, {
30886
31381
  description: "Perform deletions (without this flag the command only reports)"
30887
31382
  });
30888
- principal = Option52.String("--principal", {
31383
+ principal = Option53.String("--principal", {
30889
31384
  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
31385
  });
30891
- max = Option52.String("--max", "200", { description: "Maximum deletions per run" });
30892
- graceDays = Option52.String("--grace-days", "14", {
31386
+ max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
31387
+ graceDays = Option53.String("--grace-days", "14", {
30893
31388
  description: "Days past the 30-day key life before a conversation is eligible"
30894
31389
  });
30895
- lookbackDays = Option52.String("--lookback-days", "180", {
31390
+ lookbackDays = Option53.String("--lookback-days", "180", {
30896
31391
  description: "How far back to scan ledger activity for candidates"
30897
31392
  });
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", {
31393
+ subscription = Option53.String("--subscription", { description: "Azure subscription id override" });
31394
+ endpoint = Option53.String("--endpoint", { description: "Foundry project endpoint override" });
31395
+ storageAccount = Option53.String("--storage-account", {
30901
31396
  description: "Ledger storage account name (skips tag-based discovery)"
30902
31397
  });
30903
31398
  deps = defaultDeps2();
@@ -31025,7 +31520,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
31025
31520
  };
31026
31521
 
31027
31522
  // src/commands/foundry/create.ts
31028
- import { Command as Command56, Option as Option53 } from "clipanion";
31523
+ import { Command as Command57, Option as Option54 } from "clipanion";
31029
31524
 
31030
31525
  // src/lib/foundry-create.ts
31031
31526
  init_errors();
@@ -31263,7 +31758,7 @@ async function createFoundryProject(args) {
31263
31758
  init_errors();
31264
31759
  var FoundryCreateCommand = class extends M8tCommand {
31265
31760
  static paths = [["foundry", "create"]];
31266
- static usage = Command56.Usage({
31761
+ static usage = Command57.Usage({
31267
31762
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
31268
31763
  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
31764
  examples: [
@@ -31272,16 +31767,16 @@ var FoundryCreateCommand = class extends M8tCommand {
31272
31767
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
31273
31768
  ]
31274
31769
  });
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");
31770
+ resourceGroup = Option54.String("--resource-group");
31771
+ location = Option54.String("--location");
31772
+ account = Option54.String("--account");
31773
+ project = Option54.String("--project", "m8t");
31774
+ model = Option54.String("--model", "gpt-4.1-mini");
31775
+ modelVersion = Option54.String("--model-version", "2025-04-14");
31776
+ capacity = Option54.String("--capacity", "50");
31777
+ subscription = Option54.String("--subscription");
31778
+ skipQuotaCheck = Option54.Boolean("--skip-quota-check", false);
31779
+ output = Option54.String("--output");
31285
31780
  async executeCommand() {
31286
31781
  const mode = resolveOutputMode(
31287
31782
  this.output,
@@ -31353,22 +31848,22 @@ var FoundryCreateCommand = class extends M8tCommand {
31353
31848
  };
31354
31849
 
31355
31850
  // src/commands/foundry/await-ready.ts
31356
- import { Command as Command57, Option as Option54 } from "clipanion";
31851
+ import { Command as Command58, Option as Option55 } from "clipanion";
31357
31852
  import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
31358
31853
  init_errors();
31359
31854
  var FoundryAwaitReadyCommand = class extends M8tCommand {
31360
31855
  static paths = [["foundry", "await-ready"]];
31361
- static usage = Command57.Usage({
31856
+ static usage = Command58.Usage({
31362
31857
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
31363
31858
  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
31859
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
31365
31860
  });
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");
31861
+ endpoint = Option55.String("--endpoint");
31862
+ consecutive = Option55.String("--consecutive", "3");
31863
+ attempts = Option55.String("--attempts", "60");
31864
+ interval = Option55.String("--interval", "5");
31865
+ subscription = Option55.String("--subscription");
31866
+ output = Option55.String("--output");
31372
31867
  async executeCommand() {
31373
31868
  const mode = resolveOutputMode(this.output, this.context.stdout);
31374
31869
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -31402,7 +31897,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
31402
31897
  };
31403
31898
 
31404
31899
  // src/commands/bootstrap/preflight.ts
31405
- import { Command as Command58, Option as Option55 } from "clipanion";
31900
+ import { Command as Command59, Option as Option56 } from "clipanion";
31406
31901
 
31407
31902
  // ../../packages/telemetry-contract/artifact/tier-map.ts
31408
31903
  var EVENT_TIERS = {
@@ -31454,7 +31949,7 @@ function preflightRenderable(results) {
31454
31949
  }
31455
31950
  var BootstrapPreflightCommand = class extends M8tCommand {
31456
31951
  static paths = [["bootstrap", "preflight"]];
31457
- static usage = Command58.Usage({
31952
+ static usage = Command59.Usage({
31458
31953
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
31459
31954
  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
31955
  examples: [
@@ -31463,9 +31958,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
31463
31958
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
31464
31959
  ]
31465
31960
  });
31466
- clientId = Option55.String("--client-id");
31467
- subscription = Option55.String("--subscription");
31468
- location = Option55.String("--location", {
31961
+ clientId = Option56.String("--client-id");
31962
+ subscription = Option56.String("--subscription");
31963
+ location = Option56.String("--location", {
31469
31964
  description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
31470
31965
  });
31471
31966
  async executeCommand() {
@@ -31565,7 +32060,7 @@ ${colors.error(" " + why)}
31565
32060
  import * as fs34 from "fs";
31566
32061
  import * as os15 from "os";
31567
32062
  import * as path37 from "path";
31568
- import { Command as Command59, Option as Option56 } from "clipanion";
32063
+ import { Command as Command60, Option as Option57 } from "clipanion";
31569
32064
  init_errors();
31570
32065
 
31571
32066
  // src/lib/bootstrap-mi.ts
@@ -31930,12 +32425,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
31930
32425
  // src/commands/bootstrap/launch.ts
31931
32426
  var DEFAULT_RG = "rg-m8t-stack";
31932
32427
  var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
31933
- var DEFAULT_INSTALLER_TAG = "v0.1.73";
32428
+ var DEFAULT_INSTALLER_TAG = "v0.1.74";
31934
32429
  var ACI_NAME = "m8t-installer";
31935
32430
  var MI_NAME = "m8t-installer-mi";
31936
32431
  var BootstrapLaunchCommand = class extends M8tCommand {
31937
32432
  static paths = [["bootstrap", "launch"]];
31938
- static usage = Command59.Usage({
32433
+ static usage = Command60.Usage({
31939
32434
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
31940
32435
  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
32436
  examples: [
@@ -31946,31 +32441,31 @@ var BootstrapLaunchCommand = class extends M8tCommand {
31946
32441
  ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
31947
32442
  ]
31948
32443
  });
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");
32444
+ location = Option57.String("--location");
32445
+ resourceGroup = Option57.String("--resource-group");
32446
+ clientId = Option57.String("--client-id");
32447
+ subscription = Option57.String("--subscription");
32448
+ installerTag = Option57.String("--installer-tag");
31954
32449
  // Full image ref override (registry + repo + tag) — an escape hatch when the
31955
32450
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
31956
32451
  // 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." });
32452
+ installerImage = Option57.String("--installer-image");
32453
+ gatewayImageRef = Option57.String("--gateway-image-ref");
32454
+ githubAppCreds = Option57.String("--github-app-creds");
32455
+ contactEmail = Option57.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
32456
+ company = Option57.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
31962
32457
  // Value-carrying on purpose: a bare --force would be cargo-culted into
31963
32458
  // runbooks and harness prompts and erode the protection, whereas a faithful
31964
32459
  // paste can never accidentally carry the victim group's name. It AUTHORIZES
31965
32460
  // 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." });
32461
+ 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." });
32462
+ org = Option57.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
32463
+ noBrains = Option57.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
31969
32464
  // The opt-out TELEMETRY.md names. Without it there is no way to decline at
31970
32465
  // install time — the ACI env is built entirely from these options, so a
31971
32466
  // founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
31972
32467
  // 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)." });
32468
+ 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
32469
  async executeCommand() {
31975
32470
  const location = typeof this.location === "string" ? this.location : void 0;
31976
32471
  if (!location) {
@@ -32132,7 +32627,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32132
32627
  };
32133
32628
 
32134
32629
  // src/commands/bootstrap/status.ts
32135
- import { Command as Command61, Option as Option58 } from "clipanion";
32630
+ import { Command as Command62, Option as Option59 } from "clipanion";
32136
32631
  init_errors();
32137
32632
 
32138
32633
  // src/lib/bootstrap-aci-state.ts
@@ -34309,7 +34804,7 @@ async function uninstallCompanion(options) {
34309
34804
  // src/commands/companion/install.ts
34310
34805
  import * as os19 from "os";
34311
34806
  import * as path43 from "path";
34312
- import { Command as Command60, Option as Option57 } from "clipanion";
34807
+ import { Command as Command61, Option as Option58 } from "clipanion";
34313
34808
 
34314
34809
  // src/lib/companion-channel.ts
34315
34810
  async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
@@ -34440,7 +34935,7 @@ Version: ${state.version}
34440
34935
  }
34441
34936
  var CompanionInstallCommand = class extends M8tCommand {
34442
34937
  static paths = [["companion", "install"]];
34443
- static usage = Command60.Usage({
34938
+ static usage = Command61.Usage({
34444
34939
  description: "Install the desktop companions for this user from the release channel.",
34445
34940
  examples: [
34446
34941
  ["Install the released build", "$0 companion install"],
@@ -34450,10 +34945,10 @@ var CompanionInstallCommand = class extends M8tCommand {
34450
34945
  ]
34451
34946
  ]
34452
34947
  });
34453
- from = Option57.String("--from", {
34948
+ from = Option58.String("--from", {
34454
34949
  description: "A locally staged build directory instead of the released one."
34455
34950
  });
34456
- resourceGroup = Option57.String("--resource-group", {
34951
+ resourceGroup = Option58.String("--resource-group", {
34457
34952
  description: "Which deployment to bind to, when the subscription holds more than one."
34458
34953
  });
34459
34954
  async executeCommand() {
@@ -34668,7 +35163,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
34668
35163
  // runbooks and shakedown recipes that a founder may already be part-way
34669
35164
  // through — it prints a deprecation notice and does the right thing.
34670
35165
  static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
34671
- static usage = Command61.Usage({
35166
+ static usage = Command62.Usage({
34672
35167
  description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
34673
35168
  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
35169
  examples: [
@@ -34677,12 +35172,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
34677
35172
  ["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
34678
35173
  ]
34679
35174
  });
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");
35175
+ watch = Option59.Boolean("--watch", false);
35176
+ output = Option59.String("--output");
35177
+ repoRoot = Option59.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
35178
+ finalize = Option59.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
35179
+ subscription = Option59.String("--subscription");
35180
+ resourceGroup = Option59.String("--resource-group");
34686
35181
  async executeCommand() {
34687
35182
  const state = await readBootstrapState();
34688
35183
  if (!state) {
@@ -34815,7 +35310,7 @@ function formatStatus(d) {
34815
35310
  }
34816
35311
 
34817
35312
  // src/commands/bootstrap/reap.ts
34818
- import { Command as Command62, Option as Option59 } from "clipanion";
35313
+ import { Command as Command63, Option as Option60 } from "clipanion";
34819
35314
  init_errors();
34820
35315
 
34821
35316
  // src/lib/bootstrap-reap.ts
@@ -34909,7 +35404,7 @@ async function reapInstaller(opts) {
34909
35404
  // src/commands/bootstrap/reap.ts
34910
35405
  var BootstrapReapCommand = class extends M8tCommand {
34911
35406
  static paths = [["bootstrap", "reap"]];
34912
- static usage = Command62.Usage({
35407
+ static usage = Command63.Usage({
34913
35408
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
34914
35409
  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
35410
  examples: [
@@ -34919,9 +35414,9 @@ var BootstrapReapCommand = class extends M8tCommand {
34919
35414
  ["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
34920
35415
  ]
34921
35416
  });
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." });
35417
+ force = Option60.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
35418
+ sweepOrphans = Option60.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
35419
+ yes = Option60.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
34925
35420
  async executeCommand() {
34926
35421
  if (this.sweepOrphans === true) {
34927
35422
  const { subscriptionId: sub } = await getAzAccount();
@@ -35015,7 +35510,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
35015
35510
  };
35016
35511
 
35017
35512
  // src/commands/bootstrap/ui.ts
35018
- import { Command as Command63, Option as Option60 } from "clipanion";
35513
+ import { Command as Command64, Option as Option61 } from "clipanion";
35019
35514
 
35020
35515
  // src/lib/bootstrap-ui.ts
35021
35516
  import * as fs40 from "fs";
@@ -35090,7 +35585,7 @@ function renderDeprecationNotice() {
35090
35585
  }
35091
35586
  var BootstrapUiCommand = class extends M8tCommand {
35092
35587
  static paths = [["bootstrap", "ui"]];
35093
- static usage = Command63.Usage({
35588
+ static usage = Command64.Usage({
35094
35589
  description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
35095
35590
  details: [
35096
35591
  "The local onboarding chat has been retired. Your details are collected by",
@@ -35110,14 +35605,14 @@ var BootstrapUiCommand = class extends M8tCommand {
35110
35605
  // Accepted and ignored, deliberately: removing them would turn an old script's
35111
35606
  // harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
35112
35607
  // 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, {
35608
+ repoRoot = Option61.String("--repo-root", { description: "Ignored (deprecated)." });
35609
+ port = Option61.String("--port", "3000", { description: "Ignored (deprecated)." });
35610
+ endpoint = Option61.String("--endpoint", { description: "Ignored (deprecated)." });
35611
+ prepOnly = Option61.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
35612
+ skipInstall = Option61.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
35613
+ foreground = Option61.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
35614
+ voice = Option61.Boolean("--voice", false, { description: "Ignored (deprecated)." });
35615
+ stop = Option61.Boolean("--stop", false, {
35121
35616
  description: "Shut down a local chat UI left running by an earlier version of this command."
35122
35617
  });
35123
35618
  // Not `async`: there is nothing left to await. Everything this command used to
@@ -35139,7 +35634,7 @@ var BootstrapUiCommand = class extends M8tCommand {
35139
35634
 
35140
35635
  // src/commands/bootstrap/profile.ts
35141
35636
  import * as readline3 from "readline/promises";
35142
- import { Command as Command64, Option as Option61 } from "clipanion";
35637
+ import { Command as Command65, Option as Option62 } from "clipanion";
35143
35638
 
35144
35639
  // src/lib/profile-collect.ts
35145
35640
  init_errors();
@@ -35308,7 +35803,7 @@ async function openChatInvite(deps = {}) {
35308
35803
  // src/commands/bootstrap/profile.ts
35309
35804
  var BootstrapProfileCommand = class extends M8tCommand {
35310
35805
  static paths = [["bootstrap", "profile"]];
35311
- static usage = Command64.Usage({
35806
+ static usage = Command65.Usage({
35312
35807
  description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
35313
35808
  details: [
35314
35809
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
@@ -35328,12 +35823,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
35328
35823
  ["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
35329
35824
  ]
35330
35825
  });
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)." });
35826
+ founderEmail = Option62.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
35827
+ advisorName = Option62.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
35828
+ advisorEmail = Option62.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
35829
+ noAdvisor = Option62.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
35830
+ noChat = Option62.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
35831
+ print = Option62.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
35337
35832
  async executeCommand() {
35338
35833
  const stdin = this.context.stdin;
35339
35834
  const stdout = this.context.stdout;
@@ -35426,10 +35921,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
35426
35921
  };
35427
35922
 
35428
35923
  // src/commands/bootstrap/seed-profile.ts
35429
- import { Command as Command65, Option as Option62 } from "clipanion";
35924
+ import { Command as Command66, Option as Option63 } from "clipanion";
35430
35925
  var BootstrapSeedProfileCommand = class extends M8tCommand {
35431
35926
  static paths = [["bootstrap", "seed-profile"]];
35432
- static usage = Command65.Usage({
35927
+ static usage = Command66.Usage({
35433
35928
  description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
35434
35929
  details: [
35435
35930
  "Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
@@ -35446,11 +35941,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
35446
35941
  ["Seed now (idempotent)", "$0 bootstrap seed-profile"]
35447
35942
  ]
35448
35943
  });
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");
35944
+ endpoint = Option63.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
35945
+ brain = Option63.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
35946
+ watch = Option63.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
35947
+ timeout = Option63.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
35948
+ githubAppCreds = Option63.String("--github-app-creds");
35454
35949
  async executeCommand() {
35455
35950
  const ctx = await resolveSeedContext({
35456
35951
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -35538,7 +36033,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
35538
36033
  import * as fs41 from "fs";
35539
36034
  import * as os22 from "os";
35540
36035
  import * as path46 from "path";
35541
- import { Command as Command66, Option as Option63 } from "clipanion";
36036
+ import { Command as Command67, Option as Option64 } from "clipanion";
35542
36037
  init_errors();
35543
36038
 
35544
36039
  // src/lib/telemetry-enroll.ts
@@ -35620,7 +36115,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
35620
36115
  }
35621
36116
  var TelemetryEnrollCommand = class extends M8tCommand {
35622
36117
  static paths = [["telemetry", "enroll"]];
35623
- static usage = Command66.Usage({
36118
+ static usage = Command67.Usage({
35624
36119
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
35625
36120
  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
36121
  examples: [
@@ -35628,11 +36123,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
35628
36123
  ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
35629
36124
  ]
35630
36125
  });
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." });
36126
+ company = Option64.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
36127
+ contactEmail = Option64.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
36128
+ subscription = Option64.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
36129
+ resourceGroup = Option64.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
36130
+ force = Option64.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
35636
36131
  async executeCommand() {
35637
36132
  const account = await getAzAccount();
35638
36133
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
@@ -35682,7 +36177,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
35682
36177
  };
35683
36178
 
35684
36179
  // src/commands/companion/bridge.ts
35685
- import { Command as Command67, Option as Option64 } from "clipanion";
36180
+ import { Command as Command68, Option as Option65 } from "clipanion";
35686
36181
 
35687
36182
  // ../../packages/companion-bridge-contract/src/index.ts
35688
36183
  var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
@@ -37149,14 +37644,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
37149
37644
  return 3;
37150
37645
  }
37151
37646
  }
37152
- var CompanionBridgeCommand = class extends Command67 {
37647
+ var CompanionBridgeCommand = class extends Command68 {
37153
37648
  static paths = [["companion", "_bridge"]];
37154
37649
  /**
37155
37650
  * One process serving many requests instead of one per request, so the
37156
37651
  * session keeps its authenticated context between them. A CLI predating the
37157
37652
  * flag rejects it outright, which is how the app knows to fall back.
37158
37653
  */
37159
- serve = Option64.Boolean("--serve", false);
37654
+ serve = Option65.Boolean("--serve", false);
37160
37655
  async execute() {
37161
37656
  if (this.serve) {
37162
37657
  return runCompanionBridgeServe(
@@ -37174,7 +37669,7 @@ var CompanionBridgeCommand = class extends Command67 {
37174
37669
  };
37175
37670
 
37176
37671
  // src/commands/companion/status.ts
37177
- import { Command as Command68 } from "clipanion";
37672
+ import { Command as Command69 } from "clipanion";
37178
37673
  async function withTimeout(work, ms) {
37179
37674
  let timer;
37180
37675
  try {
@@ -37246,7 +37741,7 @@ Run: m8t companion install
37246
37741
  }
37247
37742
  var CompanionStatusCommand = class extends M8tCommand {
37248
37743
  static paths = [["companion", "status"]];
37249
- static usage = Command68.Usage({
37744
+ static usage = Command69.Usage({
37250
37745
  description: "Verify the installed desktop companion without launching it."
37251
37746
  });
37252
37747
  async executeCommand() {
@@ -37260,7 +37755,7 @@ var CompanionStatusCommand = class extends M8tCommand {
37260
37755
  };
37261
37756
 
37262
37757
  // src/commands/companion/repair.ts
37263
- import { Command as Command69, Option as Option65 } from "clipanion";
37758
+ import { Command as Command70, Option as Option66 } from "clipanion";
37264
37759
  async function runCompanionRepairCommand(stdout, repair) {
37265
37760
  const state = await repair();
37266
37761
  if (state.state === "not-released") {
@@ -37279,10 +37774,10 @@ async function runCompanionRepairCommand(stdout, repair) {
37279
37774
  }
37280
37775
  var CompanionRepairCommand = class extends M8tCommand {
37281
37776
  static paths = [["companion", "repair"]];
37282
- static usage = Command69.Usage({
37777
+ static usage = Command70.Usage({
37283
37778
  description: "Restore the desktop companions and start-at-login state."
37284
37779
  });
37285
- resourceGroup = Option65.String("--resource-group", {
37780
+ resourceGroup = Option66.String("--resource-group", {
37286
37781
  description: "Which deployment to bind to, when the subscription holds more than one."
37287
37782
  });
37288
37783
  async executeCommand() {
@@ -37300,7 +37795,7 @@ var CompanionRepairCommand = class extends M8tCommand {
37300
37795
  };
37301
37796
 
37302
37797
  // src/commands/companion/uninstall.ts
37303
- import { Command as Command70 } from "clipanion";
37798
+ import { Command as Command71 } from "clipanion";
37304
37799
  async function runCompanionUninstallCommand(stdout, uninstall) {
37305
37800
  const state = await uninstall();
37306
37801
  if (state.state !== "not-installed") {
@@ -37312,7 +37807,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
37312
37807
  }
37313
37808
  var CompanionUninstallCommand = class extends M8tCommand {
37314
37809
  static paths = [["companion", "uninstall"]];
37315
- static usage = Command70.Usage({
37810
+ static usage = Command71.Usage({
37316
37811
  description: "Remove only this user's desktop companion installation."
37317
37812
  });
37318
37813
  async executeCommand() {
@@ -37373,6 +37868,7 @@ cli.register(PlatformConvergeCommand);
37373
37868
  cli.register(PlatformClearIntentCommand);
37374
37869
  cli.register(PlatformRequestUpdateCommand);
37375
37870
  cli.register(PlatformSeedStampCommand);
37871
+ cli.register(PlatformGatewayAdoptCommand);
37376
37872
  cli.register(PlatformPolicyCommand);
37377
37873
  cli.register(PlatformEnableCostReportCommand);
37378
37874
  cli.register(PlatformEmailCommand);