@m8t-stack/cli 0.2.82 → 0.2.84

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
@@ -1467,7 +1467,7 @@ var init_enable_hosted_brain = __esm({
1467
1467
  import { Builtins, Cli } from "clipanion";
1468
1468
 
1469
1469
  // src/lib/package-version.ts
1470
- var CLI_VERSION = "0.2.82";
1470
+ var CLI_VERSION = "0.2.84";
1471
1471
 
1472
1472
  // src/lib/render-error.ts
1473
1473
  init_errors();
@@ -3801,12 +3801,12 @@ function encodeURIPath(str4) {
3801
3801
  return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
3802
3802
  }
3803
3803
  var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
3804
- var createPathTagFunction = (pathEncoder = encodeURIPath) => function path48(statics, ...params) {
3804
+ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path47(statics, ...params) {
3805
3805
  if (statics.length === 1)
3806
3806
  return statics[0];
3807
3807
  let postPath = false;
3808
3808
  const invalidSegments = [];
3809
- const path49 = statics.reduce((previousValue, currentValue, index) => {
3809
+ const path48 = statics.reduce((previousValue, currentValue, index) => {
3810
3810
  if (/[?#]/.test(currentValue)) {
3811
3811
  postPath = true;
3812
3812
  }
@@ -3823,7 +3823,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path48(sta
3823
3823
  }
3824
3824
  return previousValue + currentValue + (index === params.length ? "" : encoded);
3825
3825
  }, "");
3826
- const pathOnly = path49.split(/[?#]/, 1)[0];
3826
+ const pathOnly = path48.split(/[?#]/, 1)[0];
3827
3827
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
3828
3828
  let match;
3829
3829
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -3844,10 +3844,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path48(sta
3844
3844
  }, "");
3845
3845
  throw new OpenAIError(`Path parameters result in path with invalid segments:
3846
3846
  ${invalidSegments.map((e) => e.error).join("\n")}
3847
- ${path49}
3847
+ ${path48}
3848
3848
  ${underline}`);
3849
3849
  }
3850
- return path49;
3850
+ return path48;
3851
3851
  };
3852
3852
  var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
3853
3853
 
@@ -12298,9 +12298,9 @@ var OpenAI = class {
12298
12298
  this.apiKey = token;
12299
12299
  return true;
12300
12300
  }
12301
- buildURL(path48, query, defaultBaseURL) {
12301
+ buildURL(path47, query, defaultBaseURL) {
12302
12302
  const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
12303
- const url = isAbsoluteURL(path48) ? new URL(path48) : new URL(baseURL + (baseURL.endsWith("/") && path48.startsWith("/") ? path48.slice(1) : path48));
12303
+ const url = isAbsoluteURL(path47) ? new URL(path47) : new URL(baseURL + (baseURL.endsWith("/") && path47.startsWith("/") ? path47.slice(1) : path47));
12304
12304
  const defaultQuery = this.defaultQuery();
12305
12305
  const pathQuery = Object.fromEntries(url.searchParams);
12306
12306
  if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
@@ -12330,24 +12330,24 @@ var OpenAI = class {
12330
12330
  */
12331
12331
  async prepareRequest(request, { url, options }) {
12332
12332
  }
12333
- get(path48, opts) {
12334
- return this.methodRequest("get", path48, opts);
12333
+ get(path47, opts) {
12334
+ return this.methodRequest("get", path47, opts);
12335
12335
  }
12336
- post(path48, opts) {
12337
- return this.methodRequest("post", path48, opts);
12336
+ post(path47, opts) {
12337
+ return this.methodRequest("post", path47, opts);
12338
12338
  }
12339
- patch(path48, opts) {
12340
- return this.methodRequest("patch", path48, opts);
12339
+ patch(path47, opts) {
12340
+ return this.methodRequest("patch", path47, opts);
12341
12341
  }
12342
- put(path48, opts) {
12343
- return this.methodRequest("put", path48, opts);
12342
+ put(path47, opts) {
12343
+ return this.methodRequest("put", path47, opts);
12344
12344
  }
12345
- delete(path48, opts) {
12346
- return this.methodRequest("delete", path48, opts);
12345
+ delete(path47, opts) {
12346
+ return this.methodRequest("delete", path47, opts);
12347
12347
  }
12348
- methodRequest(method, path48, opts) {
12348
+ methodRequest(method, path47, opts) {
12349
12349
  return this.request(Promise.resolve(opts).then((opts2) => {
12350
- return { method, path: path48, ...opts2 };
12350
+ return { method, path: path47, ...opts2 };
12351
12351
  }));
12352
12352
  }
12353
12353
  request(options, remainingRetries = null) {
@@ -12472,8 +12472,8 @@ var OpenAI = class {
12472
12472
  }));
12473
12473
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
12474
12474
  }
12475
- getAPIList(path48, Page2, opts) {
12476
- return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path48, ...opts2 })) : { method: "get", path: path48, ...opts });
12475
+ getAPIList(path47, Page2, opts) {
12476
+ return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path47, ...opts2 })) : { method: "get", path: path47, ...opts });
12477
12477
  }
12478
12478
  requestAPIList(Page2, options) {
12479
12479
  const request = this.makeRequest(options, null, void 0);
@@ -12567,8 +12567,8 @@ var OpenAI = class {
12567
12567
  }
12568
12568
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
12569
12569
  const options = { ...inputOptions };
12570
- const { method, path: path48, query, defaultBaseURL } = options;
12571
- const url = this.buildURL(path48, query, defaultBaseURL);
12570
+ const { method, path: path47, query, defaultBaseURL } = options;
12571
+ const url = this.buildURL(path47, query, defaultBaseURL);
12572
12572
  if ("timeout" in options)
12573
12573
  validatePositiveInteger("timeout", options.timeout);
12574
12574
  options.timeout = options.timeout ?? this.timeout;
@@ -13633,10 +13633,10 @@ var BindListCommand = class extends M8tCommand {
13633
13633
  const qs = new URLSearchParams();
13634
13634
  if (this.channel) qs.set("channel", this.channel);
13635
13635
  if (this.status) qs.set("status", this.status);
13636
- const path48 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
13636
+ const path47 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
13637
13637
  const data = await apiCall(
13638
13638
  { gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
13639
- { method: "GET", path: path48 },
13639
+ { method: "GET", path: path47 },
13640
13640
  (msg) => this.context.stderr.write(msg + "\n")
13641
13641
  );
13642
13642
  if (mode === "json") {
@@ -14882,8 +14882,8 @@ ${r.stderr}`;
14882
14882
  message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
14883
14883
  });
14884
14884
  },
14885
- getFile: async (path48) => {
14886
- const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path48}`]);
14885
+ getFile: async (path47) => {
14886
+ const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path47}`]);
14887
14887
  return r.exitCode === 0 ? r.stdout : null;
14888
14888
  },
14889
14889
  isEmpty: async () => {
@@ -16434,7 +16434,7 @@ function appRepoProbe(args) {
16434
16434
  const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
16435
16435
  return res.status;
16436
16436
  },
16437
- getFile: (path48) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path48, fetchImpl: args.fetchImpl }),
16437
+ getFile: (path47) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path47, fetchImpl: args.fetchImpl }),
16438
16438
  isEmpty: async () => {
16439
16439
  const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
16440
16440
  return res.status === 404;
@@ -20001,6 +20001,7 @@ import { DefaultAzureCredential as DefaultAzureCredential16 } from "@azure/ident
20001
20001
 
20002
20002
  // src/lib/platform-update.ts
20003
20003
  init_errors();
20004
+ import semver from "semver";
20004
20005
  var DEFAULT_IMAGE_REPO = "ghcr.io/m8t-labs/m8t";
20005
20006
  function parseImageRef2(ref) {
20006
20007
  let rest = ref;
@@ -20055,10 +20056,22 @@ function compareSemver(a, b) {
20055
20056
  }
20056
20057
  return 0;
20057
20058
  }
20059
+ function comparePlatformVersion(a, b) {
20060
+ const bare = (v) => v.trim().replace(/^platform-/, "").replace(/^v/, "");
20061
+ const va = semver.valid(bare(a));
20062
+ const vb = semver.valid(bare(b));
20063
+ if (!va || !vb) {
20064
+ throw new LocalCliError({
20065
+ code: "PLATFORM_BAD_SEMVER",
20066
+ message: `comparePlatformVersion needs platform versions, got '${a}' / '${b}'.`
20067
+ });
20068
+ }
20069
+ return semver.compare(va, vb);
20070
+ }
20058
20071
  function pickNewestSemver(tags) {
20059
- const semver = tags.filter(isSemverTag);
20060
- if (semver.length === 0) return null;
20061
- return semver.reduce((best, t) => compareSemver(t, best) > 0 ? t : best);
20072
+ const semver2 = tags.filter(isSemverTag);
20073
+ if (semver2.length === 0) return null;
20074
+ return semver2.reduce((best, t) => compareSemver(t, best) > 0 ? t : best);
20062
20075
  }
20063
20076
  function parseContainerAppResourceId(id) {
20064
20077
  const m = /\/subscriptions\/([^/]+)\/resourceGroups\/([^/]+)\/providers\/Microsoft\.App\/containerApps\/([^/]+)/i.exec(
@@ -20104,8 +20117,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
20104
20117
  hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
20105
20118
  });
20106
20119
  }
20107
- const path48 = repo.replace(/^ghcr\.io\//, "");
20108
- const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path48}:pull`);
20120
+ const path47 = repo.replace(/^ghcr\.io\//, "");
20121
+ const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path47}:pull`);
20109
20122
  if (!tokenRes.ok) {
20110
20123
  throw new LocalCliError({
20111
20124
  code: "PLATFORM_GHCR_TOKEN_FAILED",
@@ -20121,7 +20134,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
20121
20134
  hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
20122
20135
  });
20123
20136
  }
20124
- const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path48}/tags/list`, {
20137
+ const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path47}/tags/list`, {
20125
20138
  headers: { Authorization: `Bearer ${token}` }
20126
20139
  });
20127
20140
  if (!tagsRes.ok) {
@@ -20298,6 +20311,8 @@ function entityToStamp(e) {
20298
20311
  var RELEASES_REPO = "m8t-labs/m8t-releases";
20299
20312
  var RELEASES_BASE = `https://github.com/${RELEASES_REPO}/releases`;
20300
20313
  var CHANNEL_LATEST_URL = `${RELEASES_BASE}/latest/download/manifest.json`;
20314
+ var CANARY_POINTER_TAG = "canary";
20315
+ var CHANNEL_CANARY_URL = `${RELEASES_BASE}/download/${CANARY_POINTER_TAG}/manifest.json`;
20301
20316
  function platformTag(version) {
20302
20317
  const bare = version.trim().replace(/^platform-/, "").replace(/^v/, "");
20303
20318
  return `platform-v${bare}`;
@@ -20309,13 +20324,46 @@ function releaseAssetUrl(tag, asset) {
20309
20324
  return `${RELEASES_BASE}/download/${tag}/${asset}`;
20310
20325
  }
20311
20326
 
20327
+ // ../../packages/platform-release/dist/esm/canary-version.js
20328
+ var CANARY_IDENTIFIER = "canary";
20329
+ var STABLE_CORE = String.raw`\d+\.\d+\.\d+`;
20330
+ var CANARY_SUFFIX = String.raw`-${CANARY_IDENTIFIER}\.(?:0|[1-9]\d*)`;
20331
+ var CANARY_VERSION_PATTERN = `^${STABLE_CORE}${CANARY_SUFFIX}$`;
20332
+ var PLATFORM_VERSION_PATTERN = `^${STABLE_CORE}(?:${CANARY_SUFFIX})?$`;
20333
+ var PLATFORM_TAG_PATTERN = `^platform-v${STABLE_CORE}(?:${CANARY_SUFFIX})?$`;
20334
+ var CANARY_VERSION_RE = new RegExp(CANARY_VERSION_PATTERN);
20335
+ var STABLE_CORE_RE = new RegExp(`^${STABLE_CORE}$`);
20336
+
20312
20337
  // ../../packages/platform-release/dist/esm/apply-request.js
20313
20338
  var APPLY_REQUEST_PK = "system";
20314
20339
  var APPLY_REQUEST_RK = "apply-request";
20315
20340
  var IN_FLIGHT = ["claimed", "applying"];
20341
+ var LEASE_MS = 30 * 60 * 1e3;
20342
+ var MAX_LEASE_TAKEOVERS = 2;
20343
+ var MAX_HEALTHY_IN_FLIGHT_MS = LEASE_MS * (MAX_LEASE_TAKEOVERS + 1);
20316
20344
  function isInFlight(status) {
20317
20345
  return IN_FLIGHT.includes(status);
20318
20346
  }
20347
+ var BLOCKS_NEW_INTENT = ["pending", "claimed", "applying", "awaiting-engine-update"];
20348
+ function blocksNewIntent(status) {
20349
+ return BLOCKS_NEW_INTENT.includes(status);
20350
+ }
20351
+ function newIntent(target, provenance, nowIso) {
20352
+ return {
20353
+ schemaVersion: 1,
20354
+ target,
20355
+ provenance,
20356
+ status: "pending",
20357
+ phase: null,
20358
+ claimedBy: null,
20359
+ leaseUntil: null,
20360
+ attempt: 0,
20361
+ breaker: "none",
20362
+ result: null,
20363
+ createdAt: nowIso,
20364
+ updatedAt: nowIso
20365
+ };
20366
+ }
20319
20367
  function applyRequestToEntity(r) {
20320
20368
  return { partitionKey: APPLY_REQUEST_PK, rowKey: APPLY_REQUEST_RK, value: JSON.stringify(r), updatedAt: r.updatedAt };
20321
20369
  }
@@ -20354,11 +20402,10 @@ function manifestSourceForVersionTag(channelUrl, version) {
20354
20402
  const tag = platformTag(version);
20355
20403
  const custom = channelUrl.trim();
20356
20404
  if (!custom || custom === CHANNEL_LATEST_URL) return { channel: true, version: tag };
20357
- if (custom.includes("/releases/latest/download/")) {
20358
- return { url: custom.replace("/releases/latest/download/", `/releases/download/${tag}/`) };
20359
- }
20360
- return { url: custom };
20405
+ const swapped = custom.replace(POINTER_SEGMENT, `/releases/download/${tag}/`);
20406
+ return { url: swapped };
20361
20407
  }
20408
+ var POINTER_SEGMENT = /\/releases\/(?:latest\/download|download\/[^/]+)\//;
20362
20409
  async function fetchManifest(source, deps = {}) {
20363
20410
  const readFile9 = deps.readFile ?? ((p) => fs23.readFileSync(p, "utf8"));
20364
20411
  const fetchImpl = deps.fetchImpl ?? fetch;
@@ -21630,25 +21677,25 @@ function mergeThreeWay(ours, base, theirs) {
21630
21677
  fs27.rmSync(dir, { recursive: true, force: true });
21631
21678
  }
21632
21679
  }
21633
- var skip = (path48, skipReason) => ({ path: path48, kind: "skip", skipReason });
21680
+ var skip = (path47, skipReason) => ({ path: path47, kind: "skip", skipReason });
21634
21681
  function classifyCandidate(c) {
21635
- const { path: path48, base, ours, theirs } = c;
21636
- if (theirs === base) return { path: path48, kind: "noop" };
21682
+ const { path: path47, base, ours, theirs } = c;
21683
+ if (theirs === base) return { path: path47, kind: "noop" };
21637
21684
  if (base === null) {
21638
- if (theirs === null) return { path: path48, kind: "noop" };
21639
- if (ours === null) return { path: path48, kind: "add", content: theirs };
21640
- if (ours === theirs) return { path: path48, kind: "noop" };
21641
- return skip(path48, "you already have a file at this path");
21685
+ if (theirs === null) return { path: path47, kind: "noop" };
21686
+ if (ours === null) return { path: path47, kind: "add", content: theirs };
21687
+ if (ours === theirs) return { path: path47, kind: "noop" };
21688
+ return skip(path47, "you already have a file at this path");
21642
21689
  }
21643
21690
  if (ours === null) {
21644
- return theirs === null ? { path: path48, kind: "noop" } : skip(path48, "you removed this file; upstream changed it");
21691
+ return theirs === null ? { path: path47, kind: "noop" } : skip(path47, "you removed this file; upstream changed it");
21645
21692
  }
21646
21693
  if (theirs === null) {
21647
- return ours === base ? { path: path48, kind: "delete" } : skip(path48, "you edited this file; upstream removed it");
21694
+ return ours === base ? { path: path47, kind: "delete" } : skip(path47, "you edited this file; upstream removed it");
21648
21695
  }
21649
- if (ours === base) return { path: path48, kind: "update", content: theirs };
21696
+ if (ours === base) return { path: path47, kind: "update", content: theirs };
21650
21697
  const m = mergeThreeWay(ours, base, theirs);
21651
- return m.ok ? { path: path48, kind: "update", content: m.merged } : skip(path48, "your edits conflict with the update");
21698
+ return m.ok ? { path: path47, kind: "update", content: m.merged } : skip(path47, "your edits conflict with the update");
21652
21699
  }
21653
21700
 
21654
21701
  // src/lib/seed-pr-body.ts
@@ -21835,7 +21882,7 @@ function bindRepoApi(token, repo) {
21835
21882
  openPr: (head, base, title, body) => openPullRequestViaApp({ token, repo, base, head, title, body }),
21836
21883
  updatePr: (n, title, body) => updatePullRequestViaApp({ token, repo, number: n, title, body }),
21837
21884
  closePr: (n, comment) => closePullRequestViaApp({ token, repo, number: n, comment }),
21838
- readFileAtRef: (path48, ref) => readRepoFileViaApp({ token, repo, path: path48, ref })
21885
+ readFileAtRef: (path47, ref) => readRepoFileViaApp({ token, repo, path: path47, ref })
21839
21886
  };
21840
21887
  }
21841
21888
  async function runSeedRefresh(args) {
@@ -22337,7 +22384,6 @@ function resolveHeadlessContextFromEnv(env) {
22337
22384
 
22338
22385
  // src/lib/apply-request-store.ts
22339
22386
  import { TableClient as TableClient4 } from "@azure/data-tables";
22340
- var MAX_LEASE_TAKEOVERS = 2;
22341
22387
  async function openApplyTable(opts) {
22342
22388
  const { tableEndpoint } = await discoverStampStorage(opts);
22343
22389
  return new TableClient4(tableEndpoint, "Metadata", opts.credential);
@@ -22423,32 +22469,59 @@ function assertForwardTarget(manifest) {
22423
22469
  }
22424
22470
 
22425
22471
  // src/lib/platform-self-update.ts
22426
- import * as fs30 from "fs";
22427
- import * as path32 from "path";
22428
22472
  function toVTag2(v) {
22429
22473
  return v.startsWith("v") ? v : `v${v}`;
22430
22474
  }
22431
- function readBakedReleaseVersion(repoRoot) {
22432
- if (!repoRoot) return null;
22433
- try {
22434
- const raw = fs30.readFileSync(path32.join(repoRoot, "installer", "version.json"), "utf8");
22435
- const v = JSON.parse(raw).version;
22436
- return typeof v === "string" && v.length > 0 ? v : null;
22437
- } catch {
22438
- return null;
22439
- }
22440
- }
22441
- function needsEngineUpdate(manifest, runningVersion, bakedRelease) {
22475
+ function needsEngineUpdate(manifest, runningVersion, runningImage) {
22442
22476
  if (runningVersion === "0.0.0-dev") return false;
22443
- if (bakedRelease != null && bakedRelease.length > 0 && bakedRelease !== manifest.components.installer.version) return true;
22477
+ if (needsEngineImageHop(manifest, runningVersion, runningImage ?? null)) return true;
22444
22478
  const running = toVTag2(runningVersion);
22445
22479
  const min = toVTag2(manifest.components.cli.min);
22446
22480
  return compareSemver(running, min) < 0;
22447
22481
  }
22482
+ function needsPreClaimEngineUpdate(manifest, runningVersion, runningImage) {
22483
+ return needsEngineImageHop(manifest, runningVersion, runningImage ?? null);
22484
+ }
22485
+ function targetEngineImage(manifest) {
22486
+ const inst = manifest.components.installer;
22487
+ return `${inst.ref}:${inst.tag}`;
22488
+ }
22489
+ function ownJobName(env = process.env) {
22490
+ const name = (env.UPDATER_JOB_NAME ?? "").trim();
22491
+ return name.length > 0 ? name : null;
22492
+ }
22493
+ async function readOwnJobImage(opts) {
22494
+ const job = opts.jobName ?? ownJobName();
22495
+ if (!job) return null;
22496
+ const image = (await runAz([
22497
+ "containerapp",
22498
+ "job",
22499
+ "show",
22500
+ "-n",
22501
+ job,
22502
+ "-g",
22503
+ opts.resourceGroup,
22504
+ "--subscription",
22505
+ opts.subscriptionId,
22506
+ "--query",
22507
+ "properties.template.containers[0].image",
22508
+ "-o",
22509
+ "tsv",
22510
+ "--only-show-errors"
22511
+ ])).trim();
22512
+ if (!image || image === "None") {
22513
+ throw new Error(`could not read the updater job's current image (job ${job}) \u2014 refusing to decide a self-update blind`);
22514
+ }
22515
+ return image;
22516
+ }
22517
+ function needsEngineImageHop(manifest, runningVersion, runningImage) {
22518
+ if (runningVersion === "0.0.0-dev") return false;
22519
+ if (runningImage === null || runningImage.trim().length === 0) return false;
22520
+ return runningImage.trim() !== targetEngineImage(manifest);
22521
+ }
22448
22522
  async function updateOwnJobImage(opts) {
22449
- const inst = opts.manifest.components.installer;
22450
- const image = `${inst.ref}:${inst.tag}`;
22451
- const job = opts.jobName ?? process.env.UPDATER_JOB_NAME;
22523
+ const image = targetEngineImage(opts.manifest);
22524
+ const job = opts.jobName ?? ownJobName();
22452
22525
  if (!job) throw new Error("UPDATER_JOB_NAME unset \u2014 cannot self-update the updater job image");
22453
22526
  await runAz([
22454
22527
  "containerapp",
@@ -22618,13 +22691,9 @@ async function readBreaker(client) {
22618
22691
 
22619
22692
  // src/commands/platform/converge.ts
22620
22693
  init_errors();
22621
- function canonicalVersion(v) {
22622
- const bare = v.replace(/^platform-/, "");
22623
- return bare.startsWith("v") ? bare : `v${bare}`;
22624
- }
22625
22694
  function isForwardTarget(target, installed) {
22626
22695
  if (!installed) return true;
22627
- return compareSemver(canonicalVersion(target), canonicalVersion(installed)) >= 0;
22696
+ return comparePlatformVersion(target, installed) >= 0;
22628
22697
  }
22629
22698
  async function failIfStillInFlight(client, now, error) {
22630
22699
  const cur = await readApplyRequest(client);
@@ -22655,6 +22724,28 @@ var PlatformConvergeCommand = class extends M8tCommand {
22655
22724
  subscriptionId: ctx.subscriptionId,
22656
22725
  resourceGroup: ctx.resourceGroup
22657
22726
  });
22727
+ let runningImage = null;
22728
+ const outstanding = await readApplyRequest(client);
22729
+ if (outstanding && blocksNewIntent(outstanding.status)) {
22730
+ runningImage = await readOwnJobImage({
22731
+ subscriptionId: ctx.subscriptionId,
22732
+ resourceGroup: ctx.resourceGroup
22733
+ });
22734
+ if (runningImage) {
22735
+ const preManifest = await fetchManifest(targetManifestSource(ctx, outstanding.target));
22736
+ if (needsPreClaimEngineUpdate(preManifest, CLI_VERSION, runningImage)) {
22737
+ log(
22738
+ `self-updating BEFORE claiming (running ${runningImage} \u2192 target ${targetEngineImage(preManifest)}); the next tick claims with the new engine.`
22739
+ );
22740
+ await updateOwnJobImage({
22741
+ subscriptionId: ctx.subscriptionId,
22742
+ resourceGroup: ctx.resourceGroup,
22743
+ manifest: preManifest
22744
+ });
22745
+ return 0;
22746
+ }
22747
+ }
22748
+ }
22658
22749
  const claimed = await claimApplyRequest(client, executionId, now(), 30 * 60 * 1e3);
22659
22750
  if (!claimed) {
22660
22751
  log("no pending apply-request \u2014 nothing to converge.");
@@ -22689,10 +22780,9 @@ var PlatformConvergeCommand = class extends M8tCommand {
22689
22780
  return 1;
22690
22781
  }
22691
22782
  const repoRoot = tryResolveRepoRoot();
22692
- const bakedRelease = readBakedReleaseVersion(repoRoot);
22693
- if (needsEngineUpdate(manifest, CLI_VERSION, bakedRelease)) {
22783
+ if (needsEngineUpdate(manifest, CLI_VERSION, runningImage)) {
22694
22784
  log(
22695
- `self-updating the updater image (baked release ${bakedRelease ?? "(none)"} / engine ${CLI_VERSION} \u2192 target installer ${manifest.components.installer.version}, cli.min ${manifest.components.cli.min}).`
22785
+ `self-updating the updater image (running ${runningImage ?? "(not the job)"} / engine ${CLI_VERSION} \u2192 target ${targetEngineImage(manifest)}, cli.min ${manifest.components.cli.min}).`
22696
22786
  );
22697
22787
  await updateOwnJobImage({
22698
22788
  subscriptionId: ctx.subscriptionId,
@@ -22854,12 +22944,270 @@ function targetManifestSource(ctx, target) {
22854
22944
  return manifestSourceForVersionTag(ctx.channelUrl, target);
22855
22945
  }
22856
22946
 
22857
- // src/commands/platform/seed-stamp.ts
22858
- import { readFileSync as readFileSync21 } from "fs";
22947
+ // src/commands/platform/clear-intent.ts
22859
22948
  import { Command as Command37, Option as Option34 } from "clipanion";
22860
22949
  import { DefaultAzureCredential as DefaultAzureCredential18, ManagedIdentityCredential as ManagedIdentityCredential2 } from "@azure/identity";
22861
22950
  init_errors();
22862
22951
 
22952
+ // src/lib/apply-intent-clear.ts
22953
+ var CLEARED_BY_OPERATOR = "cleared by an operator via `m8t platform clear-intent`";
22954
+ function decideClear(row, nowIso, force) {
22955
+ if (!row) return { action: "none", reason: "there is no apply-request to clear." };
22956
+ if (!isInFlight(row.status) && row.status !== "pending" && row.status !== "awaiting-engine-update") {
22957
+ return { action: "none", reason: `the apply-request is already terminal (${row.status}) \u2014 a new intent can be written.` };
22958
+ }
22959
+ const leaseLive = isInFlight(row.status) && row.leaseUntil !== null && Date.parse(row.leaseUntil) > Date.parse(nowIso);
22960
+ if (leaseLive && !force) {
22961
+ return {
22962
+ action: "refuse",
22963
+ reason: `the apply-request is ${row.status} and its lease is still live until ${row.leaseUntil ?? "(unknown)"} \u2014 a converge may be running right now. Clearing it could start a second apply alongside the first. Wait for the lease to expire, or pass --force if you know the engine is gone.`
22964
+ };
22965
+ }
22966
+ const held = leaseLive ? " (lease still live \u2014 forced)" : "";
22967
+ return { action: "clear", reason: `clearing a ${row.status} apply-request for target ${row.target}${held}.` };
22968
+ }
22969
+ function clearedRow(row, nowIso) {
22970
+ return {
22971
+ ...row,
22972
+ status: "failed",
22973
+ result: {
22974
+ appliedVersion: row.result?.appliedVersion ?? null,
22975
+ error: `${CLEARED_BY_OPERATOR} (was ${row.status}${row.phase ? `, phase "${row.phase}"` : ""})`
22976
+ },
22977
+ updatedAt: nowIso
22978
+ };
22979
+ }
22980
+
22981
+ // src/commands/platform/clear-intent.ts
22982
+ var PlatformClearIntentCommand = class extends M8tCommand {
22983
+ static paths = [["platform", "clear-intent"]];
22984
+ static usage = Command37.Usage({
22985
+ description: "Clear a stuck platform update request so a new one can be made.",
22986
+ details: "An update request that never settles blocks every later request, and the routines that would normally rescue it all run inside the updater itself \u2014 which is no help when the updater is what is stuck. This clears it from outside. Refuses while an update may still be running, unless --force.",
22987
+ examples: [
22988
+ ["Clear a stuck request", "m8t platform clear-intent --resource-group rg-m8t --subscription <id>"],
22989
+ ["Clear one whose lease is still live", "m8t platform clear-intent --resource-group rg-m8t --subscription <id> --force"]
22990
+ ]
22991
+ });
22992
+ subscription = Option34.String("--subscription");
22993
+ resourceGroup = Option34.String("--resource-group");
22994
+ miClientId = Option34.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
22995
+ force = Option34.Boolean("--force", false, {
22996
+ description: "Clear even while the lease is live. Only when you know the engine that held it is gone."
22997
+ });
22998
+ output = Option34.String("--output");
22999
+ async executeCommand() {
23000
+ const mode = resolveOutputMode(this.output, this.context.stdout);
23001
+ const need = (v, flag) => {
23002
+ if (typeof v !== "string" || v.trim().length === 0) {
23003
+ throw new LocalCliError({ code: "PLATFORM_CLEAR_INTENT_ARG_MISSING", message: `${flag} is required.` });
23004
+ }
23005
+ return v.trim();
23006
+ };
23007
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential2({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential18();
23008
+ const client = await openApplyTable({
23009
+ credential: credential2,
23010
+ subscriptionId: need(this.subscription, "--subscription"),
23011
+ resourceGroup: need(this.resourceGroup, "--resource-group")
23012
+ });
23013
+ const row = await readApplyRequest(client);
23014
+ const decision = decideClear(row, (/* @__PURE__ */ new Date()).toISOString(), this.force);
23015
+ if (decision.action === "refuse") {
23016
+ throw new LocalCliError({
23017
+ code: "PLATFORM_CLEAR_INTENT_REFUSED",
23018
+ message: decision.reason,
23019
+ hint: "Re-run with --force once you have confirmed no converge is running."
23020
+ });
23021
+ }
23022
+ if (decision.action === "none" || !row) {
23023
+ this.context.stdout.write(
23024
+ mode === "json" ? renderJson({ cleared: false, reason: decision.reason }) + "\n" : `${decision.reason}
23025
+ `
23026
+ );
23027
+ return 0;
23028
+ }
23029
+ const next = clearedRow(row, (/* @__PURE__ */ new Date()).toISOString());
23030
+ await client.updateEntity(applyRequestToEntity(next), "Merge");
23031
+ const result2 = { cleared: true, previousStatus: row.status, target: row.target, reason: decision.reason };
23032
+ this.context.stdout.write(
23033
+ mode === "json" ? renderJson(result2) + "\n" : `cleared: ${row.status} \u2192 failed (target ${row.target}). A new update can now be requested.
23034
+ `
23035
+ );
23036
+ return 0;
23037
+ }
23038
+ };
23039
+
23040
+ // src/commands/platform/request-update.ts
23041
+ import { Command as Command38, Option as Option35 } from "clipanion";
23042
+ import { DefaultAzureCredential as DefaultAzureCredential19, ManagedIdentityCredential as ManagedIdentityCredential3 } from "@azure/identity";
23043
+ init_errors();
23044
+
23045
+ // src/lib/request-update-follow.ts
23046
+ var TERMINAL = {
23047
+ success: true,
23048
+ failed: false,
23049
+ "rolled-back": false,
23050
+ held: false
23051
+ };
23052
+ function isTerminal(status) {
23053
+ return status in TERMINAL;
23054
+ }
23055
+ function classifyPolledRow(row, target) {
23056
+ if (!row) return null;
23057
+ if (row.target !== target) return { kind: "not-ours", otherTarget: row.target };
23058
+ if (isTerminal(row.status)) return { kind: "settled", row };
23059
+ return { kind: "in-progress", where: positionLabel(row.status, row.phase) };
23060
+ }
23061
+ function positionLabel(status, phase) {
23062
+ return phase ? `${status} (${phase})` : status;
23063
+ }
23064
+ function outcomeExitCode(settled) {
23065
+ if (!settled) return 1;
23066
+ return TERMINAL[settled.status] ? 0 : 1;
23067
+ }
23068
+ function outcomeStatus(settled) {
23069
+ return settled?.status ?? "timed-out";
23070
+ }
23071
+
23072
+ // src/commands/platform/request-update.ts
23073
+ var PlatformRequestUpdateCommand = class extends M8tCommand {
23074
+ static paths = [["platform", "request-update"]];
23075
+ static usage = Command38.Usage({
23076
+ description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
23077
+ details: "Writes the update request the updater job claims on its next tick. This is the ONLY privilege the requester holds \u2014 it does not deploy anything itself; the install's own updater fetches the release, applies it, health-gates it and rolls back on failure. With --wait, follows the request to a terminal state and exits non-zero if it did not succeed.",
23078
+ examples: [
23079
+ ["Request an update", "m8t platform request-update --version 0.7.2 --resource-group rg-m8t --subscription <id>"],
23080
+ ["Request it and wait", "m8t platform request-update --version 0.7.2 --wait --resource-group rg-m8t --subscription <id>"]
23081
+ ]
23082
+ });
23083
+ version = Option35.String("--version", { description: "The platform version to converge to." });
23084
+ subscription = Option35.String("--subscription");
23085
+ resourceGroup = Option35.String("--resource-group");
23086
+ source = Option35.String("--source", "cd", { description: "Who is asking: cd | button | policy." });
23087
+ identity = Option35.String("--identity", "", { description: "Recorded on the request, for the audit trail." });
23088
+ miClientId = Option35.String("--mi-client-id");
23089
+ wait = Option35.Boolean("--wait", false, { description: "Follow the request until it settles." });
23090
+ /**
23091
+ * Follow an EXISTING request without writing a new one.
23092
+ *
23093
+ * Lets a caller write the request in one step and wait in another, which is
23094
+ * what makes the ordering provable: the row is a fact before anything is
23095
+ * started against it, instead of a background write racing a sleep.
23096
+ */
23097
+ waitOnly = Option35.Boolean("--wait-only", false, { description: "Follow the current request without creating one." });
23098
+ timeoutMinutes = Option35.String("--timeout-minutes", "45");
23099
+ pollSeconds = Option35.String("--poll-seconds", "20");
23100
+ output = Option35.String("--output");
23101
+ async executeCommand() {
23102
+ const mode = resolveOutputMode(this.output, this.context.stdout);
23103
+ const need = (v, flag) => {
23104
+ if (typeof v !== "string" || v.trim().length === 0) {
23105
+ throw new LocalCliError({ code: "PLATFORM_REQUEST_UPDATE_ARG_MISSING", message: `${flag} is required.` });
23106
+ }
23107
+ return v.trim();
23108
+ };
23109
+ const target = need(this.version, "--version");
23110
+ const SOURCES2 = ["cd", "button", "policy"];
23111
+ if (!SOURCES2.includes(this.source)) {
23112
+ throw new LocalCliError({ code: "PLATFORM_REQUEST_UPDATE_BAD_SOURCE", message: `--source must be cd, button or policy; got '${this.source}'.` });
23113
+ }
23114
+ const source = this.source;
23115
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential3({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential19();
23116
+ const client = await openApplyTable({
23117
+ credential: credential2,
23118
+ subscriptionId: need(this.subscription, "--subscription"),
23119
+ resourceGroup: need(this.resourceGroup, "--resource-group")
23120
+ });
23121
+ const log = (m) => {
23122
+ this.context.stderr.write(`${m}
23123
+ `);
23124
+ };
23125
+ if (!this.waitOnly) {
23126
+ const existing = await readApplyRequest(client);
23127
+ if (existing && blocksNewIntent(existing.status)) {
23128
+ throw new LocalCliError({
23129
+ code: "PLATFORM_REQUEST_UPDATE_IN_PROGRESS",
23130
+ message: `an update request for ${existing.target} is already ${existing.status}.`,
23131
+ hint: "Wait for it to settle, or clear it with `m8t platform clear-intent` if nothing is advancing it."
23132
+ });
23133
+ }
23134
+ const now = (/* @__PURE__ */ new Date()).toISOString();
23135
+ const intent = newIntent(target, { source, identity: this.identity || source, at: now }, now);
23136
+ await client.upsertEntity(applyRequestToEntity(intent), "Replace");
23137
+ log(`requested update to ${target} (source ${source}).`);
23138
+ } else {
23139
+ log(`following the existing request for ${target}.`);
23140
+ }
23141
+ if (!this.wait && !this.waitOnly) {
23142
+ this.context.stdout.write(
23143
+ mode === "json" ? renderJson({ requested: true, target, waited: false }) + "\n" : `requested update to ${target}.
23144
+ `
23145
+ );
23146
+ return 0;
23147
+ }
23148
+ const settled = await this.follow(client, target, log);
23149
+ const result2 = {
23150
+ requested: true,
23151
+ target,
23152
+ waited: true,
23153
+ status: outcomeStatus(settled),
23154
+ error: settled?.result?.error ?? null
23155
+ };
23156
+ this.context.stdout.write(
23157
+ mode === "json" ? renderJson(result2) + "\n" : `${target}: ${result2.status}${result2.error ? ` \u2014 ${result2.error}` : ""}
23158
+ `
23159
+ );
23160
+ return outcomeExitCode(settled);
23161
+ }
23162
+ /**
23163
+ * Follow the request until it settles.
23164
+ *
23165
+ * `awaiting-engine-update` is explicitly NOT a failure and not a stall: the
23166
+ * updater parks a request there while it replaces its own image, so a converge
23167
+ * legitimately spans two ticks. Treating that as stuck would fail a build for
23168
+ * doing exactly what it is supposed to do.
23169
+ */
23170
+ async follow(client, target, log) {
23171
+ const timeoutMs = Number(this.timeoutMinutes) * 60 * 1e3;
23172
+ const pollMs = Number(this.pollSeconds) * 1e3;
23173
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
23174
+ throw new LocalCliError({ code: "PLATFORM_REQUEST_UPDATE_BAD_TIMEOUT", message: `--timeout-minutes must be a positive number, got '${this.timeoutMinutes}'.` });
23175
+ }
23176
+ const deadline = Date.now() + timeoutMs;
23177
+ let lastReported = "";
23178
+ let consecutiveAbsent = 0;
23179
+ while (Date.now() < deadline) {
23180
+ await new Promise((r) => setTimeout(r, pollMs));
23181
+ const verdict = classifyPolledRow(await readApplyRequest(client), target);
23182
+ if (!verdict) {
23183
+ consecutiveAbsent += 1;
23184
+ if (consecutiveAbsent >= 3) {
23185
+ throw new LocalCliError({
23186
+ code: "PLATFORM_REQUEST_UPDATE_ROW_ABSENT",
23187
+ message: `no update request found for ${target} after ${String(consecutiveAbsent)} polls.`,
23188
+ hint: "Something removed the row after it was written. Check for a concurrent `clear-intent` or a competing requester."
23189
+ });
23190
+ }
23191
+ continue;
23192
+ }
23193
+ consecutiveAbsent = 0;
23194
+ if (verdict.kind === "settled") return verdict.row;
23195
+ const line2 = verdict.kind === "not-ours" ? ` (ignoring a request for ${verdict.otherTarget} \u2014 not the one we made)` : ` ${verdict.where}`;
23196
+ if (line2 !== lastReported) {
23197
+ log(line2);
23198
+ lastReported = line2;
23199
+ }
23200
+ }
23201
+ return null;
23202
+ }
23203
+ };
23204
+
23205
+ // src/commands/platform/seed-stamp.ts
23206
+ import { readFileSync as readFileSync20 } from "fs";
23207
+ import { Command as Command39, Option as Option36 } from "clipanion";
23208
+ import { DefaultAzureCredential as DefaultAzureCredential20, ManagedIdentityCredential as ManagedIdentityCredential4 } from "@azure/identity";
23209
+ init_errors();
23210
+
22863
23211
  // src/lib/platform-stamp-seed.ts
22864
23212
  var IMAGE_COMPONENTS = ["gateway", "codingAgent", "azureExecutor"];
22865
23213
  var BARE_SEMVER = /^\d+\.\d+\.\d+$/;
@@ -22942,7 +23290,7 @@ function buildSeedStamp(d, cliVersion, nowIso) {
22942
23290
 
22943
23291
  // src/commands/platform/seed-stamp.ts
22944
23292
  async function seedStamp2(args) {
22945
- const readFileImpl = args.readFileImpl ?? ((p) => readFileSync21(p, "utf8"));
23293
+ const readFileImpl = args.readFileImpl ?? ((p) => readFileSync20(p, "utf8"));
22946
23294
  const readStampImpl = args.readStampImpl ?? readStamp;
22947
23295
  const writeStampImpl = args.writeStampImpl ?? ((stamp2, o) => writeStamp({ ...o, stamp: stamp2 }));
22948
23296
  const discoverImpl = args.discoverImpl ?? discoverStampStorage;
@@ -22988,15 +23336,15 @@ async function seedStamp2(args) {
22988
23336
  }
22989
23337
  var PlatformSeedStampCommand = class extends M8tCommand {
22990
23338
  static paths = [["platform", "seed-stamp"]];
22991
- static usage = Command37.Usage({
23339
+ static usage = Command39.Usage({
22992
23340
  description: "INTERNAL: write the initial installed-state stamp from a build-time install descriptor.",
22993
23341
  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'."
22994
23342
  });
22995
- descriptor = Option34.String("--descriptor", { description: "Path to install-descriptor.json (baked into the installer image)." });
22996
- subscription = Option34.String("--subscription");
22997
- resourceGroup = Option34.String("--resource-group");
22998
- miClientId = Option34.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
22999
- output = Option34.String("--output");
23343
+ descriptor = Option36.String("--descriptor", { description: "Path to install-descriptor.json (baked into the installer image)." });
23344
+ subscription = Option36.String("--subscription");
23345
+ resourceGroup = Option36.String("--resource-group");
23346
+ miClientId = Option36.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
23347
+ output = Option36.String("--output");
23000
23348
  async executeCommand() {
23001
23349
  const mode = resolveOutputMode(this.output, this.context.stdout);
23002
23350
  const need = (v, flag) => {
@@ -23009,7 +23357,7 @@ var PlatformSeedStampCommand = class extends M8tCommand {
23009
23357
  this.context.stderr.write(`${m}
23010
23358
  `);
23011
23359
  };
23012
- const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential2({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential18();
23360
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential4({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential20();
23013
23361
  const result2 = await seedStamp2({
23014
23362
  descriptorPath: need(this.descriptor, "--descriptor"),
23015
23363
  credential: credential2,
@@ -23027,105 +23375,9 @@ var PlatformSeedStampCommand = class extends M8tCommand {
23027
23375
  }
23028
23376
  };
23029
23377
 
23030
- // src/commands/platform/stamp-build.ts
23031
- import { Command as Command38, Option as Option35 } from "clipanion";
23032
- import { DefaultAzureCredential as DefaultAzureCredential19, ManagedIdentityCredential as ManagedIdentityCredential3 } from "@azure/identity";
23033
- init_errors();
23034
-
23035
- // src/lib/platform-stamp-build.ts
23036
- var BARE_SEMVER2 = /^\d+\.\d+\.\d+$/;
23037
- function buildRunningBuildStamp(input) {
23038
- if (!BARE_SEMVER2.test(input.version)) {
23039
- throw new Error(
23040
- `platform version must be bare X.Y.Z, got "${input.version}" \u2014 a build tag cannot be represented as a version.`
23041
- );
23042
- }
23043
- return {
23044
- schemaVersion: 1,
23045
- platformVersion: platformTag(input.version),
23046
- previousPlatformVersion: input.previousPlatformVersion,
23047
- updatedAt: input.nowIso,
23048
- lastResult: "success",
23049
- cli: input.cliVersion,
23050
- origin: "cd",
23051
- components: {
23052
- gateway: { tag: input.gatewayTag, digest: input.gatewayDigest, state: "managed" },
23053
- codingAgent: { tag: "", digest: "", state: "external" },
23054
- azureExecutor: { tag: "", digest: "", state: "external" },
23055
- personas: input.personas,
23056
- infra: { treeSha: input.infraTreeSha },
23057
- ...input.brainSeeds ? { brainSeeds: input.brainSeeds } : {}
23058
- }
23059
- };
23060
- }
23061
-
23062
- // src/commands/platform/stamp-build.ts
23063
- var PlatformStampBuildCommand = class extends M8tCommand {
23064
- static paths = [["platform", "stamp-build"]];
23065
- static usage = Command38.Usage({
23066
- description: "Record the currently running build as this install's platform version.",
23067
- 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."
23068
- });
23069
- version = Option35.String("--version", { description: "The running build's own version, bare X.Y.Z." });
23070
- gatewayTag = Option35.String("--gateway-tag", { description: "The tag of the gateway image this build deployed." });
23071
- gatewayDigest = Option35.String("--gateway-digest", { description: "The digest of the gateway image this build deployed." });
23072
- subscription = Option35.String("--subscription");
23073
- resourceGroup = Option35.String("--resource-group");
23074
- miClientId = Option35.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
23075
- output = Option35.String("--output");
23076
- async executeCommand() {
23077
- const mode = resolveOutputMode(this.output, this.context.stdout);
23078
- const need = (v, flag) => {
23079
- if (typeof v !== "string" || v.trim().length === 0) {
23080
- throw new LocalCliError({ code: "PLATFORM_STAMP_BUILD_ARG_MISSING", message: `${flag} is required.` });
23081
- }
23082
- return v.trim();
23083
- };
23084
- const onProgress = (m) => {
23085
- this.context.stderr.write(`${m}
23086
- `);
23087
- };
23088
- const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential3({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential19();
23089
- const ctx = {
23090
- credential: credential2,
23091
- subscriptionId: need(this.subscription, "--subscription"),
23092
- resourceGroup: need(this.resourceGroup, "--resource-group")
23093
- };
23094
- const { accountName } = await discoverStampStorage(ctx);
23095
- const existing = await readStampOutcome(ctx);
23096
- if (existing.source === "unreadable") {
23097
- throw new LocalCliError({
23098
- code: "PLATFORM_STAMP_BUILD_PRIOR_UNREADABLE",
23099
- message: "Could not read the existing platform stamp; refusing to overwrite it blind.",
23100
- 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."
23101
- });
23102
- }
23103
- const previousPlatformVersion = existing.source === "explicit" ? existing.stamp.platformVersion : null;
23104
- const priorComponents = existing.source === "explicit" ? existing.stamp.components : void 0;
23105
- const stamp = buildRunningBuildStamp({
23106
- version: need(this.version, "--version"),
23107
- gatewayTag: need(this.gatewayTag, "--gateway-tag"),
23108
- gatewayDigest: this.gatewayDigest ?? "",
23109
- previousPlatformVersion,
23110
- cliVersion: CLI_VERSION,
23111
- nowIso: (/* @__PURE__ */ new Date()).toISOString(),
23112
- personas: priorComponents?.personas ?? {},
23113
- infraTreeSha: priorComponents?.infra.treeSha ?? "",
23114
- ...priorComponents?.brainSeeds ? { brainSeeds: priorComponents.brainSeeds } : {}
23115
- });
23116
- await writeStamp({ ...ctx, stamp, onProgress });
23117
- const result2 = { platformVersion: stamp.platformVersion, previous: previousPlatformVersion, storageAccount: accountName };
23118
- this.context.stdout.write(
23119
- mode === "json" ? renderJson(result2) + "\n" : `platform stamp: ${result2.platformVersion} (previous ${result2.previous ?? "none"}, storage ${result2.storageAccount})
23120
- `
23121
- );
23122
- return 0;
23123
- }
23124
- };
23125
-
23126
23378
  // src/commands/platform/policy.ts
23127
- import { Command as Command39, Option as Option36 } from "clipanion";
23128
- import { DefaultAzureCredential as DefaultAzureCredential20, ManagedIdentityCredential as ManagedIdentityCredential4 } from "@azure/identity";
23379
+ import { Command as Command40, Option as Option37 } from "clipanion";
23380
+ import { DefaultAzureCredential as DefaultAzureCredential21, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
23129
23381
  init_errors();
23130
23382
 
23131
23383
  // src/lib/platform-policy.ts
@@ -23180,16 +23432,16 @@ async function readPolicy(opts) {
23180
23432
  // src/commands/platform/policy.ts
23181
23433
  var PlatformPolicyCommand = class extends M8tCommand {
23182
23434
  static paths = [["platform", "policy"]];
23183
- static usage = Command39.Usage({
23435
+ static usage = Command40.Usage({
23184
23436
  description: "Show or set how this install handles available platform updates.",
23185
23437
  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.",
23186
23438
  examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
23187
23439
  });
23188
- set = Option36.String("--set", { description: "notify-only | auto-critical | auto-all" });
23189
- subscription = Option36.String("--subscription");
23190
- resourceGroup = Option36.String("--resource-group");
23191
- miClientId = Option36.String("--mi-client-id");
23192
- output = Option36.String("--output");
23440
+ set = Option37.String("--set", { description: "notify-only | auto-critical | auto-all" });
23441
+ subscription = Option37.String("--subscription");
23442
+ resourceGroup = Option37.String("--resource-group");
23443
+ miClientId = Option37.String("--mi-client-id");
23444
+ output = Option37.String("--output");
23193
23445
  async executeCommand() {
23194
23446
  const mode = resolveOutputMode(this.output, this.context.stdout);
23195
23447
  const need = (v, flag) => {
@@ -23202,7 +23454,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
23202
23454
  this.context.stderr.write(`${m}
23203
23455
  `);
23204
23456
  };
23205
- const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential4({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential20();
23457
+ const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential21();
23206
23458
  const ctx = {
23207
23459
  credential: credential2,
23208
23460
  subscriptionId: need(this.subscription, "--subscription"),
@@ -23235,7 +23487,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
23235
23487
  };
23236
23488
 
23237
23489
  // src/commands/platform/enable-cost-report.ts
23238
- import { Command as Command40, Option as Option37 } from "clipanion";
23490
+ import { Command as Command41, Option as Option38 } from "clipanion";
23239
23491
 
23240
23492
  // src/lib/wire-gateway-acs.ts
23241
23493
  init_rbac();
@@ -23275,18 +23527,18 @@ async function wireGatewayForAcs(args) {
23275
23527
  init_errors();
23276
23528
  var PlatformEnableCostReportCommand = class extends M8tCommand {
23277
23529
  static paths = [["platform", "enable-cost-report"]];
23278
- static usage = Command40.Usage({
23530
+ static usage = Command41.Usage({
23279
23531
  description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
23280
23532
  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')."
23281
23533
  });
23282
- subscription = Option37.String("--subscription");
23283
- resourceGroup = Option37.String("--resource-group", {
23534
+ subscription = Option38.String("--subscription");
23535
+ resourceGroup = Option38.String("--resource-group", {
23284
23536
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
23285
23537
  });
23286
- acsEndpoint = Option37.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
23287
- acsSender = Option37.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
23288
- acsResourceId = Option37.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
23289
- output = Option37.String("--output");
23538
+ acsEndpoint = Option38.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
23539
+ acsSender = Option38.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
23540
+ acsResourceId = Option38.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
23541
+ output = Option38.String("--output");
23290
23542
  async executeCommand() {
23291
23543
  const mode = resolveOutputMode(
23292
23544
  this.output,
@@ -23360,45 +23612,45 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
23360
23612
  };
23361
23613
 
23362
23614
  // src/commands/platform/enable-auto-update.ts
23363
- import { Command as Command41, Option as Option38 } from "clipanion";
23615
+ import { Command as Command42, Option as Option39 } from "clipanion";
23364
23616
  import { confirm as confirm6 } from "@inquirer/prompts";
23365
- import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
23617
+ import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
23366
23618
  init_errors();
23367
23619
  var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
23368
23620
  static paths = [["platform", "enable-auto-update"]];
23369
- static usage = Command41.Usage({
23621
+ static usage = Command42.Usage({
23370
23622
  category: "Platform",
23371
23623
  description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
23372
23624
  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."
23373
23625
  });
23374
- subscription = Option38.String("--subscription");
23375
- resourceGroup = Option38.String("--resource-group", {
23626
+ subscription = Option39.String("--subscription");
23627
+ resourceGroup = Option39.String("--resource-group", {
23376
23628
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
23377
23629
  });
23378
- suffix = Option38.String("--suffix", {
23630
+ suffix = Option39.String("--suffix", {
23379
23631
  description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
23380
23632
  });
23381
- installerImage = Option38.String("--installer-image", {
23633
+ installerImage = Option39.String("--installer-image", {
23382
23634
  required: true,
23383
23635
  description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
23384
23636
  });
23385
- updateCron = Option38.String("--update-cron", {
23637
+ updateCron = Option39.String("--update-cron", {
23386
23638
  description: "Cron schedule for the updater job (bicep default applies when omitted)."
23387
23639
  });
23388
- channelUrl = Option38.String("--channel-url", {
23640
+ channelUrl = Option39.String("--channel-url", {
23389
23641
  description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
23390
23642
  });
23391
- location = Option38.String("--location", {
23643
+ location = Option39.String("--location", {
23392
23644
  description: "Region for the updater identity + job. Defaults to the resource group's existing resources."
23393
23645
  });
23394
- foundryTracing = Option38.String("--foundry-tracing", {
23646
+ foundryTracing = Option39.String("--foundry-tracing", {
23395
23647
  description: "project | account | skip. Pass the value the install was deployed with \u2014 omitting it lets the bicep default (project) switch tracing on."
23396
23648
  });
23397
- endpoint = Option38.String("--endpoint", {
23649
+ endpoint = Option39.String("--endpoint", {
23398
23650
  description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
23399
23651
  });
23400
- yes = Option38.Boolean("--yes", false);
23401
- output = Option38.String("--output");
23652
+ yes = Option39.Boolean("--yes", false);
23653
+ output = Option39.String("--output");
23402
23654
  async executeCommand() {
23403
23655
  const mode = resolveOutputMode(
23404
23656
  this.output,
@@ -23417,7 +23669,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
23417
23669
  resourceGroup: this.resourceGroup
23418
23670
  });
23419
23671
  const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
23420
- const credential2 = new DefaultAzureCredential21();
23672
+ const credential2 = new DefaultAzureCredential22();
23421
23673
  const account = await getAzAccount();
23422
23674
  const subscriptionId = this.subscription ?? account.subscriptionId;
23423
23675
  const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
@@ -23535,7 +23787,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
23535
23787
  };
23536
23788
 
23537
23789
  // src/commands/deploy.ts
23538
- import { Command as Command42, Option as Option39 } from "clipanion";
23790
+ import { Command as Command43, Option as Option40 } from "clipanion";
23539
23791
 
23540
23792
  // src/lib/app-reg.ts
23541
23793
  init_esm();
@@ -23869,7 +24121,7 @@ async function patchRedirectUris(appObjectId, fqdn) {
23869
24121
  init_errors();
23870
24122
 
23871
24123
  // src/lib/whatif.ts
23872
- import * as path33 from "path";
24124
+ import * as path32 from "path";
23873
24125
 
23874
24126
  // src/lib/identifier-patterns.ts
23875
24127
  var IDENTIFIER_PATTERNS = [
@@ -23925,7 +24177,7 @@ function deriveResourceType(resourceId) {
23925
24177
  return parts.join("/");
23926
24178
  }
23927
24179
  async function runWhatIf(opts) {
23928
- const bicepPath = path33.join(opts.repoRoot, "deploy", "main.bicep");
24180
+ const bicepPath = path32.join(opts.repoRoot, "deploy", "main.bicep");
23929
24181
  const out = await runAz([
23930
24182
  "deployment",
23931
24183
  "group",
@@ -24023,9 +24275,9 @@ function flattenDelta(delta, prefix = "") {
24023
24275
  const out = [];
24024
24276
  for (const d of delta) {
24025
24277
  const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
24026
- const path48 = prefix ? `${prefix}${segment}` : d.path;
24027
- if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path48));
24028
- else out.push({ ...d, path: path48 });
24278
+ const path47 = prefix ? `${prefix}${segment}` : d.path;
24279
+ if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path47));
24280
+ else out.push({ ...d, path: path47 });
24029
24281
  }
24030
24282
  return out;
24031
24283
  }
@@ -24075,52 +24327,63 @@ function classifyWhatIf(changes) {
24075
24327
  var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
24076
24328
  var DeployCommand = class extends M8tCommand {
24077
24329
  static paths = [["deploy"]];
24078
- static usage = Command42.Usage({
24330
+ static usage = Command43.Usage({
24079
24331
  description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
24080
24332
  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."
24081
24333
  });
24082
- subscription = Option39.String("--subscription");
24083
- resourceGroup = Option39.String("--resource-group", "rg-m8t-stack");
24084
- location = Option39.String("--location", "eastus");
24085
- suffix = Option39.String("--suffix", "");
24086
- imageRef = Option39.String("--image-ref", DEFAULT_IMAGE_REF);
24087
- acrPullIdentity = Option39.String("--acrpull-identity");
24088
- acrResourceId = Option39.String("--acr-resource-id");
24089
- foundryEndpoint = Option39.String("--foundry-endpoint");
24090
- foundryResourceId = Option39.String("--foundry-resource-id");
24091
- foundryTracing = Option39.String("--foundry-tracing");
24334
+ subscription = Option40.String("--subscription");
24335
+ resourceGroup = Option40.String("--resource-group", "rg-m8t-stack");
24336
+ location = Option40.String("--location", "eastus");
24337
+ suffix = Option40.String("--suffix", "");
24338
+ imageRef = Option40.String("--image-ref", DEFAULT_IMAGE_REF);
24339
+ acrPullIdentity = Option40.String("--acrpull-identity");
24340
+ acrResourceId = Option40.String("--acr-resource-id");
24341
+ foundryEndpoint = Option40.String("--foundry-endpoint");
24342
+ foundryResourceId = Option40.String("--foundry-resource-id");
24343
+ foundryTracing = Option40.String("--foundry-tracing");
24092
24344
  // project | account | skip (bicep default: project)
24093
- clientId = Option39.String("--client-id");
24094
- whatIf = Option39.Boolean("--what-if", false);
24345
+ clientId = Option40.String("--client-id");
24346
+ whatIf = Option40.Boolean("--what-if", false);
24095
24347
  // Only meaningful with --what-if. Routes the comparison through the
24096
24348
  // value-free renderers (see ./lib/whatif-redact.js) instead of the default
24097
24349
  // before/after renderer. Defaults false so a local, interactive run keeps
24098
24350
  // showing values — that is the whole diagnostic point of --what-if.
24099
24351
  // Automation that forwards this output anywhere non-private (a CI log, an
24100
24352
  // issue) MUST pass --redact.
24101
- redact = Option39.Boolean("--redact", false);
24102
- output = Option39.String("--output");
24353
+ redact = Option40.Boolean("--redact", false);
24354
+ output = Option40.String("--output");
24103
24355
  // Subscription-scoped role assignments. Omitted ⇒ the template default (true).
24104
24356
  // Pass false when deploying as a principal scoped to the resource group only:
24105
24357
  // it cannot deploy at subscription scope, and those assignments persist
24106
24358
  // idempotently from the initial deployment anyway.
24107
- assignSubscriptionRoles = Option39.String("--assign-subscription-roles");
24359
+ assignSubscriptionRoles = Option40.String("--assign-subscription-roles");
24360
+ /**
24361
+ * The installer image the updater Container-Apps Job runs.
24362
+ *
24363
+ * main.bicep only provisions the updater when an image is supplied
24364
+ * (`provisionUpdaterEffective = provisionUpdater && !empty(installerImage)`),
24365
+ * so a run that omits this describes a deployment WITHOUT an updater. For a
24366
+ * real deploy that is merely a skip; for a `--what-if` comparison it is worse
24367
+ * than that — the comparison proposes REMOVING an updater job that exists and
24368
+ * should, and reports it as drift on every single run.
24369
+ */
24370
+ installerImage = Option40.String("--installer-image");
24108
24371
  // Referee — all optional, undefined by default ⇒ the bicep defaults
24109
24372
  // apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
24110
24373
  // empty). Only pass these when explicitly enabling the referee exam stack.
24111
- gatewayCpu = Option39.String("--gateway-cpu");
24112
- gatewayMemory = Option39.String("--gateway-memory");
24113
- refereeEnabled = Option39.String("--referee-enabled");
24114
- refereeBrainRepos = Option39.String("--referee-brain-repos");
24115
- refereeFeedRepo = Option39.String("--referee-feed-repo");
24116
- refereeInstallationId = Option39.String("--referee-installation-id");
24117
- refereeWebhookHmacKvUri = Option39.String("--referee-webhook-hmac-kv-uri");
24118
- examKvUri = Option39.String("--exam-kv-uri");
24119
- examLaWorkspaceId = Option39.String("--exam-la-workspace-id");
24120
- brainEvalDeployment = Option39.String("--brain-eval-deployment");
24121
- brainAppLogin = Option39.String("--brain-app-login");
24122
- refereeCheckpointDir = Option39.String("--referee-checkpoint-dir");
24123
- examApiBase = Option39.String("--exam-api-base");
24374
+ gatewayCpu = Option40.String("--gateway-cpu");
24375
+ gatewayMemory = Option40.String("--gateway-memory");
24376
+ refereeEnabled = Option40.String("--referee-enabled");
24377
+ refereeBrainRepos = Option40.String("--referee-brain-repos");
24378
+ refereeFeedRepo = Option40.String("--referee-feed-repo");
24379
+ refereeInstallationId = Option40.String("--referee-installation-id");
24380
+ refereeWebhookHmacKvUri = Option40.String("--referee-webhook-hmac-kv-uri");
24381
+ examKvUri = Option40.String("--exam-kv-uri");
24382
+ examLaWorkspaceId = Option40.String("--exam-la-workspace-id");
24383
+ brainEvalDeployment = Option40.String("--brain-eval-deployment");
24384
+ brainAppLogin = Option40.String("--brain-app-login");
24385
+ refereeCheckpointDir = Option40.String("--referee-checkpoint-dir");
24386
+ examApiBase = Option40.String("--exam-api-base");
24124
24387
  async executeCommand() {
24125
24388
  const mode = resolveOutputMode(
24126
24389
  this.output,
@@ -24182,7 +24445,8 @@ var DeployCommand = class extends M8tCommand {
24182
24445
  brainAppLogin: this.brainAppLogin,
24183
24446
  refereeCheckpointDir: this.refereeCheckpointDir,
24184
24447
  examApiBase: this.examApiBase,
24185
- ...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) }
24448
+ ...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) },
24449
+ ...this.installerImage ? { installerImage: this.installerImage } : {}
24186
24450
  });
24187
24451
  const changes = await runWhatIf({ resourceGroup: this.resourceGroup, repoRoot: repoRoot2, params: params2 });
24188
24452
  const classified = classifyWhatIf(changes);
@@ -24250,7 +24514,8 @@ var DeployCommand = class extends M8tCommand {
24250
24514
  brainAppLogin: this.brainAppLogin,
24251
24515
  refereeCheckpointDir: this.refereeCheckpointDir,
24252
24516
  examApiBase: this.examApiBase,
24253
- ...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) }
24517
+ ...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) },
24518
+ ...this.installerImage ? { installerImage: this.installerImage } : {}
24254
24519
  });
24255
24520
  log("running Bicep deployment (~3-5 min)\u2026");
24256
24521
  const outputs = await runBicepDeployment({
@@ -24299,7 +24564,7 @@ var DeployCommand = class extends M8tCommand {
24299
24564
 
24300
24565
  // src/commands/eval/skill.ts
24301
24566
  import { spawnSync as spawnSync4 } from "child_process";
24302
- import { Command as Command43, Option as Option40 } from "clipanion";
24567
+ import { Command as Command44, Option as Option41 } from "clipanion";
24303
24568
  init_errors();
24304
24569
  var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
24305
24570
  var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
@@ -24324,14 +24589,14 @@ function parseVerdict(stdout) {
24324
24589
  }
24325
24590
  var EvalSkillCommand = class extends M8tCommand {
24326
24591
  static paths = [["eval", "skill"]];
24327
- static usage = Command43.Usage({
24592
+ static usage = Command44.Usage({
24328
24593
  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)."
24329
24594
  });
24330
- candidate = Option40.String();
24331
- skillsDir = Option40.String("--skills-dir");
24332
- noJudge = Option40.Boolean("--no-judge", false);
24333
- deployment = Option40.String("--deployment");
24334
- output = Option40.String("--output");
24595
+ candidate = Option41.String();
24596
+ skillsDir = Option41.String("--skills-dir");
24597
+ noJudge = Option41.Boolean("--no-judge", false);
24598
+ deployment = Option41.String("--deployment");
24599
+ output = Option41.String("--output");
24335
24600
  executeCommand() {
24336
24601
  return Promise.resolve(this._runCommand());
24337
24602
  }
@@ -24388,9 +24653,9 @@ var EvalSkillCommand = class extends M8tCommand {
24388
24653
 
24389
24654
  // src/commands/eval/exam.ts
24390
24655
  import { spawnSync as spawnSync5 } from "child_process";
24391
- import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync22, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
24392
- import { join as join31 } from "path";
24393
- import { Command as Command44, Option as Option41 } from "clipanion";
24656
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
24657
+ import { join as join30 } from "path";
24658
+ import { Command as Command45, Option as Option42 } from "clipanion";
24394
24659
  init_errors();
24395
24660
  init_esm();
24396
24661
  function parseArmToken(tok, opts) {
@@ -24571,9 +24836,9 @@ function resolveRefereeHome(refereeHomeOut, env) {
24571
24836
  return null;
24572
24837
  }
24573
24838
  function resolveTaskSetDir(base, worker, version) {
24574
- if (existsSync20(join31(base, version))) return version;
24839
+ if (existsSync20(join30(base, version))) return version;
24575
24840
  const prefixed = `${worker}-${version}`;
24576
- if (existsSync20(join31(base, prefixed))) return prefixed;
24841
+ if (existsSync20(join30(base, prefixed))) return prefixed;
24577
24842
  if (version === "latest" && existsSync20(base)) {
24578
24843
  const dirs = readdirSync3(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
24579
24844
  if (dirs.length > 0) return dirs[dirs.length - 1];
@@ -24589,11 +24854,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
24589
24854
  message: "cannot resolve the referee home to read the sealed-task manifest \u2014 set $REFEREE_HOME (mirroring the Python runner) so the feed can be redacted; refusing to emit a possibly-unredacted feed (Law-1)"
24590
24855
  });
24591
24856
  }
24592
- const versionDir = resolveTaskSetDir(join31(home, "tasksets", worker), worker, version);
24593
- const manifestPath = join31(home, "tasksets", worker, versionDir, "manifest.yaml");
24857
+ const versionDir = resolveTaskSetDir(join30(home, "tasksets", worker), worker, version);
24858
+ const manifestPath = join30(home, "tasksets", worker, versionDir, "manifest.yaml");
24594
24859
  let text;
24595
24860
  try {
24596
- text = readFileSync22(manifestPath, "utf-8");
24861
+ text = readFileSync21(manifestPath, "utf-8");
24597
24862
  } catch (e) {
24598
24863
  throw new LocalCliError({
24599
24864
  code: "EXAM_REDACTION_UNSAFE",
@@ -24620,24 +24885,24 @@ function buildPlan(args) {
24620
24885
  }
24621
24886
  var EvalExamCommand = class extends M8tCommand {
24622
24887
  static paths = [["eval", "exam"]];
24623
- static usage = Command44.Usage({
24888
+ static usage = Command45.Usage({
24624
24889
  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."
24625
24890
  });
24626
- worker = Option41.String();
24627
- arms = Option41.String("--arms");
24628
- taskSet = Option41.String("--task-set");
24629
- examType = Option41.String("--exam-type");
24630
- skill = Option41.String("--skill");
24631
- reps = Option41.String("-n,--reps");
24632
- probes = Option41.String("--probes");
24633
- pool = Option41.String("--pool");
24634
- out = Option41.String("--out");
24635
- dryRun = Option41.Boolean("--dry-run", false);
24636
- keepArms = Option41.Boolean("--keep-arms", false);
24637
- allowStub = Option41.Boolean("--allow-stub", false);
24638
- deployment = Option41.String("--deployment");
24639
- output = Option41.String("--output");
24640
- observeWaitS = Option41.String("--observe-wait-s");
24891
+ worker = Option42.String();
24892
+ arms = Option42.String("--arms");
24893
+ taskSet = Option42.String("--task-set");
24894
+ examType = Option42.String("--exam-type");
24895
+ skill = Option42.String("--skill");
24896
+ reps = Option42.String("-n,--reps");
24897
+ probes = Option42.String("--probes");
24898
+ pool = Option42.String("--pool");
24899
+ out = Option42.String("--out");
24900
+ dryRun = Option42.Boolean("--dry-run", false);
24901
+ keepArms = Option42.Boolean("--keep-arms", false);
24902
+ allowStub = Option42.Boolean("--allow-stub", false);
24903
+ deployment = Option42.String("--deployment");
24904
+ output = Option42.String("--output");
24905
+ observeWaitS = Option42.String("--observe-wait-s");
24641
24906
  async executeCommand() {
24642
24907
  await Promise.resolve();
24643
24908
  const worker = typeof this.worker === "string" ? this.worker : void 0;
@@ -24714,9 +24979,9 @@ var EvalExamCommand = class extends M8tCommand {
24714
24979
  if (out !== void 0) {
24715
24980
  const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
24716
24981
  mkdirSync6(out, { recursive: true });
24717
- writeFileSync8(join31(out, "verdict.json"), JSON.stringify(verdict, null, 2));
24982
+ writeFileSync8(join30(out, "verdict.json"), JSON.stringify(verdict, null, 2));
24718
24983
  const feed = redactForFeed(verdict, sealedTaskIds);
24719
- writeFileSync8(join31(out, "feed.json"), JSON.stringify(feed, null, 2));
24984
+ writeFileSync8(join30(out, "feed.json"), JSON.stringify(feed, null, 2));
24720
24985
  }
24721
24986
  if (mode === "json") {
24722
24987
  this.context.stdout.write(renderJson(verdict) + "\n");
@@ -24752,10 +25017,10 @@ var EvalExamCommand = class extends M8tCommand {
24752
25017
  };
24753
25018
 
24754
25019
  // src/commands/version.ts
24755
- import { Command as Command45, Option as Option42 } from "clipanion";
25020
+ import { Command as Command46, Option as Option43 } from "clipanion";
24756
25021
  var VersionCommand = class extends M8tCommand {
24757
25022
  static paths = [["version"], ["--version"], ["-v"]];
24758
- static usage = Command45.Usage({
25023
+ static usage = Command46.Usage({
24759
25024
  description: "Print the CLI version.",
24760
25025
  details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
24761
25026
  examples: [
@@ -24763,8 +25028,8 @@ var VersionCommand = class extends M8tCommand {
24763
25028
  ["Print as JSON", "$0 version --output json"]
24764
25029
  ]
24765
25030
  });
24766
- output = Option42.String("--output", { description: "pretty | json | auto (default)" });
24767
- verbose = Option42.Boolean("--verbose", false);
25031
+ output = Option43.String("--output", { description: "pretty | json | auto (default)" });
25032
+ verbose = Option43.Boolean("--verbose", false);
24768
25033
  executeCommand() {
24769
25034
  const mode = resolveOutputMode(
24770
25035
  this.output ?? "auto",
@@ -24795,18 +25060,18 @@ var VersionCommand = class extends M8tCommand {
24795
25060
  };
24796
25061
 
24797
25062
  // src/commands/whoami.ts
24798
- import { Command as Command46, Option as Option43 } from "clipanion";
25063
+ import { Command as Command47, Option as Option44 } from "clipanion";
24799
25064
  var WhoamiCommand = class extends M8tCommand {
24800
25065
  static paths = [["whoami"]];
24801
- static usage = Command46.Usage({
25066
+ static usage = Command47.Usage({
24802
25067
  description: "Show your identity + the gateway you'll talk to. Probes the backend."
24803
25068
  });
24804
- output = Option43.String("--output");
24805
- verbose = Option43.Boolean("--verbose", false);
24806
- subscription = Option43.String("--subscription", {
25069
+ output = Option44.String("--output");
25070
+ verbose = Option44.Boolean("--verbose", false);
25071
+ subscription = Option44.String("--subscription", {
24807
25072
  description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
24808
25073
  });
24809
- resourceGroup = Option43.String("--resource-group", {
25074
+ resourceGroup = Option44.String("--resource-group", {
24810
25075
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
24811
25076
  });
24812
25077
  async executeCommand() {
@@ -24871,7 +25136,7 @@ var WhoamiCommand = class extends M8tCommand {
24871
25136
  };
24872
25137
 
24873
25138
  // src/commands/status.ts
24874
- import { Command as Command47, Option as Option44 } from "clipanion";
25139
+ import { Command as Command48, Option as Option45 } from "clipanion";
24875
25140
 
24876
25141
  // src/lib/azd.ts
24877
25142
  init_errors();
@@ -24936,10 +25201,10 @@ async function resolveLocalContext() {
24936
25201
  // src/commands/status.ts
24937
25202
  var StatusCommand = class extends M8tCommand {
24938
25203
  static paths = [["status"]];
24939
- static usage = Command47.Usage({
25204
+ static usage = Command48.Usage({
24940
25205
  description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
24941
25206
  });
24942
- output = Option44.String("--output");
25207
+ output = Option45.String("--output");
24943
25208
  async executeCommand() {
24944
25209
  const mode = resolveOutputMode(
24945
25210
  this.output,
@@ -24977,11 +25242,11 @@ var StatusCommand = class extends M8tCommand {
24977
25242
  };
24978
25243
 
24979
25244
  // src/commands/doctor.ts
24980
- import { Command as Command48, Option as Option45 } from "clipanion";
24981
- import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
24982
- import * as fs31 from "fs";
25245
+ import { Command as Command49, Option as Option46 } from "clipanion";
25246
+ import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
25247
+ import * as fs30 from "fs";
24983
25248
  import * as os12 from "os";
24984
- import * as path34 from "path";
25249
+ import * as path33 from "path";
24985
25250
 
24986
25251
  // src/lib/doctor-checks.ts
24987
25252
  function checkAzSignedIn(az) {
@@ -25460,40 +25725,40 @@ async function resolveAccountLocation(accountId) {
25460
25725
  }
25461
25726
  }
25462
25727
  function probeRepoRoot() {
25463
- const marker = path34.join(os12.homedir(), ".m8t", "repo-root");
25464
- if (!fs31.existsSync(marker)) {
25728
+ const marker = path33.join(os12.homedir(), ".m8t", "repo-root");
25729
+ if (!fs30.existsSync(marker)) {
25465
25730
  return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
25466
25731
  }
25467
25732
  let repoPath;
25468
25733
  try {
25469
- repoPath = fs31.readFileSync(marker, "utf8").trim();
25734
+ repoPath = fs30.readFileSync(marker, "utf8").trim();
25470
25735
  } catch {
25471
25736
  return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
25472
25737
  }
25473
25738
  if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
25474
25739
  let isDir = false;
25475
25740
  try {
25476
- isDir = fs31.statSync(repoPath).isDirectory();
25741
+ isDir = fs30.statSync(repoPath).isDirectory();
25477
25742
  } catch {
25478
25743
  isDir = false;
25479
25744
  }
25480
- const isGitCheckout = isDir && fs31.existsSync(path34.join(repoPath, ".git"));
25745
+ const isGitCheckout = isDir && fs30.existsSync(path33.join(repoPath, ".git"));
25481
25746
  return { markerExists: true, path: repoPath, isDir, isGitCheckout };
25482
25747
  }
25483
25748
  function probeLegacyStateDir() {
25484
25749
  return {
25485
- legacyExists: fs31.existsSync(path34.join(os12.homedir(), ".m8t-stack")),
25486
- currentExists: fs31.existsSync(path34.join(os12.homedir(), ".m8t"))
25750
+ legacyExists: fs30.existsSync(path33.join(os12.homedir(), ".m8t-stack")),
25751
+ currentExists: fs30.existsSync(path33.join(os12.homedir(), ".m8t"))
25487
25752
  };
25488
25753
  }
25489
25754
  var DoctorCommand = class extends M8tCommand {
25490
25755
  static paths = [["doctor"]];
25491
- static usage = Command48.Usage({
25756
+ static usage = Command49.Usage({
25492
25757
  description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
25493
25758
  });
25494
- output = Option45.String("--output");
25495
- agent = Option45.String("--agent");
25496
- resourceGroup = Option45.String("--resource-group", {
25759
+ output = Option46.String("--output");
25760
+ agent = Option46.String("--agent");
25761
+ resourceGroup = Option46.String("--resource-group", {
25497
25762
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
25498
25763
  });
25499
25764
  async executeCommand() {
@@ -25566,7 +25831,7 @@ var DoctorCommand = class extends M8tCommand {
25566
25831
  let kvStatus = 0;
25567
25832
  if (kv) {
25568
25833
  try {
25569
- const token = await new DefaultAzureCredential22().getToken("https://vault.azure.net/.default");
25834
+ const token = await new DefaultAzureCredential23().getToken("https://vault.azure.net/.default");
25570
25835
  const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
25571
25836
  headers: { Authorization: `Bearer ${token.token}` }
25572
25837
  });
@@ -25586,7 +25851,7 @@ var DoctorCommand = class extends M8tCommand {
25586
25851
  if (typeof this.agent === "string" && this.agent) {
25587
25852
  checking(`delivery grant for ${this.agent}`);
25588
25853
  try {
25589
- const credential2 = new DefaultAzureCredential22();
25854
+ const credential2 = new DefaultAzureCredential23();
25590
25855
  const cur = await getAgentVersion({
25591
25856
  credential: credential2,
25592
25857
  projectEndpoint: foundry.projectEndpoint,
@@ -25629,11 +25894,11 @@ var DoctorCommand = class extends M8tCommand {
25629
25894
  };
25630
25895
 
25631
25896
  // src/commands/prereqs.ts
25632
- import { Command as Command49, Option as Option46 } from "clipanion";
25897
+ import { Command as Command50, Option as Option47 } from "clipanion";
25633
25898
  init_errors();
25634
25899
 
25635
25900
  // src/lib/prereq-deps.ts
25636
- import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
25901
+ import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
25637
25902
 
25638
25903
  // src/lib/bootstrap-preflight.ts
25639
25904
  var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
@@ -25745,7 +26010,7 @@ function buildPrereqDeps(opts = {}) {
25745
26010
  probeRedirectUri,
25746
26011
  fixFoundryAccess,
25747
26012
  fixKeyVaultAccess,
25748
- credential: () => credentialSingleton2 ??= new DefaultAzureCredential23()
26013
+ credential: () => credentialSingleton2 ??= new DefaultAzureCredential24()
25749
26014
  };
25750
26015
  }
25751
26016
 
@@ -26335,7 +26600,7 @@ function renderVerdict(v) {
26335
26600
  }
26336
26601
  var PrereqsCommand = class extends M8tCommand {
26337
26602
  static paths = [["prereqs"]];
26338
- static usage = Command49.Usage({
26603
+ static usage = Command50.Usage({
26339
26604
  description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
26340
26605
  details: "Runs one of two phases. With no platform discoverable it checks INSTALL prerequisites: your Azure sign-in, subscription and directory rights, resource-provider registrations, soft-deleted Cognitive Services accounts (they hold their model quota until purged), and model quota. With a platform live it checks USAGE prerequisites for the signed-in account: the sign-in redirect URI, Foundry data-plane access, and Key Vault secrets access. Pass --fix to repair what is repairable, and --fix --for <upn> to set a teammate up (usage phase only). Read-only without --fix.",
26341
26606
  examples: [
@@ -26346,15 +26611,15 @@ var PrereqsCommand = class extends M8tCommand {
26346
26611
  ["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
26347
26612
  ]
26348
26613
  });
26349
- fix = Option46.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
26350
- for_ = Option46.String("--for", { description: "UPN or object id of another person. Usage phase only." });
26351
- phase = Option46.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
26352
- region = Option46.String("--region", { description: "Target region. Enables the install-phase quota check, and judges soft-deleted accounts against the region you will install into (without it, they only warn)." });
26353
- model = Option46.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
26354
- clientId = Option46.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
26355
- subscription = Option46.String("--subscription");
26356
- resourceGroup = Option46.String("--resource-group");
26357
- output = Option46.String("--output");
26614
+ fix = Option47.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
26615
+ for_ = Option47.String("--for", { description: "UPN or object id of another person. Usage phase only." });
26616
+ phase = Option47.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
26617
+ region = Option47.String("--region", { description: "Target region. Enables the install-phase quota check, and judges soft-deleted accounts against the region you will install into (without it, they only warn)." });
26618
+ model = Option47.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
26619
+ clientId = Option47.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
26620
+ subscription = Option47.String("--subscription");
26621
+ resourceGroup = Option47.String("--resource-group");
26622
+ output = Option47.String("--output");
26358
26623
  async executeCommand() {
26359
26624
  const mode = resolveOutputMode(this.output, this.context.stdout);
26360
26625
  const json = mode === "json";
@@ -26410,18 +26675,18 @@ var PrereqsCommand = class extends M8tCommand {
26410
26675
  };
26411
26676
 
26412
26677
  // src/commands/switch.ts
26413
- import { Command as Command50, Option as Option47 } from "clipanion";
26678
+ import { Command as Command51, Option as Option48 } from "clipanion";
26414
26679
 
26415
26680
  // src/lib/profiles.ts
26416
- import * as fs32 from "fs/promises";
26417
- import * as path35 from "path";
26681
+ import * as fs31 from "fs/promises";
26682
+ import * as path34 from "path";
26418
26683
  import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
26419
26684
  function profilesDir() {
26420
26685
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
26421
- return path35.join(home, ".m8t", "profiles");
26686
+ return path34.join(home, ".m8t", "profiles");
26422
26687
  }
26423
26688
  function getProfilePath(name) {
26424
- return path35.join(profilesDir(), `${path35.basename(name)}.yaml`);
26689
+ return path34.join(profilesDir(), `${path34.basename(name)}.yaml`);
26425
26690
  }
26426
26691
  function slugifyTenant(domainOrId) {
26427
26692
  const base = domainOrId.split(".")[0].toLowerCase();
@@ -26430,7 +26695,7 @@ function slugifyTenant(domainOrId) {
26430
26695
  }
26431
26696
  async function listProfiles() {
26432
26697
  try {
26433
- const entries = await fs32.readdir(profilesDir());
26698
+ const entries = await fs31.readdir(profilesDir());
26434
26699
  return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
26435
26700
  } catch (e) {
26436
26701
  if (e.code === "ENOENT") return [];
@@ -26439,7 +26704,7 @@ async function listProfiles() {
26439
26704
  }
26440
26705
  async function readProfile(name) {
26441
26706
  try {
26442
- const raw = await fs32.readFile(getProfilePath(name), "utf8");
26707
+ const raw = await fs31.readFile(getProfilePath(name), "utf8");
26443
26708
  const parsed = parseYaml12(raw);
26444
26709
  if (!parsed || typeof parsed !== "object") return null;
26445
26710
  return parsed;
@@ -26450,14 +26715,14 @@ async function readProfile(name) {
26450
26715
  }
26451
26716
  async function exists(p) {
26452
26717
  try {
26453
- await fs32.stat(p);
26718
+ await fs31.stat(p);
26454
26719
  return true;
26455
26720
  } catch {
26456
26721
  return false;
26457
26722
  }
26458
26723
  }
26459
26724
  async function saveProfile(p) {
26460
- await fs32.mkdir(profilesDir(), { recursive: true });
26725
+ await fs31.mkdir(profilesDir(), { recursive: true });
26461
26726
  let name = p.name;
26462
26727
  let n = 2;
26463
26728
  while (await exists(getProfilePath(name))) {
@@ -26466,8 +26731,8 @@ async function saveProfile(p) {
26466
26731
  }
26467
26732
  const target = getProfilePath(name);
26468
26733
  const tmp = `${target}.tmp`;
26469
- await fs32.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
26470
- await fs32.rename(tmp, target);
26734
+ await fs31.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
26735
+ await fs31.rename(tmp, target);
26471
26736
  return name;
26472
26737
  }
26473
26738
 
@@ -26550,14 +26815,14 @@ async function profileSwitch(name, asName) {
26550
26815
  init_errors();
26551
26816
  var SwitchCommand = class extends M8tCommand {
26552
26817
  static paths = [["switch"]];
26553
- static usage = Command50.Usage({
26818
+ static usage = Command51.Usage({
26554
26819
  description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
26555
26820
  });
26556
- profile = Option47.String({ required: false });
26557
- subscription = Option47.String("--subscription");
26558
- list = Option47.Boolean("--list", false);
26559
- as = Option47.String("--as");
26560
- output = Option47.String("--output");
26821
+ profile = Option48.String({ required: false });
26822
+ subscription = Option48.String("--subscription");
26823
+ list = Option48.Boolean("--list", false);
26824
+ as = Option48.String("--as");
26825
+ output = Option48.String("--output");
26561
26826
  async executeCommand() {
26562
26827
  const mode = resolveOutputMode(
26563
26828
  this.output,
@@ -26614,7 +26879,7 @@ var SwitchCommand = class extends M8tCommand {
26614
26879
 
26615
26880
  // src/commands/open.ts
26616
26881
  import { spawn as spawn5 } from "child_process";
26617
- import { Command as Command51, Option as Option48 } from "clipanion";
26882
+ import { Command as Command52, Option as Option49 } from "clipanion";
26618
26883
 
26619
26884
  // src/lib/open-targets.ts
26620
26885
  init_errors();
@@ -26660,14 +26925,14 @@ function openUrl(url) {
26660
26925
  }
26661
26926
  var OpenCommand = class extends M8tCommand {
26662
26927
  static paths = [["open"]];
26663
- static usage = Command51.Usage({
26928
+ static usage = Command52.Usage({
26664
26929
  description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
26665
26930
  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)."
26666
26931
  });
26667
- target = Option48.String({ required: false });
26668
- print = Option48.Boolean("--print", false);
26669
- output = Option48.String("--output");
26670
- resourceGroup = Option48.String("--resource-group", {
26932
+ target = Option49.String({ required: false });
26933
+ print = Option49.Boolean("--print", false);
26934
+ output = Option49.String("--output");
26935
+ resourceGroup = Option49.String("--resource-group", {
26671
26936
  description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
26672
26937
  });
26673
26938
  async executeCommand() {
@@ -26711,7 +26976,7 @@ var OpenCommand = class extends M8tCommand {
26711
26976
  };
26712
26977
 
26713
26978
  // src/commands/dream/run.ts
26714
- import { Command as Command52, Option as Option49 } from "clipanion";
26979
+ import { Command as Command53, Option as Option50 } from "clipanion";
26715
26980
  import { AzureCliCredential } from "@azure/identity";
26716
26981
  import { TableClient as TableClient7 } from "@azure/data-tables";
26717
26982
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
@@ -27756,7 +28021,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
27756
28021
  // ../../packages/brain/engine/dist/esm/dream/writer.js
27757
28022
  var API2 = "https://api.github.com";
27758
28023
  var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
27759
- var isAllowedWritePath = (path48, allowedPrefix) => allowedPrefix.test(path48) && !path48.split("/").includes("..");
28024
+ var isAllowedWritePath = (path47, allowedPrefix) => allowedPrefix.test(path47) && !path47.split("/").includes("..");
27760
28025
  var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
27761
28026
  function parseFrontmatter(content) {
27762
28027
  const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
@@ -27799,8 +28064,8 @@ function makeGitDataWriter(args) {
27799
28064
  if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
27800
28065
  throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
27801
28066
  }
27802
- async function gh(token, method, path48, body) {
27803
- const res = await doFetch(`${API2}/repos/${repoPath}${path48}`, {
28067
+ async function gh(token, method, path47, body) {
28068
+ const res = await doFetch(`${API2}/repos/${repoPath}${path47}`, {
27804
28069
  method,
27805
28070
  headers: {
27806
28071
  Authorization: `Bearer ${token}`,
@@ -27813,8 +28078,8 @@ function makeGitDataWriter(args) {
27813
28078
  const text = await res.text();
27814
28079
  return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
27815
28080
  }
27816
- async function readFileWith(token, path48) {
27817
- const encodedPath = path48.split("/").map(encodeURIComponent).join("/");
28081
+ async function readFileWith(token, path47) {
28082
+ const encodedPath = path47.split("/").map(encodeURIComponent).join("/");
27818
28083
  const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
27819
28084
  if (r.status === 404)
27820
28085
  return null;
@@ -27830,9 +28095,9 @@ function makeGitDataWriter(args) {
27830
28095
  throw new Error(`fetchTip: HTTP ${String(r.status)}`);
27831
28096
  return r.json.object.sha;
27832
28097
  },
27833
- async readFile(path48) {
28098
+ async readFile(path47) {
27834
28099
  const token = await args.mintToken();
27835
- return readFileWith(token, path48);
28100
+ return readFileWith(token, path47);
27836
28101
  },
27837
28102
  async listMemory() {
27838
28103
  const token = await args.mintToken();
@@ -27841,9 +28106,9 @@ function makeGitDataWriter(args) {
27841
28106
  return [];
27842
28107
  const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
27843
28108
  const out = [];
27844
- for (const path48 of paths) {
27845
- const content = await readFileWith(token, path48);
27846
- out.push({ path: path48, frontmatter: content ? parseFrontmatter(content) : {} });
28109
+ for (const path47 of paths) {
28110
+ const content = await readFileWith(token, path47);
28111
+ out.push({ path: path47, frontmatter: content ? parseFrontmatter(content) : {} });
27847
28112
  }
27848
28113
  return out;
27849
28114
  },
@@ -28282,7 +28547,7 @@ async function loadMemoryContext(brain) {
28282
28547
  return {
28283
28548
  files,
28284
28549
  indexEntries,
28285
- originOf: (path48) => originByPath.get(path48) ?? "worker"
28550
+ originOf: (path47) => originByPath.get(path47) ?? "worker"
28286
28551
  };
28287
28552
  }
28288
28553
 
@@ -28303,14 +28568,14 @@ function checkEvidenceMembership(delta, harvested) {
28303
28568
  }
28304
28569
  var BatchAbort = class extends Error {
28305
28570
  path;
28306
- constructor(path48, message) {
28571
+ constructor(path47, message) {
28307
28572
  super(message);
28308
- this.path = path48;
28573
+ this.path = path47;
28309
28574
  this.name = "BatchAbort";
28310
28575
  }
28311
28576
  };
28312
- var isIndexFile = (path48) => /(^|\/)[^/]*_index\.md$/.test(path48);
28313
- var isMemoryIndex = (path48) => path48 === "memory/MEMORY.md";
28577
+ var isIndexFile = (path47) => /(^|\/)[^/]*_index\.md$/.test(path47);
28578
+ var isMemoryIndex = (path47) => path47 === "memory/MEMORY.md";
28314
28579
  function targetPath(delta) {
28315
28580
  switch (delta.verb) {
28316
28581
  case "new":
@@ -28327,29 +28592,29 @@ function targetPath(delta) {
28327
28592
  }
28328
28593
  async function checkOriginTier(delta, lookup) {
28329
28594
  const evidence = evidenceOf(delta) ?? [];
28330
- const path48 = targetPath(delta);
28331
- if (path48 === null)
28595
+ const path47 = targetPath(delta);
28596
+ if (path47 === null)
28332
28597
  return { ok: true };
28333
- if (isIndexFile(path48) && !isMemoryIndex(path48)) {
28334
- return { ok: false, flagged: { kind: "flagged", path: path48, tension: `refused: ${path48} is an index file (only memory/MEMORY.md is editable)`, evidence } };
28598
+ if (isIndexFile(path47) && !isMemoryIndex(path47)) {
28599
+ return { ok: false, flagged: { kind: "flagged", path: path47, tension: `refused: ${path47} is an index file (only memory/MEMORY.md is editable)`, evidence } };
28335
28600
  }
28336
- const target = await lookup(path48);
28337
- const isStructural = path48.startsWith("references/");
28601
+ const target = await lookup(path47);
28602
+ const isStructural = path47.startsWith("references/");
28338
28603
  const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
28339
28604
  const editable = origin === "worker" || origin === "dream";
28340
28605
  if (editable)
28341
28606
  return { ok: true };
28342
28607
  const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
28343
28608
  if (removesOperatorTarget) {
28344
- throw new BatchAbort(path48, `supersede/retract OLD target ${path48} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
28609
+ throw new BatchAbort(path47, `supersede/retract OLD target ${path47} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
28345
28610
  }
28346
- return { ok: false, flagged: { kind: "flagged", path: path48, tension: `operator-origin (${origin}); flag-only`, evidence } };
28611
+ return { ok: false, flagged: { kind: "flagged", path: path47, tension: `operator-origin (${origin}); flag-only`, evidence } };
28347
28612
  }
28348
28613
  var ALLOWED_MEMORY = /^memory\/.+\.md$/;
28349
28614
  var ALLOWED_INBOX = /^inbox\//;
28350
28615
  var ALLOWED_QUARANTINE = /^quarantine\//;
28351
28616
  var ALLOWED_FLAG = /^memory\//;
28352
- var hasTraversal = (path48) => path48.split("/").includes("..");
28617
+ var hasTraversal = (path47) => path47.split("/").includes("..");
28353
28618
  function checkPathAllowed(delta) {
28354
28619
  const evidence = evidenceOf(delta) ?? [];
28355
28620
  const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
@@ -28456,7 +28721,7 @@ ${inject}
28456
28721
  ---
28457
28722
  ` + body.slice(m[0].length);
28458
28723
  }
28459
- var indexLine = (path48, title, date, tags) => `- \`${path48}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
28724
+ var indexLine = (path47, title, date, tags) => `- \`${path47}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
28460
28725
  function splitIndex(index) {
28461
28726
  const all = index.split("\n");
28462
28727
  const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
@@ -28629,16 +28894,16 @@ function defaultDreamSeams(opts = {}) {
28629
28894
  const mem = await memory(deps.brain);
28630
28895
  const harvested = harvestedIds(input);
28631
28896
  const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
28632
- const lookup = (path48) => Promise.resolve(mem.files.find((f) => f.path === path48) ?? null);
28897
+ const lookup = (path47) => Promise.resolve(mem.files.find((f) => f.path === path47) ?? null);
28633
28898
  const digest = [...pendingRejected];
28634
28899
  let changes = [];
28635
28900
  let indexEdit;
28636
28901
  try {
28637
28902
  for (const raw of deltas) {
28638
28903
  const delta = forcePolicyQuarantine(raw);
28639
- const path48 = checkPathAllowed(delta);
28640
- if (!path48.ok) {
28641
- digest.push(path48.rejected);
28904
+ const path47 = checkPathAllowed(delta);
28905
+ if (!path47.ok) {
28906
+ digest.push(path47.rejected);
28642
28907
  continue;
28643
28908
  }
28644
28909
  const ev = checkEvidenceMembership(delta, harvested);
@@ -28892,21 +29157,28 @@ var ARM4 = "https://management.azure.com";
28892
29157
  var ARM_SCOPE8 = "https://management.azure.com/.default";
28893
29158
  var STORAGE_API2 = "2023-05-01";
28894
29159
  var LA_API = "2023-09-01";
29160
+ var tagOf = (a, key2) => a.tags?.[key2] ?? a.properties?.tags?.[key2];
29161
+ function selectLedgerAccount(accounts, storageAccount) {
29162
+ if (storageAccount) return accounts.find((a) => a.name === storageAccount);
29163
+ const managed = accounts.filter((a) => tagOf(a, "managedBy") === "m8t");
29164
+ return managed.find((a) => tagOf(a, "m8t") === "storage") ?? managed.at(0) ?? accounts.find((a) => tagOf(a, "m8t:role") === "ledger") ?? (accounts.length === 1 ? accounts[0] : void 0);
29165
+ }
28895
29166
  async function discoverLedgerResources(opts) {
28896
- const { credential: credential2, subscriptionId, resourceGroup } = opts;
29167
+ const { credential: credential2, subscriptionId, resourceGroup, storageAccount } = opts;
28897
29168
  const storage = await authedJsonPagedValues({
28898
29169
  credential: credential2,
28899
29170
  scope: ARM_SCOPE8,
28900
29171
  url: `${ARM4}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Storage/storageAccounts?api-version=${STORAGE_API2}`
28901
29172
  });
28902
29173
  const accounts = storage.filter((a) => a.properties?.primaryEndpoints?.table);
28903
- const ledgerAcct = accounts.find((a) => (a.tags?.["m8t:role"] ?? a.properties?.tags?.["m8t:role"]) === "ledger") ?? (accounts.length === 1 ? accounts[0] : void 0);
29174
+ const ledgerAcct = selectLedgerAccount(accounts, storageAccount);
28904
29175
  const ledgerTableEndpoint = ledgerAcct?.properties?.primaryEndpoints?.table?.replace(/\/$/, "");
28905
29176
  if (!ledgerTableEndpoint) {
29177
+ const found = accounts.map((a) => a.name).join(", ") || "(none)";
28906
29178
  throw new LocalCliError({
28907
29179
  code: "DREAM_LEDGER_STORAGE_NOT_FOUND",
28908
- message: `No AgentLedger storage account found in resource group ${resourceGroup}.`,
28909
- hint: `Found: ${accounts.map((a) => a.name).join(", ") || "(none)"}. Tag the ledger account 'm8t:role=ledger'.`
29180
+ message: storageAccount ? `Storage account '${storageAccount}' was not found in resource group ${resourceGroup}, or has no table endpoint.` : `No AgentLedger storage account found in resource group ${resourceGroup}.`,
29181
+ hint: `Found: ${found}. Pass --storage-account <name> to select one explicitly.`
28910
29182
  });
28911
29183
  }
28912
29184
  const ws = await authedJsonPagedValues({
@@ -28948,6 +29220,7 @@ async function resolveDreamContext(opts) {
28948
29220
  });
28949
29221
  const resourceGroup = rgFromScope(project.accountScope);
28950
29222
  const { ledgerTableEndpoint, workspaceId } = await discoverLedgerResources({
29223
+ storageAccount: opts.storageAccount,
28951
29224
  credential: credential2,
28952
29225
  subscriptionId,
28953
29226
  resourceGroup
@@ -28997,25 +29270,28 @@ function redactTranscripts(input) {
28997
29270
  }
28998
29271
  var DreamRunCommand = class extends M8tCommand {
28999
29272
  static paths = [["dream", "run"]];
29000
- static usage = Command52.Usage({
29273
+ static usage = Command53.Usage({
29001
29274
  description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
29002
29275
  details: "Builds AzureCliCredential + resolves the Foundry project, ledger table, and Log Analytics workspace, runs the consumption pipeline, and prints the skip-ledger, the partition invariant, and harvest stats. Transcripts are metadata-only unless --show-transcripts is passed.\n\nThe bare command is READ-ONLY. --live takes the other branch: a real model call and a commit to the worker's brain repo through a minted GitHub App token."
29003
29276
  });
29004
- worker = Option49.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
29277
+ worker = Option50.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
29005
29278
  // Opting IN to the side effects, rather than opting out of them. This command's
29006
29279
  // own help has always described a dry run, but the bare invocation used to take
29007
29280
  // the live branch — a real model call and a commit to the brain repo — so anyone
29008
29281
  // acting on `--help` got the opposite of what they read.
29009
- live = Option49.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
29010
- dryRun = Option49.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
29011
- since = Option49.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
29012
- reset = Option49.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
29013
- showTranscripts = Option49.Boolean("--show-transcripts", false, {
29282
+ live = Option50.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
29283
+ dryRun = Option50.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
29284
+ since = Option50.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
29285
+ reset = Option50.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
29286
+ showTranscripts = Option50.Boolean("--show-transcripts", false, {
29014
29287
  description: "Print transcript bodies (default: metadata only)."
29015
29288
  });
29016
- subscription = Option49.String("--subscription");
29017
- endpoint = Option49.String("--endpoint");
29018
- output = Option49.String("--output");
29289
+ subscription = Option50.String("--subscription");
29290
+ endpoint = Option50.String("--endpoint");
29291
+ storageAccount = Option50.String("--storage-account", {
29292
+ description: "Ledger storage account name (skips tag-based discovery)"
29293
+ });
29294
+ output = Option50.String("--output");
29019
29295
  // Resolution seam — overridden by tests; built lazily at runtime otherwise.
29020
29296
  deps;
29021
29297
  async executeCommand() {
@@ -29032,7 +29308,8 @@ var DreamRunCommand = class extends M8tCommand {
29032
29308
  const liveContext = await liveDeps.resolveContext({
29033
29309
  worker,
29034
29310
  subscription: typeof this.subscription === "string" ? this.subscription : void 0,
29035
- endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
29311
+ endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0,
29312
+ storageAccount: typeof this.storageAccount === "string" ? this.storageAccount : void 0
29036
29313
  });
29037
29314
  const liveCursor = await liveDeps.readCursor(liveContext.worker);
29038
29315
  const result2 = await liveDeps.runDream({
@@ -29079,7 +29356,8 @@ var DreamRunCommand = class extends M8tCommand {
29079
29356
  const context = await deps.resolveContext({
29080
29357
  worker,
29081
29358
  subscription: typeof this.subscription === "string" ? this.subscription : void 0,
29082
- endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
29359
+ endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0,
29360
+ storageAccount: typeof this.storageAccount === "string" ? this.storageAccount : void 0
29083
29361
  });
29084
29362
  const now = /* @__PURE__ */ new Date();
29085
29363
  const cursor = await deps.readCursor(context.worker);
@@ -29364,7 +29642,7 @@ function defaultDeps(overrides) {
29364
29642
 
29365
29643
  // src/commands/conversations/sweep.ts
29366
29644
  import { createHash as createHash4 } from "crypto";
29367
- import { Command as Command53, Option as Option50 } from "clipanion";
29645
+ import { Command as Command54, Option as Option51 } from "clipanion";
29368
29646
  import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
29369
29647
  import { TableClient as TableClient8 } from "@azure/data-tables";
29370
29648
  import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
@@ -29429,13 +29707,28 @@ function defaultDeps2() {
29429
29707
  const { ledgerTableEndpoint } = await discoverLedgerResources({
29430
29708
  credential: credential2,
29431
29709
  subscriptionId,
29432
- resourceGroup: m[1]
29710
+ resourceGroup: m[1],
29711
+ storageAccount: opts.storageAccount
29433
29712
  });
29434
29713
  return { projectEndpoint: project.endpoint, ledgerTableEndpoint };
29435
29714
  },
29436
29715
  async fetchRows(ctx, fromIso, toIso) {
29437
29716
  const client = new TableClient8(ctx.ledgerTableEndpoint, LEDGER_TABLE_NAME2, credential2);
29438
- return fetchLedgerRows({ workers: [], source: "all", from: fromIso, to: toIso }, client);
29717
+ try {
29718
+ return await fetchLedgerRows({ workers: [], source: "all", from: fromIso, to: toIso }, client);
29719
+ } catch (e) {
29720
+ const msg = e instanceof Error ? e.message : String(e);
29721
+ if (statusOf2(e) === 403 || msg.includes("AuthorizationPermissionMismatch")) {
29722
+ const account = /https:\/\/([^.]+)\./.exec(ctx.ledgerTableEndpoint)?.[1] ?? "the ledger account";
29723
+ throw new LocalCliError({
29724
+ code: "sweep_ledger_forbidden",
29725
+ message: `Not authorized to read the ledger table on '${account}'.`,
29726
+ hint: `Reading table rows needs a data-plane role: grant 'Storage Table Data Reader' on that storage account to the signed-in identity (owning the account is not enough).`,
29727
+ cause: e
29728
+ });
29729
+ }
29730
+ throw e;
29731
+ }
29439
29732
  },
29440
29733
  async retrieveMetadata(ctx, conversationId) {
29441
29734
  try {
@@ -29475,7 +29768,7 @@ function defaultDeps2() {
29475
29768
  }
29476
29769
  var ConversationsSweepCommand = class extends M8tCommand {
29477
29770
  static paths = [["conversations", "sweep"]];
29478
- static usage = Command53.Usage({
29771
+ static usage = Command54.Usage({
29479
29772
  category: "Conversations",
29480
29773
  description: "Delete expired public-visitor conversations (dry run by default)",
29481
29774
  details: `
@@ -29491,21 +29784,24 @@ var ConversationsSweepCommand = class extends M8tCommand {
29491
29784
  ["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
29492
29785
  ]
29493
29786
  });
29494
- doDelete = Option50.Boolean("--delete", false, {
29787
+ doDelete = Option51.Boolean("--delete", false, {
29495
29788
  description: "Perform deletions (without this flag the command only reports)"
29496
29789
  });
29497
- principal = Option50.String("--principal", {
29790
+ principal = Option51.String("--principal", {
29498
29791
  description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
29499
29792
  });
29500
- max = Option50.String("--max", "200", { description: "Maximum deletions per run" });
29501
- graceDays = Option50.String("--grace-days", "14", {
29793
+ max = Option51.String("--max", "200", { description: "Maximum deletions per run" });
29794
+ graceDays = Option51.String("--grace-days", "14", {
29502
29795
  description: "Days past the 30-day key life before a conversation is eligible"
29503
29796
  });
29504
- lookbackDays = Option50.String("--lookback-days", "180", {
29797
+ lookbackDays = Option51.String("--lookback-days", "180", {
29505
29798
  description: "How far back to scan ledger activity for candidates"
29506
29799
  });
29507
- subscription = Option50.String("--subscription", { description: "Azure subscription id override" });
29508
- endpoint = Option50.String("--endpoint", { description: "Foundry project endpoint override" });
29800
+ subscription = Option51.String("--subscription", { description: "Azure subscription id override" });
29801
+ endpoint = Option51.String("--endpoint", { description: "Foundry project endpoint override" });
29802
+ storageAccount = Option51.String("--storage-account", {
29803
+ description: "Ledger storage account name (skips tag-based discovery)"
29804
+ });
29509
29805
  deps = defaultDeps2();
29510
29806
  async executeCommand() {
29511
29807
  const out = this.context.stdout;
@@ -29522,7 +29818,11 @@ var ConversationsSweepCommand = class extends M8tCommand {
29522
29818
  const cutoffIso = new Date(now - retentionDays * DAY_MS).toISOString();
29523
29819
  const fromIso = new Date(now - lookback * DAY_MS).toISOString();
29524
29820
  const toIso = new Date(now).toISOString();
29525
- const ctx = await this.deps.resolveContext({ subscription: this.subscription, endpoint: this.endpoint });
29821
+ const ctx = await this.deps.resolveContext({
29822
+ subscription: this.subscription,
29823
+ endpoint: this.endpoint,
29824
+ storageAccount: this.storageAccount
29825
+ });
29526
29826
  out.write(`Scanning ledger activity ${fromIso} \u2192 ${toIso}; retention cutoff ${cutoffIso}.
29527
29827
  `);
29528
29828
  const rows = await this.deps.fetchRows(ctx, fromIso, toIso);
@@ -29627,7 +29927,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
29627
29927
  };
29628
29928
 
29629
29929
  // src/commands/foundry/create.ts
29630
- import { Command as Command54, Option as Option51 } from "clipanion";
29930
+ import { Command as Command55, Option as Option52 } from "clipanion";
29631
29931
 
29632
29932
  // src/lib/foundry-create.ts
29633
29933
  init_errors();
@@ -29865,7 +30165,7 @@ async function createFoundryProject(args) {
29865
30165
  init_errors();
29866
30166
  var FoundryCreateCommand = class extends M8tCommand {
29867
30167
  static paths = [["foundry", "create"]];
29868
- static usage = Command54.Usage({
30168
+ static usage = Command55.Usage({
29869
30169
  description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
29870
30170
  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).",
29871
30171
  examples: [
@@ -29874,16 +30174,16 @@ var FoundryCreateCommand = class extends M8tCommand {
29874
30174
  ["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
29875
30175
  ]
29876
30176
  });
29877
- resourceGroup = Option51.String("--resource-group");
29878
- location = Option51.String("--location");
29879
- account = Option51.String("--account");
29880
- project = Option51.String("--project", "m8t");
29881
- model = Option51.String("--model", "gpt-4.1-mini");
29882
- modelVersion = Option51.String("--model-version", "2025-04-14");
29883
- capacity = Option51.String("--capacity", "50");
29884
- subscription = Option51.String("--subscription");
29885
- skipQuotaCheck = Option51.Boolean("--skip-quota-check", false);
29886
- output = Option51.String("--output");
30177
+ resourceGroup = Option52.String("--resource-group");
30178
+ location = Option52.String("--location");
30179
+ account = Option52.String("--account");
30180
+ project = Option52.String("--project", "m8t");
30181
+ model = Option52.String("--model", "gpt-4.1-mini");
30182
+ modelVersion = Option52.String("--model-version", "2025-04-14");
30183
+ capacity = Option52.String("--capacity", "50");
30184
+ subscription = Option52.String("--subscription");
30185
+ skipQuotaCheck = Option52.Boolean("--skip-quota-check", false);
30186
+ output = Option52.String("--output");
29887
30187
  async executeCommand() {
29888
30188
  const mode = resolveOutputMode(
29889
30189
  this.output,
@@ -29955,22 +30255,22 @@ var FoundryCreateCommand = class extends M8tCommand {
29955
30255
  };
29956
30256
 
29957
30257
  // src/commands/foundry/await-ready.ts
29958
- import { Command as Command55, Option as Option52 } from "clipanion";
30258
+ import { Command as Command56, Option as Option53 } from "clipanion";
29959
30259
  import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
29960
30260
  init_errors();
29961
30261
  var FoundryAwaitReadyCommand = class extends M8tCommand {
29962
30262
  static paths = [["foundry", "await-ready"]];
29963
- static usage = Command55.Usage({
30263
+ static usage = Command56.Usage({
29964
30264
  description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
29965
30265
  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.",
29966
30266
  examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
29967
30267
  });
29968
- endpoint = Option52.String("--endpoint");
29969
- consecutive = Option52.String("--consecutive", "3");
29970
- attempts = Option52.String("--attempts", "60");
29971
- interval = Option52.String("--interval", "5");
29972
- subscription = Option52.String("--subscription");
29973
- output = Option52.String("--output");
30268
+ endpoint = Option53.String("--endpoint");
30269
+ consecutive = Option53.String("--consecutive", "3");
30270
+ attempts = Option53.String("--attempts", "60");
30271
+ interval = Option53.String("--interval", "5");
30272
+ subscription = Option53.String("--subscription");
30273
+ output = Option53.String("--output");
29974
30274
  async executeCommand() {
29975
30275
  const mode = resolveOutputMode(this.output, this.context.stdout);
29976
30276
  const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
@@ -30004,7 +30304,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
30004
30304
  };
30005
30305
 
30006
30306
  // src/commands/bootstrap/preflight.ts
30007
- import { Command as Command56, Option as Option53 } from "clipanion";
30307
+ import { Command as Command57, Option as Option54 } from "clipanion";
30008
30308
 
30009
30309
  // ../../packages/telemetry-contract/artifact/tier-map.ts
30010
30310
  var EVENT_TIERS = {
@@ -30030,9 +30330,9 @@ var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t rece
30030
30330
 
30031
30331
  // ../../packages/telemetry-contract/src/endpoints.ts
30032
30332
  var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
30033
- function ingestUrl(path48) {
30333
+ function ingestUrl(path47) {
30034
30334
  const base = INGEST_BASE_URL.replace(/\/+$/, "");
30035
- return `${base}${path48.startsWith("/") ? path48 : `/${path48}`}`;
30335
+ return `${base}${path47.startsWith("/") ? path47 : `/${path47}`}`;
30036
30336
  }
30037
30337
 
30038
30338
  // src/commands/bootstrap/preflight.ts
@@ -30056,7 +30356,7 @@ function preflightRenderable(results) {
30056
30356
  }
30057
30357
  var BootstrapPreflightCommand = class extends M8tCommand {
30058
30358
  static paths = [["bootstrap", "preflight"]];
30059
- static usage = Command56.Usage({
30359
+ static usage = Command57.Usage({
30060
30360
  description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
30061
30361
  details: "Step 1 of `m8t bootstrap`. Prints an unmissable admin-credentials notice, then checks: Owner or User Access Administrator at subscription scope (hard requirement), directory-admin capability to register the app (or pass --client-id), every Azure resource provider the install uses (registering any that are missing), soft-deleted Cognitive Services accounts (they keep their model quota until purged, so quota can read free and the model deployment still fail), and \u2014 with --location \u2014 model quota in the target region. Exits non-zero with the exact failing check + remedy. `m8t prereqs` runs the same substrate checks on their own.",
30062
30362
  examples: [
@@ -30065,9 +30365,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
30065
30365
  ["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
30066
30366
  ]
30067
30367
  });
30068
- clientId = Option53.String("--client-id");
30069
- subscription = Option53.String("--subscription");
30070
- location = Option53.String("--location", {
30368
+ clientId = Option54.String("--client-id");
30369
+ subscription = Option54.String("--subscription");
30370
+ location = Option54.String("--location", {
30071
30371
  description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
30072
30372
  });
30073
30373
  async executeCommand() {
@@ -30164,10 +30464,10 @@ ${colors.error(" " + why)}
30164
30464
  }
30165
30465
 
30166
30466
  // src/commands/bootstrap/launch.ts
30167
- import * as fs35 from "fs";
30467
+ import * as fs34 from "fs";
30168
30468
  import * as os15 from "os";
30169
- import * as path38 from "path";
30170
- import { Command as Command57, Option as Option54 } from "clipanion";
30469
+ import * as path37 from "path";
30470
+ import { Command as Command58, Option as Option55 } from "clipanion";
30171
30471
  init_errors();
30172
30472
 
30173
30473
  // src/lib/bootstrap-mi.ts
@@ -30323,9 +30623,9 @@ async function kickInstaller(s) {
30323
30623
 
30324
30624
  // src/lib/bootstrap-status.ts
30325
30625
  import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
30326
- import * as fs33 from "fs/promises";
30626
+ import * as fs32 from "fs/promises";
30327
30627
  import * as os13 from "os";
30328
- import * as path36 from "path";
30628
+ import * as path35 from "path";
30329
30629
  init_errors();
30330
30630
  var STATUS_CONTAINER = "status";
30331
30631
  var STATUS_BLOB = "status.json";
@@ -30364,7 +30664,7 @@ async function readStatusBlob(opts) {
30364
30664
  cause: e
30365
30665
  });
30366
30666
  }
30367
- const tmpFile = path36.join(os13.tmpdir(), `m8t-status-${randomUUID2()}.json`);
30667
+ const tmpFile = path35.join(os13.tmpdir(), `m8t-status-${randomUUID2()}.json`);
30368
30668
  try {
30369
30669
  await runAz([
30370
30670
  "storage",
@@ -30383,7 +30683,7 @@ async function readStatusBlob(opts) {
30383
30683
  "--no-progress",
30384
30684
  "--only-show-errors"
30385
30685
  ]);
30386
- const raw = await fs33.readFile(tmpFile, "utf8");
30686
+ const raw = await fs32.readFile(tmpFile, "utf8");
30387
30687
  return JSON.parse(raw.trim());
30388
30688
  } catch (e) {
30389
30689
  throw new LocalCliError({
@@ -30393,23 +30693,23 @@ async function readStatusBlob(opts) {
30393
30693
  cause: e
30394
30694
  });
30395
30695
  } finally {
30396
- await fs33.rm(tmpFile, { force: true });
30696
+ await fs32.rm(tmpFile, { force: true });
30397
30697
  }
30398
30698
  }
30399
30699
 
30400
30700
  // src/lib/bootstrap-state.ts
30401
- import * as fs34 from "fs/promises";
30701
+ import * as fs33 from "fs/promises";
30402
30702
  import * as os14 from "os";
30403
- import * as path37 from "path";
30703
+ import * as path36 from "path";
30404
30704
  function stateDir(home) {
30405
- return path37.join(home, ".m8t");
30705
+ return path36.join(home, ".m8t");
30406
30706
  }
30407
30707
  function statePath(home) {
30408
- return path37.join(stateDir(home), "bootstrap.json");
30708
+ return path36.join(stateDir(home), "bootstrap.json");
30409
30709
  }
30410
30710
  async function readBootstrapState(home = os14.homedir()) {
30411
30711
  try {
30412
- const raw = await fs34.readFile(statePath(home), "utf8");
30712
+ const raw = await fs33.readFile(statePath(home), "utf8");
30413
30713
  return JSON.parse(raw);
30414
30714
  } catch (e) {
30415
30715
  if (e.code === "ENOENT") return null;
@@ -30417,10 +30717,10 @@ async function readBootstrapState(home = os14.homedir()) {
30417
30717
  }
30418
30718
  }
30419
30719
  async function writeBootstrapState(state, home = os14.homedir()) {
30420
- await fs34.mkdir(stateDir(home), { recursive: true });
30720
+ await fs33.mkdir(stateDir(home), { recursive: true });
30421
30721
  const tmp = `${statePath(home)}.tmp`;
30422
- await fs34.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
30423
- await fs34.rename(tmp, statePath(home));
30722
+ await fs33.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
30723
+ await fs33.rename(tmp, statePath(home));
30424
30724
  }
30425
30725
 
30426
30726
  // src/lib/bootstrap-occupancy.ts
@@ -30537,7 +30837,7 @@ var ACI_NAME = "m8t-installer";
30537
30837
  var MI_NAME = "m8t-installer-mi";
30538
30838
  var BootstrapLaunchCommand = class extends M8tCommand {
30539
30839
  static paths = [["bootstrap", "launch"]];
30540
- static usage = Command57.Usage({
30840
+ static usage = Command58.Usage({
30541
30841
  description: "Create + authorize the installer managed identity, then kick the cloud installer.",
30542
30842
  details: "Step 2 of `m8t bootstrap` (run after `preflight`). Ensures the resource group, creates the m8t app registration (or uses --client-id), creates a user-assigned managed identity granted Owner at subscription scope, and launches the published m8t-installer image as an ACI run-to-completion job under that identity. Writes ~/.m8t/bootstrap.json for `status` and `reap`, and threads your Azure object id so the installer can grant you the platform's data-plane roles itself.",
30543
30843
  examples: [
@@ -30548,26 +30848,26 @@ var BootstrapLaunchCommand = class extends M8tCommand {
30548
30848
  ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
30549
30849
  ]
30550
30850
  });
30551
- location = Option54.String("--location");
30552
- resourceGroup = Option54.String("--resource-group");
30553
- clientId = Option54.String("--client-id");
30554
- subscription = Option54.String("--subscription");
30555
- installerTag = Option54.String("--installer-tag");
30851
+ location = Option55.String("--location");
30852
+ resourceGroup = Option55.String("--resource-group");
30853
+ clientId = Option55.String("--client-id");
30854
+ subscription = Option55.String("--subscription");
30855
+ installerTag = Option55.String("--installer-tag");
30556
30856
  // Full image ref override (registry + repo + tag) — an escape hatch when the
30557
30857
  // default org/tag is wrong for the current CLI (e.g. a stale published build).
30558
30858
  // Wins over --installer-tag / the pinned default.
30559
- installerImage = Option54.String("--installer-image");
30560
- gatewayImageRef = Option54.String("--gateway-image-ref");
30561
- githubAppCreds = Option54.String("--github-app-creds");
30562
- contactEmail = Option54.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
30563
- company = Option54.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
30859
+ installerImage = Option55.String("--installer-image");
30860
+ gatewayImageRef = Option55.String("--gateway-image-ref");
30861
+ githubAppCreds = Option55.String("--github-app-creds");
30862
+ contactEmail = Option55.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
30863
+ company = Option55.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
30564
30864
  // Value-carrying on purpose: a bare --force would be cargo-culted into
30565
30865
  // runbooks and harness prompts and erode the protection, whereas a faithful
30566
30866
  // paste can never accidentally carry the victim group's name. It AUTHORIZES
30567
30867
  // the target; --resource-group is what CHOOSES it.
30568
- reinstallInto = Option54.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
30569
- org = Option54.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
30570
- noBrains = Option54.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
30868
+ reinstallInto = Option55.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
30869
+ org = Option55.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
30870
+ noBrains = Option55.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
30571
30871
  async executeCommand() {
30572
30872
  const location = typeof this.location === "string" ? this.location : void 0;
30573
30873
  if (!location) {
@@ -30606,13 +30906,13 @@ var BootstrapLaunchCommand = class extends M8tCommand {
30606
30906
  `);
30607
30907
  return 0;
30608
30908
  }
30609
- const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path38.join(os15.homedir(), ".m8t", "github-app.json");
30909
+ const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path37.join(os15.homedir(), ".m8t", "github-app.json");
30610
30910
  const skipBrains = this.noBrains === true;
30611
30911
  const requestedOrg = typeof this.org === "string" ? this.org : void 0;
30612
30912
  let githubApp;
30613
30913
  if (skipBrains) {
30614
30914
  out("installing without brain-backed workers; no GitHub App credentials will be used");
30615
- } else if (!fs35.existsSync(credsPath)) {
30915
+ } else if (!fs34.existsSync(credsPath)) {
30616
30916
  this.context.stderr.write(buildMissingCredsRefusal(credsPath));
30617
30917
  throw new LocalCliError({
30618
30918
  code: "GITHUB_APP_CREDS_MISSING",
@@ -30622,7 +30922,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
30622
30922
  } else {
30623
30923
  let parsed;
30624
30924
  try {
30625
- parsed = JSON.parse(fs35.readFileSync(credsPath, "utf8"));
30925
+ parsed = JSON.parse(fs34.readFileSync(credsPath, "utf8"));
30626
30926
  } catch {
30627
30927
  throw new LocalCliError({
30628
30928
  code: "GITHUB_APP_CREDS_INVALID",
@@ -30640,7 +30940,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
30640
30940
  }
30641
30941
  let pem;
30642
30942
  try {
30643
- pem = fs35.readFileSync(parsed.pemPath, "utf8");
30943
+ pem = fs34.readFileSync(parsed.pemPath, "utf8");
30644
30944
  } catch {
30645
30945
  throw new LocalCliError({
30646
30946
  code: "GITHUB_APP_PEM_MISSING",
@@ -30728,7 +31028,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
30728
31028
  };
30729
31029
 
30730
31030
  // src/commands/bootstrap/status.ts
30731
- import { Command as Command59, Option as Option56 } from "clipanion";
31031
+ import { Command as Command60, Option as Option57 } from "clipanion";
30732
31032
  init_errors();
30733
31033
 
30734
31034
  // src/lib/bootstrap-aci-state.ts
@@ -30754,15 +31054,15 @@ async function getAciState(opts) {
30754
31054
  }
30755
31055
 
30756
31056
  // src/lib/bootstrap-finalize.ts
30757
- import * as fs39 from "fs/promises";
31057
+ import * as fs38 from "fs/promises";
30758
31058
  import * as os19 from "os";
30759
- import * as path44 from "path";
31059
+ import * as path43 from "path";
30760
31060
 
30761
31061
  // src/lib/company-profile-seed.ts
30762
31062
  import { spawn as spawn6 } from "child_process";
30763
- import { closeSync, openSync, readFileSync as readFileSync25 } from "fs";
31063
+ import { closeSync, openSync, readFileSync as readFileSync24 } from "fs";
30764
31064
  import * as os16 from "os";
30765
- import * as path39 from "path";
31065
+ import * as path38 from "path";
30766
31066
  init_errors();
30767
31067
 
30768
31068
  // src/lib/onboarding-profile.ts
@@ -31487,9 +31787,9 @@ function composeFounderIdentityNote(id) {
31487
31787
 
31488
31788
  // src/lib/company-profile-seed.ts
31489
31789
  function readGithubAppCreds(credsPath) {
31490
- const p = credsPath ?? path39.join(os16.homedir(), ".m8t", "github-app.json");
31790
+ const p = credsPath ?? path38.join(os16.homedir(), ".m8t", "github-app.json");
31491
31791
  try {
31492
- return JSON.parse(readFileSync25(p, "utf8"));
31792
+ return JSON.parse(readFileSync24(p, "utf8"));
31493
31793
  } catch {
31494
31794
  return null;
31495
31795
  }
@@ -31558,7 +31858,7 @@ function upsertMemoryIndexOnce(existing, line2, targetPath2) {
31558
31858
  async function applyProfileToBrain(args) {
31559
31859
  const token = await mintInstallationTokenFromPem({
31560
31860
  appId: args.appCreds.appId,
31561
- privateKeyPem: readFileSync25(args.appCreds.pemPath, "utf8"),
31861
+ privateKeyPem: readFileSync24(args.appCreds.pemPath, "utf8"),
31562
31862
  installationId: args.appCreds.installationId,
31563
31863
  fetchImpl: args.fetchImpl
31564
31864
  });
@@ -31648,7 +31948,7 @@ async function applyProfileToBrains(args) {
31648
31948
  }
31649
31949
  }
31650
31950
  function spawnDetachedSeedWatch() {
31651
- const logPath = path39.join(os16.homedir(), ".m8t", "seed-profile.log");
31951
+ const logPath = path38.join(os16.homedir(), ".m8t", "seed-profile.log");
31652
31952
  const fd = openSync(logPath, "a");
31653
31953
  try {
31654
31954
  const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
@@ -31726,16 +32026,16 @@ function renderInstallSummary(args) {
31726
32026
 
31727
32027
  // src/lib/companion-install.ts
31728
32028
  import { constants as constants2 } from "fs";
31729
- import * as fs37 from "fs/promises";
31730
- import * as path41 from "path";
32029
+ import * as fs36 from "fs/promises";
32030
+ import * as path40 from "path";
31731
32031
  import { randomUUID as randomUUID3 } from "crypto";
31732
32032
  import { execFile, spawn as spawn7 } from "child_process";
31733
32033
 
31734
32034
  // src/lib/companion-artifact.ts
31735
32035
  import { createHash as createHash7 } from "crypto";
31736
32036
  import { constants } from "fs";
31737
- import * as fs36 from "fs/promises";
31738
- import * as path40 from "path";
32037
+ import * as fs35 from "fs/promises";
32038
+ import * as path39 from "path";
31739
32039
  var SHA256 = /^[a-f0-9]{64}$/u;
31740
32040
  var VERSION2 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u;
31741
32041
  function exactKeys(value, expected) {
@@ -31743,23 +32043,23 @@ function exactKeys(value, expected) {
31743
32043
  return actual.length === expected.length && actual.every((key2, index) => key2 === [...expected].sort()[index]);
31744
32044
  }
31745
32045
  function normalizedRelative(value) {
31746
- if (value.length === 0 || value.includes("\\") || value.includes("\0") || path40.posix.isAbsolute(value)) {
32046
+ if (value.length === 0 || value.includes("\\") || value.includes("\0") || path39.posix.isAbsolute(value)) {
31747
32047
  throw new Error("Artifact path must be a normalized relative POSIX path");
31748
32048
  }
31749
- const normalized = path40.posix.normalize(value);
32049
+ const normalized = path39.posix.normalize(value);
31750
32050
  if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../")) {
31751
32051
  throw new Error("Artifact path escapes the payload root");
31752
32052
  }
31753
32053
  return value;
31754
32054
  }
31755
32055
  function validateLink(entryPath, target) {
31756
- if (target.length === 0 || target.includes("\\") || target.includes("\0") || path40.posix.isAbsolute(target)) {
32056
+ if (target.length === 0 || target.includes("\\") || target.includes("\0") || path39.posix.isAbsolute(target)) {
31757
32057
  throw new Error("Artifact symlink target must be relative");
31758
32058
  }
31759
- const resolved = path40.posix.normalize(
31760
- path40.posix.join(path40.posix.dirname(entryPath), target)
32059
+ const resolved = path39.posix.normalize(
32060
+ path39.posix.join(path39.posix.dirname(entryPath), target)
31761
32061
  );
31762
- if (resolved === ".." || resolved.startsWith("../") || path40.posix.isAbsolute(resolved)) {
32062
+ if (resolved === ".." || resolved.startsWith("../") || path39.posix.isAbsolute(resolved)) {
31763
32063
  throw new Error("Artifact symlink target escapes the payload root");
31764
32064
  }
31765
32065
  return target;
@@ -31840,17 +32140,17 @@ function parseArtifactManifest(value) {
31840
32140
  };
31841
32141
  }
31842
32142
  async function sha256File2(filePath) {
31843
- return createHash7("sha256").update(await fs36.readFile(filePath)).digest("hex");
32143
+ return createHash7("sha256").update(await fs35.readFile(filePath)).digest("hex");
31844
32144
  }
31845
32145
  async function walk2(root, relative4 = "") {
31846
- const directory = path40.join(root, ...relative4.split("/").filter(Boolean));
31847
- const children = await fs36.readdir(directory, { withFileTypes: true });
32146
+ const directory = path39.join(root, ...relative4.split("/").filter(Boolean));
32147
+ const children = await fs35.readdir(directory, { withFileTypes: true });
31848
32148
  const entries = [];
31849
32149
  for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) {
31850
32150
  const childRelative = relative4 ? `${relative4}/${child.name}` : child.name;
31851
32151
  normalizedRelative(childRelative);
31852
- const childPath = path40.join(directory, child.name);
31853
- const stat5 = await fs36.lstat(childPath);
32152
+ const childPath = path39.join(directory, child.name);
32153
+ const stat5 = await fs35.lstat(childPath);
31854
32154
  if (stat5.isDirectory()) {
31855
32155
  entries.push(...await walk2(root, childRelative));
31856
32156
  } else if (stat5.isFile()) {
@@ -31862,7 +32162,7 @@ async function walk2(root, relative4 = "") {
31862
32162
  sha256: await sha256File2(childPath)
31863
32163
  });
31864
32164
  } else if (stat5.isSymbolicLink()) {
31865
- const target = await fs36.readlink(childPath);
32165
+ const target = await fs35.readlink(childPath);
31866
32166
  entries.push({
31867
32167
  type: "symlink",
31868
32168
  path: childRelative,
@@ -31875,19 +32175,19 @@ async function walk2(root, relative4 = "") {
31875
32175
  return entries;
31876
32176
  }
31877
32177
  async function ensureRealDirectory(root) {
31878
- const stat5 = await fs36.lstat(root);
32178
+ const stat5 = await fs35.lstat(root);
31879
32179
  if (stat5.isSymbolicLink()) throw new Error("Artifact root is a symbolic link");
31880
32180
  if (!stat5.isDirectory()) throw new Error("Artifact root is not a directory");
31881
32181
  }
31882
32182
  async function validateResolvedLinks(root, entries) {
31883
- const realRoot = await fs36.realpath(root);
32183
+ const realRoot = await fs35.realpath(root);
31884
32184
  for (const entry of entries) {
31885
32185
  if (entry.type !== "symlink") continue;
31886
32186
  try {
31887
- const linkPath = path40.join(root, ...entry.path.split("/"));
31888
- const resolved = await fs36.realpath(linkPath);
31889
- const relative4 = path40.relative(realRoot, resolved);
31890
- if (relative4 === ".." || relative4.startsWith(`..${path40.sep}`) || path40.isAbsolute(relative4)) {
32187
+ const linkPath = path39.join(root, ...entry.path.split("/"));
32188
+ const resolved = await fs35.realpath(linkPath);
32189
+ const relative4 = path39.relative(realRoot, resolved);
32190
+ if (relative4 === ".." || relative4.startsWith(`..${path39.sep}`) || path39.isAbsolute(relative4)) {
31891
32191
  throw new Error("Artifact symlink chain escapes the payload root");
31892
32192
  }
31893
32193
  } catch (error) {
@@ -31917,13 +32217,13 @@ async function buildArtifactManifest(payloadRoot, input) {
31917
32217
  return manifest;
31918
32218
  }
31919
32219
  async function readArtifactManifest(manifestPath) {
31920
- const before = await fs36.lstat(manifestPath);
32220
+ const before = await fs35.lstat(manifestPath);
31921
32221
  if (before.isSymbolicLink() || !before.isFile()) {
31922
32222
  throw new Error("Artifact manifest is a symbolic link or non-file");
31923
32223
  }
31924
32224
  let handle;
31925
32225
  try {
31926
- handle = await fs36.open(
32226
+ handle = await fs35.open(
31927
32227
  manifestPath,
31928
32228
  constants.O_RDONLY | constants.O_NOFOLLOW
31929
32229
  );
@@ -31940,9 +32240,9 @@ async function readArtifactManifest(manifestPath) {
31940
32240
  }
31941
32241
  }
31942
32242
  function ensureContainedEntry(root, entryPath) {
31943
- const absolute = path40.join(root, ...entryPath.split("/"));
31944
- const relative4 = path40.relative(root, absolute);
31945
- if (relative4.startsWith("..") || path40.isAbsolute(relative4)) {
32243
+ const absolute = path39.join(root, ...entryPath.split("/"));
32244
+ const relative4 = path39.relative(root, absolute);
32245
+ if (relative4.startsWith("..") || path39.isAbsolute(relative4)) {
31946
32246
  throw new Error("Artifact entry escapes payload root");
31947
32247
  }
31948
32248
  return absolute;
@@ -31981,27 +32281,27 @@ async function verifyArtifactPayload(payloadRoot, manifest) {
31981
32281
  async function copyArtifactPayload(sourceRoot, targetRoot, manifest) {
31982
32282
  await verifyArtifactSource(sourceRoot, manifest);
31983
32283
  try {
31984
- const targetStat = await fs36.lstat(targetRoot);
32284
+ const targetStat = await fs35.lstat(targetRoot);
31985
32285
  if (targetStat.isSymbolicLink()) {
31986
32286
  throw new Error("Install target is a symbolic link");
31987
32287
  }
31988
32288
  if (!targetStat.isDirectory()) throw new Error("Install target is not a directory");
31989
- if ((await fs36.readdir(targetRoot)).length > 0) {
32289
+ if ((await fs35.readdir(targetRoot)).length > 0) {
31990
32290
  throw new Error("Install staging target is not empty");
31991
32291
  }
31992
32292
  } catch (error) {
31993
32293
  if (error.code !== "ENOENT") throw error;
31994
- await fs36.mkdir(targetRoot, { recursive: false, mode: 448 });
32294
+ await fs35.mkdir(targetRoot, { recursive: false, mode: 448 });
31995
32295
  }
31996
32296
  for (const entry of manifest.entries) {
31997
32297
  const source = ensureContainedEntry(sourceRoot, entry.path);
31998
32298
  const target = ensureContainedEntry(targetRoot, entry.path);
31999
- await fs36.mkdir(path40.dirname(target), { recursive: true, mode: 448 });
32299
+ await fs35.mkdir(path39.dirname(target), { recursive: true, mode: 448 });
32000
32300
  if (entry.type === "file") {
32001
- await fs36.copyFile(source, target);
32002
- await fs36.chmod(target, entry.mode);
32301
+ await fs35.copyFile(source, target);
32302
+ await fs35.chmod(target, entry.mode);
32003
32303
  } else {
32004
- await fs36.symlink(entry.target, target);
32304
+ await fs35.symlink(entry.target, target);
32005
32305
  }
32006
32306
  }
32007
32307
  await verifyArtifactPayload(targetRoot, manifest);
@@ -32040,17 +32340,17 @@ function execFileOutput(file, args) {
32040
32340
  }
32041
32341
  async function setCompanionStartAtLogin(input) {
32042
32342
  if (input.platform === "darwin") {
32043
- const launchAgents = path41.join(input.homeDirectory, "Library", "LaunchAgents");
32044
- const registration = path41.join(
32343
+ const launchAgents = path40.join(input.homeDirectory, "Library", "LaunchAgents");
32344
+ const registration = path40.join(
32045
32345
  launchAgents,
32046
32346
  "com.m8t.companion.plist"
32047
32347
  );
32048
32348
  await assertNotSymlink(registration, "Start-at-login registration");
32049
32349
  if (!input.enabled) {
32050
- await fs37.rm(registration, { force: true });
32350
+ await fs36.rm(registration, { force: true });
32051
32351
  return;
32052
32352
  }
32053
- await fs37.mkdir(launchAgents, { recursive: true, mode: 448 });
32353
+ await fs36.mkdir(launchAgents, { recursive: true, mode: 448 });
32054
32354
  const plist = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict><key>Label</key><string>com.m8t.companion</string><key>ProgramArguments</key><array><string>' + xmlEscape(input.executable) + "</string></array><key>RunAtLoad</key><true/></dict></plist>\n";
32055
32355
  await atomicWriteText(registration, plist, 384);
32056
32356
  return;
@@ -32102,7 +32402,7 @@ async function setCompanionStartAtLogin(input) {
32102
32402
  }
32103
32403
  async function getCompanionStartAtLogin(input) {
32104
32404
  if (input.platform === "darwin") {
32105
- const registration = path41.join(
32405
+ const registration = path40.join(
32106
32406
  input.homeDirectory,
32107
32407
  "Library",
32108
32408
  "LaunchAgents",
@@ -32164,26 +32464,26 @@ function assertSupported(options) {
32164
32464
  }
32165
32465
  function companionInstallPaths(options) {
32166
32466
  assertSupported(options);
32167
- const companionState = path41.join(
32467
+ const companionState = path40.join(
32168
32468
  options.homeDirectory,
32169
32469
  ".m8t",
32170
32470
  "companion"
32171
32471
  );
32172
- const targetRoot = options.platform === "darwin" ? path41.join(
32472
+ const targetRoot = options.platform === "darwin" ? path40.join(
32173
32473
  options.homeDirectory,
32174
32474
  "Applications",
32175
32475
  "m8t Companion.app"
32176
- ) : path41.join(
32177
- options.localAppData ?? path41.join(options.homeDirectory, "AppData", "Local"),
32476
+ ) : path40.join(
32477
+ options.localAppData ?? path40.join(options.homeDirectory, "AppData", "Local"),
32178
32478
  "m8t",
32179
32479
  "companion",
32180
32480
  "app"
32181
32481
  );
32182
32482
  return {
32183
32483
  targetRoot,
32184
- installManifest: path41.join(companionState, "install-v1.json"),
32185
- runtimeBinding: path41.join(companionState, "runtime-v1.json"),
32186
- preferences: path41.join(companionState, "preferences-v1.json")
32484
+ installManifest: path40.join(companionState, "install-v1.json"),
32485
+ runtimeBinding: path40.join(companionState, "runtime-v1.json"),
32486
+ preferences: path40.join(companionState, "preferences-v1.json")
32187
32487
  };
32188
32488
  }
32189
32489
  function exactKeys2(value, keys) {
@@ -32194,7 +32494,7 @@ function parseInstallManifest(value) {
32194
32494
  throw new Error("Install manifest schema is invalid");
32195
32495
  }
32196
32496
  const record = value;
32197
- if (!exactKeys2(record, INSTALL_KEYS) || record.schemaVersion !== 1 || typeof record.version !== "string" || record.platform !== "darwin" && record.platform !== "win32" || record.architecture !== "arm64" && record.architecture !== "x64" || typeof record.entryRelativePath !== "string" || typeof record.artifactTreeSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(record.artifactTreeSha256) || typeof record.ownedTargetRoot !== "string" || !path41.isAbsolute(record.ownedTargetRoot)) {
32497
+ if (!exactKeys2(record, INSTALL_KEYS) || record.schemaVersion !== 1 || typeof record.version !== "string" || record.platform !== "darwin" && record.platform !== "win32" || record.architecture !== "arm64" && record.architecture !== "x64" || typeof record.entryRelativePath !== "string" || typeof record.artifactTreeSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(record.artifactTreeSha256) || typeof record.ownedTargetRoot !== "string" || !path40.isAbsolute(record.ownedTargetRoot)) {
32198
32498
  throw new Error("Install manifest schema is invalid");
32199
32499
  }
32200
32500
  return record;
@@ -32204,7 +32504,7 @@ function parseRuntimeBinding(value, platform) {
32204
32504
  throw new Error("Runtime binding schema is invalid");
32205
32505
  }
32206
32506
  const record = value;
32207
- const paths = platform === "win32" ? path41.win32 : path41.posix;
32507
+ const paths = platform === "win32" ? path40.win32 : path40.posix;
32208
32508
  if (!exactKeys2(record, RUNTIME_KEYS) || record.schemaVersion !== 1 || typeof record.nodeExecutable !== "string" || !paths.isAbsolute(record.nodeExecutable) || typeof record.cliEntry !== "string" || !paths.isAbsolute(record.cliEntry) || typeof record.gatewayOrigin !== "string") {
32209
32509
  throw new Error("Runtime binding schema is invalid");
32210
32510
  }
@@ -32229,7 +32529,7 @@ function validateGatewayOrigin(value) {
32229
32529
  }
32230
32530
  async function assertNotSymlink(filePath, kind) {
32231
32531
  try {
32232
- if ((await fs37.lstat(filePath)).isSymbolicLink()) {
32532
+ if ((await fs36.lstat(filePath)).isSymbolicLink()) {
32233
32533
  throw new Error(`${kind} is a symbolic link`);
32234
32534
  }
32235
32535
  } catch (error) {
@@ -32238,20 +32538,20 @@ async function assertNotSymlink(filePath, kind) {
32238
32538
  }
32239
32539
  }
32240
32540
  async function assertOwnedDirectoryChain(anchor, targetDirectory) {
32241
- const relative4 = path41.relative(anchor, targetDirectory);
32242
- if (relative4 === ".." || relative4.startsWith(`..${path41.sep}`) || path41.isAbsolute(relative4)) {
32541
+ const relative4 = path40.relative(anchor, targetDirectory);
32542
+ if (relative4 === ".." || relative4.startsWith(`..${path40.sep}`) || path40.isAbsolute(relative4)) {
32243
32543
  throw new Error("Companion owned directory escapes its trusted anchor");
32244
32544
  }
32245
- const segments = relative4.split(path41.sep).filter(Boolean);
32545
+ const segments = relative4.split(path40.sep).filter(Boolean);
32246
32546
  let current = anchor;
32247
- const anchorStatus = await fs37.lstat(anchor);
32547
+ const anchorStatus = await fs36.lstat(anchor);
32248
32548
  if (anchorStatus.isSymbolicLink() || !anchorStatus.isDirectory()) {
32249
32549
  throw new Error("Companion owned directory anchor is unsafe");
32250
32550
  }
32251
32551
  for (const segment of segments) {
32252
- current = path41.join(current, segment);
32552
+ current = path40.join(current, segment);
32253
32553
  try {
32254
- const status = await fs37.lstat(current);
32554
+ const status = await fs36.lstat(current);
32255
32555
  if (status.isSymbolicLink() || !status.isDirectory()) {
32256
32556
  throw new Error("Companion owned directory contains a symbolic link");
32257
32557
  }
@@ -32264,19 +32564,19 @@ async function assertOwnedDirectoryChain(anchor, targetDirectory) {
32264
32564
  async function assertOwnedParents(options, paths) {
32265
32565
  await assertOwnedDirectoryChain(
32266
32566
  options.homeDirectory,
32267
- path41.dirname(paths.installManifest)
32567
+ path40.dirname(paths.installManifest)
32268
32568
  );
32269
- const targetAnchor = options.platform === "win32" ? options.localAppData ?? path41.join(options.homeDirectory, "AppData", "Local") : options.homeDirectory;
32270
- await assertOwnedDirectoryChain(targetAnchor, path41.dirname(paths.targetRoot));
32569
+ const targetAnchor = options.platform === "win32" ? options.localAppData ?? path40.join(options.homeDirectory, "AppData", "Local") : options.homeDirectory;
32570
+ await assertOwnedDirectoryChain(targetAnchor, path40.dirname(paths.targetRoot));
32271
32571
  }
32272
32572
  async function readRegularText(filePath, maxBytes) {
32273
- const before = await fs37.lstat(filePath);
32573
+ const before = await fs36.lstat(filePath);
32274
32574
  if (before.isSymbolicLink() || !before.isFile()) {
32275
32575
  throw new Error("Companion state file is not a regular file");
32276
32576
  }
32277
32577
  let handle;
32278
32578
  try {
32279
- handle = await fs37.open(
32579
+ handle = await fs36.open(
32280
32580
  filePath,
32281
32581
  constants2.O_RDONLY | constants2.O_NOFOLLOW
32282
32582
  );
@@ -32299,27 +32599,27 @@ async function atomicWriteJson(filePath, value) {
32299
32599
  }
32300
32600
  async function atomicWriteText(filePath, contents, mode) {
32301
32601
  await assertNotSymlink(filePath, "Companion state file");
32302
- await fs37.mkdir(path41.dirname(filePath), {
32602
+ await fs36.mkdir(path40.dirname(filePath), {
32303
32603
  recursive: true,
32304
32604
  mode: 448
32305
32605
  });
32306
32606
  const temporary = `${filePath}.${randomUUID3()}.tmp`;
32307
32607
  try {
32308
- await fs37.writeFile(temporary, contents, {
32608
+ await fs36.writeFile(temporary, contents, {
32309
32609
  mode,
32310
32610
  flag: "wx"
32311
32611
  });
32312
- await fs37.rename(temporary, filePath);
32313
- await fs37.chmod(filePath, mode).catch(() => void 0);
32612
+ await fs36.rename(temporary, filePath);
32613
+ await fs36.chmod(filePath, mode).catch(() => void 0);
32314
32614
  } finally {
32315
- await fs37.rm(temporary, { force: true }).catch(() => void 0);
32615
+ await fs36.rm(temporary, { force: true }).catch(() => void 0);
32316
32616
  }
32317
32617
  }
32318
32618
  async function realRegularFile(filePath, executable) {
32319
- const real = await fs37.realpath(filePath);
32320
- const stat5 = await fs37.stat(real);
32619
+ const real = await fs36.realpath(filePath);
32620
+ const stat5 = await fs36.stat(real);
32321
32621
  if (!stat5.isFile()) throw new Error("Companion launch target is not a file");
32322
- await fs37.access(real, executable ? constants2.X_OK : constants2.R_OK);
32622
+ await fs36.access(real, executable ? constants2.X_OK : constants2.R_OK);
32323
32623
  return real;
32324
32624
  }
32325
32625
  function defaultLaunch(executable) {
@@ -32335,7 +32635,7 @@ async function companionIsRunning(platform, executable) {
32335
32635
  if (platform !== "win32") return false;
32336
32636
  let handle;
32337
32637
  try {
32338
- handle = await fs37.open(executable, "r+");
32638
+ handle = await fs36.open(executable, "r+");
32339
32639
  } catch (error) {
32340
32640
  const code = error.code;
32341
32641
  if (code === "ENOENT") return false;
@@ -32416,7 +32716,7 @@ async function statusCompanion(options) {
32416
32716
  realRegularFile(runtime.nodeExecutable, options.platform !== "win32"),
32417
32717
  realRegularFile(runtime.cliEntry, false)
32418
32718
  ]);
32419
- const executable = path41.join(
32719
+ const executable = path40.join(
32420
32720
  paths.targetRoot,
32421
32721
  ...install.entryRelativePath.split("/")
32422
32722
  );
@@ -32449,7 +32749,7 @@ async function snapshotFile(filePath) {
32449
32749
  }
32450
32750
  async function restoreFile(filePath, bytes) {
32451
32751
  if (bytes === null) {
32452
- await fs37.rm(filePath, { force: true });
32752
+ await fs36.rm(filePath, { force: true });
32453
32753
  } else {
32454
32754
  await atomicWriteJson(filePath, JSON.parse(bytes.toString("utf8")));
32455
32755
  }
@@ -32464,8 +32764,8 @@ async function converge(options, force) {
32464
32764
  if (artifactManifest.platform !== options.platform || artifactManifest.architecture !== options.architecture) {
32465
32765
  throw new Error("Companion artifact does not match this OS and architecture");
32466
32766
  }
32467
- const payloadRoot = path41.join(
32468
- path41.dirname(options.artifactManifestPath),
32767
+ const payloadRoot = path40.join(
32768
+ path40.dirname(options.artifactManifestPath),
32469
32769
  "payload"
32470
32770
  );
32471
32771
  await verifyArtifactSource(payloadRoot, artifactManifest);
@@ -32485,7 +32785,7 @@ async function converge(options, force) {
32485
32785
  if (priorInstalled) {
32486
32786
  await assertCompanionNotRunning(
32487
32787
  options,
32488
- path41.join(
32788
+ path40.join(
32489
32789
  priorInstalled.paths.targetRoot,
32490
32790
  ...priorInstalled.install.entryRelativePath.split("/")
32491
32791
  )
@@ -32493,7 +32793,7 @@ async function converge(options, force) {
32493
32793
  }
32494
32794
  if (current.state === "not-installed") {
32495
32795
  try {
32496
- await fs37.lstat(paths.targetRoot);
32796
+ await fs36.lstat(paths.targetRoot);
32497
32797
  throw new Error(
32498
32798
  "The fixed companion target exists without an owned install manifest"
32499
32799
  );
@@ -32504,7 +32804,7 @@ async function converge(options, force) {
32504
32804
  const stage = `${paths.targetRoot}.m8t-stage-${randomUUID3()}`;
32505
32805
  const backup = `${paths.targetRoot}.m8t-backup-${randomUUID3()}`;
32506
32806
  const copy = options.copyPayload ?? copyArtifactPayload;
32507
- await fs37.mkdir(path41.dirname(paths.targetRoot), {
32807
+ await fs36.mkdir(path40.dirname(paths.targetRoot), {
32508
32808
  recursive: true,
32509
32809
  mode: 448
32510
32810
  });
@@ -32521,12 +32821,12 @@ async function converge(options, force) {
32521
32821
  try {
32522
32822
  await copy(payloadRoot, stage, artifactManifest);
32523
32823
  try {
32524
- await fs37.rename(paths.targetRoot, backup);
32824
+ await fs36.rename(paths.targetRoot, backup);
32525
32825
  movedPrior = true;
32526
32826
  } catch (error) {
32527
32827
  if (error.code !== "ENOENT") throw error;
32528
32828
  }
32529
- await fs37.rename(stage, paths.targetRoot);
32829
+ await fs36.rename(stage, paths.targetRoot);
32530
32830
  installedStage = true;
32531
32831
  const nodeExecutable = await realRegularFile(
32532
32832
  options.nodeExecutable,
@@ -32568,7 +32868,7 @@ async function converge(options, force) {
32568
32868
  await readClosedJson(paths.runtimeBinding),
32569
32869
  options.platform
32570
32870
  );
32571
- const executable = path41.join(
32871
+ const executable = path40.join(
32572
32872
  paths.targetRoot,
32573
32873
  ...artifactManifest.entryRelativePath.split("/")
32574
32874
  );
@@ -32578,7 +32878,7 @@ async function converge(options, force) {
32578
32878
  platform: options.platform,
32579
32879
  executable: ownedExecutable
32580
32880
  }));
32581
- priorLoginExecutable = priorInstalled ? path41.join(
32881
+ priorLoginExecutable = priorInstalled ? path40.join(
32582
32882
  priorInstalled.paths.targetRoot,
32583
32883
  ...priorInstalled.install.entryRelativePath.split("/")
32584
32884
  ) : executable;
@@ -32589,7 +32889,7 @@ async function converge(options, force) {
32589
32889
  );
32590
32890
  loginChanged = true;
32591
32891
  await (options.launch ?? defaultLaunch)(executable);
32592
- await fs37.rm(backup, { recursive: true, force: true });
32892
+ await fs36.rm(backup, { recursive: true, force: true });
32593
32893
  return {
32594
32894
  state: "installed",
32595
32895
  version: artifactManifest.version,
@@ -32603,15 +32903,15 @@ async function converge(options, force) {
32603
32903
  () => void 0
32604
32904
  );
32605
32905
  }
32606
- await fs37.rm(stage, { recursive: true, force: true }).catch(() => void 0);
32906
+ await fs36.rm(stage, { recursive: true, force: true }).catch(() => void 0);
32607
32907
  if (installedStage) {
32608
- await fs37.rm(paths.targetRoot, {
32908
+ await fs36.rm(paths.targetRoot, {
32609
32909
  recursive: true,
32610
32910
  force: true
32611
32911
  }).catch(() => void 0);
32612
32912
  }
32613
32913
  if (movedPrior) {
32614
- await fs37.rename(backup, paths.targetRoot).catch(() => void 0);
32914
+ await fs36.rename(backup, paths.targetRoot).catch(() => void 0);
32615
32915
  }
32616
32916
  await Promise.all([
32617
32917
  restoreFile(paths.installManifest, snapshots[0]),
@@ -32637,14 +32937,14 @@ async function uninstallCompanion(options) {
32637
32937
  if (install.ownedTargetRoot !== paths.targetRoot) {
32638
32938
  throw new Error("Refusing to remove a non-owned companion target");
32639
32939
  }
32640
- const executable = path41.join(
32940
+ const executable = path40.join(
32641
32941
  paths.targetRoot,
32642
32942
  ...install.entryRelativePath.split("/")
32643
32943
  );
32644
32944
  await assertCompanionNotRunning(options, executable);
32645
32945
  await (options.setStartAtLogin ?? (() => Promise.resolve()))(executable, false);
32646
32946
  const remove = options.removeOwnedPath ?? (async (ownedPath, recursive) => {
32647
- await fs37.rm(ownedPath, { recursive, force: true });
32947
+ await fs36.rm(ownedPath, { recursive, force: true });
32648
32948
  });
32649
32949
  await remove(paths.targetRoot, true);
32650
32950
  await remove(paths.installManifest, false);
@@ -32655,8 +32955,8 @@ async function uninstallCompanion(options) {
32655
32955
 
32656
32956
  // src/commands/companion/install.ts
32657
32957
  import * as os18 from "os";
32658
- import * as path43 from "path";
32659
- import { Command as Command58, Option as Option55 } from "clipanion";
32958
+ import * as path42 from "path";
32959
+ import { Command as Command59, Option as Option56 } from "clipanion";
32660
32960
 
32661
32961
  // src/lib/companion-channel.ts
32662
32962
  async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
@@ -32673,9 +32973,9 @@ async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps =
32673
32973
  // src/lib/companion-download.ts
32674
32974
  import { createHash as createHash8 } from "crypto";
32675
32975
  import { execFile as execFile2 } from "child_process";
32676
- import * as fs38 from "fs/promises";
32976
+ import * as fs37 from "fs/promises";
32677
32977
  import * as os17 from "os";
32678
- import * as path42 from "path";
32978
+ import * as path41 from "path";
32679
32979
  init_errors();
32680
32980
  var MAX_ASSET_BYTES = 512 * 1024 * 1024;
32681
32981
  function extractArchive(archive, into) {
@@ -32728,20 +33028,20 @@ async function downloadCompanionArtifact(component, deps = {}) {
32728
33028
  hint: "Nothing was unpacked. This is what a corrupted download or a substituted file looks like \u2014 retry, and report it if it persists."
32729
33029
  });
32730
33030
  }
32731
- const root = await (deps.makeTemporaryDirectory ?? (() => fs38.mkdtemp(path42.join(os17.tmpdir(), "m8t-companion-"))))();
33031
+ const root = await (deps.makeTemporaryDirectory ?? (() => fs37.mkdtemp(path41.join(os17.tmpdir(), "m8t-companion-"))))();
32732
33032
  const dispose = async () => {
32733
- await fs38.rm(root, { recursive: true, force: true }).catch(() => void 0);
33033
+ await fs37.rm(root, { recursive: true, force: true }).catch(() => void 0);
32734
33034
  };
32735
33035
  try {
32736
- const archive = path42.join(root, pinned.asset);
32737
- await fs38.writeFile(archive, bytes, { mode: 384 });
32738
- const unpacked = path42.join(root, "unpacked");
32739
- await fs38.mkdir(unpacked, { recursive: false, mode: 448 });
33036
+ const archive = path41.join(root, pinned.asset);
33037
+ await fs37.writeFile(archive, bytes, { mode: 384 });
33038
+ const unpacked = path41.join(root, "unpacked");
33039
+ await fs37.mkdir(unpacked, { recursive: false, mode: 448 });
32740
33040
  await (deps.extract ?? extractArchive)(archive, unpacked);
32741
- await fs38.rm(archive, { force: true });
33041
+ await fs37.rm(archive, { force: true });
32742
33042
  return {
32743
33043
  version: component.version,
32744
- artifactManifestPath: path42.join(unpacked, "artifact-v1.json"),
33044
+ artifactManifestPath: path41.join(unpacked, "artifact-v1.json"),
32745
33045
  dispose
32746
33046
  };
32747
33047
  } catch (error) {
@@ -32772,7 +33072,7 @@ async function convergeCompanionFromChannel(converge2, options = {}) {
32772
33072
  let artifactManifestPath;
32773
33073
  let dispose = () => Promise.resolve();
32774
33074
  if (stagedDirectory !== void 0) {
32775
- artifactManifestPath = path43.resolve(stagedDirectory, "artifact-v1.json");
33075
+ artifactManifestPath = path42.resolve(stagedDirectory, "artifact-v1.json");
32776
33076
  } else {
32777
33077
  const release = await readCompanionRelease(
32778
33078
  platformVersion !== void 0 ? { channel: true, version: platformTag(platformVersion) } : { url: CHANNEL_LATEST_URL }
@@ -32814,7 +33114,7 @@ Version: ${state.version}
32814
33114
  }
32815
33115
  var CompanionInstallCommand = class extends M8tCommand {
32816
33116
  static paths = [["companion", "install"]];
32817
- static usage = Command58.Usage({
33117
+ static usage = Command59.Usage({
32818
33118
  description: "Install the desktop companions for this user from the release channel.",
32819
33119
  examples: [
32820
33120
  ["Install the released build", "$0 companion install"],
@@ -32824,10 +33124,10 @@ var CompanionInstallCommand = class extends M8tCommand {
32824
33124
  ]
32825
33125
  ]
32826
33126
  });
32827
- from = Option55.String("--from", {
33127
+ from = Option56.String("--from", {
32828
33128
  description: "A locally staged build directory instead of the released one."
32829
33129
  });
32830
- resourceGroup = Option55.String("--resource-group", {
33130
+ resourceGroup = Option56.String("--resource-group", {
32831
33131
  description: "Which deployment to bind to, when the subscription holds more than one."
32832
33132
  });
32833
33133
  async executeCommand() {
@@ -32850,7 +33150,7 @@ function resolveRepoRootMarker(args) {
32850
33150
  }
32851
33151
  async function looksLikeCheckout(dir) {
32852
33152
  try {
32853
- return (await fs39.stat(path44.join(dir, "brain-template"))).isDirectory();
33153
+ return (await fs38.stat(path43.join(dir, "brain-template"))).isDirectory();
32854
33154
  } catch {
32855
33155
  return false;
32856
33156
  }
@@ -32869,18 +33169,18 @@ var defaultDeps3 = {
32869
33169
  homedir: () => os19.homedir()
32870
33170
  };
32871
33171
  async function finalizeInstall(args, deps = defaultDeps3) {
32872
- const markerDir = path44.join(deps.homedir(), ".m8t");
32873
- const markerPath = path44.join(markerDir, "repo-root");
33172
+ const markerDir = path43.join(deps.homedir(), ".m8t");
33173
+ const markerPath = path43.join(markerDir, "repo-root");
32874
33174
  const cwd = process.cwd();
32875
- const existing = await fs39.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
33175
+ const existing = await fs38.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
32876
33176
  const repoRoot = resolveRepoRootMarker({
32877
33177
  ...args.repoRoot !== void 0 ? { explicit: args.repoRoot } : {},
32878
33178
  cwd,
32879
33179
  cwdIsCheckout: await looksLikeCheckout(cwd),
32880
33180
  existing
32881
33181
  });
32882
- await fs39.mkdir(markerDir, { recursive: true });
32883
- await fs39.writeFile(markerPath, `${repoRoot}
33182
+ await fs38.mkdir(markerDir, { recursive: true });
33183
+ await fs38.writeFile(markerPath, `${repoRoot}
32884
33184
  `, "utf8");
32885
33185
  let webappUrl;
32886
33186
  try {
@@ -32958,7 +33258,7 @@ async function finalizeInstall(args, deps = defaultDeps3) {
32958
33258
  }
32959
33259
  let brainOrg = null;
32960
33260
  try {
32961
- const credsRaw = await fs39.readFile(path44.join(markerDir, "github-app.json"), "utf8");
33261
+ const credsRaw = await fs38.readFile(path43.join(markerDir, "github-app.json"), "utf8");
32962
33262
  const creds = JSON.parse(credsRaw);
32963
33263
  brainOrg = typeof creds.org === "string" ? creds.org : null;
32964
33264
  } catch {
@@ -33043,7 +33343,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
33043
33343
  // runbooks and shakedown recipes that a founder may already be part-way
33044
33344
  // through — it prints a deprecation notice and does the right thing.
33045
33345
  static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
33046
- static usage = Command59.Usage({
33346
+ static usage = Command60.Usage({
33047
33347
  description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
33048
33348
  details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.\n\nOn reaching done under --watch this also completes the local half of the install, which nothing else in the bootstrap path can do: it registers the webapp's sign-in redirect URI (the installer runs as a managed identity with no directory role, and at launch time the gateway FQDN did not exist yet), writes ~/.m8t/repo-root and the gateway discovery cache, and seeds your advisors' brains from the onboarding intake. Re-runnable \u2014 `m8t bootstrap finish` is a deprecated alias that does exactly this on an already-done install.",
33049
33349
  examples: [
@@ -33052,12 +33352,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
33052
33352
  ["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
33053
33353
  ]
33054
33354
  });
33055
- watch = Option56.Boolean("--watch", false);
33056
- output = Option56.String("--output");
33057
- repoRoot = Option56.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
33058
- finalize = Option56.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
33059
- subscription = Option56.String("--subscription");
33060
- resourceGroup = Option56.String("--resource-group");
33355
+ watch = Option57.Boolean("--watch", false);
33356
+ output = Option57.String("--output");
33357
+ repoRoot = Option57.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
33358
+ finalize = Option57.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
33359
+ subscription = Option57.String("--subscription");
33360
+ resourceGroup = Option57.String("--resource-group");
33061
33361
  async executeCommand() {
33062
33362
  const state = await readBootstrapState();
33063
33363
  if (!state) {
@@ -33190,7 +33490,7 @@ function formatStatus(d) {
33190
33490
  }
33191
33491
 
33192
33492
  // src/commands/bootstrap/reap.ts
33193
- import { Command as Command60, Option as Option57 } from "clipanion";
33493
+ import { Command as Command61, Option as Option58 } from "clipanion";
33194
33494
  init_errors();
33195
33495
 
33196
33496
  // src/lib/bootstrap-reap.ts
@@ -33284,7 +33584,7 @@ async function reapInstaller(opts) {
33284
33584
  // src/commands/bootstrap/reap.ts
33285
33585
  var BootstrapReapCommand = class extends M8tCommand {
33286
33586
  static paths = [["bootstrap", "reap"]];
33287
- static usage = Command60.Usage({
33587
+ static usage = Command61.Usage({
33288
33588
  description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
33289
33589
  details: "Runs locally on the 'done' signal (the installer can't delete its own identity). The platform RG and the gateway's own assignments persist. A failed install is left intact for diagnosis unless --force.\n\n--sweep-orphans is a DIFFERENT and much broader mode: instead of reaping this install, it scans the WHOLE SUBSCRIPTION for m8t role assignments left behind by installs whose resources are gone \u2014 orphaned installer Owner-at-subscription-scope grants and orphaned gateway subscription-scope roles. It is a dry run that only lists what it found unless you also pass --yes, which deletes them.",
33290
33590
  examples: [
@@ -33294,9 +33594,9 @@ var BootstrapReapCommand = class extends M8tCommand {
33294
33594
  ["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
33295
33595
  ]
33296
33596
  });
33297
- force = Option57.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
33298
- sweepOrphans = Option57.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
33299
- yes = Option57.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
33597
+ force = Option58.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
33598
+ sweepOrphans = Option58.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
33599
+ yes = Option58.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
33300
33600
  async executeCommand() {
33301
33601
  if (this.sweepOrphans === true) {
33302
33602
  const { subscriptionId: sub } = await getAzAccount();
@@ -33390,18 +33690,18 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
33390
33690
  };
33391
33691
 
33392
33692
  // src/commands/bootstrap/ui.ts
33393
- import * as fs41 from "fs";
33693
+ import * as fs40 from "fs";
33394
33694
  import * as os21 from "os";
33395
- import * as path46 from "path";
33396
- import { Command as Command61, Option as Option58 } from "clipanion";
33397
- import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
33695
+ import * as path45 from "path";
33696
+ import { Command as Command62, Option as Option59 } from "clipanion";
33697
+ import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
33398
33698
  init_errors();
33399
33699
 
33400
33700
  // src/lib/bootstrap-ui.ts
33401
- import * as fs40 from "fs";
33701
+ import * as fs39 from "fs";
33402
33702
  import * as net from "net";
33403
33703
  import * as os20 from "os";
33404
- import * as path45 from "path";
33704
+ import * as path44 from "path";
33405
33705
  import { randomBytes as randomBytes3 } from "crypto";
33406
33706
  import { spawn as spawn8, spawnSync as spawnSync6 } from "child_process";
33407
33707
  init_errors();
@@ -33589,11 +33889,11 @@ async function ensureFounderFoundryRole(args) {
33589
33889
  });
33590
33890
  }
33591
33891
  function writeWebEnvLocal(args) {
33592
- const envPath = path45.join(args.repoRoot, "apps", "web", ".env.local");
33892
+ const envPath = path44.join(args.repoRoot, "apps", "web", ".env.local");
33593
33893
  let prior = "";
33594
- if (fs40.existsSync(envPath)) {
33595
- prior = fs40.readFileSync(envPath, "utf8");
33596
- fs40.copyFileSync(envPath, envPath + ".bak");
33894
+ if (fs39.existsSync(envPath)) {
33895
+ prior = fs39.readFileSync(envPath, "utf8");
33896
+ fs39.copyFileSync(envPath, envPath + ".bak");
33597
33897
  }
33598
33898
  const priorSecret = readValidInternalSecret(prior);
33599
33899
  const internalSecret = priorSecret ?? (args.randomBytesImpl ?? randomBytes3)(32).toString("base64url");
@@ -33619,9 +33919,9 @@ function writeWebEnvLocal(args) {
33619
33919
  "NEXT_PUBLIC_M8T_ONBOARDING=1",
33620
33920
  ""
33621
33921
  ].join("\n");
33622
- fs40.writeFileSync(envPath, body, { encoding: "utf8", mode: 384 });
33922
+ fs39.writeFileSync(envPath, body, { encoding: "utf8", mode: 384 });
33623
33923
  try {
33624
- fs40.chmodSync(envPath, 384);
33924
+ fs39.chmodSync(envPath, 384);
33625
33925
  } catch {
33626
33926
  }
33627
33927
  return envPath;
@@ -33637,10 +33937,10 @@ function readValidInternalSecret(envText) {
33637
33937
  return isValidInternalSecret(values[0]) ? values[0] : void 0;
33638
33938
  }
33639
33939
  function readWebInternalSecret(repoRoot) {
33640
- const envPath = path45.join(repoRoot, "apps", "web", ".env.local");
33940
+ const envPath = path44.join(repoRoot, "apps", "web", ".env.local");
33641
33941
  let secret;
33642
33942
  try {
33643
- secret = readValidInternalSecret(fs40.readFileSync(envPath, "utf8"));
33943
+ secret = readValidInternalSecret(fs39.readFileSync(envPath, "utf8"));
33644
33944
  } catch {
33645
33945
  secret = void 0;
33646
33946
  }
@@ -33692,17 +33992,17 @@ async function installWebDeps(repoRoot) {
33692
33992
  await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
33693
33993
  }
33694
33994
  function onboardingUiPaths(home = os20.homedir()) {
33695
- const dir = path45.join(home, ".m8t");
33995
+ const dir = path44.join(home, ".m8t");
33696
33996
  return {
33697
- logPath: path45.join(dir, "onboarding-ui.log"),
33698
- pidPath: path45.join(dir, "onboarding-ui.pid")
33997
+ logPath: path44.join(dir, "onboarding-ui.log"),
33998
+ pidPath: path44.join(dir, "onboarding-ui.pid")
33699
33999
  };
33700
34000
  }
33701
34001
  function onboardingRelayPaths(home = os20.homedir()) {
33702
- const dir = path45.join(home, ".m8t");
34002
+ const dir = path44.join(home, ".m8t");
33703
34003
  return {
33704
- logPath: path45.join(dir, "onboarding-relay.log"),
33705
- pidPath: path45.join(dir, "onboarding-relay.pid")
34004
+ logPath: path44.join(dir, "onboarding-relay.log"),
34005
+ pidPath: path44.join(dir, "onboarding-relay.pid")
33706
34006
  };
33707
34007
  }
33708
34008
  function isLocalPortOpen(port) {
@@ -33724,7 +34024,7 @@ function isLocalPortOpen(port) {
33724
34024
  }
33725
34025
  function removePidFile(pidPath) {
33726
34026
  try {
33727
- fs40.unlinkSync(pidPath);
34027
+ fs39.unlinkSync(pidPath);
33728
34028
  } catch {
33729
34029
  }
33730
34030
  }
@@ -33737,7 +34037,7 @@ function parseManagedPid(text) {
33737
34037
  function readLiveManagedPid(pidPath) {
33738
34038
  let text;
33739
34039
  try {
33740
- text = fs40.readFileSync(pidPath, "utf8").trim();
34040
+ text = fs39.readFileSync(pidPath, "utf8").trim();
33741
34041
  } catch {
33742
34042
  return null;
33743
34043
  }
@@ -33776,8 +34076,8 @@ async function serveOnboardingUiDetached(args) {
33776
34076
  });
33777
34077
  }
33778
34078
  if (managedPid != null) return { alreadyRunning: true, logPath };
33779
- fs40.mkdirSync(path45.dirname(logPath), { recursive: true });
33780
- const fd = fs40.openSync(logPath, "a");
34079
+ fs39.mkdirSync(path44.dirname(logPath), { recursive: true });
34080
+ const fd = fs39.openSync(logPath, "a");
33781
34081
  const child = spawn8("pnpm", ["--filter", "web", "dev"], {
33782
34082
  cwd: args.repoRoot,
33783
34083
  detached: true,
@@ -33786,7 +34086,7 @@ async function serveOnboardingUiDetached(args) {
33786
34086
  // pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
33787
34087
  shell: process.platform === "win32"
33788
34088
  });
33789
- fs40.closeSync(fd);
34089
+ fs39.closeSync(fd);
33790
34090
  if (child.pid == null) {
33791
34091
  throw new LocalCliError({
33792
34092
  code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
@@ -33795,7 +34095,7 @@ async function serveOnboardingUiDetached(args) {
33795
34095
  });
33796
34096
  }
33797
34097
  child.unref();
33798
- fs40.writeFileSync(pidPath, String(child.pid), "utf8");
34098
+ fs39.writeFileSync(pidPath, String(child.pid), "utf8");
33799
34099
  const deadline = Date.now() + 45e3;
33800
34100
  const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
33801
34101
  while (Date.now() < deadline) {
@@ -33827,8 +34127,8 @@ async function serveOnboardingRelayDetached(args) {
33827
34127
  });
33828
34128
  }
33829
34129
  if (managedPid != null) return { alreadyRunning: true, logPath };
33830
- fs40.mkdirSync(path45.dirname(logPath), { recursive: true });
33831
- const fd = fs40.openSync(logPath, "a");
34130
+ fs39.mkdirSync(path44.dirname(logPath), { recursive: true });
34131
+ const fd = fs39.openSync(logPath, "a");
33832
34132
  const child = spawn8("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
33833
34133
  cwd: args.repoRoot,
33834
34134
  detached: true,
@@ -33846,7 +34146,7 @@ async function serveOnboardingRelayDetached(args) {
33846
34146
  // pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
33847
34147
  shell: process.platform === "win32"
33848
34148
  });
33849
- fs40.closeSync(fd);
34149
+ fs39.closeSync(fd);
33850
34150
  if (child.pid == null) {
33851
34151
  throw new LocalCliError({
33852
34152
  code: "BOOTSTRAP_UI_RELAY_SPAWN_FAILED",
@@ -33855,7 +34155,7 @@ async function serveOnboardingRelayDetached(args) {
33855
34155
  });
33856
34156
  }
33857
34157
  child.unref();
33858
- fs40.writeFileSync(pidPath, String(child.pid), "utf8");
34158
+ fs39.writeFileSync(pidPath, String(child.pid), "utf8");
33859
34159
  const deadline = Date.now() + 45e3;
33860
34160
  const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
33861
34161
  while (Date.now() < deadline) {
@@ -33877,7 +34177,7 @@ function autoOpenOnboardingUi(url, open3 = tryOpenUrl) {
33877
34177
  function stopPidFile(pidPath) {
33878
34178
  let pidStr;
33879
34179
  try {
33880
- pidStr = fs40.readFileSync(pidPath, "utf8").trim();
34180
+ pidStr = fs39.readFileSync(pidPath, "utf8").trim();
33881
34181
  } catch {
33882
34182
  return false;
33883
34183
  }
@@ -33894,7 +34194,7 @@ function stopPidFile(pidPath) {
33894
34194
  }
33895
34195
  }
33896
34196
  try {
33897
- fs40.unlinkSync(pidPath);
34197
+ fs39.unlinkSync(pidPath);
33898
34198
  } catch {
33899
34199
  }
33900
34200
  return true;
@@ -34221,7 +34521,7 @@ function renderDeployFailure(error) {
34221
34521
  }
34222
34522
  var BootstrapUiCommand = class extends M8tCommand {
34223
34523
  static paths = [["bootstrap", "ui"]];
34224
- static usage = Command61.Usage({
34524
+ static usage = Command62.Usage({
34225
34525
  description: "Deploy Ezra + start the local onboarding chat UI in the background (returns immediately).",
34226
34526
  details: [
34227
34527
  "Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
@@ -34242,16 +34542,16 @@ var BootstrapUiCommand = class extends M8tCommand {
34242
34542
  ["Experimental: start the voice relay (no effect on the text-only intake)", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
34243
34543
  ]
34244
34544
  });
34245
- repoRoot = Option58.String("--repo-root");
34246
- port = Option58.String("--port", "3000");
34247
- endpoint = Option58.String("--endpoint", {
34545
+ repoRoot = Option59.String("--repo-root");
34546
+ port = Option59.String("--port", "3000");
34547
+ endpoint = Option59.String("--endpoint", {
34248
34548
  description: "Foundry project endpoint to target \u2014 overrides discovery when this install's resource group holds more than one Foundry project."
34249
34549
  });
34250
- prepOnly = Option58.Boolean("--prep-only", false);
34251
- skipInstall = Option58.Boolean("--skip-install", false);
34252
- stop = Option58.Boolean("--stop", false);
34253
- foreground = Option58.Boolean("--foreground", false);
34254
- voice = Option58.Boolean("--voice", false, {
34550
+ prepOnly = Option59.Boolean("--prep-only", false);
34551
+ skipInstall = Option59.Boolean("--skip-install", false);
34552
+ stop = Option59.Boolean("--stop", false);
34553
+ foreground = Option59.Boolean("--foreground", false);
34554
+ voice = Option59.Boolean("--voice", false, {
34255
34555
  description: "Experimental: when serving, starts the voice relay and writes the intake voice env var. The onboarding intake is text-only and is unaffected by this flag \u2014 no voice worker is registered for it."
34256
34556
  });
34257
34557
  async executeCommand() {
@@ -34291,10 +34591,10 @@ var BootstrapUiCommand = class extends M8tCommand {
34291
34591
  this.context.stderr.write(` ${colors.dim(m)}
34292
34592
  `);
34293
34593
  };
34294
- if (fs41.existsSync(path46.join(os21.homedir(), ".m8t", "config.yaml"))) {
34594
+ if (fs40.existsSync(path45.join(os21.homedir(), ".m8t", "config.yaml"))) {
34295
34595
  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."));
34296
34596
  }
34297
- const credential2 = new DefaultAzureCredential24();
34597
+ const credential2 = new DefaultAzureCredential25();
34298
34598
  const account = await getAzAccount();
34299
34599
  assertNodeVersion();
34300
34600
  out("waiting for Foundry (the installer's foundry-create phase)\u2026");
@@ -34423,10 +34723,10 @@ var BootstrapUiCommand = class extends M8tCommand {
34423
34723
  };
34424
34724
 
34425
34725
  // src/commands/bootstrap/seed-profile.ts
34426
- import { Command as Command62, Option as Option59 } from "clipanion";
34726
+ import { Command as Command63, Option as Option60 } from "clipanion";
34427
34727
  var BootstrapSeedProfileCommand = class extends M8tCommand {
34428
34728
  static paths = [["bootstrap", "seed-profile"]];
34429
- static usage = Command62.Usage({
34729
+ static usage = Command63.Usage({
34430
34730
  description: "Seed your advisors' brains with the founder + company profile from the onboarding intake.",
34431
34731
  details: "Reads the latest onboarding conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/ezra-brain via the GitHub App. Idempotent. --watch polls until the founder finishes the intake.",
34432
34732
  examples: [
@@ -34434,11 +34734,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
34434
34734
  ["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
34435
34735
  ]
34436
34736
  });
34437
- endpoint = Option59.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
34438
- brain = Option59.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
34439
- watch = Option59.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
34440
- timeout = Option59.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
34441
- githubAppCreds = Option59.String("--github-app-creds");
34737
+ endpoint = Option60.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
34738
+ brain = Option60.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
34739
+ watch = Option60.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
34740
+ timeout = Option60.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
34741
+ githubAppCreds = Option60.String("--github-app-creds");
34442
34742
  async executeCommand() {
34443
34743
  const ctx = await resolveSeedContext({
34444
34744
  endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
@@ -34498,10 +34798,10 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
34498
34798
  };
34499
34799
 
34500
34800
  // src/commands/telemetry/enroll.ts
34501
- import * as fs42 from "fs";
34801
+ import * as fs41 from "fs";
34502
34802
  import * as os22 from "os";
34503
- import * as path47 from "path";
34504
- import { Command as Command63, Option as Option60 } from "clipanion";
34803
+ import * as path46 from "path";
34804
+ import { Command as Command64, Option as Option61 } from "clipanion";
34505
34805
  init_errors();
34506
34806
 
34507
34807
  // src/lib/telemetry-enroll.ts
@@ -34583,7 +34883,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
34583
34883
  }
34584
34884
  var TelemetryEnrollCommand = class extends M8tCommand {
34585
34885
  static paths = [["telemetry", "enroll"]];
34586
- static usage = Command63.Usage({
34886
+ static usage = Command64.Usage({
34587
34887
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
34588
34888
  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.",
34589
34889
  examples: [
@@ -34591,11 +34891,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
34591
34891
  ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
34592
34892
  ]
34593
34893
  });
34594
- company = Option60.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
34595
- contactEmail = Option60.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
34596
- subscription = Option60.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
34597
- resourceGroup = Option60.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
34598
- force = Option60.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
34894
+ company = Option61.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
34895
+ contactEmail = Option61.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
34896
+ subscription = Option61.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
34897
+ resourceGroup = Option61.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
34898
+ force = Option61.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
34599
34899
  async executeCommand() {
34600
34900
  const account = await getAzAccount();
34601
34901
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
@@ -34628,13 +34928,13 @@ var TelemetryEnrollCommand = class extends M8tCommand {
34628
34928
  ...company ? { company } : {},
34629
34929
  ...contactEmail ? { contactEmail } : {}
34630
34930
  });
34631
- const keyDir = fs42.mkdtempSync(path47.join(os22.tmpdir(), "m8t-ingest-key-"));
34632
- const keyFile = path47.join(keyDir, "key");
34931
+ const keyDir = fs41.mkdtempSync(path46.join(os22.tmpdir(), "m8t-ingest-key-"));
34932
+ const keyFile = path46.join(keyDir, "key");
34633
34933
  try {
34634
- fs42.writeFileSync(keyFile, ingestKey, { mode: 384 });
34934
+ fs41.writeFileSync(keyFile, ingestKey, { mode: 384 });
34635
34935
  await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--file", keyFile, "--only-show-errors"]);
34636
34936
  } finally {
34637
- fs42.rmSync(keyDir, { recursive: true, force: true });
34937
+ fs41.rmSync(keyDir, { recursive: true, force: true });
34638
34938
  }
34639
34939
  this.context.stdout.write(
34640
34940
  `${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
@@ -34645,7 +34945,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
34645
34945
  };
34646
34946
 
34647
34947
  // src/commands/companion/bridge.ts
34648
- import { Command as Command64, Option as Option61 } from "clipanion";
34948
+ import { Command as Command65, Option as Option62 } from "clipanion";
34649
34949
 
34650
34950
  // ../../packages/companion-bridge-contract/src/index.ts
34651
34951
  var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
@@ -36101,14 +36401,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps4) {
36101
36401
  return 3;
36102
36402
  }
36103
36403
  }
36104
- var CompanionBridgeCommand = class extends Command64 {
36404
+ var CompanionBridgeCommand = class extends Command65 {
36105
36405
  static paths = [["companion", "_bridge"]];
36106
36406
  /**
36107
36407
  * One process serving many requests instead of one per request, so the
36108
36408
  * session keeps its authenticated context between them. A CLI predating the
36109
36409
  * flag rejects it outright, which is how the app knows to fall back.
36110
36410
  */
36111
- serve = Option61.Boolean("--serve", false);
36411
+ serve = Option62.Boolean("--serve", false);
36112
36412
  async execute() {
36113
36413
  if (this.serve) {
36114
36414
  return runCompanionBridgeServe(
@@ -36126,7 +36426,7 @@ var CompanionBridgeCommand = class extends Command64 {
36126
36426
  };
36127
36427
 
36128
36428
  // src/commands/companion/status.ts
36129
- import { Command as Command65 } from "clipanion";
36429
+ import { Command as Command66 } from "clipanion";
36130
36430
  async function withTimeout(work, ms) {
36131
36431
  let timer;
36132
36432
  try {
@@ -36198,7 +36498,7 @@ Run: m8t companion install
36198
36498
  }
36199
36499
  var CompanionStatusCommand = class extends M8tCommand {
36200
36500
  static paths = [["companion", "status"]];
36201
- static usage = Command65.Usage({
36501
+ static usage = Command66.Usage({
36202
36502
  description: "Verify the installed desktop companion without launching it."
36203
36503
  });
36204
36504
  async executeCommand() {
@@ -36212,7 +36512,7 @@ var CompanionStatusCommand = class extends M8tCommand {
36212
36512
  };
36213
36513
 
36214
36514
  // src/commands/companion/repair.ts
36215
- import { Command as Command66, Option as Option62 } from "clipanion";
36515
+ import { Command as Command67, Option as Option63 } from "clipanion";
36216
36516
  async function runCompanionRepairCommand(stdout, repair) {
36217
36517
  const state = await repair();
36218
36518
  if (state.state === "not-released") {
@@ -36231,10 +36531,10 @@ async function runCompanionRepairCommand(stdout, repair) {
36231
36531
  }
36232
36532
  var CompanionRepairCommand = class extends M8tCommand {
36233
36533
  static paths = [["companion", "repair"]];
36234
- static usage = Command66.Usage({
36534
+ static usage = Command67.Usage({
36235
36535
  description: "Restore the desktop companions and start-at-login state."
36236
36536
  });
36237
- resourceGroup = Option62.String("--resource-group", {
36537
+ resourceGroup = Option63.String("--resource-group", {
36238
36538
  description: "Which deployment to bind to, when the subscription holds more than one."
36239
36539
  });
36240
36540
  async executeCommand() {
@@ -36248,7 +36548,7 @@ var CompanionRepairCommand = class extends M8tCommand {
36248
36548
  };
36249
36549
 
36250
36550
  // src/commands/companion/uninstall.ts
36251
- import { Command as Command67 } from "clipanion";
36551
+ import { Command as Command68 } from "clipanion";
36252
36552
  async function runCompanionUninstallCommand(stdout, uninstall) {
36253
36553
  const state = await uninstall();
36254
36554
  if (state.state !== "not-installed") {
@@ -36260,7 +36560,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
36260
36560
  }
36261
36561
  var CompanionUninstallCommand = class extends M8tCommand {
36262
36562
  static paths = [["companion", "uninstall"]];
36263
- static usage = Command67.Usage({
36563
+ static usage = Command68.Usage({
36264
36564
  description: "Remove only this user's desktop companion installation."
36265
36565
  });
36266
36566
  async executeCommand() {
@@ -36317,8 +36617,9 @@ cli.register(AzureExecDeployCommand);
36317
36617
  cli.register(PlatformStatusCommand);
36318
36618
  cli.register(PlatformUpdateCommand);
36319
36619
  cli.register(PlatformConvergeCommand);
36620
+ cli.register(PlatformClearIntentCommand);
36621
+ cli.register(PlatformRequestUpdateCommand);
36320
36622
  cli.register(PlatformSeedStampCommand);
36321
- cli.register(PlatformStampBuildCommand);
36322
36623
  cli.register(PlatformPolicyCommand);
36323
36624
  cli.register(PlatformEnableCostReportCommand);
36324
36625
  cli.register(PlatformEnableAutoUpdateCommand);