@m8t-stack/cli 0.2.16 → 0.2.17

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.
Files changed (3) hide show
  1. package/dist/cli.js +1645 -611
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -814,6 +814,7 @@ async function createCoderVersion(args) {
814
814
  environment_variables: args.env
815
815
  };
816
816
  const metadata = {
817
+ ...Object.fromEntries(Object.entries(args.metadata).filter(([, v2]) => v2 !== null)),
817
818
  source: args.metadata.source,
818
819
  kind: args.metadata.kind,
819
820
  persona: args.metadata.persona
@@ -886,6 +887,7 @@ async function createPromptVersion(args) {
886
887
  persona: args.metadata.persona
887
888
  };
888
889
  if (args.metadata.personaVersion !== null) metadata.personaVersion = args.metadata.personaVersion;
890
+ if (args.metadata.fillableFieldValues !== void 0) metadata.fillableFieldValues = args.metadata.fillableFieldValues;
889
891
  const version = await project.agents.createVersion(args.name, definition, { metadata });
890
892
  const v = version.version;
891
893
  if (v === void 0) {
@@ -940,6 +942,24 @@ async function grantFoundryUser(args) {
940
942
  // RoleAssignmentExists — already granted
941
943
  });
942
944
  }
945
+ async function grantStorageTableDataContributor(args) {
946
+ const roleAssignmentId = randomUUID();
947
+ const url = `${ARM2}${args.scope}/providers/Microsoft.Authorization/roleAssignments/${roleAssignmentId}?api-version=${RA_API}`;
948
+ await authedJsonResult({
949
+ credential: args.credential,
950
+ scope: ARM_SCOPE3,
951
+ method: "PUT",
952
+ url,
953
+ body: {
954
+ properties: {
955
+ roleDefinitionId: `/subscriptions/${args.subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/${STORAGE_TABLE_DATA_CONTRIBUTOR_ROLE_ID}`,
956
+ principalId: args.principalId,
957
+ principalType: args.principalType ?? "User"
958
+ }
959
+ },
960
+ okStatuses: [409]
961
+ });
962
+ }
943
963
  async function listRoleAssignments(credential2, scope, filter) {
944
964
  const q = `api-version=${RA_API}${filter ? `&$filter=${encodeURIComponent(filter)}` : ""}`;
945
965
  const url = `${ARM2}${scope}/providers/Microsoft.Authorization/roleAssignments?${q}`;
@@ -1057,7 +1077,7 @@ async function resolveKeyVaultResourceId(args) {
1057
1077
  }
1058
1078
  return id;
1059
1079
  }
1060
- var ARM2, ARM_SCOPE3, RA_API, FOUNDRY_USER_ROLE_ID, ACR_PULL_ROLE_ID, OWNER_ROLE_ID, USER_ACCESS_ADMIN_ROLE_ID, RBAC_ADMIN_ROLE_ID, ASSIGNING_ROLE_IDS, KV_SECRETS_USER_ROLE_ID, CONTRIBUTOR_ROLE_ID;
1080
+ var ARM2, ARM_SCOPE3, RA_API, FOUNDRY_USER_ROLE_ID, ACR_PULL_ROLE_ID, OWNER_ROLE_ID, USER_ACCESS_ADMIN_ROLE_ID, RBAC_ADMIN_ROLE_ID, STORAGE_TABLE_DATA_CONTRIBUTOR_ROLE_ID, ASSIGNING_ROLE_IDS, KV_SECRETS_USER_ROLE_ID, CONTRIBUTOR_ROLE_ID;
1061
1081
  var init_rbac = __esm({
1062
1082
  "src/lib/rbac.ts"() {
1063
1083
  "use strict";
@@ -1071,6 +1091,7 @@ var init_rbac = __esm({
1071
1091
  OWNER_ROLE_ID = "8e3af657-a8ff-443c-a75c-2fe8c4bcb635";
1072
1092
  USER_ACCESS_ADMIN_ROLE_ID = "18d7d88d-d35e-4fb5-a5c3-7773c20a72d9";
1073
1093
  RBAC_ADMIN_ROLE_ID = "f58310d9-a9f6-439a-9e8d-f62e7b41a168";
1094
+ STORAGE_TABLE_DATA_CONTRIBUTOR_ROLE_ID = "0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3";
1074
1095
  ASSIGNING_ROLE_IDS = [OWNER_ROLE_ID, USER_ACCESS_ADMIN_ROLE_ID, RBAC_ADMIN_ROLE_ID];
1075
1096
  KV_SECRETS_USER_ROLE_ID = "4633458b-17de-408a-b874-0445c86b69e6";
1076
1097
  CONTRIBUTOR_ROLE_ID = "b24988ac-6180-42a0-ab88-20f7382dd24c";
@@ -1206,7 +1227,7 @@ var init_enable_hosted_brain = __esm({
1206
1227
  import { Builtins, Cli } from "clipanion";
1207
1228
 
1208
1229
  // src/lib/package-version.ts
1209
- var CLI_VERSION = "0.2.16";
1230
+ var CLI_VERSION = "0.2.17";
1210
1231
 
1211
1232
  // src/lib/render-error.ts
1212
1233
  init_errors();
@@ -1520,7 +1541,8 @@ var colors = {
1520
1541
  hint: (s) => pc.dim(s),
1521
1542
  success: (s) => pc.green(s),
1522
1543
  field: (s) => pc.bold(s),
1523
- dim: (s) => pc.dim(s)
1544
+ dim: (s) => pc.dim(s),
1545
+ warn: (s) => pc.yellow(s)
1524
1546
  };
1525
1547
 
1526
1548
  // ../../node_modules/.pnpm/openai@6.44.0_ws@8.21.0_zod@4.4.3/node_modules/openai/internal/tslib.mjs
@@ -3304,12 +3326,12 @@ function encodeURIPath(str4) {
3304
3326
  return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
3305
3327
  }
3306
3328
  var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
3307
- var createPathTagFunction = (pathEncoder = encodeURIPath) => function path29(statics, ...params) {
3329
+ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path32(statics, ...params) {
3308
3330
  if (statics.length === 1)
3309
3331
  return statics[0];
3310
3332
  let postPath = false;
3311
3333
  const invalidSegments = [];
3312
- const path30 = statics.reduce((previousValue, currentValue, index) => {
3334
+ const path33 = statics.reduce((previousValue, currentValue, index) => {
3313
3335
  if (/[?#]/.test(currentValue)) {
3314
3336
  postPath = true;
3315
3337
  }
@@ -3326,7 +3348,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path29(sta
3326
3348
  }
3327
3349
  return previousValue + currentValue + (index === params.length ? "" : encoded);
3328
3350
  }, "");
3329
- const pathOnly = path30.split(/[?#]/, 1)[0];
3351
+ const pathOnly = path33.split(/[?#]/, 1)[0];
3330
3352
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
3331
3353
  let match;
3332
3354
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -3347,10 +3369,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path29(sta
3347
3369
  }, "");
3348
3370
  throw new OpenAIError(`Path parameters result in path with invalid segments:
3349
3371
  ${invalidSegments.map((e) => e.error).join("\n")}
3350
- ${path30}
3372
+ ${path33}
3351
3373
  ${underline}`);
3352
3374
  }
3353
- return path30;
3375
+ return path33;
3354
3376
  };
3355
3377
  var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
3356
3378
 
@@ -10867,9 +10889,9 @@ var OpenAI = class {
10867
10889
  this.apiKey = token;
10868
10890
  return true;
10869
10891
  }
10870
- buildURL(path29, query, defaultBaseURL) {
10892
+ buildURL(path32, query, defaultBaseURL) {
10871
10893
  const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
10872
- const url = isAbsoluteURL(path29) ? new URL(path29) : new URL(baseURL + (baseURL.endsWith("/") && path29.startsWith("/") ? path29.slice(1) : path29));
10894
+ const url = isAbsoluteURL(path32) ? new URL(path32) : new URL(baseURL + (baseURL.endsWith("/") && path32.startsWith("/") ? path32.slice(1) : path32));
10873
10895
  const defaultQuery = this.defaultQuery();
10874
10896
  const pathQuery = Object.fromEntries(url.searchParams);
10875
10897
  if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
@@ -10897,24 +10919,24 @@ var OpenAI = class {
10897
10919
  */
10898
10920
  async prepareRequest(request, { url, options }) {
10899
10921
  }
10900
- get(path29, opts) {
10901
- return this.methodRequest("get", path29, opts);
10922
+ get(path32, opts) {
10923
+ return this.methodRequest("get", path32, opts);
10902
10924
  }
10903
- post(path29, opts) {
10904
- return this.methodRequest("post", path29, opts);
10925
+ post(path32, opts) {
10926
+ return this.methodRequest("post", path32, opts);
10905
10927
  }
10906
- patch(path29, opts) {
10907
- return this.methodRequest("patch", path29, opts);
10928
+ patch(path32, opts) {
10929
+ return this.methodRequest("patch", path32, opts);
10908
10930
  }
10909
- put(path29, opts) {
10910
- return this.methodRequest("put", path29, opts);
10931
+ put(path32, opts) {
10932
+ return this.methodRequest("put", path32, opts);
10911
10933
  }
10912
- delete(path29, opts) {
10913
- return this.methodRequest("delete", path29, opts);
10934
+ delete(path32, opts) {
10935
+ return this.methodRequest("delete", path32, opts);
10914
10936
  }
10915
- methodRequest(method, path29, opts) {
10937
+ methodRequest(method, path32, opts) {
10916
10938
  return this.request(Promise.resolve(opts).then((opts2) => {
10917
- return { method, path: path29, ...opts2 };
10939
+ return { method, path: path32, ...opts2 };
10918
10940
  }));
10919
10941
  }
10920
10942
  request(options, remainingRetries = null) {
@@ -11036,8 +11058,8 @@ var OpenAI = class {
11036
11058
  }));
11037
11059
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
11038
11060
  }
11039
- getAPIList(path29, Page2, opts) {
11040
- return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path29, ...opts2 })) : { method: "get", path: path29, ...opts });
11061
+ getAPIList(path32, Page2, opts) {
11062
+ return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path32, ...opts2 })) : { method: "get", path: path32, ...opts });
11041
11063
  }
11042
11064
  requestAPIList(Page2, options) {
11043
11065
  const request = this.makeRequest(options, null, void 0);
@@ -11131,8 +11153,8 @@ var OpenAI = class {
11131
11153
  }
11132
11154
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
11133
11155
  const options = { ...inputOptions };
11134
- const { method, path: path29, query, defaultBaseURL } = options;
11135
- const url = this.buildURL(path29, query, defaultBaseURL);
11156
+ const { method, path: path32, query, defaultBaseURL } = options;
11157
+ const url = this.buildURL(path32, query, defaultBaseURL);
11136
11158
  if ("timeout" in options)
11137
11159
  validatePositiveInteger("timeout", options.timeout);
11138
11160
  options.timeout = options.timeout ?? this.timeout;
@@ -12134,10 +12156,10 @@ var BindListCommand = class extends M8tCommand {
12134
12156
  const qs = new URLSearchParams();
12135
12157
  if (this.channel) qs.set("channel", this.channel);
12136
12158
  if (this.status) qs.set("status", this.status);
12137
- const path29 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
12159
+ const path32 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
12138
12160
  const data = await apiCall(
12139
12161
  { gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
12140
- { method: "GET", path: path29 },
12162
+ { method: "GET", path: path32 },
12141
12163
  (msg) => this.context.stderr.write(msg + "\n")
12142
12164
  );
12143
12165
  if (mode === "json") {
@@ -13338,8 +13360,8 @@ ${r.stderr}`;
13338
13360
  message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
13339
13361
  });
13340
13362
  },
13341
- getFile: async (path29) => {
13342
- const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path29}`]);
13363
+ getFile: async (path32) => {
13364
+ const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path32}`]);
13343
13365
  return r.exitCode === 0 ? r.stdout : null;
13344
13366
  },
13345
13367
  isEmpty: async () => {
@@ -13788,12 +13810,12 @@ function stripBrainLoader(instructions) {
13788
13810
  if (h !== -1) return instructions.slice(0, h).replace(/\s+$/, "");
13789
13811
  return instructions.replace(/\s+$/, "");
13790
13812
  }
13791
- function appendBrainLoader(base, loaderText) {
13813
+ function appendBrainLoader(base, loaderText2) {
13792
13814
  const clean2 = stripBrainLoader(base);
13793
13815
  return `${clean2}
13794
13816
 
13795
13817
  ${LOADER_START}
13796
- ${loaderText.replace(/\s+$/, "")}
13818
+ ${loaderText2.replace(/\s+$/, "")}
13797
13819
  ${LOADER_END}
13798
13820
  `;
13799
13821
  }
@@ -13834,9 +13856,32 @@ function hasA2aSnippet(instructions) {
13834
13856
 
13835
13857
  // src/lib/brain-link.ts
13836
13858
  init_foundry_agent_get();
13859
+
13860
+ // src/lib/foundry-agent-version.ts
13861
+ init_errors();
13862
+ var FOUNDRY_DATA_SCOPE = "https://ai.azure.com/.default";
13863
+ async function createAgentVersion(args, fetchImpl = fetch) {
13864
+ const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
13865
+ if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
13866
+ const url = `${args.projectEndpoint}/agents/${args.agentName}/versions?api-version=v1`;
13867
+ const res = await fetchImpl(url, {
13868
+ method: "POST",
13869
+ headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} },
13870
+ body: JSON.stringify({ definition: args.definition, metadata: args.metadata })
13871
+ });
13872
+ if (!res.ok) {
13873
+ const text = await res.text();
13874
+ throw new LocalCliError({ code: "AGENT_CREATE_VERSION_FAILED", message: `POST ${url}: HTTP ${res.status.toString()}
13875
+ ${text.slice(0, 500)}` });
13876
+ }
13877
+ const data = await res.json();
13878
+ if (!data.version) throw new LocalCliError({ code: "AGENT_CREATE_VERSION_NO_VERSION", message: "createVersion returned no version" });
13879
+ return data.version;
13880
+ }
13881
+
13882
+ // src/lib/brain-link.ts
13837
13883
  init_brain_yaml_mirror();
13838
13884
  var FOUNDRY_ARM_API = "2025-04-01-preview";
13839
- var FOUNDRY_DATA_SCOPE = "https://ai.azure.com/.default";
13840
13885
  var ARM_SCOPE4 = "https://management.azure.com/.default";
13841
13886
  var MCP_TOOL_NAMES = [
13842
13887
  "get_file_contents",
@@ -14043,10 +14088,6 @@ ${text.slice(0, 300)}`
14043
14088
  }
14044
14089
  }
14045
14090
  async function createBrainEnabledVersion(args) {
14046
- const fndToken = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
14047
- if (!fndToken?.token) {
14048
- throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
14049
- }
14050
14091
  const otherTools = (args.currentDefinition.tools ?? []).filter(
14051
14092
  (t) => !(t.type === "mcp" && t.server_label === "brain")
14052
14093
  );
@@ -14067,25 +14108,13 @@ async function createBrainEnabledVersion(args) {
14067
14108
  ...args.currentMetadata,
14068
14109
  brain: args.brainLinkJson
14069
14110
  };
14070
- const url = `${args.projectEndpoint}/agents/${args.agentName}/versions?api-version=v1`;
14071
- const res = await fetch(url, {
14072
- method: "POST",
14073
- headers: { Authorization: `Bearer ${fndToken.token}`, "Content-Type": "application/json" },
14074
- body: JSON.stringify({ definition, metadata })
14111
+ return createAgentVersion({
14112
+ credential: args.credential,
14113
+ projectEndpoint: args.projectEndpoint,
14114
+ agentName: args.agentName,
14115
+ definition,
14116
+ metadata
14075
14117
  });
14076
- if (!res.ok) {
14077
- const text = await res.text();
14078
- throw new LocalCliError({
14079
- code: "AGENT_CREATE_VERSION_FAILED",
14080
- message: `POST ${url}: HTTP ${res.status.toString()}
14081
- ${text.slice(0, 500)}`
14082
- });
14083
- }
14084
- const data = await res.json();
14085
- if (!data.version) {
14086
- throw new LocalCliError({ code: "AGENT_CREATE_VERSION_NO_VERSION", message: `createVersion returned no version` });
14087
- }
14088
- return data.version;
14089
14118
  }
14090
14119
 
14091
14120
  // src/lib/brain-seed.ts
@@ -14125,8 +14154,8 @@ function isM8tBrainMarker(yamlText) {
14125
14154
  }
14126
14155
  if (!parsed || typeof parsed !== "object") return false;
14127
14156
  const root = parsed;
14128
- const isObj2 = (v) => typeof v === "object" && v !== null;
14129
- return isObj2(root.engine) || isObj2(root.processes) || isObj2(root.link);
14157
+ const isObj3 = (v) => typeof v === "object" && v !== null;
14158
+ return isObj3(root.engine) || isObj3(root.processes) || isObj3(root.link);
14130
14159
  }
14131
14160
  async function createBlankRepo(args) {
14132
14161
  const res = await fetch(`https://api.github.com/orgs/${args.org}/repos`, {
@@ -14280,7 +14309,7 @@ function appRepoProbe(args) {
14280
14309
  const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14281
14310
  return res.status;
14282
14311
  },
14283
- getFile: (path29) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path29, fetchImpl: args.fetchImpl }),
14312
+ getFile: (path32) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path32, fetchImpl: args.fetchImpl }),
14284
14313
  isEmpty: async () => {
14285
14314
  const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14286
14315
  return res.status === 404;
@@ -14599,7 +14628,9 @@ var BrainCreateCommand = class extends M8tCommand {
14599
14628
  repoRoot,
14600
14629
  installationId: resolvedInstallationId,
14601
14630
  skipIfLinked: true,
14602
- onProgress: progress
14631
+ onProgress: progress,
14632
+ subscriptionId,
14633
+ project
14603
14634
  });
14604
14635
  } finally {
14605
14636
  fs10.rmSync(tmpDir, { recursive: true, force: true });
@@ -15290,7 +15321,7 @@ async function deployPromptAdvisor(args) {
15290
15321
  model,
15291
15322
  instructions,
15292
15323
  reasoningEffort,
15293
- metadata: { persona: args.persona, personaVersion }
15324
+ metadata: { persona: args.persona, personaVersion, fillableFieldValues: JSON.stringify(valuesMap) }
15294
15325
  });
15295
15326
  }
15296
15327
 
@@ -15416,7 +15447,7 @@ async function awaitDataPlaneReady(opts) {
15416
15447
  const consecutive = opts.consecutive ?? 3;
15417
15448
  const attempts = opts.attempts ?? 60;
15418
15449
  const intervalMs = opts.intervalMs ?? 5e3;
15419
- const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
15450
+ const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
15420
15451
  let streak = 0;
15421
15452
  for (let i = 1; i <= attempts; i++) {
15422
15453
  const r = await opts.probe();
@@ -15438,7 +15469,7 @@ async function awaitDataPlaneReady(opts) {
15438
15469
  streak = 0;
15439
15470
  opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
15440
15471
  }
15441
- if (i < attempts) await sleep2(intervalMs);
15472
+ if (i < attempts) await sleep3(intervalMs);
15442
15473
  }
15443
15474
  return { ready: false, attempts };
15444
15475
  }
@@ -15559,7 +15590,6 @@ function str2(v) {
15559
15590
 
15560
15591
  // src/lib/a2a-enable.ts
15561
15592
  var FOUNDRY_ARM_API3 = "2025-04-01-preview";
15562
- var FOUNDRY_DATA_SCOPE3 = "https://ai.azure.com/.default";
15563
15593
  var ARM_SCOPE6 = "https://management.azure.com/.default";
15564
15594
  var A2A_TOOLS = ["discover_workers", "invoke_worker", "check_delegation"];
15565
15595
  async function enableA2a(args) {
@@ -15580,7 +15610,7 @@ async function enableA2a(args) {
15580
15610
  if (current.definition.kind === "hosted") {
15581
15611
  progress("Registering the hosted target (metadata only)\u2026");
15582
15612
  const metadata2 = { ...current.metadata ?? {}, a2a: "true", a2aCard: a2aCardJson };
15583
- const foundryVersion2 = await createVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, definition: current.definition, metadata: metadata2, extraHeaders: { "Foundry-Features": "HostedAgents=V1Preview" } });
15613
+ const foundryVersion2 = await createAgentVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, definition: current.definition, metadata: metadata2, extraHeaders: { "Foundry-Features": "HostedAgents=V1Preview" } });
15584
15614
  return { mode: "target", foundryVersion: foundryVersion2 };
15585
15615
  }
15586
15616
  if (current.definition.kind !== "prompt") {
@@ -15605,7 +15635,7 @@ async function enableA2a(args) {
15605
15635
  a2aCard: a2aCardJson,
15606
15636
  a2aBearerHash: bearerHash
15607
15637
  };
15608
- const foundryVersion = await createVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, definition, metadata });
15638
+ const foundryVersion = await createAgentVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, definition, metadata });
15609
15639
  return { mode: "caller", connectionName, foundryVersion };
15610
15640
  }
15611
15641
  async function disableA2a(args) {
@@ -15622,7 +15652,7 @@ async function disableA2a(args) {
15622
15652
  delete metadata.a2a;
15623
15653
  delete metadata.a2aCard;
15624
15654
  delete metadata.a2aBearerHash;
15625
- const foundryVersion = await createVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, definition, metadata });
15655
+ const foundryVersion = await createAgentVersion({ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, definition, metadata });
15626
15656
  progress("Deleting the A2A connection\u2026");
15627
15657
  await deleteConnection2({ credential: args.credential, projectArmId: args.projectArmId, connectionName });
15628
15658
  return { connectionName, foundryVersion };
@@ -15664,20 +15694,6 @@ async function deleteConnection2(args) {
15664
15694
  await fetch(url, { method: "DELETE", headers: { Authorization: `Bearer ${token.token}` } }).catch(() => {
15665
15695
  });
15666
15696
  }
15667
- async function createVersion(args) {
15668
- const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE3);
15669
- if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
15670
- const url = `${args.projectEndpoint}/agents/${args.agentName}/versions?api-version=v1`;
15671
- const res = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} }, body: JSON.stringify({ definition: args.definition, metadata: args.metadata }) });
15672
- if (!res.ok) {
15673
- const text = await res.text();
15674
- throw new LocalCliError({ code: "A2A_CREATE_VERSION_FAILED", message: `POST ${url}: HTTP ${res.status.toString()}
15675
- ${text.slice(0, 500)}` });
15676
- }
15677
- const data = await res.json();
15678
- if (!data.version) throw new LocalCliError({ code: "A2A_CREATE_VERSION_NO_VERSION", message: "createVersion returned no version" });
15679
- return data.version;
15680
- }
15681
15697
 
15682
15698
  // src/lib/agent-remove.ts
15683
15699
  init_foundry_agents();
@@ -16275,6 +16291,7 @@ import * as path16 from "path";
16275
16291
  import { Command as Command28, Option as Option26 } from "clipanion";
16276
16292
  import { DefaultAzureCredential as DefaultAzureCredential13 } from "@azure/identity";
16277
16293
  init_errors();
16294
+ init_foundry_agent_get();
16278
16295
 
16279
16296
  // src/lib/preconditions.ts
16280
16297
  init_errors();
@@ -16480,7 +16497,7 @@ init_errors();
16480
16497
  async function deployHostedWorker(args) {
16481
16498
  const onProgress = args.onProgress ?? ((_m) => {
16482
16499
  });
16483
- const sleep2 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16500
+ const sleep3 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16484
16501
  const now = args.now ?? (() => Date.now());
16485
16502
  const timeout = args.pollTimeoutMs ?? 8 * 60 * 1e3;
16486
16503
  const interval = args.pollIntervalMs ?? 1e4;
@@ -16532,7 +16549,7 @@ async function deployHostedWorker(args) {
16532
16549
  hint: "Provisioning can take 2\u20135 min; re-run with the same name to resume, or check the version status in the Foundry portal."
16533
16550
  });
16534
16551
  }
16535
- await sleep2(interval);
16552
+ await sleep3(interval);
16536
16553
  }
16537
16554
  }
16538
16555
 
@@ -16726,20 +16743,6 @@ var CoderDeployCommand = class extends M8tCommand {
16726
16743
  hint: "small=0.5/1Gi \xB7 medium=1/2Gi \xB7 large=2/4Gi"
16727
16744
  });
16728
16745
  }
16729
- const env = {
16730
- MODEL_DEPLOYMENT_NAME: this.modelDeployment ?? (typeof this.brain === "string" ? "gpt-5-mini" : DEFAULT_MODEL)
16731
- };
16732
- for (const pair of this.env ?? []) {
16733
- const eq = pair.indexOf("=");
16734
- if (eq <= 0) {
16735
- throw new LocalCliError({
16736
- code: "USAGE",
16737
- message: `Invalid --env '${pair}'. Expected KEY=VALUE.`,
16738
- hint: "Example: --env M8T_CODER_MAX_ITERATIONS=20"
16739
- });
16740
- }
16741
- env[pair.slice(0, eq)] = pair.slice(eq + 1);
16742
- }
16743
16746
  const persona = this.persona ?? "coding-agent";
16744
16747
  const imageInput = this.image ?? DEFAULT_IMAGE;
16745
16748
  const repoRef = imageInput.includes("/") ? imageInput : `${DEFAULT_REGISTRY}/${imageInput}`;
@@ -16760,6 +16763,30 @@ var CoderDeployCommand = class extends M8tCommand {
16760
16763
  interactive,
16761
16764
  endpoint: this.endpoint
16762
16765
  });
16766
+ let currentEnv = {};
16767
+ let currentMetadata = {};
16768
+ try {
16769
+ const current = await getAgentVersion({ credential: credential2, projectEndpoint: project.endpoint, agentName: this.name });
16770
+ const def = current.definition;
16771
+ currentEnv = { ...def.environment_variables ?? {} };
16772
+ currentMetadata = { ...current.metadata ?? {} };
16773
+ } catch {
16774
+ }
16775
+ const env = {
16776
+ ...currentEnv,
16777
+ MODEL_DEPLOYMENT_NAME: this.modelDeployment ?? currentEnv.MODEL_DEPLOYMENT_NAME ?? (typeof this.brain === "string" ? "gpt-5-mini" : DEFAULT_MODEL)
16778
+ };
16779
+ for (const pair of this.env ?? []) {
16780
+ const eq = pair.indexOf("=");
16781
+ if (eq <= 0) {
16782
+ throw new LocalCliError({
16783
+ code: "USAGE",
16784
+ message: `Invalid --env '${pair}'. Expected KEY=VALUE.`,
16785
+ hint: "Example: --env M8T_CODER_MAX_ITERATIONS=20"
16786
+ });
16787
+ }
16788
+ env[pair.slice(0, eq)] = pair.slice(eq + 1);
16789
+ }
16763
16790
  const staged = await stagePublicImageIfNeeded({ image: requestedImage, project });
16764
16791
  for (const n of staged.notes) {
16765
16792
  this.context.stderr.write(`${colors.hint("note:")} ${n}
@@ -16804,7 +16831,7 @@ var CoderDeployCommand = class extends M8tCommand {
16804
16831
  cpu: preset.cpu,
16805
16832
  memory: preset.memory,
16806
16833
  env,
16807
- metadata: { source: "m8t-stack-POC", kind: "hosted", persona: personaName, personaVersion },
16834
+ metadata: { ...currentMetadata, source: "m8t-stack-POC", kind: "hosted", persona: personaName, personaVersion },
16808
16835
  onProgress
16809
16836
  });
16810
16837
  if (typeof this.brain === "string") {
@@ -17336,6 +17363,7 @@ var AzureExecDeployCommand = class extends M8tCommand {
17336
17363
 
17337
17364
  // src/commands/platform/status.ts
17338
17365
  import { Command as Command31, Option as Option29 } from "clipanion";
17366
+ import { DefaultAzureCredential as DefaultAzureCredential16 } from "@azure/identity";
17339
17367
 
17340
17368
  // src/lib/platform-update.ts
17341
17369
  init_errors();
@@ -17411,20 +17439,6 @@ function planUpdate(opts) {
17411
17439
  }
17412
17440
  return { kind: "roll", current: currentTag, available, target: available, repo: opts.imageRepo };
17413
17441
  }
17414
- function summarizePlan(plan) {
17415
- switch (plan.kind) {
17416
- case "noop":
17417
- return { current: plan.current, available: plan.available, verdict: "up to date" };
17418
- case "roll":
17419
- return { current: plan.current, available: plan.available, verdict: "update available" };
17420
- case "no-versions":
17421
- return { current: plan.current, available: "-", verdict: "no published versions found" };
17422
- case "refuse-byoc":
17423
- return { current: plan.current, available: "-", verdict: `private-ACR image (${plan.currentRepo}) \u2014 not tracked` };
17424
- case "tag-not-found":
17425
- return { current: plan.current, available: plan.requested, verdict: `tag ${plan.requested} not published` };
17426
- }
17427
- }
17428
17442
  async function fetchPublicTags(repo, fetchImpl = fetch) {
17429
17443
  if (!repo.startsWith("ghcr.io/")) {
17430
17444
  throw new LocalCliError({
@@ -17433,8 +17447,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17433
17447
  hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
17434
17448
  });
17435
17449
  }
17436
- const path29 = repo.replace(/^ghcr\.io\//, "");
17437
- const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path29}:pull`);
17450
+ const path32 = repo.replace(/^ghcr\.io\//, "");
17451
+ const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path32}:pull`);
17438
17452
  if (!tokenRes.ok) {
17439
17453
  throw new LocalCliError({
17440
17454
  code: "PLATFORM_GHCR_TOKEN_FAILED",
@@ -17450,7 +17464,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17450
17464
  hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
17451
17465
  });
17452
17466
  }
17453
- const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path29}/tags/list`, {
17467
+ const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path32}/tags/list`, {
17454
17468
  headers: { Authorization: `Bearer ${token}` }
17455
17469
  });
17456
17470
  if (!tagsRes.ok) {
@@ -17463,210 +17477,1375 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17463
17477
  return body.tags ?? [];
17464
17478
  }
17465
17479
 
17466
- // src/commands/platform/status.ts
17467
- var PlatformStatusCommand = class extends M8tCommand {
17468
- static paths = [["platform", "status"]];
17469
- static usage = Command31.Usage({
17470
- description: "Show the deployed platform image vs the newest published version.",
17471
- details: "Discovers the live gateway, reads its current image tag, and compares it to the newest vX.Y.Z published on the public GHCR repo. Read-only \u2014 never rolls a revision."
17472
- });
17473
- subscription = Option29.String("--subscription");
17474
- imageRepo = Option29.String("--image-repo", DEFAULT_IMAGE_REPO);
17475
- output = Option29.String("--output");
17476
- resourceGroup = Option29.String("--resource-group", {
17477
- description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
17478
- });
17479
- async executeCommand() {
17480
- const mode = resolveOutputMode(
17481
- this.output,
17482
- this.context.stdout
17483
- );
17484
- const ctx = await resolveGatewayContext({
17485
- interactive: mode !== "json",
17486
- subscriptionId: this.subscription,
17487
- resourceGroup: this.resourceGroup
17488
- });
17489
- const { resourceGroup, name } = parseContainerAppResourceId(ctx.containerAppResourceId);
17490
- const currentImage = (await runAz([
17491
- "containerapp",
17492
- "show",
17493
- "-g",
17494
- resourceGroup,
17495
- "-n",
17496
- name,
17497
- "--query",
17498
- "properties.template.containers[0].image",
17499
- "-o",
17500
- "tsv"
17501
- ])).trim();
17502
- const tags = await fetchPublicTags(this.imageRepo);
17503
- const plan = planUpdate({ currentImage, availableTags: tags, imageRepo: this.imageRepo });
17504
- const s = summarizePlan(plan);
17505
- if (mode === "json") {
17506
- this.context.stdout.write(renderJson({ gateway: name, resourceGroup, ...s, kind: plan.kind }) + "\n");
17507
- return 0;
17508
- }
17509
- this.context.stdout.write(
17510
- renderKeyValueBlock([
17511
- { key: "gateway", value: name },
17512
- { key: "current", value: s.current },
17513
- { key: "available", value: s.available },
17514
- { key: "status", value: s.verdict }
17515
- ]) + "\n"
17516
- );
17517
- return 0;
17518
- }
17519
- };
17480
+ // src/lib/release-channel.ts
17481
+ import * as fs18 from "fs";
17520
17482
 
17521
- // src/commands/platform/update.ts
17522
- import { Command as Command32, Option as Option30 } from "clipanion";
17523
- import { confirm as confirm5 } from "@inquirer/prompts";
17524
- init_errors();
17525
- var PlatformUpdateCommand = class extends M8tCommand {
17526
- static paths = [["platform", "update"]];
17527
- static usage = Command32.Usage({
17528
- description: "Roll the gateway to the newest published platform image.",
17529
- details: "Discovers the live gateway, compares its image tag to the newest vX.Y.Z on the public GHCR repo, and (if newer) rolls a new Container Apps revision via 'az containerapp update --image'. A fresh tag is required to roll in single-revision mode. Use --check to preview, --to to pin a version, --yes to skip the prompt."
17530
- });
17531
- subscription = Option30.String("--subscription");
17532
- imageRepo = Option30.String("--image-repo", DEFAULT_IMAGE_REPO);
17533
- to = Option30.String("--to");
17534
- resourceGroup = Option30.String("--resource-group", {
17535
- description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
17536
- });
17537
- check = Option30.Boolean("--check", false);
17538
- yes = Option30.Boolean("--yes", false);
17539
- output = Option30.String("--output");
17540
- async executeCommand() {
17541
- const mode = resolveOutputMode(
17542
- this.output,
17543
- this.context.stdout
17544
- );
17545
- const interactive = this.context.stdout.isTTY === true;
17546
- const log = (msg) => {
17547
- if (mode !== "json") this.context.stdout.write(msg + "\n");
17548
- };
17549
- const ctx = await resolveGatewayContext({
17550
- interactive: mode !== "json",
17551
- subscriptionId: this.subscription,
17552
- resourceGroup: this.resourceGroup
17553
- });
17554
- const { resourceGroup, name } = parseContainerAppResourceId(ctx.containerAppResourceId);
17555
- const currentImage = (await runAz([
17556
- "containerapp",
17557
- "show",
17558
- "-g",
17559
- resourceGroup,
17560
- "-n",
17561
- name,
17562
- "--query",
17563
- "properties.template.containers[0].image",
17564
- "-o",
17565
- "tsv"
17566
- ])).trim();
17567
- const tags = await fetchPublicTags(this.imageRepo);
17568
- const plan = planUpdate({
17569
- currentImage,
17570
- availableTags: tags,
17571
- imageRepo: this.imageRepo,
17572
- to: this.to
17573
- });
17574
- const s = summarizePlan(plan);
17575
- if (plan.kind === "refuse-byoc") {
17576
- throw new LocalCliError({
17577
- code: "PLATFORM_BYOC_NOT_TRACKED",
17578
- message: `This gateway runs a private-ACR image (${plan.currentRepo}), not the tracked public repo (${plan.trackedRepo}).`,
17579
- hint: "BYOC instances update by rebuilding + pushing your own image, then 'm8t deploy --image-ref <your-acr>'. Use --image-repo to track a different public repo."
17580
- });
17483
+ // src/lib/release-manifest.ts
17484
+ var SEVERITIES = ["critical", "recommended", "optional"];
17485
+ var IMAGE_KEYS = ["gateway", "codingAgent", "azureExecutor", "installer"];
17486
+ function isObj2(v) {
17487
+ return typeof v === "object" && v !== null && !Array.isArray(v);
17488
+ }
17489
+ function validateManifest(m) {
17490
+ const errors = [];
17491
+ if (!isObj2(m)) return ["manifest is not an object"];
17492
+ if (m.schemaVersion !== 1) errors.push("schemaVersion must be the integer 1");
17493
+ const p = m.platform;
17494
+ if (!isObj2(p)) {
17495
+ errors.push("platform is required");
17496
+ } else {
17497
+ for (const f of ["version", "tag", "releasedAt", "commit", "notes", "notesUrl"]) {
17498
+ if (typeof p[f] !== "string" || p[f].length === 0) errors.push(`platform.${f} is required`);
17581
17499
  }
17582
- if (plan.kind === "tag-not-found") {
17583
- throw new LocalCliError({
17584
- code: "PLATFORM_TAG_NOT_FOUND",
17585
- message: `Tag ${plan.requested} is not published on ${this.imageRepo}.`,
17586
- hint: "Run 'm8t platform status' to see the newest available version, or omit --to."
17587
- });
17500
+ if (!SEVERITIES.includes(p.severity)) errors.push(`platform.severity must be one of ${SEVERITIES.join("|")}`);
17501
+ if (!(typeof p.previousVersion === "string" || p.previousVersion === null)) {
17502
+ errors.push("platform.previousVersion must be a string or null");
17588
17503
  }
17589
- if (plan.kind === "no-versions") {
17590
- throw new LocalCliError({
17591
- code: "PLATFORM_NO_VERSIONS",
17592
- message: `No published vX.Y.Z tags found on ${this.imageRepo}.`,
17593
- hint: "Publish one via the release-image workflow, or check --image-repo."
17594
- });
17504
+ }
17505
+ const c = m.components;
17506
+ if (!isObj2(c)) {
17507
+ errors.push("components is required");
17508
+ return errors;
17509
+ }
17510
+ for (const key2 of IMAGE_KEYS) {
17511
+ const img = c[key2];
17512
+ if (!isObj2(img)) {
17513
+ errors.push(`components.${key2} is required`);
17514
+ continue;
17595
17515
  }
17596
- if (mode !== "json") {
17597
- this.context.stdout.write(
17598
- renderKeyValueBlock([
17599
- { key: "gateway", value: name },
17600
- { key: "current", value: s.current },
17601
- { key: "available", value: s.available },
17602
- { key: "status", value: s.verdict }
17603
- ]) + "\n"
17604
- );
17516
+ if (img.kind !== "image") errors.push(`components.${key2}.kind must be "image"`);
17517
+ for (const f of ["ref", "tag", "version"]) {
17518
+ if (typeof img[f] !== "string" || img[f].length === 0) errors.push(`components.${key2}.${f} is required`);
17605
17519
  }
17606
- if (plan.kind === "noop") {
17607
- if (mode === "json") this.context.stdout.write(renderJson({ ...s, kind: plan.kind, rolled: false }) + "\n");
17608
- else log(colors.success("already up to date \u2713"));
17609
- return 0;
17520
+ if (typeof img.digest !== "string" || !/^sha256:[0-9a-f]+$/.test(img.digest)) {
17521
+ errors.push(`components.${key2}.digest must be a sha256:\u2026 digest`);
17610
17522
  }
17611
- if (this.check) {
17612
- if (mode === "json") this.context.stdout.write(renderJson({ ...s, kind: plan.kind, rolled: false, wouldRollTo: plan.target }) + "\n");
17613
- else log(colors.dim(`(--check) would roll to ${plan.target}`));
17614
- return 0;
17523
+ }
17524
+ const cli2 = c.cli;
17525
+ if (!isObj2(cli2) || cli2.kind !== "npm") {
17526
+ errors.push("components.cli (kind: npm) is required");
17527
+ } else {
17528
+ for (const f of ["package", "version", "min", "recommended"]) {
17529
+ if (typeof cli2[f] !== "string" || cli2[f].length === 0) errors.push(`components.cli.${f} is required`);
17615
17530
  }
17616
- if (!this.yes && interactive) {
17617
- const ok = await confirm5({
17618
- message: `Roll ${name} from ${plan.current} to ${plan.target}?`,
17619
- default: true
17620
- });
17621
- if (!ok) {
17622
- log(colors.dim("aborted \u2014 no change."));
17623
- return 0;
17531
+ }
17532
+ const personas = c.personas;
17533
+ if (!isObj2(personas) || !isObj2(personas.items)) {
17534
+ errors.push("components.personas.items is required");
17535
+ } else {
17536
+ for (const [name, item] of Object.entries(personas.items)) {
17537
+ if (!isObj2(item) || typeof item.path !== "string" || item.path.length === 0) {
17538
+ errors.push(`components.personas.items.${name}.path is required`);
17539
+ }
17540
+ if (!isObj2(item) || typeof item.treeSha !== "string" || item.treeSha.length < 4) {
17541
+ errors.push(`components.personas.items.${name}.treeSha is required`);
17624
17542
  }
17625
17543
  }
17626
- log(`rolling to ${plan.target}\u2026`);
17627
- await runAz([
17628
- "containerapp",
17629
- "update",
17630
- "-n",
17631
- name,
17632
- "-g",
17633
- resourceGroup,
17634
- "--image",
17635
- `${plan.repo}:${plan.target}`
17636
- ]);
17637
- if (mode === "json") this.context.stdout.write(renderJson({ ...s, kind: plan.kind, rolled: true, rolledTo: plan.target }) + "\n");
17638
- else log(colors.success(`rolled ${plan.current} \u2192 ${plan.target} \u2713`));
17639
- return 0;
17640
17544
  }
17641
- };
17642
-
17643
- // src/commands/platform/enable-cost-report.ts
17644
- import { Command as Command33, Option as Option31 } from "clipanion";
17645
-
17646
- // src/lib/wire-gateway-acs.ts
17647
- init_rbac();
17648
- async function wireGatewayForAcs(args) {
17649
- try {
17650
- await args.az([
17651
- "role",
17652
- "assignment",
17653
- "create",
17654
- "--assignee-object-id",
17655
- args.gatewayPrincipalId,
17656
- "--assignee-principal-type",
17657
- "ServicePrincipal",
17658
- "--role",
17659
- CONTRIBUTOR_ROLE_ID,
17660
- "--scope",
17661
- args.acsResourceId
17662
- ]);
17663
- } catch (e) {
17664
- if (!(e instanceof Error && /RoleAssignmentExists/i.test(e.message))) throw e;
17545
+ const seeds = c.brainSeeds;
17546
+ if (!isObj2(seeds) || !isObj2(seeds.items)) {
17547
+ errors.push("components.brainSeeds.items is required");
17548
+ } else {
17549
+ for (const [name, item] of Object.entries(seeds.items)) {
17550
+ if (!isObj2(item) || typeof item.path !== "string" || item.path.length === 0) {
17551
+ errors.push(`components.brainSeeds.items.${name}.path is required`);
17552
+ }
17553
+ if (!isObj2(item) || !isObj2(item.subtrees)) {
17554
+ errors.push(`components.brainSeeds.items.${name}.subtrees is required`);
17555
+ }
17556
+ }
17665
17557
  }
17666
- await args.az([
17667
- "containerapp",
17668
- "update",
17669
- "-n",
17558
+ const infra = c.infra;
17559
+ if (infra !== void 0) {
17560
+ if (!isObj2(infra) || infra.kind !== "infra") {
17561
+ errors.push('components.infra.kind must be "infra"');
17562
+ }
17563
+ if (!isObj2(infra) || typeof infra.treeSha !== "string" || infra.treeSha.length < 4) {
17564
+ errors.push("components.infra.treeSha is required");
17565
+ }
17566
+ }
17567
+ return errors;
17568
+ }
17569
+
17570
+ // src/lib/release-channel.ts
17571
+ init_errors();
17572
+ var CHANNEL_LATEST_URL = "https://github.com/m8t-run/m8t/releases/latest/download/manifest.json";
17573
+ function channelUrlForVersion(tag) {
17574
+ return `https://github.com/m8t-run/m8t/releases/download/${tag}/manifest.json`;
17575
+ }
17576
+ async function fetchManifest(source, deps = {}) {
17577
+ const readFile8 = deps.readFile ?? ((p) => fs18.readFileSync(p, "utf8"));
17578
+ const fetchImpl = deps.fetchImpl ?? fetch;
17579
+ const ghToken = deps.ghToken ?? getGhToken;
17580
+ let raw;
17581
+ if ("file" in source) {
17582
+ try {
17583
+ raw = readFile8(source.file);
17584
+ } catch (e) {
17585
+ throw new LocalCliError({ code: "PLATFORM_MANIFEST_READ_FAILED", message: `Could not read manifest file '${source.file}': ${e.message}` });
17586
+ }
17587
+ } else {
17588
+ const url = "url" in source ? source.url : source.version ? channelUrlForVersion(source.version) : CHANNEL_LATEST_URL;
17589
+ let token;
17590
+ try {
17591
+ token = await ghToken();
17592
+ } catch {
17593
+ token = void 0;
17594
+ }
17595
+ const res = await fetchImpl(url, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
17596
+ if (!res.ok) {
17597
+ throw new LocalCliError({
17598
+ code: "PLATFORM_MANIFEST_FETCH_FAILED",
17599
+ message: `GET ${url} returned HTTP ${res.status.toString()}.`,
17600
+ hint: "The release channel is auth-gated until the repo is public. Run 'gh auth login', or pass --manifest-file / --manifest-url."
17601
+ });
17602
+ }
17603
+ raw = JSON.stringify(await res.json());
17604
+ }
17605
+ let parsed;
17606
+ try {
17607
+ parsed = JSON.parse(raw);
17608
+ } catch (e) {
17609
+ throw new LocalCliError({ code: "PLATFORM_MANIFEST_PARSE_FAILED", message: `Manifest is not valid JSON: ${e.message}` });
17610
+ }
17611
+ const errors = validateManifest(parsed);
17612
+ if (errors.length > 0) {
17613
+ throw new LocalCliError({
17614
+ code: "PLATFORM_MANIFEST_INVALID",
17615
+ message: `Fetched manifest failed validation:
17616
+ - ${errors.join("\n - ")}`
17617
+ });
17618
+ }
17619
+ return parsed;
17620
+ }
17621
+
17622
+ // src/lib/release-content.ts
17623
+ import * as fs19 from "fs";
17624
+ import * as os8 from "os";
17625
+ import * as path18 from "path";
17626
+ import { execFileSync } from "child_process";
17627
+ init_errors();
17628
+ var OWNER_REPO = "m8t-run/m8t";
17629
+ function parseTreeResponse(body) {
17630
+ const map = /* @__PURE__ */ new Map();
17631
+ for (const e of body.tree ?? []) if (e.type === "tree") map.set(e.path, e.sha);
17632
+ return { treeShaOf: (p) => map.get(p.replace(/\/$/, "")) };
17633
+ }
17634
+ async function fetchRepoTree(commit, deps = {}) {
17635
+ const fetchImpl = deps.fetchImpl ?? fetch;
17636
+ const ghToken = deps.ghToken ?? getGhToken;
17637
+ let token;
17638
+ try {
17639
+ token = await ghToken();
17640
+ } catch {
17641
+ token = void 0;
17642
+ }
17643
+ const url = `https://api.github.com/repos/${OWNER_REPO}/git/trees/${commit}?recursive=1`;
17644
+ const res = await fetchImpl(url, {
17645
+ headers: { Accept: "application/vnd.github+json", ...token ? { Authorization: `Bearer ${token}` } : {} }
17646
+ });
17647
+ if (!res.ok) {
17648
+ throw new LocalCliError({ code: "PLATFORM_TREE_FETCH_FAILED", message: `GET ${url} \u2192 HTTP ${res.status.toString()}.` });
17649
+ }
17650
+ return parseTreeResponse(await res.json());
17651
+ }
17652
+ function verifyPersonaTrees(manifest, tree) {
17653
+ const out = [];
17654
+ for (const [name, item] of Object.entries(manifest.components.personas.items)) {
17655
+ const actual = tree.treeShaOf(item.path);
17656
+ if (actual !== void 0 && actual !== item.treeSha) {
17657
+ out.push(`persona '${name}' (${item.path}): manifest pins ${item.treeSha} but commit tree has ${actual}`);
17658
+ }
17659
+ }
17660
+ return out;
17661
+ }
17662
+ async function resolveReleaseContent(manifest, opts = {}) {
17663
+ if (opts.contentDir) {
17664
+ if (!fs19.existsSync(path18.join(opts.contentDir, "personas"))) {
17665
+ throw new LocalCliError({ code: "PLATFORM_CONTENT_DIR_INVALID", message: `--content-dir '${opts.contentDir}' has no personas/ \u2014 not a repo checkout.` });
17666
+ }
17667
+ return opts.contentDir;
17668
+ }
17669
+ const commit = manifest.platform.commit;
17670
+ const cacheRoot = opts.cacheRoot ?? path18.join(os8.homedir(), ".m8t-stack", "cache");
17671
+ const dest = path18.join(cacheRoot, `release-${commit}`);
17672
+ if (fs19.existsSync(path18.join(dest, "personas"))) return dest;
17673
+ const fetchImpl = opts.fetchImpl ?? fetch;
17674
+ const ghToken = opts.ghToken ?? getGhToken;
17675
+ let token;
17676
+ try {
17677
+ token = await ghToken();
17678
+ } catch {
17679
+ token = void 0;
17680
+ }
17681
+ const url = `https://api.github.com/repos/${OWNER_REPO}/tarball/${commit}`;
17682
+ const res = await fetchImpl(url, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
17683
+ if (!res.ok) {
17684
+ throw new LocalCliError({ code: "PLATFORM_TARBALL_FETCH_FAILED", message: `GET ${url} \u2192 HTTP ${res.status.toString()}.` });
17685
+ }
17686
+ fs19.mkdirSync(cacheRoot, { recursive: true });
17687
+ const tmp = fs19.mkdtempSync(path18.join(cacheRoot, `.dl-${commit.slice(0, 8)}-`));
17688
+ try {
17689
+ const tgz = path18.join(tmp, "src.tar.gz");
17690
+ fs19.writeFileSync(tgz, Buffer.from(await res.arrayBuffer()));
17691
+ const unpacked = path18.join(tmp, "unpacked");
17692
+ fs19.mkdirSync(unpacked);
17693
+ extractTarGz(tgz, unpacked);
17694
+ try {
17695
+ fs19.renameSync(unpacked, dest);
17696
+ } catch (e) {
17697
+ const err = e;
17698
+ const isBenignRace = (err.code === "EEXIST" || err.code === "ENOTEMPTY") && fs19.existsSync(path18.join(dest, "personas"));
17699
+ if (!isBenignRace) {
17700
+ throw new LocalCliError({
17701
+ code: "PLATFORM_CACHE_PUBLISH_FAILED",
17702
+ message: `Could not publish release cache to ${dest}: ${err.message}`
17703
+ });
17704
+ }
17705
+ }
17706
+ } finally {
17707
+ fs19.rmSync(tmp, { recursive: true, force: true });
17708
+ }
17709
+ return dest;
17710
+ }
17711
+ function extractTarGz(tgz, into) {
17712
+ execFileSync("tar", ["-xzf", tgz, "-C", into, "--strip-components=1"]);
17713
+ }
17714
+
17715
+ // src/lib/platform-stamp.ts
17716
+ import { TableClient, AzureNamedKeyCredential } from "@azure/data-tables";
17717
+ init_errors();
17718
+ init_rbac();
17719
+
17720
+ // src/lib/platform-storage-discovery.ts
17721
+ init_http();
17722
+ init_errors();
17723
+ var ARM3 = "https://management.azure.com";
17724
+ var ARM_SCOPE7 = "https://management.azure.com/.default";
17725
+ var STORAGE_API = "2023-05-01";
17726
+ async function discoverStampStorage(opts) {
17727
+ const list = await authedJson({
17728
+ credential: opts.credential,
17729
+ scope: ARM_SCOPE7,
17730
+ method: "GET",
17731
+ url: `${ARM3}/subscriptions/${opts.subscriptionId}/resourceGroups/${opts.resourceGroup}/providers/Microsoft.Storage/storageAccounts?api-version=${STORAGE_API}`
17732
+ }) ?? {};
17733
+ const accounts = (list.value ?? []).filter((a) => a.properties?.primaryEndpoints?.table);
17734
+ const chosen = accounts.find((a) => a.tags?.["m8t-stack"] === "storage") ?? accounts.find((a) => a.tags?.["m8t-stack:role"] === "gateway" || a.tags?.["m8t-stack:role"] === "ledger") ?? (accounts.length === 1 ? accounts[0] : void 0);
17735
+ const tableEndpoint = chosen?.properties?.primaryEndpoints?.table?.replace(/\/$/, "");
17736
+ if (!tableEndpoint || !chosen?.id || !chosen.name) {
17737
+ throw new LocalCliError({
17738
+ code: "PLATFORM_STAMP_STORAGE_NOT_FOUND",
17739
+ message: `No table-capable storage account found in resource group ${opts.resourceGroup}.`,
17740
+ hint: `Found: ${accounts.map((a) => a.name).join(", ") || "(none)"}.`
17741
+ });
17742
+ }
17743
+ return { tableEndpoint, accountResourceId: chosen.id, accountName: chosen.name };
17744
+ }
17745
+
17746
+ // src/lib/platform-stamp.ts
17747
+ var TABLE = "Metadata";
17748
+ var PK = "system";
17749
+ var RK = "platform";
17750
+ function stampToEntity(s) {
17751
+ return {
17752
+ partitionKey: PK,
17753
+ rowKey: RK,
17754
+ platformVersion: s.platformVersion,
17755
+ updatedAt: s.updatedAt,
17756
+ lastResult: s.lastResult,
17757
+ value: JSON.stringify(s)
17758
+ };
17759
+ }
17760
+ function entityToStamp(e) {
17761
+ const raw = e.value;
17762
+ if (typeof raw !== "string") return null;
17763
+ try {
17764
+ return JSON.parse(raw);
17765
+ } catch {
17766
+ return null;
17767
+ }
17768
+ }
17769
+ async function makeSharedKeyClient(opts) {
17770
+ const rg = /\/resourceGroups\/([^/]+)\//i.exec(opts.accountResourceId)?.[1];
17771
+ if (!rg) return null;
17772
+ const key2 = (await runAz(["storage", "account", "keys", "list", "--account-name", opts.accountName, "-g", rg, "--query", "[0].value", "-o", "tsv"])).trim();
17773
+ if (!key2) return null;
17774
+ return new TableClient(opts.tableEndpoint, TABLE, new AzureNamedKeyCredential(opts.accountName, key2));
17775
+ }
17776
+ async function writeStamp(opts) {
17777
+ const { tableEndpoint, accountResourceId, accountName } = await discoverStampStorage(opts);
17778
+ const entity = stampToEntity(opts.stamp);
17779
+ const aad = new TableClient(tableEndpoint, TABLE, opts.credential);
17780
+ await aad.createTable().catch(() => void 0);
17781
+ try {
17782
+ await aad.upsertEntity(entity, "Replace");
17783
+ return;
17784
+ } catch (e) {
17785
+ if (!is403(e)) throw e;
17786
+ }
17787
+ opts.onProgress?.("writing the platform stamp with the account key \u2014 your identity lacks the Storage Table data role.");
17788
+ const shared = await makeSharedKeyClient({ tableEndpoint, accountResourceId, accountName });
17789
+ if (shared) {
17790
+ await shared.createTable().catch(() => void 0);
17791
+ await shared.upsertEntity(entity, "Replace");
17792
+ return;
17793
+ }
17794
+ opts.onProgress?.("shared-key access is disabled \u2014 granting Storage Table Data Contributor to your identity (one-time)\u2026");
17795
+ try {
17796
+ const oid = await getCallerObjectId();
17797
+ await grantStorageTableDataContributor({
17798
+ credential: opts.credential,
17799
+ subscriptionId: opts.subscriptionId,
17800
+ scope: accountResourceId,
17801
+ principalId: oid
17802
+ });
17803
+ } catch (e) {
17804
+ throw new LocalCliError({
17805
+ code: "PLATFORM_STAMP_NO_ACCESS",
17806
+ message: `Cannot write the platform stamp: AAD lacks the Storage Table data role for ${accountName}, shared-key access is disabled, and self-granting the role failed.`,
17807
+ hint: "Grant yourself 'Storage Table Data Contributor' on the storage account and retry.",
17808
+ cause: e
17809
+ });
17810
+ }
17811
+ for (let i = 0; i < 6; i++) {
17812
+ await sleep2(1e4);
17813
+ try {
17814
+ await aad.upsertEntity(entity, "Replace");
17815
+ return;
17816
+ } catch (e) {
17817
+ if (!is403(e)) throw e;
17818
+ }
17819
+ }
17820
+ throw new LocalCliError({
17821
+ code: "PLATFORM_STAMP_NO_ACCESS",
17822
+ message: `Cannot write the platform stamp: granted 'Storage Table Data Contributor' on ${accountName}, but the data-plane role has not propagated yet.`,
17823
+ hint: "The role assignment persists \u2014 retry 'm8t platform update' in a few minutes."
17824
+ });
17825
+ }
17826
+ async function readStamp(opts) {
17827
+ const { tableEndpoint, accountResourceId, accountName } = await discoverStampStorage(opts);
17828
+ const aad = new TableClient(tableEndpoint, TABLE, opts.credential);
17829
+ try {
17830
+ const row = await aad.getEntity(PK, RK);
17831
+ return entityToStamp(row);
17832
+ } catch (e) {
17833
+ if (is404(e)) return null;
17834
+ if (!is403(e)) return null;
17835
+ }
17836
+ try {
17837
+ const shared = await makeSharedKeyClient({ tableEndpoint, accountResourceId, accountName });
17838
+ if (!shared) return null;
17839
+ const row = await shared.getEntity(PK, RK);
17840
+ return entityToStamp(row);
17841
+ } catch {
17842
+ return null;
17843
+ }
17844
+ }
17845
+ function is403(e) {
17846
+ return e.statusCode === 403;
17847
+ }
17848
+ function is404(e) {
17849
+ return e.statusCode === 404;
17850
+ }
17851
+ function sleep2(ms) {
17852
+ return new Promise((r) => setTimeout(r, ms));
17853
+ }
17854
+
17855
+ // src/lib/platform-converge.ts
17856
+ import * as fs21 from "fs";
17857
+ import * as path20 from "path";
17858
+ import { parse as parseYaml10 } from "yaml";
17859
+
17860
+ // src/lib/deploy.ts
17861
+ import * as fs20 from "fs/promises";
17862
+ import * as os9 from "os";
17863
+ import * as path19 from "path";
17864
+ init_errors();
17865
+ var FOUNDRY_TRACING_MODES = ["project", "account", "skip"];
17866
+ function parseFoundryTracingMode(v) {
17867
+ if (v === void 0) return void 0;
17868
+ if (!FOUNDRY_TRACING_MODES.includes(v)) {
17869
+ throw new LocalCliError({
17870
+ code: "DEPLOY_INVALID_TRACING_MODE",
17871
+ message: `Invalid --foundry-tracing value: '${v}'.`,
17872
+ hint: `Use one of: ${FOUNDRY_TRACING_MODES.join(", ")}.`
17873
+ });
17874
+ }
17875
+ return v;
17876
+ }
17877
+ function buildBicepParams(p) {
17878
+ const params = [
17879
+ `location=${p.location}`,
17880
+ `suffix=${p.suffix}`,
17881
+ `imageRef=${p.imageRef}`,
17882
+ `tenantId=${p.tenantId}`,
17883
+ `clientId=${p.clientId}`,
17884
+ `foundryResourceId=${p.foundryResourceId}`,
17885
+ `foundryProjectEndpoint=${p.foundryProjectEndpoint}`,
17886
+ `acrPullIdentityResourceId=${p.acrPullIdentityResourceId}`,
17887
+ `acrResourceId=${p.acrResourceId}`
17888
+ ];
17889
+ if (p.foundryTracingMode) params.push(`foundryTracingMode=${p.foundryTracingMode}`);
17890
+ return params;
17891
+ }
17892
+ async function resolveRepoRoot2(home = os9.homedir()) {
17893
+ const marker = path19.join(home, ".m8t-stack", "repo-root");
17894
+ try {
17895
+ const root = (await fs20.readFile(marker, "utf8")).trim();
17896
+ if (!root) throw new Error("empty");
17897
+ return root;
17898
+ } catch {
17899
+ throw new LocalCliError({
17900
+ code: "DEPLOY_NO_REPO_ROOT",
17901
+ message: "~/.m8t-stack/repo-root marker is missing or empty.",
17902
+ hint: "Run 'm8t install' from an m8t-stack checkout (it writes the marker), or run 'm8t deploy' from the repo."
17903
+ });
17904
+ }
17905
+ }
17906
+ async function resolveFoundryResourceId(endpoint, explicit) {
17907
+ if (explicit) return explicit;
17908
+ const m = /^https:\/\/([^.]+)\.services\.ai\.azure\.com/.exec(endpoint);
17909
+ if (!m) {
17910
+ throw new LocalCliError({
17911
+ code: "DEPLOY_FOUNDRY_ENDPOINT_BAD",
17912
+ message: `Cannot parse the Foundry account name from endpoint: ${endpoint}`,
17913
+ hint: "Pass --foundry-resource-id <ARM-id> explicitly."
17914
+ });
17915
+ }
17916
+ const accounts = JSON.parse(
17917
+ await runAz([
17918
+ "cognitiveservices",
17919
+ "account",
17920
+ "list",
17921
+ "--query",
17922
+ `[?name=='${m[1]}'].id`,
17923
+ "--output",
17924
+ "json"
17925
+ ])
17926
+ );
17927
+ if (accounts.length === 0) {
17928
+ throw new LocalCliError({
17929
+ code: "DEPLOY_FOUNDRY_NOT_FOUND",
17930
+ message: `No Foundry account named '${m[1]}' in the active subscription.`,
17931
+ hint: "Check the endpoint, or pass --foundry-resource-id explicitly."
17932
+ });
17933
+ }
17934
+ if (accounts.length > 1) {
17935
+ throw new LocalCliError({
17936
+ code: "DEPLOY_FOUNDRY_AMBIGUOUS",
17937
+ message: `Multiple Foundry accounts named '${m[1]}' \u2014 ambiguous.`,
17938
+ hint: "Pass --foundry-resource-id <ARM-id> to disambiguate."
17939
+ });
17940
+ }
17941
+ return accounts[0];
17942
+ }
17943
+ async function resolveAcrResourceId(opts) {
17944
+ if (opts.explicit) return opts.explicit;
17945
+ const host = opts.imageRef.split("/")[0];
17946
+ if (!host.endsWith(".azurecr.io")) return "";
17947
+ const acrName = host.split(".")[0];
17948
+ try {
17949
+ const id = (await runAz(["acr", "show", "-n", acrName, "--query", "id", "-o", "tsv"])).trim();
17950
+ if (!id) throw new Error("empty id");
17951
+ return id;
17952
+ } catch (e) {
17953
+ if (e instanceof LocalCliError) throw e;
17954
+ throw new LocalCliError({
17955
+ code: "DEPLOY_ACR_NOT_FOUND",
17956
+ message: `Cannot resolve the ACR '${acrName}' for image ${opts.imageRef}.`,
17957
+ hint: "Create it (az acr create), sign in (az acr login), and push the web image (docker buildx \u2026 --push); or pass --acr-resource-id <ARM-id>."
17958
+ });
17959
+ }
17960
+ }
17961
+ async function ensureResourceGroup(name, location) {
17962
+ const existing = JSON.parse(
17963
+ await runAz(["group", "show", "--name", name, "--output", "json"]).catch(() => "") || "null"
17964
+ );
17965
+ if (existing) return;
17966
+ await runAz(["group", "create", "--name", name, "--location", location, "--tags", "m8t-stack=deployment"]);
17967
+ }
17968
+ function resolveAcrPullIdentity(opts) {
17969
+ return opts.explicit ?? "";
17970
+ }
17971
+ async function runBicepDeployment(opts) {
17972
+ const bicepPath = path19.join(opts.repoRoot, "deploy", "main.bicep");
17973
+ const result = JSON.parse(
17974
+ await runAz([
17975
+ "deployment",
17976
+ "group",
17977
+ "create",
17978
+ "--name",
17979
+ opts.deploymentName,
17980
+ "--resource-group",
17981
+ opts.resourceGroup,
17982
+ "--template-file",
17983
+ bicepPath,
17984
+ "--parameters",
17985
+ ...opts.params,
17986
+ "--output",
17987
+ "json"
17988
+ ])
17989
+ );
17990
+ const outputs = result.properties?.outputs ?? {};
17991
+ const fqdn = outputs.containerAppFqdn?.value;
17992
+ if (typeof fqdn !== "string" || !fqdn) {
17993
+ throw new LocalCliError({
17994
+ code: "DEPLOY_NO_FQDN",
17995
+ message: "Bicep deployment did not return containerAppFqdn.",
17996
+ hint: "Check deploy/main.bicep outputs and the deployment log."
17997
+ });
17998
+ }
17999
+ return {
18000
+ containerAppFqdn: fqdn,
18001
+ resourceNames: outputs.resourceNames?.value ?? {}
18002
+ };
18003
+ }
18004
+
18005
+ // src/lib/platform-converge.ts
18006
+ init_foundry_agent_get();
18007
+ init_errors();
18008
+
18009
+ // src/lib/persona-compose.ts
18010
+ function composeInstructions(args) {
18011
+ const live = args.current.definition.instructions ?? "";
18012
+ let out = args.baseBody;
18013
+ const hadLoader = stripBrainLoader(live) !== live;
18014
+ if (hadLoader && args.loaderText) {
18015
+ const templated = args.brainRepo ? args.loaderText.replaceAll("{{brain_repo}}", args.brainRepo) : args.loaderText;
18016
+ out = appendBrainLoader(out, templated);
18017
+ }
18018
+ if (hasA2aSnippet(live)) out = appendSnippet(out);
18019
+ return out;
18020
+ }
18021
+ function resolveFillableValues(current, yamlValues) {
18022
+ const raw = current.metadata?.fillableFieldValues;
18023
+ if (typeof raw === "string") {
18024
+ try {
18025
+ return JSON.parse(raw);
18026
+ } catch {
18027
+ }
18028
+ }
18029
+ return yamlValues;
18030
+ }
18031
+ function isNoop(candidate2, current) {
18032
+ const a = candidate2.definition.instructions ?? "";
18033
+ const b = current.definition.instructions ?? "";
18034
+ if (a !== b) return false;
18035
+ if (JSON.stringify(candidate2.definition.tools ?? []) !== JSON.stringify(current.definition.tools ?? [])) return false;
18036
+ return JSON.stringify(sortObj(candidate2.metadata)) === JSON.stringify(sortObj(current.metadata ?? {}));
18037
+ }
18038
+ function sortObj(o) {
18039
+ return Object.fromEntries(Object.entries(o).sort(([x], [y]) => x.localeCompare(y)));
18040
+ }
18041
+
18042
+ // src/lib/platform-converge.ts
18043
+ var ORDER = ["infra", "gateway", "codingAgent", "azureExecutor", "personas"];
18044
+ function infraTargetSha(manifest, tree) {
18045
+ return manifest.components.infra?.treeSha ?? tree.treeShaOf("deploy") ?? "";
18046
+ }
18047
+ function diffPlan(manifest, stamp, tree, opts = {}) {
18048
+ const actions = [];
18049
+ const skipped = [];
18050
+ const adopt = stamp === null;
18051
+ const want = (k) => opts.only === void 0 || opts.only === k;
18052
+ for (const component of ORDER) {
18053
+ if (!want(component)) {
18054
+ skipped.push({ component, reason: "only-filter" });
18055
+ continue;
18056
+ }
18057
+ if (component === "infra") {
18058
+ if (opts.skipInfra) {
18059
+ skipped.push({ component, reason: "up-to-date" });
18060
+ continue;
18061
+ }
18062
+ const to = infraTargetSha(manifest, tree);
18063
+ const from2 = stamp?.components.infra.treeSha ?? null;
18064
+ if (opts.forceInfra) actions.push({ component, reason: "forced", from: from2, to });
18065
+ else if (adopt) actions.push({ component, reason: "adopt", from: from2, to });
18066
+ else if (from2 !== to) skipped.push({ component, reason: "deferred" });
18067
+ else skipped.push({ component, reason: "up-to-date" });
18068
+ continue;
18069
+ }
18070
+ if (component === "personas") {
18071
+ for (const [name, item] of Object.entries(manifest.components.personas.items)) {
18072
+ const from2 = stamp?.components.personas[name]?.treeSha ?? null;
18073
+ if (adopt) actions.push({ component, personaName: name, reason: "adopt", from: from2, to: item.treeSha });
18074
+ else if (from2 !== item.treeSha) actions.push({ component, personaName: name, reason: "changed", from: from2, to: item.treeSha });
18075
+ else skipped.push({ component, personaName: name, reason: "up-to-date" });
18076
+ }
18077
+ continue;
18078
+ }
18079
+ const img = manifest.components[component];
18080
+ const cur = stamp?.components[component];
18081
+ if (cur?.state === "external") {
18082
+ skipped.push({ component, reason: "external" });
18083
+ continue;
18084
+ }
18085
+ const from = cur?.tag ?? null;
18086
+ if (adopt) actions.push({ component, reason: "adopt", from, to: img.tag });
18087
+ else if (from !== img.tag) actions.push({ component, reason: "changed", from, to: img.tag });
18088
+ else skipped.push({ component, reason: "up-to-date" });
18089
+ }
18090
+ return { targetVersion: manifest.platform.tag, previousVersion: manifest.platform.previousVersion, actions, skipped };
18091
+ }
18092
+ function statusRows(manifest, stamp, tree, opts = {}) {
18093
+ const plan = diffPlan(manifest, stamp, tree, opts);
18094
+ const rows = [];
18095
+ for (const a of plan.actions) {
18096
+ rows.push({
18097
+ component: a.component,
18098
+ ...a.personaName ? { personaName: a.personaName } : {},
18099
+ installed: a.from ?? "(none)",
18100
+ target: a.to,
18101
+ status: a.reason === "adopt" ? "needs-adopt" : "drift"
18102
+ });
18103
+ }
18104
+ for (const s of plan.skipped) {
18105
+ rows.push({
18106
+ component: s.component,
18107
+ ...s.personaName ? { personaName: s.personaName } : {},
18108
+ installed: installedValueOf(stamp, s.component, s.personaName),
18109
+ target: targetValueOf(manifest, tree, s.component, s.personaName),
18110
+ status: s.reason
18111
+ });
18112
+ }
18113
+ return rows;
18114
+ }
18115
+ function installedValueOf(stamp, component, personaName) {
18116
+ if (!stamp) return "-";
18117
+ if (component === "infra") return stamp.components.infra.treeSha || "-";
18118
+ if (component === "personas") {
18119
+ if (!personaName || !Object.hasOwn(stamp.components.personas, personaName)) return "-";
18120
+ return stamp.components.personas[personaName].treeSha;
18121
+ }
18122
+ return stamp.components[component].tag;
18123
+ }
18124
+ function targetValueOf(manifest, tree, component, personaName) {
18125
+ if (component === "infra") return infraTargetSha(manifest, tree) || "-";
18126
+ if (component === "personas") {
18127
+ if (!personaName || !Object.hasOwn(manifest.components.personas.items, personaName)) return "-";
18128
+ return manifest.components.personas.items[personaName].treeSha;
18129
+ }
18130
+ return manifest.components[component].tag;
18131
+ }
18132
+ async function applyPlan(plan, deps, ctx) {
18133
+ const prior = await deps.readStamp();
18134
+ const stamp = seedStamp(prior, ctx.manifest, plan);
18135
+ delete stamp.lastError;
18136
+ const applied = [];
18137
+ try {
18138
+ for (const a of plan.actions) {
18139
+ if (a.component === "infra") {
18140
+ stamp.components.infra = await deps.applyInfra(a, ctx);
18141
+ } else if (a.component === "gateway") {
18142
+ stamp.components.gateway = await deps.applyGateway(a, ctx);
18143
+ } else if (a.component === "codingAgent" || a.component === "azureExecutor") {
18144
+ const r = await deps.applyAgent(a, ctx);
18145
+ if (r !== "absent") {
18146
+ stamp.components[a.component] = { ...r, state: "managed" };
18147
+ } else {
18148
+ ctx.onProgress?.(`${a.component}: not deployed on this install \u2014 stamp left unchanged.`);
18149
+ }
18150
+ } else {
18151
+ if (!a.personaName) {
18152
+ throw new LocalCliError({ code: "PLATFORM_PERSONA_NO_NAME", message: "persona action missing personaName" });
18153
+ }
18154
+ const r = await deps.applyPersona(a, ctx);
18155
+ if (r === "absent") {
18156
+ ctx.onProgress?.(`persona '${a.personaName}': not deployed on this install \u2014 stamp left unchanged.`);
18157
+ } else {
18158
+ const prev = Object.hasOwn(stamp.components.personas, a.personaName) ? stamp.components.personas[a.personaName] : void 0;
18159
+ stamp.components.personas[a.personaName] = r === "noop" ? { treeSha: a.to, foundryVersion: prev?.foundryVersion ?? "" } : r;
18160
+ }
18161
+ }
18162
+ applied.push(a);
18163
+ stamp.lastResult = "partial";
18164
+ stamp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
18165
+ await deps.writeStamp(stamp);
18166
+ }
18167
+ await deps.healthGate(ctx, applied);
18168
+ stamp.lastResult = "success";
18169
+ delete stamp.lastError;
18170
+ const isTargeted = plan.skipped.some((s) => s.reason === "only-filter");
18171
+ if (!isTargeted && plan.targetVersion !== stamp.platformVersion) {
18172
+ stamp.previousPlatformVersion = stamp.platformVersion;
18173
+ stamp.platformVersion = plan.targetVersion;
18174
+ }
18175
+ stamp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
18176
+ await deps.writeStamp(stamp);
18177
+ return { stamp, applied };
18178
+ } catch (e) {
18179
+ stamp.lastResult = "failed";
18180
+ stamp.lastError = {
18181
+ component: applied.length < plan.actions.length ? plan.actions[applied.length].component : "healthGate",
18182
+ message: e.message
18183
+ };
18184
+ stamp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
18185
+ await deps.writeStamp(stamp).catch(() => {
18186
+ });
18187
+ throw e;
18188
+ }
18189
+ }
18190
+ function seedStamp(prior, manifest, plan) {
18191
+ const blankImg = () => ({ tag: "", digest: "", state: "managed" });
18192
+ return prior ? { ...prior } : {
18193
+ schemaVersion: 1,
18194
+ platformVersion: plan.targetVersion,
18195
+ previousPlatformVersion: plan.previousVersion,
18196
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
18197
+ lastResult: "partial",
18198
+ cli: "",
18199
+ components: { gateway: blankImg(), codingAgent: blankImg(), azureExecutor: blankImg(), personas: {}, infra: { treeSha: "" } }
18200
+ };
18201
+ }
18202
+ async function applyGatewayImage(a, ctx, gatewayResourceId) {
18203
+ const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
18204
+ const current = (await runAz(["containerapp", "show", "-g", resourceGroup, "-n", name, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
18205
+ const tags = await fetchPublicTags(DEFAULT_IMAGE_REPO);
18206
+ const plan = planUpdate({ currentImage: current, availableTags: tags, imageRepo: DEFAULT_IMAGE_REPO, to: a.to });
18207
+ const digest = ctx.manifest.components.gateway.digest;
18208
+ switch (plan.kind) {
18209
+ case "refuse-byoc":
18210
+ ctx.onProgress?.(`gateway is BYOC (${plan.currentRepo}) \u2014 marking externally managed.`);
18211
+ return { tag: a.to, digest, state: "external" };
18212
+ case "roll":
18213
+ await runAz(["containerapp", "update", "-n", name, "-g", resourceGroup, "--image", `${plan.repo}:${plan.target}`]);
18214
+ return { tag: a.to, digest, state: "managed" };
18215
+ case "noop":
18216
+ return { tag: a.to, digest, state: "managed" };
18217
+ case "tag-not-found":
18218
+ throw new LocalCliError({
18219
+ code: "PLATFORM_GATEWAY_TAG_UNAVAILABLE",
18220
+ message: `Gateway image tag '${a.to}' is not published on the tracked repo \u2014 cannot converge the gateway.`,
18221
+ hint: "The manifest may be ahead of the registry (propagation lag), or the repo is wrong. Retry shortly or check --image-repo."
18222
+ });
18223
+ case "no-versions":
18224
+ throw new LocalCliError({
18225
+ code: "PLATFORM_GATEWAY_NO_VERSIONS",
18226
+ message: "No published gateway image versions found on the tracked repo."
18227
+ });
18228
+ }
18229
+ }
18230
+ async function applyInfraDeploy(a, ctx, target, opts = {}) {
18231
+ if (a.reason !== "forced") {
18232
+ ctx.onProgress?.(`infra baseline recorded at ${target} (not re-provisioned; apply with --force-infra or the update job).`);
18233
+ return { treeSha: target };
18234
+ }
18235
+ ctx.onProgress?.("converging infra (idempotent bicep deploy)\u2026");
18236
+ const params = await resolveBicepParamsForConverge(ctx, opts);
18237
+ await runBicepDeployment({
18238
+ resourceGroup: ctx.resourceGroup,
18239
+ repoRoot: ctx.contentDir,
18240
+ params: buildBicepParams(params),
18241
+ deploymentName: `m8t-converge-${target.slice(0, 12)}`
18242
+ });
18243
+ return { treeSha: target };
18244
+ }
18245
+ async function resolveBicepParamsForConverge(ctx, opts = {}) {
18246
+ if (!opts.suffix) {
18247
+ throw new LocalCliError({
18248
+ code: "PLATFORM_INFRA_SUFFIX_REQUIRED",
18249
+ message: "A --force-infra converge deploy requires an explicit --suffix.",
18250
+ hint: "Pass --suffix <existing-suffix> (from the deployed resource names) to avoid provisioning duplicate resources."
18251
+ });
18252
+ }
18253
+ const location = (await runAz(["resource", "list", "-g", ctx.resourceGroup, "--query", "[0].location", "-o", "tsv"])).trim();
18254
+ const foundryResourceId = await resolveFoundryResourceId(opts.foundryEndpoint ?? "", opts.foundryResourceId);
18255
+ const acrPullIdentityResourceId = resolveAcrPullIdentity({ explicit: opts.acrPullIdentity });
18256
+ const imageRef = `${ctx.manifest.components.gateway.ref}:${ctx.manifest.components.gateway.tag}`;
18257
+ const acrResourceId = await resolveAcrResourceId({ imageRef, explicit: opts.acrResourceId });
18258
+ return {
18259
+ location,
18260
+ suffix: opts.suffix,
18261
+ imageRef,
18262
+ tenantId: opts.tenantId ?? "",
18263
+ clientId: opts.clientId ?? "",
18264
+ foundryResourceId,
18265
+ foundryProjectEndpoint: opts.foundryEndpoint ?? "",
18266
+ acrPullIdentityResourceId,
18267
+ acrResourceId,
18268
+ foundryTracingMode: opts.foundryTracingMode
18269
+ };
18270
+ }
18271
+ function swapHostedImage(def, newImage) {
18272
+ return { ...def, image: newImage };
18273
+ }
18274
+ async function applyAgentImage(a, ctx, opts) {
18275
+ const current = await getAgentVersion({ credential: ctx.credential, projectEndpoint: opts.project.endpoint, agentName: opts.agentName });
18276
+ const staged = await stagePublicImageIfNeeded({ image: opts.ghcrImage, project: opts.project, onProgress: ctx.onProgress });
18277
+ const definition = swapHostedImage(current.definition, staged.image);
18278
+ await createAgentVersion({
18279
+ credential: ctx.credential,
18280
+ projectEndpoint: opts.project.endpoint,
18281
+ agentName: opts.agentName,
18282
+ definition,
18283
+ metadata: current.metadata ?? {},
18284
+ extraHeaders: { "Foundry-Features": "HostedAgents=V1Preview" }
18285
+ });
18286
+ const digest = a.component === "codingAgent" ? ctx.manifest.components.codingAgent.digest : ctx.manifest.components.azureExecutor.digest;
18287
+ return { tag: a.to, digest };
18288
+ }
18289
+ function declaredFillableFields(personaPath) {
18290
+ let raw;
18291
+ try {
18292
+ raw = fs21.readFileSync(personaPath, "utf8");
18293
+ } catch {
18294
+ return [];
18295
+ }
18296
+ const fmMatch = /^---\n([\s\S]*?)\n---/.exec(raw);
18297
+ if (!fmMatch) return [];
18298
+ try {
18299
+ const fm = parseYaml10(fmMatch[1]);
18300
+ return fm["fillable-fields"] ?? [];
18301
+ } catch {
18302
+ return [];
18303
+ }
18304
+ }
18305
+ function defaultsOf(declared) {
18306
+ const out = {};
18307
+ for (const f of declared) {
18308
+ if (f.default !== void 0) out[f.name] = f.default;
18309
+ }
18310
+ return out;
18311
+ }
18312
+ function injectAndRender(personaPath, values, agentName) {
18313
+ return renderPersonaBody(personaPath, values, agentName);
18314
+ }
18315
+ function loaderText(loaderPath) {
18316
+ if (!loaderPath) return null;
18317
+ try {
18318
+ return fs21.readFileSync(loaderPath, "utf8");
18319
+ } catch {
18320
+ return null;
18321
+ }
18322
+ }
18323
+ function brainRepoOf(current) {
18324
+ const raw = current.metadata?.brain;
18325
+ if (typeof raw !== "string") return null;
18326
+ try {
18327
+ const parsed = JSON.parse(raw);
18328
+ return parsed.repo || null;
18329
+ } catch {
18330
+ return null;
18331
+ }
18332
+ }
18333
+ async function applyPersona(a, ctx, opts) {
18334
+ const current = await getAgentVersion({ credential: ctx.credential, projectEndpoint: opts.project.endpoint, agentName: opts.agentName });
18335
+ const personaPath = path20.join(opts.personaDir, "persona.md");
18336
+ const yamlValues = readAgentYaml(opts.agentName)?.fillableFieldValues ?? null;
18337
+ const values = resolveFillableValues(current, yamlValues);
18338
+ const declared = declaredFillableFields(personaPath);
18339
+ if (declared.length > 0 && values === null) {
18340
+ const defaultBody = injectAndRender(personaPath, defaultsOf(declared), opts.agentName);
18341
+ const candidateInstructions = composeInstructions({
18342
+ baseBody: defaultBody,
18343
+ current,
18344
+ loaderText: loaderText(opts.loaderPath),
18345
+ brainRepo: brainRepoOf(current)
18346
+ });
18347
+ if (candidateInstructions !== (current.definition.instructions ?? "")) {
18348
+ throw new LocalCliError({
18349
+ code: "PLATFORM_PERSONA_UNRECOVERABLE",
18350
+ message: `Cannot re-version '${opts.agentName}' \u2014 its instance field values are not recoverable (would revert to defaults). Re-deploy the advisor, or pass --content-dir with the correct values.`
18351
+ });
18352
+ }
18353
+ }
18354
+ const missing = declared.filter((f) => !values?.[f.name] && f.default === void 0);
18355
+ if (missing.length > 0) {
18356
+ throw new LocalCliError({
18357
+ code: "PLATFORM_PERSONA_FIELD_MISSING",
18358
+ message: `Persona '${a.personaName ?? opts.agentName}' needs values for: ${missing.map((f) => f.name).join(", ")}.`
18359
+ });
18360
+ }
18361
+ const baseBody = injectAndRender(personaPath, values ?? defaultsOf(declared), opts.agentName);
18362
+ const instructions = composeInstructions({
18363
+ baseBody,
18364
+ current,
18365
+ loaderText: loaderText(opts.loaderPath),
18366
+ brainRepo: brainRepoOf(current)
18367
+ });
18368
+ const metadata = {
18369
+ ...current.metadata ?? {},
18370
+ persona: a.personaName ?? opts.agentName,
18371
+ ...values ? { fillableFieldValues: JSON.stringify(values) } : {}
18372
+ };
18373
+ const candidate2 = { definition: { ...current.definition, instructions }, metadata };
18374
+ if (isNoop(candidate2, current)) return "noop";
18375
+ const foundryVersion = await createAgentVersion({
18376
+ credential: ctx.credential,
18377
+ projectEndpoint: opts.project.endpoint,
18378
+ agentName: opts.agentName,
18379
+ definition: candidate2.definition,
18380
+ metadata
18381
+ });
18382
+ return { treeSha: a.to, foundryVersion };
18383
+ }
18384
+
18385
+ // src/commands/platform/status.ts
18386
+ var PlatformStatusCommand = class extends M8tCommand {
18387
+ static paths = [["platform", "status"]];
18388
+ static usage = Command31.Usage({
18389
+ description: "Show per-component platform drift against a release manifest.",
18390
+ details: "Fetches a release manifest (the current channel by default), reads the installed-state stamp, and reports each component's installed vs target version and drift status. Read-only \u2014 never writes the stamp, never converges anything (use 'm8t platform update' for that). --verify additionally probes the live gateway (image tag + /api/version) best-effort."
18391
+ });
18392
+ subscription = Option29.String("--subscription");
18393
+ resourceGroup = Option29.String("--resource-group", {
18394
+ description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
18395
+ });
18396
+ to = Option29.String("--to", {
18397
+ description: "Check status against a pinned platform version (defaults to the current release channel)."
18398
+ });
18399
+ contentDir = Option29.String("--content-dir", {
18400
+ description: "Path to a local m8t-stack repo checkout (skips the tarball tree-SHA fetch)."
18401
+ });
18402
+ manifestUrl = Option29.String("--manifest-url", {
18403
+ description: "Fetch the release manifest from this URL instead of the release channel."
18404
+ });
18405
+ manifestFile = Option29.String("--manifest-file", {
18406
+ description: "Read the release manifest from this local file instead of the release channel."
18407
+ });
18408
+ verify = Option29.Boolean("--verify", false, {
18409
+ description: "Additionally probe the live gateway (image tag + /api/version) and report stamp-vs-live."
18410
+ });
18411
+ output = Option29.String("--output");
18412
+ async executeCommand() {
18413
+ const mode = resolveOutputMode(
18414
+ this.output,
18415
+ this.context.stdout
18416
+ );
18417
+ const log = (msg) => {
18418
+ if (mode !== "json") this.context.stdout.write(msg + "\n");
18419
+ };
18420
+ const to = typeof this.to === "string" ? this.to : void 0;
18421
+ const contentDirFlag = typeof this.contentDir === "string" ? this.contentDir : void 0;
18422
+ const manifestUrl = typeof this.manifestUrl === "string" ? this.manifestUrl : void 0;
18423
+ const manifestFile = typeof this.manifestFile === "string" ? this.manifestFile : void 0;
18424
+ const gw = await resolveGatewayContext({
18425
+ interactive: mode !== "json",
18426
+ subscriptionId: this.subscription,
18427
+ resourceGroup: this.resourceGroup
18428
+ });
18429
+ const { resourceGroup } = parseContainerAppResourceId(gw.containerAppResourceId);
18430
+ const credential2 = new DefaultAzureCredential16();
18431
+ const account = await getAzAccount();
18432
+ const subscriptionId = this.subscription ?? account.subscriptionId;
18433
+ const source = manifestFile ? { file: manifestFile } : manifestUrl ? { url: manifestUrl } : { channel: true, version: to };
18434
+ const manifest = await fetchManifest(source);
18435
+ const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
18436
+ const tree = contentDirFlag ? { treeShaOf: () => void 0 } : await fetchRepoTree(manifest.platform.commit);
18437
+ const rows = statusRows(manifest, stamp, tree);
18438
+ let verify;
18439
+ if (this.verify) {
18440
+ verify = await this.probeLive(gw.containerAppResourceId, gw.gatewayUrl, stamp?.components.gateway.tag);
18441
+ }
18442
+ if (mode === "json") {
18443
+ this.context.stdout.write(
18444
+ renderJson({
18445
+ kind: "platform-status",
18446
+ gateway: resourceGroup,
18447
+ targetVersion: manifest.platform.tag,
18448
+ managed: stamp !== null,
18449
+ rows,
18450
+ ...verify ? { verify } : {}
18451
+ }) + "\n"
18452
+ );
18453
+ return 0;
18454
+ }
18455
+ log(colors.field(`target: ${manifest.platform.tag}${stamp === null ? " (unmanaged \u2014 no stamp found)" : ""}`));
18456
+ this.context.stdout.write(this.renderRows(rows) + "\n");
18457
+ if (verify) {
18458
+ log("");
18459
+ log(colors.field("live verify:"));
18460
+ log(` stamp gateway tag: ${verify.stampGatewayTag}`);
18461
+ log(` live gateway tag: ${verify.liveGatewayTag}`);
18462
+ log(
18463
+ ` match: ${verify.gatewayTagMatch === "unknown" ? colors.warn("unknown") : verify.gatewayTagMatch ? colors.success("yes") : colors.warn("no")}`
18464
+ );
18465
+ log(` /api/version: ${verify.apiVersion}`);
18466
+ }
18467
+ return 0;
18468
+ }
18469
+ renderRows(rows) {
18470
+ const tableRows = rows.map((r) => ({
18471
+ component: r.personaName ? `personas:${r.personaName}` : r.component,
18472
+ installed: r.installed,
18473
+ target: r.target,
18474
+ status: r.status
18475
+ }));
18476
+ return renderTable(tableRows, [
18477
+ { key: "component", label: "COMPONENT" },
18478
+ { key: "installed", label: "INSTALLED" },
18479
+ { key: "target", label: "TARGET" },
18480
+ { key: "status", label: "STATUS" }
18481
+ ]);
18482
+ }
18483
+ /**
18484
+ * Best-effort live probe for --verify: the live gateway image tag (az) and
18485
+ * GET /api/version. Any failure (auth-gated endpoint, transient network
18486
+ * error, etc.) degrades the affected field to "unknown" rather than
18487
+ * throwing — a verify probe must never crash the read-only status command.
18488
+ */
18489
+ async probeLive(gatewayResourceId, gatewayUrl, stampGatewayTag) {
18490
+ let liveGatewayTag = "unknown";
18491
+ try {
18492
+ const { resourceGroup, name } = parseContainerAppResourceId(gatewayResourceId);
18493
+ const image = (await runAz([
18494
+ "containerapp",
18495
+ "show",
18496
+ "-g",
18497
+ resourceGroup,
18498
+ "-n",
18499
+ name,
18500
+ "--query",
18501
+ "properties.template.containers[0].image",
18502
+ "-o",
18503
+ "tsv"
18504
+ ])).trim();
18505
+ const tag = image.split(":").pop();
18506
+ if (tag) liveGatewayTag = tag;
18507
+ } catch {
18508
+ liveGatewayTag = "unknown";
18509
+ }
18510
+ let apiVersion = "unknown";
18511
+ try {
18512
+ const res = await fetch(`${gatewayUrl}/api/version`, { method: "GET" });
18513
+ if (res.ok) {
18514
+ const body = await res.json().catch(() => ({}));
18515
+ apiVersion = body.version ?? "unknown";
18516
+ } else if (res.status === 401 || res.status === 403) {
18517
+ apiVersion = "unknown (auth-gated)";
18518
+ }
18519
+ } catch {
18520
+ apiVersion = "unknown";
18521
+ }
18522
+ const stampTag = stampGatewayTag ?? "unknown";
18523
+ const gatewayTagMatch = liveGatewayTag === "unknown" || stampTag === "unknown" ? "unknown" : liveGatewayTag === stampTag;
18524
+ return { liveGatewayTag, stampGatewayTag: stampTag, gatewayTagMatch, apiVersion };
18525
+ }
18526
+ };
18527
+
18528
+ // src/commands/platform/update.ts
18529
+ import { Command as Command32, Option as Option30 } from "clipanion";
18530
+ import { confirm as confirm5 } from "@inquirer/prompts";
18531
+ import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/identity";
18532
+
18533
+ // src/lib/platform-converge-cli.ts
18534
+ import * as path21 from "path";
18535
+ init_errors();
18536
+ var HOSTED_AGENT_PERSONA_DIR = {
18537
+ codingAgent: "coding-agent",
18538
+ azureExecutor: "azure-executor"
18539
+ };
18540
+ async function buildConvergeDeps(args) {
18541
+ const warn = args.onWarn ?? (() => {
18542
+ });
18543
+ const discoveredAgents = await listM8tAgents({ credential: args.credential, projectEndpoint: args.project.endpoint });
18544
+ const discovered = {};
18545
+ for (const a of discoveredAgents) {
18546
+ const persona = a.metadata.persona;
18547
+ if (persona) discovered[persona] = a.name;
18548
+ }
18549
+ const loaderPath = path21.join(args.contentDir, "targets/foundry/brain-loader.md");
18550
+ return {
18551
+ async applyPersona(a, ctx) {
18552
+ const personaName = a.personaName;
18553
+ if (!personaName) {
18554
+ throw new LocalCliError({ code: "PLATFORM_PERSONA_NO_NAME", message: "persona action missing personaName" });
18555
+ }
18556
+ const agentName = discovered[personaName];
18557
+ if (!agentName) {
18558
+ warn(`persona '${personaName}' is not deployed on this install \u2014 skipping.`);
18559
+ return "absent";
18560
+ }
18561
+ const personaItem = ctx.manifest.components.personas.items[personaName];
18562
+ return applyPersona(a, ctx, {
18563
+ agentName,
18564
+ project: args.project,
18565
+ personaDir: path21.join(ctx.contentDir, personaItem.path),
18566
+ loaderPath
18567
+ });
18568
+ },
18569
+ async applyAgent(a, ctx) {
18570
+ if (a.component !== "codingAgent" && a.component !== "azureExecutor") {
18571
+ throw new LocalCliError({
18572
+ code: "PLATFORM_AGENT_BAD_COMPONENT",
18573
+ message: `applyAgent called with non-hosted component '${a.component}'`
18574
+ });
18575
+ }
18576
+ const dir = HOSTED_AGENT_PERSONA_DIR[a.component];
18577
+ const agentName = dir ? discovered[dir] : void 0;
18578
+ if (!agentName) {
18579
+ warn(`hosted agent for component '${a.component}' is not deployed on this install \u2014 skipping.`);
18580
+ return "absent";
18581
+ }
18582
+ const componentRef = ctx.manifest.components[a.component];
18583
+ return applyAgentImage(a, ctx, {
18584
+ agentName,
18585
+ project: args.project,
18586
+ ghcrImage: `${componentRef.ref}:${a.to}`
18587
+ });
18588
+ },
18589
+ async applyGateway(a, ctx) {
18590
+ return applyGatewayImage(a, ctx, args.gatewayResourceId);
18591
+ },
18592
+ async applyInfra(a, ctx) {
18593
+ const target = infraTargetSha(ctx.manifest, args.tree);
18594
+ return applyInfraDeploy(a, ctx, target, { suffix: args.suffix });
18595
+ },
18596
+ async healthGate(_ctx, applied) {
18597
+ for (const a of applied) {
18598
+ let agentName;
18599
+ if (a.component === "personas" && a.personaName) {
18600
+ agentName = discovered[a.personaName];
18601
+ } else if (a.component === "codingAgent" || a.component === "azureExecutor") {
18602
+ const dir = HOSTED_AGENT_PERSONA_DIR[a.component];
18603
+ agentName = dir ? discovered[dir] : void 0;
18604
+ }
18605
+ if (!agentName) continue;
18606
+ await awaitAgentQueryable({ credential: args.credential, projectEndpoint: args.project.endpoint, agentName, onProgress: args.onProgress });
18607
+ }
18608
+ if (args.gatewayUrl) {
18609
+ try {
18610
+ const res = await fetch(`${args.gatewayUrl}/api/version`, { method: "GET" });
18611
+ if (res.ok) {
18612
+ const body = await res.json().catch(() => ({}));
18613
+ args.onProgress?.(`gateway reachable (version: ${body.version ?? "unknown"}).`);
18614
+ } else if (res.status === 401 || res.status === 403) {
18615
+ args.onProgress?.(`gateway reachable (auth-gated, HTTP ${res.status.toString()}).`);
18616
+ } else {
18617
+ warn(`gateway health probe returned HTTP ${res.status.toString()} \u2014 not treated as fatal.`);
18618
+ }
18619
+ } catch (e) {
18620
+ warn(`gateway health probe failed: ${e.message} \u2014 not treated as fatal.`);
18621
+ }
18622
+ }
18623
+ },
18624
+ async readStamp() {
18625
+ return readStamp({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
18626
+ },
18627
+ async writeStamp(s) {
18628
+ await writeStamp({
18629
+ credential: args.credential,
18630
+ subscriptionId: args.subscriptionId,
18631
+ resourceGroup: args.resourceGroup,
18632
+ stamp: { ...s, cli: CLI_VERSION },
18633
+ onProgress: args.onProgress
18634
+ });
18635
+ }
18636
+ };
18637
+ }
18638
+ function toVTag(v) {
18639
+ return v.startsWith("v") ? v : `v${v}`;
18640
+ }
18641
+ function assertCliVersionOk(manifest, runningVersion, onWarn = () => {
18642
+ }) {
18643
+ const cli2 = manifest.components.cli;
18644
+ const running = toVTag(runningVersion);
18645
+ const min = toVTag(cli2.min);
18646
+ const recommended = toVTag(cli2.recommended);
18647
+ if (compareSemver(running, min) < 0) {
18648
+ throw new LocalCliError({
18649
+ code: "PLATFORM_CLI_TOO_OLD",
18650
+ message: `This CLI (${runningVersion}) is older than the minimum supported version (${cli2.min}) for platform ${manifest.platform.tag}.`,
18651
+ hint: "Upgrade: 'npm install -g @m8t-stack/cli@latest' (or your package manager's equivalent)."
18652
+ });
18653
+ }
18654
+ if (compareSemver(running, recommended) < 0) {
18655
+ onWarn(`This CLI (${runningVersion}) is older than the recommended version (${cli2.recommended}) for platform ${manifest.platform.tag}. Consider upgrading.`);
18656
+ }
18657
+ }
18658
+
18659
+ // src/commands/platform/update.ts
18660
+ var PlatformUpdateCommand = class extends M8tCommand {
18661
+ static paths = [["platform", "update"]];
18662
+ static usage = Command32.Usage({
18663
+ description: "Converge the whole platform (gateway, hosted agents, personas, infra) to a release manifest.",
18664
+ details: "Fetches a release manifest (the current channel by default), diffs it against the installed-state stamp, and converges each changed component via its own executor: rolls the gateway/hosted-agent images, re-versions prompt personas (compose-preserving), and records infra state. A deploy/ tree drift is reported but NOT applied unless --force-infra is passed. Use --check to preview, --only to converge a single component, --yes to skip the confirmation prompt."
18665
+ });
18666
+ subscription = Option30.String("--subscription");
18667
+ resourceGroup = Option30.String("--resource-group", {
18668
+ description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
18669
+ });
18670
+ to = Option30.String("--to", {
18671
+ description: "Pin the platform version to converge to (defaults to the current release channel)."
18672
+ });
18673
+ only = Option30.String("--only", {
18674
+ description: "Converge only one component: infra | gateway | codingAgent | azureExecutor | personas."
18675
+ });
18676
+ check = Option30.Boolean("--check", false);
18677
+ yes = Option30.Boolean("--yes", false);
18678
+ skipInfra = Option30.Boolean("--skip-infra", false);
18679
+ forceInfra = Option30.Boolean("--force-infra", false);
18680
+ suffix = Option30.String("--suffix", {
18681
+ description: "Required with --force-infra \u2014 the existing deployment's resource-name suffix (avoids provisioning duplicates)."
18682
+ });
18683
+ contentDir = Option30.String("--content-dir", {
18684
+ description: "Path to a local m8t-stack repo checkout (skips the tarball fetch + tree-drift check)."
18685
+ });
18686
+ manifestUrl = Option30.String("--manifest-url", {
18687
+ description: "Fetch the release manifest from this URL instead of the release channel."
18688
+ });
18689
+ manifestFile = Option30.String("--manifest-file", {
18690
+ description: "Read the release manifest from this local file instead of the release channel."
18691
+ });
18692
+ endpoint = Option30.String("--endpoint", {
18693
+ description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
18694
+ });
18695
+ output = Option30.String("--output");
18696
+ async executeCommand() {
18697
+ const mode = resolveOutputMode(
18698
+ this.output,
18699
+ this.context.stdout
18700
+ );
18701
+ const interactive = this.context.stdout.isTTY === true;
18702
+ const log = (msg) => {
18703
+ if (mode !== "json") this.context.stdout.write(msg + "\n");
18704
+ };
18705
+ const to = typeof this.to === "string" ? this.to : void 0;
18706
+ const only = typeof this.only === "string" ? this.only : void 0;
18707
+ const suffix = typeof this.suffix === "string" ? this.suffix : void 0;
18708
+ const contentDirFlag = typeof this.contentDir === "string" ? this.contentDir : void 0;
18709
+ const manifestUrl = typeof this.manifestUrl === "string" ? this.manifestUrl : void 0;
18710
+ const manifestFile = typeof this.manifestFile === "string" ? this.manifestFile : void 0;
18711
+ const gw = await resolveGatewayContext({
18712
+ interactive: mode !== "json",
18713
+ subscriptionId: this.subscription,
18714
+ resourceGroup: this.resourceGroup
18715
+ });
18716
+ const { resourceGroup } = parseContainerAppResourceId(gw.containerAppResourceId);
18717
+ const credential2 = new DefaultAzureCredential17();
18718
+ const account = await getAzAccount();
18719
+ const subscriptionId = this.subscription ?? account.subscriptionId;
18720
+ const onProgress = (m) => {
18721
+ log(colors.dim(m));
18722
+ };
18723
+ const onWarn = (w) => {
18724
+ log(colors.warn(w));
18725
+ };
18726
+ onProgress("resolving Foundry project\u2026");
18727
+ const project = await resolveFoundryProject({
18728
+ credential: credential2,
18729
+ subscriptionId,
18730
+ interactive,
18731
+ endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
18732
+ });
18733
+ const source = manifestFile ? { file: manifestFile } : manifestUrl ? { url: manifestUrl } : { channel: true, version: to };
18734
+ onProgress("fetching release manifest\u2026");
18735
+ const manifest = await fetchManifest(source);
18736
+ assertCliVersionOk(manifest, CLI_VERSION, onWarn);
18737
+ const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
18738
+ onProgress("resolving release content\u2026");
18739
+ const contentDir = await resolveReleaseContent(manifest, { contentDir: contentDirFlag });
18740
+ const tree = contentDirFlag ? { treeShaOf: () => void 0 } : await fetchRepoTree(manifest.platform.commit);
18741
+ if (!contentDirFlag) {
18742
+ const mismatches = verifyPersonaTrees(manifest, tree);
18743
+ if (mismatches.length > 0) {
18744
+ log(colors.warn(`warning: ${mismatches.length.toString()} persona tree mismatch(es) vs the manifest.`));
18745
+ }
18746
+ }
18747
+ const plan = diffPlan(manifest, stamp, tree, { only, forceInfra: this.forceInfra, skipInfra: this.skipInfra });
18748
+ this.renderPlan(plan, mode);
18749
+ const deferredInfra = plan.skipped.find((s) => s.component === "infra" && s.reason === "deferred");
18750
+ if (deferredInfra) {
18751
+ log(
18752
+ colors.warn(
18753
+ `infra changed in ${plan.targetVersion} (deploy/ tree drift) \u2014 NOT applied by the CLI. Run 'm8t platform update --force-infra --suffix <n>' or let the update job converge it.`
18754
+ )
18755
+ );
18756
+ }
18757
+ if (this.check) {
18758
+ if (mode === "json") this.context.stdout.write(renderJson({ ...plan, checked: true, applied: false }) + "\n");
18759
+ return 0;
18760
+ }
18761
+ if (plan.actions.length === 0) {
18762
+ if (mode === "json") this.context.stdout.write(renderJson({ ...plan, applied: false }) + "\n");
18763
+ else log(colors.success("already up to date \u2713"));
18764
+ return 0;
18765
+ }
18766
+ if (!this.yes && interactive) {
18767
+ const ok = await confirm5({
18768
+ message: `Converge ${plan.actions.length.toString()} component(s) to ${plan.targetVersion}?`,
18769
+ default: true
18770
+ });
18771
+ if (!ok) {
18772
+ log(colors.dim("aborted \u2014 no change."));
18773
+ return 0;
18774
+ }
18775
+ }
18776
+ const deps = await buildConvergeDeps({
18777
+ credential: credential2,
18778
+ subscriptionId,
18779
+ resourceGroup,
18780
+ contentDir,
18781
+ manifest,
18782
+ tree,
18783
+ project,
18784
+ gatewayResourceId: gw.containerAppResourceId,
18785
+ gatewayUrl: gw.gatewayUrl,
18786
+ suffix,
18787
+ onProgress,
18788
+ onWarn
18789
+ });
18790
+ await applyPlan(plan, deps, { credential: credential2, subscriptionId, resourceGroup, contentDir, manifest, onProgress });
18791
+ if (mode === "json") this.context.stdout.write(renderJson({ ...plan, applied: true }) + "\n");
18792
+ else log(colors.success(`converged \u2192 ${plan.targetVersion} \u2713`));
18793
+ return 0;
18794
+ }
18795
+ renderPlan(plan, mode) {
18796
+ if (mode === "json") return;
18797
+ const rows = [
18798
+ ...plan.actions.map((a) => ({
18799
+ component: a.personaName ? `personas:${a.personaName}` : a.component,
18800
+ from: a.from ?? "(none)",
18801
+ to: a.to,
18802
+ reason: a.reason
18803
+ })),
18804
+ ...plan.skipped.map((s) => ({
18805
+ component: s.personaName ? `personas:${s.personaName}` : s.component,
18806
+ from: "-",
18807
+ to: "-",
18808
+ reason: s.reason
18809
+ }))
18810
+ ];
18811
+ this.context.stdout.write(
18812
+ renderTable(rows, [
18813
+ { key: "component", label: "COMPONENT" },
18814
+ { key: "from", label: "FROM" },
18815
+ { key: "to", label: "TO" },
18816
+ { key: "reason", label: "REASON" }
18817
+ ]) + "\n"
18818
+ );
18819
+ }
18820
+ };
18821
+
18822
+ // src/commands/platform/enable-cost-report.ts
18823
+ import { Command as Command33, Option as Option31 } from "clipanion";
18824
+
18825
+ // src/lib/wire-gateway-acs.ts
18826
+ init_rbac();
18827
+ async function wireGatewayForAcs(args) {
18828
+ try {
18829
+ await args.az([
18830
+ "role",
18831
+ "assignment",
18832
+ "create",
18833
+ "--assignee-object-id",
18834
+ args.gatewayPrincipalId,
18835
+ "--assignee-principal-type",
18836
+ "ServicePrincipal",
18837
+ "--role",
18838
+ CONTRIBUTOR_ROLE_ID,
18839
+ "--scope",
18840
+ args.acsResourceId
18841
+ ]);
18842
+ } catch (e) {
18843
+ if (!(e instanceof Error && /RoleAssignmentExists/i.test(e.message))) throw e;
18844
+ }
18845
+ await args.az([
18846
+ "containerapp",
18847
+ "update",
18848
+ "-n",
17670
18849
  args.gatewayApp,
17671
18850
  "-g",
17672
18851
  args.resourceGroup,
@@ -18023,156 +19202,11 @@ async function patchRedirectUris(appObjectId, fqdn) {
18023
19202
  ]);
18024
19203
  }
18025
19204
 
18026
- // src/lib/deploy.ts
18027
- import * as fs18 from "fs/promises";
18028
- import * as os8 from "os";
18029
- import * as path18 from "path";
18030
- init_errors();
18031
- var FOUNDRY_TRACING_MODES = ["project", "account", "skip"];
18032
- function parseFoundryTracingMode(v) {
18033
- if (v === void 0) return void 0;
18034
- if (!FOUNDRY_TRACING_MODES.includes(v)) {
18035
- throw new LocalCliError({
18036
- code: "DEPLOY_INVALID_TRACING_MODE",
18037
- message: `Invalid --foundry-tracing value: '${v}'.`,
18038
- hint: `Use one of: ${FOUNDRY_TRACING_MODES.join(", ")}.`
18039
- });
18040
- }
18041
- return v;
18042
- }
18043
- function buildBicepParams(p) {
18044
- const params = [
18045
- `location=${p.location}`,
18046
- `suffix=${p.suffix}`,
18047
- `imageRef=${p.imageRef}`,
18048
- `tenantId=${p.tenantId}`,
18049
- `clientId=${p.clientId}`,
18050
- `foundryResourceId=${p.foundryResourceId}`,
18051
- `foundryProjectEndpoint=${p.foundryProjectEndpoint}`,
18052
- `acrPullIdentityResourceId=${p.acrPullIdentityResourceId}`,
18053
- `acrResourceId=${p.acrResourceId}`
18054
- ];
18055
- if (p.foundryTracingMode) params.push(`foundryTracingMode=${p.foundryTracingMode}`);
18056
- return params;
18057
- }
18058
- async function resolveRepoRoot2(home = os8.homedir()) {
18059
- const marker = path18.join(home, ".m8t-stack", "repo-root");
18060
- try {
18061
- const root = (await fs18.readFile(marker, "utf8")).trim();
18062
- if (!root) throw new Error("empty");
18063
- return root;
18064
- } catch {
18065
- throw new LocalCliError({
18066
- code: "DEPLOY_NO_REPO_ROOT",
18067
- message: "~/.m8t-stack/repo-root marker is missing or empty.",
18068
- hint: "Run 'm8t install' from an m8t-stack checkout (it writes the marker), or run 'm8t deploy' from the repo."
18069
- });
18070
- }
18071
- }
18072
- async function resolveFoundryResourceId(endpoint, explicit) {
18073
- if (explicit) return explicit;
18074
- const m = /^https:\/\/([^.]+)\.services\.ai\.azure\.com/.exec(endpoint);
18075
- if (!m) {
18076
- throw new LocalCliError({
18077
- code: "DEPLOY_FOUNDRY_ENDPOINT_BAD",
18078
- message: `Cannot parse the Foundry account name from endpoint: ${endpoint}`,
18079
- hint: "Pass --foundry-resource-id <ARM-id> explicitly."
18080
- });
18081
- }
18082
- const accounts = JSON.parse(
18083
- await runAz([
18084
- "cognitiveservices",
18085
- "account",
18086
- "list",
18087
- "--query",
18088
- `[?name=='${m[1]}'].id`,
18089
- "--output",
18090
- "json"
18091
- ])
18092
- );
18093
- if (accounts.length === 0) {
18094
- throw new LocalCliError({
18095
- code: "DEPLOY_FOUNDRY_NOT_FOUND",
18096
- message: `No Foundry account named '${m[1]}' in the active subscription.`,
18097
- hint: "Check the endpoint, or pass --foundry-resource-id explicitly."
18098
- });
18099
- }
18100
- if (accounts.length > 1) {
18101
- throw new LocalCliError({
18102
- code: "DEPLOY_FOUNDRY_AMBIGUOUS",
18103
- message: `Multiple Foundry accounts named '${m[1]}' \u2014 ambiguous.`,
18104
- hint: "Pass --foundry-resource-id <ARM-id> to disambiguate."
18105
- });
18106
- }
18107
- return accounts[0];
18108
- }
18109
- async function resolveAcrResourceId(opts) {
18110
- if (opts.explicit) return opts.explicit;
18111
- const host = opts.imageRef.split("/")[0];
18112
- if (!host.endsWith(".azurecr.io")) return "";
18113
- const acrName = host.split(".")[0];
18114
- try {
18115
- const id = (await runAz(["acr", "show", "-n", acrName, "--query", "id", "-o", "tsv"])).trim();
18116
- if (!id) throw new Error("empty id");
18117
- return id;
18118
- } catch (e) {
18119
- if (e instanceof LocalCliError) throw e;
18120
- throw new LocalCliError({
18121
- code: "DEPLOY_ACR_NOT_FOUND",
18122
- message: `Cannot resolve the ACR '${acrName}' for image ${opts.imageRef}.`,
18123
- hint: "Create it (az acr create), sign in (az acr login), and push the web image (docker buildx \u2026 --push); or pass --acr-resource-id <ARM-id>."
18124
- });
18125
- }
18126
- }
18127
- async function ensureResourceGroup(name, location) {
18128
- const existing = JSON.parse(
18129
- await runAz(["group", "show", "--name", name, "--output", "json"]).catch(() => "") || "null"
18130
- );
18131
- if (existing) return;
18132
- await runAz(["group", "create", "--name", name, "--location", location, "--tags", "m8t-stack=deployment"]);
18133
- }
18134
- function resolveAcrPullIdentity(opts) {
18135
- return opts.explicit ?? "";
18136
- }
18137
- async function runBicepDeployment(opts) {
18138
- const bicepPath = path18.join(opts.repoRoot, "deploy", "main.bicep");
18139
- const result = JSON.parse(
18140
- await runAz([
18141
- "deployment",
18142
- "group",
18143
- "create",
18144
- "--name",
18145
- opts.deploymentName,
18146
- "--resource-group",
18147
- opts.resourceGroup,
18148
- "--template-file",
18149
- bicepPath,
18150
- "--parameters",
18151
- ...opts.params,
18152
- "--output",
18153
- "json"
18154
- ])
18155
- );
18156
- const outputs = result.properties?.outputs ?? {};
18157
- const fqdn = outputs.containerAppFqdn?.value;
18158
- if (typeof fqdn !== "string" || !fqdn) {
18159
- throw new LocalCliError({
18160
- code: "DEPLOY_NO_FQDN",
18161
- message: "Bicep deployment did not return containerAppFqdn.",
18162
- hint: "Check deploy/main.bicep outputs and the deployment log."
18163
- });
18164
- }
18165
- return {
18166
- containerAppFqdn: fqdn,
18167
- resourceNames: outputs.resourceNames?.value ?? {}
18168
- };
18169
- }
18170
-
18171
19205
  // src/commands/deploy.ts
18172
19206
  init_errors();
18173
19207
 
18174
19208
  // src/lib/whatif.ts
18175
- import * as path19 from "path";
19209
+ import * as path22 from "path";
18176
19210
  function deriveResourceType(resourceId) {
18177
19211
  if (resourceId.startsWith("[")) return "";
18178
19212
  const afterProviders = resourceId.split("/providers/")[1];
@@ -18183,7 +19217,7 @@ function deriveResourceType(resourceId) {
18183
19217
  return parts.join("/");
18184
19218
  }
18185
19219
  async function runWhatIf(opts) {
18186
- const bicepPath = path19.join(opts.repoRoot, "deploy", "main.bicep");
19220
+ const bicepPath = path22.join(opts.repoRoot, "deploy", "main.bicep");
18187
19221
  const out = await runAz([
18188
19222
  "deployment",
18189
19223
  "group",
@@ -18248,9 +19282,9 @@ function flattenDelta(delta, prefix = "") {
18248
19282
  const out = [];
18249
19283
  for (const d of delta) {
18250
19284
  const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
18251
- const path29 = prefix ? `${prefix}${segment}` : d.path;
18252
- if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path29));
18253
- else out.push({ ...d, path: path29 });
19285
+ const path32 = prefix ? `${prefix}${segment}` : d.path;
19286
+ if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path32));
19287
+ else out.push({ ...d, path: path32 });
18254
19288
  }
18255
19289
  return out;
18256
19290
  }
@@ -18520,8 +19554,8 @@ var EvalSkillCommand = class extends M8tCommand {
18520
19554
 
18521
19555
  // src/commands/eval/exam.ts
18522
19556
  import { spawnSync as spawnSync4 } from "child_process";
18523
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync13, existsSync as existsSync12, readdirSync } from "fs";
18524
- import { join as join19 } from "path";
19557
+ import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync15, existsSync as existsSync13, readdirSync } from "fs";
19558
+ import { join as join22 } from "path";
18525
19559
  import { Command as Command36, Option as Option34 } from "clipanion";
18526
19560
  init_errors();
18527
19561
  init_esm();
@@ -18687,10 +19721,10 @@ function resolveRefereeHome(refereeHomeOut, env) {
18687
19721
  return null;
18688
19722
  }
18689
19723
  function resolveTaskSetDir(base, worker, version) {
18690
- if (existsSync12(join19(base, version))) return version;
19724
+ if (existsSync13(join22(base, version))) return version;
18691
19725
  const prefixed = `${worker}-${version}`;
18692
- if (existsSync12(join19(base, prefixed))) return prefixed;
18693
- if (version === "latest" && existsSync12(base)) {
19726
+ if (existsSync13(join22(base, prefixed))) return prefixed;
19727
+ if (version === "latest" && existsSync13(base)) {
18694
19728
  const dirs = readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
18695
19729
  if (dirs.length > 0) return dirs[dirs.length - 1];
18696
19730
  }
@@ -18705,11 +19739,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
18705
19739
  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)"
18706
19740
  });
18707
19741
  }
18708
- const versionDir = resolveTaskSetDir(join19(home, "tasksets", worker), worker, version);
18709
- const manifestPath = join19(home, "tasksets", worker, versionDir, "manifest.yaml");
19742
+ const versionDir = resolveTaskSetDir(join22(home, "tasksets", worker), worker, version);
19743
+ const manifestPath = join22(home, "tasksets", worker, versionDir, "manifest.yaml");
18710
19744
  let text;
18711
19745
  try {
18712
- text = readFileSync13(manifestPath, "utf-8");
19746
+ text = readFileSync15(manifestPath, "utf-8");
18713
19747
  } catch (e) {
18714
19748
  throw new LocalCliError({
18715
19749
  code: "EXAM_REDACTION_UNSAFE",
@@ -18827,10 +19861,10 @@ var EvalExamCommand = class extends M8tCommand {
18827
19861
  const verdict = parseExamVerdict(res.stdout);
18828
19862
  if (out !== void 0) {
18829
19863
  const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
18830
- mkdirSync3(out, { recursive: true });
18831
- writeFileSync3(join19(out, "verdict.json"), JSON.stringify(verdict, null, 2));
19864
+ mkdirSync4(out, { recursive: true });
19865
+ writeFileSync4(join22(out, "verdict.json"), JSON.stringify(verdict, null, 2));
18832
19866
  const feed = redactForFeed(verdict, sealedTaskIds);
18833
- writeFileSync3(join19(out, "feed.json"), JSON.stringify(feed, null, 2));
19867
+ writeFileSync4(join22(out, "feed.json"), JSON.stringify(feed, null, 2));
18834
19868
  }
18835
19869
  if (mode === "json") {
18836
19870
  this.context.stdout.write(renderJson(verdict) + "\n");
@@ -19092,10 +20126,10 @@ var StatusCommand = class extends M8tCommand {
19092
20126
 
19093
20127
  // src/commands/doctor.ts
19094
20128
  import { Command as Command40, Option as Option38 } from "clipanion";
19095
- import { DefaultAzureCredential as DefaultAzureCredential16 } from "@azure/identity";
19096
- import * as fs19 from "fs";
19097
- import * as os9 from "os";
19098
- import * as path20 from "path";
20129
+ import { DefaultAzureCredential as DefaultAzureCredential18 } from "@azure/identity";
20130
+ import * as fs22 from "fs";
20131
+ import * as os10 from "os";
20132
+ import * as path23 from "path";
19099
20133
 
19100
20134
  // src/lib/doctor-checks.ts
19101
20135
  function checkAzSignedIn(az) {
@@ -19320,24 +20354,24 @@ async function resolveAccountLocation(accountId) {
19320
20354
  }
19321
20355
  }
19322
20356
  function probeRepoRoot() {
19323
- const marker = path20.join(os9.homedir(), ".m8t-stack", "repo-root");
19324
- if (!fs19.existsSync(marker)) {
20357
+ const marker = path23.join(os10.homedir(), ".m8t-stack", "repo-root");
20358
+ if (!fs22.existsSync(marker)) {
19325
20359
  return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
19326
20360
  }
19327
20361
  let repoPath;
19328
20362
  try {
19329
- repoPath = fs19.readFileSync(marker, "utf8").trim();
20363
+ repoPath = fs22.readFileSync(marker, "utf8").trim();
19330
20364
  } catch {
19331
20365
  return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
19332
20366
  }
19333
20367
  if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
19334
20368
  let isDir = false;
19335
20369
  try {
19336
- isDir = fs19.statSync(repoPath).isDirectory();
20370
+ isDir = fs22.statSync(repoPath).isDirectory();
19337
20371
  } catch {
19338
20372
  isDir = false;
19339
20373
  }
19340
- const isGitCheckout = isDir && fs19.existsSync(path20.join(repoPath, ".git"));
20374
+ const isGitCheckout = isDir && fs22.existsSync(path23.join(repoPath, ".git"));
19341
20375
  return { markerExists: true, path: repoPath, isDir, isGitCheckout };
19342
20376
  }
19343
20377
  var DoctorCommand = class extends M8tCommand {
@@ -19420,7 +20454,7 @@ var DoctorCommand = class extends M8tCommand {
19420
20454
  if (typeof this.agent === "string" && this.agent) {
19421
20455
  checking(`delivery grant for ${this.agent}`);
19422
20456
  try {
19423
- const credential2 = new DefaultAzureCredential16();
20457
+ const credential2 = new DefaultAzureCredential18();
19424
20458
  const cur = await getAgentVersion({
19425
20459
  credential: credential2,
19426
20460
  projectEndpoint: foundry.projectEndpoint,
@@ -19466,15 +20500,15 @@ var DoctorCommand = class extends M8tCommand {
19466
20500
  import { Command as Command41, Option as Option39 } from "clipanion";
19467
20501
 
19468
20502
  // src/lib/profiles.ts
19469
- import * as fs20 from "fs/promises";
19470
- import * as path21 from "path";
19471
- import { parse as parseYaml10, stringify as stringifyYaml4 } from "yaml";
20503
+ import * as fs23 from "fs/promises";
20504
+ import * as path24 from "path";
20505
+ import { parse as parseYaml11, stringify as stringifyYaml4 } from "yaml";
19472
20506
  function profilesDir() {
19473
20507
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
19474
- return path21.join(home, ".m8t-stack", "profiles");
20508
+ return path24.join(home, ".m8t-stack", "profiles");
19475
20509
  }
19476
20510
  function getProfilePath(name) {
19477
- return path21.join(profilesDir(), `${path21.basename(name)}.yaml`);
20511
+ return path24.join(profilesDir(), `${path24.basename(name)}.yaml`);
19478
20512
  }
19479
20513
  function slugifyTenant(domainOrId) {
19480
20514
  const base = domainOrId.split(".")[0].toLowerCase();
@@ -19483,7 +20517,7 @@ function slugifyTenant(domainOrId) {
19483
20517
  }
19484
20518
  async function listProfiles() {
19485
20519
  try {
19486
- const entries = await fs20.readdir(profilesDir());
20520
+ const entries = await fs23.readdir(profilesDir());
19487
20521
  return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
19488
20522
  } catch (e) {
19489
20523
  if (e.code === "ENOENT") return [];
@@ -19492,8 +20526,8 @@ async function listProfiles() {
19492
20526
  }
19493
20527
  async function readProfile(name) {
19494
20528
  try {
19495
- const raw = await fs20.readFile(getProfilePath(name), "utf8");
19496
- const parsed = parseYaml10(raw);
20529
+ const raw = await fs23.readFile(getProfilePath(name), "utf8");
20530
+ const parsed = parseYaml11(raw);
19497
20531
  if (!parsed || typeof parsed !== "object") return null;
19498
20532
  return parsed;
19499
20533
  } catch (e) {
@@ -19503,14 +20537,14 @@ async function readProfile(name) {
19503
20537
  }
19504
20538
  async function exists(p) {
19505
20539
  try {
19506
- await fs20.stat(p);
20540
+ await fs23.stat(p);
19507
20541
  return true;
19508
20542
  } catch {
19509
20543
  return false;
19510
20544
  }
19511
20545
  }
19512
20546
  async function saveProfile(p) {
19513
- await fs20.mkdir(profilesDir(), { recursive: true });
20547
+ await fs23.mkdir(profilesDir(), { recursive: true });
19514
20548
  let name = p.name;
19515
20549
  let n = 2;
19516
20550
  while (await exists(getProfilePath(name))) {
@@ -19519,8 +20553,8 @@ async function saveProfile(p) {
19519
20553
  }
19520
20554
  const target = getProfilePath(name);
19521
20555
  const tmp = `${target}.tmp`;
19522
- await fs20.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
19523
- await fs20.rename(tmp, target);
20556
+ await fs23.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
20557
+ await fs23.rename(tmp, target);
19524
20558
  return name;
19525
20559
  }
19526
20560
 
@@ -19766,7 +20800,7 @@ var OpenCommand = class extends M8tCommand {
19766
20800
  // src/commands/dream/run.ts
19767
20801
  import { Command as Command43, Option as Option41 } from "clipanion";
19768
20802
  import { AzureCliCredential } from "@azure/identity";
19769
- import { TableClient as TableClient2 } from "@azure/data-tables";
20803
+ import { TableClient as TableClient3 } from "@azure/data-tables";
19770
20804
  import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
19771
20805
  import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
19772
20806
 
@@ -20073,7 +21107,7 @@ var ConvFetchError = class extends Error {
20073
21107
  };
20074
21108
 
20075
21109
  // ../../packages/brain/engine/dist/esm/cursor.js
20076
- import { TableClient } from "@azure/data-tables";
21110
+ import { TableClient as TableClient2 } from "@azure/data-tables";
20077
21111
 
20078
21112
  // ../../packages/agent-ledger/dist/esm/row-key.js
20079
21113
  var MAX_MS = 864e13;
@@ -20195,7 +21229,7 @@ async function commitCursorAdvance(plan, cursor) {
20195
21229
  }
20196
21230
  var CURSOR_TABLE_NAME = "BrainDreamCursor";
20197
21231
  function makeTableCursor(credential2, tableEndpoint) {
20198
- const client = new TableClient(tableEndpoint, CURSOR_TABLE_NAME, credential2);
21232
+ const client = new TableClient2(tableEndpoint, CURSOR_TABLE_NAME, credential2);
20199
21233
  return new TableCursor(client);
20200
21234
  }
20201
21235
  function formatCursorPreview(cursor) {
@@ -20272,7 +21306,7 @@ function mapItems(raw) {
20272
21306
  return acc.reverse();
20273
21307
  }
20274
21308
  function makeConversationSource(client, opts = {}) {
20275
- const sleep2 = opts.sleep ?? defaultSleep;
21309
+ const sleep3 = opts.sleep ?? defaultSleep;
20276
21310
  return {
20277
21311
  async items(conversationId) {
20278
21312
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
@@ -20290,7 +21324,7 @@ function makeConversationSource(client, opts = {}) {
20290
21324
  throw new ConvFetchError("auth", `auth failed reading ${conversationId}`);
20291
21325
  const retryable = status === void 0 || status === TRANSIENT;
20292
21326
  if (retryable && attempt < MAX_ATTEMPTS - 1) {
20293
- await sleep2(attempt);
21327
+ await sleep3(attempt);
20294
21328
  continue;
20295
21329
  }
20296
21330
  throw new ConvFetchError("terminal", `exhausted ${String(MAX_ATTEMPTS)} retries reading ${conversationId}: ${e?.message ?? String(e)}`);
@@ -20689,7 +21723,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
20689
21723
  // ../../packages/brain/engine/dist/esm/dream/writer.js
20690
21724
  var API2 = "https://api.github.com";
20691
21725
  var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
20692
- var isAllowedWritePath = (path29) => WRITER_ALLOWED_PREFIX.test(path29) && !path29.split("/").includes("..");
21726
+ var isAllowedWritePath = (path32) => WRITER_ALLOWED_PREFIX.test(path32) && !path32.split("/").includes("..");
20693
21727
  var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
20694
21728
  function parseFrontmatter(content) {
20695
21729
  const m = /^---\n([\s\S]*?)\n---/.exec(content);
@@ -20728,8 +21762,8 @@ function parseFrontmatter(content) {
20728
21762
  function makeGitDataWriter(args) {
20729
21763
  const doFetch = args.fetchImpl ?? fetch;
20730
21764
  const repoPath = args.repository;
20731
- async function gh(token, method, path29, body) {
20732
- const res = await doFetch(`${API2}/repos/${repoPath}${path29}`, {
21765
+ async function gh(token, method, path32, body) {
21766
+ const res = await doFetch(`${API2}/repos/${repoPath}${path32}`, {
20733
21767
  method,
20734
21768
  headers: {
20735
21769
  Authorization: `Bearer ${token}`,
@@ -20742,8 +21776,8 @@ function makeGitDataWriter(args) {
20742
21776
  const text = await res.text();
20743
21777
  return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
20744
21778
  }
20745
- async function readFileWith(token, path29) {
20746
- const encodedPath = path29.split("/").map(encodeURIComponent).join("/");
21779
+ async function readFileWith(token, path32) {
21780
+ const encodedPath = path32.split("/").map(encodeURIComponent).join("/");
20747
21781
  const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
20748
21782
  if (r.status === 404)
20749
21783
  return null;
@@ -20759,9 +21793,9 @@ function makeGitDataWriter(args) {
20759
21793
  throw new Error(`fetchTip: HTTP ${String(r.status)}`);
20760
21794
  return r.json.object.sha;
20761
21795
  },
20762
- async readFile(path29) {
21796
+ async readFile(path32) {
20763
21797
  const token = await args.mintToken();
20764
- return readFileWith(token, path29);
21798
+ return readFileWith(token, path32);
20765
21799
  },
20766
21800
  async listMemory() {
20767
21801
  const token = await args.mintToken();
@@ -20770,9 +21804,9 @@ function makeGitDataWriter(args) {
20770
21804
  return [];
20771
21805
  const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
20772
21806
  const out = [];
20773
- for (const path29 of paths) {
20774
- const content = await readFileWith(token, path29);
20775
- out.push({ path: path29, frontmatter: content ? parseFrontmatter(content) : {} });
21807
+ for (const path32 of paths) {
21808
+ const content = await readFileWith(token, path32);
21809
+ out.push({ path: path32, frontmatter: content ? parseFrontmatter(content) : {} });
20776
21810
  }
20777
21811
  return out;
20778
21812
  },
@@ -21074,7 +22108,7 @@ function extractJson(text) {
21074
22108
  return JSON.parse(candidate2.slice(start, end + 1));
21075
22109
  }
21076
22110
  async function propose(model, system, user, opts = {}) {
21077
- const sleep2 = opts.sleep ?? defaultSleep2;
22111
+ const sleep3 = opts.sleep ?? defaultSleep2;
21078
22112
  const modelName = opts.model ?? MODEL_NAME_FALLBACK;
21079
22113
  let inputTokens = 0;
21080
22114
  let outputTokens = 0;
@@ -21090,7 +22124,7 @@ async function propose(model, system, user, opts = {}) {
21090
22124
  } catch (e) {
21091
22125
  lastErr = `parse: ${e.message}`;
21092
22126
  if (attempt < MAX_MODEL_ATTEMPTS - 1) {
21093
- await sleep2(attempt);
22127
+ await sleep3(attempt);
21094
22128
  continue;
21095
22129
  }
21096
22130
  break;
@@ -21099,7 +22133,7 @@ async function propose(model, system, user, opts = {}) {
21099
22133
  if (!Array.isArray(rawDeltas)) {
21100
22134
  lastErr = "schema: top-level { deltas: [] } missing";
21101
22135
  if (attempt < MAX_MODEL_ATTEMPTS - 1) {
21102
- await sleep2(attempt);
22136
+ await sleep3(attempt);
21103
22137
  continue;
21104
22138
  }
21105
22139
  break;
@@ -21209,7 +22243,7 @@ async function loadMemoryContext(brain) {
21209
22243
  return {
21210
22244
  files,
21211
22245
  indexEntries,
21212
- originOf: (path29) => originByPath.get(path29) ?? "worker"
22246
+ originOf: (path32) => originByPath.get(path32) ?? "worker"
21213
22247
  };
21214
22248
  }
21215
22249
 
@@ -21230,14 +22264,14 @@ function checkEvidenceMembership(delta, harvested) {
21230
22264
  }
21231
22265
  var BatchAbort = class extends Error {
21232
22266
  path;
21233
- constructor(path29, message) {
22267
+ constructor(path32, message) {
21234
22268
  super(message);
21235
- this.path = path29;
22269
+ this.path = path32;
21236
22270
  this.name = "BatchAbort";
21237
22271
  }
21238
22272
  };
21239
- var isIndexFile = (path29) => /(^|\/)[^/]*_index\.md$/.test(path29);
21240
- var isMemoryIndex = (path29) => path29 === "memory/MEMORY.md";
22273
+ var isIndexFile = (path32) => /(^|\/)[^/]*_index\.md$/.test(path32);
22274
+ var isMemoryIndex = (path32) => path32 === "memory/MEMORY.md";
21241
22275
  function targetPath(delta) {
21242
22276
  switch (delta.verb) {
21243
22277
  case "new":
@@ -21254,29 +22288,29 @@ function targetPath(delta) {
21254
22288
  }
21255
22289
  async function checkOriginTier(delta, lookup) {
21256
22290
  const evidence = evidenceOf(delta) ?? [];
21257
- const path29 = targetPath(delta);
21258
- if (path29 === null)
22291
+ const path32 = targetPath(delta);
22292
+ if (path32 === null)
21259
22293
  return { ok: true };
21260
- if (isIndexFile(path29) && !isMemoryIndex(path29)) {
21261
- return { ok: false, flagged: { kind: "flagged", path: path29, tension: `refused: ${path29} is an index file (only memory/MEMORY.md is editable)`, evidence } };
22294
+ if (isIndexFile(path32) && !isMemoryIndex(path32)) {
22295
+ return { ok: false, flagged: { kind: "flagged", path: path32, tension: `refused: ${path32} is an index file (only memory/MEMORY.md is editable)`, evidence } };
21262
22296
  }
21263
- const target = await lookup(path29);
21264
- const isStructural = path29.startsWith("references/");
22297
+ const target = await lookup(path32);
22298
+ const isStructural = path32.startsWith("references/");
21265
22299
  const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
21266
22300
  const editable = origin === "worker" || origin === "dream";
21267
22301
  if (editable)
21268
22302
  return { ok: true };
21269
22303
  const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
21270
22304
  if (removesOperatorTarget) {
21271
- throw new BatchAbort(path29, `supersede/retract OLD target ${path29} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
22305
+ throw new BatchAbort(path32, `supersede/retract OLD target ${path32} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
21272
22306
  }
21273
- return { ok: false, flagged: { kind: "flagged", path: path29, tension: `operator-origin (${origin}); flag-only`, evidence } };
22307
+ return { ok: false, flagged: { kind: "flagged", path: path32, tension: `operator-origin (${origin}); flag-only`, evidence } };
21274
22308
  }
21275
22309
  var ALLOWED_MEMORY = /^memory\/.+\.md$/;
21276
22310
  var ALLOWED_INBOX = /^inbox\//;
21277
22311
  var ALLOWED_QUARANTINE = /^quarantine\//;
21278
22312
  var ALLOWED_FLAG = /^memory\//;
21279
- var hasTraversal = (path29) => path29.split("/").includes("..");
22313
+ var hasTraversal = (path32) => path32.split("/").includes("..");
21280
22314
  function checkPathAllowed(delta) {
21281
22315
  const evidence = evidenceOf(delta) ?? [];
21282
22316
  const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
@@ -21383,7 +22417,7 @@ ${inject}
21383
22417
  ---
21384
22418
  `);
21385
22419
  }
21386
- var indexLine = (path29, title, date, tags) => `- \`${path29}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
22420
+ var indexLine = (path32, title, date, tags) => `- \`${path32}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
21387
22421
  function splitIndex(index) {
21388
22422
  const all = index.split("\n");
21389
22423
  const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
@@ -21556,16 +22590,16 @@ function defaultDreamSeams(opts = {}) {
21556
22590
  const mem = await memory(deps.brain);
21557
22591
  const harvested = harvestedIds(input);
21558
22592
  const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
21559
- const lookup = (path29) => Promise.resolve(mem.files.find((f) => f.path === path29) ?? null);
22593
+ const lookup = (path32) => Promise.resolve(mem.files.find((f) => f.path === path32) ?? null);
21560
22594
  const digest = [...pendingRejected];
21561
22595
  let changes = [];
21562
22596
  let indexEdit;
21563
22597
  try {
21564
22598
  for (const raw of deltas) {
21565
22599
  const delta = forcePolicyQuarantine(raw);
21566
- const path29 = checkPathAllowed(delta);
21567
- if (!path29.ok) {
21568
- digest.push(path29.rejected);
22600
+ const path32 = checkPathAllowed(delta);
22601
+ if (!path32.ok) {
22602
+ digest.push(path32.rejected);
21569
22603
  continue;
21570
22604
  }
21571
22605
  const ev = checkEvidenceMembership(delta, harvested);
@@ -21760,7 +22794,7 @@ async function emitDigest(input, deps, digest) {
21760
22794
  }
21761
22795
 
21762
22796
  // ../../packages/brain/engine/dist/esm/dream/config.js
21763
- import { parse as parseYaml11 } from "yaml";
22797
+ import { parse as parseYaml12 } from "yaml";
21764
22798
  var DEFAULT_DREAM_MODEL = "gpt-5.4";
21765
22799
  function asCadence(v) {
21766
22800
  return v === "nightly" ? "nightly" : "off";
@@ -21768,7 +22802,7 @@ function asCadence(v) {
21768
22802
  function parseDreamerConfig(rawYaml, env) {
21769
22803
  let parsed;
21770
22804
  try {
21771
- parsed = parseYaml11(rawYaml);
22805
+ parsed = parseYaml12(rawYaml);
21772
22806
  } catch {
21773
22807
  return { enabled: false, cadence: "off", model: env.M8T_DREAM_MODEL ?? DEFAULT_DREAM_MODEL };
21774
22808
  }
@@ -21782,31 +22816,31 @@ function parseDreamerConfig(rawYaml, env) {
21782
22816
  }
21783
22817
 
21784
22818
  // ../../packages/brain/engine/dist/esm/cost-report/config.js
21785
- import { parse as parseYaml12 } from "yaml";
22819
+ import { parse as parseYaml13 } from "yaml";
21786
22820
 
21787
22821
  // ../../packages/brain/engine/dist/esm/librarian/codify/persistence-guard.js
21788
22822
  var ZERO_WIDTH_RE = new RegExp("\u200B|\u200C|\u200D|\uFEFF", "g");
21789
22823
 
21790
22824
  // ../../packages/brain/engine/dist/esm/librarian/janitor/frontmatter.js
21791
- import { parse as parseYaml13, stringify as stringifyYaml5 } from "yaml";
22825
+ import { parse as parseYaml14, stringify as stringifyYaml5 } from "yaml";
21792
22826
 
21793
22827
  // ../../packages/brain/engine/dist/esm/librarian/config.js
21794
- import { parse as parseYaml14 } from "yaml";
22828
+ import { parse as parseYaml15 } from "yaml";
21795
22829
 
21796
22830
  // src/lib/dream-discovery.ts
21797
22831
  init_http();
21798
22832
  init_errors();
21799
- var ARM3 = "https://management.azure.com";
21800
- var ARM_SCOPE7 = "https://management.azure.com/.default";
21801
- var STORAGE_API = "2023-05-01";
22833
+ var ARM4 = "https://management.azure.com";
22834
+ var ARM_SCOPE8 = "https://management.azure.com/.default";
22835
+ var STORAGE_API2 = "2023-05-01";
21802
22836
  var LA_API = "2023-09-01";
21803
22837
  async function discoverLedgerResources(opts) {
21804
22838
  const { credential: credential2, subscriptionId, resourceGroup } = opts;
21805
22839
  const storage = await authedJson({
21806
22840
  credential: credential2,
21807
- scope: ARM_SCOPE7,
22841
+ scope: ARM_SCOPE8,
21808
22842
  method: "GET",
21809
- url: `${ARM3}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Storage/storageAccounts?api-version=${STORAGE_API}`
22843
+ url: `${ARM4}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Storage/storageAccounts?api-version=${STORAGE_API2}`
21810
22844
  }) ?? {};
21811
22845
  const accounts = (storage.value ?? []).filter((a) => a.properties?.primaryEndpoints?.table);
21812
22846
  const ledgerAcct = accounts.find((a) => (a.tags?.["m8t-stack:role"] ?? a.properties?.tags?.["m8t-stack:role"]) === "ledger") ?? (accounts.length === 1 ? accounts[0] : void 0);
@@ -21820,9 +22854,9 @@ async function discoverLedgerResources(opts) {
21820
22854
  }
21821
22855
  const laList = await authedJson({
21822
22856
  credential: credential2,
21823
- scope: ARM_SCOPE7,
22857
+ scope: ARM_SCOPE8,
21824
22858
  method: "GET",
21825
- url: `${ARM3}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.OperationalInsights/workspaces?api-version=${LA_API}`
22859
+ url: `${ARM4}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.OperationalInsights/workspaces?api-version=${LA_API}`
21826
22860
  }) ?? {};
21827
22861
  const ws = laList.value ?? [];
21828
22862
  const workspaceId = (ws.length === 1 ? ws[0] : ws.find((w) => w.properties?.customerId))?.properties?.customerId;
@@ -22116,7 +23150,7 @@ AppDependencies
22116
23150
  };
22117
23151
  }
22118
23152
  function buildLiveSources(context, credential2) {
22119
- const ledgerClient = new TableClient2(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
23153
+ const ledgerClient = new TableClient3(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
22120
23154
  const ledger = makeLedgerSource(
22121
23155
  async (pk, sinceTs) => fetchLedgerRows(
22122
23156
  { workers: [pk], source: "all", from: sinceTs, to: "9999-12-31T23:59:59.999Z" },
@@ -22785,9 +23819,9 @@ ${colors.error(" " + why)}
22785
23819
  }
22786
23820
 
22787
23821
  // src/commands/bootstrap/launch.ts
22788
- import * as fs23 from "fs";
22789
- import * as os12 from "os";
22790
- import * as path24 from "path";
23822
+ import * as fs26 from "fs";
23823
+ import * as os13 from "os";
23824
+ import * as path27 from "path";
22791
23825
  import { Command as Command47, Option as Option45 } from "clipanion";
22792
23826
  init_errors();
22793
23827
 
@@ -22933,9 +23967,9 @@ async function kickInstaller(s) {
22933
23967
 
22934
23968
  // src/lib/bootstrap-status.ts
22935
23969
  import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
22936
- import * as fs21 from "fs/promises";
22937
- import * as os10 from "os";
22938
- import * as path22 from "path";
23970
+ import * as fs24 from "fs/promises";
23971
+ import * as os11 from "os";
23972
+ import * as path25 from "path";
22939
23973
  init_errors();
22940
23974
  var STATUS_CONTAINER = "status";
22941
23975
  var STATUS_BLOB = "status.json";
@@ -22974,7 +24008,7 @@ async function readStatusBlob(opts) {
22974
24008
  cause: e
22975
24009
  });
22976
24010
  }
22977
- const tmpFile = path22.join(os10.tmpdir(), `m8t-status-${randomUUID2()}.json`);
24011
+ const tmpFile = path25.join(os11.tmpdir(), `m8t-status-${randomUUID2()}.json`);
22978
24012
  try {
22979
24013
  await runAz([
22980
24014
  "storage",
@@ -22993,7 +24027,7 @@ async function readStatusBlob(opts) {
22993
24027
  "--no-progress",
22994
24028
  "--only-show-errors"
22995
24029
  ]);
22996
- const raw = await fs21.readFile(tmpFile, "utf8");
24030
+ const raw = await fs24.readFile(tmpFile, "utf8");
22997
24031
  return JSON.parse(raw.trim());
22998
24032
  } catch (e) {
22999
24033
  throw new LocalCliError({
@@ -23003,34 +24037,34 @@ async function readStatusBlob(opts) {
23003
24037
  cause: e
23004
24038
  });
23005
24039
  } finally {
23006
- await fs21.rm(tmpFile, { force: true });
24040
+ await fs24.rm(tmpFile, { force: true });
23007
24041
  }
23008
24042
  }
23009
24043
 
23010
24044
  // src/lib/bootstrap-state.ts
23011
- import * as fs22 from "fs/promises";
23012
- import * as os11 from "os";
23013
- import * as path23 from "path";
24045
+ import * as fs25 from "fs/promises";
24046
+ import * as os12 from "os";
24047
+ import * as path26 from "path";
23014
24048
  function stateDir(home) {
23015
- return path23.join(home, ".m8t-stack");
24049
+ return path26.join(home, ".m8t-stack");
23016
24050
  }
23017
24051
  function statePath(home) {
23018
- return path23.join(stateDir(home), "bootstrap.json");
24052
+ return path26.join(stateDir(home), "bootstrap.json");
23019
24053
  }
23020
- async function readBootstrapState(home = os11.homedir()) {
24054
+ async function readBootstrapState(home = os12.homedir()) {
23021
24055
  try {
23022
- const raw = await fs22.readFile(statePath(home), "utf8");
24056
+ const raw = await fs25.readFile(statePath(home), "utf8");
23023
24057
  return JSON.parse(raw);
23024
24058
  } catch (e) {
23025
24059
  if (e.code === "ENOENT") return null;
23026
24060
  throw e;
23027
24061
  }
23028
24062
  }
23029
- async function writeBootstrapState(state, home = os11.homedir()) {
23030
- await fs22.mkdir(stateDir(home), { recursive: true });
24063
+ async function writeBootstrapState(state, home = os12.homedir()) {
24064
+ await fs25.mkdir(stateDir(home), { recursive: true });
23031
24065
  const tmp = `${statePath(home)}.tmp`;
23032
- await fs22.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
23033
- await fs22.rename(tmp, statePath(home));
24066
+ await fs25.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
24067
+ await fs25.rename(tmp, statePath(home));
23034
24068
  }
23035
24069
 
23036
24070
  // src/commands/bootstrap/launch.ts
@@ -23070,12 +24104,12 @@ var BootstrapLaunchCommand = class extends M8tCommand {
23070
24104
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
23071
24105
  const out = (m) => this.context.stderr.write(` ${colors.dim(m)}
23072
24106
  `);
23073
- const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path24.join(os12.homedir(), ".m8t-stack", "github-app.json");
24107
+ const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path27.join(os13.homedir(), ".m8t-stack", "github-app.json");
23074
24108
  let githubApp;
23075
- if (fs23.existsSync(credsPath)) {
24109
+ if (fs26.existsSync(credsPath)) {
23076
24110
  let parsed;
23077
24111
  try {
23078
- parsed = JSON.parse(fs23.readFileSync(credsPath, "utf8"));
24112
+ parsed = JSON.parse(fs26.readFileSync(credsPath, "utf8"));
23079
24113
  } catch {
23080
24114
  throw new LocalCliError({
23081
24115
  code: "GITHUB_APP_CREDS_INVALID",
@@ -23085,7 +24119,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
23085
24119
  }
23086
24120
  let pem;
23087
24121
  try {
23088
- pem = fs23.readFileSync(parsed.pemPath, "utf8");
24122
+ pem = fs26.readFileSync(parsed.pemPath, "utf8");
23089
24123
  } catch {
23090
24124
  throw new LocalCliError({
23091
24125
  code: "GITHUB_APP_PEM_MISSING",
@@ -23205,7 +24239,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
23205
24239
  typeof this.output === "string" ? this.output : void 0,
23206
24240
  this.context.stdout
23207
24241
  );
23208
- const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
24242
+ const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
23209
24243
  const watch = this.watch === true;
23210
24244
  for (; ; ) {
23211
24245
  let doc;
@@ -23215,7 +24249,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
23215
24249
  if (watch && e instanceof LocalCliError && e.code === "BOOTSTRAP_STATUS_UNREADABLE") {
23216
24250
  if (mode === "pretty") this.context.stderr.write(` ${colors.dim("waiting for the installer to start\u2026")}
23217
24251
  `);
23218
- await sleep2(1e4);
24252
+ await sleep3(1e4);
23219
24253
  continue;
23220
24254
  }
23221
24255
  throw e;
@@ -23240,7 +24274,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
23240
24274
  );
23241
24275
  return 1;
23242
24276
  }
23243
- await sleep2(1e4);
24277
+ await sleep3(1e4);
23244
24278
  }
23245
24279
  }
23246
24280
  };
@@ -23410,17 +24444,17 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
23410
24444
  };
23411
24445
 
23412
24446
  // src/commands/bootstrap/finish.ts
23413
- import * as fs24 from "fs/promises";
23414
- import * as os14 from "os";
23415
- import * as path26 from "path";
24447
+ import * as fs27 from "fs/promises";
24448
+ import * as os15 from "os";
24449
+ import * as path29 from "path";
23416
24450
  import { Command as Command50, Option as Option48 } from "clipanion";
23417
24451
  init_errors();
23418
24452
 
23419
24453
  // src/lib/company-profile-seed.ts
23420
24454
  import { spawn as spawn6 } from "child_process";
23421
- import { closeSync, openSync, readFileSync as readFileSync16 } from "fs";
23422
- import * as os13 from "os";
23423
- import * as path25 from "path";
24455
+ import { closeSync, openSync, readFileSync as readFileSync18 } from "fs";
24456
+ import * as os14 from "os";
24457
+ import * as path28 from "path";
23424
24458
  init_errors();
23425
24459
 
23426
24460
  // src/lib/onboarding-profile.ts
@@ -23601,9 +24635,9 @@ function composeFounderIdentityNote(id) {
23601
24635
 
23602
24636
  // src/lib/company-profile-seed.ts
23603
24637
  function readGithubAppCreds(credsPath) {
23604
- const p = credsPath ?? path25.join(os13.homedir(), ".m8t-stack", "github-app.json");
24638
+ const p = credsPath ?? path28.join(os14.homedir(), ".m8t-stack", "github-app.json");
23605
24639
  try {
23606
- return JSON.parse(readFileSync16(p, "utf8"));
24640
+ return JSON.parse(readFileSync18(p, "utf8"));
23607
24641
  } catch {
23608
24642
  return null;
23609
24643
  }
@@ -23631,7 +24665,7 @@ async function resolveSeedContext(opts) {
23631
24665
  async function applyProfileToBrain(args) {
23632
24666
  const token = await mintInstallationTokenFromPem({
23633
24667
  appId: args.appCreds.appId,
23634
- privateKeyPem: readFileSync16(args.appCreds.pemPath, "utf8"),
24668
+ privateKeyPem: readFileSync18(args.appCreds.pemPath, "utf8"),
23635
24669
  installationId: args.appCreds.installationId,
23636
24670
  fetchImpl: args.fetchImpl
23637
24671
  });
@@ -23666,7 +24700,7 @@ async function applyProfileToBrain(args) {
23666
24700
  });
23667
24701
  }
23668
24702
  function spawnDetachedSeedWatch() {
23669
- const logPath = path25.join(os13.homedir(), ".m8t-stack", "seed-profile.log");
24703
+ const logPath = path28.join(os14.homedir(), ".m8t-stack", "seed-profile.log");
23670
24704
  const fd = openSync(logPath, "a");
23671
24705
  try {
23672
24706
  const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
@@ -23765,9 +24799,9 @@ var BootstrapFinishCommand = class extends M8tCommand {
23765
24799
  });
23766
24800
  }
23767
24801
  const repoRoot = (typeof this.repoRoot === "string" ? this.repoRoot : void 0) ?? process.cwd();
23768
- const markerDir = path26.join(os14.homedir(), ".m8t-stack");
23769
- await fs24.mkdir(markerDir, { recursive: true });
23770
- await fs24.writeFile(path26.join(markerDir, "repo-root"), `${repoRoot}
24802
+ const markerDir = path29.join(os15.homedir(), ".m8t-stack");
24803
+ await fs27.mkdir(markerDir, { recursive: true });
24804
+ await fs27.writeFile(path29.join(markerDir, "repo-root"), `${repoRoot}
23771
24805
  `, "utf8");
23772
24806
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? state.subscriptionId;
23773
24807
  const resourceGroup = (typeof this.resourceGroup === "string" ? this.resourceGroup : void 0) ?? state.resourceGroup;
@@ -23803,7 +24837,7 @@ var BootstrapFinishCommand = class extends M8tCommand {
23803
24837
  }
23804
24838
  let brainOrg = null;
23805
24839
  try {
23806
- const credsRaw = await fs24.readFile(path26.join(markerDir, "github-app.json"), "utf8");
24840
+ const credsRaw = await fs27.readFile(path29.join(markerDir, "github-app.json"), "utf8");
23807
24841
  const creds = JSON.parse(credsRaw);
23808
24842
  brainOrg = typeof creds.org === "string" ? creds.org : null;
23809
24843
  } catch {
@@ -23834,18 +24868,18 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
23834
24868
  };
23835
24869
 
23836
24870
  // src/commands/bootstrap/ui.ts
23837
- import * as fs26 from "fs";
23838
- import * as os16 from "os";
23839
- import * as path28 from "path";
24871
+ import * as fs29 from "fs";
24872
+ import * as os17 from "os";
24873
+ import * as path31 from "path";
23840
24874
  import { Command as Command51, Option as Option49 } from "clipanion";
23841
- import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/identity";
24875
+ import { DefaultAzureCredential as DefaultAzureCredential19 } from "@azure/identity";
23842
24876
  init_errors();
23843
24877
 
23844
24878
  // src/lib/bootstrap-ui.ts
23845
- import * as fs25 from "fs";
24879
+ import * as fs28 from "fs";
23846
24880
  import * as net from "net";
23847
- import * as os15 from "os";
23848
- import * as path27 from "path";
24881
+ import * as os16 from "os";
24882
+ import * as path30 from "path";
23849
24883
  import { spawn as spawn7 } from "child_process";
23850
24884
  init_errors();
23851
24885
  init_rbac();
@@ -23853,7 +24887,7 @@ async function resolveFoundryEndpointWithWait(args, opts = {}) {
23853
24887
  const pollMs = opts.pollMs ?? 1e4;
23854
24888
  const timeoutMs = opts.timeoutMs ?? 15 * 6e4;
23855
24889
  const deadline = Date.now() + timeoutMs;
23856
- const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
24890
+ const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
23857
24891
  for (; ; ) {
23858
24892
  try {
23859
24893
  const p = await resolveFoundryProject({
@@ -23887,7 +24921,7 @@ async function resolveFoundryEndpointWithWait(args, opts = {}) {
23887
24921
  });
23888
24922
  }
23889
24923
  opts.onWait?.("waiting for Foundry-create to finish\u2026");
23890
- await sleep2(pollMs);
24924
+ await sleep3(pollMs);
23891
24925
  }
23892
24926
  }
23893
24927
  }
@@ -23912,9 +24946,9 @@ async function ensureFounderFoundryRole(args) {
23912
24946
  });
23913
24947
  }
23914
24948
  function writeWebEnvLocal(args) {
23915
- const envPath = path27.join(args.repoRoot, "apps", "web", ".env.local");
23916
- if (fs25.existsSync(envPath)) {
23917
- fs25.copyFileSync(envPath, envPath + ".bak");
24949
+ const envPath = path30.join(args.repoRoot, "apps", "web", ".env.local");
24950
+ if (fs28.existsSync(envPath)) {
24951
+ fs28.copyFileSync(envPath, envPath + ".bak");
23918
24952
  }
23919
24953
  const body = [
23920
24954
  "# Written by `m8t bootstrap ui` \u2014 onboarding chat-only run (Simple Stacey).",
@@ -23928,7 +24962,7 @@ function writeWebEnvLocal(args) {
23928
24962
  "NEXT_PUBLIC_M8T_ONBOARDING=1",
23929
24963
  ""
23930
24964
  ].join("\n");
23931
- fs25.writeFileSync(envPath, body, "utf8");
24965
+ fs28.writeFileSync(envPath, body, "utf8");
23932
24966
  return envPath;
23933
24967
  }
23934
24968
  function assertNodeVersion(versionString = process.version) {
@@ -23969,11 +25003,11 @@ async function installWebDeps(repoRoot) {
23969
25003
  });
23970
25004
  await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
23971
25005
  }
23972
- function onboardingUiPaths(home = os15.homedir()) {
23973
- const dir = path27.join(home, ".m8t-stack");
25006
+ function onboardingUiPaths(home = os16.homedir()) {
25007
+ const dir = path30.join(home, ".m8t-stack");
23974
25008
  return {
23975
- logPath: path27.join(dir, "onboarding-ui.log"),
23976
- pidPath: path27.join(dir, "onboarding-ui.pid")
25009
+ logPath: path30.join(dir, "onboarding-ui.log"),
25010
+ pidPath: path30.join(dir, "onboarding-ui.pid")
23977
25011
  };
23978
25012
  }
23979
25013
  async function serveOnboardingUiDetached(args) {
@@ -23997,8 +25031,8 @@ async function serveOnboardingUiDetached(args) {
23997
25031
  if (await isPortOpen()) {
23998
25032
  return { alreadyRunning: true, logPath };
23999
25033
  }
24000
- fs25.mkdirSync(path27.dirname(logPath), { recursive: true });
24001
- const fd = fs25.openSync(logPath, "a");
25034
+ fs28.mkdirSync(path30.dirname(logPath), { recursive: true });
25035
+ const fd = fs28.openSync(logPath, "a");
24002
25036
  const child = spawn7("pnpm", ["--filter", "web", "dev"], {
24003
25037
  cwd: args.repoRoot,
24004
25038
  detached: true,
@@ -24006,7 +25040,7 @@ async function serveOnboardingUiDetached(args) {
24006
25040
  env: { ...process.env, PORT: args.port },
24007
25041
  shell: false
24008
25042
  });
24009
- fs25.closeSync(fd);
25043
+ fs28.closeSync(fd);
24010
25044
  if (child.pid == null) {
24011
25045
  throw new LocalCliError({
24012
25046
  code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
@@ -24015,11 +25049,11 @@ async function serveOnboardingUiDetached(args) {
24015
25049
  });
24016
25050
  }
24017
25051
  child.unref();
24018
- fs25.writeFileSync(pidPath, String(child.pid), "utf8");
25052
+ fs28.writeFileSync(pidPath, String(child.pid), "utf8");
24019
25053
  const deadline = Date.now() + 45e3;
24020
- const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
25054
+ const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
24021
25055
  while (Date.now() < deadline) {
24022
- await sleep2(500);
25056
+ await sleep3(500);
24023
25057
  if (await isPortOpen()) break;
24024
25058
  }
24025
25059
  return { alreadyRunning: false, logPath };
@@ -24034,11 +25068,11 @@ function autoOpenOnboardingUi(url, open = tryOpenUrl) {
24034
25068
  } catch {
24035
25069
  }
24036
25070
  }
24037
- function stopOnboardingUi(home = os15.homedir()) {
25071
+ function stopOnboardingUi(home = os16.homedir()) {
24038
25072
  const { pidPath } = onboardingUiPaths(home);
24039
25073
  let pidStr;
24040
25074
  try {
24041
- pidStr = fs25.readFileSync(pidPath, "utf8").trim();
25075
+ pidStr = fs28.readFileSync(pidPath, "utf8").trim();
24042
25076
  } catch {
24043
25077
  return false;
24044
25078
  }
@@ -24051,7 +25085,7 @@ function stopOnboardingUi(home = os15.homedir()) {
24051
25085
  }
24052
25086
  }
24053
25087
  try {
24054
- fs25.unlinkSync(pidPath);
25088
+ fs28.unlinkSync(pidPath);
24055
25089
  } catch {
24056
25090
  }
24057
25091
  return true;
@@ -24167,10 +25201,10 @@ var BootstrapUiCommand = class extends M8tCommand {
24167
25201
  this.context.stderr.write(` ${colors.dim(m)}
24168
25202
  `);
24169
25203
  };
24170
- if (fs26.existsSync(path28.join(os16.homedir(), ".m8t-stack", "config.yaml"))) {
25204
+ if (fs29.existsSync(path31.join(os17.homedir(), ".m8t-stack", "config.yaml"))) {
24171
25205
  out(colors.error("\u26A0 an existing ~/.m8t-stack/config.yaml will override the onboarding app registration in apps/web \u2014 if Microsoft sign-in fails, move it aside and retry."));
24172
25206
  }
24173
- const credential2 = new DefaultAzureCredential17();
25207
+ const credential2 = new DefaultAzureCredential19();
24174
25208
  const account = await getAzAccount();
24175
25209
  assertNodeVersion();
24176
25210
  out("waiting for Foundry (the installer's foundry-create phase)\u2026");
@@ -24273,7 +25307,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
24273
25307
  const parsedTimeout = Number(rawTimeout);
24274
25308
  const timeoutMin = rawTimeout !== "" && Number.isFinite(parsedTimeout) ? parsedTimeout : 20;
24275
25309
  const deadline = Date.now() + timeoutMin * 6e4;
24276
- const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
25310
+ const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
24277
25311
  for (; ; ) {
24278
25312
  const token = await getFoundryToken();
24279
25313
  const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
@@ -24296,7 +25330,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
24296
25330
  }
24297
25331
  this.context.stderr.write(` ${colors.dim("waiting for the questionnaire to complete\u2026")}
24298
25332
  `);
24299
- await sleep2(2e4);
25333
+ await sleep3(2e4);
24300
25334
  }
24301
25335
  }
24302
25336
  };