@m8t-stack/cli 0.2.121 → 0.2.123

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.121";
1457
+ var CLI_VERSION = "0.2.123";
1458
1458
 
1459
1459
  // src/lib/render-error.ts
1460
1460
  init_errors();
@@ -16353,8 +16353,8 @@ function isM8tBrainMarker(yamlText) {
16353
16353
  }
16354
16354
  if (!parsed || typeof parsed !== "object") return false;
16355
16355
  const root = parsed;
16356
- const isObj4 = (v) => typeof v === "object" && v !== null;
16357
- return isObj4(root.engine) || isObj4(root.processes) || isObj4(root.link);
16356
+ const isObj3 = (v) => typeof v === "object" && v !== null;
16357
+ return isObj3(root.engine) || isObj3(root.processes) || isObj3(root.link);
16358
16358
  }
16359
16359
  async function createBlankRepo(args) {
16360
16360
  const res = await fetch(`https://api.github.com/orgs/${args.org}/repos`, {
@@ -19485,15 +19485,16 @@ async function stagePublicImageIfNeeded(args) {
19485
19485
  const notes = [];
19486
19486
  const { host, repo, tag } = parseImageRef(args.image);
19487
19487
  if (isAcrHost(host)) return { image: args.image, staged: false, notes };
19488
+ const sourceRef = args.digest ? `${host}/${repo}@${args.digest}` : args.image;
19488
19489
  const acrName = deriveAcrName(args.project.accountName);
19489
19490
  const resourceGroup = resourceGroupFromScope(args.project.accountScope);
19490
- args.onProgress?.(`staging ${args.image} into ${acrName} (server-side import)\u2026`);
19491
+ args.onProgress?.(`staging ${sourceRef} into ${acrName} (server-side import)\u2026`);
19491
19492
  const acr = await ensureAcr({ acrName, resourceGroup, location: args.project.region });
19492
19493
  if (acr.created) notes.push(`created ACR '${acrName}' in ${resourceGroup} for hosted-agent images.`);
19493
- const { imported } = await ensureImageInAcr({ acrName, sourceRef: args.image, repo, tag });
19494
+ const { imported } = await ensureImageInAcr({ acrName, sourceRef, repo, tag });
19494
19495
  const acrRef = `${acr.loginServer}/${repo}:${tag}`;
19495
19496
  notes.push(
19496
- imported ? `imported ${args.image} \u2192 ${acrRef} (server-side, no local build).` : `image already present in ${acrName}; using ${acrRef}.`
19497
+ imported ? `imported ${sourceRef} \u2192 ${acrRef} (server-side, no local build).` : `image already present in ${acrName}; using ${acrRef}.`
19497
19498
  );
19498
19499
  return { image: acrRef, staged: true, notes };
19499
19500
  }
@@ -20536,6 +20537,12 @@ var AzureExecDeployCommand = class extends M8tCommand {
20536
20537
  name = Option31.String();
20537
20538
  image = Option31.String("--image");
20538
20539
  imageTag = Option31.String("--image-tag");
20540
+ // `sha256:<hex>` to import FROM, when the caller knows exactly which bytes it
20541
+ // wants. The cloud installer passes the release manifest's digest so the
20542
+ // executor an install deploys is the one the RELEASE names, not whatever the
20543
+ // engine's own baked pin happened to be — the same correction the gateway got.
20544
+ // The destination in the customer's ACR stays tagged; only the source is pinned.
20545
+ imageDigest = Option31.String("--image-digest", { description: "Import the image from this digest instead of the tag. Must be sha256:<64 hex>." });
20539
20546
  size = Option31.String("--size");
20540
20547
  scope = Option31.String("--scope");
20541
20548
  resourceGroup = Option31.String("--resource-group");
@@ -20624,7 +20631,18 @@ var AzureExecDeployCommand = class extends M8tCommand {
20624
20631
  currentMetadata = { ...current.metadata ?? {} };
20625
20632
  } catch {
20626
20633
  }
20627
- const staged = await stagePublicImageIfNeeded({ image: requestedImage, project });
20634
+ const imageDigest = typeof this.imageDigest === "string" ? this.imageDigest.trim() : "";
20635
+ if (imageDigest && !/^sha256:[0-9a-f]{64}$/.test(imageDigest)) {
20636
+ throw new LocalCliError({
20637
+ code: "AZURE_EXEC_IMAGE_DIGEST_INVALID",
20638
+ message: `--image-digest must be sha256:<64 hex>, got ${JSON.stringify(imageDigest)}.`
20639
+ });
20640
+ }
20641
+ const staged = await stagePublicImageIfNeeded({
20642
+ image: requestedImage,
20643
+ project,
20644
+ ...imageDigest ? { digest: imageDigest } : {}
20645
+ });
20628
20646
  for (const n of staged.notes) {
20629
20647
  warnings.add(n);
20630
20648
  }
@@ -22659,6 +22677,13 @@ var PlatformStatusCommand = class extends M8tCommand {
22659
22677
  kind: "platform-status",
22660
22678
  gateway: resourceGroup,
22661
22679
  targetVersion: manifest.platform.tag,
22680
+ // What the install RECORDS it is running, as opposed to what the channel
22681
+ // currently offers (targetVersion). `managed` already says whether a stamp
22682
+ // exists; this says what it holds — the row the updater diffs against and
22683
+ // fleet telemetry aggregates, which was previously unreadable from this
22684
+ // command at all. null when unstamped, never absent, so a consumer can
22685
+ // tell "no stamp" from "field not emitted by an older CLI".
22686
+ installedVersion: stamp?.platformVersion ?? null,
22662
22687
  managed: stamp !== null,
22663
22688
  rows,
22664
22689
  ...verify ? { verify } : {}
@@ -22666,7 +22691,9 @@ var PlatformStatusCommand = class extends M8tCommand {
22666
22691
  );
22667
22692
  return 0;
22668
22693
  }
22669
- log(colors.field(`target: ${manifest.platform.tag}${stamp === null ? " (unmanaged \u2014 no stamp found)" : ""}`));
22694
+ log(colors.field(
22695
+ `installed: ${stamp?.platformVersion ?? "(none)"} target: ${manifest.platform.tag}` + (stamp === null ? " (unmanaged \u2014 no stamp found)" : "")
22696
+ ));
22670
22697
  this.context.stdout.write(this.renderRows(rows) + "\n");
22671
22698
  if (verify) {
22672
22699
  log("");
@@ -24533,74 +24560,26 @@ import { DefaultAzureCredential as DefaultAzureCredential20, ManagedIdentityCred
24533
24560
  init_errors();
24534
24561
 
24535
24562
  // src/lib/platform-stamp-seed.ts
24536
- var IMAGE_COMPONENTS = ["gateway", "codingAgent", "azureExecutor"];
24537
- var BARE_SEMVER = /^\d+\.\d+\.\d+$/;
24563
+ var BARE_SEMVER = /^\d+\.\d+\.\d+(?:-canary\.(?:0|[1-9]\d*))?$/;
24538
24564
  function isBarePlatformVersion(v) {
24539
24565
  return typeof v === "string" && BARE_SEMVER.test(v);
24540
24566
  }
24541
- function isObj3(v) {
24542
- return typeof v === "object" && v !== null && !Array.isArray(v);
24543
- }
24544
- function validateDescriptor(raw) {
24545
- const errors = [];
24546
- if (!isObj3(raw)) return ["descriptor is not an object"];
24547
- if (raw.schemaVersion !== 1) errors.push("schemaVersion must be the integer 1");
24548
- if (typeof raw.platformVersion !== "string" || !BARE_SEMVER.test(raw.platformVersion)) {
24549
- errors.push(`platformVersion must be a bare X.Y.Z release version, got ${JSON.stringify(raw.platformVersion)}`);
24550
- }
24551
- if (!isObj3(raw.generatedFrom)) {
24552
- errors.push("generatedFrom is required");
24553
- } else {
24554
- if (typeof raw.generatedFrom.commit !== "string") {
24555
- errors.push("generatedFrom.commit is required");
24556
- }
24557
- if (typeof raw.generatedFrom.generatedAt !== "string") {
24558
- errors.push("generatedFrom.generatedAt is required");
24559
- }
24560
- }
24561
- const images = raw.images;
24562
- if (!isObj3(images)) {
24563
- errors.push("images is required");
24564
- } else {
24565
- for (const key2 of IMAGE_COMPONENTS) {
24566
- const img = images[key2];
24567
- if (!isObj3(img)) {
24568
- errors.push(`images.${key2} is required`);
24569
- continue;
24570
- }
24571
- if (typeof img.ref !== "string" || img.ref.length === 0) errors.push(`images.${key2}.ref is required`);
24572
- if (typeof img.tag !== "string" || img.tag.length === 0) errors.push(`images.${key2}.tag is required`);
24573
- if (typeof img.digest !== "string") errors.push(`images.${key2}.digest must be a string (may be empty)`);
24574
- }
24575
- }
24576
- const content = raw.content;
24577
- if (content !== void 0) {
24578
- if (!isObj3(content)) {
24579
- errors.push("content must be an object when present");
24580
- } else {
24581
- if (!isObj3(content.personas)) errors.push("content.personas must be an object");
24582
- if (typeof content.infra !== "string") errors.push("content.infra must be a string");
24583
- if (typeof content.brainSeedsRev !== "string") errors.push("content.brainSeedsRev must be a string");
24584
- }
24585
- }
24586
- return errors;
24587
- }
24588
- function buildSeedStamp(d, cliVersion, nowIso, platformVersionOverride) {
24589
- if (platformVersionOverride !== void 0 && !isBarePlatformVersion(platformVersionOverride)) {
24590
- throw new Error(`platformVersionOverride must be a bare X.Y.Z release version, got ${JSON.stringify(platformVersionOverride)}`);
24567
+ function buildSeedStamp(m, cliVersion, nowIso) {
24568
+ if (!isBarePlatformVersion(m.platform.version)) {
24569
+ throw new Error(`manifest platform.version must be a bare X.Y.Z release version, got ${JSON.stringify(m.platform.version)}`);
24591
24570
  }
24592
24571
  const img = (k) => ({
24593
- tag: d.images[k].tag,
24594
- digest: d.images[k].digest,
24572
+ tag: m.components[k].tag,
24573
+ digest: m.components[k].digest,
24595
24574
  state: "managed"
24596
24575
  });
24597
24576
  const personas = {};
24598
- for (const [name, treeSha] of Object.entries(d.content?.personas ?? {})) {
24599
- personas[name] = { treeSha, foundryVersion: "" };
24577
+ for (const [name, item] of Object.entries(m.components.personas.items)) {
24578
+ personas[name] = { treeSha: item.treeSha, foundryVersion: "" };
24600
24579
  }
24601
24580
  const stamp = {
24602
24581
  schemaVersion: 1,
24603
- platformVersion: platformTag(platformVersionOverride ?? d.platformVersion),
24582
+ platformVersion: platformTag(m.platform.version),
24604
24583
  previousPlatformVersion: null,
24605
24584
  updatedAt: nowIso,
24606
24585
  lastResult: "success",
@@ -24611,22 +24590,16 @@ function buildSeedStamp(d, cliVersion, nowIso, platformVersionOverride) {
24611
24590
  codingAgent: img("codingAgent"),
24612
24591
  azureExecutor: img("azureExecutor"),
24613
24592
  personas,
24614
- infra: { treeSha: d.content?.infra ?? "" }
24593
+ infra: { treeSha: m.components.infra?.treeSha ?? "" }
24615
24594
  }
24616
24595
  };
24617
- if (d.content?.brainSeedsRev) stamp.components.brainSeeds = { seedRev: d.content.brainSeedsRev };
24596
+ const seedRev = seedSetRev(m);
24597
+ if (seedRev) stamp.components.brainSeeds = { seedRev };
24618
24598
  return stamp;
24619
24599
  }
24620
24600
 
24621
24601
  // src/commands/platform/seed-stamp.ts
24622
24602
  async function seedStamp2(args) {
24623
- if (args.platformVersion !== void 0 && !isBarePlatformVersion(args.platformVersion)) {
24624
- throw new LocalCliError({
24625
- code: "PLATFORM_SEED_VERSION_INVALID",
24626
- message: `--platform-version must be a bare X.Y.Z release version, got ${JSON.stringify(args.platformVersion)}.`,
24627
- hint: "Pass the platform release this install is deploying, e.g. --platform-version 0.7.8. Omit the flag to stamp the version baked into the installer image."
24628
- });
24629
- }
24630
24603
  const readFileImpl = args.readFileImpl ?? ((p) => readFileSync20(p, "utf8"));
24631
24604
  const readStampImpl = args.readStampImpl ?? readStamp;
24632
24605
  const writeStampImpl = args.writeStampImpl ?? ((stamp2, o) => writeStamp({ ...o, stamp: stamp2 }));
@@ -24634,34 +24607,34 @@ async function seedStamp2(args) {
24634
24607
  const ctx = { credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup };
24635
24608
  let raw;
24636
24609
  try {
24637
- raw = JSON.parse(readFileImpl(args.descriptorPath));
24610
+ raw = JSON.parse(readFileImpl(args.manifestPath));
24638
24611
  } catch (e) {
24639
24612
  throw new LocalCliError({
24640
- code: "PLATFORM_SEED_DESCRIPTOR_UNREADABLE",
24641
- message: `Cannot read the install descriptor at ${args.descriptorPath}.`,
24642
- hint: "The descriptor is generated at installer-image build time (scripts/install-descriptor-gen.mjs). An image built without that step cannot seed a stamp.",
24613
+ code: "PLATFORM_SEED_MANIFEST_UNREADABLE",
24614
+ message: `Cannot read the release manifest at ${args.manifestPath}.`,
24615
+ hint: "The installer writes it at preflight after verifying its digest. Reaching this without one means the manifest gate was bypassed.",
24643
24616
  cause: e
24644
24617
  });
24645
24618
  }
24646
- const errors = validateDescriptor(raw);
24619
+ const errors = validateManifest(raw);
24647
24620
  if (errors.length > 0) {
24648
24621
  throw new LocalCliError({
24649
- code: "PLATFORM_SEED_DESCRIPTOR_INVALID",
24650
- message: `The install descriptor at ${args.descriptorPath} is not valid: ${errors.join("; ")}.`,
24651
- hint: "Regenerate it with scripts/install-descriptor-gen.mjs."
24622
+ code: "PLATFORM_SEED_MANIFEST_INVALID",
24623
+ message: `The release manifest at ${args.manifestPath} is not valid: ${errors.join("; ")}.`,
24624
+ hint: "This install was launched against a manifest the platform cannot read. Nothing was stamped."
24652
24625
  });
24653
24626
  }
24654
- const descriptor = raw;
24627
+ const manifest = raw;
24655
24628
  const { accountName } = await discoverImpl(ctx);
24656
24629
  const existing = await readStampImpl(ctx);
24657
24630
  if (existing) {
24658
24631
  args.onProgress?.(`platform stamp already present (${existing.platformVersion}) \u2014 leaving it untouched.`);
24659
24632
  return { outcome: "already-stamped", platformVersion: existing.platformVersion, storageAccount: accountName };
24660
24633
  }
24661
- const stamp = buildSeedStamp(descriptor, CLI_VERSION, args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString(), args.platformVersion);
24634
+ const stamp = buildSeedStamp(manifest, CLI_VERSION, args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString());
24662
24635
  await writeStampImpl(stamp, { ...ctx, onProgress: args.onProgress });
24663
24636
  return {
24664
- outcome: descriptor.content ? "seeded" : "seeded-partial",
24637
+ outcome: "seeded",
24665
24638
  platformVersion: stamp.platformVersion,
24666
24639
  storageAccount: accountName,
24667
24640
  components: {
@@ -24674,11 +24647,10 @@ async function seedStamp2(args) {
24674
24647
  var PlatformSeedStampCommand = class extends M8tCommand {
24675
24648
  static paths = [["platform", "seed-stamp"]];
24676
24649
  static usage = Command39.Usage({
24677
- description: "INTERNAL: write the initial installed-state stamp from a build-time install descriptor.",
24678
- details: "Used by the cloud installer at the end of a from-zero install so the platform records which release it is running. Writes the stamp ONLY IF ABSENT \u2014 it never overwrites a stamp written by the converge engine, so a re-run or a container restart is harmless. Not a founder-facing command: to change what is installed, use 'm8t platform update'."
24650
+ description: "INTERNAL: write the initial installed-state stamp from the release manifest this install deployed.",
24651
+ details: "Used by the cloud installer at the end of a from-zero install so the platform records which release it is running. Seeds from the SAME document every later converge stamps from, so the first update diffs against a row that describes the same release. Writes the stamp ONLY IF ABSENT \u2014 it never overwrites a stamp written by the converge engine, so a re-run or a container restart is harmless. Not a founder-facing command: to change what is installed, use 'm8t platform update'."
24679
24652
  });
24680
- descriptor = Option36.String("--descriptor", { description: "Path to install-descriptor.json (baked into the installer image)." });
24681
- platformVersion = Option36.String("--platform-version", { description: "Bare X.Y.Z release this install is deploying. Overrides the descriptor's baked version, which lags by one declaration under publish->select. Omit to use the baked value." });
24653
+ manifest = Option36.String("--manifest", { description: "Path to the fetched, digest-verified release manifest for the release this install deployed." });
24682
24654
  subscription = Option36.String("--subscription");
24683
24655
  resourceGroup = Option36.String("--resource-group");
24684
24656
  miClientId = Option36.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
@@ -24697,13 +24669,7 @@ var PlatformSeedStampCommand = class extends M8tCommand {
24697
24669
  };
24698
24670
  const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential4({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential20();
24699
24671
  const result2 = await seedStamp2({
24700
- descriptorPath: need(this.descriptor, "--descriptor"),
24701
- // Absent flag ⇒ undefined ⇒ the descriptor's baked value. A flag PASSED but
24702
- // empty is not the same thing and is refused below, deliberately: the
24703
- // installer omits the flag entirely when it has nothing to say
24704
- // (installer/lib.sh), so an empty one means a launcher computed a version and
24705
- // got nothing — which should be loud, not silently absorbed by the fallback.
24706
- platformVersion: typeof this.platformVersion === "string" ? this.platformVersion.trim() : void 0,
24672
+ manifestPath: need(this.manifest, "--manifest"),
24707
24673
  credential: credential2,
24708
24674
  subscriptionId: need(this.subscription, "--subscription"),
24709
24675
  resourceGroup: need(this.resourceGroup, "--resource-group"),
@@ -24719,23 +24685,141 @@ var PlatformSeedStampCommand = class extends M8tCommand {
24719
24685
  }
24720
24686
  };
24721
24687
 
24722
- // src/commands/platform/gateway-adopt.ts
24688
+ // src/commands/platform/verify-content.ts
24689
+ import { readFileSync as readFileSync21 } from "fs";
24723
24690
  import { Command as Command40, Option as Option37 } from "clipanion";
24691
+ init_errors();
24692
+
24693
+ // src/lib/platform-content-crosscheck.ts
24694
+ function crossCheckContent(manifest, content) {
24695
+ const problems = [];
24696
+ const manifestInfra = manifest.components.infra?.treeSha ?? "";
24697
+ if (content.infra !== manifestInfra) {
24698
+ problems.push({ field: "infra", baked: content.infra, manifest: manifestInfra });
24699
+ }
24700
+ for (const [name, item] of Object.entries(manifest.components.personas.items)) {
24701
+ if (!Object.hasOwn(content.personas, name)) continue;
24702
+ if (content.personas[name] !== item.treeSha) {
24703
+ problems.push({ field: `personas.${name}`, baked: content.personas[name], manifest: item.treeSha });
24704
+ }
24705
+ }
24706
+ const manifestSeeds = manifest.components.brainSeeds.items;
24707
+ for (const entry of content.brainSeedsRev.split("|")) {
24708
+ if (!entry) continue;
24709
+ const first = entry.indexOf(":");
24710
+ const second = entry.indexOf(":", first + 1);
24711
+ if (first < 0 || second < 0) continue;
24712
+ const name = entry.slice(0, first);
24713
+ const sub = entry.slice(first + 1, second);
24714
+ const baked = entry.slice(second + 1);
24715
+ if (!Object.hasOwn(manifestSeeds, name)) continue;
24716
+ const subtrees = manifestSeeds[name].subtrees;
24717
+ if (!Object.hasOwn(subtrees, sub)) continue;
24718
+ const want = subtrees[sub] ?? "-";
24719
+ if (baked !== want) {
24720
+ problems.push({ field: `brainSeeds.${name}.${sub}`, baked, manifest: want });
24721
+ }
24722
+ }
24723
+ return problems;
24724
+ }
24725
+ function describeCrossCheckProblems(problems, version) {
24726
+ if (problems.length === 0) return "";
24727
+ const lines = problems.map((p) => ` ${p.field}
24728
+ this image carries: ${p.baked}
24729
+ ${version} names: ${p.manifest}`);
24730
+ return `This installer image does not carry the content that platform ${version} names.
24731
+ ${lines.join("\n")}
24732
+
24733
+ The install was stopped before anything was deployed. This is a fault in the release, not in your setup: the engine and the manifest were built from different trees. Get the newest CLI with \`npm install -g @m8t-stack/cli\` and start the install again.`;
24734
+ }
24735
+
24736
+ // src/commands/platform/verify-content.ts
24737
+ function readJson(path47, what) {
24738
+ let raw;
24739
+ try {
24740
+ raw = readFileSync21(path47, "utf8");
24741
+ } catch (e) {
24742
+ throw new LocalCliError({ code: "PLATFORM_VERIFY_READ_FAILED", message: `Could not read the ${what} at '${path47}': ${e.message}` });
24743
+ }
24744
+ try {
24745
+ return JSON.parse(raw);
24746
+ } catch (e) {
24747
+ throw new LocalCliError({ code: "PLATFORM_VERIFY_PARSE_FAILED", message: `The ${what} at '${path47}' is not valid JSON: ${e.message}` });
24748
+ }
24749
+ }
24750
+ var PlatformVerifyContentCommand = class extends M8tCommand {
24751
+ static paths = [["platform", "verify-content"]];
24752
+ static usage = Command40.Usage({
24753
+ category: "Platform",
24754
+ description: "INTERNAL: check that this installer image's baked content is what the release manifest names.",
24755
+ details: "Run by the cloud installer at preflight, before anything is deployed. Compares the image's install-descriptor.json content block (persona tree SHAs, the deploy/ tree SHA, the brain-seed rev \u2014 all computed from the tree the image actually baked) against the same facts in the release manifest the install was handed. Exits non-zero, naming every field that disagrees and both values, when they do not match. Not a founder-facing command."
24756
+ });
24757
+ manifest = Option37.String("--manifest", { description: "Path to the fetched, digest-verified release manifest." });
24758
+ descriptor = Option37.String("--descriptor", { description: "Path to install-descriptor.json (baked into this image)." });
24759
+ // Not `async`: this command is pure file reads and a comparison, with no
24760
+ // awaited work at all. The base class wants a Promise, so it gets one.
24761
+ executeCommand() {
24762
+ const need = (v, flag) => {
24763
+ if (typeof v !== "string" || v.trim().length === 0) {
24764
+ throw new LocalCliError({ code: "PLATFORM_VERIFY_ARG_MISSING", message: `${flag} is required.` });
24765
+ }
24766
+ return v.trim();
24767
+ };
24768
+ const manifestPath = need(this.manifest, "--manifest");
24769
+ const rawManifest = readJson(manifestPath, "release manifest");
24770
+ const manifestErrors = validateManifest(rawManifest);
24771
+ if (manifestErrors.length > 0) {
24772
+ throw new LocalCliError({
24773
+ code: "PLATFORM_VERIFY_MANIFEST_INVALID",
24774
+ message: `The release manifest at '${manifestPath}' is not a manifest this platform can read:
24775
+ - ${manifestErrors.join("\n - ")}`,
24776
+ hint: "Nothing was compared and nothing was deployed. This is a fault in the release, not in your setup."
24777
+ });
24778
+ }
24779
+ const manifest = rawManifest;
24780
+ const descriptor = readJson(need(this.descriptor, "--descriptor"), "install descriptor");
24781
+ const content = descriptor.content;
24782
+ if (!content || typeof content.brainSeedsRev !== "string" || typeof content.infra !== "string" || typeof content.personas !== "object") {
24783
+ throw new LocalCliError({
24784
+ code: "PLATFORM_VERIFY_DESCRIPTOR_INCOMPLETE",
24785
+ message: "This installer image's descriptor carries no usable content block, so there is nothing to check the release manifest against.",
24786
+ hint: "The image was built without scripts/install-descriptor-gen.mjs, or built where it could not read its own tree. It is malformed \u2014 reinstall with the current CLI (`npm install -g @m8t-stack/cli`)."
24787
+ });
24788
+ }
24789
+ const problems = crossCheckContent(manifest, {
24790
+ personas: content.personas,
24791
+ infra: content.infra,
24792
+ brainSeedsRev: content.brainSeedsRev
24793
+ });
24794
+ if (problems.length > 0) {
24795
+ throw new LocalCliError({
24796
+ code: "PLATFORM_VERIFY_CONTENT_MISMATCH",
24797
+ message: describeCrossCheckProblems(problems, manifest.platform.version)
24798
+ });
24799
+ }
24800
+ this.context.stderr.write(`content verified against platform ${manifest.platform.version}
24801
+ `);
24802
+ return Promise.resolve(0);
24803
+ }
24804
+ };
24805
+
24806
+ // src/commands/platform/gateway-adopt.ts
24807
+ import { Command as Command41, Option as Option38 } from "clipanion";
24724
24808
  import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
24725
24809
  init_errors();
24726
24810
  var PlatformGatewayAdoptCommand = class extends M8tCommand {
24727
24811
  static paths = [["platform", "gateway", "adopt"]];
24728
- static usage = Command40.Usage({
24812
+ static usage = Command41.Usage({
24729
24813
  description: "Explicitly authorize one legacy m8t ACR gateway for managed, digest-pinned updates.",
24730
24814
  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."
24731
24815
  });
24732
- subscription = Option37.String("--subscription");
24733
- resourceGroup = Option37.String("--resource-group");
24734
- target = Option37.String("--target", { description: "Published platform target whose installer contains adoption support." });
24735
- expectedImage = Option37.String("--expected-image", { description: "Exact live legacy ACR image ref." });
24736
- expectedDigest = Option37.String("--expected-digest", { description: "Exact sha256 digest resolved from that ACR image." });
24737
- expectedAcrResourceId = Option37.String("--expected-acr-resource-id", { description: "Exact ARM id of the legacy ACR." });
24738
- output = Option37.String("--output");
24816
+ subscription = Option38.String("--subscription");
24817
+ resourceGroup = Option38.String("--resource-group");
24818
+ target = Option38.String("--target", { description: "Published platform target whose installer contains adoption support." });
24819
+ expectedImage = Option38.String("--expected-image", { description: "Exact live legacy ACR image ref." });
24820
+ expectedDigest = Option38.String("--expected-digest", { description: "Exact sha256 digest resolved from that ACR image." });
24821
+ expectedAcrResourceId = Option38.String("--expected-acr-resource-id", { description: "Exact ARM id of the legacy ACR." });
24822
+ output = Option38.String("--output");
24739
24823
  async executeCommand() {
24740
24824
  const need = (v, flag) => {
24741
24825
  if (typeof v !== "string" || v.trim() === "") throw new LocalCliError({ code: "PLATFORM_GATEWAY_ADOPTION_ARG_MISSING", message: `${flag} is required.` });
@@ -24802,7 +24886,7 @@ var PlatformGatewayAdoptCommand = class extends M8tCommand {
24802
24886
  };
24803
24887
 
24804
24888
  // src/commands/platform/policy.ts
24805
- import { Command as Command41, Option as Option38 } from "clipanion";
24889
+ import { Command as Command42, Option as Option39 } from "clipanion";
24806
24890
  import { DefaultAzureCredential as DefaultAzureCredential22, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
24807
24891
  init_errors();
24808
24892
 
@@ -24858,16 +24942,16 @@ async function readPolicy(opts) {
24858
24942
  // src/commands/platform/policy.ts
24859
24943
  var PlatformPolicyCommand = class extends M8tCommand {
24860
24944
  static paths = [["platform", "policy"]];
24861
- static usage = Command41.Usage({
24945
+ static usage = Command42.Usage({
24862
24946
  description: "Show or set how this install handles available platform updates.",
24863
24947
  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.",
24864
24948
  examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
24865
24949
  });
24866
- set = Option38.String("--set", { description: "notify-only | auto-critical | auto-all" });
24867
- subscription = Option38.String("--subscription");
24868
- resourceGroup = Option38.String("--resource-group");
24869
- miClientId = Option38.String("--mi-client-id");
24870
- output = Option38.String("--output");
24950
+ set = Option39.String("--set", { description: "notify-only | auto-critical | auto-all" });
24951
+ subscription = Option39.String("--subscription");
24952
+ resourceGroup = Option39.String("--resource-group");
24953
+ miClientId = Option39.String("--mi-client-id");
24954
+ output = Option39.String("--output");
24871
24955
  async executeCommand() {
24872
24956
  const mode = resolveOutputMode(this.output, this.context.stdout);
24873
24957
  const need = (v, flag) => {
@@ -24913,7 +24997,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
24913
24997
  };
24914
24998
 
24915
24999
  // src/commands/platform/enable-cost-report.ts
24916
- import { Command as Command42, Option as Option39 } from "clipanion";
25000
+ import { Command as Command43, Option as Option40 } from "clipanion";
24917
25001
 
24918
25002
  // src/lib/wire-gateway-acs.ts
24919
25003
  init_rbac();
@@ -24953,18 +25037,18 @@ async function wireGatewayForAcs(args) {
24953
25037
  init_errors();
24954
25038
  var PlatformEnableCostReportCommand = class extends M8tCommand {
24955
25039
  static paths = [["platform", "enable-cost-report"]];
24956
- static usage = Command42.Usage({
25040
+ static usage = Command43.Usage({
24957
25041
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
24958
25042
  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')."
24959
25043
  });
24960
- subscription = Option39.String("--subscription");
24961
- resourceGroup = Option39.String("--resource-group", {
25044
+ subscription = Option40.String("--subscription");
25045
+ resourceGroup = Option40.String("--resource-group", {
24962
25046
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24963
25047
  });
24964
- acsEndpoint = Option39.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
24965
- acsSender = Option39.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
24966
- acsResourceId = Option39.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
24967
- output = Option39.String("--output");
25048
+ acsEndpoint = Option40.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
25049
+ acsSender = Option40.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
25050
+ acsResourceId = Option40.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
25051
+ output = Option40.String("--output");
24968
25052
  async executeCommand() {
24969
25053
  const mode = resolveOutputMode(
24970
25054
  this.output,
@@ -25038,13 +25122,13 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
25038
25122
  };
25039
25123
 
25040
25124
  // src/commands/platform/email.ts
25041
- import { Command as Command43, Option as Option40 } from "clipanion";
25125
+ import { Command as Command44, Option as Option41 } from "clipanion";
25042
25126
  import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
25043
25127
  import { CommunicationServiceManagementClient as CommunicationServiceManagementClient2 } from "@azure/arm-communication";
25044
25128
  init_errors();
25045
25129
  var PlatformEmailCommand = class extends M8tCommand {
25046
25130
  static paths = [["platform", "email"]];
25047
- static usage = Command43.Usage({
25131
+ static usage = Command44.Usage({
25048
25132
  description: "Turn outbound email (advisor handoffs) on or off for this install.",
25049
25133
  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.",
25050
25134
  examples: [
@@ -25052,19 +25136,19 @@ var PlatformEmailCommand = class extends M8tCommand {
25052
25136
  ["Turn it off (a shared, public-facing deployment should stay off)", "m8t platform email off"]
25053
25137
  ]
25054
25138
  });
25055
- state = Option40.String({ required: true, name: "on|off" });
25056
- subscription = Option40.String("--subscription");
25057
- resourceGroup = Option40.String("--resource-group", {
25139
+ state = Option41.String({ required: true, name: "on|off" });
25140
+ subscription = Option41.String("--subscription");
25141
+ resourceGroup = Option41.String("--resource-group", {
25058
25142
  description: "m8t resource group, to disambiguate in a multi-deployment subscription."
25059
25143
  });
25060
- agent = Option40.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
25061
- kvUri = Option40.String("--kv-uri", {
25144
+ agent = Option41.String("--agent", { description: `Executor agent name. Resolved by persona when omitted (usually ${EXECUTOR_AGENT_FALLBACK}).` });
25145
+ kvUri = Option41.String("--kv-uri", {
25062
25146
  description: "Install's Key Vault URI. Only needed if no ACS is on record yet and one must be provisioned."
25063
25147
  });
25064
- endpoint = Option40.String("--endpoint", {
25148
+ endpoint = Option41.String("--endpoint", {
25065
25149
  description: "Foundry project endpoint, to disambiguate a subscription holding several."
25066
25150
  });
25067
- output = Option40.String("--output");
25151
+ output = Option41.String("--output");
25068
25152
  async executeCommand() {
25069
25153
  const wanted = this.state.trim().toLowerCase();
25070
25154
  if (wanted !== "on" && wanted !== "off") {
@@ -25214,45 +25298,45 @@ var PlatformEmailCommand = class extends M8tCommand {
25214
25298
  };
25215
25299
 
25216
25300
  // src/commands/platform/enable-auto-update.ts
25217
- import { Command as Command44, Option as Option41 } from "clipanion";
25301
+ import { Command as Command45, Option as Option42 } from "clipanion";
25218
25302
  import { confirm as confirm6 } from "@inquirer/prompts";
25219
25303
  import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
25220
25304
  init_errors();
25221
25305
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
25222
25306
  static paths = [["platform", "enable-auto-update"]];
25223
- static usage = Command44.Usage({
25307
+ static usage = Command45.Usage({
25224
25308
  category: "Platform",
25225
25309
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
25226
25310
  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."
25227
25311
  });
25228
- subscription = Option41.String("--subscription");
25229
- resourceGroup = Option41.String("--resource-group", {
25312
+ subscription = Option42.String("--subscription");
25313
+ resourceGroup = Option42.String("--resource-group", {
25230
25314
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
25231
25315
  });
25232
- suffix = Option41.String("--suffix", {
25316
+ suffix = Option42.String("--suffix", {
25233
25317
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
25234
25318
  });
25235
- installerImage = Option41.String("--installer-image", {
25319
+ installerImage = Option42.String("--installer-image", {
25236
25320
  required: true,
25237
25321
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
25238
25322
  });
25239
- updateCron = Option41.String("--update-cron", {
25323
+ updateCron = Option42.String("--update-cron", {
25240
25324
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
25241
25325
  });
25242
- channelUrl = Option41.String("--channel-url", {
25326
+ channelUrl = Option42.String("--channel-url", {
25243
25327
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
25244
25328
  });
25245
- location = Option41.String("--location", {
25329
+ location = Option42.String("--location", {
25246
25330
  description: "Region for the updater identity + job. Defaults to this install's stamped region, then to the resource group's existing resources."
25247
25331
  });
25248
- foundryTracing = Option41.String("--foundry-tracing", {
25332
+ foundryTracing = Option42.String("--foundry-tracing", {
25249
25333
  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)."
25250
25334
  });
25251
- endpoint = Option41.String("--endpoint", {
25335
+ endpoint = Option42.String("--endpoint", {
25252
25336
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
25253
25337
  });
25254
- yes = Option41.Boolean("--yes", false);
25255
- output = Option41.String("--output");
25338
+ yes = Option42.Boolean("--yes", false);
25339
+ output = Option42.String("--output");
25256
25340
  async executeCommand() {
25257
25341
  const mode = resolveOutputMode(
25258
25342
  this.output,
@@ -25433,7 +25517,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
25433
25517
  };
25434
25518
 
25435
25519
  // src/commands/deploy.ts
25436
- import { Command as Command45, Option as Option42 } from "clipanion";
25520
+ import { Command as Command46, Option as Option43 } from "clipanion";
25437
25521
 
25438
25522
  // src/lib/app-reg.ts
25439
25523
  init_esm();
@@ -25997,15 +26081,15 @@ function classifyWhatIf(changes) {
25997
26081
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
25998
26082
  var DeployCommand = class extends M8tCommand {
25999
26083
  static paths = [["deploy"]];
26000
- static usage = Command45.Usage({
26084
+ static usage = Command46.Usage({
26001
26085
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
26002
26086
  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."
26003
26087
  });
26004
- subscription = Option42.String("--subscription");
26005
- resourceGroup = Option42.String("--resource-group", "rg-m8t-stack");
26006
- location = Option42.String("--location", "eastus");
26007
- suffix = Option42.String("--suffix", "");
26008
- imageRef = Option42.String("--image-ref", DEFAULT_IMAGE_REF);
26088
+ subscription = Option43.String("--subscription");
26089
+ resourceGroup = Option43.String("--resource-group", "rg-m8t-stack");
26090
+ location = Option43.String("--location", "eastus");
26091
+ suffix = Option43.String("--suffix", "");
26092
+ imageRef = Option43.String("--image-ref", DEFAULT_IMAGE_REF);
26009
26093
  // Gateway-only override. Empty ⇒ the gateway uses --image-ref, which is the
26010
26094
  // from-zero case. It exists because the gateway and the voice relay do NOT
26011
26095
  // always run the same image: a converge preserves a BYOC gateway on its own
@@ -26013,28 +26097,28 @@ var DeployCommand = class extends M8tCommand {
26013
26097
  // (`--what-if`) that can only express one image therefore reports the other
26014
26098
  // app as drift on every single run, forever — which is exactly what the
26015
26099
  // infra-drift gate did from 2026-08-11.
26016
- gatewayImageRef = Option42.String("--gateway-image-ref", "");
26017
- acrPullIdentity = Option42.String("--acrpull-identity");
26018
- acrResourceId = Option42.String("--acr-resource-id");
26019
- foundryEndpoint = Option42.String("--foundry-endpoint");
26020
- foundryResourceId = Option42.String("--foundry-resource-id");
26021
- foundryTracing = Option42.String("--foundry-tracing");
26100
+ gatewayImageRef = Option43.String("--gateway-image-ref", "");
26101
+ acrPullIdentity = Option43.String("--acrpull-identity");
26102
+ acrResourceId = Option43.String("--acr-resource-id");
26103
+ foundryEndpoint = Option43.String("--foundry-endpoint");
26104
+ foundryResourceId = Option43.String("--foundry-resource-id");
26105
+ foundryTracing = Option43.String("--foundry-tracing");
26022
26106
  // project | account | skip (bicep default: project)
26023
- clientId = Option42.String("--client-id");
26024
- whatIf = Option42.Boolean("--what-if", false);
26107
+ clientId = Option43.String("--client-id");
26108
+ whatIf = Option43.Boolean("--what-if", false);
26025
26109
  // Only meaningful with --what-if. Routes the comparison through the
26026
26110
  // value-free renderers (see ./lib/whatif-redact.js) instead of the default
26027
26111
  // before/after renderer. Defaults false so a local, interactive run keeps
26028
26112
  // showing values — that is the whole diagnostic point of --what-if.
26029
26113
  // Automation that forwards this output anywhere non-private (a CI log, an
26030
26114
  // issue) MUST pass --redact.
26031
- redact = Option42.Boolean("--redact", false);
26032
- output = Option42.String("--output");
26115
+ redact = Option43.Boolean("--redact", false);
26116
+ output = Option43.String("--output");
26033
26117
  // Subscription-scoped role assignments. Omitted ⇒ the template default (true).
26034
26118
  // Pass false when deploying as a principal scoped to the resource group only:
26035
26119
  // it cannot deploy at subscription scope, and those assignments persist
26036
26120
  // idempotently from the initial deployment anyway.
26037
- assignSubscriptionRoles = Option42.String("--assign-subscription-roles");
26121
+ assignSubscriptionRoles = Option43.String("--assign-subscription-roles");
26038
26122
  /**
26039
26123
  * The installer image the updater Container-Apps Job runs.
26040
26124
  *
@@ -26045,23 +26129,23 @@ var DeployCommand = class extends M8tCommand {
26045
26129
  * than that — the comparison proposes REMOVING an updater job that exists and
26046
26130
  * should, and reports it as drift on every single run.
26047
26131
  */
26048
- installerImage = Option42.String("--installer-image");
26132
+ installerImage = Option43.String("--installer-image");
26049
26133
  // Referee — all optional, undefined by default ⇒ the bicep defaults
26050
26134
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
26051
26135
  // empty). Only pass these when explicitly enabling the referee exam stack.
26052
- gatewayCpu = Option42.String("--gateway-cpu");
26053
- gatewayMemory = Option42.String("--gateway-memory");
26054
- refereeEnabled = Option42.String("--referee-enabled");
26055
- refereeBrainRepos = Option42.String("--referee-brain-repos");
26056
- refereeFeedRepo = Option42.String("--referee-feed-repo");
26057
- refereeInstallationId = Option42.String("--referee-installation-id");
26058
- refereeWebhookHmacKvUri = Option42.String("--referee-webhook-hmac-kv-uri");
26059
- examKvUri = Option42.String("--exam-kv-uri");
26060
- examLaWorkspaceId = Option42.String("--exam-la-workspace-id");
26061
- brainEvalDeployment = Option42.String("--brain-eval-deployment");
26062
- brainAppLogin = Option42.String("--brain-app-login");
26063
- refereeCheckpointDir = Option42.String("--referee-checkpoint-dir");
26064
- examApiBase = Option42.String("--exam-api-base");
26136
+ gatewayCpu = Option43.String("--gateway-cpu");
26137
+ gatewayMemory = Option43.String("--gateway-memory");
26138
+ refereeEnabled = Option43.String("--referee-enabled");
26139
+ refereeBrainRepos = Option43.String("--referee-brain-repos");
26140
+ refereeFeedRepo = Option43.String("--referee-feed-repo");
26141
+ refereeInstallationId = Option43.String("--referee-installation-id");
26142
+ refereeWebhookHmacKvUri = Option43.String("--referee-webhook-hmac-kv-uri");
26143
+ examKvUri = Option43.String("--exam-kv-uri");
26144
+ examLaWorkspaceId = Option43.String("--exam-la-workspace-id");
26145
+ brainEvalDeployment = Option43.String("--brain-eval-deployment");
26146
+ brainAppLogin = Option43.String("--brain-app-login");
26147
+ refereeCheckpointDir = Option43.String("--referee-checkpoint-dir");
26148
+ examApiBase = Option43.String("--exam-api-base");
26065
26149
  async executeCommand() {
26066
26150
  const mode = resolveOutputMode(
26067
26151
  this.output,
@@ -26245,7 +26329,7 @@ var DeployCommand = class extends M8tCommand {
26245
26329
 
26246
26330
  // src/commands/eval/skill.ts
26247
26331
  import { spawnSync as spawnSync4 } from "child_process";
26248
- import { Command as Command46, Option as Option43 } from "clipanion";
26332
+ import { Command as Command47, Option as Option44 } from "clipanion";
26249
26333
  init_errors();
26250
26334
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
26251
26335
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -26270,14 +26354,14 @@ function parseVerdict(stdout) {
26270
26354
  }
26271
26355
  var EvalSkillCommand = class extends M8tCommand {
26272
26356
  static paths = [["eval", "skill"]];
26273
- static usage = Command46.Usage({
26357
+ static usage = Command47.Usage({
26274
26358
  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)."
26275
26359
  });
26276
- candidate = Option43.String();
26277
- skillsDir = Option43.String("--skills-dir");
26278
- noJudge = Option43.Boolean("--no-judge", false);
26279
- deployment = Option43.String("--deployment");
26280
- output = Option43.String("--output");
26360
+ candidate = Option44.String();
26361
+ skillsDir = Option44.String("--skills-dir");
26362
+ noJudge = Option44.Boolean("--no-judge", false);
26363
+ deployment = Option44.String("--deployment");
26364
+ output = Option44.String("--output");
26281
26365
  executeCommand() {
26282
26366
  return Promise.resolve(this._runCommand());
26283
26367
  }
@@ -26334,9 +26418,9 @@ var EvalSkillCommand = class extends M8tCommand {
26334
26418
 
26335
26419
  // src/commands/eval/exam.ts
26336
26420
  import { spawnSync as spawnSync5 } from "child_process";
26337
- import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
26421
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync22, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
26338
26422
  import { join as join30 } from "path";
26339
- import { Command as Command47, Option as Option44 } from "clipanion";
26423
+ import { Command as Command48, Option as Option45 } from "clipanion";
26340
26424
  init_errors();
26341
26425
  init_esm();
26342
26426
  function parseArmToken(tok, opts) {
@@ -26539,7 +26623,7 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
26539
26623
  const manifestPath = join30(home, "tasksets", worker, versionDir, "manifest.yaml");
26540
26624
  let text;
26541
26625
  try {
26542
- text = readFileSync21(manifestPath, "utf-8");
26626
+ text = readFileSync22(manifestPath, "utf-8");
26543
26627
  } catch (e) {
26544
26628
  throw new LocalCliError({
26545
26629
  code: "EXAM_REDACTION_UNSAFE",
@@ -26566,24 +26650,24 @@ function buildPlan(args) {
26566
26650
  }
26567
26651
  var EvalExamCommand = class extends M8tCommand {
26568
26652
  static paths = [["eval", "exam"]];
26569
- static usage = Command47.Usage({
26653
+ static usage = Command48.Usage({
26570
26654
  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."
26571
26655
  });
26572
- worker = Option44.String();
26573
- arms = Option44.String("--arms");
26574
- taskSet = Option44.String("--task-set");
26575
- examType = Option44.String("--exam-type");
26576
- skill = Option44.String("--skill");
26577
- reps = Option44.String("-n,--reps");
26578
- probes = Option44.String("--probes");
26579
- pool = Option44.String("--pool");
26580
- out = Option44.String("--out");
26581
- dryRun = Option44.Boolean("--dry-run", false);
26582
- keepArms = Option44.Boolean("--keep-arms", false);
26583
- allowStub = Option44.Boolean("--allow-stub", false);
26584
- deployment = Option44.String("--deployment");
26585
- output = Option44.String("--output");
26586
- observeWaitS = Option44.String("--observe-wait-s");
26656
+ worker = Option45.String();
26657
+ arms = Option45.String("--arms");
26658
+ taskSet = Option45.String("--task-set");
26659
+ examType = Option45.String("--exam-type");
26660
+ skill = Option45.String("--skill");
26661
+ reps = Option45.String("-n,--reps");
26662
+ probes = Option45.String("--probes");
26663
+ pool = Option45.String("--pool");
26664
+ out = Option45.String("--out");
26665
+ dryRun = Option45.Boolean("--dry-run", false);
26666
+ keepArms = Option45.Boolean("--keep-arms", false);
26667
+ allowStub = Option45.Boolean("--allow-stub", false);
26668
+ deployment = Option45.String("--deployment");
26669
+ output = Option45.String("--output");
26670
+ observeWaitS = Option45.String("--observe-wait-s");
26587
26671
  async executeCommand() {
26588
26672
  await Promise.resolve();
26589
26673
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -26698,10 +26782,10 @@ var EvalExamCommand = class extends M8tCommand {
26698
26782
  };
26699
26783
 
26700
26784
  // src/commands/version.ts
26701
- import { Command as Command48, Option as Option45 } from "clipanion";
26785
+ import { Command as Command49, Option as Option46 } from "clipanion";
26702
26786
  var VersionCommand = class extends M8tCommand {
26703
26787
  static paths = [["version"], ["--version"], ["-v"]];
26704
- static usage = Command48.Usage({
26788
+ static usage = Command49.Usage({
26705
26789
  description: "Print the CLI version.",
26706
26790
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
26707
26791
  examples: [
@@ -26709,8 +26793,8 @@ var VersionCommand = class extends M8tCommand {
26709
26793
  ["Print as JSON", "$0 version --output json"]
26710
26794
  ]
26711
26795
  });
26712
- output = Option45.String("--output", { description: "pretty | json | auto (default)" });
26713
- verbose = Option45.Boolean("--verbose", false);
26796
+ output = Option46.String("--output", { description: "pretty | json | auto (default)" });
26797
+ verbose = Option46.Boolean("--verbose", false);
26714
26798
  executeCommand() {
26715
26799
  const mode = resolveOutputMode(
26716
26800
  this.output ?? "auto",
@@ -26741,18 +26825,18 @@ var VersionCommand = class extends M8tCommand {
26741
26825
  };
26742
26826
 
26743
26827
  // src/commands/whoami.ts
26744
- import { Command as Command49, Option as Option46 } from "clipanion";
26828
+ import { Command as Command50, Option as Option47 } from "clipanion";
26745
26829
  var WhoamiCommand = class extends M8tCommand {
26746
26830
  static paths = [["whoami"]];
26747
- static usage = Command49.Usage({
26831
+ static usage = Command50.Usage({
26748
26832
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
26749
26833
  });
26750
- output = Option46.String("--output");
26751
- verbose = Option46.Boolean("--verbose", false);
26752
- subscription = Option46.String("--subscription", {
26834
+ output = Option47.String("--output");
26835
+ verbose = Option47.Boolean("--verbose", false);
26836
+ subscription = Option47.String("--subscription", {
26753
26837
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
26754
26838
  });
26755
- resourceGroup = Option46.String("--resource-group", {
26839
+ resourceGroup = Option47.String("--resource-group", {
26756
26840
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
26757
26841
  });
26758
26842
  async executeCommand() {
@@ -26817,7 +26901,7 @@ var WhoamiCommand = class extends M8tCommand {
26817
26901
  };
26818
26902
 
26819
26903
  // src/commands/status.ts
26820
- import { Command as Command50, Option as Option47 } from "clipanion";
26904
+ import { Command as Command51, Option as Option48 } from "clipanion";
26821
26905
 
26822
26906
  // src/lib/azd.ts
26823
26907
  init_errors();
@@ -26882,10 +26966,10 @@ async function resolveLocalContext() {
26882
26966
  // src/commands/status.ts
26883
26967
  var StatusCommand = class extends M8tCommand {
26884
26968
  static paths = [["status"]];
26885
- static usage = Command50.Usage({
26969
+ static usage = Command51.Usage({
26886
26970
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
26887
26971
  });
26888
- output = Option47.String("--output");
26972
+ output = Option48.String("--output");
26889
26973
  async executeCommand() {
26890
26974
  const mode = resolveOutputMode(
26891
26975
  this.output,
@@ -26923,7 +27007,7 @@ var StatusCommand = class extends M8tCommand {
26923
27007
  };
26924
27008
 
26925
27009
  // src/commands/doctor.ts
26926
- import { Command as Command51, Option as Option48 } from "clipanion";
27010
+ import { Command as Command52, Option as Option49 } from "clipanion";
26927
27011
  import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
26928
27012
  import * as fs30 from "fs";
26929
27013
  import * as os12 from "os";
@@ -27471,12 +27555,12 @@ function probeLegacyStateDir() {
27471
27555
  }
27472
27556
  var DoctorCommand = class extends M8tCommand {
27473
27557
  static paths = [["doctor"]];
27474
- static usage = Command51.Usage({
27558
+ static usage = Command52.Usage({
27475
27559
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
27476
27560
  });
27477
- output = Option48.String("--output");
27478
- agent = Option48.String("--agent");
27479
- resourceGroup = Option48.String("--resource-group", {
27561
+ output = Option49.String("--output");
27562
+ agent = Option49.String("--agent");
27563
+ resourceGroup = Option49.String("--resource-group", {
27480
27564
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
27481
27565
  });
27482
27566
  async executeCommand() {
@@ -27666,7 +27750,7 @@ var DoctorCommand = class extends M8tCommand {
27666
27750
  };
27667
27751
 
27668
27752
  // src/commands/prereqs.ts
27669
- import { Command as Command52, Option as Option49 } from "clipanion";
27753
+ import { Command as Command53, Option as Option50 } from "clipanion";
27670
27754
  init_errors();
27671
27755
 
27672
27756
  // src/lib/prereq-deps.ts
@@ -28372,7 +28456,7 @@ function renderVerdict(v) {
28372
28456
  }
28373
28457
  var PrereqsCommand = class extends M8tCommand {
28374
28458
  static paths = [["prereqs"]];
28375
- static usage = Command52.Usage({
28459
+ static usage = Command53.Usage({
28376
28460
  description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
28377
28461
  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.",
28378
28462
  examples: [
@@ -28383,15 +28467,15 @@ var PrereqsCommand = class extends M8tCommand {
28383
28467
  ["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
28384
28468
  ]
28385
28469
  });
28386
- fix = Option49.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
28387
- for_ = Option49.String("--for", { description: "UPN or object id of another person. Usage phase only." });
28388
- phase = Option49.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
28389
- 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)." });
28390
- model = Option49.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
28391
- clientId = Option49.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
28392
- subscription = Option49.String("--subscription");
28393
- resourceGroup = Option49.String("--resource-group");
28394
- output = Option49.String("--output");
28470
+ fix = Option50.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
28471
+ for_ = Option50.String("--for", { description: "UPN or object id of another person. Usage phase only." });
28472
+ phase = Option50.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
28473
+ region = Option50.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)." });
28474
+ model = Option50.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
28475
+ clientId = Option50.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
28476
+ subscription = Option50.String("--subscription");
28477
+ resourceGroup = Option50.String("--resource-group");
28478
+ output = Option50.String("--output");
28395
28479
  async executeCommand() {
28396
28480
  const mode = resolveOutputMode(this.output, this.context.stdout);
28397
28481
  const json = mode === "json";
@@ -28447,7 +28531,7 @@ var PrereqsCommand = class extends M8tCommand {
28447
28531
  };
28448
28532
 
28449
28533
  // src/commands/switch.ts
28450
- import { Command as Command53, Option as Option50 } from "clipanion";
28534
+ import { Command as Command54, Option as Option51 } from "clipanion";
28451
28535
 
28452
28536
  // src/lib/profiles.ts
28453
28537
  import * as fs31 from "fs/promises";
@@ -28587,14 +28671,14 @@ async function profileSwitch(name, asName) {
28587
28671
  init_errors();
28588
28672
  var SwitchCommand = class extends M8tCommand {
28589
28673
  static paths = [["switch"]];
28590
- static usage = Command53.Usage({
28674
+ static usage = Command54.Usage({
28591
28675
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
28592
28676
  });
28593
- profile = Option50.String({ required: false });
28594
- subscription = Option50.String("--subscription");
28595
- list = Option50.Boolean("--list", false);
28596
- as = Option50.String("--as");
28597
- output = Option50.String("--output");
28677
+ profile = Option51.String({ required: false });
28678
+ subscription = Option51.String("--subscription");
28679
+ list = Option51.Boolean("--list", false);
28680
+ as = Option51.String("--as");
28681
+ output = Option51.String("--output");
28598
28682
  async executeCommand() {
28599
28683
  const mode = resolveOutputMode(
28600
28684
  this.output,
@@ -28651,7 +28735,7 @@ var SwitchCommand = class extends M8tCommand {
28651
28735
 
28652
28736
  // src/commands/open.ts
28653
28737
  import { spawn as spawn5 } from "child_process";
28654
- import { Command as Command54, Option as Option51 } from "clipanion";
28738
+ import { Command as Command55, Option as Option52 } from "clipanion";
28655
28739
 
28656
28740
  // src/lib/open-targets.ts
28657
28741
  init_errors();
@@ -28697,14 +28781,14 @@ function openUrl(url) {
28697
28781
  }
28698
28782
  var OpenCommand = class extends M8tCommand {
28699
28783
  static paths = [["open"]];
28700
- static usage = Command54.Usage({
28784
+ static usage = Command55.Usage({
28701
28785
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
28702
28786
  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)."
28703
28787
  });
28704
- target = Option51.String({ required: false });
28705
- print = Option51.Boolean("--print", false);
28706
- output = Option51.String("--output");
28707
- resourceGroup = Option51.String("--resource-group", {
28788
+ target = Option52.String({ required: false });
28789
+ print = Option52.Boolean("--print", false);
28790
+ output = Option52.String("--output");
28791
+ resourceGroup = Option52.String("--resource-group", {
28708
28792
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
28709
28793
  });
28710
28794
  async executeCommand() {
@@ -28748,7 +28832,7 @@ var OpenCommand = class extends M8tCommand {
28748
28832
  };
28749
28833
 
28750
28834
  // src/commands/dream/run.ts
28751
- import { Command as Command55, Option as Option52 } from "clipanion";
28835
+ import { Command as Command56, Option as Option53 } from "clipanion";
28752
28836
  import { AzureCliCredential } from "@azure/identity";
28753
28837
  import { TableClient as TableClient8 } from "@azure/data-tables";
28754
28838
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
@@ -31265,28 +31349,28 @@ function redactTranscripts(input) {
31265
31349
  }
31266
31350
  var DreamRunCommand = class extends M8tCommand {
31267
31351
  static paths = [["dream", "run"]];
31268
- static usage = Command55.Usage({
31352
+ static usage = Command56.Usage({
31269
31353
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
31270
31354
  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."
31271
31355
  });
31272
- worker = Option52.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
31356
+ worker = Option53.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
31273
31357
  // Opting IN to the side effects, rather than opting out of them. This command's
31274
31358
  // own help has always described a dry run, but the bare invocation used to take
31275
31359
  // the live branch — a real model call and a commit to the brain repo — so anyone
31276
31360
  // acting on `--help` got the opposite of what they read.
31277
- live = Option52.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
31278
- dryRun = Option52.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
31279
- since = Option52.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
31280
- reset = Option52.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
31281
- showTranscripts = Option52.Boolean("--show-transcripts", false, {
31361
+ live = Option53.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
31362
+ dryRun = Option53.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
31363
+ since = Option53.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
31364
+ reset = Option53.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
31365
+ showTranscripts = Option53.Boolean("--show-transcripts", false, {
31282
31366
  description: "Print transcript bodies (default: metadata only)."
31283
31367
  });
31284
- subscription = Option52.String("--subscription");
31285
- endpoint = Option52.String("--endpoint");
31286
- storageAccount = Option52.String("--storage-account", {
31368
+ subscription = Option53.String("--subscription");
31369
+ endpoint = Option53.String("--endpoint");
31370
+ storageAccount = Option53.String("--storage-account", {
31287
31371
  description: "Ledger storage account name (skips tag-based discovery)"
31288
31372
  });
31289
- output = Option52.String("--output");
31373
+ output = Option53.String("--output");
31290
31374
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
31291
31375
  deps;
31292
31376
  async executeCommand() {
@@ -31661,7 +31745,7 @@ function defaultDeps(overrides) {
31661
31745
 
31662
31746
  // src/commands/conversations/sweep.ts
31663
31747
  import { createHash as createHash5 } from "crypto";
31664
- import { Command as Command56, Option as Option53 } from "clipanion";
31748
+ import { Command as Command57, Option as Option54 } from "clipanion";
31665
31749
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
31666
31750
  import { TableClient as TableClient9 } from "@azure/data-tables";
31667
31751
  import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
@@ -31787,7 +31871,7 @@ function defaultDeps2() {
31787
31871
  }
31788
31872
  var ConversationsSweepCommand = class extends M8tCommand {
31789
31873
  static paths = [["conversations", "sweep"]];
31790
- static usage = Command56.Usage({
31874
+ static usage = Command57.Usage({
31791
31875
  category: "Conversations",
31792
31876
  description: "Delete expired public-visitor conversations (dry run by default)",
31793
31877
  details: `
@@ -31803,22 +31887,22 @@ var ConversationsSweepCommand = class extends M8tCommand {
31803
31887
  ["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
31804
31888
  ]
31805
31889
  });
31806
- doDelete = Option53.Boolean("--delete", false, {
31890
+ doDelete = Option54.Boolean("--delete", false, {
31807
31891
  description: "Perform deletions (without this flag the command only reports)"
31808
31892
  });
31809
- principal = Option53.String("--principal", {
31893
+ principal = Option54.String("--principal", {
31810
31894
  description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
31811
31895
  });
31812
- max = Option53.String("--max", "200", { description: "Maximum deletions per run" });
31813
- graceDays = Option53.String("--grace-days", "14", {
31896
+ max = Option54.String("--max", "200", { description: "Maximum deletions per run" });
31897
+ graceDays = Option54.String("--grace-days", "14", {
31814
31898
  description: "Days past the 30-day key life before a conversation is eligible"
31815
31899
  });
31816
- lookbackDays = Option53.String("--lookback-days", "180", {
31900
+ lookbackDays = Option54.String("--lookback-days", "180", {
31817
31901
  description: "How far back to scan ledger activity for candidates"
31818
31902
  });
31819
- subscription = Option53.String("--subscription", { description: "Azure subscription id override" });
31820
- endpoint = Option53.String("--endpoint", { description: "Foundry project endpoint override" });
31821
- storageAccount = Option53.String("--storage-account", {
31903
+ subscription = Option54.String("--subscription", { description: "Azure subscription id override" });
31904
+ endpoint = Option54.String("--endpoint", { description: "Foundry project endpoint override" });
31905
+ storageAccount = Option54.String("--storage-account", {
31822
31906
  description: "Ledger storage account name (skips tag-based discovery)"
31823
31907
  });
31824
31908
  deps = defaultDeps2();
@@ -31946,7 +32030,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
31946
32030
  };
31947
32031
 
31948
32032
  // src/commands/foundry/create.ts
31949
- import { Command as Command57, Option as Option54 } from "clipanion";
32033
+ import { Command as Command58, Option as Option55 } from "clipanion";
31950
32034
 
31951
32035
  // src/lib/foundry-create.ts
31952
32036
  init_errors();
@@ -32184,7 +32268,7 @@ async function createFoundryProject(args) {
32184
32268
  init_errors();
32185
32269
  var FoundryCreateCommand = class extends M8tCommand {
32186
32270
  static paths = [["foundry", "create"]];
32187
- static usage = Command57.Usage({
32271
+ static usage = Command58.Usage({
32188
32272
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
32189
32273
  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).",
32190
32274
  examples: [
@@ -32193,16 +32277,16 @@ var FoundryCreateCommand = class extends M8tCommand {
32193
32277
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
32194
32278
  ]
32195
32279
  });
32196
- resourceGroup = Option54.String("--resource-group");
32197
- location = Option54.String("--location");
32198
- account = Option54.String("--account");
32199
- project = Option54.String("--project", "m8t");
32200
- model = Option54.String("--model", "gpt-4.1-mini");
32201
- modelVersion = Option54.String("--model-version", "2025-04-14");
32202
- capacity = Option54.String("--capacity", "50");
32203
- subscription = Option54.String("--subscription");
32204
- skipQuotaCheck = Option54.Boolean("--skip-quota-check", false);
32205
- output = Option54.String("--output");
32280
+ resourceGroup = Option55.String("--resource-group");
32281
+ location = Option55.String("--location");
32282
+ account = Option55.String("--account");
32283
+ project = Option55.String("--project", "m8t");
32284
+ model = Option55.String("--model", "gpt-4.1-mini");
32285
+ modelVersion = Option55.String("--model-version", "2025-04-14");
32286
+ capacity = Option55.String("--capacity", "50");
32287
+ subscription = Option55.String("--subscription");
32288
+ skipQuotaCheck = Option55.Boolean("--skip-quota-check", false);
32289
+ output = Option55.String("--output");
32206
32290
  async executeCommand() {
32207
32291
  const mode = resolveOutputMode(
32208
32292
  this.output,
@@ -32274,22 +32358,22 @@ var FoundryCreateCommand = class extends M8tCommand {
32274
32358
  };
32275
32359
 
32276
32360
  // src/commands/foundry/await-ready.ts
32277
- import { Command as Command58, Option as Option55 } from "clipanion";
32361
+ import { Command as Command59, Option as Option56 } from "clipanion";
32278
32362
  import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
32279
32363
  init_errors();
32280
32364
  var FoundryAwaitReadyCommand = class extends M8tCommand {
32281
32365
  static paths = [["foundry", "await-ready"]];
32282
- static usage = Command58.Usage({
32366
+ static usage = Command59.Usage({
32283
32367
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
32284
32368
  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.",
32285
32369
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
32286
32370
  });
32287
- endpoint = Option55.String("--endpoint");
32288
- consecutive = Option55.String("--consecutive", "3");
32289
- attempts = Option55.String("--attempts", "60");
32290
- interval = Option55.String("--interval", "5");
32291
- subscription = Option55.String("--subscription");
32292
- output = Option55.String("--output");
32371
+ endpoint = Option56.String("--endpoint");
32372
+ consecutive = Option56.String("--consecutive", "3");
32373
+ attempts = Option56.String("--attempts", "60");
32374
+ interval = Option56.String("--interval", "5");
32375
+ subscription = Option56.String("--subscription");
32376
+ output = Option56.String("--output");
32293
32377
  async executeCommand() {
32294
32378
  const mode = resolveOutputMode(this.output, this.context.stdout);
32295
32379
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -32323,7 +32407,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
32323
32407
  };
32324
32408
 
32325
32409
  // src/commands/bootstrap/preflight.ts
32326
- import { Command as Command59, Option as Option56 } from "clipanion";
32410
+ import { Command as Command60, Option as Option57 } from "clipanion";
32327
32411
 
32328
32412
  // ../../packages/telemetry-contract/artifact/tier-map.ts
32329
32413
  var EVENT_TIERS = {
@@ -32375,7 +32459,7 @@ function preflightRenderable(results) {
32375
32459
  }
32376
32460
  var BootstrapPreflightCommand = class extends M8tCommand {
32377
32461
  static paths = [["bootstrap", "preflight"]];
32378
- static usage = Command59.Usage({
32462
+ static usage = Command60.Usage({
32379
32463
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
32380
32464
  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.",
32381
32465
  examples: [
@@ -32384,9 +32468,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
32384
32468
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
32385
32469
  ]
32386
32470
  });
32387
- clientId = Option56.String("--client-id");
32388
- subscription = Option56.String("--subscription");
32389
- location = Option56.String("--location", {
32471
+ clientId = Option57.String("--client-id");
32472
+ subscription = Option57.String("--subscription");
32473
+ location = Option57.String("--location", {
32390
32474
  description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
32391
32475
  });
32392
32476
  async executeCommand() {
@@ -32483,10 +32567,10 @@ ${colors.error(" " + why)}
32483
32567
  }
32484
32568
 
32485
32569
  // src/commands/bootstrap/launch.ts
32486
- import * as fs34 from "fs";
32570
+ import * as fs35 from "fs";
32487
32571
  import * as os15 from "os";
32488
32572
  import * as path37 from "path";
32489
- import { Command as Command60, Option as Option57 } from "clipanion";
32573
+ import { Command as Command61, Option as Option58 } from "clipanion";
32490
32574
  init_errors();
32491
32575
 
32492
32576
  // src/lib/bootstrap-mi.ts
@@ -32589,6 +32673,8 @@ function buildAciCreateArgs(s) {
32589
32673
  `INSTALLER_IMAGE_REF=${s.image}`
32590
32674
  ];
32591
32675
  if (s.platformVersion) env.push(`M8T_PLATFORM_VERSION=${s.platformVersion}`);
32676
+ if (s.releaseManifestSha256) env.push(`M8T_RELEASE_MANIFEST_SHA256=${s.releaseManifestSha256}`);
32677
+ if (s.unreleasedDebug) env.push("M8T_UNRELEASED_DEBUG=1");
32592
32678
  if (s.gatewayImageRef) env.push(`GATEWAY_IMAGE_REF=${s.gatewayImageRef}`);
32593
32679
  if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
32594
32680
  if (s.skipBrains) env.push("SKIP_BRAINS=true");
@@ -32849,6 +32935,142 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
32849
32935
  `;
32850
32936
  }
32851
32937
 
32938
+ // src/lib/bootstrap-launch-target.ts
32939
+ init_errors();
32940
+ var BARE_VERSION = /^\d+\.\d+\.\d+(?:-canary\.(?:0|[1-9]\d*))?$/;
32941
+ function bareVersionOf(raw) {
32942
+ const bare = raw.trim().replace(/^platform-v/, "");
32943
+ return BARE_VERSION.test(bare) ? bare : null;
32944
+ }
32945
+ function engineImageOf(m) {
32946
+ const c = m.manifest.components.installer;
32947
+ return `${c.ref}:${c.tag}`;
32948
+ }
32949
+ async function resolveLaunchTarget(args) {
32950
+ const { pinnedInstallerRef, pinnedPlatformVersion, fetchSealed } = args;
32951
+ const release = typeof args.release === "string" && args.release.length > 0 ? args.release : void 0;
32952
+ const tagOverride = typeof args.installerTag === "string" && args.installerTag.length > 0 ? args.installerTag : void 0;
32953
+ const imageOverride = typeof args.installerImage === "string" && args.installerImage.length > 0 ? args.installerImage : void 0;
32954
+ const pinnedRepo = pinnedInstallerRef.slice(0, pinnedInstallerRef.lastIndexOf(":"));
32955
+ const overrideEngine = imageOverride ?? (tagOverride !== void 0 ? `${pinnedRepo}:${tagOverride}` : void 0);
32956
+ const explicitEngine = overrideEngine !== void 0 && overrideEngine !== pinnedInstallerRef;
32957
+ const overrideFlag = imageOverride !== void 0 ? "--installer-image" : "--installer-tag";
32958
+ if (release !== void 0) {
32959
+ const bare = bareVersionOf(release);
32960
+ if (!bare) {
32961
+ throw new LocalCliError({
32962
+ code: "BOOTSTRAP_RELEASE_INVALID",
32963
+ message: `--release must name a platform release, got ${JSON.stringify(release)}.`,
32964
+ hint: "Pass a release version, e.g. --release 0.7.2 (or --release platform-v0.7.2). There is no 'latest' here on purpose: an install pins the release it installs."
32965
+ });
32966
+ }
32967
+ const sealedManifest2 = await fetchSealed(bare);
32968
+ const engineFromManifest2 = engineImageOf(sealedManifest2);
32969
+ if (explicitEngine && overrideEngine !== engineFromManifest2) {
32970
+ throw new LocalCliError({
32971
+ code: "BOOTSTRAP_RELEASE_OVERRIDE_CONFLICT",
32972
+ message: `--release ${bare} names the engine ${engineFromManifest2}, but ${overrideFlag} names ${overrideEngine}.`,
32973
+ hint: `Drop the override to install release ${bare} as published, or drop --release to launch that engine as an unreleased-debug install.`
32974
+ });
32975
+ }
32976
+ assertManifestSelfConsistent(sealedManifest2, bare);
32977
+ return { kind: "released", version: bare, engineImage: engineFromManifest2, manifestSha256: sealedManifest2.digest };
32978
+ }
32979
+ if (explicitEngine) {
32980
+ const engineImage = overrideEngine;
32981
+ return {
32982
+ kind: "unreleased-debug",
32983
+ engineImage,
32984
+ reason: `${overrideFlag} points this install at ${engineImage}, which no release manifest names. The install will deploy what that engine carries and will NOT claim a platform release version.`
32985
+ };
32986
+ }
32987
+ const sealedManifest = await fetchSealed(pinnedPlatformVersion);
32988
+ assertManifestSelfConsistent(sealedManifest, pinnedPlatformVersion);
32989
+ const engineFromManifest = engineImageOf(sealedManifest);
32990
+ if (engineFromManifest !== pinnedInstallerRef) {
32991
+ throw new LocalCliError({
32992
+ code: "BOOTSTRAP_ENGINE_MANIFEST_MISMATCH",
32993
+ message: `This CLI installs platform ${pinnedPlatformVersion} with the engine ${pinnedInstallerRef}, but ${pinnedPlatformVersion}'s manifest names ${engineFromManifest}. The release and this CLI disagree about which engine runs it.`,
32994
+ hint: `Get the newest CLI with \`npm install -g @m8t-stack/cli\` \u2014 the CLI is what pins the engine. If that does not resolve it, install the release explicitly with \`m8t bootstrap launch --release ${pinnedPlatformVersion}\`, which takes the engine from the manifest.`
32995
+ });
32996
+ }
32997
+ return {
32998
+ kind: "released",
32999
+ version: pinnedPlatformVersion,
33000
+ engineImage: pinnedInstallerRef,
33001
+ manifestSha256: sealedManifest.digest
33002
+ };
33003
+ }
33004
+ function assertManifestSelfConsistent(sealedManifest, expected) {
33005
+ const declared = sealedManifest.manifest.platform.version;
33006
+ if (declared !== expected) {
33007
+ throw new LocalCliError({
33008
+ code: "BOOTSTRAP_MANIFEST_VERSION_MISMATCH",
33009
+ message: `Asked the channel for platform ${expected}, but the manifest it returned declares ${declared}.`,
33010
+ hint: "The channel pointer may be misconfigured, or that release's manifest asset is wrong. Nothing was installed."
33011
+ });
33012
+ }
33013
+ }
33014
+
33015
+ // src/lib/manifest-seal.ts
33016
+ import * as fs34 from "fs";
33017
+ import { createHash as createHash8 } from "crypto";
33018
+ init_errors();
33019
+ function manifestDigestOf(raw) {
33020
+ return `sha256:${createHash8("sha256").update(raw, "utf8").digest("hex")}`;
33021
+ }
33022
+ async function fetchSealedManifest(source, deps = {}) {
33023
+ const readFile10 = deps.readFile ?? ((p) => fs34.readFileSync(p, "utf8"));
33024
+ const fetchImpl = deps.fetchImpl ?? fetch;
33025
+ const ghToken = deps.ghToken ?? getGhToken;
33026
+ let raw;
33027
+ if ("file" in source) {
33028
+ try {
33029
+ raw = readFile10(source.file);
33030
+ } catch (e) {
33031
+ throw new LocalCliError({
33032
+ code: "PLATFORM_MANIFEST_READ_FAILED",
33033
+ message: `Could not read manifest file '${source.file}': ${e.message}`
33034
+ });
33035
+ }
33036
+ } else {
33037
+ const url = "url" in source ? source.url : source.version ? channelUrlForVersion(source.version) : CHANNEL_LATEST_URL;
33038
+ let token;
33039
+ try {
33040
+ token = await ghToken();
33041
+ } catch {
33042
+ token = void 0;
33043
+ }
33044
+ const res = await fetchImpl(url, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
33045
+ if (!res.ok) {
33046
+ throw new LocalCliError({
33047
+ code: "PLATFORM_MANIFEST_FETCH_FAILED",
33048
+ message: `GET ${url} returned HTTP ${res.status.toString()}.`,
33049
+ hint: "The engine fetches this same URL and must see the same bytes. Check the release exists and its manifest asset is published."
33050
+ });
33051
+ }
33052
+ raw = await res.text();
33053
+ }
33054
+ let parsed;
33055
+ try {
33056
+ parsed = JSON.parse(raw);
33057
+ } catch (e) {
33058
+ throw new LocalCliError({
33059
+ code: "PLATFORM_MANIFEST_PARSE_FAILED",
33060
+ message: `Manifest is not valid JSON: ${e.message}`
33061
+ });
33062
+ }
33063
+ const errors = validateManifest(parsed);
33064
+ if (errors.length > 0) {
33065
+ throw new LocalCliError({
33066
+ code: "PLATFORM_MANIFEST_INVALID",
33067
+ message: `Fetched manifest failed validation:
33068
+ - ${errors.join("\n - ")}`
33069
+ });
33070
+ }
33071
+ return { raw, digest: manifestDigestOf(raw), manifest: parsed };
33072
+ }
33073
+
32852
33074
  // src/commands/bootstrap/launch.ts
32853
33075
  var DEFAULT_RG = "rg-m8t-stack";
32854
33076
  var DEFAULT_INSTALLER = `${PINNED_INSTALLER.registry}/${PINNED_INSTALLER.image}`;
@@ -32857,7 +33079,7 @@ var ACI_NAME = "m8t-installer";
32857
33079
  var MI_NAME = "m8t-installer-mi";
32858
33080
  var BootstrapLaunchCommand = class extends M8tCommand {
32859
33081
  static paths = [["bootstrap", "launch"]];
32860
- static usage = Command60.Usage({
33082
+ static usage = Command61.Usage({
32861
33083
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
32862
33084
  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.",
32863
33085
  examples: [
@@ -32868,31 +33090,37 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32868
33090
  ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
32869
33091
  ]
32870
33092
  });
32871
- location = Option57.String("--location");
32872
- resourceGroup = Option57.String("--resource-group");
32873
- clientId = Option57.String("--client-id");
32874
- subscription = Option57.String("--subscription");
32875
- installerTag = Option57.String("--installer-tag");
33093
+ location = Option58.String("--location");
33094
+ resourceGroup = Option58.String("--resource-group");
33095
+ clientId = Option58.String("--client-id");
33096
+ subscription = Option58.String("--subscription");
33097
+ // Install a PAST release, properly: its sealed manifest, the engine ITS manifest
33098
+ // names, and a stamp that honestly says which release this is. The replication
33099
+ // case ("they installed 0.7.2 and won't update") used to be approximated with
33100
+ // --installer-tag, which pins the engine and nothing else — right box, wrong
33101
+ // everything else, and silent about it.
33102
+ release = Option58.String("--release", { description: "Install a specific published release (e.g. 0.7.2) instead of the one this CLI pins. Uses that release's own engine and manifest." });
33103
+ installerTag = Option58.String("--installer-tag");
32876
33104
  // Full image ref override (registry + repo + tag) — an escape hatch when the
32877
33105
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
32878
33106
  // Wins over --installer-tag / the pinned default.
32879
- installerImage = Option57.String("--installer-image");
32880
- gatewayImageRef = Option57.String("--gateway-image-ref");
32881
- githubAppCreds = Option57.String("--github-app-creds");
32882
- contactEmail = Option57.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
32883
- company = Option57.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
33107
+ installerImage = Option58.String("--installer-image");
33108
+ gatewayImageRef = Option58.String("--gateway-image-ref");
33109
+ githubAppCreds = Option58.String("--github-app-creds");
33110
+ contactEmail = Option58.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
33111
+ company = Option58.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
32884
33112
  // Value-carrying on purpose: a bare --force would be cargo-culted into
32885
33113
  // runbooks and harness prompts and erode the protection, whereas a faithful
32886
33114
  // paste can never accidentally carry the victim group's name. It AUTHORIZES
32887
33115
  // the target; --resource-group is what CHOOSES it.
32888
- 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." });
32889
- org = Option57.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
32890
- noBrains = Option57.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
33116
+ reinstallInto = Option58.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
33117
+ org = Option58.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
33118
+ noBrains = Option58.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
32891
33119
  // The opt-out TELEMETRY.md names. Without it there is no way to decline at
32892
33120
  // install time — the ACI env is built entirely from these options, so a
32893
33121
  // founder setting FOUNDRY_TRACING in their own shell reaches nothing. A
32894
33122
  // privacy notice that documents a control has to be a control that exists.
32895
- 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)." });
33123
+ foundryTracing = Option58.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)." });
32896
33124
  async executeCommand() {
32897
33125
  const location = typeof this.location === "string" ? this.location : void 0;
32898
33126
  if (!location) {
@@ -32901,15 +33129,38 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32901
33129
  const resourceGroupOpt = typeof this.resourceGroup === "string" ? this.resourceGroup : void 0;
32902
33130
  const resourceGroup = resourceGroupOpt ?? DEFAULT_RG;
32903
33131
  const clientIdOpt = typeof this.clientId === "string" ? this.clientId : void 0;
32904
- const installerTag = (typeof this.installerTag === "string" ? this.installerTag : void 0) ?? DEFAULT_INSTALLER_TAG;
32905
- const installerImageOverride = typeof this.installerImage === "string" ? this.installerImage : void 0;
32906
- const installerImage = installerImageOverride ?? `${DEFAULT_INSTALLER}:${installerTag}`;
32907
33132
  const gatewayImageRef = typeof this.gatewayImageRef === "string" ? this.gatewayImageRef : void 0;
32908
33133
  const foundryTracing = parseFoundryTracingMode(typeof this.foundryTracing === "string" ? this.foundryTracing : void 0) ?? DEFAULT_FOUNDRY_TRACING;
32909
33134
  const account = await getAzAccount();
32910
33135
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
32911
33136
  const out = (m) => this.context.stderr.write(` ${colors.dim(m)}
32912
33137
  `);
33138
+ const target = await resolveLaunchTarget({
33139
+ pinnedInstallerRef: `${DEFAULT_INSTALLER}:${DEFAULT_INSTALLER_TAG}`,
33140
+ pinnedPlatformVersion: PINNED_PLATFORM_VERSION,
33141
+ ...typeof this.release === "string" ? { release: this.release } : {},
33142
+ ...typeof this.installerTag === "string" ? { installerTag: this.installerTag } : {},
33143
+ ...typeof this.installerImage === "string" ? { installerImage: this.installerImage } : {},
33144
+ // THE SAME RULE THE ENGINE APPLIES, from the same helper family. The engine
33145
+ // derives its URL with manifest_url_for (installer/lib.sh), which honours
33146
+ // M8T_UPDATE_CHANNEL_URL — and this launch threads that very variable into
33147
+ // the container below. Sealing against the DEFAULT channel while the engine
33148
+ // fetched a custom one would make the two hash different documents and
33149
+ // refuse every custom-channel install at preflight, blaming the network.
33150
+ // Every shakedown rig uses a custom channel.
33151
+ fetchSealed: async (version) => fetchSealedManifest(manifestSourceForVersionTag(process.env.M8T_UPDATE_CHANNEL_URL ?? "", platformTag(version)))
33152
+ });
33153
+ const installerImage = target.engineImage;
33154
+ const installerTag = installerImage.slice(installerImage.lastIndexOf(":") + 1);
33155
+ if (target.kind === "unreleased-debug") {
33156
+ this.context.stderr.write(
33157
+ `${colors.warn("!")} unreleased-debug install: ${target.reason}
33158
+ ${colors.hint("this install will report no platform version, and the updater will treat it as unrecognised.")}
33159
+ `
33160
+ );
33161
+ } else {
33162
+ out(`installing platform ${target.version} with engine ${installerImage}`);
33163
+ }
32913
33164
  const reinstallInto = typeof this.reinstallInto === "string" ? this.reinstallInto : void 0;
32914
33165
  if (reinstallInto !== void 0 && reinstallInto.toLowerCase() !== resourceGroup.toLowerCase()) {
32915
33166
  const targetExplanation = resourceGroupOpt === void 0 ? `you did not pass --resource-group, so the target is the default '${DEFAULT_RG}'` : `the target resource group is '${resourceGroup}'`;
@@ -32938,7 +33189,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32938
33189
  let githubApp;
32939
33190
  if (skipBrains) {
32940
33191
  out("installing without brain-backed workers; no GitHub App credentials will be used");
32941
- } else if (!fs34.existsSync(credsPath)) {
33192
+ } else if (!fs35.existsSync(credsPath)) {
32942
33193
  this.context.stderr.write(buildMissingCredsRefusal(credsPath));
32943
33194
  throw new LocalCliError({
32944
33195
  code: "GITHUB_APP_CREDS_MISSING",
@@ -32948,7 +33199,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32948
33199
  } else {
32949
33200
  let parsed;
32950
33201
  try {
32951
- parsed = JSON.parse(fs34.readFileSync(credsPath, "utf8"));
33202
+ parsed = JSON.parse(fs35.readFileSync(credsPath, "utf8"));
32952
33203
  } catch {
32953
33204
  throw new LocalCliError({
32954
33205
  code: "GITHUB_APP_CREDS_INVALID",
@@ -32966,7 +33217,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
32966
33217
  }
32967
33218
  let pem;
32968
33219
  try {
32969
- pem = fs34.readFileSync(parsed.pemPath, "utf8");
33220
+ pem = fs35.readFileSync(parsed.pemPath, "utf8");
32970
33221
  } catch {
32971
33222
  throw new LocalCliError({
32972
33223
  code: "GITHUB_APP_PEM_MISSING",
@@ -33022,7 +33273,6 @@ var BootstrapLaunchCommand = class extends M8tCommand {
33022
33273
  } catch (e) {
33023
33274
  out(`warning: could not determine your Azure object id (${e.message}) \u2014 the install will not grant you data-plane access; run 'm8t prereqs --fix' afterwards`);
33024
33275
  }
33025
- const platformVersion = installerImage === PINNED_INSTALLER.ref ? PINNED_PLATFORM_VERSION : "";
33026
33276
  out("launching the cloud installer (ACI)\u2026");
33027
33277
  await kickInstaller({
33028
33278
  aciName: ACI_NAME,
@@ -33036,7 +33286,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
33036
33286
  gatewayImageRef,
33037
33287
  foundryTracing,
33038
33288
  githubApp,
33039
- ...platformVersion ? { platformVersion } : {},
33289
+ ...target.kind === "released" ? { platformVersion: target.version, releaseManifestSha256: target.manifestSha256 } : { unreleasedDebug: true },
33040
33290
  ...founderObjectId ? { founderObjectId } : {},
33041
33291
  ...skipBrains ? { skipBrains: true } : {},
33042
33292
  // Opt-in only. The signed-in UPN was previously seeded here automatically;
@@ -33056,7 +33306,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
33056
33306
  };
33057
33307
 
33058
33308
  // src/commands/bootstrap/status.ts
33059
- import { Command as Command62, Option as Option59 } from "clipanion";
33309
+ import { Command as Command63, Option as Option60 } from "clipanion";
33060
33310
  init_errors();
33061
33311
 
33062
33312
  // src/lib/bootstrap-aci-state.ts
@@ -33083,13 +33333,13 @@ async function getAciState(opts) {
33083
33333
 
33084
33334
  // src/lib/bootstrap-finalize.ts
33085
33335
  init_errors();
33086
- import * as fs39 from "fs/promises";
33336
+ import * as fs40 from "fs/promises";
33087
33337
  import * as os20 from "os";
33088
33338
  import * as path44 from "path";
33089
33339
 
33090
33340
  // src/lib/company-profile-seed.ts
33091
33341
  import { spawn as spawn6 } from "child_process";
33092
- import { closeSync, openSync, readFileSync as readFileSync24 } from "fs";
33342
+ import { closeSync, openSync, readFileSync as readFileSync26 } from "fs";
33093
33343
  import * as os17 from "os";
33094
33344
  import * as path39 from "path";
33095
33345
  init_errors();
@@ -33686,7 +33936,7 @@ async function getSignedInUserIdentity(runAzImpl = runAz) {
33686
33936
  }
33687
33937
 
33688
33938
  // src/lib/onboarding-profile-store.ts
33689
- import * as fs35 from "fs/promises";
33939
+ import * as fs36 from "fs/promises";
33690
33940
  import * as os16 from "os";
33691
33941
  import * as path38 from "path";
33692
33942
  var ONBOARDING_PROFILE_FILE = "onboarding-profile.json";
@@ -33727,7 +33977,7 @@ function canonical(value) {
33727
33977
  async function readOnboardingProfile(home = os16.homedir()) {
33728
33978
  let raw;
33729
33979
  try {
33730
- raw = await fs35.readFile(file(home), "utf8");
33980
+ raw = await fs36.readFile(file(home), "utf8");
33731
33981
  } catch {
33732
33982
  return null;
33733
33983
  }
@@ -33738,11 +33988,11 @@ async function readOnboardingProfile(home = os16.homedir()) {
33738
33988
  }
33739
33989
  }
33740
33990
  async function writeOnboardingProfile(profile, home = os16.homedir()) {
33741
- await fs35.mkdir(dir(home), { recursive: true });
33991
+ await fs36.mkdir(dir(home), { recursive: true });
33742
33992
  const target = file(home);
33743
33993
  const tmp = `${target}.tmp`;
33744
- await fs35.writeFile(tmp, JSON.stringify(profile, null, 2), { encoding: "utf8", mode: 384 });
33745
- await fs35.rename(tmp, target);
33994
+ await fs36.writeFile(tmp, JSON.stringify(profile, null, 2), { encoding: "utf8", mode: 384 });
33995
+ await fs36.rename(tmp, target);
33746
33996
  }
33747
33997
  function toOnboardingBlock(profile) {
33748
33998
  return {
@@ -33759,7 +34009,7 @@ function toOnboardingBlock(profile) {
33759
34009
  function readGithubAppCreds(credsPath) {
33760
34010
  const p = credsPath ?? path39.join(os17.homedir(), ".m8t", "github-app.json");
33761
34011
  try {
33762
- return JSON.parse(readFileSync24(p, "utf8"));
34012
+ return JSON.parse(readFileSync26(p, "utf8"));
33763
34013
  } catch {
33764
34014
  return null;
33765
34015
  }
@@ -33828,7 +34078,7 @@ function upsertMemoryIndexOnce(existing, line2, targetPath2) {
33828
34078
  async function applyProfileToBrain(args) {
33829
34079
  const token = await mintInstallationTokenFromPem({
33830
34080
  appId: args.appCreds.appId,
33831
- privateKeyPem: readFileSync24(args.appCreds.pemPath, "utf8"),
34081
+ privateKeyPem: readFileSync26(args.appCreds.pemPath, "utf8"),
33832
34082
  installationId: args.appCreds.installationId,
33833
34083
  fetchImpl: args.fetchImpl
33834
34084
  });
@@ -34037,15 +34287,15 @@ function renderInstallSummary(args) {
34037
34287
 
34038
34288
  // src/lib/companion-install.ts
34039
34289
  import { constants as constants2 } from "fs";
34040
- import * as fs38 from "fs/promises";
34290
+ import * as fs39 from "fs/promises";
34041
34291
  import * as path42 from "path";
34042
34292
  import { randomUUID as randomUUID4 } from "crypto";
34043
34293
  import { execFile as execFile2, spawn as spawn7 } from "child_process";
34044
34294
 
34045
34295
  // src/lib/companion-artifact.ts
34046
- import { createHash as createHash8 } from "crypto";
34296
+ import { createHash as createHash9 } from "crypto";
34047
34297
  import { constants } from "fs";
34048
- import * as fs36 from "fs/promises";
34298
+ import * as fs37 from "fs/promises";
34049
34299
  import * as path40 from "path";
34050
34300
  var SHA256 = /^[a-f0-9]{64}$/u;
34051
34301
  var VERSION2 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u;
@@ -34081,7 +34331,7 @@ function canonicalEntry(entry) {
34081
34331
  `;
34082
34332
  }
34083
34333
  function artifactTreeSha256(entries) {
34084
- const hash = createHash8("sha256");
34334
+ const hash = createHash9("sha256");
34085
34335
  const ordered = [...entries].sort(
34086
34336
  (left, right) => left.path.localeCompare(right.path)
34087
34337
  );
@@ -34151,17 +34401,17 @@ function parseArtifactManifest(value) {
34151
34401
  };
34152
34402
  }
34153
34403
  async function sha256File2(filePath) {
34154
- return createHash8("sha256").update(await fs36.readFile(filePath)).digest("hex");
34404
+ return createHash9("sha256").update(await fs37.readFile(filePath)).digest("hex");
34155
34405
  }
34156
34406
  async function walk2(root, relative4 = "") {
34157
34407
  const directory = path40.join(root, ...relative4.split("/").filter(Boolean));
34158
- const children = await fs36.readdir(directory, { withFileTypes: true });
34408
+ const children = await fs37.readdir(directory, { withFileTypes: true });
34159
34409
  const entries = [];
34160
34410
  for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) {
34161
34411
  const childRelative = relative4 ? `${relative4}/${child.name}` : child.name;
34162
34412
  normalizedRelative(childRelative);
34163
34413
  const childPath = path40.join(directory, child.name);
34164
- const stat5 = await fs36.lstat(childPath);
34414
+ const stat5 = await fs37.lstat(childPath);
34165
34415
  if (stat5.isDirectory()) {
34166
34416
  entries.push(...await walk2(root, childRelative));
34167
34417
  } else if (stat5.isFile()) {
@@ -34173,7 +34423,7 @@ async function walk2(root, relative4 = "") {
34173
34423
  sha256: await sha256File2(childPath)
34174
34424
  });
34175
34425
  } else if (stat5.isSymbolicLink()) {
34176
- const target = await fs36.readlink(childPath);
34426
+ const target = await fs37.readlink(childPath);
34177
34427
  entries.push({
34178
34428
  type: "symlink",
34179
34429
  path: childRelative,
@@ -34186,17 +34436,17 @@ async function walk2(root, relative4 = "") {
34186
34436
  return entries;
34187
34437
  }
34188
34438
  async function ensureRealDirectory(root) {
34189
- const stat5 = await fs36.lstat(root);
34439
+ const stat5 = await fs37.lstat(root);
34190
34440
  if (stat5.isSymbolicLink()) throw new Error("Artifact root is a symbolic link");
34191
34441
  if (!stat5.isDirectory()) throw new Error("Artifact root is not a directory");
34192
34442
  }
34193
34443
  async function validateResolvedLinks(root, entries) {
34194
- const realRoot = await fs36.realpath(root);
34444
+ const realRoot = await fs37.realpath(root);
34195
34445
  for (const entry of entries) {
34196
34446
  if (entry.type !== "symlink") continue;
34197
34447
  try {
34198
34448
  const linkPath = path40.join(root, ...entry.path.split("/"));
34199
- const resolved = await fs36.realpath(linkPath);
34449
+ const resolved = await fs37.realpath(linkPath);
34200
34450
  const relative4 = path40.relative(realRoot, resolved);
34201
34451
  if (relative4 === ".." || relative4.startsWith(`..${path40.sep}`) || path40.isAbsolute(relative4)) {
34202
34452
  throw new Error("Artifact symlink chain escapes the payload root");
@@ -34228,13 +34478,13 @@ async function buildArtifactManifest(payloadRoot, input) {
34228
34478
  return manifest;
34229
34479
  }
34230
34480
  async function readArtifactManifest(manifestPath) {
34231
- const before = await fs36.lstat(manifestPath);
34481
+ const before = await fs37.lstat(manifestPath);
34232
34482
  if (before.isSymbolicLink() || !before.isFile()) {
34233
34483
  throw new Error("Artifact manifest is a symbolic link or non-file");
34234
34484
  }
34235
34485
  let handle;
34236
34486
  try {
34237
- handle = await fs36.open(
34487
+ handle = await fs37.open(
34238
34488
  manifestPath,
34239
34489
  constants.O_RDONLY | constants.O_NOFOLLOW
34240
34490
  );
@@ -34292,37 +34542,37 @@ async function verifyArtifactPayload(payloadRoot, manifest) {
34292
34542
  async function copyArtifactPayload(sourceRoot, targetRoot, manifest) {
34293
34543
  await verifyArtifactSource(sourceRoot, manifest);
34294
34544
  try {
34295
- const targetStat = await fs36.lstat(targetRoot);
34545
+ const targetStat = await fs37.lstat(targetRoot);
34296
34546
  if (targetStat.isSymbolicLink()) {
34297
34547
  throw new Error("Install target is a symbolic link");
34298
34548
  }
34299
34549
  if (!targetStat.isDirectory()) throw new Error("Install target is not a directory");
34300
- if ((await fs36.readdir(targetRoot)).length > 0) {
34550
+ if ((await fs37.readdir(targetRoot)).length > 0) {
34301
34551
  throw new Error("Install staging target is not empty");
34302
34552
  }
34303
34553
  } catch (error) {
34304
34554
  if (error.code !== "ENOENT") throw error;
34305
- await fs36.mkdir(targetRoot, { recursive: false, mode: 448 });
34555
+ await fs37.mkdir(targetRoot, { recursive: false, mode: 448 });
34306
34556
  }
34307
34557
  for (const entry of manifest.entries) {
34308
34558
  const source = ensureContainedEntry(sourceRoot, entry.path);
34309
34559
  const target = ensureContainedEntry(targetRoot, entry.path);
34310
- await fs36.mkdir(path40.dirname(target), { recursive: true, mode: 448 });
34560
+ await fs37.mkdir(path40.dirname(target), { recursive: true, mode: 448 });
34311
34561
  if (entry.type === "file") {
34312
- await fs36.copyFile(source, target);
34313
- await fs36.chmod(target, entry.mode);
34562
+ await fs37.copyFile(source, target);
34563
+ await fs37.chmod(target, entry.mode);
34314
34564
  } else {
34315
- await fs36.symlink(entry.target, target);
34565
+ await fs37.symlink(entry.target, target);
34316
34566
  }
34317
34567
  }
34318
34568
  await verifyArtifactPayload(targetRoot, manifest);
34319
34569
  }
34320
34570
 
34321
34571
  // src/lib/companion-download.ts
34322
- import { createHash as createHash9 } from "crypto";
34572
+ import { createHash as createHash10 } from "crypto";
34323
34573
  import { execFile } from "child_process";
34324
34574
  import { createReadStream } from "fs";
34325
- import * as fs37 from "fs/promises";
34575
+ import * as fs38 from "fs/promises";
34326
34576
  import * as os18 from "os";
34327
34577
  import * as path41 from "path";
34328
34578
  init_errors();
@@ -34370,11 +34620,11 @@ function declaredLength(response) {
34370
34620
  return Number.isSafeInteger(value) && value >= 0 ? value : null;
34371
34621
  }
34372
34622
  async function usableBytes(partial) {
34373
- const status = await fs37.lstat(partial).catch(() => null);
34623
+ const status = await fs38.lstat(partial).catch(() => null);
34374
34624
  if (status === null) return 0;
34375
34625
  if (!status.isFile()) {
34376
- await fs37.rm(partial, { recursive: true, force: true }).catch(() => void 0);
34377
- if (await fs37.lstat(partial).then(() => true, () => false)) {
34626
+ await fs38.rm(partial, { recursive: true, force: true }).catch(() => void 0);
34627
+ if (await fs38.lstat(partial).then(() => true, () => false)) {
34378
34628
  throw new LocalCliError({
34379
34629
  code: "COMPANION_DOWNLOAD_PATH_OCCUPIED",
34380
34630
  message: `${partial} is not a file and could not be cleared.`,
@@ -34386,14 +34636,14 @@ async function usableBytes(partial) {
34386
34636
  return status.size;
34387
34637
  }
34388
34638
  async function sweepForeignPartials(directory, keep) {
34389
- const entries = await fs37.readdir(directory).catch(() => []);
34639
+ const entries = await fs38.readdir(directory).catch(() => []);
34390
34640
  await Promise.all(
34391
- entries.filter((name) => name.endsWith(".part") && name !== keep).map((name) => fs37.rm(path41.join(directory, name), { recursive: true, force: true }))
34641
+ entries.filter((name) => name.endsWith(".part") && name !== keep).map((name) => fs38.rm(path41.join(directory, name), { recursive: true, force: true }))
34392
34642
  );
34393
34643
  }
34394
34644
  function sha256File3(filePath) {
34395
34645
  return new Promise((resolve6, reject) => {
34396
- const hash = createHash9("sha256");
34646
+ const hash = createHash10("sha256");
34397
34647
  const stream = createReadStream(filePath);
34398
34648
  stream.on("error", reject);
34399
34649
  stream.on("data", (chunk) => hash.update(chunk));
@@ -34407,7 +34657,7 @@ async function transferToDisk(input) {
34407
34657
  for (let attempt = 0; attempt < MAX_ATTEMPTS2; attempt++) {
34408
34658
  let existing = await usableBytes(input.partial);
34409
34659
  if (existing > input.maxAssetBytes) {
34410
- await fs37.rm(input.partial, { force: true }).catch(() => void 0);
34660
+ await fs38.rm(input.partial, { force: true }).catch(() => void 0);
34411
34661
  existing = 0;
34412
34662
  }
34413
34663
  let received = existing;
@@ -34424,7 +34674,7 @@ async function transferToDisk(input) {
34424
34674
  if (existing > 0 && await sha256File3(input.partial).catch(() => null) === input.expectedDigest) {
34425
34675
  return;
34426
34676
  }
34427
- await fs37.rm(input.partial, { force: true }).catch(() => void 0);
34677
+ await fs38.rm(input.partial, { force: true }).catch(() => void 0);
34428
34678
  lastError = new Error("the interrupted download no longer fits the published asset");
34429
34679
  continue;
34430
34680
  }
@@ -34449,7 +34699,7 @@ async function transferToDisk(input) {
34449
34699
  const range = parseContentRange(response.headers.get("content-range"));
34450
34700
  if (range?.start !== existing) {
34451
34701
  await response.body?.cancel().catch(() => void 0);
34452
- await fs37.rm(input.partial, { force: true }).catch(() => void 0);
34702
+ await fs38.rm(input.partial, { force: true }).catch(() => void 0);
34453
34703
  lastError = new Error("the server answered a different range than the one requested");
34454
34704
  continue;
34455
34705
  }
@@ -34462,14 +34712,14 @@ async function transferToDisk(input) {
34462
34712
  const declaredBody = declaredLength(response);
34463
34713
  if (total !== null && total > input.maxAssetBytes || declaredBody !== null && declaredBody > input.maxAssetBytes) {
34464
34714
  await response.body?.cancel().catch(() => void 0);
34465
- await fs37.rm(input.partial, { force: true }).catch(() => void 0);
34715
+ await fs38.rm(input.partial, { force: true }).catch(() => void 0);
34466
34716
  throw tooLarge(input.asset);
34467
34717
  }
34468
34718
  if (offset > 0) input.onEvent({ kind: "resumed", received: offset, total });
34469
34719
  received = offset;
34470
34720
  const body = response.body;
34471
34721
  if (body === null) throw new Error("the response carried no body");
34472
- const handle = await fs37.open(input.partial, offset > 0 ? "a" : "w", 384);
34722
+ const handle = await fs38.open(input.partial, offset > 0 ? "a" : "w", 384);
34473
34723
  try {
34474
34724
  for await (const chunk of body) {
34475
34725
  received += chunk.byteLength;
@@ -34492,7 +34742,7 @@ async function transferToDisk(input) {
34492
34742
  } catch (error) {
34493
34743
  if (error instanceof LocalCliError) {
34494
34744
  if (error.code === "COMPANION_ASSET_TOO_LARGE") {
34495
- await fs37.rm(input.partial, { force: true }).catch(() => void 0);
34745
+ await fs38.rm(input.partial, { force: true }).catch(() => void 0);
34496
34746
  }
34497
34747
  throw error;
34498
34748
  }
@@ -34508,7 +34758,7 @@ async function transferToDisk(input) {
34508
34758
  }
34509
34759
  }
34510
34760
  }
34511
- const kept = await fs37.lstat(input.partial).then(
34761
+ const kept = await fs38.lstat(input.partial).then(
34512
34762
  (status) => status.isFile() ? status.size : 0,
34513
34763
  () => 0
34514
34764
  );
@@ -34535,7 +34785,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
34535
34785
  const directory = deps.downloadDirectory ?? companionDownloadDirectory(os18.homedir());
34536
34786
  const partialName = `${pinned.sha256}.part`;
34537
34787
  const partial = path41.join(directory, partialName);
34538
- await fs37.mkdir(directory, { recursive: true, mode: 448 });
34788
+ await fs38.mkdir(directory, { recursive: true, mode: 448 });
34539
34789
  await sweepForeignPartials(directory, partialName);
34540
34790
  await transferToDisk({
34541
34791
  url,
@@ -34549,20 +34799,20 @@ async function downloadCompanionArtifact(component, deps = {}) {
34549
34799
  });
34550
34800
  const digest = await sha256File3(partial);
34551
34801
  if (digest !== pinned.sha256) {
34552
- await fs37.rm(partial, { force: true }).catch(() => void 0);
34802
+ await fs38.rm(partial, { force: true }).catch(() => void 0);
34553
34803
  throw new LocalCliError({
34554
34804
  code: "COMPANION_ASSET_DIGEST_MISMATCH",
34555
34805
  message: `${pinned.asset} does not match the digest the release pins for it.`,
34556
34806
  hint: "Nothing was unpacked. This is what a corrupted download or a substituted file looks like \u2014 retry, and report it if it persists."
34557
34807
  });
34558
34808
  }
34559
- const root = await (deps.makeTemporaryDirectory ?? (() => fs37.mkdtemp(path41.join(os18.tmpdir(), "m8t-companion-"))))();
34809
+ const root = await (deps.makeTemporaryDirectory ?? (() => fs38.mkdtemp(path41.join(os18.tmpdir(), "m8t-companion-"))))();
34560
34810
  const dispose = async () => {
34561
- await fs37.rm(root, { recursive: true, force: true }).catch(() => void 0);
34811
+ await fs38.rm(root, { recursive: true, force: true }).catch(() => void 0);
34562
34812
  };
34563
34813
  try {
34564
34814
  const unpacked = path41.join(root, "unpacked");
34565
- await fs37.mkdir(unpacked, { recursive: false, mode: 448 });
34815
+ await fs38.mkdir(unpacked, { recursive: false, mode: 448 });
34566
34816
  await (deps.extract ?? extractArchive)(partial, unpacked);
34567
34817
  return {
34568
34818
  version: component.version,
@@ -34573,7 +34823,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
34573
34823
  await dispose();
34574
34824
  throw error;
34575
34825
  } finally {
34576
- await fs37.rm(partial, { force: true }).catch(() => void 0);
34826
+ await fs38.rm(partial, { force: true }).catch(() => void 0);
34577
34827
  }
34578
34828
  }
34579
34829
 
@@ -34617,10 +34867,10 @@ async function setCompanionStartAtLogin(input) {
34617
34867
  );
34618
34868
  await assertNotSymlink(registration, "Start-at-login registration");
34619
34869
  if (!input.enabled) {
34620
- await fs38.rm(registration, { force: true });
34870
+ await fs39.rm(registration, { force: true });
34621
34871
  return;
34622
34872
  }
34623
- await fs38.mkdir(launchAgents, { recursive: true, mode: 448 });
34873
+ await fs39.mkdir(launchAgents, { recursive: true, mode: 448 });
34624
34874
  const plist = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict><key>Label</key><string>com.m8t.companion</string><key>ProgramArguments</key><array><string>' + xmlEscape(input.executable) + "</string></array><key>RunAtLoad</key><true/></dict></plist>\n";
34625
34875
  await atomicWriteText(registration, plist, 384);
34626
34876
  return;
@@ -34802,7 +35052,7 @@ function validateGatewayOrigin(value) {
34802
35052
  }
34803
35053
  async function assertNotSymlink(filePath, kind) {
34804
35054
  try {
34805
- if ((await fs38.lstat(filePath)).isSymbolicLink()) {
35055
+ if ((await fs39.lstat(filePath)).isSymbolicLink()) {
34806
35056
  throw new Error(`${kind} is a symbolic link`);
34807
35057
  }
34808
35058
  } catch (error) {
@@ -34817,14 +35067,14 @@ async function assertOwnedDirectoryChain(anchor, targetDirectory) {
34817
35067
  }
34818
35068
  const segments = relative4.split(path42.sep).filter(Boolean);
34819
35069
  let current = anchor;
34820
- const anchorStatus = await fs38.lstat(anchor);
35070
+ const anchorStatus = await fs39.lstat(anchor);
34821
35071
  if (anchorStatus.isSymbolicLink() || !anchorStatus.isDirectory()) {
34822
35072
  throw new Error("Companion owned directory anchor is unsafe");
34823
35073
  }
34824
35074
  for (const segment of segments) {
34825
35075
  current = path42.join(current, segment);
34826
35076
  try {
34827
- const status = await fs38.lstat(current);
35077
+ const status = await fs39.lstat(current);
34828
35078
  if (status.isSymbolicLink() || !status.isDirectory()) {
34829
35079
  throw new Error("Companion owned directory contains a symbolic link");
34830
35080
  }
@@ -34843,13 +35093,13 @@ async function assertOwnedParents(options, paths) {
34843
35093
  await assertOwnedDirectoryChain(targetAnchor, path42.dirname(paths.targetRoot));
34844
35094
  }
34845
35095
  async function readRegularText(filePath, maxBytes) {
34846
- const before = await fs38.lstat(filePath);
35096
+ const before = await fs39.lstat(filePath);
34847
35097
  if (before.isSymbolicLink() || !before.isFile()) {
34848
35098
  throw new Error("Companion state file is not a regular file");
34849
35099
  }
34850
35100
  let handle;
34851
35101
  try {
34852
- handle = await fs38.open(
35102
+ handle = await fs39.open(
34853
35103
  filePath,
34854
35104
  constants2.O_RDONLY | constants2.O_NOFOLLOW
34855
35105
  );
@@ -34872,27 +35122,27 @@ async function atomicWriteJson(filePath, value) {
34872
35122
  }
34873
35123
  async function atomicWriteText(filePath, contents, mode) {
34874
35124
  await assertNotSymlink(filePath, "Companion state file");
34875
- await fs38.mkdir(path42.dirname(filePath), {
35125
+ await fs39.mkdir(path42.dirname(filePath), {
34876
35126
  recursive: true,
34877
35127
  mode: 448
34878
35128
  });
34879
35129
  const temporary = `${filePath}.${randomUUID4()}.tmp`;
34880
35130
  try {
34881
- await fs38.writeFile(temporary, contents, {
35131
+ await fs39.writeFile(temporary, contents, {
34882
35132
  mode,
34883
35133
  flag: "wx"
34884
35134
  });
34885
- await fs38.rename(temporary, filePath);
34886
- await fs38.chmod(filePath, mode).catch(() => void 0);
35135
+ await fs39.rename(temporary, filePath);
35136
+ await fs39.chmod(filePath, mode).catch(() => void 0);
34887
35137
  } finally {
34888
- await fs38.rm(temporary, { force: true }).catch(() => void 0);
35138
+ await fs39.rm(temporary, { force: true }).catch(() => void 0);
34889
35139
  }
34890
35140
  }
34891
35141
  async function realRegularFile(filePath, executable) {
34892
- const real = await fs38.realpath(filePath);
34893
- const stat5 = await fs38.stat(real);
35142
+ const real = await fs39.realpath(filePath);
35143
+ const stat5 = await fs39.stat(real);
34894
35144
  if (!stat5.isFile()) throw new Error("Companion launch target is not a file");
34895
- await fs38.access(real, executable ? constants2.X_OK : constants2.R_OK);
35145
+ await fs39.access(real, executable ? constants2.X_OK : constants2.R_OK);
34896
35146
  return real;
34897
35147
  }
34898
35148
  function defaultLaunch(executable) {
@@ -34908,7 +35158,7 @@ async function companionIsRunning(platform, executable) {
34908
35158
  if (platform !== "win32") return false;
34909
35159
  let handle;
34910
35160
  try {
34911
- handle = await fs38.open(executable, "r+");
35161
+ handle = await fs39.open(executable, "r+");
34912
35162
  } catch (error) {
34913
35163
  const code = error.code;
34914
35164
  if (code === "ENOENT") return false;
@@ -35022,7 +35272,7 @@ async function snapshotFile(filePath) {
35022
35272
  }
35023
35273
  async function restoreFile(filePath, bytes) {
35024
35274
  if (bytes === null) {
35025
- await fs38.rm(filePath, { force: true });
35275
+ await fs39.rm(filePath, { force: true });
35026
35276
  } else {
35027
35277
  await atomicWriteJson(filePath, JSON.parse(bytes.toString("utf8")));
35028
35278
  }
@@ -35066,7 +35316,7 @@ async function converge(options, force) {
35066
35316
  }
35067
35317
  if (current.state === "not-installed") {
35068
35318
  try {
35069
- await fs38.lstat(paths.targetRoot);
35319
+ await fs39.lstat(paths.targetRoot);
35070
35320
  throw new Error(
35071
35321
  "The fixed companion target exists without an owned install manifest"
35072
35322
  );
@@ -35077,7 +35327,7 @@ async function converge(options, force) {
35077
35327
  const stage = `${paths.targetRoot}.m8t-stage-${randomUUID4()}`;
35078
35328
  const backup = `${paths.targetRoot}.m8t-backup-${randomUUID4()}`;
35079
35329
  const copy = options.copyPayload ?? copyArtifactPayload;
35080
- await fs38.mkdir(path42.dirname(paths.targetRoot), {
35330
+ await fs39.mkdir(path42.dirname(paths.targetRoot), {
35081
35331
  recursive: true,
35082
35332
  mode: 448
35083
35333
  });
@@ -35094,12 +35344,12 @@ async function converge(options, force) {
35094
35344
  try {
35095
35345
  await copy(payloadRoot, stage, artifactManifest);
35096
35346
  try {
35097
- await fs38.rename(paths.targetRoot, backup);
35347
+ await fs39.rename(paths.targetRoot, backup);
35098
35348
  movedPrior = true;
35099
35349
  } catch (error) {
35100
35350
  if (error.code !== "ENOENT") throw error;
35101
35351
  }
35102
- await fs38.rename(stage, paths.targetRoot);
35352
+ await fs39.rename(stage, paths.targetRoot);
35103
35353
  installedStage = true;
35104
35354
  const nodeExecutable = await realRegularFile(
35105
35355
  options.nodeExecutable,
@@ -35162,7 +35412,7 @@ async function converge(options, force) {
35162
35412
  );
35163
35413
  loginChanged = true;
35164
35414
  await (options.launch ?? defaultLaunch)(executable);
35165
- await fs38.rm(backup, { recursive: true, force: true });
35415
+ await fs39.rm(backup, { recursive: true, force: true });
35166
35416
  return {
35167
35417
  state: "installed",
35168
35418
  version: artifactManifest.version,
@@ -35176,15 +35426,15 @@ async function converge(options, force) {
35176
35426
  () => void 0
35177
35427
  );
35178
35428
  }
35179
- await fs38.rm(stage, { recursive: true, force: true }).catch(() => void 0);
35429
+ await fs39.rm(stage, { recursive: true, force: true }).catch(() => void 0);
35180
35430
  if (installedStage) {
35181
- await fs38.rm(paths.targetRoot, {
35431
+ await fs39.rm(paths.targetRoot, {
35182
35432
  recursive: true,
35183
35433
  force: true
35184
35434
  }).catch(() => void 0);
35185
35435
  }
35186
35436
  if (movedPrior) {
35187
- await fs38.rename(backup, paths.targetRoot).catch(() => void 0);
35437
+ await fs39.rename(backup, paths.targetRoot).catch(() => void 0);
35188
35438
  }
35189
35439
  await Promise.all([
35190
35440
  restoreFile(paths.installManifest, snapshots[0]),
@@ -35205,7 +35455,7 @@ async function uninstallCompanion(options) {
35205
35455
  const expectedPaths = companionInstallPaths(options);
35206
35456
  await assertOwnedParents(options, expectedPaths);
35207
35457
  const remove = options.removeOwnedPath ?? (async (ownedPath, recursive) => {
35208
- await fs38.rm(ownedPath, { recursive, force: true });
35458
+ await fs39.rm(ownedPath, { recursive, force: true });
35209
35459
  });
35210
35460
  const installed2 = await readInstalled(options);
35211
35461
  if (!installed2) {
@@ -35233,7 +35483,7 @@ async function uninstallCompanion(options) {
35233
35483
  // src/commands/companion/install.ts
35234
35484
  import * as os19 from "os";
35235
35485
  import * as path43 from "path";
35236
- import { Command as Command61, Option as Option58 } from "clipanion";
35486
+ import { Command as Command62, Option as Option59 } from "clipanion";
35237
35487
 
35238
35488
  // src/lib/companion-channel.ts
35239
35489
  async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
@@ -35364,7 +35614,7 @@ Version: ${state.version}
35364
35614
  }
35365
35615
  var CompanionInstallCommand = class extends M8tCommand {
35366
35616
  static paths = [["companion", "install"]];
35367
- static usage = Command61.Usage({
35617
+ static usage = Command62.Usage({
35368
35618
  description: "Install the desktop companions for this user from the release channel.",
35369
35619
  examples: [
35370
35620
  ["Install the released build", "$0 companion install"],
@@ -35374,10 +35624,10 @@ var CompanionInstallCommand = class extends M8tCommand {
35374
35624
  ]
35375
35625
  ]
35376
35626
  });
35377
- from = Option58.String("--from", {
35627
+ from = Option59.String("--from", {
35378
35628
  description: "A locally staged build directory instead of the released one."
35379
35629
  });
35380
- resourceGroup = Option58.String("--resource-group", {
35630
+ resourceGroup = Option59.String("--resource-group", {
35381
35631
  description: "Which deployment to bind to, when the subscription holds more than one."
35382
35632
  });
35383
35633
  async executeCommand() {
@@ -35402,7 +35652,7 @@ function resolveRepoRootMarker(args) {
35402
35652
  }
35403
35653
  async function looksLikeCheckout(dir2) {
35404
35654
  try {
35405
- return (await fs39.stat(path44.join(dir2, "brain-template"))).isDirectory();
35655
+ return (await fs40.stat(path44.join(dir2, "brain-template"))).isDirectory();
35406
35656
  } catch {
35407
35657
  return false;
35408
35658
  }
@@ -35425,15 +35675,15 @@ async function finalizeInstall(args, deps = defaultFinalizeDeps) {
35425
35675
  const markerDir = path44.join(deps.homedir(), ".m8t");
35426
35676
  const markerPath = path44.join(markerDir, "repo-root");
35427
35677
  const cwd = process.cwd();
35428
- const existing = await fs39.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
35678
+ const existing = await fs40.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
35429
35679
  const repoRoot = resolveRepoRootMarker({
35430
35680
  ...args.repoRoot !== void 0 ? { explicit: args.repoRoot } : {},
35431
35681
  cwd,
35432
35682
  cwdIsCheckout: await looksLikeCheckout(cwd),
35433
35683
  existing
35434
35684
  });
35435
- await fs39.mkdir(markerDir, { recursive: true });
35436
- await fs39.writeFile(markerPath, `${repoRoot}
35685
+ await fs40.mkdir(markerDir, { recursive: true });
35686
+ await fs40.writeFile(markerPath, `${repoRoot}
35437
35687
  `, "utf8");
35438
35688
  let webappUrl;
35439
35689
  try {
@@ -35592,7 +35842,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
35592
35842
  // runbooks and shakedown recipes that a founder may already be part-way
35593
35843
  // through — it prints a deprecation notice and does the right thing.
35594
35844
  static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
35595
- static usage = Command62.Usage({
35845
+ static usage = Command63.Usage({
35596
35846
  description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
35597
35847
  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.",
35598
35848
  examples: [
@@ -35601,12 +35851,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
35601
35851
  ["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
35602
35852
  ]
35603
35853
  });
35604
- watch = Option59.Boolean("--watch", false);
35605
- output = Option59.String("--output");
35606
- repoRoot = Option59.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
35607
- finalize = Option59.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
35608
- subscription = Option59.String("--subscription");
35609
- resourceGroup = Option59.String("--resource-group");
35854
+ watch = Option60.Boolean("--watch", false);
35855
+ output = Option60.String("--output");
35856
+ repoRoot = Option60.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
35857
+ finalize = Option60.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
35858
+ subscription = Option60.String("--subscription");
35859
+ resourceGroup = Option60.String("--resource-group");
35610
35860
  async executeCommand() {
35611
35861
  const state = await readBootstrapState();
35612
35862
  if (!state) {
@@ -35739,7 +35989,7 @@ function formatStatus(d) {
35739
35989
  }
35740
35990
 
35741
35991
  // src/commands/bootstrap/reap.ts
35742
- import { Command as Command63, Option as Option60 } from "clipanion";
35992
+ import { Command as Command64, Option as Option61 } from "clipanion";
35743
35993
  init_errors();
35744
35994
 
35745
35995
  // src/lib/bootstrap-reap.ts
@@ -35833,7 +36083,7 @@ async function reapInstaller(opts) {
35833
36083
  // src/commands/bootstrap/reap.ts
35834
36084
  var BootstrapReapCommand = class extends M8tCommand {
35835
36085
  static paths = [["bootstrap", "reap"]];
35836
- static usage = Command63.Usage({
36086
+ static usage = Command64.Usage({
35837
36087
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
35838
36088
  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.",
35839
36089
  examples: [
@@ -35843,9 +36093,9 @@ var BootstrapReapCommand = class extends M8tCommand {
35843
36093
  ["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
35844
36094
  ]
35845
36095
  });
35846
- force = Option60.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
35847
- sweepOrphans = Option60.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
35848
- yes = Option60.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
36096
+ force = Option61.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
36097
+ sweepOrphans = Option61.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
36098
+ yes = Option61.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
35849
36099
  async executeCommand() {
35850
36100
  if (this.sweepOrphans === true) {
35851
36101
  const { subscriptionId: sub } = await getAzAccount();
@@ -35939,10 +36189,10 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
35939
36189
  };
35940
36190
 
35941
36191
  // src/commands/bootstrap/ui.ts
35942
- import { Command as Command64, Option as Option61 } from "clipanion";
36192
+ import { Command as Command65, Option as Option62 } from "clipanion";
35943
36193
 
35944
36194
  // src/lib/bootstrap-ui.ts
35945
- import * as fs40 from "fs";
36195
+ import * as fs41 from "fs";
35946
36196
  import * as net from "net";
35947
36197
  import * as os21 from "os";
35948
36198
  import * as path45 from "path";
@@ -35978,7 +36228,7 @@ function parseManagedPid(text) {
35978
36228
  function stopPidFile(pidPath) {
35979
36229
  let pidStr;
35980
36230
  try {
35981
- pidStr = fs40.readFileSync(pidPath, "utf8").trim();
36231
+ pidStr = fs41.readFileSync(pidPath, "utf8").trim();
35982
36232
  } catch {
35983
36233
  return false;
35984
36234
  }
@@ -35995,7 +36245,7 @@ function stopPidFile(pidPath) {
35995
36245
  }
35996
36246
  }
35997
36247
  try {
35998
- fs40.unlinkSync(pidPath);
36248
+ fs41.unlinkSync(pidPath);
35999
36249
  } catch {
36000
36250
  }
36001
36251
  return true;
@@ -36014,7 +36264,7 @@ function renderDeprecationNotice() {
36014
36264
  }
36015
36265
  var BootstrapUiCommand = class extends M8tCommand {
36016
36266
  static paths = [["bootstrap", "ui"]];
36017
- static usage = Command64.Usage({
36267
+ static usage = Command65.Usage({
36018
36268
  description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
36019
36269
  details: [
36020
36270
  "The local onboarding chat has been retired. Your details are collected by",
@@ -36034,14 +36284,14 @@ var BootstrapUiCommand = class extends M8tCommand {
36034
36284
  // Accepted and ignored, deliberately: removing them would turn an old script's
36035
36285
  // harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
36036
36286
  // the whole surface when the rewrite lands.
36037
- repoRoot = Option61.String("--repo-root", { description: "Ignored (deprecated)." });
36038
- port = Option61.String("--port", "3000", { description: "Ignored (deprecated)." });
36039
- endpoint = Option61.String("--endpoint", { description: "Ignored (deprecated)." });
36040
- prepOnly = Option61.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
36041
- skipInstall = Option61.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
36042
- foreground = Option61.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
36043
- voice = Option61.Boolean("--voice", false, { description: "Ignored (deprecated)." });
36044
- stop = Option61.Boolean("--stop", false, {
36287
+ repoRoot = Option62.String("--repo-root", { description: "Ignored (deprecated)." });
36288
+ port = Option62.String("--port", "3000", { description: "Ignored (deprecated)." });
36289
+ endpoint = Option62.String("--endpoint", { description: "Ignored (deprecated)." });
36290
+ prepOnly = Option62.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
36291
+ skipInstall = Option62.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
36292
+ foreground = Option62.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
36293
+ voice = Option62.Boolean("--voice", false, { description: "Ignored (deprecated)." });
36294
+ stop = Option62.Boolean("--stop", false, {
36045
36295
  description: "Shut down a local chat UI left running by an earlier version of this command."
36046
36296
  });
36047
36297
  // Not `async`: there is nothing left to await. Everything this command used to
@@ -36063,7 +36313,7 @@ var BootstrapUiCommand = class extends M8tCommand {
36063
36313
 
36064
36314
  // src/commands/bootstrap/profile.ts
36065
36315
  import * as readline3 from "readline/promises";
36066
- import { Command as Command65, Option as Option62 } from "clipanion";
36316
+ import { Command as Command66, Option as Option63 } from "clipanion";
36067
36317
 
36068
36318
  // src/lib/profile-collect.ts
36069
36319
  init_errors();
@@ -36232,7 +36482,7 @@ async function openChatInvite(deps = {}) {
36232
36482
  // src/commands/bootstrap/profile.ts
36233
36483
  var BootstrapProfileCommand = class extends M8tCommand {
36234
36484
  static paths = [["bootstrap", "profile"]];
36235
- static usage = Command65.Usage({
36485
+ static usage = Command66.Usage({
36236
36486
  description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
36237
36487
  details: [
36238
36488
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
@@ -36252,12 +36502,12 @@ var BootstrapProfileCommand = class extends M8tCommand {
36252
36502
  ["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
36253
36503
  ]
36254
36504
  });
36255
- founderEmail = Option62.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
36256
- advisorName = Option62.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
36257
- advisorEmail = Option62.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
36258
- noAdvisor = Option62.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
36259
- noChat = Option62.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
36260
- print = Option62.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
36505
+ founderEmail = Option63.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
36506
+ advisorName = Option63.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
36507
+ advisorEmail = Option63.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
36508
+ noAdvisor = Option63.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
36509
+ noChat = Option63.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
36510
+ print = Option63.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
36261
36511
  async executeCommand() {
36262
36512
  const stdin = this.context.stdin;
36263
36513
  const stdout = this.context.stdout;
@@ -36350,10 +36600,10 @@ var BootstrapProfileCommand = class extends M8tCommand {
36350
36600
  };
36351
36601
 
36352
36602
  // src/commands/bootstrap/seed-profile.ts
36353
- import { Command as Command66, Option as Option63 } from "clipanion";
36603
+ import { Command as Command67, Option as Option64 } from "clipanion";
36354
36604
  var BootstrapSeedProfileCommand = class extends M8tCommand {
36355
36605
  static paths = [["bootstrap", "seed-profile"]];
36356
- static usage = Command66.Usage({
36606
+ static usage = Command67.Usage({
36357
36607
  description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
36358
36608
  details: [
36359
36609
  "Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
@@ -36370,11 +36620,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
36370
36620
  ["Seed now (idempotent)", "$0 bootstrap seed-profile"]
36371
36621
  ]
36372
36622
  });
36373
- endpoint = Option63.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
36374
- brain = Option63.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
36375
- watch = Option63.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
36376
- timeout = Option63.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
36377
- githubAppCreds = Option63.String("--github-app-creds");
36623
+ endpoint = Option64.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
36624
+ brain = Option64.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
36625
+ watch = Option64.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
36626
+ timeout = Option64.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
36627
+ githubAppCreds = Option64.String("--github-app-creds");
36378
36628
  async executeCommand() {
36379
36629
  const ctx = await resolveSeedContext({
36380
36630
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -36459,10 +36709,10 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
36459
36709
  };
36460
36710
 
36461
36711
  // src/commands/telemetry/enroll.ts
36462
- import * as fs41 from "fs";
36712
+ import * as fs42 from "fs";
36463
36713
  import * as os22 from "os";
36464
36714
  import * as path46 from "path";
36465
- import { Command as Command67, Option as Option64 } from "clipanion";
36715
+ import { Command as Command68, Option as Option65 } from "clipanion";
36466
36716
  init_errors();
36467
36717
 
36468
36718
  // src/lib/telemetry-enroll.ts
@@ -36544,7 +36794,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
36544
36794
  }
36545
36795
  var TelemetryEnrollCommand = class extends M8tCommand {
36546
36796
  static paths = [["telemetry", "enroll"]];
36547
- static usage = Command67.Usage({
36797
+ static usage = Command68.Usage({
36548
36798
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
36549
36799
  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.",
36550
36800
  examples: [
@@ -36552,11 +36802,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
36552
36802
  ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
36553
36803
  ]
36554
36804
  });
36555
- company = Option64.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
36556
- contactEmail = Option64.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
36557
- subscription = Option64.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
36558
- resourceGroup = Option64.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
36559
- force = Option64.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
36805
+ company = Option65.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
36806
+ contactEmail = Option65.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
36807
+ subscription = Option65.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
36808
+ resourceGroup = Option65.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
36809
+ force = Option65.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
36560
36810
  async executeCommand() {
36561
36811
  const account = await getAzAccount();
36562
36812
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
@@ -36589,13 +36839,13 @@ var TelemetryEnrollCommand = class extends M8tCommand {
36589
36839
  ...company ? { company } : {},
36590
36840
  ...contactEmail ? { contactEmail } : {}
36591
36841
  });
36592
- const keyDir = fs41.mkdtempSync(path46.join(os22.tmpdir(), "m8t-ingest-key-"));
36842
+ const keyDir = fs42.mkdtempSync(path46.join(os22.tmpdir(), "m8t-ingest-key-"));
36593
36843
  const keyFile = path46.join(keyDir, "key");
36594
36844
  try {
36595
- fs41.writeFileSync(keyFile, ingestKey, { mode: 384 });
36845
+ fs42.writeFileSync(keyFile, ingestKey, { mode: 384 });
36596
36846
  await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--file", keyFile, "--only-show-errors"]);
36597
36847
  } finally {
36598
- fs41.rmSync(keyDir, { recursive: true, force: true });
36848
+ fs42.rmSync(keyDir, { recursive: true, force: true });
36599
36849
  }
36600
36850
  this.context.stdout.write(
36601
36851
  `${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
@@ -36606,7 +36856,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
36606
36856
  };
36607
36857
 
36608
36858
  // src/commands/companion/bridge.ts
36609
- import { Command as Command68, Option as Option65 } from "clipanion";
36859
+ import { Command as Command69, Option as Option66 } from "clipanion";
36610
36860
 
36611
36861
  // ../../packages/companion-bridge-contract/src/index.ts
36612
36862
  var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
@@ -38073,14 +38323,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
38073
38323
  return 3;
38074
38324
  }
38075
38325
  }
38076
- var CompanionBridgeCommand = class extends Command68 {
38326
+ var CompanionBridgeCommand = class extends Command69 {
38077
38327
  static paths = [["companion", "_bridge"]];
38078
38328
  /**
38079
38329
  * One process serving many requests instead of one per request, so the
38080
38330
  * session keeps its authenticated context between them. A CLI predating the
38081
38331
  * flag rejects it outright, which is how the app knows to fall back.
38082
38332
  */
38083
- serve = Option65.Boolean("--serve", false);
38333
+ serve = Option66.Boolean("--serve", false);
38084
38334
  async execute() {
38085
38335
  if (this.serve) {
38086
38336
  return runCompanionBridgeServe(
@@ -38098,7 +38348,7 @@ var CompanionBridgeCommand = class extends Command68 {
38098
38348
  };
38099
38349
 
38100
38350
  // src/commands/companion/status.ts
38101
- import { Command as Command69 } from "clipanion";
38351
+ import { Command as Command70 } from "clipanion";
38102
38352
  async function withTimeout(work, ms) {
38103
38353
  let timer;
38104
38354
  try {
@@ -38170,7 +38420,7 @@ Run: m8t companion install
38170
38420
  }
38171
38421
  var CompanionStatusCommand = class extends M8tCommand {
38172
38422
  static paths = [["companion", "status"]];
38173
- static usage = Command69.Usage({
38423
+ static usage = Command70.Usage({
38174
38424
  description: "Verify the installed desktop companion without launching it."
38175
38425
  });
38176
38426
  async executeCommand() {
@@ -38184,7 +38434,7 @@ var CompanionStatusCommand = class extends M8tCommand {
38184
38434
  };
38185
38435
 
38186
38436
  // src/commands/companion/repair.ts
38187
- import { Command as Command70, Option as Option66 } from "clipanion";
38437
+ import { Command as Command71, Option as Option67 } from "clipanion";
38188
38438
  async function runCompanionRepairCommand(stdout, repair) {
38189
38439
  const state = await repair();
38190
38440
  if (state.state === "not-released") {
@@ -38203,10 +38453,10 @@ async function runCompanionRepairCommand(stdout, repair) {
38203
38453
  }
38204
38454
  var CompanionRepairCommand = class extends M8tCommand {
38205
38455
  static paths = [["companion", "repair"]];
38206
- static usage = Command70.Usage({
38456
+ static usage = Command71.Usage({
38207
38457
  description: "Restore the desktop companions and start-at-login state."
38208
38458
  });
38209
- resourceGroup = Option66.String("--resource-group", {
38459
+ resourceGroup = Option67.String("--resource-group", {
38210
38460
  description: "Which deployment to bind to, when the subscription holds more than one."
38211
38461
  });
38212
38462
  async executeCommand() {
@@ -38224,7 +38474,7 @@ var CompanionRepairCommand = class extends M8tCommand {
38224
38474
  };
38225
38475
 
38226
38476
  // src/commands/companion/uninstall.ts
38227
- import { Command as Command71 } from "clipanion";
38477
+ import { Command as Command72 } from "clipanion";
38228
38478
  async function runCompanionUninstallCommand(stdout, uninstall) {
38229
38479
  const state = await uninstall();
38230
38480
  if (state.state !== "not-installed") {
@@ -38236,7 +38486,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
38236
38486
  }
38237
38487
  var CompanionUninstallCommand = class extends M8tCommand {
38238
38488
  static paths = [["companion", "uninstall"]];
38239
- static usage = Command71.Usage({
38489
+ static usage = Command72.Usage({
38240
38490
  description: "Remove only this user's desktop companion installation."
38241
38491
  });
38242
38492
  async executeCommand() {
@@ -38297,6 +38547,7 @@ cli.register(PlatformConvergeCommand);
38297
38547
  cli.register(PlatformClearIntentCommand);
38298
38548
  cli.register(PlatformRequestUpdateCommand);
38299
38549
  cli.register(PlatformSeedStampCommand);
38550
+ cli.register(PlatformVerifyContentCommand);
38300
38551
  cli.register(PlatformGatewayAdoptCommand);
38301
38552
  cli.register(PlatformPolicyCommand);
38302
38553
  cli.register(PlatformEnableCostReportCommand);