@m8t-stack/cli 0.2.38 → 0.2.42

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
@@ -1298,7 +1298,7 @@ var init_enable_hosted_brain = __esm({
1298
1298
  import { Builtins, Cli } from "clipanion";
1299
1299
 
1300
1300
  // src/lib/package-version.ts
1301
- var CLI_VERSION = "0.2.38";
1301
+ var CLI_VERSION = "0.2.42";
1302
1302
 
1303
1303
  // src/lib/render-error.ts
1304
1304
  init_errors();
@@ -17955,6 +17955,21 @@ function entityToApplyRequest(e) {
17955
17955
  return JSON.parse(e.value);
17956
17956
  }
17957
17957
 
17958
+ // ../../packages/platform-release/dist/esm/update-policy.js
17959
+ var DEFAULT_POLICY_MODE = "auto-critical";
17960
+ var UPDATE_POLICY_PK = "system";
17961
+ var UPDATE_POLICY_RK = "update-policy";
17962
+ var MODES = ["notify-only", "auto-critical", "auto-all"];
17963
+ function isPolicyMode(x) {
17964
+ return typeof x === "string" && MODES.includes(x);
17965
+ }
17966
+ function updatePolicyToEntity(p) {
17967
+ return { partitionKey: UPDATE_POLICY_PK, rowKey: UPDATE_POLICY_RK, value: JSON.stringify(p) };
17968
+ }
17969
+ function entityToUpdatePolicy(e) {
17970
+ return JSON.parse(e.value);
17971
+ }
17972
+
17958
17973
  // ../../packages/platform-release/dist/esm/infra-params.js
17959
17974
  var INFRA_PARAMS_PK = "system";
17960
17975
  var INFRA_PARAMS_RK = "infra-params";
@@ -18236,25 +18251,31 @@ async function writeStamp(opts) {
18236
18251
  ...opts.onProgress ? { onProgress: opts.onProgress } : {}
18237
18252
  });
18238
18253
  }
18239
- async function readStamp(opts) {
18254
+ async function readStampOutcome(opts) {
18240
18255
  const { tableEndpoint, accountResourceId, accountName } = await discoverStampStorage(opts);
18241
18256
  const aad = new TableClient2(tableEndpoint, TABLE, opts.credential);
18242
18257
  try {
18243
18258
  const row = await aad.getEntity(PK, RK);
18244
- return entityToStamp(row);
18259
+ const stamp = entityToStamp(row);
18260
+ return stamp ? { source: "explicit", stamp } : { source: "unreadable" };
18245
18261
  } catch (e) {
18246
- if (is404(e)) return null;
18247
- if (!is403(e)) return null;
18262
+ if (is404(e)) return { source: "absent" };
18263
+ if (!is403(e)) return { source: "unreadable" };
18248
18264
  }
18249
18265
  try {
18250
18266
  const shared = await makeSharedKeyClient({ tableEndpoint, accountResourceId, accountName });
18251
- if (!shared) return null;
18267
+ if (!shared) return { source: "unreadable" };
18252
18268
  const row = await shared.getEntity(PK, RK);
18253
- return entityToStamp(row);
18269
+ const stamp = entityToStamp(row);
18270
+ return stamp ? { source: "explicit", stamp } : { source: "unreadable" };
18254
18271
  } catch {
18255
- return null;
18272
+ return { source: "unreadable" };
18256
18273
  }
18257
18274
  }
18275
+ async function readStamp(opts) {
18276
+ const result = await readStampOutcome(opts);
18277
+ return result.source === "explicit" ? result.stamp : null;
18278
+ }
18258
18279
 
18259
18280
  // src/lib/platform-converge.ts
18260
18281
  import * as fs22 from "fs";
@@ -18278,6 +18299,16 @@ function parseFoundryTracingMode(v) {
18278
18299
  }
18279
18300
  return v;
18280
18301
  }
18302
+ function parseAssignSubscriptionRoles(v) {
18303
+ if (v === void 0) return void 0;
18304
+ if (v === "true") return true;
18305
+ if (v === "false") return false;
18306
+ throw new LocalCliError({
18307
+ code: "DEPLOY_INVALID_ASSIGN_SUBSCRIPTION_ROLES",
18308
+ message: `Invalid --assign-subscription-roles value: '${v}'.`,
18309
+ hint: "Use one of: true, false."
18310
+ });
18311
+ }
18281
18312
  function buildBicepParams(p) {
18282
18313
  const params = [
18283
18314
  `location=${p.location}`,
@@ -20462,8 +20493,215 @@ var PlatformSeedStampCommand = class extends M8tCommand {
20462
20493
  }
20463
20494
  };
20464
20495
 
20465
- // src/commands/platform/enable-cost-report.ts
20496
+ // src/commands/platform/stamp-build.ts
20466
20497
  import { Command as Command35, Option as Option32 } from "clipanion";
20498
+ import { DefaultAzureCredential as DefaultAzureCredential19, ManagedIdentityCredential as ManagedIdentityCredential3 } from "@azure/identity";
20499
+ init_errors();
20500
+
20501
+ // src/lib/platform-stamp-build.ts
20502
+ var BARE_SEMVER2 = /^\d+\.\d+\.\d+$/;
20503
+ function buildRunningBuildStamp(input) {
20504
+ if (!BARE_SEMVER2.test(input.version)) {
20505
+ throw new Error(
20506
+ `platform version must be bare X.Y.Z, got "${input.version}" \u2014 a build tag cannot be represented as a version.`
20507
+ );
20508
+ }
20509
+ return {
20510
+ schemaVersion: 1,
20511
+ platformVersion: platformTag(input.version),
20512
+ previousPlatformVersion: input.previousPlatformVersion,
20513
+ updatedAt: input.nowIso,
20514
+ lastResult: "success",
20515
+ cli: input.cliVersion,
20516
+ origin: "cd",
20517
+ components: {
20518
+ gateway: { tag: input.gatewayTag, digest: input.gatewayDigest, state: "managed" },
20519
+ codingAgent: { tag: "", digest: "", state: "external" },
20520
+ azureExecutor: { tag: "", digest: "", state: "external" },
20521
+ personas: input.personas,
20522
+ infra: { treeSha: input.infraTreeSha },
20523
+ ...input.brainSeeds ? { brainSeeds: input.brainSeeds } : {}
20524
+ }
20525
+ };
20526
+ }
20527
+
20528
+ // src/commands/platform/stamp-build.ts
20529
+ var PlatformStampBuildCommand = class extends M8tCommand {
20530
+ static paths = [["platform", "stamp-build"]];
20531
+ static usage = Command35.Usage({
20532
+ description: "Record the currently running build as this install's platform version.",
20533
+ details: "For installs that are deployed continuously rather than from a published release. Unlike the install-time record, this overwrites on every run \u2014 the running build is the truth. Only components this writer deploys are reported; the rest are marked as managed elsewhere."
20534
+ });
20535
+ version = Option32.String("--version", { description: "The running build's own version, bare X.Y.Z." });
20536
+ gatewayTag = Option32.String("--gateway-tag", { description: "The tag of the gateway image this build deployed." });
20537
+ gatewayDigest = Option32.String("--gateway-digest", { description: "The digest of the gateway image this build deployed." });
20538
+ subscription = Option32.String("--subscription");
20539
+ resourceGroup = Option32.String("--resource-group");
20540
+ miClientId = Option32.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
20541
+ output = Option32.String("--output");
20542
+ async executeCommand() {
20543
+ const mode = resolveOutputMode(this.output, this.context.stdout);
20544
+ const need = (v, flag) => {
20545
+ if (typeof v !== "string" || v.trim().length === 0) {
20546
+ throw new LocalCliError({ code: "PLATFORM_STAMP_BUILD_ARG_MISSING", message: `${flag} is required.` });
20547
+ }
20548
+ return v.trim();
20549
+ };
20550
+ const onProgress = (m) => {
20551
+ this.context.stderr.write(`${m}
20552
+ `);
20553
+ };
20554
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential3({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential19();
20555
+ const ctx = {
20556
+ credential: credential2,
20557
+ subscriptionId: need(this.subscription, "--subscription"),
20558
+ resourceGroup: need(this.resourceGroup, "--resource-group")
20559
+ };
20560
+ const { accountName } = await discoverStampStorage(ctx);
20561
+ const existing = await readStampOutcome(ctx);
20562
+ if (existing.source === "unreadable") {
20563
+ throw new LocalCliError({
20564
+ code: "PLATFORM_STAMP_BUILD_PRIOR_UNREADABLE",
20565
+ message: "Could not read the existing platform stamp; refusing to overwrite it blind.",
20566
+ hint: "The running build is the truth about the CURRENT version, not about whether a previous one existed \u2014 retry once the prior record can be read."
20567
+ });
20568
+ }
20569
+ const previousPlatformVersion = existing.source === "explicit" ? existing.stamp.platformVersion : null;
20570
+ const priorComponents = existing.source === "explicit" ? existing.stamp.components : void 0;
20571
+ const stamp = buildRunningBuildStamp({
20572
+ version: need(this.version, "--version"),
20573
+ gatewayTag: need(this.gatewayTag, "--gateway-tag"),
20574
+ gatewayDigest: this.gatewayDigest ?? "",
20575
+ previousPlatformVersion,
20576
+ cliVersion: CLI_VERSION,
20577
+ nowIso: (/* @__PURE__ */ new Date()).toISOString(),
20578
+ personas: priorComponents?.personas ?? {},
20579
+ infraTreeSha: priorComponents?.infra.treeSha ?? "",
20580
+ ...priorComponents?.brainSeeds ? { brainSeeds: priorComponents.brainSeeds } : {}
20581
+ });
20582
+ await writeStamp({ ...ctx, stamp, onProgress });
20583
+ const result = { platformVersion: stamp.platformVersion, previous: previousPlatformVersion, storageAccount: accountName };
20584
+ this.context.stdout.write(
20585
+ mode === "json" ? renderJson(result) + "\n" : `platform stamp: ${result.platformVersion} (previous ${result.previous ?? "none"}, storage ${result.storageAccount})
20586
+ `
20587
+ );
20588
+ return 0;
20589
+ }
20590
+ };
20591
+
20592
+ // src/commands/platform/policy.ts
20593
+ import { Command as Command36, Option as Option33 } from "clipanion";
20594
+ import { DefaultAzureCredential as DefaultAzureCredential20, ManagedIdentityCredential as ManagedIdentityCredential4 } from "@azure/identity";
20595
+ init_errors();
20596
+
20597
+ // src/lib/platform-policy.ts
20598
+ import { TableClient as TableClient5 } from "@azure/data-tables";
20599
+ init_errors();
20600
+ function buildPolicy(mode, nowIso, updatedBy) {
20601
+ if (!isPolicyMode(mode)) {
20602
+ throw new LocalCliError({
20603
+ code: "PLATFORM_POLICY_MODE_INVALID",
20604
+ message: `Unrecognized update mode "${String(mode)}".`,
20605
+ hint: "Use one of: notify-only, auto-critical, auto-all."
20606
+ });
20607
+ }
20608
+ return { schemaVersion: 1, mode, updatedAt: nowIso, updatedBy };
20609
+ }
20610
+ async function writePolicy(opts) {
20611
+ const entity = updatePolicyToEntity(opts.policy);
20612
+ await upsertMetadataEntity({
20613
+ credential: opts.credential,
20614
+ subscriptionId: opts.subscriptionId,
20615
+ resourceGroup: opts.resourceGroup,
20616
+ entity,
20617
+ label: "update policy",
20618
+ errorCode: "PLATFORM_POLICY_NO_ACCESS",
20619
+ retryHint: "The role assignment persists \u2014 retry in a few minutes.",
20620
+ ...opts.onProgress ? { onProgress: opts.onProgress } : {}
20621
+ });
20622
+ }
20623
+ async function readPolicy(opts) {
20624
+ const discover = opts.discoverImpl ?? discoverStampStorage;
20625
+ const makeAad = opts.aadClientImpl ?? ((endpoint, credential2) => new TableClient5(endpoint, TABLE, credential2));
20626
+ const makeShared = opts.sharedClientImpl ?? makeSharedKeyClient;
20627
+ const { tableEndpoint, accountResourceId, accountName } = await discover(opts);
20628
+ const aad = makeAad(tableEndpoint, opts.credential);
20629
+ try {
20630
+ const row = await aad.getEntity(UPDATE_POLICY_PK, UPDATE_POLICY_RK);
20631
+ return { source: "explicit", policy: entityToUpdatePolicy(row) };
20632
+ } catch (e) {
20633
+ if (is404(e)) return { source: "absent" };
20634
+ if (!is403(e)) return { source: "unreadable" };
20635
+ }
20636
+ try {
20637
+ const shared = await makeShared({ tableEndpoint, accountResourceId, accountName });
20638
+ if (!shared) return { source: "unreadable" };
20639
+ const row = await shared.getEntity(UPDATE_POLICY_PK, UPDATE_POLICY_RK);
20640
+ return { source: "explicit", policy: entityToUpdatePolicy(row) };
20641
+ } catch {
20642
+ return { source: "unreadable" };
20643
+ }
20644
+ }
20645
+
20646
+ // src/commands/platform/policy.ts
20647
+ var PlatformPolicyCommand = class extends M8tCommand {
20648
+ static paths = [["platform", "policy"]];
20649
+ static usage = Command36.Usage({
20650
+ description: "Show or set how this install handles available platform updates.",
20651
+ 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.",
20652
+ examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
20653
+ });
20654
+ set = Option33.String("--set", { description: "notify-only | auto-critical | auto-all" });
20655
+ subscription = Option33.String("--subscription");
20656
+ resourceGroup = Option33.String("--resource-group");
20657
+ miClientId = Option33.String("--mi-client-id");
20658
+ output = Option33.String("--output");
20659
+ async executeCommand() {
20660
+ const mode = resolveOutputMode(this.output, this.context.stdout);
20661
+ const need = (v, flag) => {
20662
+ if (typeof v !== "string" || v.trim().length === 0) {
20663
+ throw new LocalCliError({ code: "PLATFORM_POLICY_ARG_MISSING", message: `${flag} is required.` });
20664
+ }
20665
+ return v.trim();
20666
+ };
20667
+ const onProgress = (m) => {
20668
+ this.context.stderr.write(`${m}
20669
+ `);
20670
+ };
20671
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential4({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential20();
20672
+ const ctx = {
20673
+ credential: credential2,
20674
+ subscriptionId: need(this.subscription, "--subscription"),
20675
+ resourceGroup: need(this.resourceGroup, "--resource-group")
20676
+ };
20677
+ if (typeof this.set === "string" && this.set.length > 0) {
20678
+ const policy = buildPolicy(this.set, (/* @__PURE__ */ new Date()).toISOString(), "cli");
20679
+ await writePolicy({ ...ctx, policy, onProgress });
20680
+ const result2 = { mode: policy.mode, updatedAt: policy.updatedAt };
20681
+ this.context.stdout.write(mode === "json" ? renderJson(result2) + "\n" : `update policy: ${policy.mode}
20682
+ `);
20683
+ return 0;
20684
+ }
20685
+ const current = await readPolicy(ctx);
20686
+ if (current.source === "unreadable") {
20687
+ const result2 = { outcome: "unreadable" };
20688
+ this.context.stdout.write(
20689
+ mode === "json" ? renderJson(result2) + "\n" : `update policy: could not be read \u2014 the current setting is unknown; do not assume it is on the default (${DEFAULT_POLICY_MODE}).
20690
+ `
20691
+ );
20692
+ return 0;
20693
+ }
20694
+ const result = current.source === "explicit" ? { mode: current.policy.mode, outcome: "explicit" } : { mode: DEFAULT_POLICY_MODE, outcome: "absent" };
20695
+ this.context.stdout.write(
20696
+ mode === "json" ? renderJson(result) + "\n" : `update policy: ${result.mode}${result.outcome === "absent" ? " (default \u2014 never set)" : ""}
20697
+ `
20698
+ );
20699
+ return 0;
20700
+ }
20701
+ };
20702
+
20703
+ // src/commands/platform/enable-cost-report.ts
20704
+ import { Command as Command37, Option as Option34 } from "clipanion";
20467
20705
 
20468
20706
  // src/lib/wire-gateway-acs.ts
20469
20707
  init_rbac();
@@ -20503,18 +20741,18 @@ async function wireGatewayForAcs(args) {
20503
20741
  init_errors();
20504
20742
  var PlatformEnableCostReportCommand = class extends M8tCommand {
20505
20743
  static paths = [["platform", "enable-cost-report"]];
20506
- static usage = Command35.Usage({
20744
+ static usage = Command37.Usage({
20507
20745
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
20508
20746
  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')."
20509
20747
  });
20510
- subscription = Option32.String("--subscription");
20511
- resourceGroup = Option32.String("--resource-group", {
20748
+ subscription = Option34.String("--subscription");
20749
+ resourceGroup = Option34.String("--resource-group", {
20512
20750
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
20513
20751
  });
20514
- acsEndpoint = Option32.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
20515
- acsSender = Option32.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
20516
- acsResourceId = Option32.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
20517
- output = Option32.String("--output");
20752
+ acsEndpoint = Option34.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
20753
+ acsSender = Option34.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
20754
+ acsResourceId = Option34.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
20755
+ output = Option34.String("--output");
20518
20756
  async executeCommand() {
20519
20757
  const mode = resolveOutputMode(
20520
20758
  this.output,
@@ -20588,45 +20826,45 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
20588
20826
  };
20589
20827
 
20590
20828
  // src/commands/platform/enable-auto-update.ts
20591
- import { Command as Command36, Option as Option33 } from "clipanion";
20829
+ import { Command as Command38, Option as Option35 } from "clipanion";
20592
20830
  import { confirm as confirm6 } from "@inquirer/prompts";
20593
- import { DefaultAzureCredential as DefaultAzureCredential19 } from "@azure/identity";
20831
+ import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
20594
20832
  init_errors();
20595
20833
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
20596
20834
  static paths = [["platform", "enable-auto-update"]];
20597
- static usage = Command36.Usage({
20835
+ static usage = Command38.Usage({
20598
20836
  category: "Platform",
20599
20837
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
20600
20838
  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."
20601
20839
  });
20602
- subscription = Option33.String("--subscription");
20603
- resourceGroup = Option33.String("--resource-group", {
20840
+ subscription = Option35.String("--subscription");
20841
+ resourceGroup = Option35.String("--resource-group", {
20604
20842
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
20605
20843
  });
20606
- suffix = Option33.String("--suffix", {
20844
+ suffix = Option35.String("--suffix", {
20607
20845
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
20608
20846
  });
20609
- installerImage = Option33.String("--installer-image", {
20847
+ installerImage = Option35.String("--installer-image", {
20610
20848
  required: true,
20611
20849
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
20612
20850
  });
20613
- updateCron = Option33.String("--update-cron", {
20851
+ updateCron = Option35.String("--update-cron", {
20614
20852
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
20615
20853
  });
20616
- channelUrl = Option33.String("--channel-url", {
20854
+ channelUrl = Option35.String("--channel-url", {
20617
20855
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
20618
20856
  });
20619
- location = Option33.String("--location", {
20857
+ location = Option35.String("--location", {
20620
20858
  description: "Region for the updater identity + job. Defaults to the resource group's existing resources."
20621
20859
  });
20622
- foundryTracing = Option33.String("--foundry-tracing", {
20860
+ foundryTracing = Option35.String("--foundry-tracing", {
20623
20861
  description: "project | account | skip. Pass the value the install was deployed with \u2014 omitting it lets the bicep default (project) switch tracing on."
20624
20862
  });
20625
- endpoint = Option33.String("--endpoint", {
20863
+ endpoint = Option35.String("--endpoint", {
20626
20864
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
20627
20865
  });
20628
- yes = Option33.Boolean("--yes", false);
20629
- output = Option33.String("--output");
20866
+ yes = Option35.Boolean("--yes", false);
20867
+ output = Option35.String("--output");
20630
20868
  async executeCommand() {
20631
20869
  const mode = resolveOutputMode(
20632
20870
  this.output,
@@ -20645,7 +20883,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
20645
20883
  resourceGroup: this.resourceGroup
20646
20884
  });
20647
20885
  const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
20648
- const credential2 = new DefaultAzureCredential19();
20886
+ const credential2 = new DefaultAzureCredential21();
20649
20887
  const account = await getAzAccount();
20650
20888
  const subscriptionId = this.subscription ?? account.subscriptionId;
20651
20889
  const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
@@ -20763,7 +21001,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
20763
21001
  };
20764
21002
 
20765
21003
  // src/commands/deploy.ts
20766
- import { Command as Command37, Option as Option34 } from "clipanion";
21004
+ import { Command as Command39, Option as Option36 } from "clipanion";
20767
21005
 
20768
21006
  // src/lib/app-reg.ts
20769
21007
  init_errors();
@@ -21038,6 +21276,51 @@ init_errors();
21038
21276
 
21039
21277
  // src/lib/whatif.ts
21040
21278
  import * as path27 from "path";
21279
+
21280
+ // src/lib/identifier-patterns.ts
21281
+ var IDENTIFIER_PATTERNS = [
21282
+ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,
21283
+ // any GUID (tenant/client/subscription id)
21284
+ /\bsha256:[0-9a-f]{8,}/gi,
21285
+ // image digests
21286
+ /\.azurecr\.io/gi,
21287
+ // registry hosts
21288
+ /\bsubscriptions\/[^\s'"]+/gi,
21289
+ // ARM ids (subscription path)
21290
+ /\bresourceGroups\/[^\s'"]+/gi
21291
+ ];
21292
+ function scrubIdentifiers(text) {
21293
+ let out = text;
21294
+ for (const pattern of IDENTIFIER_PATTERNS) out = out.replace(pattern, "[redacted]");
21295
+ return out;
21296
+ }
21297
+
21298
+ // src/lib/whatif-redact.ts
21299
+ function redactForLog(r) {
21300
+ const lines = [];
21301
+ const section = (title, items) => {
21302
+ if (items.length === 0) return;
21303
+ lines.push(`${title} (${items.length.toString()}):`);
21304
+ for (const c of items) {
21305
+ const reason = c.reason ? ` [${c.reason}]` : "";
21306
+ lines.push(` \u2022 ${c.resourceType} ${c.resourceName} ${c.path}${reason}`);
21307
+ }
21308
+ };
21309
+ section("Unexpected", r.unexpected);
21310
+ section("Known pending", r.pending);
21311
+ lines.push(`Noise suppressed: ${r.noise.length.toString()}`);
21312
+ return lines.join("\n");
21313
+ }
21314
+ function redactForReport(r) {
21315
+ return { unexpected: r.unexpected.length, pending: r.pending.length, noise: r.noise.length };
21316
+ }
21317
+ function redactWhatIfFailure(message) {
21318
+ const commandLineFailure = /^'.*?' failed: ([\s\S]*)$/.exec(message);
21319
+ const tail = commandLineFailure ? commandLineFailure[1] : message;
21320
+ return `the underlying comparison command failed (command line withheld): ${scrubIdentifiers(tail)}`;
21321
+ }
21322
+
21323
+ // src/lib/whatif.ts
21041
21324
  function deriveResourceType(resourceId) {
21042
21325
  if (resourceId.startsWith("[")) return "";
21043
21326
  const afterProviders = resourceId.split("/providers/")[1];
@@ -21069,6 +21352,10 @@ async function runWhatIf(opts) {
21069
21352
  var fmt = (c) => ` \u2022 ${c.resourceType} ${c.path}: ${JSON.stringify(c.before ?? null)} \u2192 ${JSON.stringify(c.after ?? null)}${c.reason ? ` [${c.reason}]` : ""}`;
21070
21353
  function reportWhatIf(r, opts) {
21071
21354
  const exitCode = r.unexpected.length === 0 ? 0 : 1;
21355
+ if (opts.redact) {
21356
+ const output = opts.json ? JSON.stringify({ summary: redactForReport(r), exitCode }, null, 2) : redactForLog(r);
21357
+ return { output, exitCode };
21358
+ }
21072
21359
  if (opts.json) {
21073
21360
  return { output: JSON.stringify({ summary: { noise: r.noise.length, pending: r.pending.length, unexpected: r.unexpected.length }, pending: r.pending, unexpected: r.unexpected, exitCode }, null, 2), exitCode };
21074
21361
  }
@@ -21088,6 +21375,16 @@ function reportWhatIf(r, opts) {
21088
21375
  }
21089
21376
  return { output: lines.join("\n"), exitCode };
21090
21377
  }
21378
+ var WHATIF_RUN_FAILED_EXIT_CODE = 3;
21379
+ function reportWhatIfFailure(err, opts) {
21380
+ const raw = err instanceof Error ? err.message : String(err);
21381
+ const message = opts.redact ? redactWhatIfFailure(raw) : raw;
21382
+ const exitCode = WHATIF_RUN_FAILED_EXIT_CODE;
21383
+ if (opts.json) {
21384
+ return { output: JSON.stringify({ ranToCompletion: false, error: message, exitCode }, null, 2), exitCode };
21385
+ }
21386
+ return { output: `comparison did not run to completion \u2014 NOT a drift finding: ${message}`, exitCode };
21387
+ }
21091
21388
 
21092
21389
  // src/lib/whatif-noise.ts
21093
21390
  var KNOWN_PENDING = [
@@ -21100,10 +21397,29 @@ var KNOWN_NOISE = [
21100
21397
  { resourceType: "Microsoft.App/containerApps", pathPattern: /(^|\.)runningStatus$/, reason: "RP runtime status" },
21101
21398
  { resourceType: "Microsoft.App/managedEnvironments", pathPattern: /(^|\.)peerAuthentication$/, reason: "RP default" },
21102
21399
  { resourceType: "Microsoft.App/managedEnvironments", pathPattern: /(^|\.)peerTrafficConfiguration$/, reason: "RP default" },
21400
+ // Connections can be account-scoped or project-scoped depending on the deployment's tracing mode.
21401
+ // Rules below match both resource type variants to ensure consistent noise classification.
21103
21402
  { resourceType: "Microsoft.CognitiveServices/accounts/connections", pathPattern: /(^|\.)credentials$/, reason: "write-only credential \u2014 always re-shows" },
21104
21403
  { resourceType: "Microsoft.CognitiveServices/accounts/connections", pathPattern: /(^|\.)(group|isDefault|peRequirement|peStatus|useWorkspaceManagedIdentity)$/, reason: "RP connection metadata" },
21404
+ { resourceType: "Microsoft.CognitiveServices/accounts/projects/connections", pathPattern: /(^|\.)credentials$/, reason: "write-only credential \u2014 always re-shows" },
21405
+ { resourceType: "Microsoft.CognitiveServices/accounts/projects/connections", pathPattern: /(^|\.)(group|isDefault|peRequirement|peStatus|useWorkspaceManagedIdentity)$/, reason: "RP connection metadata" },
21105
21406
  { resourceType: "Microsoft.Insights/components", pathPattern: /(^|\.)(Flow_Type|Request_Source)$/, reason: "RP-stamped" },
21106
- { resourceType: "Microsoft.ServiceBus/namespaces/queues", pathPattern: /(^|\.)(autoDeleteOnIdle|duplicateDetectionHistoryTimeWindow|maxMessageSizeInKilobytes|maxSizeInMegabytes)$/, reason: "RP default the bicep doesn't declare" }
21407
+ { resourceType: "Microsoft.ServiceBus/namespaces/queues", pathPattern: /(^|\.)(autoDeleteOnIdle|duplicateDetectionHistoryTimeWindow|maxMessageSizeInKilobytes|maxSizeInMegabytes)$/, reason: "RP default the bicep doesn't declare" },
21408
+ // The template regenerates the relay's shared secret on every deployment unless the
21409
+ // current value is passed through, so a comparison that omits it reports a fresh
21410
+ // difference every run. Ownership of that parameter sits with the voice work, not
21411
+ // here — this comparison neither reads it nor asserts on it.
21412
+ //
21413
+ // The indexed rule is deliberately end-anchored to `.value` ONLY: an unanchored
21414
+ // pattern would also swallow `configuration.secrets[N].name`,
21415
+ // `.keyVaultUrl`/vault reference, and `.identity` — a repointed vault reference or a
21416
+ // swapped backing identity on ANY container app would then be invisible to the very
21417
+ // check meant to catch it, not just the regenerated secret's own value.
21418
+ //
21419
+ // REMOVE THESE TWO RULES when the deployment path stops regenerating the value.
21420
+ // Their presence is the marker that it still does.
21421
+ { resourceType: "Microsoft.App/containerApps", pathPattern: /(^|\.)configuration\.secrets$/, reason: "shared relay secret is regenerated on every deployment \u2014 owned elsewhere" },
21422
+ { resourceType: "Microsoft.App/containerApps", pathPattern: /(^|\.)configuration\.secrets\[\d+\]\.value$/, reason: "shared relay secret is regenerated on every deployment \u2014 owned elsewhere" }
21107
21423
  ];
21108
21424
  var shortName = (resourceId) => resourceId.startsWith("[") ? "(role assignment)" : resourceId.split("/").pop() ?? resourceId;
21109
21425
  function isArmExpression(v) {
@@ -21165,40 +21481,52 @@ function classifyWhatIf(changes) {
21165
21481
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
21166
21482
  var DeployCommand = class extends M8tCommand {
21167
21483
  static paths = [["deploy"]];
21168
- static usage = Command37.Usage({
21484
+ static usage = Command39.Usage({
21169
21485
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
21170
21486
  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."
21171
21487
  });
21172
- subscription = Option34.String("--subscription");
21173
- resourceGroup = Option34.String("--resource-group", "rg-m8t-stack");
21174
- location = Option34.String("--location", "eastus");
21175
- suffix = Option34.String("--suffix", "");
21176
- imageRef = Option34.String("--image-ref", DEFAULT_IMAGE_REF);
21177
- acrPullIdentity = Option34.String("--acrpull-identity");
21178
- acrResourceId = Option34.String("--acr-resource-id");
21179
- foundryEndpoint = Option34.String("--foundry-endpoint");
21180
- foundryResourceId = Option34.String("--foundry-resource-id");
21181
- foundryTracing = Option34.String("--foundry-tracing");
21488
+ subscription = Option36.String("--subscription");
21489
+ resourceGroup = Option36.String("--resource-group", "rg-m8t-stack");
21490
+ location = Option36.String("--location", "eastus");
21491
+ suffix = Option36.String("--suffix", "");
21492
+ imageRef = Option36.String("--image-ref", DEFAULT_IMAGE_REF);
21493
+ acrPullIdentity = Option36.String("--acrpull-identity");
21494
+ acrResourceId = Option36.String("--acr-resource-id");
21495
+ foundryEndpoint = Option36.String("--foundry-endpoint");
21496
+ foundryResourceId = Option36.String("--foundry-resource-id");
21497
+ foundryTracing = Option36.String("--foundry-tracing");
21182
21498
  // project | account | skip (bicep default: project)
21183
- clientId = Option34.String("--client-id");
21184
- whatIf = Option34.Boolean("--what-if", false);
21185
- output = Option34.String("--output");
21499
+ clientId = Option36.String("--client-id");
21500
+ whatIf = Option36.Boolean("--what-if", false);
21501
+ // Only meaningful with --what-if. Routes the comparison through the
21502
+ // value-free renderers (see ./lib/whatif-redact.js) instead of the default
21503
+ // before/after renderer. Defaults false so a local, interactive run keeps
21504
+ // showing values — that is the whole diagnostic point of --what-if.
21505
+ // Automation that forwards this output anywhere non-private (a CI log, an
21506
+ // issue) MUST pass --redact.
21507
+ redact = Option36.Boolean("--redact", false);
21508
+ output = Option36.String("--output");
21509
+ // Subscription-scoped role assignments. Omitted ⇒ the template default (true).
21510
+ // Pass false when deploying as a principal scoped to the resource group only:
21511
+ // it cannot deploy at subscription scope, and those assignments persist
21512
+ // idempotently from the initial deployment anyway.
21513
+ assignSubscriptionRoles = Option36.String("--assign-subscription-roles");
21186
21514
  // Referee — all optional, undefined by default ⇒ the bicep defaults
21187
21515
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
21188
21516
  // empty). Only pass these when explicitly enabling the referee exam stack.
21189
- gatewayCpu = Option34.String("--gateway-cpu");
21190
- gatewayMemory = Option34.String("--gateway-memory");
21191
- refereeEnabled = Option34.String("--referee-enabled");
21192
- refereeBrainRepos = Option34.String("--referee-brain-repos");
21193
- refereeFeedRepo = Option34.String("--referee-feed-repo");
21194
- refereeInstallationId = Option34.String("--referee-installation-id");
21195
- refereeWebhookHmacKvUri = Option34.String("--referee-webhook-hmac-kv-uri");
21196
- examKvUri = Option34.String("--exam-kv-uri");
21197
- examLaWorkspaceId = Option34.String("--exam-la-workspace-id");
21198
- brainEvalDeployment = Option34.String("--brain-eval-deployment");
21199
- brainAppLogin = Option34.String("--brain-app-login");
21200
- refereeCheckpointDir = Option34.String("--referee-checkpoint-dir");
21201
- examApiBase = Option34.String("--exam-api-base");
21517
+ gatewayCpu = Option36.String("--gateway-cpu");
21518
+ gatewayMemory = Option36.String("--gateway-memory");
21519
+ refereeEnabled = Option36.String("--referee-enabled");
21520
+ refereeBrainRepos = Option36.String("--referee-brain-repos");
21521
+ refereeFeedRepo = Option36.String("--referee-feed-repo");
21522
+ refereeInstallationId = Option36.String("--referee-installation-id");
21523
+ refereeWebhookHmacKvUri = Option36.String("--referee-webhook-hmac-kv-uri");
21524
+ examKvUri = Option36.String("--exam-kv-uri");
21525
+ examLaWorkspaceId = Option36.String("--exam-la-workspace-id");
21526
+ brainEvalDeployment = Option36.String("--brain-eval-deployment");
21527
+ brainAppLogin = Option36.String("--brain-app-login");
21528
+ refereeCheckpointDir = Option36.String("--referee-checkpoint-dir");
21529
+ examApiBase = Option36.String("--exam-api-base");
21202
21530
  async executeCommand() {
21203
21531
  const mode = resolveOutputMode(
21204
21532
  this.output,
@@ -21207,6 +21535,69 @@ var DeployCommand = class extends M8tCommand {
21207
21535
  const log = (msg) => {
21208
21536
  if (mode !== "json") this.context.stdout.write(msg + "\n");
21209
21537
  };
21538
+ if (this.whatIf) {
21539
+ try {
21540
+ if (this.subscription) {
21541
+ await runAz(["account", "set", "--subscription", this.subscription]);
21542
+ }
21543
+ const account2 = await getAzAccount();
21544
+ const foundryEndpoint2 = this.foundryEndpoint ?? (await readFoundryConfig())?.projectEndpoint;
21545
+ if (!foundryEndpoint2) {
21546
+ throw new LocalCliError({
21547
+ code: "DEPLOY_NO_FOUNDRY_ENDPOINT",
21548
+ message: "No Foundry endpoint provided.",
21549
+ hint: "Pass --foundry-endpoint <url> or set projectEndpoint in ~/.m8t/config.yaml (m8t config set / m8t switch)."
21550
+ });
21551
+ }
21552
+ const clientId = this.clientId ?? (await readFoundryConfig())?.clientId;
21553
+ if (!clientId) {
21554
+ throw new LocalCliError({
21555
+ code: "DEPLOY_WHATIF_NO_CLIENT",
21556
+ message: "--what-if needs a client id but none was provided or configured.",
21557
+ hint: "Pass --client-id <appId>, or run from a host with ~/.m8t/config.yaml."
21558
+ });
21559
+ }
21560
+ const foundryResourceId2 = await resolveFoundryResourceId(foundryEndpoint2, this.foundryResourceId);
21561
+ const repoRoot2 = await resolveRepoRoot2();
21562
+ const acrPullIdentityResourceId2 = resolveAcrPullIdentity({ explicit: this.acrPullIdentity });
21563
+ const acrResourceId2 = acrPullIdentityResourceId2 ? "" : await resolveAcrResourceId({ imageRef: this.imageRef, explicit: this.acrResourceId });
21564
+ const params2 = buildBicepParams({
21565
+ location: this.location,
21566
+ suffix: this.suffix,
21567
+ imageRef: this.imageRef,
21568
+ tenantId: account2.tenantId,
21569
+ clientId,
21570
+ foundryResourceId: foundryResourceId2,
21571
+ foundryProjectEndpoint: foundryEndpoint2,
21572
+ acrPullIdentityResourceId: acrPullIdentityResourceId2,
21573
+ acrResourceId: acrResourceId2,
21574
+ foundryTracingMode: parseFoundryTracingMode(this.foundryTracing),
21575
+ gatewayCpu: this.gatewayCpu,
21576
+ gatewayMemory: this.gatewayMemory,
21577
+ refereeEnabled: this.refereeEnabled,
21578
+ refereeBrainRepos: this.refereeBrainRepos,
21579
+ refereeFeedRepo: this.refereeFeedRepo,
21580
+ refereeInstallationId: this.refereeInstallationId,
21581
+ refereeWebhookHmacKvUri: this.refereeWebhookHmacKvUri,
21582
+ examKvUri: this.examKvUri,
21583
+ examLaWorkspaceId: this.examLaWorkspaceId,
21584
+ brainEvalDeployment: this.brainEvalDeployment,
21585
+ brainAppLogin: this.brainAppLogin,
21586
+ refereeCheckpointDir: this.refereeCheckpointDir,
21587
+ examApiBase: this.examApiBase,
21588
+ ...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) }
21589
+ });
21590
+ const changes = await runWhatIf({ resourceGroup: this.resourceGroup, repoRoot: repoRoot2, params: params2 });
21591
+ const classified = classifyWhatIf(changes);
21592
+ const { output, exitCode } = reportWhatIf(classified, { json: mode === "json", redact: this.redact });
21593
+ this.context.stdout.write(output + "\n");
21594
+ return exitCode;
21595
+ } catch (e) {
21596
+ const { output, exitCode } = reportWhatIfFailure(e, { json: mode === "json", redact: this.redact });
21597
+ this.context.stdout.write(output + "\n");
21598
+ return exitCode;
21599
+ }
21600
+ }
21210
21601
  if (this.subscription) {
21211
21602
  await runAz(["account", "set", "--subscription", this.subscription]);
21212
21603
  }
@@ -21219,50 +21610,6 @@ var DeployCommand = class extends M8tCommand {
21219
21610
  hint: "Pass --foundry-endpoint <url> or set projectEndpoint in ~/.m8t/config.yaml (m8t config set / m8t switch)."
21220
21611
  });
21221
21612
  }
21222
- if (this.whatIf) {
21223
- const clientId = this.clientId ?? (await readFoundryConfig())?.clientId;
21224
- if (!clientId) {
21225
- throw new LocalCliError({
21226
- code: "DEPLOY_WHATIF_NO_CLIENT",
21227
- message: "--what-if needs a client id but none was provided or configured.",
21228
- hint: "Pass --client-id <appId>, or run from a host with ~/.m8t/config.yaml."
21229
- });
21230
- }
21231
- const foundryResourceId2 = await resolveFoundryResourceId(foundryEndpoint, this.foundryResourceId);
21232
- const repoRoot2 = await resolveRepoRoot2();
21233
- const acrPullIdentityResourceId2 = resolveAcrPullIdentity({ explicit: this.acrPullIdentity });
21234
- const acrResourceId2 = acrPullIdentityResourceId2 ? "" : await resolveAcrResourceId({ imageRef: this.imageRef, explicit: this.acrResourceId });
21235
- const params2 = buildBicepParams({
21236
- location: this.location,
21237
- suffix: this.suffix,
21238
- imageRef: this.imageRef,
21239
- tenantId: account.tenantId,
21240
- clientId,
21241
- foundryResourceId: foundryResourceId2,
21242
- foundryProjectEndpoint: foundryEndpoint,
21243
- acrPullIdentityResourceId: acrPullIdentityResourceId2,
21244
- acrResourceId: acrResourceId2,
21245
- foundryTracingMode: parseFoundryTracingMode(this.foundryTracing),
21246
- gatewayCpu: this.gatewayCpu,
21247
- gatewayMemory: this.gatewayMemory,
21248
- refereeEnabled: this.refereeEnabled,
21249
- refereeBrainRepos: this.refereeBrainRepos,
21250
- refereeFeedRepo: this.refereeFeedRepo,
21251
- refereeInstallationId: this.refereeInstallationId,
21252
- refereeWebhookHmacKvUri: this.refereeWebhookHmacKvUri,
21253
- examKvUri: this.examKvUri,
21254
- examLaWorkspaceId: this.examLaWorkspaceId,
21255
- brainEvalDeployment: this.brainEvalDeployment,
21256
- brainAppLogin: this.brainAppLogin,
21257
- refereeCheckpointDir: this.refereeCheckpointDir,
21258
- examApiBase: this.examApiBase
21259
- });
21260
- const changes = await runWhatIf({ resourceGroup: this.resourceGroup, repoRoot: repoRoot2, params: params2 });
21261
- const classified = classifyWhatIf(changes);
21262
- const { output, exitCode } = reportWhatIf(classified, { json: mode === "json" });
21263
- this.context.stdout.write(output + "\n");
21264
- return exitCode;
21265
- }
21266
21613
  log(this.clientId ? `using existing app reg ${this.clientId}` : "ensuring Entra app reg\u2026");
21267
21614
  const app = await ensureAppReg({ tenantId: account.tenantId, clientId: this.clientId });
21268
21615
  if (app.appObjectId === null && this.clientId) {
@@ -21303,7 +21650,8 @@ var DeployCommand = class extends M8tCommand {
21303
21650
  brainEvalDeployment: this.brainEvalDeployment,
21304
21651
  brainAppLogin: this.brainAppLogin,
21305
21652
  refereeCheckpointDir: this.refereeCheckpointDir,
21306
- examApiBase: this.examApiBase
21653
+ examApiBase: this.examApiBase,
21654
+ ...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) }
21307
21655
  });
21308
21656
  log("running Bicep deployment (~3-5 min)\u2026");
21309
21657
  const outputs = await runBicepDeployment({
@@ -21338,7 +21686,7 @@ var DeployCommand = class extends M8tCommand {
21338
21686
 
21339
21687
  // src/commands/eval/skill.ts
21340
21688
  import { spawnSync as spawnSync4 } from "child_process";
21341
- import { Command as Command38, Option as Option35 } from "clipanion";
21689
+ import { Command as Command40, Option as Option37 } from "clipanion";
21342
21690
  init_errors();
21343
21691
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
21344
21692
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -21363,14 +21711,14 @@ function parseVerdict(stdout) {
21363
21711
  }
21364
21712
  var EvalSkillCommand = class extends M8tCommand {
21365
21713
  static paths = [["eval", "skill"]];
21366
- static usage = Command38.Usage({
21714
+ static usage = Command40.Usage({
21367
21715
  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)."
21368
21716
  });
21369
- candidate = Option35.String();
21370
- skillsDir = Option35.String("--skills-dir");
21371
- noJudge = Option35.Boolean("--no-judge", false);
21372
- deployment = Option35.String("--deployment");
21373
- output = Option35.String("--output");
21717
+ candidate = Option37.String();
21718
+ skillsDir = Option37.String("--skills-dir");
21719
+ noJudge = Option37.Boolean("--no-judge", false);
21720
+ deployment = Option37.String("--deployment");
21721
+ output = Option37.String("--output");
21374
21722
  executeCommand() {
21375
21723
  return Promise.resolve(this._runCommand());
21376
21724
  }
@@ -21429,7 +21777,7 @@ var EvalSkillCommand = class extends M8tCommand {
21429
21777
  import { spawnSync as spawnSync5 } from "child_process";
21430
21778
  import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync18, existsSync as existsSync15, readdirSync as readdirSync2 } from "fs";
21431
21779
  import { join as join27 } from "path";
21432
- import { Command as Command39, Option as Option36 } from "clipanion";
21780
+ import { Command as Command41, Option as Option38 } from "clipanion";
21433
21781
  init_errors();
21434
21782
  init_esm();
21435
21783
  function parseArmToken(tok, opts) {
@@ -21659,24 +22007,24 @@ function buildPlan(args) {
21659
22007
  }
21660
22008
  var EvalExamCommand = class extends M8tCommand {
21661
22009
  static paths = [["eval", "exam"]];
21662
- static usage = Command39.Usage({
22010
+ static usage = Command41.Usage({
21663
22011
  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."
21664
22012
  });
21665
- worker = Option36.String();
21666
- arms = Option36.String("--arms");
21667
- taskSet = Option36.String("--task-set");
21668
- examType = Option36.String("--exam-type");
21669
- skill = Option36.String("--skill");
21670
- reps = Option36.String("-n,--reps");
21671
- probes = Option36.String("--probes");
21672
- pool = Option36.String("--pool");
21673
- out = Option36.String("--out");
21674
- dryRun = Option36.Boolean("--dry-run", false);
21675
- keepArms = Option36.Boolean("--keep-arms", false);
21676
- allowStub = Option36.Boolean("--allow-stub", false);
21677
- deployment = Option36.String("--deployment");
21678
- output = Option36.String("--output");
21679
- observeWaitS = Option36.String("--observe-wait-s");
22013
+ worker = Option38.String();
22014
+ arms = Option38.String("--arms");
22015
+ taskSet = Option38.String("--task-set");
22016
+ examType = Option38.String("--exam-type");
22017
+ skill = Option38.String("--skill");
22018
+ reps = Option38.String("-n,--reps");
22019
+ probes = Option38.String("--probes");
22020
+ pool = Option38.String("--pool");
22021
+ out = Option38.String("--out");
22022
+ dryRun = Option38.Boolean("--dry-run", false);
22023
+ keepArms = Option38.Boolean("--keep-arms", false);
22024
+ allowStub = Option38.Boolean("--allow-stub", false);
22025
+ deployment = Option38.String("--deployment");
22026
+ output = Option38.String("--output");
22027
+ observeWaitS = Option38.String("--observe-wait-s");
21680
22028
  async executeCommand() {
21681
22029
  await Promise.resolve();
21682
22030
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -21791,10 +22139,10 @@ var EvalExamCommand = class extends M8tCommand {
21791
22139
  };
21792
22140
 
21793
22141
  // src/commands/version.ts
21794
- import { Command as Command40, Option as Option37 } from "clipanion";
22142
+ import { Command as Command42, Option as Option39 } from "clipanion";
21795
22143
  var VersionCommand = class extends M8tCommand {
21796
22144
  static paths = [["version"], ["--version"], ["-v"]];
21797
- static usage = Command40.Usage({
22145
+ static usage = Command42.Usage({
21798
22146
  description: "Print the CLI version.",
21799
22147
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
21800
22148
  examples: [
@@ -21802,8 +22150,8 @@ var VersionCommand = class extends M8tCommand {
21802
22150
  ["Print as JSON", "$0 version --output json"]
21803
22151
  ]
21804
22152
  });
21805
- output = Option37.String("--output", { description: "pretty | json | auto (default)" });
21806
- verbose = Option37.Boolean("--verbose", false);
22153
+ output = Option39.String("--output", { description: "pretty | json | auto (default)" });
22154
+ verbose = Option39.Boolean("--verbose", false);
21807
22155
  executeCommand() {
21808
22156
  const mode = resolveOutputMode(
21809
22157
  this.output ?? "auto",
@@ -21834,18 +22182,18 @@ var VersionCommand = class extends M8tCommand {
21834
22182
  };
21835
22183
 
21836
22184
  // src/commands/whoami.ts
21837
- import { Command as Command41, Option as Option38 } from "clipanion";
22185
+ import { Command as Command43, Option as Option40 } from "clipanion";
21838
22186
  var WhoamiCommand = class extends M8tCommand {
21839
22187
  static paths = [["whoami"]];
21840
- static usage = Command41.Usage({
22188
+ static usage = Command43.Usage({
21841
22189
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
21842
22190
  });
21843
- output = Option38.String("--output");
21844
- verbose = Option38.Boolean("--verbose", false);
21845
- subscription = Option38.String("--subscription", {
22191
+ output = Option40.String("--output");
22192
+ verbose = Option40.Boolean("--verbose", false);
22193
+ subscription = Option40.String("--subscription", {
21846
22194
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
21847
22195
  });
21848
- resourceGroup = Option38.String("--resource-group", {
22196
+ resourceGroup = Option40.String("--resource-group", {
21849
22197
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
21850
22198
  });
21851
22199
  async executeCommand() {
@@ -21910,7 +22258,7 @@ var WhoamiCommand = class extends M8tCommand {
21910
22258
  };
21911
22259
 
21912
22260
  // src/commands/status.ts
21913
- import { Command as Command42, Option as Option39 } from "clipanion";
22261
+ import { Command as Command44, Option as Option41 } from "clipanion";
21914
22262
 
21915
22263
  // src/lib/azd.ts
21916
22264
  init_errors();
@@ -21975,10 +22323,10 @@ async function resolveLocalContext() {
21975
22323
  // src/commands/status.ts
21976
22324
  var StatusCommand = class extends M8tCommand {
21977
22325
  static paths = [["status"]];
21978
- static usage = Command42.Usage({
22326
+ static usage = Command44.Usage({
21979
22327
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
21980
22328
  });
21981
- output = Option39.String("--output");
22329
+ output = Option41.String("--output");
21982
22330
  async executeCommand() {
21983
22331
  const mode = resolveOutputMode(
21984
22332
  this.output,
@@ -22016,8 +22364,8 @@ var StatusCommand = class extends M8tCommand {
22016
22364
  };
22017
22365
 
22018
22366
  // src/commands/doctor.ts
22019
- import { Command as Command43, Option as Option40 } from "clipanion";
22020
- import { DefaultAzureCredential as DefaultAzureCredential20 } from "@azure/identity";
22367
+ import { Command as Command45, Option as Option42 } from "clipanion";
22368
+ import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
22021
22369
  import * as fs26 from "fs";
22022
22370
  import * as os11 from "os";
22023
22371
  import * as path28 from "path";
@@ -22292,12 +22640,12 @@ function probeLegacyStateDir() {
22292
22640
  }
22293
22641
  var DoctorCommand = class extends M8tCommand {
22294
22642
  static paths = [["doctor"]];
22295
- static usage = Command43.Usage({
22643
+ static usage = Command45.Usage({
22296
22644
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
22297
22645
  });
22298
- output = Option40.String("--output");
22299
- agent = Option40.String("--agent");
22300
- resourceGroup = Option40.String("--resource-group", {
22646
+ output = Option42.String("--output");
22647
+ agent = Option42.String("--agent");
22648
+ resourceGroup = Option42.String("--resource-group", {
22301
22649
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
22302
22650
  });
22303
22651
  async executeCommand() {
@@ -22371,7 +22719,7 @@ var DoctorCommand = class extends M8tCommand {
22371
22719
  if (typeof this.agent === "string" && this.agent) {
22372
22720
  checking(`delivery grant for ${this.agent}`);
22373
22721
  try {
22374
- const credential2 = new DefaultAzureCredential20();
22722
+ const credential2 = new DefaultAzureCredential22();
22375
22723
  const cur = await getAgentVersion({
22376
22724
  credential: credential2,
22377
22725
  projectEndpoint: foundry.projectEndpoint,
@@ -22414,7 +22762,7 @@ var DoctorCommand = class extends M8tCommand {
22414
22762
  };
22415
22763
 
22416
22764
  // src/commands/switch.ts
22417
- import { Command as Command44, Option as Option41 } from "clipanion";
22765
+ import { Command as Command46, Option as Option43 } from "clipanion";
22418
22766
 
22419
22767
  // src/lib/profiles.ts
22420
22768
  import * as fs27 from "fs/promises";
@@ -22554,14 +22902,14 @@ async function profileSwitch(name, asName) {
22554
22902
  init_errors();
22555
22903
  var SwitchCommand = class extends M8tCommand {
22556
22904
  static paths = [["switch"]];
22557
- static usage = Command44.Usage({
22905
+ static usage = Command46.Usage({
22558
22906
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
22559
22907
  });
22560
- profile = Option41.String({ required: false });
22561
- subscription = Option41.String("--subscription");
22562
- list = Option41.Boolean("--list", false);
22563
- as = Option41.String("--as");
22564
- output = Option41.String("--output");
22908
+ profile = Option43.String({ required: false });
22909
+ subscription = Option43.String("--subscription");
22910
+ list = Option43.Boolean("--list", false);
22911
+ as = Option43.String("--as");
22912
+ output = Option43.String("--output");
22565
22913
  async executeCommand() {
22566
22914
  const mode = resolveOutputMode(
22567
22915
  this.output,
@@ -22618,7 +22966,7 @@ var SwitchCommand = class extends M8tCommand {
22618
22966
 
22619
22967
  // src/commands/open.ts
22620
22968
  import { spawn as spawn5 } from "child_process";
22621
- import { Command as Command45, Option as Option42 } from "clipanion";
22969
+ import { Command as Command47, Option as Option44 } from "clipanion";
22622
22970
 
22623
22971
  // src/lib/open-targets.ts
22624
22972
  init_errors();
@@ -22664,14 +23012,14 @@ function openUrl(url) {
22664
23012
  }
22665
23013
  var OpenCommand = class extends M8tCommand {
22666
23014
  static paths = [["open"]];
22667
- static usage = Command45.Usage({
23015
+ static usage = Command47.Usage({
22668
23016
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
22669
23017
  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)."
22670
23018
  });
22671
- target = Option42.String({ required: false });
22672
- print = Option42.Boolean("--print", false);
22673
- output = Option42.String("--output");
22674
- resourceGroup = Option42.String("--resource-group", {
23019
+ target = Option44.String({ required: false });
23020
+ print = Option44.Boolean("--print", false);
23021
+ output = Option44.String("--output");
23022
+ resourceGroup = Option44.String("--resource-group", {
22675
23023
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
22676
23024
  });
22677
23025
  async executeCommand() {
@@ -22715,9 +23063,9 @@ var OpenCommand = class extends M8tCommand {
22715
23063
  };
22716
23064
 
22717
23065
  // src/commands/dream/run.ts
22718
- import { Command as Command46, Option as Option43 } from "clipanion";
23066
+ import { Command as Command48, Option as Option45 } from "clipanion";
22719
23067
  import { AzureCliCredential } from "@azure/identity";
22720
- import { TableClient as TableClient6 } from "@azure/data-tables";
23068
+ import { TableClient as TableClient7 } from "@azure/data-tables";
22721
23069
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
22722
23070
  import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
22723
23071
 
@@ -23026,7 +23374,7 @@ var ConvFetchError = class extends Error {
23026
23374
  };
23027
23375
 
23028
23376
  // ../../packages/brain/engine/dist/esm/cursor.js
23029
- import { TableClient as TableClient5 } from "@azure/data-tables";
23377
+ import { TableClient as TableClient6 } from "@azure/data-tables";
23030
23378
 
23031
23379
  // ../../packages/agent-ledger/dist/esm/row-key.js
23032
23380
  var MAX_MS = 864e13;
@@ -23166,7 +23514,7 @@ async function commitCursorAdvance(plan, cursor) {
23166
23514
  }
23167
23515
  var CURSOR_TABLE_NAME = "BrainDreamCursor";
23168
23516
  function makeTableCursor(credential2, tableEndpoint) {
23169
- const client = new TableClient5(tableEndpoint, CURSOR_TABLE_NAME, credential2);
23517
+ const client = new TableClient6(tableEndpoint, CURSOR_TABLE_NAME, credential2);
23170
23518
  return new TableCursor(client);
23171
23519
  }
23172
23520
  function formatCursorPreview(cursor) {
@@ -24942,20 +25290,20 @@ function redactTranscripts(input) {
24942
25290
  }
24943
25291
  var DreamRunCommand = class extends M8tCommand {
24944
25292
  static paths = [["dream", "run"]];
24945
- static usage = Command46.Usage({
25293
+ static usage = Command48.Usage({
24946
25294
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes).",
24947
25295
  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."
24948
25296
  });
24949
- worker = Option43.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
24950
- dryRun = Option43.Boolean("--dry-run", false, { description: "Read-only harvest; no model call, no writes." });
24951
- since = Option43.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
24952
- reset = Option43.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
24953
- showTranscripts = Option43.Boolean("--show-transcripts", false, {
25297
+ worker = Option45.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
25298
+ dryRun = Option45.Boolean("--dry-run", false, { description: "Read-only harvest; no model call, no writes." });
25299
+ since = Option45.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
25300
+ reset = Option45.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
25301
+ showTranscripts = Option45.Boolean("--show-transcripts", false, {
24954
25302
  description: "Print transcript bodies (default: metadata only)."
24955
25303
  });
24956
- subscription = Option43.String("--subscription");
24957
- endpoint = Option43.String("--endpoint");
24958
- output = Option43.String("--output");
25304
+ subscription = Option45.String("--subscription");
25305
+ endpoint = Option45.String("--endpoint");
25306
+ output = Option45.String("--output");
24959
25307
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
24960
25308
  deps;
24961
25309
  async executeCommand() {
@@ -25150,7 +25498,7 @@ AppDependencies
25150
25498
  };
25151
25499
  }
25152
25500
  function buildLiveSources(context, credential2) {
25153
- const ledgerClient = new TableClient6(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
25501
+ const ledgerClient = new TableClient7(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
25154
25502
  const ledger = makeLedgerSource(
25155
25503
  async (pk, sinceTs) => fetchLedgerRows(
25156
25504
  { workers: [pk], source: "all", from: sinceTs, to: "9999-12-31T23:59:59.999Z" },
@@ -25303,7 +25651,7 @@ function defaultDeps(overrides) {
25303
25651
  }
25304
25652
 
25305
25653
  // src/commands/foundry/create.ts
25306
- import { Command as Command47, Option as Option44 } from "clipanion";
25654
+ import { Command as Command49, Option as Option46 } from "clipanion";
25307
25655
 
25308
25656
  // src/lib/foundry-create.ts
25309
25657
  init_errors();
@@ -25541,7 +25889,7 @@ async function createFoundryProject(args) {
25541
25889
  init_errors();
25542
25890
  var FoundryCreateCommand = class extends M8tCommand {
25543
25891
  static paths = [["foundry", "create"]];
25544
- static usage = Command47.Usage({
25892
+ static usage = Command49.Usage({
25545
25893
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
25546
25894
  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).",
25547
25895
  examples: [
@@ -25550,16 +25898,16 @@ var FoundryCreateCommand = class extends M8tCommand {
25550
25898
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
25551
25899
  ]
25552
25900
  });
25553
- resourceGroup = Option44.String("--resource-group");
25554
- location = Option44.String("--location");
25555
- account = Option44.String("--account");
25556
- project = Option44.String("--project", "m8t");
25557
- model = Option44.String("--model", "gpt-4.1-mini");
25558
- modelVersion = Option44.String("--model-version", "2025-04-14");
25559
- capacity = Option44.String("--capacity", "50");
25560
- subscription = Option44.String("--subscription");
25561
- skipQuotaCheck = Option44.Boolean("--skip-quota-check", false);
25562
- output = Option44.String("--output");
25901
+ resourceGroup = Option46.String("--resource-group");
25902
+ location = Option46.String("--location");
25903
+ account = Option46.String("--account");
25904
+ project = Option46.String("--project", "m8t");
25905
+ model = Option46.String("--model", "gpt-4.1-mini");
25906
+ modelVersion = Option46.String("--model-version", "2025-04-14");
25907
+ capacity = Option46.String("--capacity", "50");
25908
+ subscription = Option46.String("--subscription");
25909
+ skipQuotaCheck = Option46.Boolean("--skip-quota-check", false);
25910
+ output = Option46.String("--output");
25563
25911
  async executeCommand() {
25564
25912
  const mode = resolveOutputMode(
25565
25913
  this.output,
@@ -25631,22 +25979,22 @@ var FoundryCreateCommand = class extends M8tCommand {
25631
25979
  };
25632
25980
 
25633
25981
  // src/commands/foundry/await-ready.ts
25634
- import { Command as Command48, Option as Option45 } from "clipanion";
25982
+ import { Command as Command50, Option as Option47 } from "clipanion";
25635
25983
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
25636
25984
  init_errors();
25637
25985
  var FoundryAwaitReadyCommand = class extends M8tCommand {
25638
25986
  static paths = [["foundry", "await-ready"]];
25639
- static usage = Command48.Usage({
25987
+ static usage = Command50.Usage({
25640
25988
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
25641
25989
  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.",
25642
25990
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
25643
25991
  });
25644
- endpoint = Option45.String("--endpoint");
25645
- consecutive = Option45.String("--consecutive", "3");
25646
- attempts = Option45.String("--attempts", "60");
25647
- interval = Option45.String("--interval", "5");
25648
- subscription = Option45.String("--subscription");
25649
- output = Option45.String("--output");
25992
+ endpoint = Option47.String("--endpoint");
25993
+ consecutive = Option47.String("--consecutive", "3");
25994
+ attempts = Option47.String("--attempts", "60");
25995
+ interval = Option47.String("--interval", "5");
25996
+ subscription = Option47.String("--subscription");
25997
+ output = Option47.String("--output");
25650
25998
  async executeCommand() {
25651
25999
  const mode = resolveOutputMode(this.output, this.context.stdout);
25652
26000
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -25680,7 +26028,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
25680
26028
  };
25681
26029
 
25682
26030
  // src/commands/bootstrap/preflight.ts
25683
- import { Command as Command49, Option as Option46 } from "clipanion";
26031
+ import { Command as Command51, Option as Option48 } from "clipanion";
25684
26032
 
25685
26033
  // ../../packages/telemetry-contract/artifact/tier-map.ts
25686
26034
  var EVENT_TIERS = {
@@ -25791,7 +26139,7 @@ function buildPreflightBanner(who) {
25791
26139
  // src/commands/bootstrap/preflight.ts
25792
26140
  var BootstrapPreflightCommand = class extends M8tCommand {
25793
26141
  static paths = [["bootstrap", "preflight"]];
25794
- static usage = Command49.Usage({
26142
+ static usage = Command51.Usage({
25795
26143
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
25796
26144
  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), and registers Microsoft.ContainerInstance. Exits non-zero with the exact failing check + remedy.",
25797
26145
  examples: [
@@ -25799,8 +26147,8 @@ var BootstrapPreflightCommand = class extends M8tCommand {
25799
26147
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
25800
26148
  ]
25801
26149
  });
25802
- clientId = Option46.String("--client-id");
25803
- subscription = Option46.String("--subscription");
26150
+ clientId = Option48.String("--client-id");
26151
+ subscription = Option48.String("--subscription");
25804
26152
  async executeCommand() {
25805
26153
  const clientId = typeof this.clientId === "string" ? this.clientId : void 0;
25806
26154
  const account = await getAzAccount();
@@ -25863,7 +26211,7 @@ ${colors.error(" " + why)}
25863
26211
  import * as fs30 from "fs";
25864
26212
  import * as os14 from "os";
25865
26213
  import * as path32 from "path";
25866
- import { Command as Command50, Option as Option47 } from "clipanion";
26214
+ import { Command as Command52, Option as Option49 } from "clipanion";
25867
26215
  init_errors();
25868
26216
 
25869
26217
  // src/lib/bootstrap-mi.ts
@@ -26210,7 +26558,7 @@ var ACI_NAME = "m8t-installer";
26210
26558
  var MI_NAME = "m8t-installer-mi";
26211
26559
  var BootstrapLaunchCommand = class extends M8tCommand {
26212
26560
  static paths = [["bootstrap", "launch"]];
26213
- static usage = Command50.Usage({
26561
+ static usage = Command52.Usage({
26214
26562
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
26215
26563
  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`/`reap`/`finish`.",
26216
26564
  examples: [
@@ -26221,24 +26569,24 @@ var BootstrapLaunchCommand = class extends M8tCommand {
26221
26569
  ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
26222
26570
  ]
26223
26571
  });
26224
- location = Option47.String("--location");
26225
- resourceGroup = Option47.String("--resource-group");
26226
- clientId = Option47.String("--client-id");
26227
- subscription = Option47.String("--subscription");
26228
- installerTag = Option47.String("--installer-tag");
26572
+ location = Option49.String("--location");
26573
+ resourceGroup = Option49.String("--resource-group");
26574
+ clientId = Option49.String("--client-id");
26575
+ subscription = Option49.String("--subscription");
26576
+ installerTag = Option49.String("--installer-tag");
26229
26577
  // Full image ref override (registry + repo + tag) — an escape hatch when the
26230
26578
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
26231
26579
  // Wins over --installer-tag / the pinned default.
26232
- installerImage = Option47.String("--installer-image");
26233
- gatewayImageRef = Option47.String("--gateway-image-ref");
26234
- githubAppCreds = Option47.String("--github-app-creds");
26235
- contactEmail = Option47.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
26236
- company = Option47.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
26580
+ installerImage = Option49.String("--installer-image");
26581
+ gatewayImageRef = Option49.String("--gateway-image-ref");
26582
+ githubAppCreds = Option49.String("--github-app-creds");
26583
+ contactEmail = Option49.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
26584
+ company = Option49.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
26237
26585
  // Value-carrying on purpose: a bare --force would be cargo-culted into
26238
26586
  // runbooks and harness prompts and erode the protection, whereas a faithful
26239
26587
  // paste can never accidentally carry the victim group's name. It AUTHORIZES
26240
26588
  // the target; --resource-group is what CHOOSES it.
26241
- reinstallInto = Option47.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
26589
+ reinstallInto = Option49.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
26242
26590
  async executeCommand() {
26243
26591
  const location = typeof this.location === "string" ? this.location : void 0;
26244
26592
  if (!location) {
@@ -26370,7 +26718,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
26370
26718
  };
26371
26719
 
26372
26720
  // src/commands/bootstrap/status.ts
26373
- import { Command as Command51, Option as Option48 } from "clipanion";
26721
+ import { Command as Command53, Option as Option50 } from "clipanion";
26374
26722
  init_errors();
26375
26723
 
26376
26724
  // src/lib/bootstrap-aci-state.ts
@@ -26398,13 +26746,13 @@ async function getAciState(opts) {
26398
26746
  // src/commands/bootstrap/status.ts
26399
26747
  var BootstrapStatusCommand = class extends M8tCommand {
26400
26748
  static paths = [["bootstrap", "status"]];
26401
- static usage = Command51.Usage({
26749
+ static usage = Command53.Usage({
26402
26750
  description: "Show the cloud installer's live status (phase, progress, result).",
26403
26751
  details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.",
26404
26752
  examples: [["One read", "$0 bootstrap status"], ["Watch to completion", "$0 bootstrap status --watch"]]
26405
26753
  });
26406
- watch = Option48.Boolean("--watch", false);
26407
- output = Option48.String("--output");
26754
+ watch = Option50.Boolean("--watch", false);
26755
+ output = Option50.String("--output");
26408
26756
  async executeCommand() {
26409
26757
  const state = await readBootstrapState();
26410
26758
  if (!state) {
@@ -26475,7 +26823,7 @@ function formatStatus(d) {
26475
26823
  }
26476
26824
 
26477
26825
  // src/commands/bootstrap/reap.ts
26478
- import { Command as Command52, Option as Option49 } from "clipanion";
26826
+ import { Command as Command54, Option as Option51 } from "clipanion";
26479
26827
  init_errors();
26480
26828
 
26481
26829
  // src/lib/bootstrap-reap.ts
@@ -26569,14 +26917,14 @@ async function reapInstaller(opts) {
26569
26917
  // src/commands/bootstrap/reap.ts
26570
26918
  var BootstrapReapCommand = class extends M8tCommand {
26571
26919
  static paths = [["bootstrap", "reap"]];
26572
- static usage = Command52.Usage({
26920
+ static usage = Command54.Usage({
26573
26921
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
26574
26922
  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.",
26575
26923
  examples: [["Reap after done", "$0 bootstrap reap"]]
26576
26924
  });
26577
- force = Option49.Boolean("--force", false);
26578
- sweepOrphans = Option49.Boolean("--sweep-orphans", false);
26579
- yes = Option49.Boolean("--yes", false);
26925
+ force = Option51.Boolean("--force", false);
26926
+ sweepOrphans = Option51.Boolean("--sweep-orphans", false);
26927
+ yes = Option51.Boolean("--yes", false);
26580
26928
  async executeCommand() {
26581
26929
  if (this.sweepOrphans === true) {
26582
26930
  const { subscriptionId: sub } = await getAzAccount();
@@ -26673,7 +27021,7 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
26673
27021
  import * as fs31 from "fs/promises";
26674
27022
  import * as os16 from "os";
26675
27023
  import * as path34 from "path";
26676
- import { Command as Command53, Option as Option50 } from "clipanion";
27024
+ import { Command as Command55, Option as Option52 } from "clipanion";
26677
27025
  init_errors();
26678
27026
 
26679
27027
  // src/lib/company-profile-seed.ts
@@ -26928,8 +27276,7 @@ function orderNewest(values) {
26928
27276
  return values.sort((a, b) => b.createdAt - a.createdAt || a.ordinal - b.ordinal || b.id.localeCompare(a.id));
26929
27277
  }
26930
27278
  async function findOnboardingProfile(args) {
26931
- const userId = decodeFoundryUser(args.token);
26932
- if (!userId) return { ...EMPTY_PROFILE_RESULT };
27279
+ if (!decodeFoundryUser(args.token)) return { ...EMPTY_PROFILE_RESULT };
26933
27280
  const doFetch = args.fetchImpl ?? fetch;
26934
27281
  const H = { Authorization: `Bearer ${args.token}` };
26935
27282
  const base = args.endpoint.replace(/\/+$/, "");
@@ -26942,7 +27289,7 @@ async function findOnboardingProfile(args) {
26942
27289
  const conversations = orderNewest(listed.flatMap((value, ordinal) => {
26943
27290
  if (!isRecord(value) || typeof value.id !== "string" || value.id.length === 0 || !isRecord(value.metadata)) return [];
26944
27291
  const metadata = value.metadata;
26945
- if (metadata.app !== "m8t-webapp" || metadata.agent !== "stacey-intake" || metadata.userId !== userId) return [];
27292
+ if (metadata.app !== "m8t-webapp" || metadata.agent !== "stacey-intake") return [];
26946
27293
  return [{
26947
27294
  id: value.id,
26948
27295
  createdAt: timestamp(value.created_at) !== Number.NEGATIVE_INFINITY ? timestamp(value.created_at) : timestamp(metadata.createdAt),
@@ -27331,14 +27678,14 @@ function renderInstallSummary(args) {
27331
27678
  // src/commands/bootstrap/finish.ts
27332
27679
  var BootstrapFinishCommand = class extends M8tCommand {
27333
27680
  static paths = [["bootstrap", "finish"]];
27334
- static usage = Command53.Usage({
27681
+ static usage = Command55.Usage({
27335
27682
  description: "Point your local tools at the now-live platform (repo-root marker, discovery cache, next steps).",
27336
27683
  details: "Final step of `m8t bootstrap` (after the install reaches done). Writes ~/.m8t/repo-root, points your local tools at the live gateway, and \u2014 if you completed the onboarding questionnaire \u2014 seeds your brain-backed advisor's brain with your company profile (best-effort; never blocks finish). Run `m8t bootstrap seed-profile` to seed manually later.",
27337
27684
  examples: [["Finish local", "$0 bootstrap finish --repo-root /path/to/m8t"]]
27338
27685
  });
27339
- repoRoot = Option50.String("--repo-root");
27340
- subscription = Option50.String("--subscription");
27341
- resourceGroup = Option50.String("--resource-group");
27686
+ repoRoot = Option52.String("--repo-root");
27687
+ subscription = Option52.String("--subscription");
27688
+ resourceGroup = Option52.String("--resource-group");
27342
27689
  async executeCommand() {
27343
27690
  const state = await readBootstrapState();
27344
27691
  if (!state) {
@@ -27441,8 +27788,8 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
27441
27788
  import * as fs33 from "fs";
27442
27789
  import * as os18 from "os";
27443
27790
  import * as path36 from "path";
27444
- import { Command as Command54, Option as Option51 } from "clipanion";
27445
- import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
27791
+ import { Command as Command56, Option as Option53 } from "clipanion";
27792
+ import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
27446
27793
  init_errors();
27447
27794
 
27448
27795
  // src/lib/bootstrap-ui.ts
@@ -28200,7 +28547,7 @@ function renderDeployFailure(error) {
28200
28547
  }
28201
28548
  var BootstrapUiCommand = class extends M8tCommand {
28202
28549
  static paths = [["bootstrap", "ui"]];
28203
- static usage = Command54.Usage({
28550
+ static usage = Command56.Usage({
28204
28551
  description: "Deploy Simple Stacey + start the local onboarding chat UI in the background (returns immediately).",
28205
28552
  details: [
28206
28553
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
@@ -28220,16 +28567,16 @@ var BootstrapUiCommand = class extends M8tCommand {
28220
28567
  ["Experimental: enable the intake voice call", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
28221
28568
  ]
28222
28569
  });
28223
- repoRoot = Option51.String("--repo-root");
28224
- port = Option51.String("--port", "3000");
28225
- endpoint = Option51.String("--endpoint", {
28570
+ repoRoot = Option53.String("--repo-root");
28571
+ port = Option53.String("--port", "3000");
28572
+ endpoint = Option53.String("--endpoint", {
28226
28573
  description: "Foundry project endpoint to target \u2014 disambiguates when the subscription has multiple projects."
28227
28574
  });
28228
- prepOnly = Option51.Boolean("--prep-only", false);
28229
- skipInstall = Option51.Boolean("--skip-install", false);
28230
- stop = Option51.Boolean("--stop", false);
28231
- foreground = Option51.Boolean("--foreground", false);
28232
- voice = Option51.Boolean("--voice", false, {
28575
+ prepOnly = Option53.Boolean("--prep-only", false);
28576
+ skipInstall = Option53.Boolean("--skip-install", false);
28577
+ stop = Option53.Boolean("--stop", false);
28578
+ foreground = Option53.Boolean("--foreground", false);
28579
+ voice = Option53.Boolean("--voice", false, {
28233
28580
  description: "Experimental/preview: enable the intake voice call and start the voice relay on :8790. Not a supported founder path \u2014 the intake is text-only by default."
28234
28581
  });
28235
28582
  async executeCommand() {
@@ -28267,7 +28614,7 @@ var BootstrapUiCommand = class extends M8tCommand {
28267
28614
  if (fs33.existsSync(path36.join(os18.homedir(), ".m8t", "config.yaml"))) {
28268
28615
  out(colors.error("\u26A0 an existing ~/.m8t/config.yaml will override the onboarding app registration in apps/web \u2014 if Microsoft sign-in fails, move it aside and retry."));
28269
28616
  }
28270
- const credential2 = new DefaultAzureCredential21();
28617
+ const credential2 = new DefaultAzureCredential23();
28271
28618
  const account = await getAzAccount();
28272
28619
  assertNodeVersion();
28273
28620
  out("waiting for Foundry (the installer's foundry-create phase)\u2026");
@@ -28383,10 +28730,10 @@ var BootstrapUiCommand = class extends M8tCommand {
28383
28730
  };
28384
28731
 
28385
28732
  // src/commands/bootstrap/seed-profile.ts
28386
- import { Command as Command55, Option as Option52 } from "clipanion";
28733
+ import { Command as Command57, Option as Option54 } from "clipanion";
28387
28734
  var BootstrapSeedProfileCommand = class extends M8tCommand {
28388
28735
  static paths = [["bootstrap", "seed-profile"]];
28389
- static usage = Command55.Usage({
28736
+ static usage = Command57.Usage({
28390
28737
  description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
28391
28738
  details: "Reads the latest stacey-intake conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/azzy-brain via the GitHub App. Idempotent. --watch polls until the founder completes the questionnaire.",
28392
28739
  examples: [
@@ -28394,11 +28741,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
28394
28741
  ["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
28395
28742
  ]
28396
28743
  });
28397
- endpoint = Option52.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
28398
- brain = Option52.String("--brain", { description: "Override the brain repo (default <org>/stacey-brain)." });
28399
- watch = Option52.Boolean("--watch", false, { description: "Poll until the questionnaire completes (or --timeout)." });
28400
- timeout = Option52.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
28401
- githubAppCreds = Option52.String("--github-app-creds");
28744
+ endpoint = Option54.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
28745
+ brain = Option54.String("--brain", { description: "Override the brain repo (default <org>/stacey-brain)." });
28746
+ watch = Option54.Boolean("--watch", false, { description: "Poll until the questionnaire completes (or --timeout)." });
28747
+ timeout = Option54.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
28748
+ githubAppCreds = Option54.String("--github-app-creds");
28402
28749
  async executeCommand() {
28403
28750
  const ctx = await resolveSeedContext({
28404
28751
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -28452,7 +28799,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
28452
28799
  import * as fs34 from "fs";
28453
28800
  import * as os19 from "os";
28454
28801
  import * as path37 from "path";
28455
- import { Command as Command56, Option as Option53 } from "clipanion";
28802
+ import { Command as Command58, Option as Option55 } from "clipanion";
28456
28803
  init_errors();
28457
28804
 
28458
28805
  // src/lib/telemetry-enroll.ts
@@ -28534,7 +28881,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
28534
28881
  }
28535
28882
  var TelemetryEnrollCommand = class extends M8tCommand {
28536
28883
  static paths = [["telemetry", "enroll"]];
28537
- static usage = Command56.Usage({
28884
+ static usage = Command58.Usage({
28538
28885
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
28539
28886
  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.",
28540
28887
  examples: [
@@ -28542,11 +28889,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
28542
28889
  ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
28543
28890
  ]
28544
28891
  });
28545
- company = Option53.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
28546
- contactEmail = Option53.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
28547
- subscription = Option53.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
28548
- resourceGroup = Option53.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
28549
- force = Option53.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
28892
+ company = Option55.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
28893
+ contactEmail = Option55.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
28894
+ subscription = Option55.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
28895
+ resourceGroup = Option55.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
28896
+ force = Option55.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
28550
28897
  async executeCommand() {
28551
28898
  const account = await getAzAccount();
28552
28899
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
@@ -28637,6 +28984,8 @@ cli.register(PlatformStatusCommand);
28637
28984
  cli.register(PlatformUpdateCommand);
28638
28985
  cli.register(PlatformConvergeCommand);
28639
28986
  cli.register(PlatformSeedStampCommand);
28987
+ cli.register(PlatformStampBuildCommand);
28988
+ cli.register(PlatformPolicyCommand);
28640
28989
  cli.register(PlatformEnableCostReportCommand);
28641
28990
  cli.register(PlatformEnableAutoUpdateCommand);
28642
28991
  cli.register(DeployCommand);