@m8t-stack/cli 0.2.13 → 0.2.15
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 +556 -220
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -781,7 +781,10 @@ var init_brain_yaml_mirror = __esm({
|
|
|
781
781
|
` enabled: true`,
|
|
782
782
|
` lifecycle: { inbox: 14d, stale: 30d, archive: 90d }`,
|
|
783
783
|
` artifactRetention: 30d`,
|
|
784
|
-
` digestRetention: 90d
|
|
784
|
+
` digestRetention: 90d`,
|
|
785
|
+
` costReport:`,
|
|
786
|
+
` enabled: true`,
|
|
787
|
+
` cadenceDays: 14`
|
|
785
788
|
].join("\n");
|
|
786
789
|
MIRROR_BANNER = [
|
|
787
790
|
`# \u2500\u2500 MIRROR \u2014 link record convenience copy. NOT the source of truth \u2014`,
|
|
@@ -1195,7 +1198,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1195
1198
|
import { Builtins, Cli } from "clipanion";
|
|
1196
1199
|
|
|
1197
1200
|
// src/lib/package-version.ts
|
|
1198
|
-
var CLI_VERSION = "0.2.
|
|
1201
|
+
var CLI_VERSION = "0.2.15";
|
|
1199
1202
|
|
|
1200
1203
|
// src/lib/render-error.ts
|
|
1201
1204
|
init_errors();
|
|
@@ -13194,6 +13197,18 @@ async function ghAuthLoginDevice(exec = defaultGhExec) {
|
|
|
13194
13197
|
});
|
|
13195
13198
|
}
|
|
13196
13199
|
}
|
|
13200
|
+
async function getGhToken(exec = defaultGhExec) {
|
|
13201
|
+
const r = await exec("gh", ["auth", "token"]);
|
|
13202
|
+
const tok = r.stdout.trim();
|
|
13203
|
+
if (r.exitCode !== 0 || !tok) {
|
|
13204
|
+
throw new LocalCliError({
|
|
13205
|
+
code: "GH_TOKEN_FAILED",
|
|
13206
|
+
message: `gh auth token failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`,
|
|
13207
|
+
hint: "Run `gh auth login` to authenticate the GitHub CLI."
|
|
13208
|
+
});
|
|
13209
|
+
}
|
|
13210
|
+
return tok;
|
|
13211
|
+
}
|
|
13197
13212
|
async function getRepoId(owner, repo, exec = defaultGhExec) {
|
|
13198
13213
|
const r = await exec("gh", ["api", `/repos/${owner}/${repo}`, "--jq", ".id"]);
|
|
13199
13214
|
if (r.exitCode !== 0) {
|
|
@@ -13297,6 +13312,34 @@ async function tryOpenUrl(url, exec = defaultGhExec) {
|
|
|
13297
13312
|
} catch {
|
|
13298
13313
|
}
|
|
13299
13314
|
}
|
|
13315
|
+
function ghRepoProbe(args) {
|
|
13316
|
+
const exec = args.exec ?? defaultGhExec;
|
|
13317
|
+
const slug = `${args.owner}/${args.repo}`;
|
|
13318
|
+
return {
|
|
13319
|
+
repoStatus: async () => {
|
|
13320
|
+
const r = await exec("gh", ["api", "-i", `/repos/${slug}`]);
|
|
13321
|
+
const combined = `${r.stdout}
|
|
13322
|
+
${r.stderr}`;
|
|
13323
|
+
const header = /^HTTP\/[\d.]+\s+(\d{3})/m.exec(combined);
|
|
13324
|
+
if (header) return Number(header[1]);
|
|
13325
|
+
const ghErr = /\(HTTP (\d{3})\)/.exec(combined);
|
|
13326
|
+
if (ghErr) return Number(ghErr[1]);
|
|
13327
|
+
if (r.exitCode === 0) return 200;
|
|
13328
|
+
throw new LocalCliError({
|
|
13329
|
+
code: "GH_REPO_PROBE_FAILED",
|
|
13330
|
+
message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
|
|
13331
|
+
});
|
|
13332
|
+
},
|
|
13333
|
+
getFile: async (path29) => {
|
|
13334
|
+
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path29}`]);
|
|
13335
|
+
return r.exitCode === 0 ? r.stdout : null;
|
|
13336
|
+
},
|
|
13337
|
+
isEmpty: async () => {
|
|
13338
|
+
const r = await exec("gh", ["api", `/repos/${slug}/contents`]);
|
|
13339
|
+
return r.exitCode !== 0;
|
|
13340
|
+
}
|
|
13341
|
+
};
|
|
13342
|
+
}
|
|
13300
13343
|
|
|
13301
13344
|
// src/commands/brain/app-create.ts
|
|
13302
13345
|
init_errors();
|
|
@@ -13531,6 +13574,7 @@ import { readFileSync as readFileSync6 } from "fs";
|
|
|
13531
13574
|
import * as fs10 from "fs";
|
|
13532
13575
|
import * as os4 from "os";
|
|
13533
13576
|
import * as path9 from "path";
|
|
13577
|
+
import * as readline2 from "readline/promises";
|
|
13534
13578
|
import { DefaultAzureCredential as DefaultAzureCredential4 } from "@azure/identity";
|
|
13535
13579
|
init_errors();
|
|
13536
13580
|
|
|
@@ -14062,6 +14106,20 @@ function materializeBrainTree(opts) {
|
|
|
14062
14106
|
init_errors();
|
|
14063
14107
|
init_esm2();
|
|
14064
14108
|
import { spawn as spawn3 } from "child_process";
|
|
14109
|
+
import { parse as parseYaml5 } from "yaml";
|
|
14110
|
+
function isM8tBrainMarker(yamlText) {
|
|
14111
|
+
if (!yamlText.trim()) return false;
|
|
14112
|
+
let parsed;
|
|
14113
|
+
try {
|
|
14114
|
+
parsed = parseYaml5(yamlText);
|
|
14115
|
+
} catch {
|
|
14116
|
+
return false;
|
|
14117
|
+
}
|
|
14118
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
14119
|
+
const root = parsed;
|
|
14120
|
+
const isObj2 = (v) => typeof v === "object" && v !== null;
|
|
14121
|
+
return isObj2(root.engine) || isObj2(root.processes) || isObj2(root.link);
|
|
14122
|
+
}
|
|
14065
14123
|
async function createBlankRepo(args) {
|
|
14066
14124
|
const res = await fetch(`https://api.github.com/orgs/${args.org}/repos`, {
|
|
14067
14125
|
method: "POST",
|
|
@@ -14192,8 +14250,119 @@ async function commitFilesViaApp(args) {
|
|
|
14192
14250
|
});
|
|
14193
14251
|
await api(`/git/refs/heads/${args.branch}`, { method: "PATCH", body: JSON.stringify({ sha: commit.sha }) });
|
|
14194
14252
|
}
|
|
14253
|
+
async function probeRepoState(probe) {
|
|
14254
|
+
const status = await probe.repoStatus();
|
|
14255
|
+
if (status === 404) return "absent";
|
|
14256
|
+
if (status !== 200) {
|
|
14257
|
+
throw new LocalCliError({
|
|
14258
|
+
code: "GH_REPO_PROBE_FAILED",
|
|
14259
|
+
message: `GET repo returned HTTP ${status.toString()} (expected 200 or 404)`
|
|
14260
|
+
});
|
|
14261
|
+
}
|
|
14262
|
+
const marker = await probe.getFile(".m8t/brain.yaml");
|
|
14263
|
+
if (marker !== null && isM8tBrainMarker(marker)) return "m8t-brain";
|
|
14264
|
+
if (await probe.isEmpty()) return "empty";
|
|
14265
|
+
return "foreign";
|
|
14266
|
+
}
|
|
14267
|
+
function appRepoProbe(args) {
|
|
14268
|
+
const doFetch = args.fetchImpl ?? fetch;
|
|
14269
|
+
const H = ghHeaders(args.token);
|
|
14270
|
+
return {
|
|
14271
|
+
repoStatus: async () => {
|
|
14272
|
+
const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
14273
|
+
return res.status;
|
|
14274
|
+
},
|
|
14275
|
+
getFile: (path29) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path29, fetchImpl: args.fetchImpl }),
|
|
14276
|
+
isEmpty: async () => {
|
|
14277
|
+
const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
14278
|
+
return res.status === 404;
|
|
14279
|
+
}
|
|
14280
|
+
};
|
|
14281
|
+
}
|
|
14282
|
+
|
|
14283
|
+
// src/lib/brain-repo-collision.ts
|
|
14284
|
+
init_errors();
|
|
14285
|
+
var DEFAULT_MAX_ATTEMPTS = 20;
|
|
14286
|
+
function actionForState(state) {
|
|
14287
|
+
switch (state) {
|
|
14288
|
+
case "absent":
|
|
14289
|
+
return "create";
|
|
14290
|
+
case "m8t-brain":
|
|
14291
|
+
return "reuse";
|
|
14292
|
+
case "empty":
|
|
14293
|
+
return "adopt";
|
|
14294
|
+
case "foreign":
|
|
14295
|
+
throw new LocalCliError({
|
|
14296
|
+
code: "FOREIGN_REPO_COLLISION",
|
|
14297
|
+
message: `A repo already exists but isn't an m8t brain.`
|
|
14298
|
+
});
|
|
14299
|
+
}
|
|
14300
|
+
}
|
|
14301
|
+
function candidate(base, n) {
|
|
14302
|
+
return n === 1 ? base : `${base}-${n.toString()}`;
|
|
14303
|
+
}
|
|
14304
|
+
async function scan(base, classify, accept, startAt, maxAttempts) {
|
|
14305
|
+
for (let n = startAt; n <= maxAttempts; n++) {
|
|
14306
|
+
const name = candidate(base, n);
|
|
14307
|
+
const state = await classify(name);
|
|
14308
|
+
if (accept(state)) return { repoName: name, state };
|
|
14309
|
+
}
|
|
14310
|
+
throw new LocalCliError({
|
|
14311
|
+
code: "REPO_NAME_SCAN_EXHAUSTED",
|
|
14312
|
+
message: `Could not find a free repo name for "${base}" after ${maxAttempts.toString()} attempts.`
|
|
14313
|
+
});
|
|
14314
|
+
}
|
|
14315
|
+
async function resolveRepoCollision(args) {
|
|
14316
|
+
const max = args.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
14317
|
+
const state = await args.classify(args.repoName);
|
|
14318
|
+
if (args.force === "new") {
|
|
14319
|
+
const found = await scan(args.repoName, args.classify, (s) => s === "absent", 1, max);
|
|
14320
|
+
return { repoName: found.repoName, action: "create" };
|
|
14321
|
+
}
|
|
14322
|
+
if (args.force === "reuse") {
|
|
14323
|
+
return { repoName: args.repoName, action: actionForState(state) };
|
|
14324
|
+
}
|
|
14325
|
+
if (state === "absent") return { repoName: args.repoName, action: "create" };
|
|
14326
|
+
if (args.interactive && args.prompt) {
|
|
14327
|
+
const choice = await args.prompt({ repoName: args.repoName, state });
|
|
14328
|
+
if (choice === "abort") {
|
|
14329
|
+
throw new LocalCliError({ code: "BRAIN_CREATE_ABORTED", message: "Aborted by the operator." });
|
|
14330
|
+
}
|
|
14331
|
+
if (choice === "new") {
|
|
14332
|
+
const found = await scan(args.repoName, args.classify, (s) => s !== "foreign", 2, max);
|
|
14333
|
+
return { repoName: found.repoName, action: actionForState(found.state) };
|
|
14334
|
+
}
|
|
14335
|
+
return { repoName: args.repoName, action: actionForState(state) };
|
|
14336
|
+
}
|
|
14337
|
+
if (state === "foreign") {
|
|
14338
|
+
throw new LocalCliError({
|
|
14339
|
+
code: "FOREIGN_REPO_COLLISION",
|
|
14340
|
+
message: `A repo with this name already exists but isn't an m8t brain, so it won't be touched. Rename that repo or install into a different org, then re-run.`
|
|
14341
|
+
});
|
|
14342
|
+
}
|
|
14343
|
+
return { repoName: args.repoName, action: actionForState(state) };
|
|
14344
|
+
}
|
|
14195
14345
|
|
|
14196
14346
|
// src/commands/brain/create.ts
|
|
14347
|
+
async function promptCollision(ctx, info) {
|
|
14348
|
+
const rl = readline2.createInterface({ input: ctx.stdin, output: ctx.stdout });
|
|
14349
|
+
try {
|
|
14350
|
+
if (info.state === "foreign") {
|
|
14351
|
+
const ans2 = (await rl.question(
|
|
14352
|
+
`A repo "${info.repoName}" already exists but isn't an m8t brain. Create a new "${info.repoName}-2" instead? [Y/n] (n aborts) `
|
|
14353
|
+
)).trim().toLowerCase();
|
|
14354
|
+
return ans2 === "" || ans2 === "y" || ans2 === "yes" ? "new" : "abort";
|
|
14355
|
+
}
|
|
14356
|
+
const ans = (await rl.question(
|
|
14357
|
+
`An m8t brain "${info.repoName}" already exists. Reuse it? [Y/n] (n creates "${info.repoName}-2", or "a" aborts) `
|
|
14358
|
+
)).trim().toLowerCase();
|
|
14359
|
+
if (ans === "" || ans === "y" || ans === "yes") return "reuse";
|
|
14360
|
+
if (ans === "a" || ans === "abort") return "abort";
|
|
14361
|
+
return "new";
|
|
14362
|
+
} finally {
|
|
14363
|
+
rl.close();
|
|
14364
|
+
}
|
|
14365
|
+
}
|
|
14197
14366
|
function stageAsGitRepo(dir) {
|
|
14198
14367
|
const steps = [
|
|
14199
14368
|
{ args: ["init", "-b", "main"], label: "git init" },
|
|
@@ -14236,6 +14405,8 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14236
14405
|
appId = Option17.String("--app-id", { description: "GitHub App id (no-gh path); with --app-pem/--installation-id, create the repo via the App instead of gh." });
|
|
14237
14406
|
appPem = Option17.String("--app-pem", { description: "Path to the App private-key PEM (no-gh path)." });
|
|
14238
14407
|
installationId = Option17.String("--installation-id", { description: "Org installation id for the App (no-gh path)." });
|
|
14408
|
+
reuse = Option17.Boolean("--reuse", false, { description: "If the brain repo already exists, reuse it (non-interactive). Errors if the repo isn't a valid m8t brain." });
|
|
14409
|
+
newName = Option17.Boolean("--new", false, { description: "If the brain repo name is taken, create a fresh suffixed repo (e.g. <name>-2) instead of reusing." });
|
|
14239
14410
|
async executeCommand() {
|
|
14240
14411
|
const owner = typeof this.owner === "string" ? this.owner : void 0;
|
|
14241
14412
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -14300,59 +14471,95 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14300
14471
|
const tmpDir = fs10.mkdtempSync(path9.join(os4.tmpdir(), `m8t-brain-create-${worker}-`));
|
|
14301
14472
|
let result;
|
|
14302
14473
|
let resolvedInstallationId = null;
|
|
14474
|
+
const force = this.reuse === true ? "reuse" : this.newName === true ? "new" : void 0;
|
|
14475
|
+
const interactive = !useAppPath && this.context.stdout.isTTY === true;
|
|
14476
|
+
let token;
|
|
14477
|
+
let classify;
|
|
14478
|
+
if (useAppPath) {
|
|
14479
|
+
token = await mintInstallationTokenFromPem({
|
|
14480
|
+
appId: appIdOpt,
|
|
14481
|
+
privateKeyPem: readFileSync6(appPemOpt, "utf8"),
|
|
14482
|
+
installationId: installationIdOpt
|
|
14483
|
+
});
|
|
14484
|
+
const tok = token;
|
|
14485
|
+
classify = (name) => probeRepoState(appRepoProbe({ token: tok, repo: `${owner}/${name}` }));
|
|
14486
|
+
} else {
|
|
14487
|
+
classify = (name) => probeRepoState(ghRepoProbe({ owner, repo: name }));
|
|
14488
|
+
}
|
|
14489
|
+
const decision = await resolveRepoCollision({
|
|
14490
|
+
repoName,
|
|
14491
|
+
classify,
|
|
14492
|
+
interactive,
|
|
14493
|
+
force,
|
|
14494
|
+
prompt: interactive ? (info) => promptCollision(
|
|
14495
|
+
{ stdin: this.context.stdin, stdout: this.context.stdout },
|
|
14496
|
+
info
|
|
14497
|
+
) : void 0
|
|
14498
|
+
});
|
|
14499
|
+
const finalName = decision.repoName;
|
|
14500
|
+
const needsPush = decision.action === "create" || decision.action === "adopt";
|
|
14501
|
+
if (finalName !== repoName) {
|
|
14502
|
+
this.context.stdout.write(`${colors.hint("note:")} using repo name ${colors.field(finalName)} (${decision.action}).
|
|
14503
|
+
`);
|
|
14504
|
+
}
|
|
14303
14505
|
try {
|
|
14304
|
-
|
|
14305
|
-
|
|
14306
|
-
|
|
14506
|
+
if (needsPush) {
|
|
14507
|
+
materializeBrainTree({ templateSrc, destDir: tmpDir, seedDir });
|
|
14508
|
+
this.context.stdout.write(
|
|
14509
|
+
seedName ? `${colors.dim("\u2192")} copied brain-template/ + overlaid seed ${colors.field(seedName)} to ${tmpDir}
|
|
14307
14510
|
` : `${colors.dim("\u2192")} copied brain-template/ to ${tmpDir}
|
|
14308
14511
|
`
|
|
14309
|
-
|
|
14512
|
+
);
|
|
14513
|
+
}
|
|
14310
14514
|
if (useAppPath) {
|
|
14311
|
-
|
|
14515
|
+
const appToken = token ?? "";
|
|
14516
|
+
if (decision.action === "create") {
|
|
14517
|
+
this.context.stdout.write(`${colors.dim("\u2192")} creating ${owner}/${finalName} via the GitHub App (no gh)\u2026
|
|
14518
|
+
`);
|
|
14519
|
+
await createBlankRepo({ token: appToken, org: owner, name: finalName, private: !this.public_ });
|
|
14520
|
+
await pushSeed({ token: appToken, org: owner, name: finalName, branch, sourceDir: tmpDir });
|
|
14521
|
+
this.context.stdout.write(`${colors.success("\u2713")} repo created + seeded via the App
|
|
14522
|
+
`);
|
|
14523
|
+
} else if (decision.action === "adopt") {
|
|
14524
|
+
this.context.stdout.write(`${colors.dim("\u2192")} seeding the existing empty repo ${owner}/${finalName} via the GitHub App\u2026
|
|
14525
|
+
`);
|
|
14526
|
+
await pushSeed({ token: appToken, org: owner, name: finalName, branch, sourceDir: tmpDir });
|
|
14527
|
+
this.context.stdout.write(`${colors.success("\u2713")} empty repo seeded via the App
|
|
14528
|
+
`);
|
|
14529
|
+
} else {
|
|
14530
|
+
this.context.stdout.write(`${colors.success("\u2713")} reusing the existing brain ${owner}/${finalName}
|
|
14312
14531
|
`);
|
|
14313
|
-
const pem = readFileSync6(appPemOpt, "utf8");
|
|
14314
|
-
const appJwt = signAppJwt({ appId: appIdOpt, privateKeyPem: pem });
|
|
14315
|
-
const tokRes = await fetch(
|
|
14316
|
-
`https://api.github.com/app/installations/${installationIdOpt}/access_tokens`,
|
|
14317
|
-
{
|
|
14318
|
-
method: "POST",
|
|
14319
|
-
headers: {
|
|
14320
|
-
Authorization: `Bearer ${appJwt}`,
|
|
14321
|
-
Accept: "application/vnd.github+json",
|
|
14322
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
14323
|
-
"User-Agent": "m8t"
|
|
14324
|
-
}
|
|
14325
|
-
}
|
|
14326
|
-
);
|
|
14327
|
-
if (!tokRes.ok) {
|
|
14328
|
-
throw new LocalCliError({
|
|
14329
|
-
code: "GH_APP_TOKEN_FAILED",
|
|
14330
|
-
message: `mint installation token HTTP ${tokRes.status.toString()}: ${(await tokRes.text()).slice(0, 300)}`
|
|
14331
|
-
});
|
|
14332
14532
|
}
|
|
14333
|
-
const token = (await tokRes.json()).token;
|
|
14334
|
-
await createBlankRepo({ token, org: owner, name: repoName, private: !this.public_ });
|
|
14335
|
-
await pushSeed({ token, org: owner, name: repoName, branch, sourceDir: tmpDir });
|
|
14336
14533
|
resolvedInstallationId = installationIdOpt;
|
|
14337
|
-
this.context.stdout.write(`${colors.success("\u2713")} repo created + seeded via the App
|
|
14338
|
-
`);
|
|
14339
14534
|
} else {
|
|
14340
|
-
|
|
14341
|
-
|
|
14535
|
+
if (decision.action === "create") {
|
|
14536
|
+
stageAsGitRepo(tmpDir);
|
|
14537
|
+
this.context.stdout.write(`${colors.dim("\u2192")} creating repo ${owner}/${finalName} (${this.public_ ? "public" : "private"})\u2026
|
|
14538
|
+
`);
|
|
14539
|
+
await createRepo({ owner, name: finalName, private: !this.public_, source: tmpDir });
|
|
14540
|
+
this.context.stdout.write(`${colors.success("\u2713")} repo created
|
|
14541
|
+
`);
|
|
14542
|
+
} else if (decision.action === "adopt") {
|
|
14543
|
+
this.context.stdout.write(`${colors.dim("\u2192")} seeding the existing empty repo ${owner}/${finalName}\u2026
|
|
14342
14544
|
`);
|
|
14343
|
-
|
|
14344
|
-
|
|
14545
|
+
const ghToken = await getGhToken();
|
|
14546
|
+
await pushSeed({ token: ghToken, org: owner, name: finalName, branch, sourceDir: tmpDir });
|
|
14547
|
+
this.context.stdout.write(`${colors.success("\u2713")} empty repo seeded
|
|
14345
14548
|
`);
|
|
14346
|
-
|
|
14549
|
+
} else {
|
|
14550
|
+
this.context.stdout.write(`${colors.success("\u2713")} reusing the existing brain ${owner}/${finalName}
|
|
14551
|
+
`);
|
|
14552
|
+
}
|
|
14553
|
+
const repoId = await getRepoId(owner, finalName);
|
|
14347
14554
|
const { slug, appId: secretAppId, privateKeyPem } = await readAppSecrets({ credential: credential2, kvUri });
|
|
14348
14555
|
const initialJwt = signAppJwt({ appId: secretAppId, privateKeyPem });
|
|
14349
|
-
resolvedInstallationId = await probeAppInstallation({ owner, repo:
|
|
14556
|
+
resolvedInstallationId = await probeAppInstallation({ owner, repo: finalName, jwt: initialJwt });
|
|
14350
14557
|
if (resolvedInstallationId) {
|
|
14351
|
-
this.context.stdout.write(` ${colors.success("\u2713")} App already installed on ${colors.field(`${owner}/${
|
|
14558
|
+
this.context.stdout.write(` ${colors.success("\u2713")} App already installed on ${colors.field(`${owner}/${finalName}`)} (installation ${resolvedInstallationId}); skipping browser open.
|
|
14352
14559
|
`);
|
|
14353
14560
|
} else {
|
|
14354
14561
|
const url = installUrl(slug, repoId);
|
|
14355
|
-
this.context.stdout.write(`${colors.field("\u2192")} Install the m8t-brain App on ${owner}/${
|
|
14562
|
+
this.context.stdout.write(`${colors.field("\u2192")} Install the m8t-brain App on ${owner}/${finalName}:
|
|
14356
14563
|
${url}
|
|
14357
14564
|
`);
|
|
14358
14565
|
await tryOpenUrl(url);
|
|
@@ -14360,10 +14567,10 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14360
14567
|
`);
|
|
14361
14568
|
resolvedInstallationId = await pollForInstallation({
|
|
14362
14569
|
owner,
|
|
14363
|
-
repo:
|
|
14570
|
+
repo: finalName,
|
|
14364
14571
|
// Mint fresh JWT each tick — App JWTs are 10-min TTL, poll defaults to 5 min
|
|
14365
14572
|
// but the timeout is a soft default, so don't depend on the inequality.
|
|
14366
|
-
probe: () => probeAppInstallation({ owner, repo:
|
|
14573
|
+
probe: () => probeAppInstallation({ owner, repo: finalName, jwt: signAppJwt({ appId: secretAppId, privateKeyPem }) }),
|
|
14367
14574
|
onTick: (n) => {
|
|
14368
14575
|
if (n % 5 === 0) this.context.stderr.write(` ${colors.dim(`\u2026still waiting (${(n * 2).toString()}s)`)}
|
|
14369
14576
|
`);
|
|
@@ -14379,7 +14586,7 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14379
14586
|
projectEndpoint: project.endpoint,
|
|
14380
14587
|
projectArmId: `${project.accountScope}/projects/${project.projectName}`,
|
|
14381
14588
|
agentName: worker,
|
|
14382
|
-
repo: `${owner}/${
|
|
14589
|
+
repo: `${owner}/${finalName}`,
|
|
14383
14590
|
branch,
|
|
14384
14591
|
repoRoot,
|
|
14385
14592
|
installationId: resolvedInstallationId,
|
|
@@ -14393,7 +14600,7 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14393
14600
|
this.context.stdout.write(
|
|
14394
14601
|
renderJson({
|
|
14395
14602
|
worker,
|
|
14396
|
-
repo: `${owner}/${
|
|
14603
|
+
repo: `${owner}/${finalName}`,
|
|
14397
14604
|
branch,
|
|
14398
14605
|
installationId: resolvedInstallationId,
|
|
14399
14606
|
connectionName: result.connectionName,
|
|
@@ -14403,10 +14610,10 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
14403
14610
|
return 0;
|
|
14404
14611
|
}
|
|
14405
14612
|
this.context.stdout.write(
|
|
14406
|
-
`${colors.success("\u2713")} created + linked ${colors.field(worker)} \u2194 ${colors.field(`${owner}/${
|
|
14613
|
+
`${colors.success("\u2713")} created + linked ${colors.field(worker)} \u2194 ${colors.field(`${owner}/${finalName}`)} (agent version ${result.foundryVersion ?? ""}).
|
|
14407
14614
|
`
|
|
14408
14615
|
);
|
|
14409
|
-
this.context.stdout.write(` ${colors.hint("brain repo:")} https://github.com/${owner}/${
|
|
14616
|
+
this.context.stdout.write(` ${colors.hint("brain repo:")} https://github.com/${owner}/${finalName}
|
|
14410
14617
|
`);
|
|
14411
14618
|
this.context.stdout.write(` ${colors.hint("token:")} rotates lazily on every invoke (60min TTL, 10min safety margin).
|
|
14412
14619
|
`);
|
|
@@ -15007,7 +15214,7 @@ init_errors();
|
|
|
15007
15214
|
// src/lib/deploy-prompt-advisor.ts
|
|
15008
15215
|
import * as fs12 from "fs";
|
|
15009
15216
|
import * as path11 from "path";
|
|
15010
|
-
import { parse as
|
|
15217
|
+
import { parse as parseYaml6 } from "yaml";
|
|
15011
15218
|
init_foundry_agents();
|
|
15012
15219
|
init_errors();
|
|
15013
15220
|
function findPersonasRoot(hint) {
|
|
@@ -15035,7 +15242,7 @@ async function deployPromptAdvisor(args) {
|
|
|
15035
15242
|
});
|
|
15036
15243
|
}
|
|
15037
15244
|
const fmMatch = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
15038
|
-
const fm = fmMatch ?
|
|
15245
|
+
const fm = fmMatch ? parseYaml6(fmMatch[1]) : {};
|
|
15039
15246
|
const model = args.model ?? fm.targets?.foundry?.model;
|
|
15040
15247
|
if (!model) {
|
|
15041
15248
|
throw new LocalCliError({
|
|
@@ -15302,7 +15509,7 @@ async function awaitAgentQueryable(args) {
|
|
|
15302
15509
|
init_errors();
|
|
15303
15510
|
init_esm();
|
|
15304
15511
|
import * as fs13 from "fs";
|
|
15305
|
-
import { parse as
|
|
15512
|
+
import { parse as parseYaml7 } from "yaml";
|
|
15306
15513
|
function readPersonaA2aCard(personaPath) {
|
|
15307
15514
|
let raw;
|
|
15308
15515
|
try {
|
|
@@ -15312,7 +15519,7 @@ function readPersonaA2aCard(personaPath) {
|
|
|
15312
15519
|
}
|
|
15313
15520
|
const fm = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
15314
15521
|
if (!fm) throw new LocalCliError({ code: "PERSONA_NO_FRONTMATTER", message: `Persona '${personaPath}' has no frontmatter.` });
|
|
15315
|
-
const front =
|
|
15522
|
+
const front = parseYaml7(fm[1]);
|
|
15316
15523
|
const role = typeof front.role === "string" ? front.role : "";
|
|
15317
15524
|
const cardYaml = front.targets?.foundry?.["a2a-card"];
|
|
15318
15525
|
if (!cardYaml || typeof cardYaml !== "object") {
|
|
@@ -15798,7 +16005,7 @@ init_errors();
|
|
|
15798
16005
|
import * as fs15 from "fs";
|
|
15799
16006
|
import * as os6 from "os";
|
|
15800
16007
|
import * as path12 from "path";
|
|
15801
|
-
import { parse as
|
|
16008
|
+
import { parse as parseYaml8 } from "yaml";
|
|
15802
16009
|
var A2A_PATH = "/api/a2a/mcp";
|
|
15803
16010
|
function resolveBridgeUrl(opts) {
|
|
15804
16011
|
const env = opts.env ?? process.env;
|
|
@@ -15822,7 +16029,7 @@ function readConfigGatewayUrl(home) {
|
|
|
15822
16029
|
const cfg = path12.join(home ?? os6.homedir(), ".m8t-stack", "config.yaml");
|
|
15823
16030
|
if (!fs15.existsSync(cfg)) return void 0;
|
|
15824
16031
|
try {
|
|
15825
|
-
const o =
|
|
16032
|
+
const o = parseYaml8(fs15.readFileSync(cfg, "utf8"));
|
|
15826
16033
|
return typeof o.gatewayUrl === "string" ? o.gatewayUrl : void 0;
|
|
15827
16034
|
} catch {
|
|
15828
16035
|
return void 0;
|
|
@@ -16324,7 +16531,7 @@ async function deployHostedWorker(args) {
|
|
|
16324
16531
|
// src/lib/persona.ts
|
|
16325
16532
|
import * as fs17 from "fs";
|
|
16326
16533
|
import * as path15 from "path";
|
|
16327
|
-
import { parse as
|
|
16534
|
+
import { parse as parseYaml9 } from "yaml";
|
|
16328
16535
|
function findRepoRoot(startDir) {
|
|
16329
16536
|
let dir = path15.resolve(startDir);
|
|
16330
16537
|
for (; ; ) {
|
|
@@ -16348,7 +16555,7 @@ function resolvePersona(persona, cwd = process.cwd()) {
|
|
|
16348
16555
|
if (!match) return { persona, personaVersion: null };
|
|
16349
16556
|
let fm = null;
|
|
16350
16557
|
try {
|
|
16351
|
-
fm =
|
|
16558
|
+
fm = parseYaml9(match[1]);
|
|
16352
16559
|
} catch {
|
|
16353
16560
|
return { persona, personaVersion: null };
|
|
16354
16561
|
}
|
|
@@ -17425,9 +17632,134 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
17425
17632
|
}
|
|
17426
17633
|
};
|
|
17427
17634
|
|
|
17428
|
-
// src/commands/
|
|
17635
|
+
// src/commands/platform/enable-cost-report.ts
|
|
17429
17636
|
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
17430
17637
|
|
|
17638
|
+
// src/lib/wire-gateway-acs.ts
|
|
17639
|
+
init_rbac();
|
|
17640
|
+
async function wireGatewayForAcs(args) {
|
|
17641
|
+
try {
|
|
17642
|
+
await args.az([
|
|
17643
|
+
"role",
|
|
17644
|
+
"assignment",
|
|
17645
|
+
"create",
|
|
17646
|
+
"--assignee-object-id",
|
|
17647
|
+
args.gatewayPrincipalId,
|
|
17648
|
+
"--assignee-principal-type",
|
|
17649
|
+
"ServicePrincipal",
|
|
17650
|
+
"--role",
|
|
17651
|
+
CONTRIBUTOR_ROLE_ID,
|
|
17652
|
+
"--scope",
|
|
17653
|
+
args.acsResourceId
|
|
17654
|
+
]);
|
|
17655
|
+
} catch (e) {
|
|
17656
|
+
if (!(e instanceof Error && /RoleAssignmentExists/i.test(e.message))) throw e;
|
|
17657
|
+
}
|
|
17658
|
+
await args.az([
|
|
17659
|
+
"containerapp",
|
|
17660
|
+
"update",
|
|
17661
|
+
"-n",
|
|
17662
|
+
args.gatewayApp,
|
|
17663
|
+
"-g",
|
|
17664
|
+
args.resourceGroup,
|
|
17665
|
+
"--set-env-vars",
|
|
17666
|
+
`M8T_ACS_ENDPOINT=${args.endpoint}`,
|
|
17667
|
+
`M8T_ACS_SENDER=${args.sender}`,
|
|
17668
|
+
"M8T_ENABLE_COST_REPORTER=1"
|
|
17669
|
+
]);
|
|
17670
|
+
}
|
|
17671
|
+
|
|
17672
|
+
// src/commands/platform/enable-cost-report.ts
|
|
17673
|
+
init_errors();
|
|
17674
|
+
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
17675
|
+
static paths = [["platform", "enable-cost-report"]];
|
|
17676
|
+
static usage = Command33.Usage({
|
|
17677
|
+
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
17678
|
+
details: "Discovers the live gateway Container App, grants its managed identity Contributor at the ACS resource scope (authorising the ACS Email send), and sets M8T_ACS_ENDPOINT / M8T_ACS_SENDER / M8T_ENABLE_COST_REPORTER=1 on the gateway. Idempotent \u2014 safely re-runnable. ACS is created during the executor deploy; pass its endpoint, sender, and resource id (from that deploy or 'az communication list')."
|
|
17679
|
+
});
|
|
17680
|
+
subscription = Option31.String("--subscription");
|
|
17681
|
+
resourceGroup = Option31.String("--resource-group", {
|
|
17682
|
+
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
17683
|
+
});
|
|
17684
|
+
acsEndpoint = Option31.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
|
|
17685
|
+
acsSender = Option31.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
|
|
17686
|
+
acsResourceId = Option31.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
|
|
17687
|
+
output = Option31.String("--output");
|
|
17688
|
+
async executeCommand() {
|
|
17689
|
+
const mode = resolveOutputMode(
|
|
17690
|
+
this.output,
|
|
17691
|
+
this.context.stdout
|
|
17692
|
+
);
|
|
17693
|
+
const log = (msg) => {
|
|
17694
|
+
if (mode !== "json") this.context.stdout.write(msg + "\n");
|
|
17695
|
+
};
|
|
17696
|
+
const ctx = await resolveGatewayContext({
|
|
17697
|
+
interactive: mode !== "json",
|
|
17698
|
+
subscriptionId: this.subscription,
|
|
17699
|
+
resourceGroup: this.resourceGroup
|
|
17700
|
+
});
|
|
17701
|
+
const { resourceGroup, name } = parseContainerAppResourceId(ctx.containerAppResourceId);
|
|
17702
|
+
const gatewayPrincipalId = (await runAz([
|
|
17703
|
+
"containerapp",
|
|
17704
|
+
"show",
|
|
17705
|
+
"-n",
|
|
17706
|
+
name,
|
|
17707
|
+
"-g",
|
|
17708
|
+
resourceGroup,
|
|
17709
|
+
"--query",
|
|
17710
|
+
"identity.principalId",
|
|
17711
|
+
"-o",
|
|
17712
|
+
"tsv"
|
|
17713
|
+
])).trim();
|
|
17714
|
+
if (gatewayPrincipalId === "") {
|
|
17715
|
+
throw new LocalCliError({
|
|
17716
|
+
code: "GATEWAY_NO_MANAGED_IDENTITY",
|
|
17717
|
+
message: `Gateway '${name}' has no managed identity principalId to grant.`,
|
|
17718
|
+
hint: "Assign a system-assigned identity to the gateway Container App, then re-run."
|
|
17719
|
+
});
|
|
17720
|
+
}
|
|
17721
|
+
await wireGatewayForAcs({
|
|
17722
|
+
az: runAz,
|
|
17723
|
+
resourceGroup,
|
|
17724
|
+
acsResourceId: this.acsResourceId,
|
|
17725
|
+
gatewayApp: name,
|
|
17726
|
+
endpoint: this.acsEndpoint,
|
|
17727
|
+
sender: this.acsSender,
|
|
17728
|
+
gatewayPrincipalId
|
|
17729
|
+
});
|
|
17730
|
+
if (mode === "json") {
|
|
17731
|
+
this.context.stdout.write(
|
|
17732
|
+
renderJson({
|
|
17733
|
+
gateway: name,
|
|
17734
|
+
resourceGroup,
|
|
17735
|
+
roleGranted: "Contributor",
|
|
17736
|
+
scope: this.acsResourceId,
|
|
17737
|
+
envSet: {
|
|
17738
|
+
M8T_ACS_ENDPOINT: this.acsEndpoint,
|
|
17739
|
+
M8T_ACS_SENDER: this.acsSender,
|
|
17740
|
+
M8T_ENABLE_COST_REPORTER: "1"
|
|
17741
|
+
}
|
|
17742
|
+
}) + "\n"
|
|
17743
|
+
);
|
|
17744
|
+
return 0;
|
|
17745
|
+
}
|
|
17746
|
+
this.context.stdout.write(
|
|
17747
|
+
renderKeyValueBlock([
|
|
17748
|
+
{ key: "gateway", value: name },
|
|
17749
|
+
{ key: "role granted", value: `Contributor @ ${this.acsResourceId}` },
|
|
17750
|
+
{ key: "M8T_ACS_ENDPOINT", value: this.acsEndpoint },
|
|
17751
|
+
{ key: "M8T_ACS_SENDER", value: this.acsSender },
|
|
17752
|
+
{ key: "M8T_ENABLE_COST_REPORTER", value: "1" }
|
|
17753
|
+
]) + "\n"
|
|
17754
|
+
);
|
|
17755
|
+
log(colors.success("cost report enabled \u2014 the gateway will send it via ACS \u2713"));
|
|
17756
|
+
return 0;
|
|
17757
|
+
}
|
|
17758
|
+
};
|
|
17759
|
+
|
|
17760
|
+
// src/commands/deploy.ts
|
|
17761
|
+
import { Command as Command34, Option as Option32 } from "clipanion";
|
|
17762
|
+
|
|
17431
17763
|
// src/lib/app-reg.ts
|
|
17432
17764
|
init_errors();
|
|
17433
17765
|
var APP_NAME = "m8t-stack-webapp";
|
|
@@ -17960,24 +18292,24 @@ function classifyWhatIf(changes) {
|
|
|
17960
18292
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-run/m8t:latest";
|
|
17961
18293
|
var DeployCommand = class extends M8tCommand {
|
|
17962
18294
|
static paths = [["deploy"]];
|
|
17963
|
-
static usage =
|
|
18295
|
+
static usage = Command34.Usage({
|
|
17964
18296
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
17965
18297
|
details: "Ensures the Entra app reg (or pass --client-id to reuse an existing one \u2014 required if you can't create app regs), writes ~/.m8t-stack/config.yaml, ensures the resource group, and runs deploy/main.bicep. The repo is located via ~/.m8t-stack/repo-root."
|
|
17966
18298
|
});
|
|
17967
|
-
subscription =
|
|
17968
|
-
resourceGroup =
|
|
17969
|
-
location =
|
|
17970
|
-
suffix =
|
|
17971
|
-
imageRef =
|
|
17972
|
-
acrPullIdentity =
|
|
17973
|
-
acrResourceId =
|
|
17974
|
-
foundryEndpoint =
|
|
17975
|
-
foundryResourceId =
|
|
17976
|
-
foundryTracing =
|
|
18299
|
+
subscription = Option32.String("--subscription");
|
|
18300
|
+
resourceGroup = Option32.String("--resource-group", "rg-m8t-stack");
|
|
18301
|
+
location = Option32.String("--location", "eastus");
|
|
18302
|
+
suffix = Option32.String("--suffix", "");
|
|
18303
|
+
imageRef = Option32.String("--image-ref", DEFAULT_IMAGE_REF);
|
|
18304
|
+
acrPullIdentity = Option32.String("--acrpull-identity");
|
|
18305
|
+
acrResourceId = Option32.String("--acr-resource-id");
|
|
18306
|
+
foundryEndpoint = Option32.String("--foundry-endpoint");
|
|
18307
|
+
foundryResourceId = Option32.String("--foundry-resource-id");
|
|
18308
|
+
foundryTracing = Option32.String("--foundry-tracing");
|
|
17977
18309
|
// project | account | skip (bicep default: project)
|
|
17978
|
-
clientId =
|
|
17979
|
-
whatIf =
|
|
17980
|
-
output =
|
|
18310
|
+
clientId = Option32.String("--client-id");
|
|
18311
|
+
whatIf = Option32.Boolean("--what-if", false);
|
|
18312
|
+
output = Option32.String("--output");
|
|
17981
18313
|
async executeCommand() {
|
|
17982
18314
|
const mode = resolveOutputMode(
|
|
17983
18315
|
this.output,
|
|
@@ -18091,7 +18423,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
18091
18423
|
|
|
18092
18424
|
// src/commands/eval/skill.ts
|
|
18093
18425
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
18094
|
-
import { Command as
|
|
18426
|
+
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
18095
18427
|
init_errors();
|
|
18096
18428
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
18097
18429
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -18116,21 +18448,21 @@ function parseVerdict(stdout) {
|
|
|
18116
18448
|
}
|
|
18117
18449
|
var EvalSkillCommand = class extends M8tCommand {
|
|
18118
18450
|
static paths = [["eval", "skill"]];
|
|
18119
|
-
static usage =
|
|
18451
|
+
static usage = Command35.Usage({
|
|
18120
18452
|
description: "Vet one inbox skill candidate (Brain Faculties Eval F1): promote / reject / needs_review. Shells out to the Python `brain-eval` core (override its path with $BRAIN_EVAL_BIN)."
|
|
18121
18453
|
});
|
|
18122
|
-
candidate =
|
|
18123
|
-
skillsDir =
|
|
18124
|
-
noJudge =
|
|
18125
|
-
deployment =
|
|
18126
|
-
output =
|
|
18454
|
+
candidate = Option33.String();
|
|
18455
|
+
skillsDir = Option33.String("--skills-dir");
|
|
18456
|
+
noJudge = Option33.Boolean("--no-judge", false);
|
|
18457
|
+
deployment = Option33.String("--deployment");
|
|
18458
|
+
output = Option33.String("--output");
|
|
18127
18459
|
executeCommand() {
|
|
18128
18460
|
return Promise.resolve(this._runCommand());
|
|
18129
18461
|
}
|
|
18130
18462
|
_runCommand() {
|
|
18131
|
-
const
|
|
18463
|
+
const candidate2 = typeof this.candidate === "string" ? this.candidate : void 0;
|
|
18132
18464
|
const skillsDir = typeof this.skillsDir === "string" ? this.skillsDir : void 0;
|
|
18133
|
-
if (!
|
|
18465
|
+
if (!candidate2) {
|
|
18134
18466
|
throw new LocalCliError({ code: "USAGE", message: "<path> positional argument is required" });
|
|
18135
18467
|
}
|
|
18136
18468
|
if (!skillsDir) {
|
|
@@ -18139,7 +18471,7 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
18139
18471
|
const outputFlag = this.output === "json" || this.output === "auto" || this.output === "pretty" ? this.output : "pretty";
|
|
18140
18472
|
const mode = resolveOutputMode(outputFlag, this.context.stdout);
|
|
18141
18473
|
const bin = process.env.BRAIN_EVAL_BIN ?? "brain-eval";
|
|
18142
|
-
const args = ["skill",
|
|
18474
|
+
const args = ["skill", candidate2, "--skills-dir", skillsDir, "--json"];
|
|
18143
18475
|
if (this.noJudge === true) args.push("--no-judge");
|
|
18144
18476
|
if (typeof this.deployment === "string") args.push("--deployment", this.deployment);
|
|
18145
18477
|
const res = spawnSync3(bin, args, { encoding: "utf-8" });
|
|
@@ -18182,7 +18514,7 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
18182
18514
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
18183
18515
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync13, existsSync as existsSync12, readdirSync } from "fs";
|
|
18184
18516
|
import { join as join19 } from "path";
|
|
18185
|
-
import { Command as
|
|
18517
|
+
import { Command as Command36, Option as Option34 } from "clipanion";
|
|
18186
18518
|
init_errors();
|
|
18187
18519
|
init_esm();
|
|
18188
18520
|
function parseArmToken(tok, opts) {
|
|
@@ -18396,23 +18728,23 @@ function buildPlan(args) {
|
|
|
18396
18728
|
}
|
|
18397
18729
|
var EvalExamCommand = class extends M8tCommand {
|
|
18398
18730
|
static paths = [["eval", "exam"]];
|
|
18399
|
-
static usage =
|
|
18731
|
+
static usage = Command36.Usage({
|
|
18400
18732
|
description: "Run a brain exam (Brain Referee E2-F2): 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."
|
|
18401
18733
|
});
|
|
18402
|
-
worker =
|
|
18403
|
-
arms =
|
|
18404
|
-
taskSet =
|
|
18405
|
-
examType =
|
|
18406
|
-
reps =
|
|
18407
|
-
probes =
|
|
18408
|
-
pool =
|
|
18409
|
-
out =
|
|
18410
|
-
dryRun =
|
|
18411
|
-
keepArms =
|
|
18412
|
-
allowStub =
|
|
18413
|
-
deployment =
|
|
18414
|
-
output =
|
|
18415
|
-
observeWaitS =
|
|
18734
|
+
worker = Option34.String();
|
|
18735
|
+
arms = Option34.String("--arms");
|
|
18736
|
+
taskSet = Option34.String("--task-set");
|
|
18737
|
+
examType = Option34.String("--exam-type");
|
|
18738
|
+
reps = Option34.String("-n,--reps");
|
|
18739
|
+
probes = Option34.String("--probes");
|
|
18740
|
+
pool = Option34.String("--pool");
|
|
18741
|
+
out = Option34.String("--out");
|
|
18742
|
+
dryRun = Option34.Boolean("--dry-run", false);
|
|
18743
|
+
keepArms = Option34.Boolean("--keep-arms", false);
|
|
18744
|
+
allowStub = Option34.Boolean("--allow-stub", false);
|
|
18745
|
+
deployment = Option34.String("--deployment");
|
|
18746
|
+
output = Option34.String("--output");
|
|
18747
|
+
observeWaitS = Option34.String("--observe-wait-s");
|
|
18416
18748
|
async executeCommand() {
|
|
18417
18749
|
await Promise.resolve();
|
|
18418
18750
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -18526,10 +18858,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
18526
18858
|
};
|
|
18527
18859
|
|
|
18528
18860
|
// src/commands/version.ts
|
|
18529
|
-
import { Command as
|
|
18861
|
+
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
18530
18862
|
var VersionCommand = class extends M8tCommand {
|
|
18531
18863
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
18532
|
-
static usage =
|
|
18864
|
+
static usage = Command37.Usage({
|
|
18533
18865
|
description: "Print the CLI version.",
|
|
18534
18866
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
18535
18867
|
examples: [
|
|
@@ -18537,8 +18869,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
18537
18869
|
["Print as JSON", "$0 version --output json"]
|
|
18538
18870
|
]
|
|
18539
18871
|
});
|
|
18540
|
-
output =
|
|
18541
|
-
verbose =
|
|
18872
|
+
output = Option35.String("--output", { description: "pretty | json | auto (default)" });
|
|
18873
|
+
verbose = Option35.Boolean("--verbose", false);
|
|
18542
18874
|
executeCommand() {
|
|
18543
18875
|
const mode = resolveOutputMode(
|
|
18544
18876
|
this.output ?? "auto",
|
|
@@ -18569,18 +18901,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
18569
18901
|
};
|
|
18570
18902
|
|
|
18571
18903
|
// src/commands/whoami.ts
|
|
18572
|
-
import { Command as
|
|
18904
|
+
import { Command as Command38, Option as Option36 } from "clipanion";
|
|
18573
18905
|
var WhoamiCommand = class extends M8tCommand {
|
|
18574
18906
|
static paths = [["whoami"]];
|
|
18575
|
-
static usage =
|
|
18907
|
+
static usage = Command38.Usage({
|
|
18576
18908
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
18577
18909
|
});
|
|
18578
|
-
output =
|
|
18579
|
-
verbose =
|
|
18580
|
-
subscription =
|
|
18910
|
+
output = Option36.String("--output");
|
|
18911
|
+
verbose = Option36.Boolean("--verbose", false);
|
|
18912
|
+
subscription = Option36.String("--subscription", {
|
|
18581
18913
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
18582
18914
|
});
|
|
18583
|
-
resourceGroup =
|
|
18915
|
+
resourceGroup = Option36.String("--resource-group", {
|
|
18584
18916
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
18585
18917
|
});
|
|
18586
18918
|
async executeCommand() {
|
|
@@ -18645,7 +18977,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
18645
18977
|
};
|
|
18646
18978
|
|
|
18647
18979
|
// src/commands/status.ts
|
|
18648
|
-
import { Command as
|
|
18980
|
+
import { Command as Command39, Option as Option37 } from "clipanion";
|
|
18649
18981
|
|
|
18650
18982
|
// src/lib/azd.ts
|
|
18651
18983
|
init_errors();
|
|
@@ -18710,10 +19042,10 @@ async function resolveLocalContext() {
|
|
|
18710
19042
|
// src/commands/status.ts
|
|
18711
19043
|
var StatusCommand = class extends M8tCommand {
|
|
18712
19044
|
static paths = [["status"]];
|
|
18713
|
-
static usage =
|
|
19045
|
+
static usage = Command39.Usage({
|
|
18714
19046
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
18715
19047
|
});
|
|
18716
|
-
output =
|
|
19048
|
+
output = Option37.String("--output");
|
|
18717
19049
|
async executeCommand() {
|
|
18718
19050
|
const mode = resolveOutputMode(
|
|
18719
19051
|
this.output,
|
|
@@ -18751,7 +19083,7 @@ var StatusCommand = class extends M8tCommand {
|
|
|
18751
19083
|
};
|
|
18752
19084
|
|
|
18753
19085
|
// src/commands/doctor.ts
|
|
18754
|
-
import { Command as
|
|
19086
|
+
import { Command as Command40, Option as Option38 } from "clipanion";
|
|
18755
19087
|
import { DefaultAzureCredential as DefaultAzureCredential16 } from "@azure/identity";
|
|
18756
19088
|
import * as fs19 from "fs";
|
|
18757
19089
|
import * as os9 from "os";
|
|
@@ -19002,12 +19334,12 @@ function probeRepoRoot() {
|
|
|
19002
19334
|
}
|
|
19003
19335
|
var DoctorCommand = class extends M8tCommand {
|
|
19004
19336
|
static paths = [["doctor"]];
|
|
19005
|
-
static usage =
|
|
19337
|
+
static usage = Command40.Usage({
|
|
19006
19338
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
19007
19339
|
});
|
|
19008
|
-
output =
|
|
19009
|
-
agent =
|
|
19010
|
-
resourceGroup =
|
|
19340
|
+
output = Option38.String("--output");
|
|
19341
|
+
agent = Option38.String("--agent");
|
|
19342
|
+
resourceGroup = Option38.String("--resource-group", {
|
|
19011
19343
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
19012
19344
|
});
|
|
19013
19345
|
async executeCommand() {
|
|
@@ -19123,12 +19455,12 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
19123
19455
|
};
|
|
19124
19456
|
|
|
19125
19457
|
// src/commands/switch.ts
|
|
19126
|
-
import { Command as
|
|
19458
|
+
import { Command as Command41, Option as Option39 } from "clipanion";
|
|
19127
19459
|
|
|
19128
19460
|
// src/lib/profiles.ts
|
|
19129
19461
|
import * as fs20 from "fs/promises";
|
|
19130
19462
|
import * as path21 from "path";
|
|
19131
|
-
import { parse as
|
|
19463
|
+
import { parse as parseYaml10, stringify as stringifyYaml4 } from "yaml";
|
|
19132
19464
|
function profilesDir() {
|
|
19133
19465
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
19134
19466
|
return path21.join(home, ".m8t-stack", "profiles");
|
|
@@ -19153,7 +19485,7 @@ async function listProfiles() {
|
|
|
19153
19485
|
async function readProfile(name) {
|
|
19154
19486
|
try {
|
|
19155
19487
|
const raw = await fs20.readFile(getProfilePath(name), "utf8");
|
|
19156
|
-
const parsed =
|
|
19488
|
+
const parsed = parseYaml10(raw);
|
|
19157
19489
|
if (!parsed || typeof parsed !== "object") return null;
|
|
19158
19490
|
return parsed;
|
|
19159
19491
|
} catch (e) {
|
|
@@ -19263,14 +19595,14 @@ async function profileSwitch(name, asName) {
|
|
|
19263
19595
|
init_errors();
|
|
19264
19596
|
var SwitchCommand = class extends M8tCommand {
|
|
19265
19597
|
static paths = [["switch"]];
|
|
19266
|
-
static usage =
|
|
19598
|
+
static usage = Command41.Usage({
|
|
19267
19599
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
19268
19600
|
});
|
|
19269
|
-
profile =
|
|
19270
|
-
subscription =
|
|
19271
|
-
list =
|
|
19272
|
-
as =
|
|
19273
|
-
output =
|
|
19601
|
+
profile = Option39.String({ required: false });
|
|
19602
|
+
subscription = Option39.String("--subscription");
|
|
19603
|
+
list = Option39.Boolean("--list", false);
|
|
19604
|
+
as = Option39.String("--as");
|
|
19605
|
+
output = Option39.String("--output");
|
|
19274
19606
|
async executeCommand() {
|
|
19275
19607
|
const mode = resolveOutputMode(
|
|
19276
19608
|
this.output,
|
|
@@ -19327,7 +19659,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
19327
19659
|
|
|
19328
19660
|
// src/commands/open.ts
|
|
19329
19661
|
import { spawn as spawn5 } from "child_process";
|
|
19330
|
-
import { Command as
|
|
19662
|
+
import { Command as Command42, Option as Option40 } from "clipanion";
|
|
19331
19663
|
|
|
19332
19664
|
// src/lib/open-targets.ts
|
|
19333
19665
|
init_errors();
|
|
@@ -19373,14 +19705,14 @@ function openUrl(url) {
|
|
|
19373
19705
|
}
|
|
19374
19706
|
var OpenCommand = class extends M8tCommand {
|
|
19375
19707
|
static paths = [["open"]];
|
|
19376
|
-
static usage =
|
|
19708
|
+
static usage = Command42.Usage({
|
|
19377
19709
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
19378
19710
|
details: "Targets: webapp (deployed app, default) | foundry (ai.azure.com) | portal (resource group in the Azure portal). Pass --print to emit the URL instead of launching a browser (also the default when stdout isn't a TTY)."
|
|
19379
19711
|
});
|
|
19380
|
-
target =
|
|
19381
|
-
print =
|
|
19382
|
-
output =
|
|
19383
|
-
resourceGroup =
|
|
19712
|
+
target = Option40.String({ required: false });
|
|
19713
|
+
print = Option40.Boolean("--print", false);
|
|
19714
|
+
output = Option40.String("--output");
|
|
19715
|
+
resourceGroup = Option40.String("--resource-group", {
|
|
19384
19716
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
19385
19717
|
});
|
|
19386
19718
|
async executeCommand() {
|
|
@@ -19424,7 +19756,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
19424
19756
|
};
|
|
19425
19757
|
|
|
19426
19758
|
// src/commands/dream/run.ts
|
|
19427
|
-
import { Command as
|
|
19759
|
+
import { Command as Command43, Option as Option41 } from "clipanion";
|
|
19428
19760
|
import { AzureCliCredential } from "@azure/identity";
|
|
19429
19761
|
import { TableClient as TableClient2 } from "@azure/data-tables";
|
|
19430
19762
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
@@ -20323,16 +20655,16 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
20323
20655
|
for (const pk of physicalPks) {
|
|
20324
20656
|
const rowsForPk = consumedRowsByPk[pk] ?? [];
|
|
20325
20657
|
const { ts, consumedRowKeys } = computeWatermark(rowsForPk, now.toISOString(), safetyLagSeconds);
|
|
20326
|
-
let
|
|
20658
|
+
let candidate2 = ts;
|
|
20327
20659
|
const prior = Object.hasOwn(cursor.watermarks, pk) ? cursor.watermarks[pk].ts : void 0;
|
|
20328
20660
|
if (prior)
|
|
20329
20661
|
priorTsByPk[pk] = prior;
|
|
20330
|
-
if (prior &&
|
|
20331
|
-
|
|
20662
|
+
if (prior && candidate2 < prior)
|
|
20663
|
+
candidate2 = prior;
|
|
20332
20664
|
const failed = failedTsByPk[pk];
|
|
20333
|
-
if (failed &&
|
|
20334
|
-
|
|
20335
|
-
watermarks[pk] = { ts:
|
|
20665
|
+
if (failed && candidate2 >= failed)
|
|
20666
|
+
candidate2 = justBefore(failed);
|
|
20667
|
+
watermarks[pk] = { ts: candidate2, consumedRowKeys };
|
|
20336
20668
|
consumedKeysByPk[pk] = consumedRowKeys;
|
|
20337
20669
|
}
|
|
20338
20670
|
return {
|
|
@@ -20726,12 +21058,12 @@ function validateDelta(d) {
|
|
|
20726
21058
|
}
|
|
20727
21059
|
function extractJson(text) {
|
|
20728
21060
|
const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(text);
|
|
20729
|
-
const
|
|
20730
|
-
const start =
|
|
20731
|
-
const end =
|
|
21061
|
+
const candidate2 = (fenced ? fenced[1] : text).trim();
|
|
21062
|
+
const start = candidate2.indexOf("{");
|
|
21063
|
+
const end = candidate2.lastIndexOf("}");
|
|
20732
21064
|
if (start === -1 || end === -1 || end < start)
|
|
20733
21065
|
throw new Error("no JSON object found");
|
|
20734
|
-
return JSON.parse(
|
|
21066
|
+
return JSON.parse(candidate2.slice(start, end + 1));
|
|
20735
21067
|
}
|
|
20736
21068
|
async function propose(model, system, user, opts = {}) {
|
|
20737
21069
|
const sleep2 = opts.sleep ?? defaultSleep2;
|
|
@@ -21311,14 +21643,14 @@ function narrowAdvance(src, kept) {
|
|
|
21311
21643
|
for (const pk of Object.keys(src.watermarks)) {
|
|
21312
21644
|
const rows = keptRowsByPk[pk] ?? [];
|
|
21313
21645
|
const { ts, consumedRowKeys } = computeWatermark(rows, now, safetyLagSeconds);
|
|
21314
|
-
let
|
|
21646
|
+
let candidate2 = ts;
|
|
21315
21647
|
const prior = priorTsByPk[pk];
|
|
21316
|
-
if (prior &&
|
|
21317
|
-
|
|
21648
|
+
if (prior && candidate2 < prior)
|
|
21649
|
+
candidate2 = prior;
|
|
21318
21650
|
const failed = src.failedTsByPk[pk];
|
|
21319
|
-
if (failed &&
|
|
21320
|
-
|
|
21321
|
-
watermarks[pk] = { ts:
|
|
21651
|
+
if (failed && candidate2 >= failed)
|
|
21652
|
+
candidate2 = justBefore(failed);
|
|
21653
|
+
watermarks[pk] = { ts: candidate2, consumedRowKeys };
|
|
21322
21654
|
consumedRowsByPk[pk] = consumedRowKeys;
|
|
21323
21655
|
}
|
|
21324
21656
|
return {
|
|
@@ -21420,7 +21752,7 @@ async function emitDigest(input, deps, digest) {
|
|
|
21420
21752
|
}
|
|
21421
21753
|
|
|
21422
21754
|
// ../../packages/brain/engine/dist/esm/dream/config.js
|
|
21423
|
-
import { parse as
|
|
21755
|
+
import { parse as parseYaml11 } from "yaml";
|
|
21424
21756
|
var DEFAULT_DREAM_MODEL = "gpt-5.4";
|
|
21425
21757
|
function asCadence(v) {
|
|
21426
21758
|
return v === "nightly" ? "nightly" : "off";
|
|
@@ -21428,7 +21760,7 @@ function asCadence(v) {
|
|
|
21428
21760
|
function parseDreamerConfig(rawYaml, env) {
|
|
21429
21761
|
let parsed;
|
|
21430
21762
|
try {
|
|
21431
|
-
parsed =
|
|
21763
|
+
parsed = parseYaml11(rawYaml);
|
|
21432
21764
|
} catch {
|
|
21433
21765
|
return { enabled: false, cadence: "off", model: env.M8T_DREAM_MODEL ?? DEFAULT_DREAM_MODEL };
|
|
21434
21766
|
}
|
|
@@ -21441,14 +21773,17 @@ function parseDreamerConfig(rawYaml, env) {
|
|
|
21441
21773
|
};
|
|
21442
21774
|
}
|
|
21443
21775
|
|
|
21776
|
+
// ../../packages/brain/engine/dist/esm/cost-report/config.js
|
|
21777
|
+
import { parse as parseYaml12 } from "yaml";
|
|
21778
|
+
|
|
21444
21779
|
// ../../packages/brain/engine/dist/esm/librarian/codify/persistence-guard.js
|
|
21445
21780
|
var ZERO_WIDTH_RE = new RegExp("\u200B|\u200C|\u200D|\uFEFF", "g");
|
|
21446
21781
|
|
|
21447
21782
|
// ../../packages/brain/engine/dist/esm/librarian/janitor/frontmatter.js
|
|
21448
|
-
import { parse as
|
|
21783
|
+
import { parse as parseYaml13, stringify as stringifyYaml5 } from "yaml";
|
|
21449
21784
|
|
|
21450
21785
|
// ../../packages/brain/engine/dist/esm/librarian/config.js
|
|
21451
|
-
import { parse as
|
|
21786
|
+
import { parse as parseYaml14 } from "yaml";
|
|
21452
21787
|
|
|
21453
21788
|
// src/lib/dream-discovery.ts
|
|
21454
21789
|
init_http();
|
|
@@ -21565,20 +21900,20 @@ function redactTranscripts(input) {
|
|
|
21565
21900
|
}
|
|
21566
21901
|
var DreamRunCommand = class extends M8tCommand {
|
|
21567
21902
|
static paths = [["dream", "run"]];
|
|
21568
|
-
static usage =
|
|
21903
|
+
static usage = Command43.Usage({
|
|
21569
21904
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes).",
|
|
21570
21905
|
details: "Builds AzureCliCredential + resolves the Foundry project, ledger table, and Log Analytics workspace, runs the consumption pipeline, and prints the skip-ledger, the partition invariant, and harvest stats. Transcripts are metadata-only unless --show-transcripts is passed."
|
|
21571
21906
|
});
|
|
21572
|
-
worker =
|
|
21573
|
-
dryRun =
|
|
21574
|
-
since =
|
|
21575
|
-
reset =
|
|
21576
|
-
showTranscripts =
|
|
21907
|
+
worker = Option41.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
21908
|
+
dryRun = Option41.Boolean("--dry-run", false, { description: "Read-only harvest; no model call, no writes." });
|
|
21909
|
+
since = Option41.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
|
|
21910
|
+
reset = Option41.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
|
|
21911
|
+
showTranscripts = Option41.Boolean("--show-transcripts", false, {
|
|
21577
21912
|
description: "Print transcript bodies (default: metadata only)."
|
|
21578
21913
|
});
|
|
21579
|
-
subscription =
|
|
21580
|
-
endpoint =
|
|
21581
|
-
output =
|
|
21914
|
+
subscription = Option41.String("--subscription");
|
|
21915
|
+
endpoint = Option41.String("--endpoint");
|
|
21916
|
+
output = Option41.String("--output");
|
|
21582
21917
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
21583
21918
|
deps;
|
|
21584
21919
|
async executeCommand() {
|
|
@@ -21926,7 +22261,7 @@ function defaultDeps(overrides) {
|
|
|
21926
22261
|
}
|
|
21927
22262
|
|
|
21928
22263
|
// src/commands/foundry/create.ts
|
|
21929
|
-
import { Command as
|
|
22264
|
+
import { Command as Command44, Option as Option42 } from "clipanion";
|
|
21930
22265
|
|
|
21931
22266
|
// src/lib/foundry-create.ts
|
|
21932
22267
|
init_errors();
|
|
@@ -22164,7 +22499,7 @@ async function createFoundryProject(args) {
|
|
|
22164
22499
|
init_errors();
|
|
22165
22500
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
22166
22501
|
static paths = [["foundry", "create"]];
|
|
22167
|
-
static usage =
|
|
22502
|
+
static usage = Command44.Usage({
|
|
22168
22503
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
22169
22504
|
details: "Non-interactive and idempotent. Creates the AIServices account (custom subdomain + project management), a project, and a model deployment (default gpt-4.1-mini @ capacity 50). Region must be hosted-agent-eligible. Emits the project endpoint as structured output. Re-run is a clean no-op (account/project skipped if present; deployment capacity converges UP, never down).",
|
|
22170
22505
|
examples: [
|
|
@@ -22173,16 +22508,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
22173
22508
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
22174
22509
|
]
|
|
22175
22510
|
});
|
|
22176
|
-
resourceGroup =
|
|
22177
|
-
location =
|
|
22178
|
-
account =
|
|
22179
|
-
project =
|
|
22180
|
-
model =
|
|
22181
|
-
modelVersion =
|
|
22182
|
-
capacity =
|
|
22183
|
-
subscription =
|
|
22184
|
-
skipQuotaCheck =
|
|
22185
|
-
output =
|
|
22511
|
+
resourceGroup = Option42.String("--resource-group");
|
|
22512
|
+
location = Option42.String("--location");
|
|
22513
|
+
account = Option42.String("--account");
|
|
22514
|
+
project = Option42.String("--project", "m8t");
|
|
22515
|
+
model = Option42.String("--model", "gpt-4.1-mini");
|
|
22516
|
+
modelVersion = Option42.String("--model-version", "2025-04-14");
|
|
22517
|
+
capacity = Option42.String("--capacity", "50");
|
|
22518
|
+
subscription = Option42.String("--subscription");
|
|
22519
|
+
skipQuotaCheck = Option42.Boolean("--skip-quota-check", false);
|
|
22520
|
+
output = Option42.String("--output");
|
|
22186
22521
|
async executeCommand() {
|
|
22187
22522
|
const mode = resolveOutputMode(
|
|
22188
22523
|
this.output,
|
|
@@ -22254,22 +22589,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
22254
22589
|
};
|
|
22255
22590
|
|
|
22256
22591
|
// src/commands/foundry/await-ready.ts
|
|
22257
|
-
import { Command as
|
|
22592
|
+
import { Command as Command45, Option as Option43 } from "clipanion";
|
|
22258
22593
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
22259
22594
|
init_errors();
|
|
22260
22595
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
22261
22596
|
static paths = [["foundry", "await-ready"]];
|
|
22262
|
-
static usage =
|
|
22597
|
+
static usage = Command45.Usage({
|
|
22263
22598
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
22264
22599
|
details: "Probes the project (GET /agents) until it returns 200 on a few consecutive tries, or fails clearly after a bounded budget. A newly-created account can serve intermittent 404 'Project not found' for minutes; run this after 'foundry create' and before deploying agents so the worker phase doesn't catch the unstable window.",
|
|
22265
22600
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
22266
22601
|
});
|
|
22267
|
-
endpoint =
|
|
22268
|
-
consecutive =
|
|
22269
|
-
attempts =
|
|
22270
|
-
interval =
|
|
22271
|
-
subscription =
|
|
22272
|
-
output =
|
|
22602
|
+
endpoint = Option43.String("--endpoint");
|
|
22603
|
+
consecutive = Option43.String("--consecutive", "3");
|
|
22604
|
+
attempts = Option43.String("--attempts", "60");
|
|
22605
|
+
interval = Option43.String("--interval", "5");
|
|
22606
|
+
subscription = Option43.String("--subscription");
|
|
22607
|
+
output = Option43.String("--output");
|
|
22273
22608
|
async executeCommand() {
|
|
22274
22609
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
22275
22610
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -22303,7 +22638,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
22303
22638
|
};
|
|
22304
22639
|
|
|
22305
22640
|
// src/commands/bootstrap/preflight.ts
|
|
22306
|
-
import { Command as
|
|
22641
|
+
import { Command as Command46, Option as Option44 } from "clipanion";
|
|
22307
22642
|
init_errors();
|
|
22308
22643
|
|
|
22309
22644
|
// src/lib/bootstrap-preflight.ts
|
|
@@ -22377,7 +22712,7 @@ function buildPreflightBanner(who) {
|
|
|
22377
22712
|
// src/commands/bootstrap/preflight.ts
|
|
22378
22713
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
22379
22714
|
static paths = [["bootstrap", "preflight"]];
|
|
22380
|
-
static usage =
|
|
22715
|
+
static usage = Command46.Usage({
|
|
22381
22716
|
description: "Loudly verify you can install m8t-stack (Owner/UAA + directory admin) and hard-stop if not.",
|
|
22382
22717
|
details: "Step 1 of `m8t bootstrap`. Prints an unmissable admin-credentials notice, then checks: Owner or User Access Administrator at subscription scope (hard requirement), directory-admin capability to register the app (or pass --client-id), and registers Microsoft.ContainerInstance. Exits non-zero with the exact failing check + remedy.",
|
|
22383
22718
|
examples: [
|
|
@@ -22385,8 +22720,8 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
22385
22720
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
22386
22721
|
]
|
|
22387
22722
|
});
|
|
22388
|
-
clientId =
|
|
22389
|
-
subscription =
|
|
22723
|
+
clientId = Option44.String("--client-id");
|
|
22724
|
+
subscription = Option44.String("--subscription");
|
|
22390
22725
|
async executeCommand() {
|
|
22391
22726
|
const clientId = typeof this.clientId === "string" ? this.clientId : void 0;
|
|
22392
22727
|
const account = await getAzAccount();
|
|
@@ -22445,7 +22780,7 @@ ${colors.error(" " + why)}
|
|
|
22445
22780
|
import * as fs23 from "fs";
|
|
22446
22781
|
import * as os12 from "os";
|
|
22447
22782
|
import * as path24 from "path";
|
|
22448
|
-
import { Command as
|
|
22783
|
+
import { Command as Command47, Option as Option45 } from "clipanion";
|
|
22449
22784
|
init_errors();
|
|
22450
22785
|
|
|
22451
22786
|
// src/lib/bootstrap-mi.ts
|
|
@@ -22698,7 +23033,7 @@ var ACI_NAME = "m8t-installer";
|
|
|
22698
23033
|
var MI_NAME = "m8t-installer-mi";
|
|
22699
23034
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
22700
23035
|
static paths = [["bootstrap", "launch"]];
|
|
22701
|
-
static usage =
|
|
23036
|
+
static usage = Command47.Usage({
|
|
22702
23037
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
22703
23038
|
details: "Step 2 of `m8t bootstrap` (run after `preflight`). Ensures the resource group, creates the m8t-stack app registration (or uses --client-id), creates a user-assigned managed identity granted Owner at subscription scope, and launches the published m8t-installer image as an ACI run-to-completion job under that identity. Writes ~/.m8t-stack/bootstrap.json for `status`/`reap`/`finish`.",
|
|
22704
23039
|
examples: [
|
|
@@ -22707,13 +23042,13 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
22707
23042
|
["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.0"]
|
|
22708
23043
|
]
|
|
22709
23044
|
});
|
|
22710
|
-
location =
|
|
22711
|
-
resourceGroup =
|
|
22712
|
-
clientId =
|
|
22713
|
-
subscription =
|
|
22714
|
-
installerTag =
|
|
22715
|
-
gatewayImageRef =
|
|
22716
|
-
githubAppCreds =
|
|
23045
|
+
location = Option45.String("--location");
|
|
23046
|
+
resourceGroup = Option45.String("--resource-group");
|
|
23047
|
+
clientId = Option45.String("--client-id");
|
|
23048
|
+
subscription = Option45.String("--subscription");
|
|
23049
|
+
installerTag = Option45.String("--installer-tag");
|
|
23050
|
+
gatewayImageRef = Option45.String("--gateway-image-ref");
|
|
23051
|
+
githubAppCreds = Option45.String("--github-app-creds");
|
|
22717
23052
|
async executeCommand() {
|
|
22718
23053
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
22719
23054
|
if (!location) {
|
|
@@ -22814,7 +23149,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
22814
23149
|
};
|
|
22815
23150
|
|
|
22816
23151
|
// src/commands/bootstrap/status.ts
|
|
22817
|
-
import { Command as
|
|
23152
|
+
import { Command as Command48, Option as Option46 } from "clipanion";
|
|
22818
23153
|
init_errors();
|
|
22819
23154
|
|
|
22820
23155
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -22842,13 +23177,13 @@ async function getAciState(opts) {
|
|
|
22842
23177
|
// src/commands/bootstrap/status.ts
|
|
22843
23178
|
var BootstrapStatusCommand = class extends M8tCommand {
|
|
22844
23179
|
static paths = [["bootstrap", "status"]];
|
|
22845
|
-
static usage =
|
|
23180
|
+
static usage = Command48.Usage({
|
|
22846
23181
|
description: "Show the cloud installer's live status (phase, progress, result).",
|
|
22847
23182
|
details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.",
|
|
22848
23183
|
examples: [["One read", "$0 bootstrap status"], ["Watch to completion", "$0 bootstrap status --watch"]]
|
|
22849
23184
|
});
|
|
22850
|
-
watch =
|
|
22851
|
-
output =
|
|
23185
|
+
watch = Option46.Boolean("--watch", false);
|
|
23186
|
+
output = Option46.String("--output");
|
|
22852
23187
|
async executeCommand() {
|
|
22853
23188
|
const state = await readBootstrapState();
|
|
22854
23189
|
if (!state) {
|
|
@@ -22919,7 +23254,7 @@ function formatStatus(d) {
|
|
|
22919
23254
|
}
|
|
22920
23255
|
|
|
22921
23256
|
// src/commands/bootstrap/reap.ts
|
|
22922
|
-
import { Command as
|
|
23257
|
+
import { Command as Command49, Option as Option47 } from "clipanion";
|
|
22923
23258
|
init_errors();
|
|
22924
23259
|
|
|
22925
23260
|
// src/lib/bootstrap-reap.ts
|
|
@@ -22995,14 +23330,14 @@ async function reapInstaller(opts) {
|
|
|
22995
23330
|
// src/commands/bootstrap/reap.ts
|
|
22996
23331
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
22997
23332
|
static paths = [["bootstrap", "reap"]];
|
|
22998
|
-
static usage =
|
|
23333
|
+
static usage = Command49.Usage({
|
|
22999
23334
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
23000
23335
|
details: "Runs locally on the 'done' signal (the installer can't delete its own identity). The platform RG and the gateway's own assignments persist. A failed install is left intact for diagnosis unless --force.",
|
|
23001
23336
|
examples: [["Reap after done", "$0 bootstrap reap"]]
|
|
23002
23337
|
});
|
|
23003
|
-
force =
|
|
23004
|
-
sweepOrphans =
|
|
23005
|
-
yes =
|
|
23338
|
+
force = Option47.Boolean("--force", false);
|
|
23339
|
+
sweepOrphans = Option47.Boolean("--sweep-orphans", false);
|
|
23340
|
+
yes = Option47.Boolean("--yes", false);
|
|
23006
23341
|
async executeCommand() {
|
|
23007
23342
|
if (this.sweepOrphans === true) {
|
|
23008
23343
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -23070,7 +23405,7 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
|
|
|
23070
23405
|
import * as fs24 from "fs/promises";
|
|
23071
23406
|
import * as os14 from "os";
|
|
23072
23407
|
import * as path26 from "path";
|
|
23073
|
-
import { Command as
|
|
23408
|
+
import { Command as Command50, Option as Option48 } from "clipanion";
|
|
23074
23409
|
init_errors();
|
|
23075
23410
|
|
|
23076
23411
|
// src/lib/company-profile-seed.ts
|
|
@@ -23400,14 +23735,14 @@ function renderInstallSummary(args) {
|
|
|
23400
23735
|
// src/commands/bootstrap/finish.ts
|
|
23401
23736
|
var BootstrapFinishCommand = class extends M8tCommand {
|
|
23402
23737
|
static paths = [["bootstrap", "finish"]];
|
|
23403
|
-
static usage =
|
|
23738
|
+
static usage = Command50.Usage({
|
|
23404
23739
|
description: "Point your local tools at the now-live platform (repo-root marker, discovery cache, next steps).",
|
|
23405
23740
|
details: "Final step of `m8t bootstrap` (after the install reaches done). Writes ~/.m8t-stack/repo-root, points your local tools at the live gateway, and \u2014 if you completed the onboarding questionnaire \u2014 seeds your brain-backed advisor's brain with your company profile (best-effort; never blocks finish). Run `m8t bootstrap seed-profile` to seed manually later.",
|
|
23406
23741
|
examples: [["Finish local", "$0 bootstrap finish --repo-root /path/to/m8t"]]
|
|
23407
23742
|
});
|
|
23408
|
-
repoRoot =
|
|
23409
|
-
subscription =
|
|
23410
|
-
resourceGroup =
|
|
23743
|
+
repoRoot = Option48.String("--repo-root");
|
|
23744
|
+
subscription = Option48.String("--subscription");
|
|
23745
|
+
resourceGroup = Option48.String("--resource-group");
|
|
23411
23746
|
async executeCommand() {
|
|
23412
23747
|
const state = await readBootstrapState();
|
|
23413
23748
|
if (!state) {
|
|
@@ -23494,7 +23829,7 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
|
|
|
23494
23829
|
import * as fs26 from "fs";
|
|
23495
23830
|
import * as os16 from "os";
|
|
23496
23831
|
import * as path28 from "path";
|
|
23497
|
-
import { Command as
|
|
23832
|
+
import { Command as Command51, Option as Option49 } from "clipanion";
|
|
23498
23833
|
import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/identity";
|
|
23499
23834
|
init_errors();
|
|
23500
23835
|
|
|
@@ -23765,7 +24100,7 @@ async function deploySimpleStacey(args) {
|
|
|
23765
24100
|
// src/commands/bootstrap/ui.ts
|
|
23766
24101
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
23767
24102
|
static paths = [["bootstrap", "ui"]];
|
|
23768
|
-
static usage =
|
|
24103
|
+
static usage = Command51.Usage({
|
|
23769
24104
|
description: "Deploy Simple Stacey + start the local onboarding chat UI in the background (returns immediately).",
|
|
23770
24105
|
details: [
|
|
23771
24106
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -23783,15 +24118,15 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
23783
24118
|
["Foreground (blocking, old behaviour)", "$0 bootstrap ui --repo-root /path/to/m8t --foreground"]
|
|
23784
24119
|
]
|
|
23785
24120
|
});
|
|
23786
|
-
repoRoot =
|
|
23787
|
-
port =
|
|
23788
|
-
endpoint =
|
|
24121
|
+
repoRoot = Option49.String("--repo-root");
|
|
24122
|
+
port = Option49.String("--port", "3000");
|
|
24123
|
+
endpoint = Option49.String("--endpoint", {
|
|
23789
24124
|
description: "Foundry project endpoint to target \u2014 disambiguates when the subscription has multiple projects."
|
|
23790
24125
|
});
|
|
23791
|
-
prepOnly =
|
|
23792
|
-
skipInstall =
|
|
23793
|
-
stop =
|
|
23794
|
-
foreground =
|
|
24126
|
+
prepOnly = Option49.Boolean("--prep-only", false);
|
|
24127
|
+
skipInstall = Option49.Boolean("--skip-install", false);
|
|
24128
|
+
stop = Option49.Boolean("--stop", false);
|
|
24129
|
+
foreground = Option49.Boolean("--foreground", false);
|
|
23795
24130
|
async executeCommand() {
|
|
23796
24131
|
if (this.stop === true) {
|
|
23797
24132
|
const stopped = stopOnboardingUi();
|
|
@@ -23898,10 +24233,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
23898
24233
|
};
|
|
23899
24234
|
|
|
23900
24235
|
// src/commands/bootstrap/seed-profile.ts
|
|
23901
|
-
import { Command as
|
|
24236
|
+
import { Command as Command52, Option as Option50 } from "clipanion";
|
|
23902
24237
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
23903
24238
|
static paths = [["bootstrap", "seed-profile"]];
|
|
23904
|
-
static usage =
|
|
24239
|
+
static usage = Command52.Usage({
|
|
23905
24240
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
|
|
23906
24241
|
details: "Reads the latest stacey-intake conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/azzy-brain via the GitHub App. Idempotent. --watch polls until the founder completes the questionnaire.",
|
|
23907
24242
|
examples: [
|
|
@@ -23909,11 +24244,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
23909
24244
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
23910
24245
|
]
|
|
23911
24246
|
});
|
|
23912
|
-
endpoint =
|
|
23913
|
-
brain =
|
|
23914
|
-
watch =
|
|
23915
|
-
timeout =
|
|
23916
|
-
githubAppCreds =
|
|
24247
|
+
endpoint = Option50.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
24248
|
+
brain = Option50.String("--brain", { description: "Override the brain repo (default <org>/stacey-brain)." });
|
|
24249
|
+
watch = Option50.Boolean("--watch", false, { description: "Poll until the questionnaire completes (or --timeout)." });
|
|
24250
|
+
timeout = Option50.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
24251
|
+
githubAppCreds = Option50.String("--github-app-creds");
|
|
23917
24252
|
async executeCommand() {
|
|
23918
24253
|
const ctx = await resolveSeedContext({
|
|
23919
24254
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -23998,6 +24333,7 @@ cli.register(CoderTeardownCommand);
|
|
|
23998
24333
|
cli.register(AzureExecDeployCommand);
|
|
23999
24334
|
cli.register(PlatformStatusCommand);
|
|
24000
24335
|
cli.register(PlatformUpdateCommand);
|
|
24336
|
+
cli.register(PlatformEnableCostReportCommand);
|
|
24001
24337
|
cli.register(DeployCommand);
|
|
24002
24338
|
cli.register(AgentDeployAdvisorCommand);
|
|
24003
24339
|
cli.register(AgentRemoveCommand);
|