@m8t-stack/cli 0.2.28 → 0.2.29
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 +164 -38
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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.
|
|
1250
|
+
var CLI_VERSION = "0.2.29";
|
|
1251
1251
|
|
|
1252
1252
|
// src/lib/render-error.ts
|
|
1253
1253
|
init_errors();
|
|
@@ -1442,49 +1442,31 @@ var BACKEND_RULES = [
|
|
|
1442
1442
|
}
|
|
1443
1443
|
];
|
|
1444
1444
|
var LOCAL_RULES = {
|
|
1445
|
-
// Task 12 — foundry-agent-get
|
|
1446
1445
|
AGENT_CREATE_VERSION_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1447
1446
|
AGENT_CREATE_VERSION_NO_VERSION: "Foundry returned 2xx but no version \u2014 likely an API regression. Re-try; if it persists, file an issue.",
|
|
1448
1447
|
AGENT_GET_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1449
1448
|
AGENT_NOT_FOUND: "Use `m8t brain create <name>` to create the worker first, OR run `m8t deploy <name>` to register an existing agent.",
|
|
1450
|
-
// Task 14 — brain-unlink
|
|
1451
1449
|
AGENT_REDEPLOY_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1452
1450
|
AGENT_REDEPLOY_NO_VERSION: "Foundry returned 2xx but no version. Re-try; if it persists, file an issue.",
|
|
1453
|
-
// Task 10/13/14
|
|
1454
1451
|
AGENT_YAML_NOT_FOUND: "Deploy the worker via `m8t deploy <name>` or the architect first.",
|
|
1455
|
-
// Task 16 — brain link command
|
|
1456
1452
|
APP_HEALTH_FAILED: "Run `m8t brain check-app` for details, then re-run this command.",
|
|
1457
|
-
// Task 14 — brain-unlink
|
|
1458
1453
|
APP_UNINSTALL_FAILED: "GitHub App-uninstall DELETE failed. Re-run with `--keep-app-install` if you want to skip this step.",
|
|
1459
|
-
// Task 13 — brain-link
|
|
1460
1454
|
ARM_AUTH: "Run `az login` and retry.",
|
|
1461
1455
|
// F2 smoke fix — m8t brain create: stageAsGitRepo step
|
|
1462
1456
|
BRAIN_GIT_INIT_FAILED: "If git is not installed, install it. Otherwise re-run with --verbose.",
|
|
1463
|
-
// Task 14 — brain-unlink
|
|
1464
1457
|
BRAIN_NOT_LINKED: "The worker has no brain link. Use `m8t brain link` or `m8t brain create` first.",
|
|
1465
|
-
// Task 13 — brain-link
|
|
1466
1458
|
BRAIN_YAML_MIRROR_FAILED: "GitHub Contents API PUT failed. Verify the App has `contents: write` permission on the repo.",
|
|
1467
|
-
// Task 13 — brain-link
|
|
1468
1459
|
CONN_CREATE_FAILED: "Foundry connection PUT failed. Verify Cognitive Services Contributor role on the project.",
|
|
1469
|
-
// Task 14 — brain-unlink
|
|
1470
1460
|
CONN_DELETE_FAILED: "Foundry connection DELETE failed. Verify your ARM subscription has Contributor on the project.",
|
|
1471
|
-
// Task 13 — brain-link
|
|
1472
1461
|
CONN_GET_FAILED: "Foundry connection GET failed. Verify your ARM subscription has access to the project.",
|
|
1473
|
-
//
|
|
1462
|
+
// rename pending: should be FOUNDRY_AUTH_FAILED
|
|
1474
1463
|
FOUNDRY_AUTH: "Run `az login` and retry.",
|
|
1475
|
-
// Task 12 — foundry-agent-get
|
|
1476
1464
|
FOUNDRY_AUTH_FAILED: "Run `az login` and retry.",
|
|
1477
|
-
// Task 15+16
|
|
1478
1465
|
KV_URI_NOT_FOUND: "Pass `--kv-uri` or set `AZURE_KEYVAULT_URI` / `KEYVAULT_URI`.",
|
|
1479
|
-
// Task 13 — brain-link
|
|
1480
1466
|
LINK_NO_INSTALLATION_ID: "Internal error \u2014 the CLI command must poll for the installation id before calling linkBrain. Report at https://github.com/m8t/m8t/issues.",
|
|
1481
|
-
// Task 16 — brain link command
|
|
1482
1467
|
M8T_REPO_ROOT_MISSING: "Re-run `m8t install` from a fresh m8t checkout \u2014 the post-install hook writes ~/.m8t/repo-root.",
|
|
1483
|
-
// Task 14 — persona-render
|
|
1484
1468
|
PERSONA_PATH_NOT_ABSOLUTE: "The persona path in ~/.m8t/foundry/<worker>.yaml must be absolute. Re-deploy the worker via the architect.",
|
|
1485
|
-
// Task 9 — persona-render
|
|
1486
1469
|
PERSONA_PLACEHOLDER_UNSATISFIED: "Re-deploy the worker via the architect to repopulate fillableFieldValues, OR edit ~/.m8t/foundry/<worker>.yaml manually.",
|
|
1487
|
-
// Task 9 — persona-render
|
|
1488
1470
|
PERSONA_READ_FAILED: "Verify the persona path in ~/.m8t/foundry/<worker>.yaml; the architect should have populated it at deploy time.",
|
|
1489
1471
|
// ── pre-existing entries ──────────────────────────────────────────────────
|
|
1490
1472
|
AUTH_NOT_SIGNED_IN: "you are not signed in to Azure \u2014 run 'az login' first.",
|
|
@@ -11524,7 +11506,7 @@ function redactAzArgs(args) {
|
|
|
11524
11506
|
}
|
|
11525
11507
|
function runAz(args) {
|
|
11526
11508
|
return new Promise((resolve3, reject) => {
|
|
11527
|
-
const proc = spawn("az", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
11509
|
+
const proc = spawn("az", args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32" });
|
|
11528
11510
|
const out = [];
|
|
11529
11511
|
const err = [];
|
|
11530
11512
|
proc.stdout.on("data", (d) => out.push(d));
|
|
@@ -13391,9 +13373,9 @@ async function probeAppInstallation(args) {
|
|
|
13391
13373
|
${body.slice(0, 300)}`);
|
|
13392
13374
|
}
|
|
13393
13375
|
async function tryOpenUrl(url, exec = defaultGhExec) {
|
|
13394
|
-
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
13376
|
+
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
13395
13377
|
try {
|
|
13396
|
-
await exec(cmd,
|
|
13378
|
+
await exec(cmd, args);
|
|
13397
13379
|
} catch {
|
|
13398
13380
|
}
|
|
13399
13381
|
}
|
|
@@ -13825,7 +13807,7 @@ function renderPersonaBody(personaPath, fillableFieldValues, instanceName) {
|
|
|
13825
13807
|
message: `Could not read persona file '${personaPath}': ${e.message}`
|
|
13826
13808
|
});
|
|
13827
13809
|
}
|
|
13828
|
-
const fmMatch = /^---\n[\s\S]*?\n
|
|
13810
|
+
const fmMatch = /^---\r?\n[\s\S]*?\r?\n---(?:\r?\n)+/.exec(raw);
|
|
13829
13811
|
const body = fmMatch ? raw.slice(fmMatch[0].length) : raw;
|
|
13830
13812
|
let rendered = body;
|
|
13831
13813
|
for (const [name, value] of Object.entries(fillableFieldValues)) {
|
|
@@ -15547,7 +15529,7 @@ async function deployPromptAdvisor(args) {
|
|
|
15547
15529
|
cause: e
|
|
15548
15530
|
});
|
|
15549
15531
|
}
|
|
15550
|
-
const fmMatch = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
15532
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
15551
15533
|
const fm = fmMatch ? parseYaml6(fmMatch[1]) : {};
|
|
15552
15534
|
const model = args.model ?? fm.targets?.foundry?.model;
|
|
15553
15535
|
if (!model) {
|
|
@@ -15823,7 +15805,7 @@ function readPersonaA2aCard(personaPath) {
|
|
|
15823
15805
|
} catch (e) {
|
|
15824
15806
|
throw new LocalCliError({ code: "PERSONA_READ_FAILED", message: `Could not read persona '${personaPath}': ${e.message}` });
|
|
15825
15807
|
}
|
|
15826
|
-
const fm = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
15808
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
15827
15809
|
if (!fm) throw new LocalCliError({ code: "PERSONA_NO_FRONTMATTER", message: `Persona '${personaPath}' has no frontmatter.` });
|
|
15828
15810
|
const front = parseYaml7(fm[1]);
|
|
15829
15811
|
const role = typeof front.role === "string" ? front.role : "";
|
|
@@ -16899,7 +16881,7 @@ function resolvePersona(persona, cwd = process.cwd()) {
|
|
|
16899
16881
|
} catch {
|
|
16900
16882
|
return { persona, personaVersion: null };
|
|
16901
16883
|
}
|
|
16902
|
-
const match = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
16884
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
16903
16885
|
if (!match) return { persona, personaVersion: null };
|
|
16904
16886
|
let fm = null;
|
|
16905
16887
|
try {
|
|
@@ -18727,7 +18709,7 @@ function declaredFillableFields(personaPath) {
|
|
|
18727
18709
|
} catch {
|
|
18728
18710
|
return [];
|
|
18729
18711
|
}
|
|
18730
|
-
const fmMatch = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
18712
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
18731
18713
|
if (!fmMatch) return [];
|
|
18732
18714
|
try {
|
|
18733
18715
|
const fm = parseYaml10(fmMatch[1]);
|
|
@@ -21635,7 +21617,7 @@ init_errors();
|
|
|
21635
21617
|
import { spawn as spawn4 } from "child_process";
|
|
21636
21618
|
function runAzd(args) {
|
|
21637
21619
|
return new Promise((resolve3, reject) => {
|
|
21638
|
-
const proc = spawn4("azd", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
21620
|
+
const proc = spawn4("azd", args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32" });
|
|
21639
21621
|
const out = [];
|
|
21640
21622
|
const err = [];
|
|
21641
21623
|
proc.stdout.on("data", (d) => out.push(d));
|
|
@@ -25370,6 +25352,32 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
25370
25352
|
|
|
25371
25353
|
// src/commands/bootstrap/preflight.ts
|
|
25372
25354
|
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
25355
|
+
|
|
25356
|
+
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
25357
|
+
var EVENT_TIERS = {
|
|
25358
|
+
"enrolled": 1,
|
|
25359
|
+
"install-started": 1,
|
|
25360
|
+
"install-succeeded": 1,
|
|
25361
|
+
"install-failed": 1,
|
|
25362
|
+
"heartbeat": 1,
|
|
25363
|
+
"update-applied": 1,
|
|
25364
|
+
"update-failed": 1,
|
|
25365
|
+
"consent-changed": 1
|
|
25366
|
+
// extended additively for tier-2 usage events: "usage-rollup": 2
|
|
25367
|
+
};
|
|
25368
|
+
var EVENT_ENUM = Object.keys(EVENT_TIERS);
|
|
25369
|
+
|
|
25370
|
+
// ../../packages/telemetry-contract/src/disclosure.ts
|
|
25371
|
+
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.";
|
|
25372
|
+
|
|
25373
|
+
// ../../packages/telemetry-contract/src/endpoints.ts
|
|
25374
|
+
var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
|
|
25375
|
+
function ingestUrl(path37) {
|
|
25376
|
+
const base = INGEST_BASE_URL.replace(/\/+$/, "");
|
|
25377
|
+
return `${base}${path37.startsWith("/") ? path37 : `/${path37}`}`;
|
|
25378
|
+
}
|
|
25379
|
+
|
|
25380
|
+
// src/commands/bootstrap/preflight.ts
|
|
25373
25381
|
init_errors();
|
|
25374
25382
|
|
|
25375
25383
|
// src/lib/bootstrap-preflight.ts
|
|
@@ -25459,6 +25467,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
25459
25467
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
25460
25468
|
const oid = await getCallerObjectId();
|
|
25461
25469
|
this.context.stdout.write(buildPreflightBanner({ upn: account.upn, tenantId: account.tenantId, subscriptionId }) + "\n");
|
|
25470
|
+
this.context.stdout.write(`
|
|
25471
|
+
${colors.dim(DISCLOSURE_TIER1)}
|
|
25472
|
+
`);
|
|
25462
25473
|
this.context.stdout.write("Checking your access\u2026\n");
|
|
25463
25474
|
const sub = await checkSubScopeAdmin(oid, subscriptionId);
|
|
25464
25475
|
this.context.stdout.write(line(sub.ok, `${sub.role ?? "Owner/UAA"} at subscription scope`, "resource + role-assignment rights"));
|
|
@@ -25610,6 +25621,7 @@ function buildAciCreateArgs(s) {
|
|
|
25610
25621
|
];
|
|
25611
25622
|
if (s.gatewayImageRef) env.push(`GATEWAY_IMAGE_REF=${s.gatewayImageRef}`);
|
|
25612
25623
|
if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
|
|
25624
|
+
if (s.enrollContactEmail) env.push(`M8T_ENROLL_CONTACT_EMAIL=${s.enrollContactEmail}`);
|
|
25613
25625
|
if (s.githubApp) {
|
|
25614
25626
|
const g = s.githubApp;
|
|
25615
25627
|
env.push(
|
|
@@ -25760,7 +25772,7 @@ async function writeBootstrapState(state, home = os13.homedir()) {
|
|
|
25760
25772
|
// src/commands/bootstrap/launch.ts
|
|
25761
25773
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
25762
25774
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
25763
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
25775
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.34";
|
|
25764
25776
|
var ACI_NAME = "m8t-installer";
|
|
25765
25777
|
var MI_NAME = "m8t-installer-mi";
|
|
25766
25778
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
@@ -25876,7 +25888,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
25876
25888
|
image: installerImage,
|
|
25877
25889
|
gatewayImageRef,
|
|
25878
25890
|
foundryTracing: "skip",
|
|
25879
|
-
githubApp
|
|
25891
|
+
githubApp,
|
|
25892
|
+
enrollContactEmail: account.upn
|
|
25880
25893
|
});
|
|
25881
25894
|
this.context.stdout.write(
|
|
25882
25895
|
`${colors.success("\u2713")} installer launched in ${colors.field(resourceGroup)} (${location}).
|
|
@@ -26640,7 +26653,7 @@ import * as fs32 from "fs";
|
|
|
26640
26653
|
import * as net from "net";
|
|
26641
26654
|
import * as os17 from "os";
|
|
26642
26655
|
import * as path35 from "path";
|
|
26643
|
-
import { spawn as spawn7 } from "child_process";
|
|
26656
|
+
import { spawn as spawn7, spawnSync as spawnSync6 } from "child_process";
|
|
26644
26657
|
init_errors();
|
|
26645
26658
|
init_rbac();
|
|
26646
26659
|
|
|
@@ -26906,7 +26919,8 @@ async function serveOnboardingUiDetached(args) {
|
|
|
26906
26919
|
detached: true,
|
|
26907
26920
|
stdio: ["ignore", fd, fd],
|
|
26908
26921
|
env: { ...process.env, PORT: args.port },
|
|
26909
|
-
shell
|
|
26922
|
+
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
26923
|
+
shell: process.platform === "win32"
|
|
26910
26924
|
});
|
|
26911
26925
|
fs32.closeSync(fd);
|
|
26912
26926
|
if (child.pid == null) {
|
|
@@ -26946,7 +26960,8 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
26946
26960
|
RELAY_ALLOWED_ORIGINS: "http://localhost:3000",
|
|
26947
26961
|
RELAY_PORT: "8790"
|
|
26948
26962
|
},
|
|
26949
|
-
shell
|
|
26963
|
+
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
26964
|
+
shell: process.platform === "win32"
|
|
26950
26965
|
});
|
|
26951
26966
|
fs32.closeSync(fd);
|
|
26952
26967
|
if (child.pid == null) {
|
|
@@ -26985,10 +27000,14 @@ function stopPidFile(pidPath) {
|
|
|
26985
27000
|
}
|
|
26986
27001
|
const pid = Number(pidStr);
|
|
26987
27002
|
if (!Number.isNaN(pid) && pid > 0) {
|
|
26988
|
-
|
|
26989
|
-
|
|
26990
|
-
}
|
|
26991
|
-
|
|
27003
|
+
if (process.platform === "win32") {
|
|
27004
|
+
spawnSync6("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
27005
|
+
} else {
|
|
27006
|
+
try {
|
|
27007
|
+
process.kill(-pid, "SIGTERM");
|
|
27008
|
+
} catch (e) {
|
|
27009
|
+
if (e.code !== "ESRCH") throw e;
|
|
27010
|
+
}
|
|
26992
27011
|
}
|
|
26993
27012
|
}
|
|
26994
27013
|
try {
|
|
@@ -27240,6 +27259,112 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
27240
27259
|
}
|
|
27241
27260
|
};
|
|
27242
27261
|
|
|
27262
|
+
// src/commands/telemetry/enroll.ts
|
|
27263
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
27264
|
+
init_errors();
|
|
27265
|
+
|
|
27266
|
+
// src/lib/telemetry-enroll.ts
|
|
27267
|
+
init_errors();
|
|
27268
|
+
async function enroll(args) {
|
|
27269
|
+
const f = args.fetchImpl ?? fetch;
|
|
27270
|
+
const url = args.baseUrl ? `${args.baseUrl.replace(/\/+$/, "")}/api/ingest/enroll` : ingestUrl("/api/ingest/enroll");
|
|
27271
|
+
let res;
|
|
27272
|
+
try {
|
|
27273
|
+
res = await f(url, {
|
|
27274
|
+
method: "POST",
|
|
27275
|
+
headers: { "content-type": "application/json" },
|
|
27276
|
+
body: JSON.stringify({ schemaVersion: 1, company: args.company, contactEmail: args.contactEmail, subscriptionId: args.subscriptionId, proof: null })
|
|
27277
|
+
});
|
|
27278
|
+
} catch (e) {
|
|
27279
|
+
throw new LocalCliError({ code: "TELEMETRY_ENROLL_UNREACHABLE", message: `Could not reach the telemetry ingest to enroll: ${e instanceof Error ? e.message : String(e)}` });
|
|
27280
|
+
}
|
|
27281
|
+
if (!res.ok) {
|
|
27282
|
+
const body = await res.text().catch(() => "");
|
|
27283
|
+
throw new LocalCliError({ code: "TELEMETRY_ENROLL_FAILED", message: `Enroll failed (HTTP ${res.status.toString()}): ${body}` });
|
|
27284
|
+
}
|
|
27285
|
+
const json = await res.json();
|
|
27286
|
+
if (!json.instanceId || !json.ingestKey) {
|
|
27287
|
+
throw new LocalCliError({ code: "TELEMETRY_ENROLL_BAD_RESPONSE", message: "Enroll response missing instanceId/ingestKey." });
|
|
27288
|
+
}
|
|
27289
|
+
return { instanceId: json.instanceId, ingestKey: json.ingestKey };
|
|
27290
|
+
}
|
|
27291
|
+
|
|
27292
|
+
// src/commands/telemetry/enroll.ts
|
|
27293
|
+
var INGEST_KEY_SECRET = "m8t-ingest-key";
|
|
27294
|
+
async function resolveKeyVaultName(containerAppResourceId) {
|
|
27295
|
+
const kvUri = (await runAz([
|
|
27296
|
+
"containerapp",
|
|
27297
|
+
"show",
|
|
27298
|
+
"--ids",
|
|
27299
|
+
containerAppResourceId,
|
|
27300
|
+
"--query",
|
|
27301
|
+
"properties.template.containers[0].env[?name=='KEY_VAULT_URI'].value | [0]",
|
|
27302
|
+
"-o",
|
|
27303
|
+
"tsv"
|
|
27304
|
+
])).trim();
|
|
27305
|
+
if (!kvUri) {
|
|
27306
|
+
throw new LocalCliError({
|
|
27307
|
+
code: "TELEMETRY_ENROLL_NO_KV",
|
|
27308
|
+
message: "Could not resolve the platform Key Vault: the gateway Container App has no KEY_VAULT_URI env var.",
|
|
27309
|
+
hint: "Verify the deployment via 'az containerapp show --ids <id> --query properties.template.containers[0].env'."
|
|
27310
|
+
});
|
|
27311
|
+
}
|
|
27312
|
+
const host = new URL(kvUri).hostname;
|
|
27313
|
+
const name = host.split(".")[0];
|
|
27314
|
+
if (!name) {
|
|
27315
|
+
throw new LocalCliError({ code: "TELEMETRY_ENROLL_BAD_KV_URI", message: `Could not parse a Key Vault name from KEY_VAULT_URI '${kvUri}'.` });
|
|
27316
|
+
}
|
|
27317
|
+
return name;
|
|
27318
|
+
}
|
|
27319
|
+
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
27320
|
+
static paths = [["telemetry", "enroll"]];
|
|
27321
|
+
static usage = Command55.Usage({
|
|
27322
|
+
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
27323
|
+
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.",
|
|
27324
|
+
examples: [["Enroll", "$0 telemetry enroll"]]
|
|
27325
|
+
});
|
|
27326
|
+
company = Option52.String("--company", { description: "Your company name (optional)." });
|
|
27327
|
+
contactEmail = Option52.String("--contact-email", { description: "Contact email (default: your signed-in az UPN)." });
|
|
27328
|
+
subscription = Option52.String("--subscription", { description: "Azure subscription id (default: your active az subscription)." });
|
|
27329
|
+
resourceGroup = Option52.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
27330
|
+
force = Option52.Boolean("--force", false, { description: "Re-enroll even if a key is already stored (mints a new instance id)." });
|
|
27331
|
+
async executeCommand() {
|
|
27332
|
+
const account = await getAzAccount();
|
|
27333
|
+
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
27334
|
+
const contactEmail = (typeof this.contactEmail === "string" ? this.contactEmail : void 0) ?? account.upn;
|
|
27335
|
+
const company = (typeof this.company === "string" ? this.company : void 0) ?? "";
|
|
27336
|
+
const d = await discoverGateway({
|
|
27337
|
+
subscriptionId,
|
|
27338
|
+
interactive: false,
|
|
27339
|
+
resourceGroup: typeof this.resourceGroup === "string" ? this.resourceGroup : void 0
|
|
27340
|
+
});
|
|
27341
|
+
const kvName = await resolveKeyVaultName(d.containerAppResourceId);
|
|
27342
|
+
if (this.force !== true) {
|
|
27343
|
+
let existing = "";
|
|
27344
|
+
try {
|
|
27345
|
+
existing = await runAz(["keyvault", "secret", "show", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--query", "value", "-o", "tsv"]);
|
|
27346
|
+
} catch (e) {
|
|
27347
|
+
rethrowIfAzUnavailable(e);
|
|
27348
|
+
existing = "";
|
|
27349
|
+
}
|
|
27350
|
+
if (existing.trim()) {
|
|
27351
|
+
throw new LocalCliError({
|
|
27352
|
+
code: "TELEMETRY_ALREADY_ENROLLED",
|
|
27353
|
+
message: "This installation is already enrolled.",
|
|
27354
|
+
hint: "Pass --force to re-enroll (mints a new instance id)."
|
|
27355
|
+
});
|
|
27356
|
+
}
|
|
27357
|
+
}
|
|
27358
|
+
const { instanceId, ingestKey } = await enroll({ company, contactEmail, subscriptionId });
|
|
27359
|
+
await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--value", ingestKey, "--only-show-errors"]);
|
|
27360
|
+
this.context.stdout.write(
|
|
27361
|
+
`${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
|
|
27362
|
+
`
|
|
27363
|
+
);
|
|
27364
|
+
return 0;
|
|
27365
|
+
}
|
|
27366
|
+
};
|
|
27367
|
+
|
|
27243
27368
|
// src/cli.ts
|
|
27244
27369
|
var cli = new Cli({
|
|
27245
27370
|
binaryName: "m8t",
|
|
@@ -27300,6 +27425,7 @@ cli.register(BootstrapReapCommand);
|
|
|
27300
27425
|
cli.register(BootstrapFinishCommand);
|
|
27301
27426
|
cli.register(BootstrapUiCommand);
|
|
27302
27427
|
cli.register(BootstrapSeedProfileCommand);
|
|
27428
|
+
cli.register(TelemetryEnrollCommand);
|
|
27303
27429
|
async function main() {
|
|
27304
27430
|
try {
|
|
27305
27431
|
return await cli.run(process.argv.slice(2));
|