@m8t-stack/cli 0.2.34 → 0.2.36
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/README.md +13 -10
- package/dist/cli.js +273 -104
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -293,19 +293,9 @@ var init_secrets = __esm({
|
|
|
293
293
|
});
|
|
294
294
|
|
|
295
295
|
// ../../packages/github-app-auth/dist/esm/cache.js
|
|
296
|
-
var cache_exports = {};
|
|
297
|
-
__export(cache_exports, {
|
|
298
|
-
EXPIRY_SAFETY_MS: () => EXPIRY_SAFETY_MS,
|
|
299
|
-
_resetTokenCache: () => _resetTokenCache,
|
|
300
|
-
getCachedToken: () => getCachedToken,
|
|
301
|
-
putCachedToken: () => putCachedToken
|
|
302
|
-
});
|
|
303
296
|
function key(installationId, repository) {
|
|
304
297
|
return `${installationId}:${repository}`;
|
|
305
298
|
}
|
|
306
|
-
function _resetTokenCache() {
|
|
307
|
-
cache2.clear();
|
|
308
|
-
}
|
|
309
299
|
function getCachedToken(installationId, repository) {
|
|
310
300
|
const e = cache2.get(key(installationId, repository));
|
|
311
301
|
if (!e)
|
|
@@ -384,17 +374,18 @@ var init_mint = __esm({
|
|
|
384
374
|
});
|
|
385
375
|
|
|
386
376
|
// ../../packages/github-app-auth/dist/esm/rotate.js
|
|
377
|
+
function connectionStateKey(projectArmId, connectionName) {
|
|
378
|
+
return `${projectArmId}/connections/${connectionName}`;
|
|
379
|
+
}
|
|
387
380
|
async function rotateConnectionAuth(args) {
|
|
388
|
-
const { getCachedToken: getCachedToken2, putCachedToken: putCachedToken2 } = await Promise.resolve().then(() => (init_cache(), cache_exports));
|
|
389
|
-
const before = getCachedToken2(args.installationId, args.repository);
|
|
390
381
|
const minted = await mintInstallationToken({
|
|
391
382
|
credential: args.credential,
|
|
392
383
|
kvUri: args.kvUri,
|
|
393
384
|
installationId: args.installationId,
|
|
394
385
|
repository: args.repository
|
|
395
386
|
});
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
387
|
+
const stateKey = connectionStateKey(args.projectArmId, args.connectionName);
|
|
388
|
+
if (connectionPatchState.get(stateKey) !== minted.token) {
|
|
398
389
|
const armTokenResp = await args.credential.getToken(ARM_SCOPE);
|
|
399
390
|
if (!armTokenResp?.token) {
|
|
400
391
|
throw new Error("rotateConnectionAuth: failed to acquire ARM management token");
|
|
@@ -423,17 +414,18 @@ async function rotateConnectionAuth(args) {
|
|
|
423
414
|
throw new Error(`rotateConnectionAuth: HTTP ${String(res.status)} on PATCH ${url}
|
|
424
415
|
${text.slice(0, 500)}`);
|
|
425
416
|
}
|
|
426
|
-
|
|
417
|
+
connectionPatchState.set(stateKey, minted.token);
|
|
427
418
|
}
|
|
428
419
|
return { rotatedAt: /* @__PURE__ */ new Date(), expiresAt: minted.expiresAt };
|
|
429
420
|
}
|
|
430
|
-
var ARM_SCOPE, FOUNDRY_API;
|
|
421
|
+
var ARM_SCOPE, FOUNDRY_API, connectionPatchState;
|
|
431
422
|
var init_rotate = __esm({
|
|
432
423
|
"../../packages/github-app-auth/dist/esm/rotate.js"() {
|
|
433
424
|
"use strict";
|
|
434
425
|
init_mint();
|
|
435
426
|
ARM_SCOPE = "https://management.azure.com/.default";
|
|
436
427
|
FOUNDRY_API = "2025-04-01-preview";
|
|
428
|
+
connectionPatchState = /* @__PURE__ */ new Map();
|
|
437
429
|
}
|
|
438
430
|
});
|
|
439
431
|
|
|
@@ -673,6 +665,53 @@ var init_foundry_agent_get = __esm({
|
|
|
673
665
|
}
|
|
674
666
|
});
|
|
675
667
|
|
|
668
|
+
// src/lib/foundry-agent-version.ts
|
|
669
|
+
async function createAgentVersion(args, fetchImpl = fetch) {
|
|
670
|
+
const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
|
|
671
|
+
if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
|
|
672
|
+
const url = `${args.projectEndpoint}/agents/${args.agentName}/versions?api-version=v1`;
|
|
673
|
+
const res = await fetchImpl(url, {
|
|
674
|
+
method: "POST",
|
|
675
|
+
headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} },
|
|
676
|
+
body: JSON.stringify({ definition: args.definition, metadata: args.metadata })
|
|
677
|
+
});
|
|
678
|
+
if (!res.ok) {
|
|
679
|
+
const text = await res.text();
|
|
680
|
+
throw new LocalCliError({ code: "AGENT_CREATE_VERSION_FAILED", message: `POST ${url}: HTTP ${res.status.toString()}
|
|
681
|
+
${text.slice(0, 500)}` });
|
|
682
|
+
}
|
|
683
|
+
await ensureAgentEndpointEntraIsolation(
|
|
684
|
+
{ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, extraHeaders: args.extraHeaders },
|
|
685
|
+
fetchImpl
|
|
686
|
+
);
|
|
687
|
+
const data = await res.json();
|
|
688
|
+
if (!data.version) throw new LocalCliError({ code: "AGENT_CREATE_VERSION_NO_VERSION", message: "createVersion returned no version" });
|
|
689
|
+
return data.version;
|
|
690
|
+
}
|
|
691
|
+
async function ensureAgentEndpointEntraIsolation(args, fetchImpl = fetch) {
|
|
692
|
+
const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
|
|
693
|
+
if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
|
|
694
|
+
const url = `${args.projectEndpoint}/agents/${args.agentName}?api-version=v1`;
|
|
695
|
+
const res = await fetchImpl(url, {
|
|
696
|
+
method: "PATCH",
|
|
697
|
+
headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} },
|
|
698
|
+
body: JSON.stringify({ agent_endpoint: { authorization_schemes: [{ type: "Entra", isolation_key_source: { kind: "Entra" } }] } })
|
|
699
|
+
});
|
|
700
|
+
if (!res.ok) {
|
|
701
|
+
const text = await res.text();
|
|
702
|
+
throw new LocalCliError({ code: "AGENT_ENDPOINT_AUTH_PATCH_FAILED", message: `PATCH ${url}: HTTP ${res.status.toString()}
|
|
703
|
+
${text.slice(0, 500)}` });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
var FOUNDRY_DATA_SCOPE;
|
|
707
|
+
var init_foundry_agent_version = __esm({
|
|
708
|
+
"src/lib/foundry-agent-version.ts"() {
|
|
709
|
+
"use strict";
|
|
710
|
+
init_errors();
|
|
711
|
+
FOUNDRY_DATA_SCOPE = "https://ai.azure.com/.default";
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
676
715
|
// src/lib/brain-yaml-mirror.ts
|
|
677
716
|
import { parse as parseYaml4 } from "yaml";
|
|
678
717
|
function linkSection(args) {
|
|
@@ -841,6 +880,12 @@ async function createCoderVersion(args) {
|
|
|
841
880
|
foundryFeatures: "HostedAgents=V1Preview",
|
|
842
881
|
metadata
|
|
843
882
|
});
|
|
883
|
+
await ensureAgentEndpointEntraIsolation({
|
|
884
|
+
credential: args.credential,
|
|
885
|
+
projectEndpoint: args.endpoint,
|
|
886
|
+
agentName: args.name,
|
|
887
|
+
extraHeaders: { "Foundry-Features": "HostedAgents=V1Preview" }
|
|
888
|
+
});
|
|
844
889
|
const v = version.version;
|
|
845
890
|
if (v === void 0) {
|
|
846
891
|
throw new LocalCliError({
|
|
@@ -905,6 +950,11 @@ async function createPromptVersion(args) {
|
|
|
905
950
|
if (args.metadata.personaVersion !== null) metadata.personaVersion = args.metadata.personaVersion;
|
|
906
951
|
if (args.metadata.fillableFieldValues !== void 0) metadata.fillableFieldValues = args.metadata.fillableFieldValues;
|
|
907
952
|
const version = await project.agents.createVersion(args.name, definition, { metadata });
|
|
953
|
+
await ensureAgentEndpointEntraIsolation({
|
|
954
|
+
credential: args.credential,
|
|
955
|
+
projectEndpoint: args.endpoint,
|
|
956
|
+
agentName: args.name
|
|
957
|
+
});
|
|
908
958
|
const v = version.version;
|
|
909
959
|
if (v === void 0) {
|
|
910
960
|
throw new LocalCliError({
|
|
@@ -931,6 +981,7 @@ var init_foundry_agents = __esm({
|
|
|
931
981
|
"use strict";
|
|
932
982
|
init_http();
|
|
933
983
|
init_errors();
|
|
984
|
+
init_foundry_agent_version();
|
|
934
985
|
METADATA_SOURCE = "m8t";
|
|
935
986
|
FOUNDRY_SCOPE2 = "https://ai.azure.com/.default";
|
|
936
987
|
API = "v1";
|
|
@@ -1247,7 +1298,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1247
1298
|
import { Builtins, Cli } from "clipanion";
|
|
1248
1299
|
|
|
1249
1300
|
// src/lib/package-version.ts
|
|
1250
|
-
var CLI_VERSION = "0.2.
|
|
1301
|
+
var CLI_VERSION = "0.2.36";
|
|
1251
1302
|
|
|
1252
1303
|
// src/lib/render-error.ts
|
|
1253
1304
|
init_errors();
|
|
@@ -1341,7 +1392,7 @@ var BACKEND_RULES = [
|
|
|
1341
1392
|
reason: "no_adapter_registered",
|
|
1342
1393
|
hint: (e) => {
|
|
1343
1394
|
const channel = getDetailField(e, "channel") ?? "<channel>";
|
|
1344
|
-
return `no adapter for channel '${channel}'.
|
|
1395
|
+
return `no adapter for channel '${channel}'. The channel-specific adapter is not registered in this build.`;
|
|
1345
1396
|
}
|
|
1346
1397
|
},
|
|
1347
1398
|
{
|
|
@@ -1444,6 +1495,7 @@ var BACKEND_RULES = [
|
|
|
1444
1495
|
var LOCAL_RULES = {
|
|
1445
1496
|
AGENT_CREATE_VERSION_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1446
1497
|
AGENT_CREATE_VERSION_NO_VERSION: "Foundry returned 2xx but no version \u2014 likely an API regression. Re-try; if it persists, file an issue.",
|
|
1498
|
+
AGENT_ENDPOINT_AUTH_PATCH_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1447
1499
|
AGENT_GET_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1448
1500
|
AGENT_NOT_FOUND: "Use `m8t brain create <name>` to create the worker first, OR run `m8t deploy <name>` to register an existing agent.",
|
|
1449
1501
|
AGENT_REDEPLOY_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
@@ -1452,6 +1504,14 @@ var LOCAL_RULES = {
|
|
|
1452
1504
|
APP_HEALTH_FAILED: "Run `m8t brain check-app` for details, then re-run this command.",
|
|
1453
1505
|
APP_UNINSTALL_FAILED: "GitHub App-uninstall DELETE failed. Re-run with `--keep-app-install` if you want to skip this step.",
|
|
1454
1506
|
ARM_AUTH: "Run `az login` and retry.",
|
|
1507
|
+
// BOOTSTRAP_OCCUPANCY_UNVERIFIED and BOOTSTRAP_REINSTALL_TARGET_MISMATCH are
|
|
1508
|
+
// deliberately absent here: both are always thrown with an inline `hint`
|
|
1509
|
+
// (see bootstrap-occupancy.ts / commands/bootstrap/launch.ts), and
|
|
1510
|
+
// render-error.ts prefers that inline hint over this table — so a table
|
|
1511
|
+
// entry for either code would be dead text that could drift from what
|
|
1512
|
+
// actually prints. BOOTSTRAP_TARGET_OCCUPIED has no inline hint, so its
|
|
1513
|
+
// entry below is the live one.
|
|
1514
|
+
BOOTSTRAP_TARGET_OCCUPIED: "Pick an empty resource group with `--resource-group <new-rg>`, or run with both `--resource-group <rg>` and `--reinstall-into <rg>` (same group name) to install into the existing one anyway.",
|
|
1455
1515
|
// m8t brain create: stageAsGitRepo step
|
|
1456
1516
|
BRAIN_GIT_INIT_FAILED: "If git is not installed, install it. Otherwise re-run with --verbose.",
|
|
1457
1517
|
BRAIN_NOT_LINKED: "The worker has no brain link. Use `m8t brain link` or `m8t brain create` first.",
|
|
@@ -11302,6 +11362,14 @@ function isUndiciDispatcherVersionMismatchError(error) {
|
|
|
11302
11362
|
return false;
|
|
11303
11363
|
}
|
|
11304
11364
|
|
|
11365
|
+
// ../../packages/foundry-invoke/dist/esm/openai-error.js
|
|
11366
|
+
function openAIErrorStatus(err) {
|
|
11367
|
+
if (typeof err !== "object" || err === null)
|
|
11368
|
+
return void 0;
|
|
11369
|
+
const status = err.status;
|
|
11370
|
+
return typeof status === "number" ? status : void 0;
|
|
11371
|
+
}
|
|
11372
|
+
|
|
11305
11373
|
// ../../packages/foundry-invoke/dist/esm/invoke-cli-core.js
|
|
11306
11374
|
function isBrainConnPropagation(err) {
|
|
11307
11375
|
const e = err;
|
|
@@ -11328,9 +11396,10 @@ function errText(err) {
|
|
|
11328
11396
|
return `${e.code ?? ""} ${e.error?.code ?? ""} ${e.error?.message ?? ""} ${e.message ?? ""}`.trim();
|
|
11329
11397
|
}
|
|
11330
11398
|
function errStatus(err) {
|
|
11399
|
+
const direct = openAIErrorStatus(err);
|
|
11400
|
+
if (direct !== void 0)
|
|
11401
|
+
return direct;
|
|
11331
11402
|
const e = err;
|
|
11332
|
-
if (typeof e.status === "number")
|
|
11333
|
-
return e.status;
|
|
11334
11403
|
if (typeof e.upstreamStatus === "number")
|
|
11335
11404
|
return e.upstreamStatus;
|
|
11336
11405
|
const m = /\b(4\d\d|5\d\d)\b/.exec(e.message ?? "");
|
|
@@ -11928,7 +11997,7 @@ var BindAddCommand = class extends M8tCommand {
|
|
|
11928
11997
|
static paths = [["bind", "add"]];
|
|
11929
11998
|
static usage = Command2.Usage({
|
|
11930
11999
|
description: "Create a channel\u2192worker binding with bot token storage.",
|
|
11931
|
-
details: "Stores the bot token in Key Vault and writes a Bindings table row. Returns the webhook URL the channel platform should call.
|
|
12000
|
+
details: "Stores the bot token in Key Vault and writes a Bindings table row. Returns the webhook URL the channel platform should call. Channels without a registered adapter (Slack, Teams) fail with 'no_adapter_registered'.",
|
|
11932
12001
|
examples: [
|
|
11933
12002
|
[
|
|
11934
12003
|
"Add a Telegram binding for the cmo worker",
|
|
@@ -13897,30 +13966,7 @@ function hasA2aSnippet(instructions) {
|
|
|
13897
13966
|
|
|
13898
13967
|
// src/lib/brain-link.ts
|
|
13899
13968
|
init_foundry_agent_get();
|
|
13900
|
-
|
|
13901
|
-
// src/lib/foundry-agent-version.ts
|
|
13902
|
-
init_errors();
|
|
13903
|
-
var FOUNDRY_DATA_SCOPE = "https://ai.azure.com/.default";
|
|
13904
|
-
async function createAgentVersion(args, fetchImpl = fetch) {
|
|
13905
|
-
const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
|
|
13906
|
-
if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
|
|
13907
|
-
const url = `${args.projectEndpoint}/agents/${args.agentName}/versions?api-version=v1`;
|
|
13908
|
-
const res = await fetchImpl(url, {
|
|
13909
|
-
method: "POST",
|
|
13910
|
-
headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} },
|
|
13911
|
-
body: JSON.stringify({ definition: args.definition, metadata: args.metadata })
|
|
13912
|
-
});
|
|
13913
|
-
if (!res.ok) {
|
|
13914
|
-
const text = await res.text();
|
|
13915
|
-
throw new LocalCliError({ code: "AGENT_CREATE_VERSION_FAILED", message: `POST ${url}: HTTP ${res.status.toString()}
|
|
13916
|
-
${text.slice(0, 500)}` });
|
|
13917
|
-
}
|
|
13918
|
-
const data = await res.json();
|
|
13919
|
-
if (!data.version) throw new LocalCliError({ code: "AGENT_CREATE_VERSION_NO_VERSION", message: "createVersion returned no version" });
|
|
13920
|
-
return data.version;
|
|
13921
|
-
}
|
|
13922
|
-
|
|
13923
|
-
// src/lib/brain-link.ts
|
|
13969
|
+
init_foundry_agent_version();
|
|
13924
13970
|
init_brain_yaml_mirror();
|
|
13925
13971
|
var FOUNDRY_ARM_API = "2025-04-01-preview";
|
|
13926
13972
|
var ARM_SCOPE4 = "https://management.azure.com/.default";
|
|
@@ -14111,7 +14157,7 @@ ${text.slice(0, 300)}`
|
|
|
14111
14157
|
target: MCP_SERVER_URL,
|
|
14112
14158
|
isSharedToAll: false,
|
|
14113
14159
|
credentials: { keys: { Authorization: "Bearer placeholder-will-be-rotated" } },
|
|
14114
|
-
metadata: { managedBy: "m8t-brain-
|
|
14160
|
+
metadata: { managedBy: "m8t-brain-link" }
|
|
14115
14161
|
}
|
|
14116
14162
|
});
|
|
14117
14163
|
const putRes = await fetch(url, {
|
|
@@ -14947,7 +14993,7 @@ var BrainLinkCommand = class extends M8tCommand {
|
|
|
14947
14993
|
static paths = [["brain", "link"]];
|
|
14948
14994
|
static usage = Command19.Usage({
|
|
14949
14995
|
description: "Link a worker to an existing brain repo. Idempotent.",
|
|
14950
|
-
details: "Installs the GitHub App on <repo> (browser click + poll), mints an installation token + PATCHes the Foundry connection, deploys a new agent version with the brain loader + tools[type:mcp] + metadata.brain JSON string, and pushes .m8t/brain.yaml to the repo.
|
|
14996
|
+
details: "Installs the GitHub App on <repo> (browser click + poll), mints an installation token + PATCHes the Foundry connection, deploys a new agent version with the brain loader + tools[type:mcp] + metadata.brain JSON string, and pushes .m8t/brain.yaml to the repo. PAT mode keeps working untouched. --force re-runs the full link cascade (loader re-render + createVersion + brain.yaml mirror) even when metadata.brain already matches \u2014 useful to rescue a stuck or stale link.",
|
|
14951
14997
|
examples: [
|
|
14952
14998
|
["Link cmo to orkeren21/cmo-brain", "$0 brain link cmo --repo orkeren21/cmo-brain"],
|
|
14953
14999
|
["Force a re-link (re-render brain loader + new agent version, even if already linked)", "$0 brain link cmo --repo orkeren21/cmo-brain --force"]
|
|
@@ -15222,8 +15268,8 @@ import * as fs12 from "fs";
|
|
|
15222
15268
|
import * as os5 from "os";
|
|
15223
15269
|
import * as path11 from "path";
|
|
15224
15270
|
init_foundry_agent_get();
|
|
15271
|
+
init_foundry_agent_version();
|
|
15225
15272
|
var FOUNDRY_ARM_API2 = "2025-04-01-preview";
|
|
15226
|
-
var FOUNDRY_DATA_SCOPE2 = "https://ai.azure.com/.default";
|
|
15227
15273
|
var ARM_SCOPE5 = "https://management.azure.com/.default";
|
|
15228
15274
|
var GITHUB_APP_API = "https://api.github.com";
|
|
15229
15275
|
async function unlinkBrain(args) {
|
|
@@ -15309,10 +15355,6 @@ function parseBrainMeta(raw) {
|
|
|
15309
15355
|
}
|
|
15310
15356
|
}
|
|
15311
15357
|
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
15358
|
const otherTools = (args.currentDefinition.tools ?? []).filter(
|
|
15317
15359
|
(t) => !(t.type === "mcp" && t.server_label === "brain")
|
|
15318
15360
|
);
|
|
@@ -15325,28 +15367,13 @@ async function createBrainStrippedVersion(args) {
|
|
|
15325
15367
|
...args.currentMetadata,
|
|
15326
15368
|
brain: ""
|
|
15327
15369
|
};
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15370
|
+
return createAgentVersion({
|
|
15371
|
+
credential: args.credential,
|
|
15372
|
+
projectEndpoint: args.projectEndpoint,
|
|
15373
|
+
agentName: args.agentName,
|
|
15374
|
+
definition,
|
|
15375
|
+
metadata
|
|
15333
15376
|
});
|
|
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
15377
|
}
|
|
15351
15378
|
async function deleteConnection(args) {
|
|
15352
15379
|
const armToken = await args.credential.getToken(ARM_SCOPE5);
|
|
@@ -15687,6 +15714,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
15687
15714
|
// src/lib/a2a-enable.ts
|
|
15688
15715
|
init_errors();
|
|
15689
15716
|
init_foundry_agent_get();
|
|
15717
|
+
init_foundry_agent_version();
|
|
15690
15718
|
import { randomBytes as randomBytes2, createHash } from "crypto";
|
|
15691
15719
|
|
|
15692
15720
|
// src/lib/data-plane-ready.ts
|
|
@@ -18399,6 +18427,7 @@ async function runBicepDeployment(opts) {
|
|
|
18399
18427
|
|
|
18400
18428
|
// src/lib/platform-converge.ts
|
|
18401
18429
|
init_foundry_agent_get();
|
|
18430
|
+
init_foundry_agent_version();
|
|
18402
18431
|
init_errors();
|
|
18403
18432
|
|
|
18404
18433
|
// src/lib/persona-compose.ts
|
|
@@ -20049,7 +20078,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
20049
20078
|
static usage = Command33.Usage({
|
|
20050
20079
|
category: "Platform",
|
|
20051
20080
|
description: "Headless updater-job driver: claim a pending apply-request and converge the platform to it.",
|
|
20052
|
-
details: "The Managed-Identity-authenticated entry point run by the updater Container Apps job. One invocation is one tick: it claims the pending apply-request row (if any), refuses a downgrade against the installed stamp, self-fetches and validates the target manifest, self-updates its own engine image when the manifest requires a newer CLI, then drives the
|
|
20081
|
+
details: "The Managed-Identity-authenticated entry point run by the updater Container Apps job. One invocation is one tick: it claims the pending apply-request row (if any), refuses a downgrade against the installed stamp, self-fetches and validates the target manifest, self-updates its own engine image when the manifest requires a newer CLI, then drives the converge engine behind the health-gate + auto-rollback. Reads its subscription/RG/endpoint/channel from env (MI_CLIENT_ID, SUBSCRIPTION_ID, RESOURCE_GROUP, FOUNDRY_ENDPOINT, M8T_UPDATE_CHANNEL_URL). Not intended for interactive use \u2014 the founder-facing path is 'm8t platform update'."
|
|
20053
20082
|
});
|
|
20054
20083
|
async executeCommand() {
|
|
20055
20084
|
const ctx = resolveHeadlessContextFromEnv(process.env);
|
|
@@ -21335,7 +21364,7 @@ function parseVerdict(stdout) {
|
|
|
21335
21364
|
var EvalSkillCommand = class extends M8tCommand {
|
|
21336
21365
|
static paths = [["eval", "skill"]];
|
|
21337
21366
|
static usage = Command38.Usage({
|
|
21338
|
-
description: "Vet one inbox skill candidate
|
|
21367
|
+
description: "Vet one inbox skill candidate: promote / reject / needs_review. Shells out to the Python `brain-eval` core (override its path with $BRAIN_EVAL_BIN)."
|
|
21339
21368
|
});
|
|
21340
21369
|
candidate = Option35.String();
|
|
21341
21370
|
skillsDir = Option35.String("--skills-dir");
|
|
@@ -21423,7 +21452,7 @@ function parseArmToken(tok, opts) {
|
|
|
21423
21452
|
if (!opts.allowStub) {
|
|
21424
21453
|
throw new LocalCliError({
|
|
21425
21454
|
code: "USAGE",
|
|
21426
|
-
message: `imported
|
|
21455
|
+
message: `imported arms are stub-only (a one-file placeholder) \u2014 pass --allow-stub to run one anyway, or use live/off/pinned for a real scored run (got '${tok}')`
|
|
21427
21456
|
});
|
|
21428
21457
|
}
|
|
21429
21458
|
return { state: { imported: ref }, profile };
|
|
@@ -21531,7 +21560,7 @@ function resolveJudgeDeployment(flag, env) {
|
|
|
21531
21560
|
if (typeof fromEnv === "string" && fromEnv.length > 0) return { deployment: fromEnv };
|
|
21532
21561
|
return {
|
|
21533
21562
|
deployment: "gpt-5-mini",
|
|
21534
|
-
warning: "no --deployment and no $EXAM_JUDGE_DEPLOYMENT \u2014 falling back to gpt-5-mini as the judge. Name the DISTINCT judge deployment before any blessing/calibration run
|
|
21563
|
+
warning: "no --deployment and no $EXAM_JUDGE_DEPLOYMENT \u2014 falling back to gpt-5-mini as the judge. Name the DISTINCT judge deployment before any blessing/calibration run."
|
|
21535
21564
|
};
|
|
21536
21565
|
}
|
|
21537
21566
|
var BRAIN_REPO_MARKERS = ["m8t-labs/", "/azure-advisor-brain", "/stacey-brain", "exam-arm-"];
|
|
@@ -21539,7 +21568,7 @@ function assertOutNotInBrainRepo(out) {
|
|
|
21539
21568
|
if (BRAIN_REPO_MARKERS.some((m) => out.includes(m))) {
|
|
21540
21569
|
throw new LocalCliError({
|
|
21541
21570
|
code: "USAGE",
|
|
21542
|
-
message: `--out '${out}' is under a brain repo \u2014 reports MUST live in the referee home (Law-1 guard
|
|
21571
|
+
message: `--out '${out}' is under a brain repo \u2014 reports MUST live in the referee home (Law-1 guard)`
|
|
21543
21572
|
});
|
|
21544
21573
|
}
|
|
21545
21574
|
}
|
|
@@ -21631,7 +21660,7 @@ function buildPlan(args) {
|
|
|
21631
21660
|
var EvalExamCommand = class extends M8tCommand {
|
|
21632
21661
|
static paths = [["eval", "exam"]];
|
|
21633
21662
|
static usage = Command39.Usage({
|
|
21634
|
-
description: "Run a brain exam
|
|
21663
|
+
description: "Run a brain exam: impact A/B + dream-delta. Resolves the plan, then shells out to the Python `brain-exam` orchestrator (override its path with $BRAIN_EXAM_BIN). Renders an ExamVerdict: three-valued verdict + power note + per-task flips."
|
|
21635
21664
|
});
|
|
21636
21665
|
worker = Option36.String();
|
|
21637
21666
|
arms = Option36.String("--arms");
|
|
@@ -21704,7 +21733,7 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
21704
21733
|
this.context.stdout.write(renderJson(plan) + "\n");
|
|
21705
21734
|
this.context.stdout.write(
|
|
21706
21735
|
`
|
|
21707
|
-
(dry-run) ~${String(arms.length)} arms x n=${String(reps)}; judge grading fan-out is the cost driver \u2014 confirm the run sits inside the ~150-550 judge-call band before driving workers
|
|
21736
|
+
(dry-run) ~${String(arms.length)} arms x n=${String(reps)}; judge grading fan-out is the cost driver \u2014 confirm the run sits inside the ~150-550 judge-call band before driving workers.
|
|
21708
21737
|
`
|
|
21709
21738
|
);
|
|
21710
21739
|
return 0;
|
|
@@ -24307,7 +24336,7 @@ function forcePolicyQuarantine(delta) {
|
|
|
24307
24336
|
return {
|
|
24308
24337
|
verb: "quarantine",
|
|
24309
24338
|
slug: slugify(delta.title),
|
|
24310
|
-
reason: "code-side standing-policy/auto-approval/privilege assertion
|
|
24339
|
+
reason: "code-side standing-policy/auto-approval/privilege assertion",
|
|
24311
24340
|
body: delta.body,
|
|
24312
24341
|
evidence: delta.evidence
|
|
24313
24342
|
};
|
|
@@ -25668,7 +25697,7 @@ var EVENT_TIERS = {
|
|
|
25668
25697
|
var EVENT_ENUM = Object.keys(EVENT_TIERS);
|
|
25669
25698
|
|
|
25670
25699
|
// ../../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
|
|
25700
|
+
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
25701
|
|
|
25673
25702
|
// ../../packages/telemetry-contract/src/endpoints.ts
|
|
25674
25703
|
var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
|
|
@@ -25680,6 +25709,17 @@ function ingestUrl(path38) {
|
|
|
25680
25709
|
// src/commands/bootstrap/preflight.ts
|
|
25681
25710
|
init_errors();
|
|
25682
25711
|
|
|
25712
|
+
// src/lib/spend-disclosure.ts
|
|
25713
|
+
var SPEND_DISCLOSURE = [
|
|
25714
|
+
"You are about to install the m8t platform.",
|
|
25715
|
+
"",
|
|
25716
|
+
" - It installs into YOUR Azure subscription and YOUR GitHub account or organization.",
|
|
25717
|
+
" - The Azure resources it creates bill to your Azure account - this uses your budget.",
|
|
25718
|
+
" - With your workers installed, you can ask Azzy what you're spending at any time,",
|
|
25719
|
+
" and you can turn on a cost report by email every two weeks.",
|
|
25720
|
+
" - You can remove it later - see guides/uninstall.md."
|
|
25721
|
+
].join("\n");
|
|
25722
|
+
|
|
25683
25723
|
// src/lib/bootstrap-preflight.ts
|
|
25684
25724
|
var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
|
|
25685
25725
|
async function checkSubScopeAdmin(callerObjectId, subscriptionId) {
|
|
@@ -25766,6 +25806,7 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
25766
25806
|
const account = await getAzAccount();
|
|
25767
25807
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
25768
25808
|
const oid = await getCallerObjectId();
|
|
25809
|
+
this.context.stdout.write(SPEND_DISCLOSURE + "\n\n");
|
|
25769
25810
|
this.context.stdout.write(buildPreflightBanner({ upn: account.upn, tenantId: account.tenantId, subscriptionId }) + "\n");
|
|
25770
25811
|
this.context.stdout.write(`
|
|
25771
25812
|
${colors.dim(DISCLOSURE_TIER1)}
|
|
@@ -25928,6 +25969,7 @@ function buildAciCreateArgs(s) {
|
|
|
25928
25969
|
if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
|
|
25929
25970
|
if (s.updateChannelUrl) env.push(`M8T_UPDATE_CHANNEL_URL=${s.updateChannelUrl}`);
|
|
25930
25971
|
if (s.enrollContactEmail) env.push(`M8T_ENROLL_CONTACT_EMAIL=${s.enrollContactEmail}`);
|
|
25972
|
+
if (s.enrollCompany) env.push(`M8T_ENROLL_COMPANY=${s.enrollCompany}`);
|
|
25931
25973
|
if (s.githubApp) {
|
|
25932
25974
|
const g = s.githubApp;
|
|
25933
25975
|
env.push(
|
|
@@ -26075,10 +26117,95 @@ async function writeBootstrapState(state, home = os13.homedir()) {
|
|
|
26075
26117
|
await fs29.rename(tmp, statePath(home));
|
|
26076
26118
|
}
|
|
26077
26119
|
|
|
26120
|
+
// src/lib/bootstrap-occupancy.ts
|
|
26121
|
+
init_errors();
|
|
26122
|
+
var SAMPLE_LIMIT = 3;
|
|
26123
|
+
var M8T_NAME_RE = /^m8t/i;
|
|
26124
|
+
async function checkTargetRgOccupancy(opts) {
|
|
26125
|
+
let existsRaw;
|
|
26126
|
+
try {
|
|
26127
|
+
existsRaw = await runAz([
|
|
26128
|
+
"group",
|
|
26129
|
+
"exists",
|
|
26130
|
+
"--name",
|
|
26131
|
+
opts.resourceGroup,
|
|
26132
|
+
"--subscription",
|
|
26133
|
+
opts.subscriptionId,
|
|
26134
|
+
"-o",
|
|
26135
|
+
"tsv"
|
|
26136
|
+
]);
|
|
26137
|
+
} catch (cause) {
|
|
26138
|
+
throw unverified(opts.resourceGroup, cause);
|
|
26139
|
+
}
|
|
26140
|
+
const exists2 = existsRaw.trim().toLowerCase();
|
|
26141
|
+
if (exists2 === "false") return { state: "absent", found: [], total: 0, m8tShaped: false };
|
|
26142
|
+
if (exists2 !== "true") {
|
|
26143
|
+
throw unverified(opts.resourceGroup, new Error(`unexpected 'az group exists' output: ${exists2}`));
|
|
26144
|
+
}
|
|
26145
|
+
let names;
|
|
26146
|
+
try {
|
|
26147
|
+
const listRaw = await runAz([
|
|
26148
|
+
"resource",
|
|
26149
|
+
"list",
|
|
26150
|
+
"--resource-group",
|
|
26151
|
+
opts.resourceGroup,
|
|
26152
|
+
"--subscription",
|
|
26153
|
+
opts.subscriptionId,
|
|
26154
|
+
"--query",
|
|
26155
|
+
"[].name",
|
|
26156
|
+
"-o",
|
|
26157
|
+
"json"
|
|
26158
|
+
]);
|
|
26159
|
+
names = JSON.parse(listRaw);
|
|
26160
|
+
} catch (cause) {
|
|
26161
|
+
throw unverified(opts.resourceGroup, cause);
|
|
26162
|
+
}
|
|
26163
|
+
if (!Array.isArray(names)) {
|
|
26164
|
+
throw unverified(opts.resourceGroup, new Error("resource listing was not an array"));
|
|
26165
|
+
}
|
|
26166
|
+
if (names.length === 0) return { state: "empty", found: [], total: 0, m8tShaped: false };
|
|
26167
|
+
return {
|
|
26168
|
+
state: "occupied",
|
|
26169
|
+
found: names.slice(0, SAMPLE_LIMIT),
|
|
26170
|
+
total: names.length,
|
|
26171
|
+
// Computed BEFORE the cap: an m8t-named resource sorting past the sample
|
|
26172
|
+
// must still escalate the wording.
|
|
26173
|
+
m8tShaped: names.some((n) => M8T_NAME_RE.test(n))
|
|
26174
|
+
};
|
|
26175
|
+
}
|
|
26176
|
+
function unverified(resourceGroup, cause) {
|
|
26177
|
+
return new LocalCliError({
|
|
26178
|
+
code: "BOOTSTRAP_OCCUPANCY_UNVERIFIED",
|
|
26179
|
+
message: `Could not verify whether resource group '${resourceGroup}' is empty.`,
|
|
26180
|
+
hint: "Check your connection and 'az login', then retry.",
|
|
26181
|
+
cause
|
|
26182
|
+
});
|
|
26183
|
+
}
|
|
26184
|
+
function buildOccupiedRefusal(args) {
|
|
26185
|
+
const { resourceGroup: rg, subscriptionId, location, occupancy: o } = args;
|
|
26186
|
+
const hidden = o.total - o.found.length;
|
|
26187
|
+
const more = hidden > 0 ? ` (+${String(hidden)} more)` : "";
|
|
26188
|
+
const risk = o.m8tShaped ? "Installing into a non-empty resource group risks overwriting what's already there.\n This looks like an existing m8t deployment; installing here would overwrite its identity." : "Installing into a non-empty resource group risks overwriting what's already there.";
|
|
26189
|
+
return [
|
|
26190
|
+
"",
|
|
26191
|
+
"\u26D4 CANNOT PROCEED",
|
|
26192
|
+
` Resource group '${rg}' in subscription ${subscriptionId} is not empty \u2014 it holds`,
|
|
26193
|
+
` ${String(o.total)} resources, including: ${o.found.join(", ")}${more}.`,
|
|
26194
|
+
` ${risk}`,
|
|
26195
|
+
" remedy: pick an empty resource group \u2014",
|
|
26196
|
+
` m8t bootstrap launch --location ${location} --resource-group <new-rg>`,
|
|
26197
|
+
" or, if you really mean to install into this one:",
|
|
26198
|
+
` m8t bootstrap launch --location ${location} --resource-group ${rg} --reinstall-into ${rg}`,
|
|
26199
|
+
" Nothing has been created.",
|
|
26200
|
+
"",
|
|
26201
|
+
""
|
|
26202
|
+
].join("\n");
|
|
26203
|
+
}
|
|
26204
|
+
|
|
26078
26205
|
// src/commands/bootstrap/launch.ts
|
|
26079
26206
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
26080
26207
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
26081
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
26208
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.42";
|
|
26082
26209
|
var ACI_NAME = "m8t-installer";
|
|
26083
26210
|
var MI_NAME = "m8t-installer-mi";
|
|
26084
26211
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
@@ -26090,7 +26217,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26090
26217
|
["Launch in eastus2", "$0 bootstrap launch --location eastus2"],
|
|
26091
26218
|
["BYO app registration", "$0 bootstrap launch --location eastus2 --client-id <appId>"],
|
|
26092
26219
|
["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"]
|
|
26220
|
+
["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.33"],
|
|
26221
|
+
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
26094
26222
|
]
|
|
26095
26223
|
});
|
|
26096
26224
|
location = Option47.String("--location");
|
|
@@ -26104,12 +26232,20 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26104
26232
|
installerImage = Option47.String("--installer-image");
|
|
26105
26233
|
gatewayImageRef = Option47.String("--gateway-image-ref");
|
|
26106
26234
|
githubAppCreds = Option47.String("--github-app-creds");
|
|
26235
|
+
contactEmail = Option47.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
26236
|
+
company = Option47.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
26237
|
+
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
26238
|
+
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
26239
|
+
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
26240
|
+
// the target; --resource-group is what CHOOSES it.
|
|
26241
|
+
reinstallInto = Option47.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
|
|
26107
26242
|
async executeCommand() {
|
|
26108
26243
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
26109
26244
|
if (!location) {
|
|
26110
26245
|
throw new LocalCliError({ code: "USAGE", message: "--location is required.", hint: "Example: m8t bootstrap launch --location eastus2" });
|
|
26111
26246
|
}
|
|
26112
|
-
const
|
|
26247
|
+
const resourceGroupOpt = typeof this.resourceGroup === "string" ? this.resourceGroup : void 0;
|
|
26248
|
+
const resourceGroup = resourceGroupOpt ?? DEFAULT_RG;
|
|
26113
26249
|
const clientIdOpt = typeof this.clientId === "string" ? this.clientId : void 0;
|
|
26114
26250
|
const installerTag = (typeof this.installerTag === "string" ? this.installerTag : void 0) ?? DEFAULT_INSTALLER_TAG;
|
|
26115
26251
|
const installerImageOverride = typeof this.installerImage === "string" ? this.installerImage : void 0;
|
|
@@ -26119,6 +26255,28 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26119
26255
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
26120
26256
|
const out = (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
26121
26257
|
`);
|
|
26258
|
+
const reinstallInto = typeof this.reinstallInto === "string" ? this.reinstallInto : void 0;
|
|
26259
|
+
if (reinstallInto !== void 0 && reinstallInto.toLowerCase() !== resourceGroup.toLowerCase()) {
|
|
26260
|
+
const targetExplanation = resourceGroupOpt === void 0 ? `you did not pass --resource-group, so the target is the default '${DEFAULT_RG}'` : `the target resource group is '${resourceGroup}'`;
|
|
26261
|
+
throw new LocalCliError({
|
|
26262
|
+
code: "BOOTSTRAP_REINSTALL_TARGET_MISMATCH",
|
|
26263
|
+
message: `--reinstall-into '${reinstallInto}' does not match \u2014 ${targetExplanation}.`,
|
|
26264
|
+
hint: `--reinstall-into authorizes the target; it does not choose it. Re-run with --resource-group ${reinstallInto} on its own first \u2014 the guard will show you what that group holds.`
|
|
26265
|
+
});
|
|
26266
|
+
}
|
|
26267
|
+
const occupancy = await checkTargetRgOccupancy({ resourceGroup, subscriptionId });
|
|
26268
|
+
if (occupancy.state === "occupied" && reinstallInto === void 0) {
|
|
26269
|
+
this.context.stderr.write(buildOccupiedRefusal({ resourceGroup, subscriptionId, location, occupancy }));
|
|
26270
|
+
throw new LocalCliError({
|
|
26271
|
+
code: "BOOTSTRAP_TARGET_OCCUPIED",
|
|
26272
|
+
message: `Resource group '${resourceGroup}' already holds ${String(occupancy.total)} resources.`
|
|
26273
|
+
});
|
|
26274
|
+
}
|
|
26275
|
+
if (this.context.env.M8T_HALT_AFTER_OCCUPANCY === "1") {
|
|
26276
|
+
this.context.stdout.write(`halt: occupancy check passed (${occupancy.state}) for ${resourceGroup}
|
|
26277
|
+
`);
|
|
26278
|
+
return 0;
|
|
26279
|
+
}
|
|
26122
26280
|
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path32.join(os14.homedir(), ".m8t", "github-app.json");
|
|
26123
26281
|
let githubApp;
|
|
26124
26282
|
if (fs30.existsSync(credsPath)) {
|
|
@@ -26195,7 +26353,11 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26195
26353
|
gatewayImageRef,
|
|
26196
26354
|
foundryTracing: "skip",
|
|
26197
26355
|
githubApp,
|
|
26198
|
-
|
|
26356
|
+
// Opt-in only. The signed-in UPN was previously seeded here automatically;
|
|
26357
|
+
// an installation is identified by its random instance id, and contact
|
|
26358
|
+
// details are sent only when the operator asks for them.
|
|
26359
|
+
...typeof this.contactEmail === "string" ? { enrollContactEmail: this.contactEmail } : {},
|
|
26360
|
+
...typeof this.company === "string" ? { enrollCompany: this.company } : {},
|
|
26199
26361
|
...process.env.M8T_UPDATE_CHANNEL_URL ? { updateChannelUrl: process.env.M8T_UPDATE_CHANNEL_URL } : {}
|
|
26200
26362
|
});
|
|
26201
26363
|
this.context.stdout.write(
|
|
@@ -28040,13 +28202,13 @@ async function enroll(args) {
|
|
|
28040
28202
|
const url = args.baseUrl ? `${args.baseUrl.replace(/\/+$/, "")}/api/ingest/enroll` : ingestUrl("/api/ingest/enroll");
|
|
28041
28203
|
const retryDelayMs = args.retryDelayMs ?? 1e3;
|
|
28042
28204
|
const token = await (args.tokenImpl ?? defaultToken)().catch(() => null);
|
|
28043
|
-
const
|
|
28044
|
-
|
|
28045
|
-
|
|
28046
|
-
|
|
28047
|
-
|
|
28048
|
-
|
|
28049
|
-
|
|
28205
|
+
const payload = { schemaVersion: 1 };
|
|
28206
|
+
const company = args.company?.trim();
|
|
28207
|
+
if (company) payload.company = company;
|
|
28208
|
+
const contactEmail = args.contactEmail?.trim();
|
|
28209
|
+
if (contactEmail) payload.contactEmail = contactEmail;
|
|
28210
|
+
payload.proof = token ? { type: "entra-jwt", token } : null;
|
|
28211
|
+
const body = JSON.stringify(payload);
|
|
28050
28212
|
let res;
|
|
28051
28213
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
28052
28214
|
try {
|
|
@@ -28099,19 +28261,22 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
28099
28261
|
static paths = [["telemetry", "enroll"]];
|
|
28100
28262
|
static usage = Command56.Usage({
|
|
28101
28263
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
28102
|
-
details: "
|
|
28103
|
-
examples: [
|
|
28264
|
+
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.",
|
|
28265
|
+
examples: [
|
|
28266
|
+
["Enroll", "$0 telemetry enroll"],
|
|
28267
|
+
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
28268
|
+
]
|
|
28104
28269
|
});
|
|
28105
|
-
company = Option53.String("--company", { description: "
|
|
28106
|
-
contactEmail = Option53.String("--contact-email", { description: "
|
|
28107
|
-
subscription = Option53.String("--subscription", { description: "Azure subscription id
|
|
28270
|
+
company = Option53.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
28271
|
+
contactEmail = Option53.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
28272
|
+
subscription = Option53.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
28108
28273
|
resourceGroup = Option53.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
28109
|
-
force = Option53.Boolean("--force", false, { description: "
|
|
28274
|
+
force = Option53.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
28110
28275
|
async executeCommand() {
|
|
28111
28276
|
const account = await getAzAccount();
|
|
28112
28277
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
28113
|
-
const contactEmail =
|
|
28114
|
-
const company =
|
|
28278
|
+
const contactEmail = typeof this.contactEmail === "string" ? this.contactEmail : void 0;
|
|
28279
|
+
const company = typeof this.company === "string" ? this.company : void 0;
|
|
28115
28280
|
const d = await discoverGateway({
|
|
28116
28281
|
subscriptionId,
|
|
28117
28282
|
interactive: false,
|
|
@@ -28127,14 +28292,18 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
28127
28292
|
existing = "";
|
|
28128
28293
|
}
|
|
28129
28294
|
if (existing.trim()) {
|
|
28295
|
+
const wantedContact = contactEmail !== void 0 || company !== void 0;
|
|
28130
28296
|
throw new LocalCliError({
|
|
28131
28297
|
code: "TELEMETRY_ALREADY_ENROLLED",
|
|
28132
|
-
message: "This installation is already enrolled.",
|
|
28133
|
-
hint: "
|
|
28298
|
+
message: wantedContact ? "This installation is already enrolled, so contact details can't be attached from here." : "This installation is already enrolled.",
|
|
28299
|
+
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."
|
|
28134
28300
|
});
|
|
28135
28301
|
}
|
|
28136
28302
|
}
|
|
28137
|
-
const { instanceId, ingestKey } = await enroll({
|
|
28303
|
+
const { instanceId, ingestKey } = await enroll({
|
|
28304
|
+
...company ? { company } : {},
|
|
28305
|
+
...contactEmail ? { contactEmail } : {}
|
|
28306
|
+
});
|
|
28138
28307
|
const keyDir = fs34.mkdtempSync(path37.join(os19.tmpdir(), "m8t-ingest-key-"));
|
|
28139
28308
|
const keyFile = path37.join(keyDir, "key");
|
|
28140
28309
|
try {
|