@m8t-stack/cli 0.2.28 → 0.2.30

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
@@ -1247,7 +1247,7 @@ var init_enable_hosted_brain = __esm({
1247
1247
  import { Builtins, Cli } from "clipanion";
1248
1248
 
1249
1249
  // src/lib/package-version.ts
1250
- var CLI_VERSION = "0.2.28";
1250
+ var CLI_VERSION = "0.2.30";
1251
1251
 
1252
1252
  // src/lib/render-error.ts
1253
1253
  init_errors();
@@ -1335,7 +1335,7 @@ var BACKEND_RULES = [
1335
1335
  reason: "unknown_channel",
1336
1336
  hint: () => "channel must be one of: telegram, slack, teams."
1337
1337
  },
1338
- // BAD_REQUEST — F4 binding-validation reasons
1338
+ // BAD_REQUEST — binding-validation reasons
1339
1339
  {
1340
1340
  code: "BAD_REQUEST",
1341
1341
  reason: "no_adapter_registered",
@@ -1391,13 +1391,13 @@ var BACKEND_RULES = [
1391
1391
  return `no binding '${bindingId}' found. Run 'm8t bind list' to see all bindings.`;
1392
1392
  }
1393
1393
  },
1394
- // BAD_REQUEST — F6 webhook-registration rejection
1394
+ // BAD_REQUEST — webhook-registration rejection
1395
1395
  {
1396
1396
  code: "BAD_REQUEST",
1397
1397
  reason: "webhook_registration_rejected",
1398
1398
  hint: () => "Telegram rejected the bot token. Double-check the token from BotFather and that it isn't revoked."
1399
1399
  },
1400
- // UPSTREAM_FAILED — F6 webhook-registration transient failure
1400
+ // UPSTREAM_FAILED — webhook-registration transient failure
1401
1401
  {
1402
1402
  code: "UPSTREAM_FAILED",
1403
1403
  reason: "webhook_registration_failed",
@@ -1442,51 +1442,32 @@ var BACKEND_RULES = [
1442
1442
  }
1443
1443
  ];
1444
1444
  var LOCAL_RULES = {
1445
- // Task 12 — foundry-agent-get
1446
1445
  AGENT_CREATE_VERSION_FAILED: "Re-run with `--verbose` for the full response body.",
1447
1446
  AGENT_CREATE_VERSION_NO_VERSION: "Foundry returned 2xx but no version \u2014 likely an API regression. Re-try; if it persists, file an issue.",
1448
1447
  AGENT_GET_FAILED: "Re-run with `--verbose` for the full response body.",
1449
1448
  AGENT_NOT_FOUND: "Use `m8t brain create <name>` to create the worker first, OR run `m8t deploy <name>` to register an existing agent.",
1450
- // Task 14 — brain-unlink
1451
1449
  AGENT_REDEPLOY_FAILED: "Re-run with `--verbose` for the full response body.",
1452
1450
  AGENT_REDEPLOY_NO_VERSION: "Foundry returned 2xx but no version. Re-try; if it persists, file an issue.",
1453
- // Task 10/13/14
1454
1451
  AGENT_YAML_NOT_FOUND: "Deploy the worker via `m8t deploy <name>` or the architect first.",
1455
- // Task 16 — brain link command
1456
1452
  APP_HEALTH_FAILED: "Run `m8t brain check-app` for details, then re-run this command.",
1457
- // Task 14 — brain-unlink
1458
1453
  APP_UNINSTALL_FAILED: "GitHub App-uninstall DELETE failed. Re-run with `--keep-app-install` if you want to skip this step.",
1459
- // Task 13 — brain-link
1460
1454
  ARM_AUTH: "Run `az login` and retry.",
1461
- // F2 smoke fix — m8t brain create: stageAsGitRepo step
1455
+ // m8t brain create: stageAsGitRepo step
1462
1456
  BRAIN_GIT_INIT_FAILED: "If git is not installed, install it. Otherwise re-run with --verbose.",
1463
- // Task 14 — brain-unlink
1464
1457
  BRAIN_NOT_LINKED: "The worker has no brain link. Use `m8t brain link` or `m8t brain create` first.",
1465
- // Task 13 — brain-link
1466
1458
  BRAIN_YAML_MIRROR_FAILED: "GitHub Contents API PUT failed. Verify the App has `contents: write` permission on the repo.",
1467
- // Task 13 — brain-link
1468
1459
  CONN_CREATE_FAILED: "Foundry connection PUT failed. Verify Cognitive Services Contributor role on the project.",
1469
- // Task 14 — brain-unlink
1470
1460
  CONN_DELETE_FAILED: "Foundry connection DELETE failed. Verify your ARM subscription has Contributor on the project.",
1471
- // Task 13 — brain-link
1472
1461
  CONN_GET_FAILED: "Foundry connection GET failed. Verify your ARM subscription has access to the project.",
1473
- // Task 13 — brain-link.ts (rename pending: should be FOUNDRY_AUTH_FAILED)
1462
+ // rename pending: should be FOUNDRY_AUTH_FAILED
1474
1463
  FOUNDRY_AUTH: "Run `az login` and retry.",
1475
- // Task 12 — foundry-agent-get
1476
1464
  FOUNDRY_AUTH_FAILED: "Run `az login` and retry.",
1477
- // Task 15+16
1478
1465
  KV_URI_NOT_FOUND: "Pass `--kv-uri` or set `AZURE_KEYVAULT_URI` / `KEYVAULT_URI`.",
1479
- // Task 13 — brain-link
1480
1466
  LINK_NO_INSTALLATION_ID: "Internal error \u2014 the CLI command must poll for the installation id before calling linkBrain. Report at https://github.com/m8t/m8t/issues.",
1481
- // Task 16 — brain link command
1482
1467
  M8T_REPO_ROOT_MISSING: "Re-run `m8t install` from a fresh m8t checkout \u2014 the post-install hook writes ~/.m8t/repo-root.",
1483
- // Task 14 — persona-render
1484
1468
  PERSONA_PATH_NOT_ABSOLUTE: "The persona path in ~/.m8t/foundry/<worker>.yaml must be absolute. Re-deploy the worker via the architect.",
1485
- // Task 9 — persona-render
1486
1469
  PERSONA_PLACEHOLDER_UNSATISFIED: "Re-deploy the worker via the architect to repopulate fillableFieldValues, OR edit ~/.m8t/foundry/<worker>.yaml manually.",
1487
- // Task 9 — persona-render
1488
1470
  PERSONA_READ_FAILED: "Verify the persona path in ~/.m8t/foundry/<worker>.yaml; the architect should have populated it at deploy time.",
1489
- // ── pre-existing entries ──────────────────────────────────────────────────
1490
1471
  AUTH_NOT_SIGNED_IN: "you are not signed in to Azure \u2014 run 'az login' first.",
1491
1472
  AUTH_EXPIRED: "your Azure login expired \u2014 run 'az login' to re-authenticate.",
1492
1473
  AUTH_WRONG_TENANT: "your active Azure session may be in a different tenant \u2014 run 'az login --tenant <id>'.",
@@ -11524,7 +11505,7 @@ function redactAzArgs(args) {
11524
11505
  }
11525
11506
  function runAz(args) {
11526
11507
  return new Promise((resolve3, reject) => {
11527
- const proc = spawn("az", args, { stdio: ["ignore", "pipe", "pipe"] });
11508
+ const proc = spawn("az", args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32" });
11528
11509
  const out = [];
11529
11510
  const err = [];
11530
11511
  proc.stdout.on("data", (d) => out.push(d));
@@ -13391,9 +13372,9 @@ async function probeAppInstallation(args) {
13391
13372
  ${body.slice(0, 300)}`);
13392
13373
  }
13393
13374
  async function tryOpenUrl(url, exec = defaultGhExec) {
13394
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
13375
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
13395
13376
  try {
13396
- await exec(cmd, [url]);
13377
+ await exec(cmd, args);
13397
13378
  } catch {
13398
13379
  }
13399
13380
  }
@@ -13825,7 +13806,7 @@ function renderPersonaBody(personaPath, fillableFieldValues, instanceName) {
13825
13806
  message: `Could not read persona file '${personaPath}': ${e.message}`
13826
13807
  });
13827
13808
  }
13828
- const fmMatch = /^---\n[\s\S]*?\n---\n+/.exec(raw);
13809
+ const fmMatch = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n)+/.exec(raw);
13829
13810
  const body = fmMatch ? raw.slice(fmMatch[0].length) : raw;
13830
13811
  let rendered = body;
13831
13812
  for (const [name, value] of Object.entries(fillableFieldValues)) {
@@ -14280,8 +14261,8 @@ function isM8tBrainMarker(yamlText) {
14280
14261
  }
14281
14262
  if (!parsed || typeof parsed !== "object") return false;
14282
14263
  const root = parsed;
14283
- const isObj3 = (v) => typeof v === "object" && v !== null;
14284
- return isObj3(root.engine) || isObj3(root.processes) || isObj3(root.link);
14264
+ const isObj4 = (v) => typeof v === "object" && v !== null;
14265
+ return isObj4(root.engine) || isObj4(root.processes) || isObj4(root.link);
14285
14266
  }
14286
14267
  async function createBlankRepo(args) {
14287
14268
  const res = await fetch(`https://api.github.com/orgs/${args.org}/repos`, {
@@ -15547,7 +15528,7 @@ async function deployPromptAdvisor(args) {
15547
15528
  cause: e
15548
15529
  });
15549
15530
  }
15550
- const fmMatch = /^---\n([\s\S]*?)\n---/.exec(raw);
15531
+ const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
15551
15532
  const fm = fmMatch ? parseYaml6(fmMatch[1]) : {};
15552
15533
  const model = args.model ?? fm.targets?.foundry?.model;
15553
15534
  if (!model) {
@@ -15823,7 +15804,7 @@ function readPersonaA2aCard(personaPath) {
15823
15804
  } catch (e) {
15824
15805
  throw new LocalCliError({ code: "PERSONA_READ_FAILED", message: `Could not read persona '${personaPath}': ${e.message}` });
15825
15806
  }
15826
- const fm = /^---\n([\s\S]*?)\n---/.exec(raw);
15807
+ const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
15827
15808
  if (!fm) throw new LocalCliError({ code: "PERSONA_NO_FRONTMATTER", message: `Persona '${personaPath}' has no frontmatter.` });
15828
15809
  const front = parseYaml7(fm[1]);
15829
15810
  const role = typeof front.role === "string" ? front.role : "";
@@ -16899,7 +16880,7 @@ function resolvePersona(persona, cwd = process.cwd()) {
16899
16880
  } catch {
16900
16881
  return { persona, personaVersion: null };
16901
16882
  }
16902
- const match = /^---\n([\s\S]*?)\n---/.exec(raw);
16883
+ const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
16903
16884
  if (!match) return { persona, personaVersion: null };
16904
16885
  let fm = null;
16905
16886
  try {
@@ -18727,7 +18708,7 @@ function declaredFillableFields(personaPath) {
18727
18708
  } catch {
18728
18709
  return [];
18729
18710
  }
18730
- const fmMatch = /^---\n([\s\S]*?)\n---/.exec(raw);
18711
+ const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
18731
18712
  if (!fmMatch) return [];
18732
18713
  try {
18733
18714
  const fm = parseYaml10(fmMatch[1]);
@@ -20202,8 +20183,181 @@ function targetManifestSource(ctx, target) {
20202
20183
  return manifestSourceForVersionTag(ctx.channelUrl, target);
20203
20184
  }
20204
20185
 
20205
- // src/commands/platform/enable-cost-report.ts
20186
+ // src/commands/platform/seed-stamp.ts
20187
+ import { readFileSync as readFileSync17 } from "fs";
20206
20188
  import { Command as Command34, Option as Option31 } from "clipanion";
20189
+ import { DefaultAzureCredential as DefaultAzureCredential18, ManagedIdentityCredential as ManagedIdentityCredential2 } from "@azure/identity";
20190
+ init_errors();
20191
+
20192
+ // src/lib/platform-stamp-seed.ts
20193
+ var IMAGE_COMPONENTS = ["gateway", "codingAgent", "azureExecutor"];
20194
+ var BARE_SEMVER = /^\d+\.\d+\.\d+$/;
20195
+ function isObj3(v) {
20196
+ return typeof v === "object" && v !== null && !Array.isArray(v);
20197
+ }
20198
+ function validateDescriptor(raw) {
20199
+ const errors = [];
20200
+ if (!isObj3(raw)) return ["descriptor is not an object"];
20201
+ if (raw.schemaVersion !== 1) errors.push("schemaVersion must be the integer 1");
20202
+ if (typeof raw.platformVersion !== "string" || !BARE_SEMVER.test(raw.platformVersion)) {
20203
+ errors.push(`platformVersion must be a bare X.Y.Z release version, got ${JSON.stringify(raw.platformVersion)}`);
20204
+ }
20205
+ if (!isObj3(raw.generatedFrom)) {
20206
+ errors.push("generatedFrom is required");
20207
+ } else {
20208
+ if (typeof raw.generatedFrom.commit !== "string") {
20209
+ errors.push("generatedFrom.commit is required");
20210
+ }
20211
+ if (typeof raw.generatedFrom.generatedAt !== "string") {
20212
+ errors.push("generatedFrom.generatedAt is required");
20213
+ }
20214
+ }
20215
+ const images = raw.images;
20216
+ if (!isObj3(images)) {
20217
+ errors.push("images is required");
20218
+ } else {
20219
+ for (const key2 of IMAGE_COMPONENTS) {
20220
+ const img = images[key2];
20221
+ if (!isObj3(img)) {
20222
+ errors.push(`images.${key2} is required`);
20223
+ continue;
20224
+ }
20225
+ if (typeof img.ref !== "string" || img.ref.length === 0) errors.push(`images.${key2}.ref is required`);
20226
+ if (typeof img.tag !== "string" || img.tag.length === 0) errors.push(`images.${key2}.tag is required`);
20227
+ if (typeof img.digest !== "string") errors.push(`images.${key2}.digest must be a string (may be empty)`);
20228
+ }
20229
+ }
20230
+ const content = raw.content;
20231
+ if (content !== void 0) {
20232
+ if (!isObj3(content)) {
20233
+ errors.push("content must be an object when present");
20234
+ } else {
20235
+ if (!isObj3(content.personas)) errors.push("content.personas must be an object");
20236
+ if (typeof content.infra !== "string") errors.push("content.infra must be a string");
20237
+ if (typeof content.brainSeedsRev !== "string") errors.push("content.brainSeedsRev must be a string");
20238
+ }
20239
+ }
20240
+ return errors;
20241
+ }
20242
+ function buildSeedStamp(d, cliVersion, nowIso) {
20243
+ const img = (k) => ({
20244
+ tag: d.images[k].tag,
20245
+ digest: d.images[k].digest,
20246
+ state: "managed"
20247
+ });
20248
+ const personas = {};
20249
+ for (const [name, treeSha] of Object.entries(d.content?.personas ?? {})) {
20250
+ personas[name] = { treeSha, foundryVersion: "" };
20251
+ }
20252
+ const stamp = {
20253
+ schemaVersion: 1,
20254
+ platformVersion: platformTag(d.platformVersion),
20255
+ previousPlatformVersion: null,
20256
+ updatedAt: nowIso,
20257
+ lastResult: "success",
20258
+ cli: cliVersion,
20259
+ origin: "install-seed",
20260
+ components: {
20261
+ gateway: img("gateway"),
20262
+ codingAgent: img("codingAgent"),
20263
+ azureExecutor: img("azureExecutor"),
20264
+ personas,
20265
+ infra: { treeSha: d.content?.infra ?? "" }
20266
+ }
20267
+ };
20268
+ if (d.content?.brainSeedsRev) stamp.components.brainSeeds = { seedRev: d.content.brainSeedsRev };
20269
+ return stamp;
20270
+ }
20271
+
20272
+ // src/commands/platform/seed-stamp.ts
20273
+ async function seedStamp2(args) {
20274
+ const readFileImpl = args.readFileImpl ?? ((p) => readFileSync17(p, "utf8"));
20275
+ const readStampImpl = args.readStampImpl ?? readStamp;
20276
+ const writeStampImpl = args.writeStampImpl ?? ((stamp2, o) => writeStamp({ ...o, stamp: stamp2 }));
20277
+ const discoverImpl = args.discoverImpl ?? discoverStampStorage;
20278
+ const ctx = { credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup };
20279
+ let raw;
20280
+ try {
20281
+ raw = JSON.parse(readFileImpl(args.descriptorPath));
20282
+ } catch (e) {
20283
+ throw new LocalCliError({
20284
+ code: "PLATFORM_SEED_DESCRIPTOR_UNREADABLE",
20285
+ message: `Cannot read the install descriptor at ${args.descriptorPath}.`,
20286
+ 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.",
20287
+ cause: e
20288
+ });
20289
+ }
20290
+ const errors = validateDescriptor(raw);
20291
+ if (errors.length > 0) {
20292
+ throw new LocalCliError({
20293
+ code: "PLATFORM_SEED_DESCRIPTOR_INVALID",
20294
+ message: `The install descriptor at ${args.descriptorPath} is not valid: ${errors.join("; ")}.`,
20295
+ hint: "Regenerate it with scripts/install-descriptor-gen.mjs."
20296
+ });
20297
+ }
20298
+ const descriptor = raw;
20299
+ const { accountName } = await discoverImpl(ctx);
20300
+ const existing = await readStampImpl(ctx);
20301
+ if (existing) {
20302
+ args.onProgress?.(`platform stamp already present (${existing.platformVersion}) \u2014 leaving it untouched.`);
20303
+ return { outcome: "already-stamped", platformVersion: existing.platformVersion, storageAccount: accountName };
20304
+ }
20305
+ const stamp = buildSeedStamp(descriptor, CLI_VERSION, args.nowIso ?? (/* @__PURE__ */ new Date()).toISOString());
20306
+ await writeStampImpl(stamp, { ...ctx, onProgress: args.onProgress });
20307
+ return {
20308
+ outcome: descriptor.content ? "seeded" : "seeded-partial",
20309
+ platformVersion: stamp.platformVersion,
20310
+ storageAccount: accountName,
20311
+ components: {
20312
+ gateway: stamp.components.gateway.tag,
20313
+ codingAgent: stamp.components.codingAgent.tag,
20314
+ azureExecutor: stamp.components.azureExecutor.tag
20315
+ }
20316
+ };
20317
+ }
20318
+ var PlatformSeedStampCommand = class extends M8tCommand {
20319
+ static paths = [["platform", "seed-stamp"]];
20320
+ static usage = Command34.Usage({
20321
+ description: "INTERNAL: write the initial installed-state stamp from a build-time install descriptor.",
20322
+ 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'."
20323
+ });
20324
+ descriptor = Option31.String("--descriptor", { description: "Path to install-descriptor.json (baked into the installer image)." });
20325
+ subscription = Option31.String("--subscription");
20326
+ resourceGroup = Option31.String("--resource-group");
20327
+ miClientId = Option31.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
20328
+ output = Option31.String("--output");
20329
+ async executeCommand() {
20330
+ const mode = resolveOutputMode(this.output, this.context.stdout);
20331
+ const need = (v, flag) => {
20332
+ if (typeof v !== "string" || v.trim().length === 0) {
20333
+ throw new LocalCliError({ code: "PLATFORM_SEED_ARG_MISSING", message: `${flag} is required.` });
20334
+ }
20335
+ return v.trim();
20336
+ };
20337
+ const onProgress = (m) => {
20338
+ this.context.stderr.write(`${m}
20339
+ `);
20340
+ };
20341
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential2({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential18();
20342
+ const result = await seedStamp2({
20343
+ descriptorPath: need(this.descriptor, "--descriptor"),
20344
+ credential: credential2,
20345
+ subscriptionId: need(this.subscription, "--subscription"),
20346
+ resourceGroup: need(this.resourceGroup, "--resource-group"),
20347
+ onProgress
20348
+ });
20349
+ if (mode === "json") {
20350
+ this.context.stdout.write(renderJson(result) + "\n");
20351
+ } else {
20352
+ this.context.stdout.write(`${result.outcome}: ${result.platformVersion} (storage ${result.storageAccount})
20353
+ `);
20354
+ }
20355
+ return 0;
20356
+ }
20357
+ };
20358
+
20359
+ // src/commands/platform/enable-cost-report.ts
20360
+ import { Command as Command35, Option as Option32 } from "clipanion";
20207
20361
 
20208
20362
  // src/lib/wire-gateway-acs.ts
20209
20363
  init_rbac();
@@ -20243,18 +20397,18 @@ async function wireGatewayForAcs(args) {
20243
20397
  init_errors();
20244
20398
  var PlatformEnableCostReportCommand = class extends M8tCommand {
20245
20399
  static paths = [["platform", "enable-cost-report"]];
20246
- static usage = Command34.Usage({
20400
+ static usage = Command35.Usage({
20247
20401
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
20248
20402
  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')."
20249
20403
  });
20250
- subscription = Option31.String("--subscription");
20251
- resourceGroup = Option31.String("--resource-group", {
20404
+ subscription = Option32.String("--subscription");
20405
+ resourceGroup = Option32.String("--resource-group", {
20252
20406
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
20253
20407
  });
20254
- acsEndpoint = Option31.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
20255
- acsSender = Option31.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
20256
- acsResourceId = Option31.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
20257
- output = Option31.String("--output");
20408
+ acsEndpoint = Option32.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
20409
+ acsSender = Option32.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
20410
+ acsResourceId = Option32.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
20411
+ output = Option32.String("--output");
20258
20412
  async executeCommand() {
20259
20413
  const mode = resolveOutputMode(
20260
20414
  this.output,
@@ -20328,39 +20482,39 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
20328
20482
  };
20329
20483
 
20330
20484
  // src/commands/platform/enable-auto-update.ts
20331
- import { Command as Command35, Option as Option32 } from "clipanion";
20485
+ import { Command as Command36, Option as Option33 } from "clipanion";
20332
20486
  import { confirm as confirm6 } from "@inquirer/prompts";
20333
- import { DefaultAzureCredential as DefaultAzureCredential18 } from "@azure/identity";
20487
+ import { DefaultAzureCredential as DefaultAzureCredential19 } from "@azure/identity";
20334
20488
  init_errors();
20335
20489
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
20336
20490
  static paths = [["platform", "enable-auto-update"]];
20337
- static usage = Command35.Usage({
20491
+ static usage = Command36.Usage({
20338
20492
  category: "Platform",
20339
20493
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
20340
20494
  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."
20341
20495
  });
20342
- subscription = Option32.String("--subscription");
20343
- resourceGroup = Option32.String("--resource-group", {
20496
+ subscription = Option33.String("--subscription");
20497
+ resourceGroup = Option33.String("--resource-group", {
20344
20498
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
20345
20499
  });
20346
- suffix = Option32.String("--suffix", {
20500
+ suffix = Option33.String("--suffix", {
20347
20501
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
20348
20502
  });
20349
- installerImage = Option32.String("--installer-image", {
20503
+ installerImage = Option33.String("--installer-image", {
20350
20504
  required: true,
20351
20505
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
20352
20506
  });
20353
- updateCron = Option32.String("--update-cron", {
20507
+ updateCron = Option33.String("--update-cron", {
20354
20508
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
20355
20509
  });
20356
- channelUrl = Option32.String("--channel-url", {
20510
+ channelUrl = Option33.String("--channel-url", {
20357
20511
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
20358
20512
  });
20359
- endpoint = Option32.String("--endpoint", {
20513
+ endpoint = Option33.String("--endpoint", {
20360
20514
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
20361
20515
  });
20362
- yes = Option32.Boolean("--yes", false);
20363
- output = Option32.String("--output");
20516
+ yes = Option33.Boolean("--yes", false);
20517
+ output = Option33.String("--output");
20364
20518
  async executeCommand() {
20365
20519
  const mode = resolveOutputMode(
20366
20520
  this.output,
@@ -20379,7 +20533,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
20379
20533
  resourceGroup: this.resourceGroup
20380
20534
  });
20381
20535
  const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
20382
- const credential2 = new DefaultAzureCredential18();
20536
+ const credential2 = new DefaultAzureCredential19();
20383
20537
  const account = await getAzAccount();
20384
20538
  const subscriptionId = this.subscription ?? account.subscriptionId;
20385
20539
  const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
@@ -20481,7 +20635,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
20481
20635
  };
20482
20636
 
20483
20637
  // src/commands/deploy.ts
20484
- import { Command as Command36, Option as Option33 } from "clipanion";
20638
+ import { Command as Command37, Option as Option34 } from "clipanion";
20485
20639
 
20486
20640
  // src/lib/app-reg.ts
20487
20641
  init_errors();
@@ -20883,40 +21037,40 @@ function classifyWhatIf(changes) {
20883
21037
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
20884
21038
  var DeployCommand = class extends M8tCommand {
20885
21039
  static paths = [["deploy"]];
20886
- static usage = Command36.Usage({
21040
+ static usage = Command37.Usage({
20887
21041
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
20888
21042
  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."
20889
21043
  });
20890
- subscription = Option33.String("--subscription");
20891
- resourceGroup = Option33.String("--resource-group", "rg-m8t-stack");
20892
- location = Option33.String("--location", "eastus");
20893
- suffix = Option33.String("--suffix", "");
20894
- imageRef = Option33.String("--image-ref", DEFAULT_IMAGE_REF);
20895
- acrPullIdentity = Option33.String("--acrpull-identity");
20896
- acrResourceId = Option33.String("--acr-resource-id");
20897
- foundryEndpoint = Option33.String("--foundry-endpoint");
20898
- foundryResourceId = Option33.String("--foundry-resource-id");
20899
- foundryTracing = Option33.String("--foundry-tracing");
21044
+ subscription = Option34.String("--subscription");
21045
+ resourceGroup = Option34.String("--resource-group", "rg-m8t-stack");
21046
+ location = Option34.String("--location", "eastus");
21047
+ suffix = Option34.String("--suffix", "");
21048
+ imageRef = Option34.String("--image-ref", DEFAULT_IMAGE_REF);
21049
+ acrPullIdentity = Option34.String("--acrpull-identity");
21050
+ acrResourceId = Option34.String("--acr-resource-id");
21051
+ foundryEndpoint = Option34.String("--foundry-endpoint");
21052
+ foundryResourceId = Option34.String("--foundry-resource-id");
21053
+ foundryTracing = Option34.String("--foundry-tracing");
20900
21054
  // project | account | skip (bicep default: project)
20901
- clientId = Option33.String("--client-id");
20902
- whatIf = Option33.Boolean("--what-if", false);
20903
- output = Option33.String("--output");
20904
- // Referee (E2-F5) — all optional, undefined by default ⇒ the bicep defaults
21055
+ clientId = Option34.String("--client-id");
21056
+ whatIf = Option34.Boolean("--what-if", false);
21057
+ output = Option34.String("--output");
21058
+ // Referee — all optional, undefined by default ⇒ the bicep defaults
20905
21059
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
20906
21060
  // empty). Only pass these when explicitly enabling the referee exam stack.
20907
- gatewayCpu = Option33.String("--gateway-cpu");
20908
- gatewayMemory = Option33.String("--gateway-memory");
20909
- refereeEnabled = Option33.String("--referee-enabled");
20910
- refereeBrainRepos = Option33.String("--referee-brain-repos");
20911
- refereeFeedRepo = Option33.String("--referee-feed-repo");
20912
- refereeInstallationId = Option33.String("--referee-installation-id");
20913
- refereeWebhookHmacKvUri = Option33.String("--referee-webhook-hmac-kv-uri");
20914
- examKvUri = Option33.String("--exam-kv-uri");
20915
- examLaWorkspaceId = Option33.String("--exam-la-workspace-id");
20916
- brainEvalDeployment = Option33.String("--brain-eval-deployment");
20917
- brainAppLogin = Option33.String("--brain-app-login");
20918
- refereeCheckpointDir = Option33.String("--referee-checkpoint-dir");
20919
- examApiBase = Option33.String("--exam-api-base");
21061
+ gatewayCpu = Option34.String("--gateway-cpu");
21062
+ gatewayMemory = Option34.String("--gateway-memory");
21063
+ refereeEnabled = Option34.String("--referee-enabled");
21064
+ refereeBrainRepos = Option34.String("--referee-brain-repos");
21065
+ refereeFeedRepo = Option34.String("--referee-feed-repo");
21066
+ refereeInstallationId = Option34.String("--referee-installation-id");
21067
+ refereeWebhookHmacKvUri = Option34.String("--referee-webhook-hmac-kv-uri");
21068
+ examKvUri = Option34.String("--exam-kv-uri");
21069
+ examLaWorkspaceId = Option34.String("--exam-la-workspace-id");
21070
+ brainEvalDeployment = Option34.String("--brain-eval-deployment");
21071
+ brainAppLogin = Option34.String("--brain-app-login");
21072
+ refereeCheckpointDir = Option34.String("--referee-checkpoint-dir");
21073
+ examApiBase = Option34.String("--exam-api-base");
20920
21074
  async executeCommand() {
20921
21075
  const mode = resolveOutputMode(
20922
21076
  this.output,
@@ -21056,7 +21210,7 @@ var DeployCommand = class extends M8tCommand {
21056
21210
 
21057
21211
  // src/commands/eval/skill.ts
21058
21212
  import { spawnSync as spawnSync4 } from "child_process";
21059
- import { Command as Command37, Option as Option34 } from "clipanion";
21213
+ import { Command as Command38, Option as Option35 } from "clipanion";
21060
21214
  init_errors();
21061
21215
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
21062
21216
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -21081,14 +21235,14 @@ function parseVerdict(stdout) {
21081
21235
  }
21082
21236
  var EvalSkillCommand = class extends M8tCommand {
21083
21237
  static paths = [["eval", "skill"]];
21084
- static usage = Command37.Usage({
21238
+ static usage = Command38.Usage({
21085
21239
  description: "Vet one inbox skill candidate (Brain Faculties Eval F1): promote / reject / needs_review. Shells out to the Python `brain-eval` core (override its path with $BRAIN_EVAL_BIN)."
21086
21240
  });
21087
- candidate = Option34.String();
21088
- skillsDir = Option34.String("--skills-dir");
21089
- noJudge = Option34.Boolean("--no-judge", false);
21090
- deployment = Option34.String("--deployment");
21091
- output = Option34.String("--output");
21241
+ candidate = Option35.String();
21242
+ skillsDir = Option35.String("--skills-dir");
21243
+ noJudge = Option35.Boolean("--no-judge", false);
21244
+ deployment = Option35.String("--deployment");
21245
+ output = Option35.String("--output");
21092
21246
  executeCommand() {
21093
21247
  return Promise.resolve(this._runCommand());
21094
21248
  }
@@ -21145,9 +21299,9 @@ var EvalSkillCommand = class extends M8tCommand {
21145
21299
 
21146
21300
  // src/commands/eval/exam.ts
21147
21301
  import { spawnSync as spawnSync5 } from "child_process";
21148
- import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync17, existsSync as existsSync15, readdirSync as readdirSync2 } from "fs";
21302
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync18, existsSync as existsSync15, readdirSync as readdirSync2 } from "fs";
21149
21303
  import { join as join27 } from "path";
21150
- import { Command as Command38, Option as Option35 } from "clipanion";
21304
+ import { Command as Command39, Option as Option36 } from "clipanion";
21151
21305
  init_errors();
21152
21306
  init_esm();
21153
21307
  function parseArmToken(tok, opts) {
@@ -21350,7 +21504,7 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
21350
21504
  const manifestPath = join27(home, "tasksets", worker, versionDir, "manifest.yaml");
21351
21505
  let text;
21352
21506
  try {
21353
- text = readFileSync17(manifestPath, "utf-8");
21507
+ text = readFileSync18(manifestPath, "utf-8");
21354
21508
  } catch (e) {
21355
21509
  throw new LocalCliError({
21356
21510
  code: "EXAM_REDACTION_UNSAFE",
@@ -21377,24 +21531,24 @@ function buildPlan(args) {
21377
21531
  }
21378
21532
  var EvalExamCommand = class extends M8tCommand {
21379
21533
  static paths = [["eval", "exam"]];
21380
- static usage = Command38.Usage({
21534
+ static usage = Command39.Usage({
21381
21535
  description: "Run a brain exam (Brain Referee E2-F2): 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."
21382
21536
  });
21383
- worker = Option35.String();
21384
- arms = Option35.String("--arms");
21385
- taskSet = Option35.String("--task-set");
21386
- examType = Option35.String("--exam-type");
21387
- skill = Option35.String("--skill");
21388
- reps = Option35.String("-n,--reps");
21389
- probes = Option35.String("--probes");
21390
- pool = Option35.String("--pool");
21391
- out = Option35.String("--out");
21392
- dryRun = Option35.Boolean("--dry-run", false);
21393
- keepArms = Option35.Boolean("--keep-arms", false);
21394
- allowStub = Option35.Boolean("--allow-stub", false);
21395
- deployment = Option35.String("--deployment");
21396
- output = Option35.String("--output");
21397
- observeWaitS = Option35.String("--observe-wait-s");
21537
+ worker = Option36.String();
21538
+ arms = Option36.String("--arms");
21539
+ taskSet = Option36.String("--task-set");
21540
+ examType = Option36.String("--exam-type");
21541
+ skill = Option36.String("--skill");
21542
+ reps = Option36.String("-n,--reps");
21543
+ probes = Option36.String("--probes");
21544
+ pool = Option36.String("--pool");
21545
+ out = Option36.String("--out");
21546
+ dryRun = Option36.Boolean("--dry-run", false);
21547
+ keepArms = Option36.Boolean("--keep-arms", false);
21548
+ allowStub = Option36.Boolean("--allow-stub", false);
21549
+ deployment = Option36.String("--deployment");
21550
+ output = Option36.String("--output");
21551
+ observeWaitS = Option36.String("--observe-wait-s");
21398
21552
  async executeCommand() {
21399
21553
  await Promise.resolve();
21400
21554
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -21509,10 +21663,10 @@ var EvalExamCommand = class extends M8tCommand {
21509
21663
  };
21510
21664
 
21511
21665
  // src/commands/version.ts
21512
- import { Command as Command39, Option as Option36 } from "clipanion";
21666
+ import { Command as Command40, Option as Option37 } from "clipanion";
21513
21667
  var VersionCommand = class extends M8tCommand {
21514
21668
  static paths = [["version"], ["--version"], ["-v"]];
21515
- static usage = Command39.Usage({
21669
+ static usage = Command40.Usage({
21516
21670
  description: "Print the CLI version.",
21517
21671
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
21518
21672
  examples: [
@@ -21520,8 +21674,8 @@ var VersionCommand = class extends M8tCommand {
21520
21674
  ["Print as JSON", "$0 version --output json"]
21521
21675
  ]
21522
21676
  });
21523
- output = Option36.String("--output", { description: "pretty | json | auto (default)" });
21524
- verbose = Option36.Boolean("--verbose", false);
21677
+ output = Option37.String("--output", { description: "pretty | json | auto (default)" });
21678
+ verbose = Option37.Boolean("--verbose", false);
21525
21679
  executeCommand() {
21526
21680
  const mode = resolveOutputMode(
21527
21681
  this.output ?? "auto",
@@ -21552,18 +21706,18 @@ var VersionCommand = class extends M8tCommand {
21552
21706
  };
21553
21707
 
21554
21708
  // src/commands/whoami.ts
21555
- import { Command as Command40, Option as Option37 } from "clipanion";
21709
+ import { Command as Command41, Option as Option38 } from "clipanion";
21556
21710
  var WhoamiCommand = class extends M8tCommand {
21557
21711
  static paths = [["whoami"]];
21558
- static usage = Command40.Usage({
21712
+ static usage = Command41.Usage({
21559
21713
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
21560
21714
  });
21561
- output = Option37.String("--output");
21562
- verbose = Option37.Boolean("--verbose", false);
21563
- subscription = Option37.String("--subscription", {
21715
+ output = Option38.String("--output");
21716
+ verbose = Option38.Boolean("--verbose", false);
21717
+ subscription = Option38.String("--subscription", {
21564
21718
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
21565
21719
  });
21566
- resourceGroup = Option37.String("--resource-group", {
21720
+ resourceGroup = Option38.String("--resource-group", {
21567
21721
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
21568
21722
  });
21569
21723
  async executeCommand() {
@@ -21628,14 +21782,14 @@ var WhoamiCommand = class extends M8tCommand {
21628
21782
  };
21629
21783
 
21630
21784
  // src/commands/status.ts
21631
- import { Command as Command41, Option as Option38 } from "clipanion";
21785
+ import { Command as Command42, Option as Option39 } from "clipanion";
21632
21786
 
21633
21787
  // src/lib/azd.ts
21634
21788
  init_errors();
21635
21789
  import { spawn as spawn4 } from "child_process";
21636
21790
  function runAzd(args) {
21637
21791
  return new Promise((resolve3, reject) => {
21638
- const proc = spawn4("azd", args, { stdio: ["ignore", "pipe", "pipe"] });
21792
+ const proc = spawn4("azd", args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32" });
21639
21793
  const out = [];
21640
21794
  const err = [];
21641
21795
  proc.stdout.on("data", (d) => out.push(d));
@@ -21693,10 +21847,10 @@ async function resolveLocalContext() {
21693
21847
  // src/commands/status.ts
21694
21848
  var StatusCommand = class extends M8tCommand {
21695
21849
  static paths = [["status"]];
21696
- static usage = Command41.Usage({
21850
+ static usage = Command42.Usage({
21697
21851
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
21698
21852
  });
21699
- output = Option38.String("--output");
21853
+ output = Option39.String("--output");
21700
21854
  async executeCommand() {
21701
21855
  const mode = resolveOutputMode(
21702
21856
  this.output,
@@ -21734,8 +21888,8 @@ var StatusCommand = class extends M8tCommand {
21734
21888
  };
21735
21889
 
21736
21890
  // src/commands/doctor.ts
21737
- import { Command as Command42, Option as Option39 } from "clipanion";
21738
- import { DefaultAzureCredential as DefaultAzureCredential19 } from "@azure/identity";
21891
+ import { Command as Command43, Option as Option40 } from "clipanion";
21892
+ import { DefaultAzureCredential as DefaultAzureCredential20 } from "@azure/identity";
21739
21893
  import * as fs26 from "fs";
21740
21894
  import * as os11 from "os";
21741
21895
  import * as path28 from "path";
@@ -22010,12 +22164,12 @@ function probeLegacyStateDir() {
22010
22164
  }
22011
22165
  var DoctorCommand = class extends M8tCommand {
22012
22166
  static paths = [["doctor"]];
22013
- static usage = Command42.Usage({
22167
+ static usage = Command43.Usage({
22014
22168
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
22015
22169
  });
22016
- output = Option39.String("--output");
22017
- agent = Option39.String("--agent");
22018
- resourceGroup = Option39.String("--resource-group", {
22170
+ output = Option40.String("--output");
22171
+ agent = Option40.String("--agent");
22172
+ resourceGroup = Option40.String("--resource-group", {
22019
22173
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
22020
22174
  });
22021
22175
  async executeCommand() {
@@ -22089,7 +22243,7 @@ var DoctorCommand = class extends M8tCommand {
22089
22243
  if (typeof this.agent === "string" && this.agent) {
22090
22244
  checking(`delivery grant for ${this.agent}`);
22091
22245
  try {
22092
- const credential2 = new DefaultAzureCredential19();
22246
+ const credential2 = new DefaultAzureCredential20();
22093
22247
  const cur = await getAgentVersion({
22094
22248
  credential: credential2,
22095
22249
  projectEndpoint: foundry.projectEndpoint,
@@ -22132,7 +22286,7 @@ var DoctorCommand = class extends M8tCommand {
22132
22286
  };
22133
22287
 
22134
22288
  // src/commands/switch.ts
22135
- import { Command as Command43, Option as Option40 } from "clipanion";
22289
+ import { Command as Command44, Option as Option41 } from "clipanion";
22136
22290
 
22137
22291
  // src/lib/profiles.ts
22138
22292
  import * as fs27 from "fs/promises";
@@ -22272,14 +22426,14 @@ async function profileSwitch(name, asName) {
22272
22426
  init_errors();
22273
22427
  var SwitchCommand = class extends M8tCommand {
22274
22428
  static paths = [["switch"]];
22275
- static usage = Command43.Usage({
22429
+ static usage = Command44.Usage({
22276
22430
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
22277
22431
  });
22278
- profile = Option40.String({ required: false });
22279
- subscription = Option40.String("--subscription");
22280
- list = Option40.Boolean("--list", false);
22281
- as = Option40.String("--as");
22282
- output = Option40.String("--output");
22432
+ profile = Option41.String({ required: false });
22433
+ subscription = Option41.String("--subscription");
22434
+ list = Option41.Boolean("--list", false);
22435
+ as = Option41.String("--as");
22436
+ output = Option41.String("--output");
22283
22437
  async executeCommand() {
22284
22438
  const mode = resolveOutputMode(
22285
22439
  this.output,
@@ -22336,7 +22490,7 @@ var SwitchCommand = class extends M8tCommand {
22336
22490
 
22337
22491
  // src/commands/open.ts
22338
22492
  import { spawn as spawn5 } from "child_process";
22339
- import { Command as Command44, Option as Option41 } from "clipanion";
22493
+ import { Command as Command45, Option as Option42 } from "clipanion";
22340
22494
 
22341
22495
  // src/lib/open-targets.ts
22342
22496
  init_errors();
@@ -22382,14 +22536,14 @@ function openUrl(url) {
22382
22536
  }
22383
22537
  var OpenCommand = class extends M8tCommand {
22384
22538
  static paths = [["open"]];
22385
- static usage = Command44.Usage({
22539
+ static usage = Command45.Usage({
22386
22540
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
22387
22541
  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)."
22388
22542
  });
22389
- target = Option41.String({ required: false });
22390
- print = Option41.Boolean("--print", false);
22391
- output = Option41.String("--output");
22392
- resourceGroup = Option41.String("--resource-group", {
22543
+ target = Option42.String({ required: false });
22544
+ print = Option42.Boolean("--print", false);
22545
+ output = Option42.String("--output");
22546
+ resourceGroup = Option42.String("--resource-group", {
22393
22547
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
22394
22548
  });
22395
22549
  async executeCommand() {
@@ -22433,7 +22587,7 @@ var OpenCommand = class extends M8tCommand {
22433
22587
  };
22434
22588
 
22435
22589
  // src/commands/dream/run.ts
22436
- import { Command as Command45, Option as Option42 } from "clipanion";
22590
+ import { Command as Command46, Option as Option43 } from "clipanion";
22437
22591
  import { AzureCliCredential } from "@azure/identity";
22438
22592
  import { TableClient as TableClient5 } from "@azure/data-tables";
22439
22593
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
@@ -22778,7 +22932,7 @@ var TableCursor = class {
22778
22932
  * Point-read the canonical worker's cursor row. Catches BOTH the
22779
22933
  * missing-table (TableNotFound) and missing-row (ResourceNotFound) 404s and
22780
22934
  * returns an absent (from-the-beginning) cursor WITHOUT creating the table or
22781
- * writing anything (DESIGN §3 — "createTable is bound only to advance()").
22935
+ * writing anything.
22782
22936
  * Any other error (e.g. 403 RBAC) propagates raw.
22783
22937
  */
22784
22938
  async read(worker) {
@@ -22804,9 +22958,9 @@ var TableCursor = class {
22804
22958
  }
22805
22959
  /**
22806
22960
  * Persist the cursor for the canonical worker. This is the ONLY place that
22807
- * creates the BrainDreamCursor table (DESIGN §3 — read() never does DDL); a
22808
- * TableAlreadyExists race is benign. The non-dry F3 path calls this; F2 ships
22809
- * it wired + unit-tested. The per-PK watermark map is serialized to JSON
22961
+ * creates the BrainDreamCursor table; a
22962
+ * TableAlreadyExists race is benign. The non-dry path calls this, already
22963
+ * wired and unit-tested. The per-PK watermark map is serialized to JSON
22810
22964
  * (Table cells are scalar — same rationale as agent-ledger's `artifacts`).
22811
22965
  */
22812
22966
  async advance(worker, cursor) {
@@ -22939,6 +23093,35 @@ function makeLedgerSource(read) {
22939
23093
  // ../../packages/brain/engine/dist/esm/conversation-source.js
22940
23094
  var TRANSIENT = 500;
22941
23095
  var MAX_ATTEMPTS = 5;
23096
+ var VOICE_PREFIX = "[[m8t:voice-transcript]] ";
23097
+ var MAX_VOICE_HEADER_BYTES = 4096;
23098
+ function isVoiceEnvelope(value) {
23099
+ if (typeof value !== "object" || value === null)
23100
+ return false;
23101
+ const item = value;
23102
+ return item.v === 1 && typeof item.callId === "string" && item.callId.length > 0 && typeof item.startedAt === "string" && item.startedAt.length > 0 && typeof item.endedAt === "string" && item.endedAt.length > 0 && typeof item.turnAt === "string" && item.turnAt.length > 0;
23103
+ }
23104
+ function stripVoiceEnvelope(value) {
23105
+ if (!value.startsWith(VOICE_PREFIX))
23106
+ return value;
23107
+ const newline = value.indexOf("\n");
23108
+ if (newline < VOICE_PREFIX.length)
23109
+ return value;
23110
+ const header = value.slice(VOICE_PREFIX.length, newline);
23111
+ if (new TextEncoder().encode(header).byteLength > MAX_VOICE_HEADER_BYTES || header.length % 4 === 1 || !/^[A-Za-z0-9_-]+$/.test(header))
23112
+ return value;
23113
+ try {
23114
+ const padded = header.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - header.length % 4) % 4);
23115
+ const bytes = Uint8Array.from(Buffer.from(padded, "base64"));
23116
+ if (Buffer.from(bytes).toString("base64url") !== header)
23117
+ return value;
23118
+ const json = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
23119
+ const parsed = JSON.parse(json);
23120
+ return isVoiceEnvelope(parsed) ? value.slice(newline + 1) : value;
23121
+ } catch {
23122
+ return value;
23123
+ }
23124
+ }
22942
23125
  function statusOf(e) {
22943
23126
  const err = e;
22944
23127
  return err?.statusCode ?? err?.status;
@@ -22954,7 +23137,7 @@ function mapItems(raw) {
22954
23137
  continue;
22955
23138
  if (item.role !== "user" && item.role !== "assistant")
22956
23139
  continue;
22957
- const text = Array.isArray(item.content) ? item.content.map((c) => typeof c.text === "string" ? c.text : "").filter(Boolean).join("\n\n") : "";
23140
+ const text = Array.isArray(item.content) ? stripVoiceEnvelope(item.content.map((c) => typeof c.text === "string" && c.text.length > 0 ? c.text : typeof c.transcript === "string" && c.transcript.length > 0 ? c.transcript : "").filter(Boolean).join("\n\n")) : "";
22958
23141
  const at = typeof item.created_at === "number" ? new Date(item.created_at * 1e3).toISOString() : void 0;
22959
23142
  acc.push(at ? { role: item.role, text, at } : { role: item.role, text });
22960
23143
  }
@@ -22988,7 +23171,7 @@ function makeConversationSource(client, opts = {}) {
22988
23171
  throw new ConvFetchError("terminal", `exhausted retries reading ${conversationId}`);
22989
23172
  },
22990
23173
  /**
22991
- * The a2a recovery gate's data source (DESIGN §2.3, Option A). Reads the
23174
+ * The a2a recovery gate's data source (Option A). Reads the
22992
23175
  * conversation's own `metadata.app` so the recovery path can HARD-gate on the
22993
23176
  * real app tag rather than emit-time ledger tagging. Degrades to null on any
22994
23177
  * error → the caller treats an un-provable a2a conv as ungated (skipped).
@@ -23272,7 +23455,7 @@ async function runPipeline(args) {
23272
23455
  consumed,
23273
23456
  recovered,
23274
23457
  skipped,
23275
- // Wall-clock elapsed around the awaited live I/O (FIX 4) — measured from the
23458
+ // Wall-clock elapsed around the awaited live I/O — measured from the
23276
23459
  // real/injected monotonic clock, NOT the pure `now` (which never moves within a run).
23277
23460
  latencyMs: Math.round(monotonicNow() - t0)
23278
23461
  };
@@ -23393,11 +23576,11 @@ var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
23393
23576
  var isAllowedWritePath = (path37, allowedPrefix) => allowedPrefix.test(path37) && !path37.split("/").includes("..");
23394
23577
  var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
23395
23578
  function parseFrontmatter(content) {
23396
- const m = /^---\n([\s\S]*?)\n---/.exec(content);
23579
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
23397
23580
  if (!m)
23398
23581
  return {};
23399
23582
  const fm = {};
23400
- const lines = m[1].split("\n");
23583
+ const lines = m[1].split(/\r?\n/);
23401
23584
  let pendingKey = null;
23402
23585
  for (const line2 of lines) {
23403
23586
  const item = /^\s+-\s+(.*)$/.exec(line2);
@@ -24075,7 +24258,7 @@ function frontmatter(fields) {
24075
24258
  return lines.join("\n");
24076
24259
  }
24077
24260
  function injectFrontmatter(body, fields) {
24078
- const m = /^---\n([\s\S]*?)\n---\n?/.exec(body);
24261
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(body);
24079
24262
  const inject = Object.entries(fields).map(([k, v]) => Array.isArray(v) ? `${k}:
24080
24263
  ${v.map((i) => ` - ${i}`).join("\n")}` : `${k}: ${v}`).join("\n");
24081
24264
  if (!m)
@@ -24084,11 +24267,11 @@ ${inject}
24084
24267
  ---
24085
24268
  ${body}`;
24086
24269
  const block = m[1].replace(/\s*$/, "");
24087
- return body.replace(m[0], `---
24270
+ return `---
24088
24271
  ${block}
24089
24272
  ${inject}
24090
24273
  ---
24091
- `);
24274
+ ` + body.slice(m[0].length);
24092
24275
  }
24093
24276
  var indexLine = (path37, title, date, tags) => `- \`${path37}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
24094
24277
  function splitIndex(index) {
@@ -24248,7 +24431,7 @@ function defaultDreamSeams(opts = {}) {
24248
24431
  const user = buildUserPrompt(input, mem.files);
24249
24432
  const res = await propose(deps.model, system, user, {
24250
24433
  ...opts.sleep ? { sleep: opts.sleep } : {},
24251
- // The configured model NAME for the cost digest (DESIGN §4.1/§9). Without
24434
+ // The configured model NAME for the cost digest. Without
24252
24435
  // this, propose falls back to its default and every cost line mis-reports
24253
24436
  // the model an operator actually configured (e.g. gpt-5-mini → "gpt-5.4").
24254
24437
  ...deps.modelName ? { model: deps.modelName } : {}
@@ -24631,20 +24814,20 @@ function redactTranscripts(input) {
24631
24814
  }
24632
24815
  var DreamRunCommand = class extends M8tCommand {
24633
24816
  static paths = [["dream", "run"]];
24634
- static usage = Command45.Usage({
24817
+ static usage = Command46.Usage({
24635
24818
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes).",
24636
24819
  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."
24637
24820
  });
24638
- worker = Option42.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
24639
- dryRun = Option42.Boolean("--dry-run", false, { description: "Read-only harvest; no model call, no writes." });
24640
- since = Option42.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
24641
- reset = Option42.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
24642
- showTranscripts = Option42.Boolean("--show-transcripts", false, {
24821
+ worker = Option43.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
24822
+ dryRun = Option43.Boolean("--dry-run", false, { description: "Read-only harvest; no model call, no writes." });
24823
+ since = Option43.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
24824
+ reset = Option43.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
24825
+ showTranscripts = Option43.Boolean("--show-transcripts", false, {
24643
24826
  description: "Print transcript bodies (default: metadata only)."
24644
24827
  });
24645
- subscription = Option42.String("--subscription");
24646
- endpoint = Option42.String("--endpoint");
24647
- output = Option42.String("--output");
24828
+ subscription = Option43.String("--subscription");
24829
+ endpoint = Option43.String("--endpoint");
24830
+ output = Option43.String("--output");
24648
24831
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
24649
24832
  deps;
24650
24833
  async executeCommand() {
@@ -24900,7 +25083,7 @@ async function buildWriteDeps(context, credential2, cursor) {
24900
25083
  return {
24901
25084
  model: makeFoundryModelClient(openai, model),
24902
25085
  modelName: model,
24903
- // surface the resolved name for the cost digest (DESIGN §4.1/§9)
25086
+ // surface the resolved name for the cost digest
24904
25087
  brain,
24905
25088
  cursor,
24906
25089
  clock: () => /* @__PURE__ */ new Date(),
@@ -24992,7 +25175,7 @@ function defaultDeps(overrides) {
24992
25175
  }
24993
25176
 
24994
25177
  // src/commands/foundry/create.ts
24995
- import { Command as Command46, Option as Option43 } from "clipanion";
25178
+ import { Command as Command47, Option as Option44 } from "clipanion";
24996
25179
 
24997
25180
  // src/lib/foundry-create.ts
24998
25181
  init_errors();
@@ -25230,7 +25413,7 @@ async function createFoundryProject(args) {
25230
25413
  init_errors();
25231
25414
  var FoundryCreateCommand = class extends M8tCommand {
25232
25415
  static paths = [["foundry", "create"]];
25233
- static usage = Command46.Usage({
25416
+ static usage = Command47.Usage({
25234
25417
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
25235
25418
  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).",
25236
25419
  examples: [
@@ -25239,16 +25422,16 @@ var FoundryCreateCommand = class extends M8tCommand {
25239
25422
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
25240
25423
  ]
25241
25424
  });
25242
- resourceGroup = Option43.String("--resource-group");
25243
- location = Option43.String("--location");
25244
- account = Option43.String("--account");
25245
- project = Option43.String("--project", "m8t");
25246
- model = Option43.String("--model", "gpt-4.1-mini");
25247
- modelVersion = Option43.String("--model-version", "2025-04-14");
25248
- capacity = Option43.String("--capacity", "50");
25249
- subscription = Option43.String("--subscription");
25250
- skipQuotaCheck = Option43.Boolean("--skip-quota-check", false);
25251
- output = Option43.String("--output");
25425
+ resourceGroup = Option44.String("--resource-group");
25426
+ location = Option44.String("--location");
25427
+ account = Option44.String("--account");
25428
+ project = Option44.String("--project", "m8t");
25429
+ model = Option44.String("--model", "gpt-4.1-mini");
25430
+ modelVersion = Option44.String("--model-version", "2025-04-14");
25431
+ capacity = Option44.String("--capacity", "50");
25432
+ subscription = Option44.String("--subscription");
25433
+ skipQuotaCheck = Option44.Boolean("--skip-quota-check", false);
25434
+ output = Option44.String("--output");
25252
25435
  async executeCommand() {
25253
25436
  const mode = resolveOutputMode(
25254
25437
  this.output,
@@ -25320,22 +25503,22 @@ var FoundryCreateCommand = class extends M8tCommand {
25320
25503
  };
25321
25504
 
25322
25505
  // src/commands/foundry/await-ready.ts
25323
- import { Command as Command47, Option as Option44 } from "clipanion";
25506
+ import { Command as Command48, Option as Option45 } from "clipanion";
25324
25507
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
25325
25508
  init_errors();
25326
25509
  var FoundryAwaitReadyCommand = class extends M8tCommand {
25327
25510
  static paths = [["foundry", "await-ready"]];
25328
- static usage = Command47.Usage({
25511
+ static usage = Command48.Usage({
25329
25512
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
25330
25513
  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.",
25331
25514
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
25332
25515
  });
25333
- endpoint = Option44.String("--endpoint");
25334
- consecutive = Option44.String("--consecutive", "3");
25335
- attempts = Option44.String("--attempts", "60");
25336
- interval = Option44.String("--interval", "5");
25337
- subscription = Option44.String("--subscription");
25338
- output = Option44.String("--output");
25516
+ endpoint = Option45.String("--endpoint");
25517
+ consecutive = Option45.String("--consecutive", "3");
25518
+ attempts = Option45.String("--attempts", "60");
25519
+ interval = Option45.String("--interval", "5");
25520
+ subscription = Option45.String("--subscription");
25521
+ output = Option45.String("--output");
25339
25522
  async executeCommand() {
25340
25523
  const mode = resolveOutputMode(this.output, this.context.stdout);
25341
25524
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -25369,7 +25552,33 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
25369
25552
  };
25370
25553
 
25371
25554
  // src/commands/bootstrap/preflight.ts
25372
- import { Command as Command48, Option as Option45 } from "clipanion";
25555
+ import { Command as Command49, Option as Option46 } from "clipanion";
25556
+
25557
+ // ../../packages/telemetry-contract/artifact/tier-map.ts
25558
+ var EVENT_TIERS = {
25559
+ "enrolled": 1,
25560
+ "install-started": 1,
25561
+ "install-succeeded": 1,
25562
+ "install-failed": 1,
25563
+ "heartbeat": 1,
25564
+ "update-applied": 1,
25565
+ "update-failed": 1,
25566
+ "consent-changed": 1
25567
+ // extended additively for tier-2 usage events: "usage-rollup": 2
25568
+ };
25569
+ var EVENT_ENUM = Object.keys(EVENT_TIERS);
25570
+
25571
+ // ../../packages/telemetry-contract/src/disclosure.ts
25572
+ var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t receives limited operational data linked to it \u2014 installation and update events, installed versions, and service health signals, along with the company name and contact email you provide. This is service data used only to operate and support the platform; it contains none of your content. Details: TELEMETRY.md.";
25573
+
25574
+ // ../../packages/telemetry-contract/src/endpoints.ts
25575
+ var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
25576
+ function ingestUrl(path37) {
25577
+ const base = INGEST_BASE_URL.replace(/\/+$/, "");
25578
+ return `${base}${path37.startsWith("/") ? path37 : `/${path37}`}`;
25579
+ }
25580
+
25581
+ // src/commands/bootstrap/preflight.ts
25373
25582
  init_errors();
25374
25583
 
25375
25584
  // src/lib/bootstrap-preflight.ts
@@ -25443,7 +25652,7 @@ function buildPreflightBanner(who) {
25443
25652
  // src/commands/bootstrap/preflight.ts
25444
25653
  var BootstrapPreflightCommand = class extends M8tCommand {
25445
25654
  static paths = [["bootstrap", "preflight"]];
25446
- static usage = Command48.Usage({
25655
+ static usage = Command49.Usage({
25447
25656
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
25448
25657
  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.",
25449
25658
  examples: [
@@ -25451,14 +25660,17 @@ var BootstrapPreflightCommand = class extends M8tCommand {
25451
25660
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
25452
25661
  ]
25453
25662
  });
25454
- clientId = Option45.String("--client-id");
25455
- subscription = Option45.String("--subscription");
25663
+ clientId = Option46.String("--client-id");
25664
+ subscription = Option46.String("--subscription");
25456
25665
  async executeCommand() {
25457
25666
  const clientId = typeof this.clientId === "string" ? this.clientId : void 0;
25458
25667
  const account = await getAzAccount();
25459
25668
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
25460
25669
  const oid = await getCallerObjectId();
25461
25670
  this.context.stdout.write(buildPreflightBanner({ upn: account.upn, tenantId: account.tenantId, subscriptionId }) + "\n");
25671
+ this.context.stdout.write(`
25672
+ ${colors.dim(DISCLOSURE_TIER1)}
25673
+ `);
25462
25674
  this.context.stdout.write("Checking your access\u2026\n");
25463
25675
  const sub = await checkSubScopeAdmin(oid, subscriptionId);
25464
25676
  this.context.stdout.write(line(sub.ok, `${sub.role ?? "Owner/UAA"} at subscription scope`, "resource + role-assignment rights"));
@@ -25511,7 +25723,7 @@ ${colors.error(" " + why)}
25511
25723
  import * as fs30 from "fs";
25512
25724
  import * as os14 from "os";
25513
25725
  import * as path32 from "path";
25514
- import { Command as Command49, Option as Option46 } from "clipanion";
25726
+ import { Command as Command50, Option as Option47 } from "clipanion";
25515
25727
  init_errors();
25516
25728
 
25517
25729
  // src/lib/bootstrap-mi.ts
@@ -25610,6 +25822,7 @@ function buildAciCreateArgs(s) {
25610
25822
  ];
25611
25823
  if (s.gatewayImageRef) env.push(`GATEWAY_IMAGE_REF=${s.gatewayImageRef}`);
25612
25824
  if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
25825
+ if (s.enrollContactEmail) env.push(`M8T_ENROLL_CONTACT_EMAIL=${s.enrollContactEmail}`);
25613
25826
  if (s.githubApp) {
25614
25827
  const g = s.githubApp;
25615
25828
  env.push(
@@ -25760,12 +25973,12 @@ async function writeBootstrapState(state, home = os13.homedir()) {
25760
25973
  // src/commands/bootstrap/launch.ts
25761
25974
  var DEFAULT_RG = "rg-m8t-stack";
25762
25975
  var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
25763
- var DEFAULT_INSTALLER_TAG = "v0.1.33";
25976
+ var DEFAULT_INSTALLER_TAG = "v0.1.36";
25764
25977
  var ACI_NAME = "m8t-installer";
25765
25978
  var MI_NAME = "m8t-installer-mi";
25766
25979
  var BootstrapLaunchCommand = class extends M8tCommand {
25767
25980
  static paths = [["bootstrap", "launch"]];
25768
- static usage = Command49.Usage({
25981
+ static usage = Command50.Usage({
25769
25982
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
25770
25983
  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`.",
25771
25984
  examples: [
@@ -25775,17 +25988,17 @@ var BootstrapLaunchCommand = class extends M8tCommand {
25775
25988
  ["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.33"]
25776
25989
  ]
25777
25990
  });
25778
- location = Option46.String("--location");
25779
- resourceGroup = Option46.String("--resource-group");
25780
- clientId = Option46.String("--client-id");
25781
- subscription = Option46.String("--subscription");
25782
- installerTag = Option46.String("--installer-tag");
25991
+ location = Option47.String("--location");
25992
+ resourceGroup = Option47.String("--resource-group");
25993
+ clientId = Option47.String("--client-id");
25994
+ subscription = Option47.String("--subscription");
25995
+ installerTag = Option47.String("--installer-tag");
25783
25996
  // Full image ref override (registry + repo + tag) — an escape hatch when the
25784
25997
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
25785
25998
  // Wins over --installer-tag / the pinned default.
25786
- installerImage = Option46.String("--installer-image");
25787
- gatewayImageRef = Option46.String("--gateway-image-ref");
25788
- githubAppCreds = Option46.String("--github-app-creds");
25999
+ installerImage = Option47.String("--installer-image");
26000
+ gatewayImageRef = Option47.String("--gateway-image-ref");
26001
+ githubAppCreds = Option47.String("--github-app-creds");
25789
26002
  async executeCommand() {
25790
26003
  const location = typeof this.location === "string" ? this.location : void 0;
25791
26004
  if (!location) {
@@ -25876,7 +26089,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
25876
26089
  image: installerImage,
25877
26090
  gatewayImageRef,
25878
26091
  foundryTracing: "skip",
25879
- githubApp
26092
+ githubApp,
26093
+ enrollContactEmail: account.upn
25880
26094
  });
25881
26095
  this.context.stdout.write(
25882
26096
  `${colors.success("\u2713")} installer launched in ${colors.field(resourceGroup)} (${location}).
@@ -25888,7 +26102,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
25888
26102
  };
25889
26103
 
25890
26104
  // src/commands/bootstrap/status.ts
25891
- import { Command as Command50, Option as Option47 } from "clipanion";
26105
+ import { Command as Command51, Option as Option48 } from "clipanion";
25892
26106
  init_errors();
25893
26107
 
25894
26108
  // src/lib/bootstrap-aci-state.ts
@@ -25916,13 +26130,13 @@ async function getAciState(opts) {
25916
26130
  // src/commands/bootstrap/status.ts
25917
26131
  var BootstrapStatusCommand = class extends M8tCommand {
25918
26132
  static paths = [["bootstrap", "status"]];
25919
- static usage = Command50.Usage({
26133
+ static usage = Command51.Usage({
25920
26134
  description: "Show the cloud installer's live status (phase, progress, result).",
25921
26135
  details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.",
25922
26136
  examples: [["One read", "$0 bootstrap status"], ["Watch to completion", "$0 bootstrap status --watch"]]
25923
26137
  });
25924
- watch = Option47.Boolean("--watch", false);
25925
- output = Option47.String("--output");
26138
+ watch = Option48.Boolean("--watch", false);
26139
+ output = Option48.String("--output");
25926
26140
  async executeCommand() {
25927
26141
  const state = await readBootstrapState();
25928
26142
  if (!state) {
@@ -25993,7 +26207,7 @@ function formatStatus(d) {
25993
26207
  }
25994
26208
 
25995
26209
  // src/commands/bootstrap/reap.ts
25996
- import { Command as Command51, Option as Option48 } from "clipanion";
26210
+ import { Command as Command52, Option as Option49 } from "clipanion";
25997
26211
  init_errors();
25998
26212
 
25999
26213
  // src/lib/bootstrap-reap.ts
@@ -26087,14 +26301,14 @@ async function reapInstaller(opts) {
26087
26301
  // src/commands/bootstrap/reap.ts
26088
26302
  var BootstrapReapCommand = class extends M8tCommand {
26089
26303
  static paths = [["bootstrap", "reap"]];
26090
- static usage = Command51.Usage({
26304
+ static usage = Command52.Usage({
26091
26305
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
26092
26306
  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.",
26093
26307
  examples: [["Reap after done", "$0 bootstrap reap"]]
26094
26308
  });
26095
- force = Option48.Boolean("--force", false);
26096
- sweepOrphans = Option48.Boolean("--sweep-orphans", false);
26097
- yes = Option48.Boolean("--yes", false);
26309
+ force = Option49.Boolean("--force", false);
26310
+ sweepOrphans = Option49.Boolean("--sweep-orphans", false);
26311
+ yes = Option49.Boolean("--yes", false);
26098
26312
  async executeCommand() {
26099
26313
  if (this.sweepOrphans === true) {
26100
26314
  const { subscriptionId: sub } = await getAzAccount();
@@ -26191,12 +26405,12 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
26191
26405
  import * as fs31 from "fs/promises";
26192
26406
  import * as os16 from "os";
26193
26407
  import * as path34 from "path";
26194
- import { Command as Command52, Option as Option49 } from "clipanion";
26408
+ import { Command as Command53, Option as Option50 } from "clipanion";
26195
26409
  init_errors();
26196
26410
 
26197
26411
  // src/lib/company-profile-seed.ts
26198
26412
  import { spawn as spawn6 } from "child_process";
26199
- import { closeSync, openSync, readFileSync as readFileSync20 } from "fs";
26413
+ import { closeSync, openSync, readFileSync as readFileSync21 } from "fs";
26200
26414
  import * as os15 from "os";
26201
26415
  import * as path33 from "path";
26202
26416
  init_errors();
@@ -26381,7 +26595,7 @@ function composeFounderIdentityNote(id) {
26381
26595
  function readGithubAppCreds(credsPath) {
26382
26596
  const p = credsPath ?? path33.join(os15.homedir(), ".m8t", "github-app.json");
26383
26597
  try {
26384
- return JSON.parse(readFileSync20(p, "utf8"));
26598
+ return JSON.parse(readFileSync21(p, "utf8"));
26385
26599
  } catch {
26386
26600
  return null;
26387
26601
  }
@@ -26409,7 +26623,7 @@ async function resolveSeedContext(opts) {
26409
26623
  async function applyProfileToBrain(args) {
26410
26624
  const token = await mintInstallationTokenFromPem({
26411
26625
  appId: args.appCreds.appId,
26412
- privateKeyPem: readFileSync20(args.appCreds.pemPath, "utf8"),
26626
+ privateKeyPem: readFileSync21(args.appCreds.pemPath, "utf8"),
26413
26627
  installationId: args.appCreds.installationId,
26414
26628
  fetchImpl: args.fetchImpl
26415
26629
  });
@@ -26521,14 +26735,14 @@ function renderInstallSummary(args) {
26521
26735
  // src/commands/bootstrap/finish.ts
26522
26736
  var BootstrapFinishCommand = class extends M8tCommand {
26523
26737
  static paths = [["bootstrap", "finish"]];
26524
- static usage = Command52.Usage({
26738
+ static usage = Command53.Usage({
26525
26739
  description: "Point your local tools at the now-live platform (repo-root marker, discovery cache, next steps).",
26526
26740
  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.",
26527
26741
  examples: [["Finish local", "$0 bootstrap finish --repo-root /path/to/m8t"]]
26528
26742
  });
26529
- repoRoot = Option49.String("--repo-root");
26530
- subscription = Option49.String("--subscription");
26531
- resourceGroup = Option49.String("--resource-group");
26743
+ repoRoot = Option50.String("--repo-root");
26744
+ subscription = Option50.String("--subscription");
26745
+ resourceGroup = Option50.String("--resource-group");
26532
26746
  async executeCommand() {
26533
26747
  const state = await readBootstrapState();
26534
26748
  if (!state) {
@@ -26631,8 +26845,8 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
26631
26845
  import * as fs33 from "fs";
26632
26846
  import * as os18 from "os";
26633
26847
  import * as path36 from "path";
26634
- import { Command as Command53, Option as Option50 } from "clipanion";
26635
- import { DefaultAzureCredential as DefaultAzureCredential20 } from "@azure/identity";
26848
+ import { Command as Command54, Option as Option51 } from "clipanion";
26849
+ import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
26636
26850
  init_errors();
26637
26851
 
26638
26852
  // src/lib/bootstrap-ui.ts
@@ -26640,7 +26854,7 @@ import * as fs32 from "fs";
26640
26854
  import * as net from "net";
26641
26855
  import * as os17 from "os";
26642
26856
  import * as path35 from "path";
26643
- import { spawn as spawn7 } from "child_process";
26857
+ import { spawn as spawn7, spawnSync as spawnSync6 } from "child_process";
26644
26858
  init_errors();
26645
26859
  init_rbac();
26646
26860
 
@@ -26906,7 +27120,8 @@ async function serveOnboardingUiDetached(args) {
26906
27120
  detached: true,
26907
27121
  stdio: ["ignore", fd, fd],
26908
27122
  env: { ...process.env, PORT: args.port },
26909
- shell: false
27123
+ // pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
27124
+ shell: process.platform === "win32"
26910
27125
  });
26911
27126
  fs32.closeSync(fd);
26912
27127
  if (child.pid == null) {
@@ -26946,7 +27161,8 @@ async function serveOnboardingRelayDetached(args) {
26946
27161
  RELAY_ALLOWED_ORIGINS: "http://localhost:3000",
26947
27162
  RELAY_PORT: "8790"
26948
27163
  },
26949
- shell: false
27164
+ // pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
27165
+ shell: process.platform === "win32"
26950
27166
  });
26951
27167
  fs32.closeSync(fd);
26952
27168
  if (child.pid == null) {
@@ -26985,10 +27201,14 @@ function stopPidFile(pidPath) {
26985
27201
  }
26986
27202
  const pid = Number(pidStr);
26987
27203
  if (!Number.isNaN(pid) && pid > 0) {
26988
- try {
26989
- process.kill(-pid, "SIGTERM");
26990
- } catch (e) {
26991
- if (e.code !== "ESRCH") throw e;
27204
+ if (process.platform === "win32") {
27205
+ spawnSync6("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
27206
+ } else {
27207
+ try {
27208
+ process.kill(-pid, "SIGTERM");
27209
+ } catch (e) {
27210
+ if (e.code !== "ESRCH") throw e;
27211
+ }
26992
27212
  }
26993
27213
  }
26994
27214
  try {
@@ -27020,7 +27240,7 @@ function renderDeployFailure(error) {
27020
27240
  }
27021
27241
  var BootstrapUiCommand = class extends M8tCommand {
27022
27242
  static paths = [["bootstrap", "ui"]];
27023
- static usage = Command53.Usage({
27243
+ static usage = Command54.Usage({
27024
27244
  description: "Deploy Simple Stacey + start the local onboarding chat UI in the background (returns immediately).",
27025
27245
  details: [
27026
27246
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
@@ -27038,15 +27258,15 @@ var BootstrapUiCommand = class extends M8tCommand {
27038
27258
  ["Foreground (blocking, old behaviour)", "$0 bootstrap ui --repo-root /path/to/m8t --foreground"]
27039
27259
  ]
27040
27260
  });
27041
- repoRoot = Option50.String("--repo-root");
27042
- port = Option50.String("--port", "3000");
27043
- endpoint = Option50.String("--endpoint", {
27261
+ repoRoot = Option51.String("--repo-root");
27262
+ port = Option51.String("--port", "3000");
27263
+ endpoint = Option51.String("--endpoint", {
27044
27264
  description: "Foundry project endpoint to target \u2014 disambiguates when the subscription has multiple projects."
27045
27265
  });
27046
- prepOnly = Option50.Boolean("--prep-only", false);
27047
- skipInstall = Option50.Boolean("--skip-install", false);
27048
- stop = Option50.Boolean("--stop", false);
27049
- foreground = Option50.Boolean("--foreground", false);
27266
+ prepOnly = Option51.Boolean("--prep-only", false);
27267
+ skipInstall = Option51.Boolean("--skip-install", false);
27268
+ stop = Option51.Boolean("--stop", false);
27269
+ foreground = Option51.Boolean("--foreground", false);
27050
27270
  async executeCommand() {
27051
27271
  if (this.stop === true) {
27052
27272
  const stopped = stopOnboardingUi();
@@ -27082,7 +27302,7 @@ var BootstrapUiCommand = class extends M8tCommand {
27082
27302
  if (fs33.existsSync(path36.join(os18.homedir(), ".m8t", "config.yaml"))) {
27083
27303
  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."));
27084
27304
  }
27085
- const credential2 = new DefaultAzureCredential20();
27305
+ const credential2 = new DefaultAzureCredential21();
27086
27306
  const account = await getAzAccount();
27087
27307
  assertNodeVersion();
27088
27308
  out("waiting for Foundry (the installer's foundry-create phase)\u2026");
@@ -27180,10 +27400,10 @@ var BootstrapUiCommand = class extends M8tCommand {
27180
27400
  };
27181
27401
 
27182
27402
  // src/commands/bootstrap/seed-profile.ts
27183
- import { Command as Command54, Option as Option51 } from "clipanion";
27403
+ import { Command as Command55, Option as Option52 } from "clipanion";
27184
27404
  var BootstrapSeedProfileCommand = class extends M8tCommand {
27185
27405
  static paths = [["bootstrap", "seed-profile"]];
27186
- static usage = Command54.Usage({
27406
+ static usage = Command55.Usage({
27187
27407
  description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
27188
27408
  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.",
27189
27409
  examples: [
@@ -27191,11 +27411,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
27191
27411
  ["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
27192
27412
  ]
27193
27413
  });
27194
- endpoint = Option51.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
27195
- brain = Option51.String("--brain", { description: "Override the brain repo (default <org>/stacey-brain)." });
27196
- watch = Option51.Boolean("--watch", false, { description: "Poll until the questionnaire completes (or --timeout)." });
27197
- timeout = Option51.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
27198
- githubAppCreds = Option51.String("--github-app-creds");
27414
+ endpoint = Option52.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
27415
+ brain = Option52.String("--brain", { description: "Override the brain repo (default <org>/stacey-brain)." });
27416
+ watch = Option52.Boolean("--watch", false, { description: "Poll until the questionnaire completes (or --timeout)." });
27417
+ timeout = Option52.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
27418
+ githubAppCreds = Option52.String("--github-app-creds");
27199
27419
  async executeCommand() {
27200
27420
  const ctx = await resolveSeedContext({
27201
27421
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -27240,6 +27460,112 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
27240
27460
  }
27241
27461
  };
27242
27462
 
27463
+ // src/commands/telemetry/enroll.ts
27464
+ import { Command as Command56, Option as Option53 } from "clipanion";
27465
+ init_errors();
27466
+
27467
+ // src/lib/telemetry-enroll.ts
27468
+ init_errors();
27469
+ async function enroll(args) {
27470
+ const f = args.fetchImpl ?? fetch;
27471
+ const url = args.baseUrl ? `${args.baseUrl.replace(/\/+$/, "")}/api/ingest/enroll` : ingestUrl("/api/ingest/enroll");
27472
+ let res;
27473
+ try {
27474
+ res = await f(url, {
27475
+ method: "POST",
27476
+ headers: { "content-type": "application/json" },
27477
+ body: JSON.stringify({ schemaVersion: 1, company: args.company, contactEmail: args.contactEmail, subscriptionId: args.subscriptionId, proof: null })
27478
+ });
27479
+ } catch (e) {
27480
+ throw new LocalCliError({ code: "TELEMETRY_ENROLL_UNREACHABLE", message: `Could not reach the telemetry ingest to enroll: ${e instanceof Error ? e.message : String(e)}` });
27481
+ }
27482
+ if (!res.ok) {
27483
+ const body = await res.text().catch(() => "");
27484
+ throw new LocalCliError({ code: "TELEMETRY_ENROLL_FAILED", message: `Enroll failed (HTTP ${res.status.toString()}): ${body}` });
27485
+ }
27486
+ const json = await res.json();
27487
+ if (!json.instanceId || !json.ingestKey) {
27488
+ throw new LocalCliError({ code: "TELEMETRY_ENROLL_BAD_RESPONSE", message: "Enroll response missing instanceId/ingestKey." });
27489
+ }
27490
+ return { instanceId: json.instanceId, ingestKey: json.ingestKey };
27491
+ }
27492
+
27493
+ // src/commands/telemetry/enroll.ts
27494
+ var INGEST_KEY_SECRET = "m8t-ingest-key";
27495
+ async function resolveKeyVaultName(containerAppResourceId) {
27496
+ const kvUri = (await runAz([
27497
+ "containerapp",
27498
+ "show",
27499
+ "--ids",
27500
+ containerAppResourceId,
27501
+ "--query",
27502
+ "properties.template.containers[0].env[?name=='KEY_VAULT_URI'].value | [0]",
27503
+ "-o",
27504
+ "tsv"
27505
+ ])).trim();
27506
+ if (!kvUri) {
27507
+ throw new LocalCliError({
27508
+ code: "TELEMETRY_ENROLL_NO_KV",
27509
+ message: "Could not resolve the platform Key Vault: the gateway Container App has no KEY_VAULT_URI env var.",
27510
+ hint: "Verify the deployment via 'az containerapp show --ids <id> --query properties.template.containers[0].env'."
27511
+ });
27512
+ }
27513
+ const host = new URL(kvUri).hostname;
27514
+ const name = host.split(".")[0];
27515
+ if (!name) {
27516
+ throw new LocalCliError({ code: "TELEMETRY_ENROLL_BAD_KV_URI", message: `Could not parse a Key Vault name from KEY_VAULT_URI '${kvUri}'.` });
27517
+ }
27518
+ return name;
27519
+ }
27520
+ var TelemetryEnrollCommand = class extends M8tCommand {
27521
+ static paths = [["telemetry", "enroll"]];
27522
+ static usage = Command56.Usage({
27523
+ description: "Enroll this installation for operational telemetry (pre-existing installs).",
27524
+ details: "Mints 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. Idempotent: refuses if a key already exists unless --force.",
27525
+ examples: [["Enroll", "$0 telemetry enroll"]]
27526
+ });
27527
+ company = Option53.String("--company", { description: "Your company name (optional)." });
27528
+ contactEmail = Option53.String("--contact-email", { description: "Contact email (default: your signed-in az UPN)." });
27529
+ subscription = Option53.String("--subscription", { description: "Azure subscription id (default: your active az subscription)." });
27530
+ resourceGroup = Option53.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
27531
+ force = Option53.Boolean("--force", false, { description: "Re-enroll even if a key is already stored (mints a new instance id)." });
27532
+ async executeCommand() {
27533
+ const account = await getAzAccount();
27534
+ const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
27535
+ const contactEmail = (typeof this.contactEmail === "string" ? this.contactEmail : void 0) ?? account.upn;
27536
+ const company = (typeof this.company === "string" ? this.company : void 0) ?? "";
27537
+ const d = await discoverGateway({
27538
+ subscriptionId,
27539
+ interactive: false,
27540
+ resourceGroup: typeof this.resourceGroup === "string" ? this.resourceGroup : void 0
27541
+ });
27542
+ const kvName = await resolveKeyVaultName(d.containerAppResourceId);
27543
+ if (this.force !== true) {
27544
+ let existing = "";
27545
+ try {
27546
+ existing = await runAz(["keyvault", "secret", "show", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--query", "value", "-o", "tsv"]);
27547
+ } catch (e) {
27548
+ rethrowIfAzUnavailable(e);
27549
+ existing = "";
27550
+ }
27551
+ if (existing.trim()) {
27552
+ throw new LocalCliError({
27553
+ code: "TELEMETRY_ALREADY_ENROLLED",
27554
+ message: "This installation is already enrolled.",
27555
+ hint: "Pass --force to re-enroll (mints a new instance id)."
27556
+ });
27557
+ }
27558
+ }
27559
+ const { instanceId, ingestKey } = await enroll({ company, contactEmail, subscriptionId });
27560
+ await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--value", ingestKey, "--only-show-errors"]);
27561
+ this.context.stdout.write(
27562
+ `${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
27563
+ `
27564
+ );
27565
+ return 0;
27566
+ }
27567
+ };
27568
+
27243
27569
  // src/cli.ts
27244
27570
  var cli = new Cli({
27245
27571
  binaryName: "m8t",
@@ -27281,6 +27607,7 @@ cli.register(AzureExecDeployCommand);
27281
27607
  cli.register(PlatformStatusCommand);
27282
27608
  cli.register(PlatformUpdateCommand);
27283
27609
  cli.register(PlatformConvergeCommand);
27610
+ cli.register(PlatformSeedStampCommand);
27284
27611
  cli.register(PlatformEnableCostReportCommand);
27285
27612
  cli.register(PlatformEnableAutoUpdateCommand);
27286
27613
  cli.register(DeployCommand);
@@ -27300,6 +27627,7 @@ cli.register(BootstrapReapCommand);
27300
27627
  cli.register(BootstrapFinishCommand);
27301
27628
  cli.register(BootstrapUiCommand);
27302
27629
  cli.register(BootstrapSeedProfileCommand);
27630
+ cli.register(TelemetryEnrollCommand);
27303
27631
  async function main() {
27304
27632
  try {
27305
27633
  return await cli.run(process.argv.slice(2));