@m8t-stack/cli 0.2.33 → 0.2.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1247,7 +1247,7 @@ var init_enable_hosted_brain = __esm({
1247
1247
  import { Builtins, Cli } from "clipanion";
1248
1248
 
1249
1249
  // src/lib/package-version.ts
1250
- var CLI_VERSION = "0.2.33";
1250
+ var CLI_VERSION = "0.2.35";
1251
1251
 
1252
1252
  // src/lib/render-error.ts
1253
1253
  init_errors();
@@ -1444,6 +1444,7 @@ var BACKEND_RULES = [
1444
1444
  var LOCAL_RULES = {
1445
1445
  AGENT_CREATE_VERSION_FAILED: "Re-run with `--verbose` for the full response body.",
1446
1446
  AGENT_CREATE_VERSION_NO_VERSION: "Foundry returned 2xx but no version \u2014 likely an API regression. Re-try; if it persists, file an issue.",
1447
+ AGENT_ENDPOINT_AUTH_PATCH_FAILED: "Re-run with `--verbose` for the full response body.",
1447
1448
  AGENT_GET_FAILED: "Re-run with `--verbose` for the full response body.",
1448
1449
  AGENT_NOT_FOUND: "Use `m8t brain create <name>` to create the worker first, OR run `m8t deploy <name>` to register an existing agent.",
1449
1450
  AGENT_REDEPLOY_FAILED: "Re-run with `--verbose` for the full response body.",
@@ -3327,12 +3328,12 @@ function encodeURIPath(str4) {
3327
3328
  return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
3328
3329
  }
3329
3330
  var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
3330
- var createPathTagFunction = (pathEncoder = encodeURIPath) => function path37(statics, ...params) {
3331
+ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path38(statics, ...params) {
3331
3332
  if (statics.length === 1)
3332
3333
  return statics[0];
3333
3334
  let postPath = false;
3334
3335
  const invalidSegments = [];
3335
- const path38 = statics.reduce((previousValue, currentValue, index) => {
3336
+ const path39 = statics.reduce((previousValue, currentValue, index) => {
3336
3337
  if (/[?#]/.test(currentValue)) {
3337
3338
  postPath = true;
3338
3339
  }
@@ -3349,7 +3350,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path37(sta
3349
3350
  }
3350
3351
  return previousValue + currentValue + (index === params.length ? "" : encoded);
3351
3352
  }, "");
3352
- const pathOnly = path38.split(/[?#]/, 1)[0];
3353
+ const pathOnly = path39.split(/[?#]/, 1)[0];
3353
3354
  const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
3354
3355
  let match;
3355
3356
  while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
@@ -3370,10 +3371,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path37(sta
3370
3371
  }, "");
3371
3372
  throw new OpenAIError(`Path parameters result in path with invalid segments:
3372
3373
  ${invalidSegments.map((e) => e.error).join("\n")}
3373
- ${path38}
3374
+ ${path39}
3374
3375
  ${underline}`);
3375
3376
  }
3376
- return path38;
3377
+ return path39;
3377
3378
  };
3378
3379
  var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
3379
3380
 
@@ -10890,9 +10891,9 @@ var OpenAI = class {
10890
10891
  this.apiKey = token;
10891
10892
  return true;
10892
10893
  }
10893
- buildURL(path37, query, defaultBaseURL) {
10894
+ buildURL(path38, query, defaultBaseURL) {
10894
10895
  const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
10895
- const url = isAbsoluteURL(path37) ? new URL(path37) : new URL(baseURL + (baseURL.endsWith("/") && path37.startsWith("/") ? path37.slice(1) : path37));
10896
+ const url = isAbsoluteURL(path38) ? new URL(path38) : new URL(baseURL + (baseURL.endsWith("/") && path38.startsWith("/") ? path38.slice(1) : path38));
10896
10897
  const defaultQuery = this.defaultQuery();
10897
10898
  const pathQuery = Object.fromEntries(url.searchParams);
10898
10899
  if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
@@ -10920,24 +10921,24 @@ var OpenAI = class {
10920
10921
  */
10921
10922
  async prepareRequest(request, { url, options }) {
10922
10923
  }
10923
- get(path37, opts) {
10924
- return this.methodRequest("get", path37, opts);
10924
+ get(path38, opts) {
10925
+ return this.methodRequest("get", path38, opts);
10925
10926
  }
10926
- post(path37, opts) {
10927
- return this.methodRequest("post", path37, opts);
10927
+ post(path38, opts) {
10928
+ return this.methodRequest("post", path38, opts);
10928
10929
  }
10929
- patch(path37, opts) {
10930
- return this.methodRequest("patch", path37, opts);
10930
+ patch(path38, opts) {
10931
+ return this.methodRequest("patch", path38, opts);
10931
10932
  }
10932
- put(path37, opts) {
10933
- return this.methodRequest("put", path37, opts);
10933
+ put(path38, opts) {
10934
+ return this.methodRequest("put", path38, opts);
10934
10935
  }
10935
- delete(path37, opts) {
10936
- return this.methodRequest("delete", path37, opts);
10936
+ delete(path38, opts) {
10937
+ return this.methodRequest("delete", path38, opts);
10937
10938
  }
10938
- methodRequest(method, path37, opts) {
10939
+ methodRequest(method, path38, opts) {
10939
10940
  return this.request(Promise.resolve(opts).then((opts2) => {
10940
- return { method, path: path37, ...opts2 };
10941
+ return { method, path: path38, ...opts2 };
10941
10942
  }));
10942
10943
  }
10943
10944
  request(options, remainingRetries = null) {
@@ -11059,8 +11060,8 @@ var OpenAI = class {
11059
11060
  }));
11060
11061
  return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
11061
11062
  }
11062
- getAPIList(path37, Page2, opts) {
11063
- return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path37, ...opts2 })) : { method: "get", path: path37, ...opts });
11063
+ getAPIList(path38, Page2, opts) {
11064
+ return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path38, ...opts2 })) : { method: "get", path: path38, ...opts });
11064
11065
  }
11065
11066
  requestAPIList(Page2, options) {
11066
11067
  const request = this.makeRequest(options, null, void 0);
@@ -11154,8 +11155,8 @@ var OpenAI = class {
11154
11155
  }
11155
11156
  async buildRequest(inputOptions, { retryCount = 0 } = {}) {
11156
11157
  const options = { ...inputOptions };
11157
- const { method, path: path37, query, defaultBaseURL } = options;
11158
- const url = this.buildURL(path37, query, defaultBaseURL);
11158
+ const { method, path: path38, query, defaultBaseURL } = options;
11159
+ const url = this.buildURL(path38, query, defaultBaseURL);
11159
11160
  if ("timeout" in options)
11160
11161
  validatePositiveInteger("timeout", options.timeout);
11161
11162
  options.timeout = options.timeout ?? this.timeout;
@@ -12182,10 +12183,10 @@ var BindListCommand = class extends M8tCommand {
12182
12183
  const qs = new URLSearchParams();
12183
12184
  if (this.channel) qs.set("channel", this.channel);
12184
12185
  if (this.status) qs.set("status", this.status);
12185
- const path37 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
12186
+ const path38 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
12186
12187
  const data = await apiCall(
12187
12188
  { gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
12188
- { method: "GET", path: path37 },
12189
+ { method: "GET", path: path38 },
12189
12190
  (msg) => this.context.stderr.write(msg + "\n")
12190
12191
  );
12191
12192
  if (mode === "json") {
@@ -13396,8 +13397,8 @@ ${r.stderr}`;
13396
13397
  message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
13397
13398
  });
13398
13399
  },
13399
- getFile: async (path37) => {
13400
- const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path37}`]);
13400
+ getFile: async (path38) => {
13401
+ const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path38}`]);
13401
13402
  return r.exitCode === 0 ? r.stdout : null;
13402
13403
  },
13403
13404
  isEmpty: async () => {
@@ -13917,8 +13918,27 @@ ${text.slice(0, 500)}` });
13917
13918
  }
13918
13919
  const data = await res.json();
13919
13920
  if (!data.version) throw new LocalCliError({ code: "AGENT_CREATE_VERSION_NO_VERSION", message: "createVersion returned no version" });
13921
+ await ensureAgentEndpointEntraIsolation(
13922
+ { credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, extraHeaders: args.extraHeaders },
13923
+ fetchImpl
13924
+ );
13920
13925
  return data.version;
13921
13926
  }
13927
+ async function ensureAgentEndpointEntraIsolation(args, fetchImpl = fetch) {
13928
+ const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
13929
+ if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
13930
+ const url = `${args.projectEndpoint}/agents/${args.agentName}?api-version=v1`;
13931
+ const res = await fetchImpl(url, {
13932
+ method: "PATCH",
13933
+ headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} },
13934
+ body: JSON.stringify({ agent_endpoint: { authorization_schemes: [{ type: "Entra", isolation_key_source: { kind: "Entra" } }] } })
13935
+ });
13936
+ if (!res.ok) {
13937
+ const text = await res.text();
13938
+ throw new LocalCliError({ code: "AGENT_ENDPOINT_AUTH_PATCH_FAILED", message: `PATCH ${url}: HTTP ${res.status.toString()}
13939
+ ${text.slice(0, 500)}` });
13940
+ }
13941
+ }
13922
13942
 
13923
13943
  // src/lib/brain-link.ts
13924
13944
  init_brain_yaml_mirror();
@@ -14487,7 +14507,7 @@ function appRepoProbe(args) {
14487
14507
  const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14488
14508
  return res.status;
14489
14509
  },
14490
- getFile: (path37) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path37, fetchImpl: args.fetchImpl }),
14510
+ getFile: (path38) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path38, fetchImpl: args.fetchImpl }),
14491
14511
  isEmpty: async () => {
14492
14512
  const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
14493
14513
  return res.status === 404;
@@ -15223,7 +15243,6 @@ import * as os5 from "os";
15223
15243
  import * as path11 from "path";
15224
15244
  init_foundry_agent_get();
15225
15245
  var FOUNDRY_ARM_API2 = "2025-04-01-preview";
15226
- var FOUNDRY_DATA_SCOPE2 = "https://ai.azure.com/.default";
15227
15246
  var ARM_SCOPE5 = "https://management.azure.com/.default";
15228
15247
  var GITHUB_APP_API = "https://api.github.com";
15229
15248
  async function unlinkBrain(args) {
@@ -15309,10 +15328,6 @@ function parseBrainMeta(raw) {
15309
15328
  }
15310
15329
  }
15311
15330
  async function createBrainStrippedVersion(args) {
15312
- const fndToken = await args.credential.getToken(FOUNDRY_DATA_SCOPE2);
15313
- if (!fndToken?.token) {
15314
- throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
15315
- }
15316
15331
  const otherTools = (args.currentDefinition.tools ?? []).filter(
15317
15332
  (t) => !(t.type === "mcp" && t.server_label === "brain")
15318
15333
  );
@@ -15325,28 +15340,13 @@ async function createBrainStrippedVersion(args) {
15325
15340
  ...args.currentMetadata,
15326
15341
  brain: ""
15327
15342
  };
15328
- const url = `${args.projectEndpoint}/agents/${args.agentName}/versions?api-version=v1`;
15329
- const res = await fetch(url, {
15330
- method: "POST",
15331
- headers: { Authorization: `Bearer ${fndToken.token}`, "Content-Type": "application/json" },
15332
- body: JSON.stringify({ definition, metadata })
15343
+ return createAgentVersion({
15344
+ credential: args.credential,
15345
+ projectEndpoint: args.projectEndpoint,
15346
+ agentName: args.agentName,
15347
+ definition,
15348
+ metadata
15333
15349
  });
15334
- if (!res.ok) {
15335
- const text = await res.text();
15336
- throw new LocalCliError({
15337
- code: "AGENT_CREATE_VERSION_FAILED",
15338
- message: `POST ${url}: HTTP ${res.status.toString()}
15339
- ${text.slice(0, 500)}`
15340
- });
15341
- }
15342
- const data = await res.json();
15343
- if (!data.version) {
15344
- throw new LocalCliError({
15345
- code: "AGENT_REDEPLOY_NO_VERSION",
15346
- message: "createVersion returned no version"
15347
- });
15348
- }
15349
- return data.version;
15350
15350
  }
15351
15351
  async function deleteConnection(args) {
15352
15352
  const armToken = await args.credential.getToken(ARM_SCOPE5);
@@ -15695,7 +15695,7 @@ async function awaitDataPlaneReady(opts) {
15695
15695
  const consecutive = opts.consecutive ?? 3;
15696
15696
  const attempts = opts.attempts ?? 60;
15697
15697
  const intervalMs = opts.intervalMs ?? 5e3;
15698
- const sleep4 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
15698
+ const sleep5 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
15699
15699
  let streak = 0;
15700
15700
  for (let i = 1; i <= attempts; i++) {
15701
15701
  const r = await opts.probe();
@@ -15717,7 +15717,7 @@ async function awaitDataPlaneReady(opts) {
15717
15717
  streak = 0;
15718
15718
  opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
15719
15719
  }
15720
- if (i < attempts) await sleep4(intervalMs);
15720
+ if (i < attempts) await sleep5(intervalMs);
15721
15721
  }
15722
15722
  return { ready: false, attempts };
15723
15723
  }
@@ -16801,7 +16801,7 @@ init_errors();
16801
16801
  async function deployHostedWorker(args) {
16802
16802
  const onProgress = args.onProgress ?? ((_m) => {
16803
16803
  });
16804
- const sleep4 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16804
+ const sleep5 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
16805
16805
  const now = args.now ?? (() => Date.now());
16806
16806
  const timeout = args.pollTimeoutMs ?? 8 * 60 * 1e3;
16807
16807
  const interval = args.pollIntervalMs ?? 1e4;
@@ -16853,7 +16853,7 @@ async function deployHostedWorker(args) {
16853
16853
  hint: "Provisioning can take 2\u20135 min; re-run with the same name to resume, or check the version status in the Foundry portal."
16854
16854
  });
16855
16855
  }
16856
- await sleep4(interval);
16856
+ await sleep5(interval);
16857
16857
  }
16858
16858
  }
16859
16859
 
@@ -17751,8 +17751,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17751
17751
  hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
17752
17752
  });
17753
17753
  }
17754
- const path37 = repo.replace(/^ghcr\.io\//, "");
17755
- const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path37}:pull`);
17754
+ const path38 = repo.replace(/^ghcr\.io\//, "");
17755
+ const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path38}:pull`);
17756
17756
  if (!tokenRes.ok) {
17757
17757
  throw new LocalCliError({
17758
17758
  code: "PLATFORM_GHCR_TOKEN_FAILED",
@@ -17768,7 +17768,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
17768
17768
  hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
17769
17769
  });
17770
17770
  }
17771
- const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path37}/tags/list`, {
17771
+ const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path38}/tags/list`, {
17772
17772
  headers: { Authorization: `Bearer ${token}` }
17773
17773
  });
17774
17774
  if (!tagsRes.ok) {
@@ -19101,25 +19101,25 @@ function mergeThreeWay(ours, base, theirs) {
19101
19101
  fs23.rmSync(dir, { recursive: true, force: true });
19102
19102
  }
19103
19103
  }
19104
- var skip = (path37, skipReason) => ({ path: path37, kind: "skip", skipReason });
19104
+ var skip = (path38, skipReason) => ({ path: path38, kind: "skip", skipReason });
19105
19105
  function classifyCandidate(c) {
19106
- const { path: path37, base, ours, theirs } = c;
19107
- if (theirs === base) return { path: path37, kind: "noop" };
19106
+ const { path: path38, base, ours, theirs } = c;
19107
+ if (theirs === base) return { path: path38, kind: "noop" };
19108
19108
  if (base === null) {
19109
- if (theirs === null) return { path: path37, kind: "noop" };
19110
- if (ours === null) return { path: path37, kind: "add", content: theirs };
19111
- if (ours === theirs) return { path: path37, kind: "noop" };
19112
- return skip(path37, "you already have a file at this path");
19109
+ if (theirs === null) return { path: path38, kind: "noop" };
19110
+ if (ours === null) return { path: path38, kind: "add", content: theirs };
19111
+ if (ours === theirs) return { path: path38, kind: "noop" };
19112
+ return skip(path38, "you already have a file at this path");
19113
19113
  }
19114
19114
  if (ours === null) {
19115
- return theirs === null ? { path: path37, kind: "noop" } : skip(path37, "you removed this file; upstream changed it");
19115
+ return theirs === null ? { path: path38, kind: "noop" } : skip(path38, "you removed this file; upstream changed it");
19116
19116
  }
19117
19117
  if (theirs === null) {
19118
- return ours === base ? { path: path37, kind: "delete" } : skip(path37, "you edited this file; upstream removed it");
19118
+ return ours === base ? { path: path38, kind: "delete" } : skip(path38, "you edited this file; upstream removed it");
19119
19119
  }
19120
- if (ours === base) return { path: path37, kind: "update", content: theirs };
19120
+ if (ours === base) return { path: path38, kind: "update", content: theirs };
19121
19121
  const m = mergeThreeWay(ours, base, theirs);
19122
- return m.ok ? { path: path37, kind: "update", content: m.merged } : skip(path37, "your edits conflict with the update");
19122
+ return m.ok ? { path: path38, kind: "update", content: m.merged } : skip(path38, "your edits conflict with the update");
19123
19123
  }
19124
19124
 
19125
19125
  // src/lib/seed-pr-body.ts
@@ -19304,7 +19304,7 @@ function bindRepoApi(token, repo) {
19304
19304
  openPr: (head, base, title, body) => openPullRequestViaApp({ token, repo, base, head, title, body }),
19305
19305
  updatePr: (n, title, body) => updatePullRequestViaApp({ token, repo, number: n, title, body }),
19306
19306
  closePr: (n, comment) => closePullRequestViaApp({ token, repo, number: n, comment }),
19307
- readFileAtRef: (path37, ref) => readRepoFileViaApp({ token, repo, path: path37, ref })
19307
+ readFileAtRef: (path38, ref) => readRepoFileViaApp({ token, repo, path: path38, ref })
19308
19308
  };
19309
19309
  }
19310
19310
  async function runSeedRefresh(args) {
@@ -21084,9 +21084,9 @@ function flattenDelta(delta, prefix = "") {
21084
21084
  const out = [];
21085
21085
  for (const d of delta) {
21086
21086
  const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
21087
- const path37 = prefix ? `${prefix}${segment}` : d.path;
21088
- if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path37));
21089
- else out.push({ ...d, path: path37 });
21087
+ const path38 = prefix ? `${prefix}${segment}` : d.path;
21088
+ if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path38));
21089
+ else out.push({ ...d, path: path38 });
21090
21090
  }
21091
21091
  return out;
21092
21092
  }
@@ -23243,7 +23243,7 @@ function mapItems(raw) {
23243
23243
  return acc.reverse();
23244
23244
  }
23245
23245
  function makeConversationSource(client, opts = {}) {
23246
- const sleep4 = opts.sleep ?? defaultSleep;
23246
+ const sleep5 = opts.sleep ?? defaultSleep;
23247
23247
  return {
23248
23248
  async items(conversationId) {
23249
23249
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
@@ -23261,7 +23261,7 @@ function makeConversationSource(client, opts = {}) {
23261
23261
  throw new ConvFetchError("auth", `auth failed reading ${conversationId}`);
23262
23262
  const retryable = status === void 0 || status === TRANSIENT;
23263
23263
  if (retryable && attempt < MAX_ATTEMPTS - 1) {
23264
- await sleep4(attempt);
23264
+ await sleep5(attempt);
23265
23265
  continue;
23266
23266
  }
23267
23267
  throw new ConvFetchError("terminal", `exhausted ${String(MAX_ATTEMPTS)} retries reading ${conversationId}: ${e?.message ?? String(e)}`);
@@ -23672,7 +23672,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
23672
23672
  // ../../packages/brain/engine/dist/esm/dream/writer.js
23673
23673
  var API2 = "https://api.github.com";
23674
23674
  var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
23675
- var isAllowedWritePath = (path37, allowedPrefix) => allowedPrefix.test(path37) && !path37.split("/").includes("..");
23675
+ var isAllowedWritePath = (path38, allowedPrefix) => allowedPrefix.test(path38) && !path38.split("/").includes("..");
23676
23676
  var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
23677
23677
  function parseFrontmatter(content) {
23678
23678
  const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
@@ -23715,8 +23715,8 @@ function makeGitDataWriter(args) {
23715
23715
  if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
23716
23716
  throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
23717
23717
  }
23718
- async function gh(token, method, path37, body) {
23719
- const res = await doFetch(`${API2}/repos/${repoPath}${path37}`, {
23718
+ async function gh(token, method, path38, body) {
23719
+ const res = await doFetch(`${API2}/repos/${repoPath}${path38}`, {
23720
23720
  method,
23721
23721
  headers: {
23722
23722
  Authorization: `Bearer ${token}`,
@@ -23729,8 +23729,8 @@ function makeGitDataWriter(args) {
23729
23729
  const text = await res.text();
23730
23730
  return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
23731
23731
  }
23732
- async function readFileWith(token, path37) {
23733
- const encodedPath = path37.split("/").map(encodeURIComponent).join("/");
23732
+ async function readFileWith(token, path38) {
23733
+ const encodedPath = path38.split("/").map(encodeURIComponent).join("/");
23734
23734
  const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
23735
23735
  if (r.status === 404)
23736
23736
  return null;
@@ -23746,9 +23746,9 @@ function makeGitDataWriter(args) {
23746
23746
  throw new Error(`fetchTip: HTTP ${String(r.status)}`);
23747
23747
  return r.json.object.sha;
23748
23748
  },
23749
- async readFile(path37) {
23749
+ async readFile(path38) {
23750
23750
  const token = await args.mintToken();
23751
- return readFileWith(token, path37);
23751
+ return readFileWith(token, path38);
23752
23752
  },
23753
23753
  async listMemory() {
23754
23754
  const token = await args.mintToken();
@@ -23757,9 +23757,9 @@ function makeGitDataWriter(args) {
23757
23757
  return [];
23758
23758
  const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
23759
23759
  const out = [];
23760
- for (const path37 of paths) {
23761
- const content = await readFileWith(token, path37);
23762
- out.push({ path: path37, frontmatter: content ? parseFrontmatter(content) : {} });
23760
+ for (const path38 of paths) {
23761
+ const content = await readFileWith(token, path38);
23762
+ out.push({ path: path38, frontmatter: content ? parseFrontmatter(content) : {} });
23763
23763
  }
23764
23764
  return out;
23765
23765
  },
@@ -24063,7 +24063,7 @@ function extractJson(text) {
24063
24063
  return JSON.parse(candidate2.slice(start, end + 1));
24064
24064
  }
24065
24065
  async function propose(model, system, user, opts = {}) {
24066
- const sleep4 = opts.sleep ?? defaultSleep2;
24066
+ const sleep5 = opts.sleep ?? defaultSleep2;
24067
24067
  const modelName = opts.model ?? MODEL_NAME_FALLBACK;
24068
24068
  let inputTokens = 0;
24069
24069
  let outputTokens = 0;
@@ -24079,7 +24079,7 @@ async function propose(model, system, user, opts = {}) {
24079
24079
  } catch (e) {
24080
24080
  lastErr = `parse: ${e.message}`;
24081
24081
  if (attempt < MAX_MODEL_ATTEMPTS - 1) {
24082
- await sleep4(attempt);
24082
+ await sleep5(attempt);
24083
24083
  continue;
24084
24084
  }
24085
24085
  break;
@@ -24088,7 +24088,7 @@ async function propose(model, system, user, opts = {}) {
24088
24088
  if (!Array.isArray(rawDeltas)) {
24089
24089
  lastErr = "schema: top-level { deltas: [] } missing";
24090
24090
  if (attempt < MAX_MODEL_ATTEMPTS - 1) {
24091
- await sleep4(attempt);
24091
+ await sleep5(attempt);
24092
24092
  continue;
24093
24093
  }
24094
24094
  break;
@@ -24198,7 +24198,7 @@ async function loadMemoryContext(brain) {
24198
24198
  return {
24199
24199
  files,
24200
24200
  indexEntries,
24201
- originOf: (path37) => originByPath.get(path37) ?? "worker"
24201
+ originOf: (path38) => originByPath.get(path38) ?? "worker"
24202
24202
  };
24203
24203
  }
24204
24204
 
@@ -24219,14 +24219,14 @@ function checkEvidenceMembership(delta, harvested) {
24219
24219
  }
24220
24220
  var BatchAbort = class extends Error {
24221
24221
  path;
24222
- constructor(path37, message) {
24222
+ constructor(path38, message) {
24223
24223
  super(message);
24224
- this.path = path37;
24224
+ this.path = path38;
24225
24225
  this.name = "BatchAbort";
24226
24226
  }
24227
24227
  };
24228
- var isIndexFile = (path37) => /(^|\/)[^/]*_index\.md$/.test(path37);
24229
- var isMemoryIndex = (path37) => path37 === "memory/MEMORY.md";
24228
+ var isIndexFile = (path38) => /(^|\/)[^/]*_index\.md$/.test(path38);
24229
+ var isMemoryIndex = (path38) => path38 === "memory/MEMORY.md";
24230
24230
  function targetPath(delta) {
24231
24231
  switch (delta.verb) {
24232
24232
  case "new":
@@ -24243,29 +24243,29 @@ function targetPath(delta) {
24243
24243
  }
24244
24244
  async function checkOriginTier(delta, lookup) {
24245
24245
  const evidence = evidenceOf(delta) ?? [];
24246
- const path37 = targetPath(delta);
24247
- if (path37 === null)
24246
+ const path38 = targetPath(delta);
24247
+ if (path38 === null)
24248
24248
  return { ok: true };
24249
- if (isIndexFile(path37) && !isMemoryIndex(path37)) {
24250
- return { ok: false, flagged: { kind: "flagged", path: path37, tension: `refused: ${path37} is an index file (only memory/MEMORY.md is editable)`, evidence } };
24249
+ if (isIndexFile(path38) && !isMemoryIndex(path38)) {
24250
+ return { ok: false, flagged: { kind: "flagged", path: path38, tension: `refused: ${path38} is an index file (only memory/MEMORY.md is editable)`, evidence } };
24251
24251
  }
24252
- const target = await lookup(path37);
24253
- const isStructural = path37.startsWith("references/");
24252
+ const target = await lookup(path38);
24253
+ const isStructural = path38.startsWith("references/");
24254
24254
  const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
24255
24255
  const editable = origin === "worker" || origin === "dream";
24256
24256
  if (editable)
24257
24257
  return { ok: true };
24258
24258
  const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
24259
24259
  if (removesOperatorTarget) {
24260
- throw new BatchAbort(path37, `supersede/retract OLD target ${path37} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
24260
+ throw new BatchAbort(path38, `supersede/retract OLD target ${path38} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
24261
24261
  }
24262
- return { ok: false, flagged: { kind: "flagged", path: path37, tension: `operator-origin (${origin}); flag-only`, evidence } };
24262
+ return { ok: false, flagged: { kind: "flagged", path: path38, tension: `operator-origin (${origin}); flag-only`, evidence } };
24263
24263
  }
24264
24264
  var ALLOWED_MEMORY = /^memory\/.+\.md$/;
24265
24265
  var ALLOWED_INBOX = /^inbox\//;
24266
24266
  var ALLOWED_QUARANTINE = /^quarantine\//;
24267
24267
  var ALLOWED_FLAG = /^memory\//;
24268
- var hasTraversal = (path37) => path37.split("/").includes("..");
24268
+ var hasTraversal = (path38) => path38.split("/").includes("..");
24269
24269
  function checkPathAllowed(delta) {
24270
24270
  const evidence = evidenceOf(delta) ?? [];
24271
24271
  const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
@@ -24372,7 +24372,7 @@ ${inject}
24372
24372
  ---
24373
24373
  ` + body.slice(m[0].length);
24374
24374
  }
24375
- var indexLine = (path37, title, date, tags) => `- \`${path37}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
24375
+ var indexLine = (path38, title, date, tags) => `- \`${path38}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
24376
24376
  function splitIndex(index) {
24377
24377
  const all = index.split("\n");
24378
24378
  const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
@@ -24545,16 +24545,16 @@ function defaultDreamSeams(opts = {}) {
24545
24545
  const mem = await memory(deps.brain);
24546
24546
  const harvested = harvestedIds(input);
24547
24547
  const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
24548
- const lookup = (path37) => Promise.resolve(mem.files.find((f) => f.path === path37) ?? null);
24548
+ const lookup = (path38) => Promise.resolve(mem.files.find((f) => f.path === path38) ?? null);
24549
24549
  const digest = [...pendingRejected];
24550
24550
  let changes = [];
24551
24551
  let indexEdit;
24552
24552
  try {
24553
24553
  for (const raw of deltas) {
24554
24554
  const delta = forcePolicyQuarantine(raw);
24555
- const path37 = checkPathAllowed(delta);
24556
- if (!path37.ok) {
24557
- digest.push(path37.rejected);
24555
+ const path38 = checkPathAllowed(delta);
24556
+ if (!path38.ok) {
24557
+ digest.push(path38.rejected);
24558
24558
  continue;
24559
24559
  }
24560
24560
  const ev = checkEvidenceMembership(delta, harvested);
@@ -25668,13 +25668,13 @@ var EVENT_TIERS = {
25668
25668
  var EVENT_ENUM = Object.keys(EVENT_TIERS);
25669
25669
 
25670
25670
  // ../../packages/telemetry-contract/src/disclosure.ts
25671
- var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t receives limited operational data linked to it \u2014 installation and update events, installed versions, and service health signals, along with the company name and contact email you provide. This is service data used only to operate and support the platform; it contains none of your content. Details: TELEMETRY.md.";
25671
+ var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t receives limited operational data linked to it \u2014 installation and update events, installed versions, and service health signals. Your installation is identified only by a random installation ID we generate at setup \u2014 an opaque value with no information encoded in it. No name, email, company, or subscription information is sent in the enrollment record unless you explicitly choose to share contact details for support. At enrollment, we also verify a cloud identity token to confirm the request comes from a real cloud account; that token names the account making the request, and is otherwise discarded immediately and never stored. This is service data used only to operate and support the platform; it contains none of your content. Details: TELEMETRY.md.";
25672
25672
 
25673
25673
  // ../../packages/telemetry-contract/src/endpoints.ts
25674
25674
  var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
25675
- function ingestUrl(path37) {
25675
+ function ingestUrl(path38) {
25676
25676
  const base = INGEST_BASE_URL.replace(/\/+$/, "");
25677
- return `${base}${path37.startsWith("/") ? path37 : `/${path37}`}`;
25677
+ return `${base}${path38.startsWith("/") ? path38 : `/${path38}`}`;
25678
25678
  }
25679
25679
 
25680
25680
  // src/commands/bootstrap/preflight.ts
@@ -25928,6 +25928,7 @@ function buildAciCreateArgs(s) {
25928
25928
  if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
25929
25929
  if (s.updateChannelUrl) env.push(`M8T_UPDATE_CHANNEL_URL=${s.updateChannelUrl}`);
25930
25930
  if (s.enrollContactEmail) env.push(`M8T_ENROLL_CONTACT_EMAIL=${s.enrollContactEmail}`);
25931
+ if (s.enrollCompany) env.push(`M8T_ENROLL_COMPANY=${s.enrollCompany}`);
25931
25932
  if (s.githubApp) {
25932
25933
  const g = s.githubApp;
25933
25934
  env.push(
@@ -26078,7 +26079,7 @@ async function writeBootstrapState(state, home = os13.homedir()) {
26078
26079
  // src/commands/bootstrap/launch.ts
26079
26080
  var DEFAULT_RG = "rg-m8t-stack";
26080
26081
  var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
26081
- var DEFAULT_INSTALLER_TAG = "v0.1.39";
26082
+ var DEFAULT_INSTALLER_TAG = "v0.1.41";
26082
26083
  var ACI_NAME = "m8t-installer";
26083
26084
  var MI_NAME = "m8t-installer-mi";
26084
26085
  var BootstrapLaunchCommand = class extends M8tCommand {
@@ -26090,7 +26091,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
26090
26091
  ["Launch in eastus2", "$0 bootstrap launch --location eastus2"],
26091
26092
  ["BYO app registration", "$0 bootstrap launch --location eastus2 --client-id <appId>"],
26092
26093
  ["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.33"],
26093
- ["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.33"]
26094
+ ["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.33"],
26095
+ ["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
26094
26096
  ]
26095
26097
  });
26096
26098
  location = Option47.String("--location");
@@ -26104,6 +26106,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
26104
26106
  installerImage = Option47.String("--installer-image");
26105
26107
  gatewayImageRef = Option47.String("--gateway-image-ref");
26106
26108
  githubAppCreds = Option47.String("--github-app-creds");
26109
+ contactEmail = Option47.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
26110
+ company = Option47.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
26107
26111
  async executeCommand() {
26108
26112
  const location = typeof this.location === "string" ? this.location : void 0;
26109
26113
  if (!location) {
@@ -26195,7 +26199,11 @@ var BootstrapLaunchCommand = class extends M8tCommand {
26195
26199
  gatewayImageRef,
26196
26200
  foundryTracing: "skip",
26197
26201
  githubApp,
26198
- enrollContactEmail: account.upn,
26202
+ // Opt-in only. The signed-in UPN was previously seeded here automatically;
26203
+ // an installation is identified by its random instance id, and contact
26204
+ // details are sent only when the operator asks for them.
26205
+ ...typeof this.contactEmail === "string" ? { enrollContactEmail: this.contactEmail } : {},
26206
+ ...typeof this.company === "string" ? { enrollCompany: this.company } : {},
26199
26207
  ...process.env.M8T_UPDATE_CHANNEL_URL ? { updateChannelUrl: process.env.M8T_UPDATE_CHANNEL_URL } : {}
26200
26208
  });
26201
26209
  this.context.stdout.write(
@@ -26256,7 +26264,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
26256
26264
  typeof this.output === "string" ? this.output : void 0,
26257
26265
  this.context.stdout
26258
26266
  );
26259
- const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
26267
+ const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
26260
26268
  const watch = this.watch === true;
26261
26269
  for (; ; ) {
26262
26270
  let doc;
@@ -26266,7 +26274,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
26266
26274
  if (watch && e instanceof LocalCliError && e.code === "BOOTSTRAP_STATUS_UNREADABLE") {
26267
26275
  if (mode === "pretty") this.context.stderr.write(` ${colors.dim("waiting for the installer to start\u2026")}
26268
26276
  `);
26269
- await sleep4(1e4);
26277
+ await sleep5(1e4);
26270
26278
  continue;
26271
26279
  }
26272
26280
  throw e;
@@ -26291,7 +26299,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
26291
26299
  );
26292
26300
  return 1;
26293
26301
  }
26294
- await sleep4(1e4);
26302
+ await sleep5(1e4);
26295
26303
  }
26296
26304
  }
26297
26305
  };
@@ -27659,9 +27667,9 @@ async function serveOnboardingUiDetached(args) {
27659
27667
  child.unref();
27660
27668
  fs32.writeFileSync(pidPath, String(child.pid), "utf8");
27661
27669
  const deadline = Date.now() + 45e3;
27662
- const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
27670
+ const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
27663
27671
  while (Date.now() < deadline) {
27664
- await sleep4(500);
27672
+ await sleep5(500);
27665
27673
  if (await isPortOpen()) break;
27666
27674
  }
27667
27675
  return { alreadyRunning: false, logPath };
@@ -27719,9 +27727,9 @@ async function serveOnboardingRelayDetached(args) {
27719
27727
  child.unref();
27720
27728
  fs32.writeFileSync(pidPath, String(child.pid), "utf8");
27721
27729
  const deadline = Date.now() + 45e3;
27722
- const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
27730
+ const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
27723
27731
  while (Date.now() < deadline) {
27724
- await sleep4(500);
27732
+ await sleep5(500);
27725
27733
  if (await isLocalPortOpen(portNum)) break;
27726
27734
  }
27727
27735
  return { alreadyRunning: false, logPath };
@@ -27978,7 +27986,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
27978
27986
  const parsedTimeout = Number(rawTimeout);
27979
27987
  const timeoutMin = rawTimeout !== "" && Number.isFinite(parsedTimeout) ? parsedTimeout : 20;
27980
27988
  const deadline = Date.now() + timeoutMin * 6e4;
27981
- const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
27989
+ const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
27982
27990
  for (; ; ) {
27983
27991
  const token = await getFoundryToken();
27984
27992
  const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
@@ -28006,33 +28014,60 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
28006
28014
  }
28007
28015
  this.context.stderr.write(` ${colors.dim("waiting for the questionnaire to complete\u2026")}
28008
28016
  `);
28009
- await sleep4(2e4);
28017
+ await sleep5(2e4);
28010
28018
  }
28011
28019
  }
28012
28020
  };
28013
28021
 
28014
28022
  // src/commands/telemetry/enroll.ts
28023
+ import * as fs34 from "fs";
28024
+ import * as os19 from "os";
28025
+ import * as path37 from "path";
28015
28026
  import { Command as Command56, Option as Option53 } from "clipanion";
28016
28027
  init_errors();
28017
28028
 
28018
28029
  // src/lib/telemetry-enroll.ts
28019
28030
  init_errors();
28031
+ async function defaultToken() {
28032
+ const out = await runAz([
28033
+ "account",
28034
+ "get-access-token",
28035
+ "--resource",
28036
+ "https://management.azure.com/",
28037
+ "--query",
28038
+ "accessToken",
28039
+ "-o",
28040
+ "tsv"
28041
+ ]);
28042
+ const token = out.trim();
28043
+ return token.length > 0 ? token : null;
28044
+ }
28045
+ var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
28020
28046
  async function enroll(args) {
28021
28047
  const f = args.fetchImpl ?? fetch;
28022
28048
  const url = args.baseUrl ? `${args.baseUrl.replace(/\/+$/, "")}/api/ingest/enroll` : ingestUrl("/api/ingest/enroll");
28049
+ const retryDelayMs = args.retryDelayMs ?? 1e3;
28050
+ const token = await (args.tokenImpl ?? defaultToken)().catch(() => null);
28051
+ const payload = { schemaVersion: 1 };
28052
+ const company = args.company?.trim();
28053
+ if (company) payload.company = company;
28054
+ const contactEmail = args.contactEmail?.trim();
28055
+ if (contactEmail) payload.contactEmail = contactEmail;
28056
+ payload.proof = token ? { type: "entra-jwt", token } : null;
28057
+ const body = JSON.stringify(payload);
28023
28058
  let res;
28024
- try {
28025
- res = await f(url, {
28026
- method: "POST",
28027
- headers: { "content-type": "application/json" },
28028
- body: JSON.stringify({ schemaVersion: 1, company: args.company, contactEmail: args.contactEmail, subscriptionId: args.subscriptionId, proof: null })
28029
- });
28030
- } catch (e) {
28031
- throw new LocalCliError({ code: "TELEMETRY_ENROLL_UNREACHABLE", message: `Could not reach the telemetry ingest to enroll: ${e instanceof Error ? e.message : String(e)}` });
28059
+ for (let attempt = 1; attempt <= 3; attempt++) {
28060
+ try {
28061
+ res = await f(url, { method: "POST", headers: { "content-type": "application/json" }, body });
28062
+ } catch (e) {
28063
+ throw new LocalCliError({ code: "TELEMETRY_ENROLL_UNREACHABLE", message: `Could not reach the telemetry ingest to enroll: ${e instanceof Error ? e.message : String(e)}` });
28064
+ }
28065
+ if (res.status !== 503 || attempt === 3) break;
28066
+ await sleep4(retryDelayMs * attempt);
28032
28067
  }
28033
- if (!res.ok) {
28034
- const body = await res.text().catch(() => "");
28035
- throw new LocalCliError({ code: "TELEMETRY_ENROLL_FAILED", message: `Enroll failed (HTTP ${res.status.toString()}): ${body}` });
28068
+ if (!res?.ok) {
28069
+ const detail = res ? await res.text().catch(() => "") : "";
28070
+ throw new LocalCliError({ code: "TELEMETRY_ENROLL_FAILED", message: `Enroll failed (HTTP ${res ? res.status.toString() : "?"}): ${detail}` });
28036
28071
  }
28037
28072
  const json = await res.json();
28038
28073
  if (!json.instanceId || !json.ingestKey) {
@@ -28072,19 +28107,22 @@ var TelemetryEnrollCommand = class extends M8tCommand {
28072
28107
  static paths = [["telemetry", "enroll"]];
28073
28108
  static usage = Command56.Usage({
28074
28109
  description: "Enroll this installation for operational telemetry (pre-existing installs).",
28075
- details: "Mints an instance id + ingest key from the m8t telemetry ingest and stores the key in your platform Key Vault, the same place the installer writes it on a fresh install. Idempotent: refuses if a key already exists unless --force.",
28076
- examples: [["Enroll", "$0 telemetry enroll"]]
28110
+ details: "Generates an instance id + ingest key from the m8t telemetry ingest and stores the key in your platform Key Vault, the same place the installer writes it on a fresh install. Your installation is identified only by that random instance id \u2014 no contact, company, or subscription details are sent unless you pass them explicitly. Idempotent: refuses if a key already exists.",
28111
+ examples: [
28112
+ ["Enroll", "$0 telemetry enroll"],
28113
+ ["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
28114
+ ]
28077
28115
  });
28078
- company = Option53.String("--company", { description: "Your company name (optional)." });
28079
- contactEmail = Option53.String("--contact-email", { description: "Contact email (default: your signed-in az UPN)." });
28080
- subscription = Option53.String("--subscription", { description: "Azure subscription id (default: your active az subscription)." });
28116
+ company = Option53.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
28117
+ contactEmail = Option53.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
28118
+ subscription = Option53.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
28081
28119
  resourceGroup = Option53.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
28082
- force = Option53.Boolean("--force", false, { description: "Re-enroll even if a key is already stored (mints a new instance id)." });
28120
+ force = Option53.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
28083
28121
  async executeCommand() {
28084
28122
  const account = await getAzAccount();
28085
28123
  const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
28086
- const contactEmail = (typeof this.contactEmail === "string" ? this.contactEmail : void 0) ?? account.upn;
28087
- const company = (typeof this.company === "string" ? this.company : void 0) ?? "";
28124
+ const contactEmail = typeof this.contactEmail === "string" ? this.contactEmail : void 0;
28125
+ const company = typeof this.company === "string" ? this.company : void 0;
28088
28126
  const d = await discoverGateway({
28089
28127
  subscriptionId,
28090
28128
  interactive: false,
@@ -28100,15 +28138,26 @@ var TelemetryEnrollCommand = class extends M8tCommand {
28100
28138
  existing = "";
28101
28139
  }
28102
28140
  if (existing.trim()) {
28141
+ const wantedContact = contactEmail !== void 0 || company !== void 0;
28103
28142
  throw new LocalCliError({
28104
28143
  code: "TELEMETRY_ALREADY_ENROLLED",
28105
- message: "This installation is already enrolled.",
28106
- hint: "Pass --force to re-enroll (mints a new instance id)."
28144
+ message: wantedContact ? "This installation is already enrolled, so contact details can't be attached from here." : "This installation is already enrolled.",
28145
+ hint: "To add or change the contact details on your record, reach out through the channels in SUPPORT.md and we'll update it for you."
28107
28146
  });
28108
28147
  }
28109
28148
  }
28110
- const { instanceId, ingestKey } = await enroll({ company, contactEmail, subscriptionId });
28111
- await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--value", ingestKey, "--only-show-errors"]);
28149
+ const { instanceId, ingestKey } = await enroll({
28150
+ ...company ? { company } : {},
28151
+ ...contactEmail ? { contactEmail } : {}
28152
+ });
28153
+ const keyDir = fs34.mkdtempSync(path37.join(os19.tmpdir(), "m8t-ingest-key-"));
28154
+ const keyFile = path37.join(keyDir, "key");
28155
+ try {
28156
+ fs34.writeFileSync(keyFile, ingestKey, { mode: 384 });
28157
+ await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--file", keyFile, "--only-show-errors"]);
28158
+ } finally {
28159
+ fs34.rmSync(keyDir, { recursive: true, force: true });
28160
+ }
28112
28161
  this.context.stdout.write(
28113
28162
  `${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
28114
28163
  `