@m8t-stack/cli 0.2.17 → 0.2.19
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 +1229 -347
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1227,7 +1227,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1227
1227
|
import { Builtins, Cli } from "clipanion";
|
|
1228
1228
|
|
|
1229
1229
|
// src/lib/package-version.ts
|
|
1230
|
-
var CLI_VERSION = "0.2.
|
|
1230
|
+
var CLI_VERSION = "0.2.19";
|
|
1231
1231
|
|
|
1232
1232
|
// src/lib/render-error.ts
|
|
1233
1233
|
init_errors();
|
|
@@ -3326,12 +3326,12 @@ function encodeURIPath(str4) {
|
|
|
3326
3326
|
return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
3327
3327
|
}
|
|
3328
3328
|
var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
3329
|
-
var createPathTagFunction = (pathEncoder = encodeURIPath) => function
|
|
3329
|
+
var createPathTagFunction = (pathEncoder = encodeURIPath) => function path33(statics, ...params) {
|
|
3330
3330
|
if (statics.length === 1)
|
|
3331
3331
|
return statics[0];
|
|
3332
3332
|
let postPath = false;
|
|
3333
3333
|
const invalidSegments = [];
|
|
3334
|
-
const
|
|
3334
|
+
const path34 = statics.reduce((previousValue, currentValue, index) => {
|
|
3335
3335
|
if (/[?#]/.test(currentValue)) {
|
|
3336
3336
|
postPath = true;
|
|
3337
3337
|
}
|
|
@@ -3348,7 +3348,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path32(sta
|
|
|
3348
3348
|
}
|
|
3349
3349
|
return previousValue + currentValue + (index === params.length ? "" : encoded);
|
|
3350
3350
|
}, "");
|
|
3351
|
-
const pathOnly =
|
|
3351
|
+
const pathOnly = path34.split(/[?#]/, 1)[0];
|
|
3352
3352
|
const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
|
|
3353
3353
|
let match;
|
|
3354
3354
|
while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
|
|
@@ -3369,10 +3369,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path32(sta
|
|
|
3369
3369
|
}, "");
|
|
3370
3370
|
throw new OpenAIError(`Path parameters result in path with invalid segments:
|
|
3371
3371
|
${invalidSegments.map((e) => e.error).join("\n")}
|
|
3372
|
-
${
|
|
3372
|
+
${path34}
|
|
3373
3373
|
${underline}`);
|
|
3374
3374
|
}
|
|
3375
|
-
return
|
|
3375
|
+
return path34;
|
|
3376
3376
|
};
|
|
3377
3377
|
var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
|
|
3378
3378
|
|
|
@@ -10889,9 +10889,9 @@ var OpenAI = class {
|
|
|
10889
10889
|
this.apiKey = token;
|
|
10890
10890
|
return true;
|
|
10891
10891
|
}
|
|
10892
|
-
buildURL(
|
|
10892
|
+
buildURL(path33, query, defaultBaseURL) {
|
|
10893
10893
|
const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
|
|
10894
|
-
const url = isAbsoluteURL(
|
|
10894
|
+
const url = isAbsoluteURL(path33) ? new URL(path33) : new URL(baseURL + (baseURL.endsWith("/") && path33.startsWith("/") ? path33.slice(1) : path33));
|
|
10895
10895
|
const defaultQuery = this.defaultQuery();
|
|
10896
10896
|
const pathQuery = Object.fromEntries(url.searchParams);
|
|
10897
10897
|
if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
|
|
@@ -10919,24 +10919,24 @@ var OpenAI = class {
|
|
|
10919
10919
|
*/
|
|
10920
10920
|
async prepareRequest(request, { url, options }) {
|
|
10921
10921
|
}
|
|
10922
|
-
get(
|
|
10923
|
-
return this.methodRequest("get",
|
|
10922
|
+
get(path33, opts) {
|
|
10923
|
+
return this.methodRequest("get", path33, opts);
|
|
10924
10924
|
}
|
|
10925
|
-
post(
|
|
10926
|
-
return this.methodRequest("post",
|
|
10925
|
+
post(path33, opts) {
|
|
10926
|
+
return this.methodRequest("post", path33, opts);
|
|
10927
10927
|
}
|
|
10928
|
-
patch(
|
|
10929
|
-
return this.methodRequest("patch",
|
|
10928
|
+
patch(path33, opts) {
|
|
10929
|
+
return this.methodRequest("patch", path33, opts);
|
|
10930
10930
|
}
|
|
10931
|
-
put(
|
|
10932
|
-
return this.methodRequest("put",
|
|
10931
|
+
put(path33, opts) {
|
|
10932
|
+
return this.methodRequest("put", path33, opts);
|
|
10933
10933
|
}
|
|
10934
|
-
delete(
|
|
10935
|
-
return this.methodRequest("delete",
|
|
10934
|
+
delete(path33, opts) {
|
|
10935
|
+
return this.methodRequest("delete", path33, opts);
|
|
10936
10936
|
}
|
|
10937
|
-
methodRequest(method,
|
|
10937
|
+
methodRequest(method, path33, opts) {
|
|
10938
10938
|
return this.request(Promise.resolve(opts).then((opts2) => {
|
|
10939
|
-
return { method, path:
|
|
10939
|
+
return { method, path: path33, ...opts2 };
|
|
10940
10940
|
}));
|
|
10941
10941
|
}
|
|
10942
10942
|
request(options, remainingRetries = null) {
|
|
@@ -11058,8 +11058,8 @@ var OpenAI = class {
|
|
|
11058
11058
|
}));
|
|
11059
11059
|
return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
|
|
11060
11060
|
}
|
|
11061
|
-
getAPIList(
|
|
11062
|
-
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path:
|
|
11061
|
+
getAPIList(path33, Page2, opts) {
|
|
11062
|
+
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path33, ...opts2 })) : { method: "get", path: path33, ...opts });
|
|
11063
11063
|
}
|
|
11064
11064
|
requestAPIList(Page2, options) {
|
|
11065
11065
|
const request = this.makeRequest(options, null, void 0);
|
|
@@ -11153,8 +11153,8 @@ var OpenAI = class {
|
|
|
11153
11153
|
}
|
|
11154
11154
|
async buildRequest(inputOptions, { retryCount = 0 } = {}) {
|
|
11155
11155
|
const options = { ...inputOptions };
|
|
11156
|
-
const { method, path:
|
|
11157
|
-
const url = this.buildURL(
|
|
11156
|
+
const { method, path: path33, query, defaultBaseURL } = options;
|
|
11157
|
+
const url = this.buildURL(path33, query, defaultBaseURL);
|
|
11158
11158
|
if ("timeout" in options)
|
|
11159
11159
|
validatePositiveInteger("timeout", options.timeout);
|
|
11160
11160
|
options.timeout = options.timeout ?? this.timeout;
|
|
@@ -12156,10 +12156,10 @@ var BindListCommand = class extends M8tCommand {
|
|
|
12156
12156
|
const qs = new URLSearchParams();
|
|
12157
12157
|
if (this.channel) qs.set("channel", this.channel);
|
|
12158
12158
|
if (this.status) qs.set("status", this.status);
|
|
12159
|
-
const
|
|
12159
|
+
const path33 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
|
|
12160
12160
|
const data = await apiCall(
|
|
12161
12161
|
{ gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
|
|
12162
|
-
{ method: "GET", path:
|
|
12162
|
+
{ method: "GET", path: path33 },
|
|
12163
12163
|
(msg) => this.context.stderr.write(msg + "\n")
|
|
12164
12164
|
);
|
|
12165
12165
|
if (mode === "json") {
|
|
@@ -13197,6 +13197,13 @@ var defaultGhExec = (cmd, args) => new Promise((resolve3) => {
|
|
|
13197
13197
|
const child = spawn2(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
13198
13198
|
let stdout = "";
|
|
13199
13199
|
let stderr = "";
|
|
13200
|
+
let settled = false;
|
|
13201
|
+
const done = (r) => {
|
|
13202
|
+
if (!settled) {
|
|
13203
|
+
settled = true;
|
|
13204
|
+
resolve3(r);
|
|
13205
|
+
}
|
|
13206
|
+
};
|
|
13200
13207
|
child.stdout.on("data", (d) => {
|
|
13201
13208
|
stdout += d.toString();
|
|
13202
13209
|
});
|
|
@@ -13204,7 +13211,10 @@ var defaultGhExec = (cmd, args) => new Promise((resolve3) => {
|
|
|
13204
13211
|
stderr += d.toString();
|
|
13205
13212
|
});
|
|
13206
13213
|
child.on("close", (code) => {
|
|
13207
|
-
|
|
13214
|
+
done({ stdout, stderr, exitCode: code ?? -1 });
|
|
13215
|
+
});
|
|
13216
|
+
child.on("error", (err) => {
|
|
13217
|
+
done({ stdout, stderr: stderr || err.message, exitCode: -1 });
|
|
13208
13218
|
});
|
|
13209
13219
|
});
|
|
13210
13220
|
async function isGhAuthed(exec = defaultGhExec) {
|
|
@@ -13360,8 +13370,8 @@ ${r.stderr}`;
|
|
|
13360
13370
|
message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
|
|
13361
13371
|
});
|
|
13362
13372
|
},
|
|
13363
|
-
getFile: async (
|
|
13364
|
-
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${
|
|
13373
|
+
getFile: async (path33) => {
|
|
13374
|
+
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path33}`]);
|
|
13365
13375
|
return r.exitCode === 0 ? r.stdout : null;
|
|
13366
13376
|
},
|
|
13367
13377
|
isEmpty: async () => {
|
|
@@ -13535,15 +13545,21 @@ function discoverKvUri(env, override) {
|
|
|
13535
13545
|
hint: "Pass --kv-uri or set AZURE_KEYVAULT_URI / KEYVAULT_URI."
|
|
13536
13546
|
});
|
|
13537
13547
|
}
|
|
13538
|
-
function
|
|
13548
|
+
function tryResolveRepoRoot() {
|
|
13539
13549
|
const marker = path5.join(os2.homedir(), ".m8t-stack", "repo-root");
|
|
13540
|
-
if (!fs5.existsSync(marker))
|
|
13550
|
+
if (!fs5.existsSync(marker)) return null;
|
|
13551
|
+
const root = fs5.readFileSync(marker, "utf8").trim();
|
|
13552
|
+
return root.length > 0 ? root : null;
|
|
13553
|
+
}
|
|
13554
|
+
function resolveRepoRoot() {
|
|
13555
|
+
const root = tryResolveRepoRoot();
|
|
13556
|
+
if (root === null) {
|
|
13541
13557
|
throw new LocalCliError({
|
|
13542
13558
|
code: "M8T_REPO_ROOT_MISSING",
|
|
13543
13559
|
message: "~/.m8t-stack/repo-root marker missing \u2014 m8t install never completed."
|
|
13544
13560
|
});
|
|
13545
13561
|
}
|
|
13546
|
-
return
|
|
13562
|
+
return root;
|
|
13547
13563
|
}
|
|
13548
13564
|
|
|
13549
13565
|
// src/commands/brain/check-app.ts
|
|
@@ -14309,7 +14325,7 @@ function appRepoProbe(args) {
|
|
|
14309
14325
|
const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
14310
14326
|
return res.status;
|
|
14311
14327
|
},
|
|
14312
|
-
getFile: (
|
|
14328
|
+
getFile: (path33) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path33, fetchImpl: args.fetchImpl }),
|
|
14313
14329
|
isEmpty: async () => {
|
|
14314
14330
|
const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
14315
14331
|
return res.status === 404;
|
|
@@ -17447,8 +17463,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
17447
17463
|
hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
|
|
17448
17464
|
});
|
|
17449
17465
|
}
|
|
17450
|
-
const
|
|
17451
|
-
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${
|
|
17466
|
+
const path33 = repo.replace(/^ghcr\.io\//, "");
|
|
17467
|
+
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path33}:pull`);
|
|
17452
17468
|
if (!tokenRes.ok) {
|
|
17453
17469
|
throw new LocalCliError({
|
|
17454
17470
|
code: "PLATFORM_GHCR_TOKEN_FAILED",
|
|
@@ -17464,7 +17480,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
17464
17480
|
hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
|
|
17465
17481
|
});
|
|
17466
17482
|
}
|
|
17467
|
-
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${
|
|
17483
|
+
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path33}/tags/list`, {
|
|
17468
17484
|
headers: { Authorization: `Bearer ${token}` }
|
|
17469
17485
|
});
|
|
17470
17486
|
if (!tagsRes.ok) {
|
|
@@ -17480,7 +17496,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
17480
17496
|
// src/lib/release-channel.ts
|
|
17481
17497
|
import * as fs18 from "fs";
|
|
17482
17498
|
|
|
17483
|
-
//
|
|
17499
|
+
// ../../packages/platform-release/dist/esm/manifest.js
|
|
17484
17500
|
var SEVERITIES = ["critical", "recommended", "optional"];
|
|
17485
17501
|
var IMAGE_KEYS = ["gateway", "codingAgent", "azureExecutor", "installer"];
|
|
17486
17502
|
function isObj2(v) {
|
|
@@ -17488,16 +17504,20 @@ function isObj2(v) {
|
|
|
17488
17504
|
}
|
|
17489
17505
|
function validateManifest(m) {
|
|
17490
17506
|
const errors = [];
|
|
17491
|
-
if (!isObj2(m))
|
|
17492
|
-
|
|
17507
|
+
if (!isObj2(m))
|
|
17508
|
+
return ["manifest is not an object"];
|
|
17509
|
+
if (m.schemaVersion !== 1)
|
|
17510
|
+
errors.push("schemaVersion must be the integer 1");
|
|
17493
17511
|
const p = m.platform;
|
|
17494
17512
|
if (!isObj2(p)) {
|
|
17495
17513
|
errors.push("platform is required");
|
|
17496
17514
|
} else {
|
|
17497
17515
|
for (const f of ["version", "tag", "releasedAt", "commit", "notes", "notesUrl"]) {
|
|
17498
|
-
if (typeof p[f] !== "string" || p[f].length === 0)
|
|
17516
|
+
if (typeof p[f] !== "string" || p[f].length === 0)
|
|
17517
|
+
errors.push(`platform.${f} is required`);
|
|
17499
17518
|
}
|
|
17500
|
-
if (!SEVERITIES.includes(p.severity))
|
|
17519
|
+
if (!SEVERITIES.includes(p.severity))
|
|
17520
|
+
errors.push(`platform.severity must be one of ${SEVERITIES.join("|")}`);
|
|
17501
17521
|
if (!(typeof p.previousVersion === "string" || p.previousVersion === null)) {
|
|
17502
17522
|
errors.push("platform.previousVersion must be a string or null");
|
|
17503
17523
|
}
|
|
@@ -17513,9 +17533,11 @@ function validateManifest(m) {
|
|
|
17513
17533
|
errors.push(`components.${key2} is required`);
|
|
17514
17534
|
continue;
|
|
17515
17535
|
}
|
|
17516
|
-
if (img.kind !== "image")
|
|
17536
|
+
if (img.kind !== "image")
|
|
17537
|
+
errors.push(`components.${key2}.kind must be "image"`);
|
|
17517
17538
|
for (const f of ["ref", "tag", "version"]) {
|
|
17518
|
-
if (typeof img[f] !== "string" || img[f].length === 0)
|
|
17539
|
+
if (typeof img[f] !== "string" || img[f].length === 0)
|
|
17540
|
+
errors.push(`components.${key2}.${f} is required`);
|
|
17519
17541
|
}
|
|
17520
17542
|
if (typeof img.digest !== "string" || !/^sha256:[0-9a-f]+$/.test(img.digest)) {
|
|
17521
17543
|
errors.push(`components.${key2}.digest must be a sha256:\u2026 digest`);
|
|
@@ -17526,7 +17548,8 @@ function validateManifest(m) {
|
|
|
17526
17548
|
errors.push("components.cli (kind: npm) is required");
|
|
17527
17549
|
} else {
|
|
17528
17550
|
for (const f of ["package", "version", "min", "recommended"]) {
|
|
17529
|
-
if (typeof cli2[f] !== "string" || cli2[f].length === 0)
|
|
17551
|
+
if (typeof cli2[f] !== "string" || cli2[f].length === 0)
|
|
17552
|
+
errors.push(`components.cli.${f} is required`);
|
|
17530
17553
|
}
|
|
17531
17554
|
}
|
|
17532
17555
|
const personas = c.personas;
|
|
@@ -17567,11 +17590,75 @@ function validateManifest(m) {
|
|
|
17567
17590
|
return errors;
|
|
17568
17591
|
}
|
|
17569
17592
|
|
|
17593
|
+
// ../../packages/platform-release/dist/esm/stamp.js
|
|
17594
|
+
var TABLE = "Metadata";
|
|
17595
|
+
var PK = "system";
|
|
17596
|
+
var RK = "platform";
|
|
17597
|
+
function stampToEntity(s) {
|
|
17598
|
+
return {
|
|
17599
|
+
partitionKey: PK,
|
|
17600
|
+
rowKey: RK,
|
|
17601
|
+
platformVersion: s.platformVersion,
|
|
17602
|
+
updatedAt: s.updatedAt,
|
|
17603
|
+
lastResult: s.lastResult,
|
|
17604
|
+
value: JSON.stringify(s)
|
|
17605
|
+
};
|
|
17606
|
+
}
|
|
17607
|
+
function entityToStamp(e) {
|
|
17608
|
+
const raw = e.value;
|
|
17609
|
+
if (typeof raw !== "string")
|
|
17610
|
+
return null;
|
|
17611
|
+
try {
|
|
17612
|
+
return JSON.parse(raw);
|
|
17613
|
+
} catch {
|
|
17614
|
+
return null;
|
|
17615
|
+
}
|
|
17616
|
+
}
|
|
17617
|
+
|
|
17618
|
+
// ../../packages/platform-release/dist/esm/channel-url.js
|
|
17619
|
+
var CHANNEL_LATEST_URL = "https://github.com/m8t-run/m8t/releases/latest/download/manifest.json";
|
|
17620
|
+
function platformTag(version) {
|
|
17621
|
+
const bare = version.trim().replace(/^platform-/, "").replace(/^v/, "");
|
|
17622
|
+
return `platform-v${bare}`;
|
|
17623
|
+
}
|
|
17624
|
+
function channelUrlForVersion(version) {
|
|
17625
|
+
return `https://github.com/m8t-run/m8t/releases/download/${platformTag(version)}/manifest.json`;
|
|
17626
|
+
}
|
|
17627
|
+
|
|
17628
|
+
// ../../packages/platform-release/dist/esm/apply-request.js
|
|
17629
|
+
var APPLY_REQUEST_PK = "system";
|
|
17630
|
+
var APPLY_REQUEST_RK = "apply-request";
|
|
17631
|
+
var IN_FLIGHT = ["claimed", "applying"];
|
|
17632
|
+
function isInFlight(status) {
|
|
17633
|
+
return IN_FLIGHT.includes(status);
|
|
17634
|
+
}
|
|
17635
|
+
function applyRequestToEntity(r) {
|
|
17636
|
+
return { partitionKey: APPLY_REQUEST_PK, rowKey: APPLY_REQUEST_RK, value: JSON.stringify(r), updatedAt: r.updatedAt };
|
|
17637
|
+
}
|
|
17638
|
+
function entityToApplyRequest(e) {
|
|
17639
|
+
return JSON.parse(e.value);
|
|
17640
|
+
}
|
|
17641
|
+
|
|
17642
|
+
// ../../packages/platform-release/dist/esm/infra-params.js
|
|
17643
|
+
var INFRA_PARAMS_PK = "system";
|
|
17644
|
+
var INFRA_PARAMS_RK = "infra-params";
|
|
17645
|
+
function infraParamsToEntity(p) {
|
|
17646
|
+
return { partitionKey: INFRA_PARAMS_PK, rowKey: INFRA_PARAMS_RK, value: JSON.stringify(p) };
|
|
17647
|
+
}
|
|
17648
|
+
function entityToInfraParams(e) {
|
|
17649
|
+
return JSON.parse(e.value);
|
|
17650
|
+
}
|
|
17651
|
+
|
|
17570
17652
|
// src/lib/release-channel.ts
|
|
17571
17653
|
init_errors();
|
|
17572
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17654
|
+
function manifestSourceForVersionTag(channelUrl, version) {
|
|
17655
|
+
const tag = platformTag(version);
|
|
17656
|
+
const custom = channelUrl.trim();
|
|
17657
|
+
if (!custom || custom === CHANNEL_LATEST_URL) return { channel: true, version: tag };
|
|
17658
|
+
if (custom.includes("/releases/latest/download/")) {
|
|
17659
|
+
return { url: custom.replace("/releases/latest/download/", `/releases/download/${tag}/`) };
|
|
17660
|
+
}
|
|
17661
|
+
return { url: custom };
|
|
17575
17662
|
}
|
|
17576
17663
|
async function fetchManifest(source, deps = {}) {
|
|
17577
17664
|
const readFile8 = deps.readFile ?? ((p) => fs18.readFileSync(p, "utf8"));
|
|
@@ -17744,28 +17831,6 @@ async function discoverStampStorage(opts) {
|
|
|
17744
17831
|
}
|
|
17745
17832
|
|
|
17746
17833
|
// src/lib/platform-stamp.ts
|
|
17747
|
-
var TABLE = "Metadata";
|
|
17748
|
-
var PK = "system";
|
|
17749
|
-
var RK = "platform";
|
|
17750
|
-
function stampToEntity(s) {
|
|
17751
|
-
return {
|
|
17752
|
-
partitionKey: PK,
|
|
17753
|
-
rowKey: RK,
|
|
17754
|
-
platformVersion: s.platformVersion,
|
|
17755
|
-
updatedAt: s.updatedAt,
|
|
17756
|
-
lastResult: s.lastResult,
|
|
17757
|
-
value: JSON.stringify(s)
|
|
17758
|
-
};
|
|
17759
|
-
}
|
|
17760
|
-
function entityToStamp(e) {
|
|
17761
|
-
const raw = e.value;
|
|
17762
|
-
if (typeof raw !== "string") return null;
|
|
17763
|
-
try {
|
|
17764
|
-
return JSON.parse(raw);
|
|
17765
|
-
} catch {
|
|
17766
|
-
return null;
|
|
17767
|
-
}
|
|
17768
|
-
}
|
|
17769
17834
|
async function makeSharedKeyClient(opts) {
|
|
17770
17835
|
const rg = /\/resourceGroups\/([^/]+)\//i.exec(opts.accountResourceId)?.[1];
|
|
17771
17836
|
if (!rg) return null;
|
|
@@ -17887,6 +17952,24 @@ function buildBicepParams(p) {
|
|
|
17887
17952
|
`acrResourceId=${p.acrResourceId}`
|
|
17888
17953
|
];
|
|
17889
17954
|
if (p.foundryTracingMode) params.push(`foundryTracingMode=${p.foundryTracingMode}`);
|
|
17955
|
+
if (p.assignSubscriptionRoles !== void 0) params.push(`assignSubscriptionRoles=${String(p.assignSubscriptionRoles)}`);
|
|
17956
|
+
if (p.provisionUpdater !== void 0) params.push(`provisionUpdater=${String(p.provisionUpdater)}`);
|
|
17957
|
+
if (p.installerImage !== void 0) params.push(`installerImage=${p.installerImage}`);
|
|
17958
|
+
if (p.updateCron !== void 0) params.push(`updateCron=${p.updateCron}`);
|
|
17959
|
+
if (p.channelUrl !== void 0) params.push(`channelUrl=${p.channelUrl}`);
|
|
17960
|
+
if (p.gatewayCpu) params.push(`gatewayCpu=${p.gatewayCpu}`);
|
|
17961
|
+
if (p.gatewayMemory) params.push(`gatewayMemory=${p.gatewayMemory}`);
|
|
17962
|
+
if (p.refereeEnabled) params.push(`refereeEnabled=${p.refereeEnabled}`);
|
|
17963
|
+
if (p.refereeBrainRepos) params.push(`refereeBrainRepos=${p.refereeBrainRepos}`);
|
|
17964
|
+
if (p.refereeFeedRepo) params.push(`refereeFeedRepo=${p.refereeFeedRepo}`);
|
|
17965
|
+
if (p.refereeInstallationId) params.push(`refereeInstallationId=${p.refereeInstallationId}`);
|
|
17966
|
+
if (p.refereeWebhookHmacKvUri) params.push(`refereeWebhookHmacKvUri=${p.refereeWebhookHmacKvUri}`);
|
|
17967
|
+
if (p.examKvUri) params.push(`examKvUri=${p.examKvUri}`);
|
|
17968
|
+
if (p.examLaWorkspaceId) params.push(`examLaWorkspaceId=${p.examLaWorkspaceId}`);
|
|
17969
|
+
if (p.brainEvalDeployment) params.push(`brainEvalDeployment=${p.brainEvalDeployment}`);
|
|
17970
|
+
if (p.brainAppLogin) params.push(`brainAppLogin=${p.brainAppLogin}`);
|
|
17971
|
+
if (p.refereeCheckpointDir) params.push(`refereeCheckpointDir=${p.refereeCheckpointDir}`);
|
|
17972
|
+
if (p.examApiBase) params.push(`examApiBase=${p.examApiBase}`);
|
|
17890
17973
|
return params;
|
|
17891
17974
|
}
|
|
17892
17975
|
async function resolveRepoRoot2(home = os9.homedir()) {
|
|
@@ -18142,22 +18225,22 @@ async function applyPlan(plan, deps, ctx) {
|
|
|
18142
18225
|
stamp.components.gateway = await deps.applyGateway(a, ctx);
|
|
18143
18226
|
} else if (a.component === "codingAgent" || a.component === "azureExecutor") {
|
|
18144
18227
|
const r = await deps.applyAgent(a, ctx);
|
|
18145
|
-
if (r
|
|
18146
|
-
|
|
18147
|
-
|
|
18148
|
-
ctx.onProgress?.(`${a.component}: not deployed on this install \u2014 stamp left unchanged.`);
|
|
18228
|
+
if (r === "absent") {
|
|
18229
|
+
ctx.onProgress?.(`${a.component}: not deployed on this install \u2014 skipped (not gated).`);
|
|
18230
|
+
continue;
|
|
18149
18231
|
}
|
|
18232
|
+
stamp.components[a.component] = { ...r, state: "managed" };
|
|
18150
18233
|
} else {
|
|
18151
18234
|
if (!a.personaName) {
|
|
18152
18235
|
throw new LocalCliError({ code: "PLATFORM_PERSONA_NO_NAME", message: "persona action missing personaName" });
|
|
18153
18236
|
}
|
|
18154
18237
|
const r = await deps.applyPersona(a, ctx);
|
|
18155
18238
|
if (r === "absent") {
|
|
18156
|
-
ctx.onProgress?.(`persona '${a.personaName}': not deployed on this install \u2014
|
|
18157
|
-
|
|
18158
|
-
const prev = Object.hasOwn(stamp.components.personas, a.personaName) ? stamp.components.personas[a.personaName] : void 0;
|
|
18159
|
-
stamp.components.personas[a.personaName] = r === "noop" ? { treeSha: a.to, foundryVersion: prev?.foundryVersion ?? "" } : r;
|
|
18239
|
+
ctx.onProgress?.(`persona '${a.personaName}': not deployed on this install \u2014 skipped (not gated).`);
|
|
18240
|
+
continue;
|
|
18160
18241
|
}
|
|
18242
|
+
const prev = Object.hasOwn(stamp.components.personas, a.personaName) ? stamp.components.personas[a.personaName] : void 0;
|
|
18243
|
+
stamp.components.personas[a.personaName] = r === "noop" ? { treeSha: a.to, foundryVersion: prev?.foundryVersion ?? "" } : r;
|
|
18161
18244
|
}
|
|
18162
18245
|
applied.push(a);
|
|
18163
18246
|
stamp.lastResult = "partial";
|
|
@@ -18174,6 +18257,9 @@ async function applyPlan(plan, deps, ctx) {
|
|
|
18174
18257
|
}
|
|
18175
18258
|
stamp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18176
18259
|
await deps.writeStamp(stamp);
|
|
18260
|
+
if (!isTargeted && ctx.suffix && deps.writeInfraParams) {
|
|
18261
|
+
await deps.writeInfraParams(ctx, ctx.suffix);
|
|
18262
|
+
}
|
|
18177
18263
|
return { stamp, applied };
|
|
18178
18264
|
} catch (e) {
|
|
18179
18265
|
stamp.lastResult = "failed";
|
|
@@ -18265,7 +18351,8 @@ async function resolveBicepParamsForConverge(ctx, opts = {}) {
|
|
|
18265
18351
|
foundryProjectEndpoint: opts.foundryEndpoint ?? "",
|
|
18266
18352
|
acrPullIdentityResourceId,
|
|
18267
18353
|
acrResourceId,
|
|
18268
|
-
foundryTracingMode: opts.foundryTracingMode
|
|
18354
|
+
foundryTracingMode: opts.foundryTracingMode,
|
|
18355
|
+
...opts.assignSubscriptionRoles !== void 0 ? { assignSubscriptionRoles: opts.assignSubscriptionRoles } : {}
|
|
18269
18356
|
};
|
|
18270
18357
|
}
|
|
18271
18358
|
function swapHostedImage(def, newImage) {
|
|
@@ -18532,6 +18619,59 @@ import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/ident
|
|
|
18532
18619
|
|
|
18533
18620
|
// src/lib/platform-converge-cli.ts
|
|
18534
18621
|
import * as path21 from "path";
|
|
18622
|
+
|
|
18623
|
+
// src/lib/platform-infra-params.ts
|
|
18624
|
+
import { TableClient as TableClient2 } from "@azure/data-tables";
|
|
18625
|
+
async function openInfraParamsTable(opts) {
|
|
18626
|
+
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
18627
|
+
return new TableClient2(tableEndpoint, "Metadata", opts.credential);
|
|
18628
|
+
}
|
|
18629
|
+
async function readInfraParams(client) {
|
|
18630
|
+
try {
|
|
18631
|
+
return entityToInfraParams(await client.getEntity(INFRA_PARAMS_PK, INFRA_PARAMS_RK));
|
|
18632
|
+
} catch {
|
|
18633
|
+
return null;
|
|
18634
|
+
}
|
|
18635
|
+
}
|
|
18636
|
+
async function writeInfraParams(client, p) {
|
|
18637
|
+
await client.upsertEntity(infraParamsToEntity(p), "Replace");
|
|
18638
|
+
}
|
|
18639
|
+
async function deriveSuffixVerified(opts) {
|
|
18640
|
+
const list = JSON.parse(
|
|
18641
|
+
await runAz([
|
|
18642
|
+
"containerapp",
|
|
18643
|
+
"list",
|
|
18644
|
+
"-g",
|
|
18645
|
+
opts.resourceGroup,
|
|
18646
|
+
"--subscription",
|
|
18647
|
+
opts.subscriptionId,
|
|
18648
|
+
"--query",
|
|
18649
|
+
"[?starts_with(name, 'm8t-gateway-')].name",
|
|
18650
|
+
"-o",
|
|
18651
|
+
"json"
|
|
18652
|
+
])
|
|
18653
|
+
);
|
|
18654
|
+
if (list.length !== 1) return null;
|
|
18655
|
+
const suffix = list[0].replace(/^m8t-gateway-/, "");
|
|
18656
|
+
const cae = JSON.parse(
|
|
18657
|
+
await runAz([
|
|
18658
|
+
"containerapp",
|
|
18659
|
+
"env",
|
|
18660
|
+
"list",
|
|
18661
|
+
"-g",
|
|
18662
|
+
opts.resourceGroup,
|
|
18663
|
+
"--subscription",
|
|
18664
|
+
opts.subscriptionId,
|
|
18665
|
+
"--query",
|
|
18666
|
+
`[?name=='m8t-cae-${suffix}'].name`,
|
|
18667
|
+
"-o",
|
|
18668
|
+
"json"
|
|
18669
|
+
])
|
|
18670
|
+
);
|
|
18671
|
+
return cae.length === 1 ? suffix : null;
|
|
18672
|
+
}
|
|
18673
|
+
|
|
18674
|
+
// src/lib/platform-converge-cli.ts
|
|
18535
18675
|
init_errors();
|
|
18536
18676
|
var HOSTED_AGENT_PERSONA_DIR = {
|
|
18537
18677
|
codingAgent: "coding-agent",
|
|
@@ -18591,7 +18731,23 @@ async function buildConvergeDeps(args) {
|
|
|
18591
18731
|
},
|
|
18592
18732
|
async applyInfra(a, ctx) {
|
|
18593
18733
|
const target = infraTargetSha(ctx.manifest, args.tree);
|
|
18594
|
-
|
|
18734
|
+
const infraTable = await openInfraParamsTable({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
|
|
18735
|
+
const stamped = (await readInfraParams(infraTable))?.bicep;
|
|
18736
|
+
args.onProgress?.("subscription-scoped modules skipped: updater is RG-scoped; sub-level changes require a founder-run CLI converge.");
|
|
18737
|
+
const opts = {
|
|
18738
|
+
suffix: args.suffix,
|
|
18739
|
+
assignSubscriptionRoles: false,
|
|
18740
|
+
...stamped ? {
|
|
18741
|
+
tenantId: stamped.tenantId,
|
|
18742
|
+
clientId: stamped.clientId,
|
|
18743
|
+
foundryEndpoint: stamped.foundryProjectEndpoint,
|
|
18744
|
+
foundryResourceId: stamped.foundryResourceId,
|
|
18745
|
+
acrPullIdentity: stamped.acrPullIdentityResourceId,
|
|
18746
|
+
acrResourceId: stamped.acrResourceId,
|
|
18747
|
+
...stamped.foundryTracingMode ? { foundryTracingMode: stamped.foundryTracingMode } : {}
|
|
18748
|
+
} : {}
|
|
18749
|
+
};
|
|
18750
|
+
return applyInfraDeploy(a, ctx, target, opts);
|
|
18595
18751
|
},
|
|
18596
18752
|
async healthGate(_ctx, applied) {
|
|
18597
18753
|
for (const a of applied) {
|
|
@@ -18632,6 +18788,22 @@ async function buildConvergeDeps(args) {
|
|
|
18632
18788
|
stamp: { ...s, cli: CLI_VERSION },
|
|
18633
18789
|
onProgress: args.onProgress
|
|
18634
18790
|
});
|
|
18791
|
+
},
|
|
18792
|
+
async writeInfraParams(ctx, _suffix) {
|
|
18793
|
+
const table = await openInfraParamsTable({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
|
|
18794
|
+
const existing = await readInfraParams(table);
|
|
18795
|
+
if (!existing) {
|
|
18796
|
+
warn("infra-params re-stamp skipped: no existing row to refresh (deploy/enable-auto-update writes the authoritative row).");
|
|
18797
|
+
return;
|
|
18798
|
+
}
|
|
18799
|
+
await writeInfraParams(table, {
|
|
18800
|
+
...existing,
|
|
18801
|
+
bicep: {
|
|
18802
|
+
...existing.bicep,
|
|
18803
|
+
imageRef: `${ctx.manifest.components.gateway.ref}:${ctx.manifest.components.gateway.tag}`
|
|
18804
|
+
},
|
|
18805
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18806
|
+
});
|
|
18635
18807
|
}
|
|
18636
18808
|
};
|
|
18637
18809
|
}
|
|
@@ -18787,7 +18959,7 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
18787
18959
|
onProgress,
|
|
18788
18960
|
onWarn
|
|
18789
18961
|
});
|
|
18790
|
-
await applyPlan(plan, deps, { credential: credential2, subscriptionId, resourceGroup, contentDir, manifest, onProgress });
|
|
18962
|
+
await applyPlan(plan, deps, { credential: credential2, subscriptionId, resourceGroup, contentDir, manifest, onProgress, suffix });
|
|
18791
18963
|
if (mode === "json") this.context.stdout.write(renderJson({ ...plan, applied: true }) + "\n");
|
|
18792
18964
|
else log(colors.success(`converged \u2192 ${plan.targetVersion} \u2713`));
|
|
18793
18965
|
return 0;
|
|
@@ -18819,8 +18991,497 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
18819
18991
|
}
|
|
18820
18992
|
};
|
|
18821
18993
|
|
|
18994
|
+
// src/commands/platform/converge.ts
|
|
18995
|
+
import { Command as Command33 } from "clipanion";
|
|
18996
|
+
|
|
18997
|
+
// src/lib/platform-converge-mi.ts
|
|
18998
|
+
import { ManagedIdentityCredential } from "@azure/identity";
|
|
18999
|
+
init_errors();
|
|
19000
|
+
function resolveHeadlessContextFromEnv(env) {
|
|
19001
|
+
const need = (k) => {
|
|
19002
|
+
const v = (env[k] ?? "").trim();
|
|
19003
|
+
if (!v) throw new LocalCliError({ code: "HEADLESS_ENV_MISSING", message: `converge: required env ${k} is unset` });
|
|
19004
|
+
return v;
|
|
19005
|
+
};
|
|
19006
|
+
const miClientId = need("MI_CLIENT_ID");
|
|
19007
|
+
return {
|
|
19008
|
+
credential: new ManagedIdentityCredential({ clientId: miClientId }),
|
|
19009
|
+
miClientId,
|
|
19010
|
+
subscriptionId: need("SUBSCRIPTION_ID"),
|
|
19011
|
+
resourceGroup: need("RESOURCE_GROUP"),
|
|
19012
|
+
endpoint: need("FOUNDRY_ENDPOINT"),
|
|
19013
|
+
channelUrl: (env.M8T_UPDATE_CHANNEL_URL ?? "").trim() || CHANNEL_LATEST_URL
|
|
19014
|
+
};
|
|
19015
|
+
}
|
|
19016
|
+
|
|
19017
|
+
// src/lib/apply-request-store.ts
|
|
19018
|
+
import { TableClient as TableClient3 } from "@azure/data-tables";
|
|
19019
|
+
async function openApplyTable(opts) {
|
|
19020
|
+
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
19021
|
+
return new TableClient3(tableEndpoint, "Metadata", opts.credential);
|
|
19022
|
+
}
|
|
19023
|
+
function hasStatus(e, n) {
|
|
19024
|
+
return e.statusCode === n;
|
|
19025
|
+
}
|
|
19026
|
+
async function readApplyRequest(client) {
|
|
19027
|
+
try {
|
|
19028
|
+
const row = await client.getEntity(APPLY_REQUEST_PK, APPLY_REQUEST_RK);
|
|
19029
|
+
return { ...entityToApplyRequest(row), etag: row.etag };
|
|
19030
|
+
} catch (e) {
|
|
19031
|
+
if (hasStatus(e, 404)) return null;
|
|
19032
|
+
throw e;
|
|
19033
|
+
}
|
|
19034
|
+
}
|
|
19035
|
+
async function claimApplyRequest(client, executionId, nowIso, leaseMs) {
|
|
19036
|
+
const cur = await readApplyRequest(client);
|
|
19037
|
+
if (!cur) return null;
|
|
19038
|
+
const leaseExpired = cur.status === "claimed" && cur.leaseUntil !== null && Date.parse(cur.leaseUntil) < Date.parse(nowIso);
|
|
19039
|
+
const claimable = cur.status === "pending" || cur.status === "awaiting-engine-update" || leaseExpired;
|
|
19040
|
+
if (!claimable) return null;
|
|
19041
|
+
const nowMs = Date.parse(nowIso);
|
|
19042
|
+
const leaseUntil = new Date((Number.isNaN(nowMs) ? Date.now() : nowMs) + leaseMs).toISOString();
|
|
19043
|
+
const claimed = { ...cur, status: "claimed", claimedBy: executionId, leaseUntil, updatedAt: nowIso };
|
|
19044
|
+
try {
|
|
19045
|
+
await client.updateEntity({ ...applyRequestToEntity(claimed), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
19046
|
+
return claimed;
|
|
19047
|
+
} catch (e) {
|
|
19048
|
+
if (hasStatus(e, 412)) return null;
|
|
19049
|
+
throw e;
|
|
19050
|
+
}
|
|
19051
|
+
}
|
|
19052
|
+
async function patchApplyRequest(client, mutate) {
|
|
19053
|
+
const cur = await readApplyRequest(client);
|
|
19054
|
+
if (!cur) return;
|
|
19055
|
+
const next = mutate(cur);
|
|
19056
|
+
await client.updateEntity({ ...applyRequestToEntity(next), etag: cur.etag }, "Merge", { etag: cur.etag });
|
|
19057
|
+
}
|
|
19058
|
+
|
|
19059
|
+
// src/lib/platform-self-update.ts
|
|
19060
|
+
import * as fs22 from "fs";
|
|
19061
|
+
import * as path22 from "path";
|
|
19062
|
+
function toVTag2(v) {
|
|
19063
|
+
return v.startsWith("v") ? v : `v${v}`;
|
|
19064
|
+
}
|
|
19065
|
+
function readBakedReleaseVersion(repoRoot) {
|
|
19066
|
+
if (!repoRoot) return null;
|
|
19067
|
+
try {
|
|
19068
|
+
const raw = fs22.readFileSync(path22.join(repoRoot, "installer", "version.json"), "utf8");
|
|
19069
|
+
const v = JSON.parse(raw).version;
|
|
19070
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
19071
|
+
} catch {
|
|
19072
|
+
return null;
|
|
19073
|
+
}
|
|
19074
|
+
}
|
|
19075
|
+
function needsEngineUpdate(manifest, runningVersion, bakedRelease) {
|
|
19076
|
+
if (runningVersion === "0.0.0-dev") return false;
|
|
19077
|
+
if (bakedRelease != null && bakedRelease.length > 0 && bakedRelease !== manifest.components.installer.version) return true;
|
|
19078
|
+
const running = toVTag2(runningVersion);
|
|
19079
|
+
const min = toVTag2(manifest.components.cli.min);
|
|
19080
|
+
return compareSemver(running, min) < 0;
|
|
19081
|
+
}
|
|
19082
|
+
async function updateOwnJobImage(opts) {
|
|
19083
|
+
const inst = opts.manifest.components.installer;
|
|
19084
|
+
const image = `${inst.ref}:${inst.tag}`;
|
|
19085
|
+
const job = opts.jobName ?? process.env.UPDATER_JOB_NAME;
|
|
19086
|
+
if (!job) throw new Error("UPDATER_JOB_NAME unset \u2014 cannot self-update the updater job image");
|
|
19087
|
+
await runAz([
|
|
19088
|
+
"containerapp",
|
|
19089
|
+
"job",
|
|
19090
|
+
"update",
|
|
19091
|
+
"-n",
|
|
19092
|
+
job,
|
|
19093
|
+
"-g",
|
|
19094
|
+
opts.resourceGroup,
|
|
19095
|
+
"--subscription",
|
|
19096
|
+
opts.subscriptionId,
|
|
19097
|
+
"--image",
|
|
19098
|
+
image,
|
|
19099
|
+
"--only-show-errors"
|
|
19100
|
+
]);
|
|
19101
|
+
}
|
|
19102
|
+
|
|
19103
|
+
// src/lib/platform-health-gate.ts
|
|
19104
|
+
function nextBreaker(cur) {
|
|
19105
|
+
return cur === "none" ? "rolled-back" : "held";
|
|
19106
|
+
}
|
|
19107
|
+
function gateTargetsFor(applied) {
|
|
19108
|
+
const targets = [];
|
|
19109
|
+
for (const a of applied) {
|
|
19110
|
+
if (a.component === "gateway") {
|
|
19111
|
+
targets.push({ kind: "gateway-version", expected: a.to });
|
|
19112
|
+
} else if (a.component === "codingAgent" || a.component === "azureExecutor") {
|
|
19113
|
+
targets.push({ kind: "agent-queryable", component: a.component });
|
|
19114
|
+
} else if (a.component === "personas" && a.personaName) {
|
|
19115
|
+
targets.push({ kind: "agent-queryable", persona: a.personaName });
|
|
19116
|
+
}
|
|
19117
|
+
}
|
|
19118
|
+
return targets;
|
|
19119
|
+
}
|
|
19120
|
+
function gatePassed(probes) {
|
|
19121
|
+
return probes.every((p) => p.ok);
|
|
19122
|
+
}
|
|
19123
|
+
async function runHealthGateWithRollback(args) {
|
|
19124
|
+
const notify = args.notify ?? (async () => {
|
|
19125
|
+
});
|
|
19126
|
+
const budgetMs = args.gateBudgetMs ?? 5 * 6e4;
|
|
19127
|
+
const target = args.plan.targetVersion;
|
|
19128
|
+
let applied = [];
|
|
19129
|
+
try {
|
|
19130
|
+
const result = await applyPlan(args.plan, args.deps, args.ctx);
|
|
19131
|
+
applied = result.applied;
|
|
19132
|
+
const outcomes = await runGate(args, applied, budgetMs);
|
|
19133
|
+
if (!gatePassed(outcomes)) {
|
|
19134
|
+
const detail = outcomes.find((o) => !o.ok)?.detail ?? "a post-apply health probe failed";
|
|
19135
|
+
return await rollbackOrHold(args, notify, detail);
|
|
19136
|
+
}
|
|
19137
|
+
await patchApplyRequest(args.client, (r) => ({
|
|
19138
|
+
...r,
|
|
19139
|
+
status: "success",
|
|
19140
|
+
breaker: r.breaker,
|
|
19141
|
+
// unchanged — a clean apply never trips the breaker
|
|
19142
|
+
result: { appliedVersion: target, error: null },
|
|
19143
|
+
updatedAt: args.now().toISOString()
|
|
19144
|
+
}));
|
|
19145
|
+
await notify({ outcome: "success", target });
|
|
19146
|
+
return "success";
|
|
19147
|
+
} catch (e) {
|
|
19148
|
+
return await rollbackOrHold(args, notify, e.message);
|
|
19149
|
+
}
|
|
19150
|
+
}
|
|
19151
|
+
async function runGate(args, applied, budgetMs) {
|
|
19152
|
+
const deadline = args.now().getTime() + budgetMs;
|
|
19153
|
+
const probeGateway = args.probeGatewayVersion ?? defaultGatewayProbe;
|
|
19154
|
+
const probeAgent = args.probeAgentQueryable ?? defaultAgentProbe;
|
|
19155
|
+
const outcomes = [];
|
|
19156
|
+
for (const t of gateTargetsFor(applied)) {
|
|
19157
|
+
if (args.now().getTime() > deadline) {
|
|
19158
|
+
outcomes.push({ ok: false, detail: "health gate exceeded its time budget" });
|
|
19159
|
+
break;
|
|
19160
|
+
}
|
|
19161
|
+
if (t.kind === "gateway-version") {
|
|
19162
|
+
if (!args.gatewayUrl) continue;
|
|
19163
|
+
outcomes.push(await probeGateway(args.gatewayUrl, t.expected));
|
|
19164
|
+
} else {
|
|
19165
|
+
const agentName = "persona" in t ? t.persona : hostedAgentName(t.component);
|
|
19166
|
+
outcomes.push(await probeAgent({ credential: args.credential, endpoint: args.endpoint, agentName }));
|
|
19167
|
+
}
|
|
19168
|
+
}
|
|
19169
|
+
return outcomes;
|
|
19170
|
+
}
|
|
19171
|
+
async function rollbackOrHold(args, notify, error) {
|
|
19172
|
+
const target = args.plan.targetVersion;
|
|
19173
|
+
const rollbackTarget = args.stamp.previousPlatformVersion;
|
|
19174
|
+
const current = await readBreaker(args.client);
|
|
19175
|
+
const next = nextBreaker(current);
|
|
19176
|
+
if (next === "held") {
|
|
19177
|
+
await patchApplyRequest(args.client, (r) => ({
|
|
19178
|
+
...r,
|
|
19179
|
+
status: "held",
|
|
19180
|
+
breaker: "held",
|
|
19181
|
+
result: { appliedVersion: r.result?.appliedVersion ?? null, error },
|
|
19182
|
+
updatedAt: args.now().toISOString()
|
|
19183
|
+
}));
|
|
19184
|
+
await notify({ outcome: "held", target, error });
|
|
19185
|
+
return "held";
|
|
19186
|
+
}
|
|
19187
|
+
if (rollbackTarget) {
|
|
19188
|
+
await reconvergeToPrevious(args, rollbackTarget);
|
|
19189
|
+
}
|
|
19190
|
+
await patchApplyRequest(args.client, (r) => ({
|
|
19191
|
+
...r,
|
|
19192
|
+
status: "rolled-back",
|
|
19193
|
+
breaker: "rolled-back",
|
|
19194
|
+
result: { appliedVersion: rollbackTarget, error },
|
|
19195
|
+
updatedAt: args.now().toISOString()
|
|
19196
|
+
}));
|
|
19197
|
+
await notify({ outcome: "rolled-back", target, error });
|
|
19198
|
+
return "rolled-back";
|
|
19199
|
+
}
|
|
19200
|
+
async function reconvergeToPrevious(args, previousVersion) {
|
|
19201
|
+
const fetchManifestImpl = args.fetchManifestImpl ?? fetchManifest;
|
|
19202
|
+
const resolveContentImpl = args.resolveReleaseContentImpl ?? resolveReleaseContent;
|
|
19203
|
+
const fetchTreeImpl = args.fetchRepoTreeImpl ?? fetchRepoTree;
|
|
19204
|
+
const prevManifest = await fetchManifestImpl(manifestSourceForVersionTag(args.channelUrl ?? "", previousVersion));
|
|
19205
|
+
const contentDir = await resolveContentImpl(prevManifest, args.repoRoot ? { contentDir: args.repoRoot } : {});
|
|
19206
|
+
const tree = prevManifest.components.infra?.treeSha ? { treeShaOf: () => void 0 } : await fetchTreeImpl(prevManifest.platform.commit);
|
|
19207
|
+
const liveStamp = await args.deps.readStamp();
|
|
19208
|
+
const rollbackPlan = diffPlan(prevManifest, liveStamp, tree);
|
|
19209
|
+
const rollbackCtx = { ...args.ctx, manifest: prevManifest, contentDir };
|
|
19210
|
+
await applyPlan(rollbackPlan, args.deps, rollbackCtx);
|
|
19211
|
+
}
|
|
19212
|
+
async function defaultGatewayProbe(gatewayUrl, expected) {
|
|
19213
|
+
try {
|
|
19214
|
+
const res = await fetch(`${gatewayUrl}/api/version`, { method: "GET" });
|
|
19215
|
+
if (res.status === 401 || res.status === 403) return { ok: true };
|
|
19216
|
+
if (!res.ok) return { ok: false, detail: `gateway /api/version returned HTTP ${res.status.toString()}` };
|
|
19217
|
+
const body = await res.json().catch(() => ({}));
|
|
19218
|
+
if (body.version && !versionMatches(body.version, expected)) {
|
|
19219
|
+
return { ok: false, detail: `gateway reports version '${body.version}', expected '${expected}'` };
|
|
19220
|
+
}
|
|
19221
|
+
return { ok: true };
|
|
19222
|
+
} catch (e) {
|
|
19223
|
+
return { ok: false, detail: `gateway unreachable: ${e.message}` };
|
|
19224
|
+
}
|
|
19225
|
+
}
|
|
19226
|
+
function versionMatches(running, expected) {
|
|
19227
|
+
const norm = (s) => s.replace(/^v/, "").trim();
|
|
19228
|
+
const r = norm(running);
|
|
19229
|
+
const e = norm(expected);
|
|
19230
|
+
return r === e || r.startsWith(e) || e.startsWith(r);
|
|
19231
|
+
}
|
|
19232
|
+
async function defaultAgentProbe(args) {
|
|
19233
|
+
try {
|
|
19234
|
+
await awaitAgentQueryable({ credential: args.credential, projectEndpoint: args.endpoint, agentName: args.agentName });
|
|
19235
|
+
return { ok: true };
|
|
19236
|
+
} catch (e) {
|
|
19237
|
+
return { ok: false, detail: `agent '${args.agentName}' not queryable: ${e.message}` };
|
|
19238
|
+
}
|
|
19239
|
+
}
|
|
19240
|
+
function hostedAgentName(component) {
|
|
19241
|
+
return component === "codingAgent" ? "coder" : "azure-executor";
|
|
19242
|
+
}
|
|
19243
|
+
async function readBreaker(client) {
|
|
19244
|
+
const row = await readApplyRequest(client);
|
|
19245
|
+
return row?.breaker ?? "none";
|
|
19246
|
+
}
|
|
19247
|
+
|
|
19248
|
+
// src/commands/platform/converge.ts
|
|
19249
|
+
init_errors();
|
|
19250
|
+
function canonicalVersion(v) {
|
|
19251
|
+
const bare = v.replace(/^platform-/, "");
|
|
19252
|
+
return bare.startsWith("v") ? bare : `v${bare}`;
|
|
19253
|
+
}
|
|
19254
|
+
function isForwardTarget(target, installed) {
|
|
19255
|
+
if (!installed) return true;
|
|
19256
|
+
return compareSemver(canonicalVersion(target), canonicalVersion(installed)) >= 0;
|
|
19257
|
+
}
|
|
19258
|
+
async function failIfStillInFlight(client, now, error) {
|
|
19259
|
+
const cur = await readApplyRequest(client);
|
|
19260
|
+
if (!cur || !isInFlight(cur.status)) return;
|
|
19261
|
+
await patchApplyRequest(client, (r) => ({
|
|
19262
|
+
...r,
|
|
19263
|
+
status: "failed",
|
|
19264
|
+
result: { appliedVersion: null, error },
|
|
19265
|
+
updatedAt: now()
|
|
19266
|
+
}));
|
|
19267
|
+
}
|
|
19268
|
+
var PlatformConvergeCommand = class extends M8tCommand {
|
|
19269
|
+
static paths = [["platform", "converge"]];
|
|
19270
|
+
static usage = Command33.Usage({
|
|
19271
|
+
category: "Platform",
|
|
19272
|
+
description: "Headless updater-job driver: claim a pending apply-request and converge the platform to it.",
|
|
19273
|
+
details: "The Managed-Identity-authenticated entry point run by the updater Container Apps job. One invocation is one tick: it claims the pending apply-request row (if any), refuses a downgrade against the installed stamp, self-fetches and validates the target manifest, self-updates its own engine image when the manifest requires a newer CLI, then drives the F02 converge engine behind the T15 health-gate + auto-rollback. Reads its subscription/RG/endpoint/channel from env (MI_CLIENT_ID, SUBSCRIPTION_ID, RESOURCE_GROUP, FOUNDRY_ENDPOINT, M8T_UPDATE_CHANNEL_URL). Not intended for interactive use \u2014 the founder-facing path is 'm8t platform update'."
|
|
19274
|
+
});
|
|
19275
|
+
async executeCommand() {
|
|
19276
|
+
const ctx = resolveHeadlessContextFromEnv(process.env);
|
|
19277
|
+
const executionId = process.env.CONTAINER_APP_JOB_EXECUTION_NAME ?? `exec-${process.pid.toString()}`;
|
|
19278
|
+
const now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
19279
|
+
const log = (m) => {
|
|
19280
|
+
this.context.stdout.write(m + "\n");
|
|
19281
|
+
};
|
|
19282
|
+
const client = await openApplyTable({
|
|
19283
|
+
credential: ctx.credential,
|
|
19284
|
+
subscriptionId: ctx.subscriptionId,
|
|
19285
|
+
resourceGroup: ctx.resourceGroup
|
|
19286
|
+
});
|
|
19287
|
+
const claimed = await claimApplyRequest(client, executionId, now(), 30 * 60 * 1e3);
|
|
19288
|
+
if (!claimed) {
|
|
19289
|
+
log("no pending apply-request \u2014 nothing to converge.");
|
|
19290
|
+
return 0;
|
|
19291
|
+
}
|
|
19292
|
+
log(`claimed apply-request \u2192 target ${claimed.target} (execution ${executionId}).`);
|
|
19293
|
+
const stamp = await readStamp({
|
|
19294
|
+
credential: ctx.credential,
|
|
19295
|
+
subscriptionId: ctx.subscriptionId,
|
|
19296
|
+
resourceGroup: ctx.resourceGroup
|
|
19297
|
+
});
|
|
19298
|
+
const installed = stamp ? stamp.platformVersion : null;
|
|
19299
|
+
if (!isForwardTarget(claimed.target, installed)) {
|
|
19300
|
+
await patchApplyRequest(client, (r) => ({
|
|
19301
|
+
...r,
|
|
19302
|
+
status: "failed",
|
|
19303
|
+
result: { appliedVersion: null, error: `refused downgrade ${installed ?? "(none)"} \u2192 ${claimed.target}` },
|
|
19304
|
+
updatedAt: now()
|
|
19305
|
+
}));
|
|
19306
|
+
log(`refused: target ${claimed.target} is a downgrade from installed ${installed ?? "(none)"}.`);
|
|
19307
|
+
return 1;
|
|
19308
|
+
}
|
|
19309
|
+
const manifest = await fetchManifest(targetManifestSource(ctx, claimed.target));
|
|
19310
|
+
if (manifest.platform.version !== claimed.target) {
|
|
19311
|
+
await patchApplyRequest(client, (r) => ({
|
|
19312
|
+
...r,
|
|
19313
|
+
status: "failed",
|
|
19314
|
+
result: { appliedVersion: null, error: `channel manifest is version ${manifest.platform.version}, expected the claimed target ${claimed.target}` },
|
|
19315
|
+
updatedAt: now()
|
|
19316
|
+
}));
|
|
19317
|
+
log(`refused: fetched manifest version ${manifest.platform.version} \u2260 claimed target ${claimed.target}.`);
|
|
19318
|
+
return 1;
|
|
19319
|
+
}
|
|
19320
|
+
const repoRoot = tryResolveRepoRoot();
|
|
19321
|
+
const bakedRelease = readBakedReleaseVersion(repoRoot);
|
|
19322
|
+
if (needsEngineUpdate(manifest, CLI_VERSION, bakedRelease)) {
|
|
19323
|
+
log(
|
|
19324
|
+
`self-updating the updater image (baked release ${bakedRelease ?? "(none)"} / engine ${CLI_VERSION} \u2192 target installer ${manifest.components.installer.version}, cli.min ${manifest.components.cli.min}).`
|
|
19325
|
+
);
|
|
19326
|
+
await updateOwnJobImage({
|
|
19327
|
+
subscriptionId: ctx.subscriptionId,
|
|
19328
|
+
resourceGroup: ctx.resourceGroup,
|
|
19329
|
+
manifest
|
|
19330
|
+
});
|
|
19331
|
+
await patchApplyRequest(client, (r) => ({
|
|
19332
|
+
...r,
|
|
19333
|
+
status: "awaiting-engine-update",
|
|
19334
|
+
phase: "updating the updater engine image",
|
|
19335
|
+
updatedAt: now()
|
|
19336
|
+
}));
|
|
19337
|
+
log("updater image bumped \u2014 awaiting-engine-update. The next tick re-claims with the new engine.");
|
|
19338
|
+
return 0;
|
|
19339
|
+
}
|
|
19340
|
+
assertCliVersionOk(manifest, CLI_VERSION, (w) => this.context.stderr.write(w + "\n"));
|
|
19341
|
+
await patchApplyRequest(client, (r) => ({ ...r, status: "applying", phase: "starting", updatedAt: now() }));
|
|
19342
|
+
let plan;
|
|
19343
|
+
let applyCtx;
|
|
19344
|
+
let deps;
|
|
19345
|
+
try {
|
|
19346
|
+
const project = await resolveFoundryProject({
|
|
19347
|
+
credential: ctx.credential,
|
|
19348
|
+
subscriptionId: ctx.subscriptionId,
|
|
19349
|
+
interactive: false,
|
|
19350
|
+
endpoint: ctx.endpoint
|
|
19351
|
+
});
|
|
19352
|
+
const contentDir = await resolveReleaseContent(manifest, repoRoot ? { contentDir: repoRoot } : {});
|
|
19353
|
+
const tree = manifest.components.infra?.treeSha ? { treeShaOf: () => void 0 } : await fetchRepoTree(manifest.platform.commit);
|
|
19354
|
+
const suffix = await this.recoverSuffix(ctx);
|
|
19355
|
+
const gatewayResourceId = await this.resolveGatewayResourceId(ctx, suffix);
|
|
19356
|
+
deps = await buildConvergeDeps({
|
|
19357
|
+
credential: ctx.credential,
|
|
19358
|
+
subscriptionId: ctx.subscriptionId,
|
|
19359
|
+
resourceGroup: ctx.resourceGroup,
|
|
19360
|
+
contentDir,
|
|
19361
|
+
manifest,
|
|
19362
|
+
tree,
|
|
19363
|
+
project,
|
|
19364
|
+
gatewayResourceId,
|
|
19365
|
+
suffix,
|
|
19366
|
+
onProgress: (m) => {
|
|
19367
|
+
void patchApplyRequest(client, (r) => ({ ...r, phase: m, updatedAt: now() })).catch(() => void 0);
|
|
19368
|
+
log(m);
|
|
19369
|
+
},
|
|
19370
|
+
onWarn: (m) => this.context.stderr.write(m + "\n")
|
|
19371
|
+
});
|
|
19372
|
+
plan = diffPlan(manifest, stamp, tree, { forceInfra: true });
|
|
19373
|
+
applyCtx = {
|
|
19374
|
+
credential: ctx.credential,
|
|
19375
|
+
subscriptionId: ctx.subscriptionId,
|
|
19376
|
+
resourceGroup: ctx.resourceGroup,
|
|
19377
|
+
contentDir,
|
|
19378
|
+
manifest,
|
|
19379
|
+
suffix,
|
|
19380
|
+
onProgress: (m) => {
|
|
19381
|
+
log(m);
|
|
19382
|
+
}
|
|
19383
|
+
};
|
|
19384
|
+
} catch (e) {
|
|
19385
|
+
await failIfStillInFlight(client, now, e.message);
|
|
19386
|
+
throw e;
|
|
19387
|
+
}
|
|
19388
|
+
const outcome = await runHealthGateWithRollback({
|
|
19389
|
+
client,
|
|
19390
|
+
deps,
|
|
19391
|
+
plan,
|
|
19392
|
+
ctx: applyCtx,
|
|
19393
|
+
stamp: stamp ?? seedStampFor(plan),
|
|
19394
|
+
manifest,
|
|
19395
|
+
credential: ctx.credential,
|
|
19396
|
+
endpoint: ctx.endpoint,
|
|
19397
|
+
// The rollback re-converge needs the SAME zero-repo-auth inputs as the
|
|
19398
|
+
// forward path: baked content (repoRoot) + the channel to resolve the
|
|
19399
|
+
// PREVIOUS version's manifest (never GitHub repo auth). Without these the
|
|
19400
|
+
// rollback would 404 on the private pre-OSS repo just like the forward
|
|
19401
|
+
// path did before this fix.
|
|
19402
|
+
channelUrl: ctx.channelUrl,
|
|
19403
|
+
repoRoot,
|
|
19404
|
+
now: () => /* @__PURE__ */ new Date()
|
|
19405
|
+
});
|
|
19406
|
+
log(`converge outcome: ${outcome}.`);
|
|
19407
|
+
return outcome === "success" ? 0 : 1;
|
|
19408
|
+
}
|
|
19409
|
+
/**
|
|
19410
|
+
* Recover the real bicep suffix WITHOUT guessing: prefer the stamped
|
|
19411
|
+
* `system/infra-params` row (written by deploy + every full converge), and
|
|
19412
|
+
* fall back to deriving it from the live gateway container-app name — but only
|
|
19413
|
+
* when that derivation VERIFIES (exactly one gateway + a matching CAE). If
|
|
19414
|
+
* both fail, throw rather than provision a duplicate stack.
|
|
19415
|
+
*/
|
|
19416
|
+
async recoverSuffix(ctx) {
|
|
19417
|
+
const infraTable = await openInfraParamsTable({
|
|
19418
|
+
credential: ctx.credential,
|
|
19419
|
+
subscriptionId: ctx.subscriptionId,
|
|
19420
|
+
resourceGroup: ctx.resourceGroup
|
|
19421
|
+
});
|
|
19422
|
+
const stamped = (await readInfraParams(infraTable))?.suffix;
|
|
19423
|
+
const suffix = stamped ?? await deriveSuffixVerified({
|
|
19424
|
+
credential: ctx.credential,
|
|
19425
|
+
subscriptionId: ctx.subscriptionId,
|
|
19426
|
+
resourceGroup: ctx.resourceGroup
|
|
19427
|
+
});
|
|
19428
|
+
if (!suffix) {
|
|
19429
|
+
throw new LocalCliError({
|
|
19430
|
+
code: "PLATFORM_INFRA_SUFFIX_UNRECOVERABLE",
|
|
19431
|
+
message: "Could not recover the deployment's resource-name suffix: no system/infra-params row is stamped, and deriving it from the live gateway did not verify (0 or >1 gateway, or no matching CAE).",
|
|
19432
|
+
hint: "Run a full 'm8t platform update --force-infra --suffix <suffix>' once to stamp system/infra-params, then re-run the updater."
|
|
19433
|
+
});
|
|
19434
|
+
}
|
|
19435
|
+
return suffix;
|
|
19436
|
+
}
|
|
19437
|
+
/** Resolve the gateway container-app ARM resource id for the recovered suffix. */
|
|
19438
|
+
async resolveGatewayResourceId(ctx, suffix) {
|
|
19439
|
+
const id = (await runAz([
|
|
19440
|
+
"containerapp",
|
|
19441
|
+
"show",
|
|
19442
|
+
"-n",
|
|
19443
|
+
`m8t-gateway-${suffix}`,
|
|
19444
|
+
"-g",
|
|
19445
|
+
ctx.resourceGroup,
|
|
19446
|
+
"--subscription",
|
|
19447
|
+
ctx.subscriptionId,
|
|
19448
|
+
"--query",
|
|
19449
|
+
"id",
|
|
19450
|
+
"-o",
|
|
19451
|
+
"tsv"
|
|
19452
|
+
])).trim();
|
|
19453
|
+
if (!id) {
|
|
19454
|
+
throw new LocalCliError({
|
|
19455
|
+
code: "PLATFORM_GATEWAY_NOT_FOUND",
|
|
19456
|
+
message: `Could not resolve the gateway container app 'm8t-gateway-${suffix}' in resource group ${ctx.resourceGroup}.`
|
|
19457
|
+
});
|
|
19458
|
+
}
|
|
19459
|
+
return id;
|
|
19460
|
+
}
|
|
19461
|
+
};
|
|
19462
|
+
function seedStampFor(plan) {
|
|
19463
|
+
return {
|
|
19464
|
+
schemaVersion: 1,
|
|
19465
|
+
platformVersion: plan.targetVersion,
|
|
19466
|
+
previousPlatformVersion: null,
|
|
19467
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19468
|
+
lastResult: "success",
|
|
19469
|
+
cli: CLI_VERSION,
|
|
19470
|
+
components: {
|
|
19471
|
+
gateway: { tag: "", digest: "", state: "managed" },
|
|
19472
|
+
codingAgent: { tag: "", digest: "", state: "managed" },
|
|
19473
|
+
azureExecutor: { tag: "", digest: "", state: "managed" },
|
|
19474
|
+
personas: {},
|
|
19475
|
+
infra: { treeSha: "" }
|
|
19476
|
+
}
|
|
19477
|
+
};
|
|
19478
|
+
}
|
|
19479
|
+
function targetManifestSource(ctx, target) {
|
|
19480
|
+
return manifestSourceForVersionTag(ctx.channelUrl, target);
|
|
19481
|
+
}
|
|
19482
|
+
|
|
18822
19483
|
// src/commands/platform/enable-cost-report.ts
|
|
18823
|
-
import { Command as
|
|
19484
|
+
import { Command as Command34, Option as Option31 } from "clipanion";
|
|
18824
19485
|
|
|
18825
19486
|
// src/lib/wire-gateway-acs.ts
|
|
18826
19487
|
init_rbac();
|
|
@@ -18860,7 +19521,7 @@ async function wireGatewayForAcs(args) {
|
|
|
18860
19521
|
init_errors();
|
|
18861
19522
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
18862
19523
|
static paths = [["platform", "enable-cost-report"]];
|
|
18863
|
-
static usage =
|
|
19524
|
+
static usage = Command34.Usage({
|
|
18864
19525
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
18865
19526
|
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')."
|
|
18866
19527
|
});
|
|
@@ -18944,8 +19605,161 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
18944
19605
|
}
|
|
18945
19606
|
};
|
|
18946
19607
|
|
|
19608
|
+
// src/commands/platform/enable-auto-update.ts
|
|
19609
|
+
import { Command as Command35, Option as Option32 } from "clipanion";
|
|
19610
|
+
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
19611
|
+
import { DefaultAzureCredential as DefaultAzureCredential18 } from "@azure/identity";
|
|
19612
|
+
init_errors();
|
|
19613
|
+
var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
19614
|
+
static paths = [["platform", "enable-auto-update"]];
|
|
19615
|
+
static usage = Command35.Usage({
|
|
19616
|
+
category: "Platform",
|
|
19617
|
+
description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
|
|
19618
|
+
details: "For an install that predates the Platform Update Framework: recovers the deployment's resource-name suffix (from the stamped system/infra-params row, or by verified derivation from the live gateway), recovers the other bicep params from live state (WITHOUT changing the deployed gateway image), and re-runs deploy/main.bicep with provisionUpdater=true \u2014 which provisions the updater managed identity, the cron Container Apps Job, and every role assignment (Owner@RG + Foundry User@account + Storage Table + Storage Blob + AcrPull), all scoped to the resource group. On success, backfills system/infra-params so future converges never have to re-derive the suffix. Idempotent \u2014 safe to re-run."
|
|
19619
|
+
});
|
|
19620
|
+
subscription = Option32.String("--subscription");
|
|
19621
|
+
resourceGroup = Option32.String("--resource-group", {
|
|
19622
|
+
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
19623
|
+
});
|
|
19624
|
+
suffix = Option32.String("--suffix", {
|
|
19625
|
+
description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
|
|
19626
|
+
});
|
|
19627
|
+
installerImage = Option32.String("--installer-image", {
|
|
19628
|
+
required: true,
|
|
19629
|
+
description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
|
|
19630
|
+
});
|
|
19631
|
+
updateCron = Option32.String("--update-cron", {
|
|
19632
|
+
description: "Cron schedule for the updater job (bicep default applies when omitted)."
|
|
19633
|
+
});
|
|
19634
|
+
channelUrl = Option32.String("--channel-url", {
|
|
19635
|
+
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
19636
|
+
});
|
|
19637
|
+
endpoint = Option32.String("--endpoint", {
|
|
19638
|
+
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
19639
|
+
});
|
|
19640
|
+
yes = Option32.Boolean("--yes", false);
|
|
19641
|
+
output = Option32.String("--output");
|
|
19642
|
+
async executeCommand() {
|
|
19643
|
+
const mode = resolveOutputMode(
|
|
19644
|
+
this.output,
|
|
19645
|
+
this.context.stdout
|
|
19646
|
+
);
|
|
19647
|
+
const interactive = this.context.stdout.isTTY === true;
|
|
19648
|
+
const log = (msg) => {
|
|
19649
|
+
if (mode !== "json") this.context.stdout.write(msg + "\n");
|
|
19650
|
+
};
|
|
19651
|
+
const onProgress = (m) => {
|
|
19652
|
+
log(colors.dim(m));
|
|
19653
|
+
};
|
|
19654
|
+
const gw = await resolveGatewayContext({
|
|
19655
|
+
interactive: mode !== "json",
|
|
19656
|
+
subscriptionId: this.subscription,
|
|
19657
|
+
resourceGroup: this.resourceGroup
|
|
19658
|
+
});
|
|
19659
|
+
const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
19660
|
+
const credential2 = new DefaultAzureCredential18();
|
|
19661
|
+
const account = await getAzAccount();
|
|
19662
|
+
const subscriptionId = this.subscription ?? account.subscriptionId;
|
|
19663
|
+
const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
|
|
19664
|
+
onProgress("recovering the deployment's resource-name suffix\u2026");
|
|
19665
|
+
const infraTable = await openInfraParamsTable({ credential: credential2, subscriptionId, resourceGroup });
|
|
19666
|
+
const stampedParams = await readInfraParams(infraTable);
|
|
19667
|
+
const derivedSuffix = explicitSuffix ? void 0 : stampedParams?.suffix ?? await deriveSuffixVerified({ credential: credential2, subscriptionId, resourceGroup }) ?? void 0;
|
|
19668
|
+
const suffix = explicitSuffix ?? derivedSuffix;
|
|
19669
|
+
if (!suffix) {
|
|
19670
|
+
throw new LocalCliError({
|
|
19671
|
+
code: "PLATFORM_ENABLE_AUTO_UPDATE_SUFFIX_UNRECOVERABLE",
|
|
19672
|
+
message: "Could not recover the deployment's resource-name suffix: no --suffix was given, no system/infra-params row is stamped, and deriving it from the live gateway did not verify (0 or >1 gateway, or no matching CAE).",
|
|
19673
|
+
hint: "Pass --suffix <existing-suffix> explicitly (from the deployed resource names, e.g. m8t-gateway-<suffix>)."
|
|
19674
|
+
});
|
|
19675
|
+
}
|
|
19676
|
+
if (!explicitSuffix && !stampedParams && !this.yes && interactive) {
|
|
19677
|
+
const ok = await confirm6({
|
|
19678
|
+
message: `Derived suffix '${suffix}' from the live gateway \u2014 proceed?`,
|
|
19679
|
+
default: true
|
|
19680
|
+
});
|
|
19681
|
+
if (!ok) {
|
|
19682
|
+
log(colors.dim("aborted \u2014 no change. Re-run with --suffix <suffix> to specify it explicitly."));
|
|
19683
|
+
return 0;
|
|
19684
|
+
}
|
|
19685
|
+
}
|
|
19686
|
+
onProgress("resolving Foundry project\u2026");
|
|
19687
|
+
const project = await resolveFoundryProject({
|
|
19688
|
+
credential: credential2,
|
|
19689
|
+
subscriptionId,
|
|
19690
|
+
interactive,
|
|
19691
|
+
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
|
|
19692
|
+
});
|
|
19693
|
+
onProgress("reading the live gateway image (left unchanged)\u2026");
|
|
19694
|
+
const imageRef = (await runAz(["containerapp", "show", "-n", gatewayName, "-g", resourceGroup, "--query", "properties.template.containers[0].image", "-o", "tsv"])).trim();
|
|
19695
|
+
if (!imageRef) {
|
|
19696
|
+
throw new LocalCliError({
|
|
19697
|
+
code: "PLATFORM_ENABLE_AUTO_UPDATE_NO_IMAGE",
|
|
19698
|
+
message: `Could not read the live image for gateway '${gatewayName}' in resource group ${resourceGroup}.`
|
|
19699
|
+
});
|
|
19700
|
+
}
|
|
19701
|
+
const location = (await runAz(["resource", "list", "-g", resourceGroup, "--subscription", subscriptionId, "--query", "[0].location", "-o", "tsv"])).trim();
|
|
19702
|
+
if (!location) {
|
|
19703
|
+
throw new LocalCliError({
|
|
19704
|
+
code: "PLATFORM_ENABLE_AUTO_UPDATE_NO_LOCATION",
|
|
19705
|
+
message: `Could not resolve a location for resource group ${resourceGroup} \u2014 it may be empty.`
|
|
19706
|
+
});
|
|
19707
|
+
}
|
|
19708
|
+
const foundryResourceId = await resolveFoundryResourceId(project.endpoint);
|
|
19709
|
+
const acrPullIdentityResourceId = resolveAcrPullIdentity({});
|
|
19710
|
+
const acrResourceId = acrPullIdentityResourceId ? "" : await resolveAcrResourceId({ imageRef });
|
|
19711
|
+
const recoveredParams = {
|
|
19712
|
+
location,
|
|
19713
|
+
suffix,
|
|
19714
|
+
imageRef,
|
|
19715
|
+
tenantId: gw.gatewayTenantId,
|
|
19716
|
+
clientId: gw.gatewayClientId,
|
|
19717
|
+
foundryResourceId,
|
|
19718
|
+
foundryProjectEndpoint: project.endpoint,
|
|
19719
|
+
acrPullIdentityResourceId,
|
|
19720
|
+
acrResourceId
|
|
19721
|
+
};
|
|
19722
|
+
onProgress("running Bicep deployment to provision the updater (~2-4 min)\u2026");
|
|
19723
|
+
const repoRoot = await resolveRepoRoot2();
|
|
19724
|
+
await runBicepDeployment({
|
|
19725
|
+
resourceGroup,
|
|
19726
|
+
repoRoot,
|
|
19727
|
+
params: buildBicepParams({
|
|
19728
|
+
...recoveredParams,
|
|
19729
|
+
provisionUpdater: true,
|
|
19730
|
+
installerImage: this.installerImage,
|
|
19731
|
+
...typeof this.updateCron === "string" ? { updateCron: this.updateCron } : {},
|
|
19732
|
+
...typeof this.channelUrl === "string" ? { channelUrl: this.channelUrl } : {}
|
|
19733
|
+
}),
|
|
19734
|
+
deploymentName: `m8t-enable-auto-update-${suffix.slice(0, 12)}`
|
|
19735
|
+
});
|
|
19736
|
+
onProgress("backfilling system/infra-params\u2026");
|
|
19737
|
+
await writeInfraParams(infraTable, {
|
|
19738
|
+
schemaVersion: 1,
|
|
19739
|
+
suffix,
|
|
19740
|
+
bicep: recoveredParams,
|
|
19741
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19742
|
+
});
|
|
19743
|
+
if (mode === "json") {
|
|
19744
|
+
this.context.stdout.write(renderJson({ suffix, resourceGroup, installerImage: this.installerImage, enabled: true }) + "\n");
|
|
19745
|
+
return 0;
|
|
19746
|
+
}
|
|
19747
|
+
this.context.stdout.write(
|
|
19748
|
+
renderKeyValueBlock([
|
|
19749
|
+
{ key: "suffix", value: suffix },
|
|
19750
|
+
{ key: "resource group", value: resourceGroup },
|
|
19751
|
+
{ key: "installer image", value: this.installerImage },
|
|
19752
|
+
{ key: "infra-params", value: "backfilled" }
|
|
19753
|
+
]) + "\n"
|
|
19754
|
+
);
|
|
19755
|
+
log(colors.success("auto-update enabled \u2014 the updater job will start converging on its next cron tick \u2713"));
|
|
19756
|
+
log(colors.dim("next: 'm8t platform status' to check drift, or 'm8t platform update' to converge interactively at any time."));
|
|
19757
|
+
return 0;
|
|
19758
|
+
}
|
|
19759
|
+
};
|
|
19760
|
+
|
|
18947
19761
|
// src/commands/deploy.ts
|
|
18948
|
-
import { Command as
|
|
19762
|
+
import { Command as Command36, Option as Option33 } from "clipanion";
|
|
18949
19763
|
|
|
18950
19764
|
// src/lib/app-reg.ts
|
|
18951
19765
|
init_errors();
|
|
@@ -19206,7 +20020,7 @@ async function patchRedirectUris(appObjectId, fqdn) {
|
|
|
19206
20020
|
init_errors();
|
|
19207
20021
|
|
|
19208
20022
|
// src/lib/whatif.ts
|
|
19209
|
-
import * as
|
|
20023
|
+
import * as path23 from "path";
|
|
19210
20024
|
function deriveResourceType(resourceId) {
|
|
19211
20025
|
if (resourceId.startsWith("[")) return "";
|
|
19212
20026
|
const afterProviders = resourceId.split("/providers/")[1];
|
|
@@ -19217,7 +20031,7 @@ function deriveResourceType(resourceId) {
|
|
|
19217
20031
|
return parts.join("/");
|
|
19218
20032
|
}
|
|
19219
20033
|
async function runWhatIf(opts) {
|
|
19220
|
-
const bicepPath =
|
|
20034
|
+
const bicepPath = path23.join(opts.repoRoot, "deploy", "main.bicep");
|
|
19221
20035
|
const out = await runAz([
|
|
19222
20036
|
"deployment",
|
|
19223
20037
|
"group",
|
|
@@ -19282,9 +20096,9 @@ function flattenDelta(delta, prefix = "") {
|
|
|
19282
20096
|
const out = [];
|
|
19283
20097
|
for (const d of delta) {
|
|
19284
20098
|
const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
|
|
19285
|
-
const
|
|
19286
|
-
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children,
|
|
19287
|
-
else out.push({ ...d, path:
|
|
20099
|
+
const path33 = prefix ? `${prefix}${segment}` : d.path;
|
|
20100
|
+
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path33));
|
|
20101
|
+
else out.push({ ...d, path: path33 });
|
|
19288
20102
|
}
|
|
19289
20103
|
return out;
|
|
19290
20104
|
}
|
|
@@ -19334,24 +20148,40 @@ function classifyWhatIf(changes) {
|
|
|
19334
20148
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-run/m8t:latest";
|
|
19335
20149
|
var DeployCommand = class extends M8tCommand {
|
|
19336
20150
|
static paths = [["deploy"]];
|
|
19337
|
-
static usage =
|
|
20151
|
+
static usage = Command36.Usage({
|
|
19338
20152
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
19339
20153
|
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."
|
|
19340
20154
|
});
|
|
19341
|
-
subscription =
|
|
19342
|
-
resourceGroup =
|
|
19343
|
-
location =
|
|
19344
|
-
suffix =
|
|
19345
|
-
imageRef =
|
|
19346
|
-
acrPullIdentity =
|
|
19347
|
-
acrResourceId =
|
|
19348
|
-
foundryEndpoint =
|
|
19349
|
-
foundryResourceId =
|
|
19350
|
-
foundryTracing =
|
|
20155
|
+
subscription = Option33.String("--subscription");
|
|
20156
|
+
resourceGroup = Option33.String("--resource-group", "rg-m8t-stack");
|
|
20157
|
+
location = Option33.String("--location", "eastus");
|
|
20158
|
+
suffix = Option33.String("--suffix", "");
|
|
20159
|
+
imageRef = Option33.String("--image-ref", DEFAULT_IMAGE_REF);
|
|
20160
|
+
acrPullIdentity = Option33.String("--acrpull-identity");
|
|
20161
|
+
acrResourceId = Option33.String("--acr-resource-id");
|
|
20162
|
+
foundryEndpoint = Option33.String("--foundry-endpoint");
|
|
20163
|
+
foundryResourceId = Option33.String("--foundry-resource-id");
|
|
20164
|
+
foundryTracing = Option33.String("--foundry-tracing");
|
|
19351
20165
|
// project | account | skip (bicep default: project)
|
|
19352
|
-
clientId =
|
|
19353
|
-
whatIf =
|
|
19354
|
-
output =
|
|
20166
|
+
clientId = Option33.String("--client-id");
|
|
20167
|
+
whatIf = Option33.Boolean("--what-if", false);
|
|
20168
|
+
output = Option33.String("--output");
|
|
20169
|
+
// Referee (E2-F5) — all optional, undefined by default ⇒ the bicep defaults
|
|
20170
|
+
// apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
|
|
20171
|
+
// empty). Only pass these when explicitly enabling the referee exam stack.
|
|
20172
|
+
gatewayCpu = Option33.String("--gateway-cpu");
|
|
20173
|
+
gatewayMemory = Option33.String("--gateway-memory");
|
|
20174
|
+
refereeEnabled = Option33.String("--referee-enabled");
|
|
20175
|
+
refereeBrainRepos = Option33.String("--referee-brain-repos");
|
|
20176
|
+
refereeFeedRepo = Option33.String("--referee-feed-repo");
|
|
20177
|
+
refereeInstallationId = Option33.String("--referee-installation-id");
|
|
20178
|
+
refereeWebhookHmacKvUri = Option33.String("--referee-webhook-hmac-kv-uri");
|
|
20179
|
+
examKvUri = Option33.String("--exam-kv-uri");
|
|
20180
|
+
examLaWorkspaceId = Option33.String("--exam-la-workspace-id");
|
|
20181
|
+
brainEvalDeployment = Option33.String("--brain-eval-deployment");
|
|
20182
|
+
brainAppLogin = Option33.String("--brain-app-login");
|
|
20183
|
+
refereeCheckpointDir = Option33.String("--referee-checkpoint-dir");
|
|
20184
|
+
examApiBase = Option33.String("--exam-api-base");
|
|
19355
20185
|
async executeCommand() {
|
|
19356
20186
|
const mode = resolveOutputMode(
|
|
19357
20187
|
this.output,
|
|
@@ -19395,7 +20225,20 @@ var DeployCommand = class extends M8tCommand {
|
|
|
19395
20225
|
foundryProjectEndpoint: foundryEndpoint,
|
|
19396
20226
|
acrPullIdentityResourceId: acrPullIdentityResourceId2,
|
|
19397
20227
|
acrResourceId: acrResourceId2,
|
|
19398
|
-
foundryTracingMode: parseFoundryTracingMode(this.foundryTracing)
|
|
20228
|
+
foundryTracingMode: parseFoundryTracingMode(this.foundryTracing),
|
|
20229
|
+
gatewayCpu: this.gatewayCpu,
|
|
20230
|
+
gatewayMemory: this.gatewayMemory,
|
|
20231
|
+
refereeEnabled: this.refereeEnabled,
|
|
20232
|
+
refereeBrainRepos: this.refereeBrainRepos,
|
|
20233
|
+
refereeFeedRepo: this.refereeFeedRepo,
|
|
20234
|
+
refereeInstallationId: this.refereeInstallationId,
|
|
20235
|
+
refereeWebhookHmacKvUri: this.refereeWebhookHmacKvUri,
|
|
20236
|
+
examKvUri: this.examKvUri,
|
|
20237
|
+
examLaWorkspaceId: this.examLaWorkspaceId,
|
|
20238
|
+
brainEvalDeployment: this.brainEvalDeployment,
|
|
20239
|
+
brainAppLogin: this.brainAppLogin,
|
|
20240
|
+
refereeCheckpointDir: this.refereeCheckpointDir,
|
|
20241
|
+
examApiBase: this.examApiBase
|
|
19399
20242
|
});
|
|
19400
20243
|
const changes = await runWhatIf({ resourceGroup: this.resourceGroup, repoRoot: repoRoot2, params: params2 });
|
|
19401
20244
|
const classified = classifyWhatIf(changes);
|
|
@@ -19430,7 +20273,20 @@ var DeployCommand = class extends M8tCommand {
|
|
|
19430
20273
|
foundryProjectEndpoint: foundryEndpoint,
|
|
19431
20274
|
acrPullIdentityResourceId,
|
|
19432
20275
|
acrResourceId,
|
|
19433
|
-
foundryTracingMode: parseFoundryTracingMode(this.foundryTracing)
|
|
20276
|
+
foundryTracingMode: parseFoundryTracingMode(this.foundryTracing),
|
|
20277
|
+
gatewayCpu: this.gatewayCpu,
|
|
20278
|
+
gatewayMemory: this.gatewayMemory,
|
|
20279
|
+
refereeEnabled: this.refereeEnabled,
|
|
20280
|
+
refereeBrainRepos: this.refereeBrainRepos,
|
|
20281
|
+
refereeFeedRepo: this.refereeFeedRepo,
|
|
20282
|
+
refereeInstallationId: this.refereeInstallationId,
|
|
20283
|
+
refereeWebhookHmacKvUri: this.refereeWebhookHmacKvUri,
|
|
20284
|
+
examKvUri: this.examKvUri,
|
|
20285
|
+
examLaWorkspaceId: this.examLaWorkspaceId,
|
|
20286
|
+
brainEvalDeployment: this.brainEvalDeployment,
|
|
20287
|
+
brainAppLogin: this.brainAppLogin,
|
|
20288
|
+
refereeCheckpointDir: this.refereeCheckpointDir,
|
|
20289
|
+
examApiBase: this.examApiBase
|
|
19434
20290
|
});
|
|
19435
20291
|
log("running Bicep deployment (~3-5 min)\u2026");
|
|
19436
20292
|
const outputs = await runBicepDeployment({
|
|
@@ -19465,7 +20321,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
19465
20321
|
|
|
19466
20322
|
// src/commands/eval/skill.ts
|
|
19467
20323
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
19468
|
-
import { Command as
|
|
20324
|
+
import { Command as Command37, Option as Option34 } from "clipanion";
|
|
19469
20325
|
init_errors();
|
|
19470
20326
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
19471
20327
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -19490,14 +20346,14 @@ function parseVerdict(stdout) {
|
|
|
19490
20346
|
}
|
|
19491
20347
|
var EvalSkillCommand = class extends M8tCommand {
|
|
19492
20348
|
static paths = [["eval", "skill"]];
|
|
19493
|
-
static usage =
|
|
20349
|
+
static usage = Command37.Usage({
|
|
19494
20350
|
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)."
|
|
19495
20351
|
});
|
|
19496
|
-
candidate =
|
|
19497
|
-
skillsDir =
|
|
19498
|
-
noJudge =
|
|
19499
|
-
deployment =
|
|
19500
|
-
output =
|
|
20352
|
+
candidate = Option34.String();
|
|
20353
|
+
skillsDir = Option34.String("--skills-dir");
|
|
20354
|
+
noJudge = Option34.Boolean("--no-judge", false);
|
|
20355
|
+
deployment = Option34.String("--deployment");
|
|
20356
|
+
output = Option34.String("--output");
|
|
19501
20357
|
executeCommand() {
|
|
19502
20358
|
return Promise.resolve(this._runCommand());
|
|
19503
20359
|
}
|
|
@@ -19554,9 +20410,9 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
19554
20410
|
|
|
19555
20411
|
// src/commands/eval/exam.ts
|
|
19556
20412
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
19557
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readFileSync as
|
|
19558
|
-
import { join as
|
|
19559
|
-
import { Command as
|
|
20413
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync16, existsSync as existsSync13, readdirSync } from "fs";
|
|
20414
|
+
import { join as join23 } from "path";
|
|
20415
|
+
import { Command as Command38, Option as Option35 } from "clipanion";
|
|
19560
20416
|
init_errors();
|
|
19561
20417
|
init_esm();
|
|
19562
20418
|
function parseArmToken(tok, opts) {
|
|
@@ -19644,7 +20500,7 @@ function parseExamVerdict(stdout) {
|
|
|
19644
20500
|
}
|
|
19645
20501
|
return v;
|
|
19646
20502
|
}
|
|
19647
|
-
var DEFAULT_REPS = { impact: 3, dream_delta: 5 };
|
|
20503
|
+
var DEFAULT_REPS = { impact: 3, dream_delta: 5, skill_abstraction: 5 };
|
|
19648
20504
|
function inferExamType(arms, explicit) {
|
|
19649
20505
|
if (explicit !== void 0) {
|
|
19650
20506
|
if (!EXAM_TYPES.has(explicit)) {
|
|
@@ -19665,6 +20521,22 @@ function inferExamType(arms, explicit) {
|
|
|
19665
20521
|
message: "could not infer --exam-type from arms \u2014 pass --exam-type impact|dream_delta|tournament"
|
|
19666
20522
|
});
|
|
19667
20523
|
}
|
|
20524
|
+
function assertAbstractionArmsExplicit(examType, arms, skill) {
|
|
20525
|
+
if (examType !== "skill_abstraction") return;
|
|
20526
|
+
if (typeof skill !== "string" || skill.length === 0) {
|
|
20527
|
+
throw new LocalCliError({
|
|
20528
|
+
code: "USAGE",
|
|
20529
|
+
message: "--exam-type skill_abstraction requires --skill <slug> (the promoted skill's slug)"
|
|
20530
|
+
});
|
|
20531
|
+
}
|
|
20532
|
+
const bothPinned = arms.length === 2 && arms.every((a) => typeof a.state === "object" && "pinned" in a.state);
|
|
20533
|
+
if (!bothPinned) {
|
|
20534
|
+
throw new LocalCliError({
|
|
20535
|
+
code: "USAGE",
|
|
20536
|
+
message: "--exam-type skill_abstraction requires exactly 2 explicit pinned arms (the promotion event's base/merge shas), e.g. --arms 'pinned:<base>+pinned:<merge>'"
|
|
20537
|
+
});
|
|
20538
|
+
}
|
|
20539
|
+
}
|
|
19668
20540
|
function resolveJudgeDeployment(flag, env) {
|
|
19669
20541
|
if (typeof flag === "string" && flag.length > 0) return { deployment: flag };
|
|
19670
20542
|
const fromEnv = env.EXAM_JUDGE_DEPLOYMENT;
|
|
@@ -19721,9 +20593,9 @@ function resolveRefereeHome(refereeHomeOut, env) {
|
|
|
19721
20593
|
return null;
|
|
19722
20594
|
}
|
|
19723
20595
|
function resolveTaskSetDir(base, worker, version) {
|
|
19724
|
-
if (existsSync13(
|
|
20596
|
+
if (existsSync13(join23(base, version))) return version;
|
|
19725
20597
|
const prefixed = `${worker}-${version}`;
|
|
19726
|
-
if (existsSync13(
|
|
20598
|
+
if (existsSync13(join23(base, prefixed))) return prefixed;
|
|
19727
20599
|
if (version === "latest" && existsSync13(base)) {
|
|
19728
20600
|
const dirs = readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
|
|
19729
20601
|
if (dirs.length > 0) return dirs[dirs.length - 1];
|
|
@@ -19739,11 +20611,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
|
|
|
19739
20611
|
message: "cannot resolve the referee home to read the sealed-task manifest \u2014 set $REFEREE_HOME (mirroring the Python runner) so the feed can be redacted; refusing to emit a possibly-unredacted feed (Law-1)"
|
|
19740
20612
|
});
|
|
19741
20613
|
}
|
|
19742
|
-
const versionDir = resolveTaskSetDir(
|
|
19743
|
-
const manifestPath =
|
|
20614
|
+
const versionDir = resolveTaskSetDir(join23(home, "tasksets", worker), worker, version);
|
|
20615
|
+
const manifestPath = join23(home, "tasksets", worker, versionDir, "manifest.yaml");
|
|
19744
20616
|
let text;
|
|
19745
20617
|
try {
|
|
19746
|
-
text =
|
|
20618
|
+
text = readFileSync16(manifestPath, "utf-8");
|
|
19747
20619
|
} catch (e) {
|
|
19748
20620
|
throw new LocalCliError({
|
|
19749
20621
|
code: "EXAM_REDACTION_UNSAFE",
|
|
@@ -19770,23 +20642,24 @@ function buildPlan(args) {
|
|
|
19770
20642
|
}
|
|
19771
20643
|
var EvalExamCommand = class extends M8tCommand {
|
|
19772
20644
|
static paths = [["eval", "exam"]];
|
|
19773
|
-
static usage =
|
|
20645
|
+
static usage = Command38.Usage({
|
|
19774
20646
|
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."
|
|
19775
20647
|
});
|
|
19776
|
-
worker =
|
|
19777
|
-
arms =
|
|
19778
|
-
taskSet =
|
|
19779
|
-
examType =
|
|
19780
|
-
|
|
19781
|
-
|
|
19782
|
-
|
|
19783
|
-
|
|
19784
|
-
|
|
19785
|
-
|
|
19786
|
-
|
|
19787
|
-
|
|
19788
|
-
|
|
19789
|
-
|
|
20648
|
+
worker = Option35.String();
|
|
20649
|
+
arms = Option35.String("--arms");
|
|
20650
|
+
taskSet = Option35.String("--task-set");
|
|
20651
|
+
examType = Option35.String("--exam-type");
|
|
20652
|
+
skill = Option35.String("--skill");
|
|
20653
|
+
reps = Option35.String("-n,--reps");
|
|
20654
|
+
probes = Option35.String("--probes");
|
|
20655
|
+
pool = Option35.String("--pool");
|
|
20656
|
+
out = Option35.String("--out");
|
|
20657
|
+
dryRun = Option35.Boolean("--dry-run", false);
|
|
20658
|
+
keepArms = Option35.Boolean("--keep-arms", false);
|
|
20659
|
+
allowStub = Option35.Boolean("--allow-stub", false);
|
|
20660
|
+
deployment = Option35.String("--deployment");
|
|
20661
|
+
output = Option35.String("--output");
|
|
20662
|
+
observeWaitS = Option35.String("--observe-wait-s");
|
|
19790
20663
|
async executeCommand() {
|
|
19791
20664
|
await Promise.resolve();
|
|
19792
20665
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -19795,6 +20668,7 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
19795
20668
|
if (!armsRaw) throw new LocalCliError({ code: "USAGE", message: "--arms <spec> is required" });
|
|
19796
20669
|
const arms = parseArmSpec(armsRaw, "git", { allowStub: this.allowStub === true });
|
|
19797
20670
|
const examType = inferExamType(arms, typeof this.examType === "string" ? this.examType : void 0);
|
|
20671
|
+
assertAbstractionArmsExplicit(examType, arms, typeof this.skill === "string" ? this.skill : void 0);
|
|
19798
20672
|
let reps = DEFAULT_REPS[examType] ?? 3;
|
|
19799
20673
|
if (typeof this.reps === "string") {
|
|
19800
20674
|
const parsed = Number.parseInt(this.reps, 10);
|
|
@@ -19862,9 +20736,9 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
19862
20736
|
if (out !== void 0) {
|
|
19863
20737
|
const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
|
|
19864
20738
|
mkdirSync4(out, { recursive: true });
|
|
19865
|
-
writeFileSync4(
|
|
20739
|
+
writeFileSync4(join23(out, "verdict.json"), JSON.stringify(verdict, null, 2));
|
|
19866
20740
|
const feed = redactForFeed(verdict, sealedTaskIds);
|
|
19867
|
-
writeFileSync4(
|
|
20741
|
+
writeFileSync4(join23(out, "feed.json"), JSON.stringify(feed, null, 2));
|
|
19868
20742
|
}
|
|
19869
20743
|
if (mode === "json") {
|
|
19870
20744
|
this.context.stdout.write(renderJson(verdict) + "\n");
|
|
@@ -19900,10 +20774,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
19900
20774
|
};
|
|
19901
20775
|
|
|
19902
20776
|
// src/commands/version.ts
|
|
19903
|
-
import { Command as
|
|
20777
|
+
import { Command as Command39, Option as Option36 } from "clipanion";
|
|
19904
20778
|
var VersionCommand = class extends M8tCommand {
|
|
19905
20779
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
19906
|
-
static usage =
|
|
20780
|
+
static usage = Command39.Usage({
|
|
19907
20781
|
description: "Print the CLI version.",
|
|
19908
20782
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
19909
20783
|
examples: [
|
|
@@ -19911,8 +20785,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
19911
20785
|
["Print as JSON", "$0 version --output json"]
|
|
19912
20786
|
]
|
|
19913
20787
|
});
|
|
19914
|
-
output =
|
|
19915
|
-
verbose =
|
|
20788
|
+
output = Option36.String("--output", { description: "pretty | json | auto (default)" });
|
|
20789
|
+
verbose = Option36.Boolean("--verbose", false);
|
|
19916
20790
|
executeCommand() {
|
|
19917
20791
|
const mode = resolveOutputMode(
|
|
19918
20792
|
this.output ?? "auto",
|
|
@@ -19943,18 +20817,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
19943
20817
|
};
|
|
19944
20818
|
|
|
19945
20819
|
// src/commands/whoami.ts
|
|
19946
|
-
import { Command as
|
|
20820
|
+
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
19947
20821
|
var WhoamiCommand = class extends M8tCommand {
|
|
19948
20822
|
static paths = [["whoami"]];
|
|
19949
|
-
static usage =
|
|
20823
|
+
static usage = Command40.Usage({
|
|
19950
20824
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
19951
20825
|
});
|
|
19952
|
-
output =
|
|
19953
|
-
verbose =
|
|
19954
|
-
subscription =
|
|
20826
|
+
output = Option37.String("--output");
|
|
20827
|
+
verbose = Option37.Boolean("--verbose", false);
|
|
20828
|
+
subscription = Option37.String("--subscription", {
|
|
19955
20829
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
19956
20830
|
});
|
|
19957
|
-
resourceGroup =
|
|
20831
|
+
resourceGroup = Option37.String("--resource-group", {
|
|
19958
20832
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
19959
20833
|
});
|
|
19960
20834
|
async executeCommand() {
|
|
@@ -20019,7 +20893,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
20019
20893
|
};
|
|
20020
20894
|
|
|
20021
20895
|
// src/commands/status.ts
|
|
20022
|
-
import { Command as
|
|
20896
|
+
import { Command as Command41, Option as Option38 } from "clipanion";
|
|
20023
20897
|
|
|
20024
20898
|
// src/lib/azd.ts
|
|
20025
20899
|
init_errors();
|
|
@@ -20084,10 +20958,10 @@ async function resolveLocalContext() {
|
|
|
20084
20958
|
// src/commands/status.ts
|
|
20085
20959
|
var StatusCommand = class extends M8tCommand {
|
|
20086
20960
|
static paths = [["status"]];
|
|
20087
|
-
static usage =
|
|
20961
|
+
static usage = Command41.Usage({
|
|
20088
20962
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
20089
20963
|
});
|
|
20090
|
-
output =
|
|
20964
|
+
output = Option38.String("--output");
|
|
20091
20965
|
async executeCommand() {
|
|
20092
20966
|
const mode = resolveOutputMode(
|
|
20093
20967
|
this.output,
|
|
@@ -20125,11 +20999,11 @@ var StatusCommand = class extends M8tCommand {
|
|
|
20125
20999
|
};
|
|
20126
21000
|
|
|
20127
21001
|
// src/commands/doctor.ts
|
|
20128
|
-
import { Command as
|
|
20129
|
-
import { DefaultAzureCredential as
|
|
20130
|
-
import * as
|
|
21002
|
+
import { Command as Command42, Option as Option39 } from "clipanion";
|
|
21003
|
+
import { DefaultAzureCredential as DefaultAzureCredential19 } from "@azure/identity";
|
|
21004
|
+
import * as fs23 from "fs";
|
|
20131
21005
|
import * as os10 from "os";
|
|
20132
|
-
import * as
|
|
21006
|
+
import * as path24 from "path";
|
|
20133
21007
|
|
|
20134
21008
|
// src/lib/doctor-checks.ts
|
|
20135
21009
|
function checkAzSignedIn(az) {
|
|
@@ -20354,34 +21228,34 @@ async function resolveAccountLocation(accountId) {
|
|
|
20354
21228
|
}
|
|
20355
21229
|
}
|
|
20356
21230
|
function probeRepoRoot() {
|
|
20357
|
-
const marker =
|
|
20358
|
-
if (!
|
|
21231
|
+
const marker = path24.join(os10.homedir(), ".m8t-stack", "repo-root");
|
|
21232
|
+
if (!fs23.existsSync(marker)) {
|
|
20359
21233
|
return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
|
|
20360
21234
|
}
|
|
20361
21235
|
let repoPath;
|
|
20362
21236
|
try {
|
|
20363
|
-
repoPath =
|
|
21237
|
+
repoPath = fs23.readFileSync(marker, "utf8").trim();
|
|
20364
21238
|
} catch {
|
|
20365
21239
|
return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
20366
21240
|
}
|
|
20367
21241
|
if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
20368
21242
|
let isDir = false;
|
|
20369
21243
|
try {
|
|
20370
|
-
isDir =
|
|
21244
|
+
isDir = fs23.statSync(repoPath).isDirectory();
|
|
20371
21245
|
} catch {
|
|
20372
21246
|
isDir = false;
|
|
20373
21247
|
}
|
|
20374
|
-
const isGitCheckout = isDir &&
|
|
21248
|
+
const isGitCheckout = isDir && fs23.existsSync(path24.join(repoPath, ".git"));
|
|
20375
21249
|
return { markerExists: true, path: repoPath, isDir, isGitCheckout };
|
|
20376
21250
|
}
|
|
20377
21251
|
var DoctorCommand = class extends M8tCommand {
|
|
20378
21252
|
static paths = [["doctor"]];
|
|
20379
|
-
static usage =
|
|
21253
|
+
static usage = Command42.Usage({
|
|
20380
21254
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
20381
21255
|
});
|
|
20382
|
-
output =
|
|
20383
|
-
agent =
|
|
20384
|
-
resourceGroup =
|
|
21256
|
+
output = Option39.String("--output");
|
|
21257
|
+
agent = Option39.String("--agent");
|
|
21258
|
+
resourceGroup = Option39.String("--resource-group", {
|
|
20385
21259
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
20386
21260
|
});
|
|
20387
21261
|
async executeCommand() {
|
|
@@ -20454,7 +21328,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
20454
21328
|
if (typeof this.agent === "string" && this.agent) {
|
|
20455
21329
|
checking(`delivery grant for ${this.agent}`);
|
|
20456
21330
|
try {
|
|
20457
|
-
const credential2 = new
|
|
21331
|
+
const credential2 = new DefaultAzureCredential19();
|
|
20458
21332
|
const cur = await getAgentVersion({
|
|
20459
21333
|
credential: credential2,
|
|
20460
21334
|
projectEndpoint: foundry.projectEndpoint,
|
|
@@ -20497,18 +21371,18 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
20497
21371
|
};
|
|
20498
21372
|
|
|
20499
21373
|
// src/commands/switch.ts
|
|
20500
|
-
import { Command as
|
|
21374
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
20501
21375
|
|
|
20502
21376
|
// src/lib/profiles.ts
|
|
20503
|
-
import * as
|
|
20504
|
-
import * as
|
|
21377
|
+
import * as fs24 from "fs/promises";
|
|
21378
|
+
import * as path25 from "path";
|
|
20505
21379
|
import { parse as parseYaml11, stringify as stringifyYaml4 } from "yaml";
|
|
20506
21380
|
function profilesDir() {
|
|
20507
21381
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20508
|
-
return
|
|
21382
|
+
return path25.join(home, ".m8t-stack", "profiles");
|
|
20509
21383
|
}
|
|
20510
21384
|
function getProfilePath(name) {
|
|
20511
|
-
return
|
|
21385
|
+
return path25.join(profilesDir(), `${path25.basename(name)}.yaml`);
|
|
20512
21386
|
}
|
|
20513
21387
|
function slugifyTenant(domainOrId) {
|
|
20514
21388
|
const base = domainOrId.split(".")[0].toLowerCase();
|
|
@@ -20517,7 +21391,7 @@ function slugifyTenant(domainOrId) {
|
|
|
20517
21391
|
}
|
|
20518
21392
|
async function listProfiles() {
|
|
20519
21393
|
try {
|
|
20520
|
-
const entries = await
|
|
21394
|
+
const entries = await fs24.readdir(profilesDir());
|
|
20521
21395
|
return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
|
|
20522
21396
|
} catch (e) {
|
|
20523
21397
|
if (e.code === "ENOENT") return [];
|
|
@@ -20526,7 +21400,7 @@ async function listProfiles() {
|
|
|
20526
21400
|
}
|
|
20527
21401
|
async function readProfile(name) {
|
|
20528
21402
|
try {
|
|
20529
|
-
const raw = await
|
|
21403
|
+
const raw = await fs24.readFile(getProfilePath(name), "utf8");
|
|
20530
21404
|
const parsed = parseYaml11(raw);
|
|
20531
21405
|
if (!parsed || typeof parsed !== "object") return null;
|
|
20532
21406
|
return parsed;
|
|
@@ -20537,14 +21411,14 @@ async function readProfile(name) {
|
|
|
20537
21411
|
}
|
|
20538
21412
|
async function exists(p) {
|
|
20539
21413
|
try {
|
|
20540
|
-
await
|
|
21414
|
+
await fs24.stat(p);
|
|
20541
21415
|
return true;
|
|
20542
21416
|
} catch {
|
|
20543
21417
|
return false;
|
|
20544
21418
|
}
|
|
20545
21419
|
}
|
|
20546
21420
|
async function saveProfile(p) {
|
|
20547
|
-
await
|
|
21421
|
+
await fs24.mkdir(profilesDir(), { recursive: true });
|
|
20548
21422
|
let name = p.name;
|
|
20549
21423
|
let n = 2;
|
|
20550
21424
|
while (await exists(getProfilePath(name))) {
|
|
@@ -20553,8 +21427,8 @@ async function saveProfile(p) {
|
|
|
20553
21427
|
}
|
|
20554
21428
|
const target = getProfilePath(name);
|
|
20555
21429
|
const tmp = `${target}.tmp`;
|
|
20556
|
-
await
|
|
20557
|
-
await
|
|
21430
|
+
await fs24.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
|
|
21431
|
+
await fs24.rename(tmp, target);
|
|
20558
21432
|
return name;
|
|
20559
21433
|
}
|
|
20560
21434
|
|
|
@@ -20637,14 +21511,14 @@ async function profileSwitch(name, asName) {
|
|
|
20637
21511
|
init_errors();
|
|
20638
21512
|
var SwitchCommand = class extends M8tCommand {
|
|
20639
21513
|
static paths = [["switch"]];
|
|
20640
|
-
static usage =
|
|
21514
|
+
static usage = Command43.Usage({
|
|
20641
21515
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
20642
21516
|
});
|
|
20643
|
-
profile =
|
|
20644
|
-
subscription =
|
|
20645
|
-
list =
|
|
20646
|
-
as =
|
|
20647
|
-
output =
|
|
21517
|
+
profile = Option40.String({ required: false });
|
|
21518
|
+
subscription = Option40.String("--subscription");
|
|
21519
|
+
list = Option40.Boolean("--list", false);
|
|
21520
|
+
as = Option40.String("--as");
|
|
21521
|
+
output = Option40.String("--output");
|
|
20648
21522
|
async executeCommand() {
|
|
20649
21523
|
const mode = resolveOutputMode(
|
|
20650
21524
|
this.output,
|
|
@@ -20701,7 +21575,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
20701
21575
|
|
|
20702
21576
|
// src/commands/open.ts
|
|
20703
21577
|
import { spawn as spawn5 } from "child_process";
|
|
20704
|
-
import { Command as
|
|
21578
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
20705
21579
|
|
|
20706
21580
|
// src/lib/open-targets.ts
|
|
20707
21581
|
init_errors();
|
|
@@ -20747,14 +21621,14 @@ function openUrl(url) {
|
|
|
20747
21621
|
}
|
|
20748
21622
|
var OpenCommand = class extends M8tCommand {
|
|
20749
21623
|
static paths = [["open"]];
|
|
20750
|
-
static usage =
|
|
21624
|
+
static usage = Command44.Usage({
|
|
20751
21625
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
20752
21626
|
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)."
|
|
20753
21627
|
});
|
|
20754
|
-
target =
|
|
20755
|
-
print =
|
|
20756
|
-
output =
|
|
20757
|
-
resourceGroup =
|
|
21628
|
+
target = Option41.String({ required: false });
|
|
21629
|
+
print = Option41.Boolean("--print", false);
|
|
21630
|
+
output = Option41.String("--output");
|
|
21631
|
+
resourceGroup = Option41.String("--resource-group", {
|
|
20758
21632
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
20759
21633
|
});
|
|
20760
21634
|
async executeCommand() {
|
|
@@ -20798,9 +21672,9 @@ var OpenCommand = class extends M8tCommand {
|
|
|
20798
21672
|
};
|
|
20799
21673
|
|
|
20800
21674
|
// src/commands/dream/run.ts
|
|
20801
|
-
import { Command as
|
|
21675
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
20802
21676
|
import { AzureCliCredential } from "@azure/identity";
|
|
20803
|
-
import { TableClient as
|
|
21677
|
+
import { TableClient as TableClient5 } from "@azure/data-tables";
|
|
20804
21678
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
20805
21679
|
import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
|
|
20806
21680
|
|
|
@@ -21107,7 +21981,7 @@ var ConvFetchError = class extends Error {
|
|
|
21107
21981
|
};
|
|
21108
21982
|
|
|
21109
21983
|
// ../../packages/brain/engine/dist/esm/cursor.js
|
|
21110
|
-
import { TableClient as
|
|
21984
|
+
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
21111
21985
|
|
|
21112
21986
|
// ../../packages/agent-ledger/dist/esm/row-key.js
|
|
21113
21987
|
var MAX_MS = 864e13;
|
|
@@ -21229,7 +22103,7 @@ async function commitCursorAdvance(plan, cursor) {
|
|
|
21229
22103
|
}
|
|
21230
22104
|
var CURSOR_TABLE_NAME = "BrainDreamCursor";
|
|
21231
22105
|
function makeTableCursor(credential2, tableEndpoint) {
|
|
21232
|
-
const client = new
|
|
22106
|
+
const client = new TableClient4(tableEndpoint, CURSOR_TABLE_NAME, credential2);
|
|
21233
22107
|
return new TableCursor(client);
|
|
21234
22108
|
}
|
|
21235
22109
|
function formatCursorPreview(cursor) {
|
|
@@ -21723,7 +22597,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
21723
22597
|
// ../../packages/brain/engine/dist/esm/dream/writer.js
|
|
21724
22598
|
var API2 = "https://api.github.com";
|
|
21725
22599
|
var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
|
|
21726
|
-
var isAllowedWritePath = (
|
|
22600
|
+
var isAllowedWritePath = (path33, allowedPrefix) => allowedPrefix.test(path33) && !path33.split("/").includes("..");
|
|
21727
22601
|
var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
21728
22602
|
function parseFrontmatter(content) {
|
|
21729
22603
|
const m = /^---\n([\s\S]*?)\n---/.exec(content);
|
|
@@ -21762,8 +22636,12 @@ function parseFrontmatter(content) {
|
|
|
21762
22636
|
function makeGitDataWriter(args) {
|
|
21763
22637
|
const doFetch = args.fetchImpl ?? fetch;
|
|
21764
22638
|
const repoPath = args.repository;
|
|
21765
|
-
|
|
21766
|
-
|
|
22639
|
+
const allowedPathPrefix = args.allowedPathPrefix ?? WRITER_ALLOWED_PREFIX;
|
|
22640
|
+
if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
|
|
22641
|
+
throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
|
|
22642
|
+
}
|
|
22643
|
+
async function gh(token, method, path33, body) {
|
|
22644
|
+
const res = await doFetch(`${API2}/repos/${repoPath}${path33}`, {
|
|
21767
22645
|
method,
|
|
21768
22646
|
headers: {
|
|
21769
22647
|
Authorization: `Bearer ${token}`,
|
|
@@ -21776,8 +22654,8 @@ function makeGitDataWriter(args) {
|
|
|
21776
22654
|
const text = await res.text();
|
|
21777
22655
|
return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
|
|
21778
22656
|
}
|
|
21779
|
-
async function readFileWith(token,
|
|
21780
|
-
const encodedPath =
|
|
22657
|
+
async function readFileWith(token, path33) {
|
|
22658
|
+
const encodedPath = path33.split("/").map(encodeURIComponent).join("/");
|
|
21781
22659
|
const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
|
|
21782
22660
|
if (r.status === 404)
|
|
21783
22661
|
return null;
|
|
@@ -21793,9 +22671,9 @@ function makeGitDataWriter(args) {
|
|
|
21793
22671
|
throw new Error(`fetchTip: HTTP ${String(r.status)}`);
|
|
21794
22672
|
return r.json.object.sha;
|
|
21795
22673
|
},
|
|
21796
|
-
async readFile(
|
|
22674
|
+
async readFile(path33) {
|
|
21797
22675
|
const token = await args.mintToken();
|
|
21798
|
-
return readFileWith(token,
|
|
22676
|
+
return readFileWith(token, path33);
|
|
21799
22677
|
},
|
|
21800
22678
|
async listMemory() {
|
|
21801
22679
|
const token = await args.mintToken();
|
|
@@ -21804,15 +22682,15 @@ function makeGitDataWriter(args) {
|
|
|
21804
22682
|
return [];
|
|
21805
22683
|
const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
|
|
21806
22684
|
const out = [];
|
|
21807
|
-
for (const
|
|
21808
|
-
const content = await readFileWith(token,
|
|
21809
|
-
out.push({ path:
|
|
22685
|
+
for (const path33 of paths) {
|
|
22686
|
+
const content = await readFileWith(token, path33);
|
|
22687
|
+
out.push({ path: path33, frontmatter: content ? parseFrontmatter(content) : {} });
|
|
21810
22688
|
}
|
|
21811
22689
|
return out;
|
|
21812
22690
|
},
|
|
21813
22691
|
async commitBatch(a) {
|
|
21814
22692
|
try {
|
|
21815
|
-
const escaping = a.changes.filter((c) => !isAllowedWritePath(c.path));
|
|
22693
|
+
const escaping = a.changes.filter((c) => !isAllowedWritePath(c.path, allowedPathPrefix));
|
|
21816
22694
|
if (escaping.length > 0) {
|
|
21817
22695
|
return {
|
|
21818
22696
|
ok: false,
|
|
@@ -21864,9 +22742,11 @@ function makeFoundryModelClient(openai, model) {
|
|
|
21864
22742
|
const create = openai.responses.create;
|
|
21865
22743
|
const stream = await create({
|
|
21866
22744
|
model,
|
|
22745
|
+
// Explicit `type: "message"` — the Responses API rejects untyped input
|
|
22746
|
+
// items (400 `Invalid value: ''`).
|
|
21867
22747
|
input: [
|
|
21868
|
-
{ role: "system", content: system },
|
|
21869
|
-
{ role: "user", content: user }
|
|
22748
|
+
{ type: "message", role: "system", content: system },
|
|
22749
|
+
{ type: "message", role: "user", content: user }
|
|
21870
22750
|
],
|
|
21871
22751
|
stream: true
|
|
21872
22752
|
}, signal ? { signal } : void 0);
|
|
@@ -22243,7 +23123,7 @@ async function loadMemoryContext(brain) {
|
|
|
22243
23123
|
return {
|
|
22244
23124
|
files,
|
|
22245
23125
|
indexEntries,
|
|
22246
|
-
originOf: (
|
|
23126
|
+
originOf: (path33) => originByPath.get(path33) ?? "worker"
|
|
22247
23127
|
};
|
|
22248
23128
|
}
|
|
22249
23129
|
|
|
@@ -22264,14 +23144,14 @@ function checkEvidenceMembership(delta, harvested) {
|
|
|
22264
23144
|
}
|
|
22265
23145
|
var BatchAbort = class extends Error {
|
|
22266
23146
|
path;
|
|
22267
|
-
constructor(
|
|
23147
|
+
constructor(path33, message) {
|
|
22268
23148
|
super(message);
|
|
22269
|
-
this.path =
|
|
23149
|
+
this.path = path33;
|
|
22270
23150
|
this.name = "BatchAbort";
|
|
22271
23151
|
}
|
|
22272
23152
|
};
|
|
22273
|
-
var isIndexFile = (
|
|
22274
|
-
var isMemoryIndex = (
|
|
23153
|
+
var isIndexFile = (path33) => /(^|\/)[^/]*_index\.md$/.test(path33);
|
|
23154
|
+
var isMemoryIndex = (path33) => path33 === "memory/MEMORY.md";
|
|
22275
23155
|
function targetPath(delta) {
|
|
22276
23156
|
switch (delta.verb) {
|
|
22277
23157
|
case "new":
|
|
@@ -22288,29 +23168,29 @@ function targetPath(delta) {
|
|
|
22288
23168
|
}
|
|
22289
23169
|
async function checkOriginTier(delta, lookup) {
|
|
22290
23170
|
const evidence = evidenceOf(delta) ?? [];
|
|
22291
|
-
const
|
|
22292
|
-
if (
|
|
23171
|
+
const path33 = targetPath(delta);
|
|
23172
|
+
if (path33 === null)
|
|
22293
23173
|
return { ok: true };
|
|
22294
|
-
if (isIndexFile(
|
|
22295
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
23174
|
+
if (isIndexFile(path33) && !isMemoryIndex(path33)) {
|
|
23175
|
+
return { ok: false, flagged: { kind: "flagged", path: path33, tension: `refused: ${path33} is an index file (only memory/MEMORY.md is editable)`, evidence } };
|
|
22296
23176
|
}
|
|
22297
|
-
const target = await lookup(
|
|
22298
|
-
const isStructural =
|
|
23177
|
+
const target = await lookup(path33);
|
|
23178
|
+
const isStructural = path33.startsWith("references/");
|
|
22299
23179
|
const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
|
|
22300
23180
|
const editable = origin === "worker" || origin === "dream";
|
|
22301
23181
|
if (editable)
|
|
22302
23182
|
return { ok: true };
|
|
22303
23183
|
const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
|
|
22304
23184
|
if (removesOperatorTarget) {
|
|
22305
|
-
throw new BatchAbort(
|
|
23185
|
+
throw new BatchAbort(path33, `supersede/retract OLD target ${path33} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
|
|
22306
23186
|
}
|
|
22307
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
23187
|
+
return { ok: false, flagged: { kind: "flagged", path: path33, tension: `operator-origin (${origin}); flag-only`, evidence } };
|
|
22308
23188
|
}
|
|
22309
23189
|
var ALLOWED_MEMORY = /^memory\/.+\.md$/;
|
|
22310
23190
|
var ALLOWED_INBOX = /^inbox\//;
|
|
22311
23191
|
var ALLOWED_QUARANTINE = /^quarantine\//;
|
|
22312
23192
|
var ALLOWED_FLAG = /^memory\//;
|
|
22313
|
-
var hasTraversal = (
|
|
23193
|
+
var hasTraversal = (path33) => path33.split("/").includes("..");
|
|
22314
23194
|
function checkPathAllowed(delta) {
|
|
22315
23195
|
const evidence = evidenceOf(delta) ?? [];
|
|
22316
23196
|
const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
|
|
@@ -22417,7 +23297,7 @@ ${inject}
|
|
|
22417
23297
|
---
|
|
22418
23298
|
`);
|
|
22419
23299
|
}
|
|
22420
|
-
var indexLine = (
|
|
23300
|
+
var indexLine = (path33, title, date, tags) => `- \`${path33}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
|
|
22421
23301
|
function splitIndex(index) {
|
|
22422
23302
|
const all = index.split("\n");
|
|
22423
23303
|
const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
|
|
@@ -22590,16 +23470,16 @@ function defaultDreamSeams(opts = {}) {
|
|
|
22590
23470
|
const mem = await memory(deps.brain);
|
|
22591
23471
|
const harvested = harvestedIds(input);
|
|
22592
23472
|
const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
|
|
22593
|
-
const lookup = (
|
|
23473
|
+
const lookup = (path33) => Promise.resolve(mem.files.find((f) => f.path === path33) ?? null);
|
|
22594
23474
|
const digest = [...pendingRejected];
|
|
22595
23475
|
let changes = [];
|
|
22596
23476
|
let indexEdit;
|
|
22597
23477
|
try {
|
|
22598
23478
|
for (const raw of deltas) {
|
|
22599
23479
|
const delta = forcePolicyQuarantine(raw);
|
|
22600
|
-
const
|
|
22601
|
-
if (!
|
|
22602
|
-
digest.push(
|
|
23480
|
+
const path33 = checkPathAllowed(delta);
|
|
23481
|
+
if (!path33.ok) {
|
|
23482
|
+
digest.push(path33.rejected);
|
|
22603
23483
|
continue;
|
|
22604
23484
|
}
|
|
22605
23485
|
const ev = checkEvidenceMembership(delta, harvested);
|
|
@@ -22942,20 +23822,20 @@ function redactTranscripts(input) {
|
|
|
22942
23822
|
}
|
|
22943
23823
|
var DreamRunCommand = class extends M8tCommand {
|
|
22944
23824
|
static paths = [["dream", "run"]];
|
|
22945
|
-
static usage =
|
|
23825
|
+
static usage = Command45.Usage({
|
|
22946
23826
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes).",
|
|
22947
23827
|
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."
|
|
22948
23828
|
});
|
|
22949
|
-
worker =
|
|
22950
|
-
dryRun =
|
|
22951
|
-
since =
|
|
22952
|
-
reset =
|
|
22953
|
-
showTranscripts =
|
|
23829
|
+
worker = Option42.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
23830
|
+
dryRun = Option42.Boolean("--dry-run", false, { description: "Read-only harvest; no model call, no writes." });
|
|
23831
|
+
since = Option42.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
|
|
23832
|
+
reset = Option42.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
|
|
23833
|
+
showTranscripts = Option42.Boolean("--show-transcripts", false, {
|
|
22954
23834
|
description: "Print transcript bodies (default: metadata only)."
|
|
22955
23835
|
});
|
|
22956
|
-
subscription =
|
|
22957
|
-
endpoint =
|
|
22958
|
-
output =
|
|
23836
|
+
subscription = Option42.String("--subscription");
|
|
23837
|
+
endpoint = Option42.String("--endpoint");
|
|
23838
|
+
output = Option42.String("--output");
|
|
22959
23839
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
22960
23840
|
deps;
|
|
22961
23841
|
async executeCommand() {
|
|
@@ -23150,7 +24030,7 @@ AppDependencies
|
|
|
23150
24030
|
};
|
|
23151
24031
|
}
|
|
23152
24032
|
function buildLiveSources(context, credential2) {
|
|
23153
|
-
const ledgerClient = new
|
|
24033
|
+
const ledgerClient = new TableClient5(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
|
|
23154
24034
|
const ledger = makeLedgerSource(
|
|
23155
24035
|
async (pk, sinceTs) => fetchLedgerRows(
|
|
23156
24036
|
{ workers: [pk], source: "all", from: sinceTs, to: "9999-12-31T23:59:59.999Z" },
|
|
@@ -23303,7 +24183,7 @@ function defaultDeps(overrides) {
|
|
|
23303
24183
|
}
|
|
23304
24184
|
|
|
23305
24185
|
// src/commands/foundry/create.ts
|
|
23306
|
-
import { Command as
|
|
24186
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
23307
24187
|
|
|
23308
24188
|
// src/lib/foundry-create.ts
|
|
23309
24189
|
init_errors();
|
|
@@ -23541,7 +24421,7 @@ async function createFoundryProject(args) {
|
|
|
23541
24421
|
init_errors();
|
|
23542
24422
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
23543
24423
|
static paths = [["foundry", "create"]];
|
|
23544
|
-
static usage =
|
|
24424
|
+
static usage = Command46.Usage({
|
|
23545
24425
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
23546
24426
|
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).",
|
|
23547
24427
|
examples: [
|
|
@@ -23550,16 +24430,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
23550
24430
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
23551
24431
|
]
|
|
23552
24432
|
});
|
|
23553
|
-
resourceGroup =
|
|
23554
|
-
location =
|
|
23555
|
-
account =
|
|
23556
|
-
project =
|
|
23557
|
-
model =
|
|
23558
|
-
modelVersion =
|
|
23559
|
-
capacity =
|
|
23560
|
-
subscription =
|
|
23561
|
-
skipQuotaCheck =
|
|
23562
|
-
output =
|
|
24433
|
+
resourceGroup = Option43.String("--resource-group");
|
|
24434
|
+
location = Option43.String("--location");
|
|
24435
|
+
account = Option43.String("--account");
|
|
24436
|
+
project = Option43.String("--project", "m8t");
|
|
24437
|
+
model = Option43.String("--model", "gpt-4.1-mini");
|
|
24438
|
+
modelVersion = Option43.String("--model-version", "2025-04-14");
|
|
24439
|
+
capacity = Option43.String("--capacity", "50");
|
|
24440
|
+
subscription = Option43.String("--subscription");
|
|
24441
|
+
skipQuotaCheck = Option43.Boolean("--skip-quota-check", false);
|
|
24442
|
+
output = Option43.String("--output");
|
|
23563
24443
|
async executeCommand() {
|
|
23564
24444
|
const mode = resolveOutputMode(
|
|
23565
24445
|
this.output,
|
|
@@ -23631,22 +24511,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
23631
24511
|
};
|
|
23632
24512
|
|
|
23633
24513
|
// src/commands/foundry/await-ready.ts
|
|
23634
|
-
import { Command as
|
|
24514
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
23635
24515
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
23636
24516
|
init_errors();
|
|
23637
24517
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
23638
24518
|
static paths = [["foundry", "await-ready"]];
|
|
23639
|
-
static usage =
|
|
24519
|
+
static usage = Command47.Usage({
|
|
23640
24520
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
23641
24521
|
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.",
|
|
23642
24522
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
23643
24523
|
});
|
|
23644
|
-
endpoint =
|
|
23645
|
-
consecutive =
|
|
23646
|
-
attempts =
|
|
23647
|
-
interval =
|
|
23648
|
-
subscription =
|
|
23649
|
-
output =
|
|
24524
|
+
endpoint = Option44.String("--endpoint");
|
|
24525
|
+
consecutive = Option44.String("--consecutive", "3");
|
|
24526
|
+
attempts = Option44.String("--attempts", "60");
|
|
24527
|
+
interval = Option44.String("--interval", "5");
|
|
24528
|
+
subscription = Option44.String("--subscription");
|
|
24529
|
+
output = Option44.String("--output");
|
|
23650
24530
|
async executeCommand() {
|
|
23651
24531
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23652
24532
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -23680,7 +24560,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
23680
24560
|
};
|
|
23681
24561
|
|
|
23682
24562
|
// src/commands/bootstrap/preflight.ts
|
|
23683
|
-
import { Command as
|
|
24563
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
23684
24564
|
init_errors();
|
|
23685
24565
|
|
|
23686
24566
|
// src/lib/bootstrap-preflight.ts
|
|
@@ -23754,7 +24634,7 @@ function buildPreflightBanner(who) {
|
|
|
23754
24634
|
// src/commands/bootstrap/preflight.ts
|
|
23755
24635
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
23756
24636
|
static paths = [["bootstrap", "preflight"]];
|
|
23757
|
-
static usage =
|
|
24637
|
+
static usage = Command48.Usage({
|
|
23758
24638
|
description: "Loudly verify you can install m8t-stack (Owner/UAA + directory admin) and hard-stop if not.",
|
|
23759
24639
|
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.",
|
|
23760
24640
|
examples: [
|
|
@@ -23762,8 +24642,8 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
23762
24642
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
23763
24643
|
]
|
|
23764
24644
|
});
|
|
23765
|
-
clientId =
|
|
23766
|
-
subscription =
|
|
24645
|
+
clientId = Option45.String("--client-id");
|
|
24646
|
+
subscription = Option45.String("--subscription");
|
|
23767
24647
|
async executeCommand() {
|
|
23768
24648
|
const clientId = typeof this.clientId === "string" ? this.clientId : void 0;
|
|
23769
24649
|
const account = await getAzAccount();
|
|
@@ -23819,10 +24699,10 @@ ${colors.error(" " + why)}
|
|
|
23819
24699
|
}
|
|
23820
24700
|
|
|
23821
24701
|
// src/commands/bootstrap/launch.ts
|
|
23822
|
-
import * as
|
|
24702
|
+
import * as fs27 from "fs";
|
|
23823
24703
|
import * as os13 from "os";
|
|
23824
|
-
import * as
|
|
23825
|
-
import { Command as
|
|
24704
|
+
import * as path28 from "path";
|
|
24705
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
23826
24706
|
init_errors();
|
|
23827
24707
|
|
|
23828
24708
|
// src/lib/bootstrap-mi.ts
|
|
@@ -23967,9 +24847,9 @@ async function kickInstaller(s) {
|
|
|
23967
24847
|
|
|
23968
24848
|
// src/lib/bootstrap-status.ts
|
|
23969
24849
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
23970
|
-
import * as
|
|
24850
|
+
import * as fs25 from "fs/promises";
|
|
23971
24851
|
import * as os11 from "os";
|
|
23972
|
-
import * as
|
|
24852
|
+
import * as path26 from "path";
|
|
23973
24853
|
init_errors();
|
|
23974
24854
|
var STATUS_CONTAINER = "status";
|
|
23975
24855
|
var STATUS_BLOB = "status.json";
|
|
@@ -24008,7 +24888,7 @@ async function readStatusBlob(opts) {
|
|
|
24008
24888
|
cause: e
|
|
24009
24889
|
});
|
|
24010
24890
|
}
|
|
24011
|
-
const tmpFile =
|
|
24891
|
+
const tmpFile = path26.join(os11.tmpdir(), `m8t-status-${randomUUID2()}.json`);
|
|
24012
24892
|
try {
|
|
24013
24893
|
await runAz([
|
|
24014
24894
|
"storage",
|
|
@@ -24027,7 +24907,7 @@ async function readStatusBlob(opts) {
|
|
|
24027
24907
|
"--no-progress",
|
|
24028
24908
|
"--only-show-errors"
|
|
24029
24909
|
]);
|
|
24030
|
-
const raw = await
|
|
24910
|
+
const raw = await fs25.readFile(tmpFile, "utf8");
|
|
24031
24911
|
return JSON.parse(raw.trim());
|
|
24032
24912
|
} catch (e) {
|
|
24033
24913
|
throw new LocalCliError({
|
|
@@ -24037,23 +24917,23 @@ async function readStatusBlob(opts) {
|
|
|
24037
24917
|
cause: e
|
|
24038
24918
|
});
|
|
24039
24919
|
} finally {
|
|
24040
|
-
await
|
|
24920
|
+
await fs25.rm(tmpFile, { force: true });
|
|
24041
24921
|
}
|
|
24042
24922
|
}
|
|
24043
24923
|
|
|
24044
24924
|
// src/lib/bootstrap-state.ts
|
|
24045
|
-
import * as
|
|
24925
|
+
import * as fs26 from "fs/promises";
|
|
24046
24926
|
import * as os12 from "os";
|
|
24047
|
-
import * as
|
|
24927
|
+
import * as path27 from "path";
|
|
24048
24928
|
function stateDir(home) {
|
|
24049
|
-
return
|
|
24929
|
+
return path27.join(home, ".m8t-stack");
|
|
24050
24930
|
}
|
|
24051
24931
|
function statePath(home) {
|
|
24052
|
-
return
|
|
24932
|
+
return path27.join(stateDir(home), "bootstrap.json");
|
|
24053
24933
|
}
|
|
24054
24934
|
async function readBootstrapState(home = os12.homedir()) {
|
|
24055
24935
|
try {
|
|
24056
|
-
const raw = await
|
|
24936
|
+
const raw = await fs26.readFile(statePath(home), "utf8");
|
|
24057
24937
|
return JSON.parse(raw);
|
|
24058
24938
|
} catch (e) {
|
|
24059
24939
|
if (e.code === "ENOENT") return null;
|
|
@@ -24061,10 +24941,10 @@ async function readBootstrapState(home = os12.homedir()) {
|
|
|
24061
24941
|
}
|
|
24062
24942
|
}
|
|
24063
24943
|
async function writeBootstrapState(state, home = os12.homedir()) {
|
|
24064
|
-
await
|
|
24944
|
+
await fs26.mkdir(stateDir(home), { recursive: true });
|
|
24065
24945
|
const tmp = `${statePath(home)}.tmp`;
|
|
24066
|
-
await
|
|
24067
|
-
await
|
|
24946
|
+
await fs26.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
24947
|
+
await fs26.rename(tmp, statePath(home));
|
|
24068
24948
|
}
|
|
24069
24949
|
|
|
24070
24950
|
// src/commands/bootstrap/launch.ts
|
|
@@ -24075,7 +24955,7 @@ var ACI_NAME = "m8t-installer";
|
|
|
24075
24955
|
var MI_NAME = "m8t-installer-mi";
|
|
24076
24956
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
24077
24957
|
static paths = [["bootstrap", "launch"]];
|
|
24078
|
-
static usage =
|
|
24958
|
+
static usage = Command49.Usage({
|
|
24079
24959
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
24080
24960
|
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`.",
|
|
24081
24961
|
examples: [
|
|
@@ -24084,13 +24964,13 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24084
24964
|
["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.0"]
|
|
24085
24965
|
]
|
|
24086
24966
|
});
|
|
24087
|
-
location =
|
|
24088
|
-
resourceGroup =
|
|
24089
|
-
clientId =
|
|
24090
|
-
subscription =
|
|
24091
|
-
installerTag =
|
|
24092
|
-
gatewayImageRef =
|
|
24093
|
-
githubAppCreds =
|
|
24967
|
+
location = Option46.String("--location");
|
|
24968
|
+
resourceGroup = Option46.String("--resource-group");
|
|
24969
|
+
clientId = Option46.String("--client-id");
|
|
24970
|
+
subscription = Option46.String("--subscription");
|
|
24971
|
+
installerTag = Option46.String("--installer-tag");
|
|
24972
|
+
gatewayImageRef = Option46.String("--gateway-image-ref");
|
|
24973
|
+
githubAppCreds = Option46.String("--github-app-creds");
|
|
24094
24974
|
async executeCommand() {
|
|
24095
24975
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
24096
24976
|
if (!location) {
|
|
@@ -24104,12 +24984,12 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24104
24984
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
24105
24985
|
const out = (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
24106
24986
|
`);
|
|
24107
|
-
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds :
|
|
24987
|
+
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path28.join(os13.homedir(), ".m8t-stack", "github-app.json");
|
|
24108
24988
|
let githubApp;
|
|
24109
|
-
if (
|
|
24989
|
+
if (fs27.existsSync(credsPath)) {
|
|
24110
24990
|
let parsed;
|
|
24111
24991
|
try {
|
|
24112
|
-
parsed = JSON.parse(
|
|
24992
|
+
parsed = JSON.parse(fs27.readFileSync(credsPath, "utf8"));
|
|
24113
24993
|
} catch {
|
|
24114
24994
|
throw new LocalCliError({
|
|
24115
24995
|
code: "GITHUB_APP_CREDS_INVALID",
|
|
@@ -24119,7 +24999,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24119
24999
|
}
|
|
24120
25000
|
let pem;
|
|
24121
25001
|
try {
|
|
24122
|
-
pem =
|
|
25002
|
+
pem = fs27.readFileSync(parsed.pemPath, "utf8");
|
|
24123
25003
|
} catch {
|
|
24124
25004
|
throw new LocalCliError({
|
|
24125
25005
|
code: "GITHUB_APP_PEM_MISSING",
|
|
@@ -24191,7 +25071,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24191
25071
|
};
|
|
24192
25072
|
|
|
24193
25073
|
// src/commands/bootstrap/status.ts
|
|
24194
|
-
import { Command as
|
|
25074
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
24195
25075
|
init_errors();
|
|
24196
25076
|
|
|
24197
25077
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -24219,13 +25099,13 @@ async function getAciState(opts) {
|
|
|
24219
25099
|
// src/commands/bootstrap/status.ts
|
|
24220
25100
|
var BootstrapStatusCommand = class extends M8tCommand {
|
|
24221
25101
|
static paths = [["bootstrap", "status"]];
|
|
24222
|
-
static usage =
|
|
25102
|
+
static usage = Command50.Usage({
|
|
24223
25103
|
description: "Show the cloud installer's live status (phase, progress, result).",
|
|
24224
25104
|
details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.",
|
|
24225
25105
|
examples: [["One read", "$0 bootstrap status"], ["Watch to completion", "$0 bootstrap status --watch"]]
|
|
24226
25106
|
});
|
|
24227
|
-
watch =
|
|
24228
|
-
output =
|
|
25107
|
+
watch = Option47.Boolean("--watch", false);
|
|
25108
|
+
output = Option47.String("--output");
|
|
24229
25109
|
async executeCommand() {
|
|
24230
25110
|
const state = await readBootstrapState();
|
|
24231
25111
|
if (!state) {
|
|
@@ -24296,7 +25176,7 @@ function formatStatus(d) {
|
|
|
24296
25176
|
}
|
|
24297
25177
|
|
|
24298
25178
|
// src/commands/bootstrap/reap.ts
|
|
24299
|
-
import { Command as
|
|
25179
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
24300
25180
|
init_errors();
|
|
24301
25181
|
|
|
24302
25182
|
// src/lib/bootstrap-reap.ts
|
|
@@ -24372,14 +25252,14 @@ async function reapInstaller(opts) {
|
|
|
24372
25252
|
// src/commands/bootstrap/reap.ts
|
|
24373
25253
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
24374
25254
|
static paths = [["bootstrap", "reap"]];
|
|
24375
|
-
static usage =
|
|
25255
|
+
static usage = Command51.Usage({
|
|
24376
25256
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
24377
25257
|
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.",
|
|
24378
25258
|
examples: [["Reap after done", "$0 bootstrap reap"]]
|
|
24379
25259
|
});
|
|
24380
|
-
force =
|
|
24381
|
-
sweepOrphans =
|
|
24382
|
-
yes =
|
|
25260
|
+
force = Option48.Boolean("--force", false);
|
|
25261
|
+
sweepOrphans = Option48.Boolean("--sweep-orphans", false);
|
|
25262
|
+
yes = Option48.Boolean("--yes", false);
|
|
24383
25263
|
async executeCommand() {
|
|
24384
25264
|
if (this.sweepOrphans === true) {
|
|
24385
25265
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -24444,17 +25324,17 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
|
|
|
24444
25324
|
};
|
|
24445
25325
|
|
|
24446
25326
|
// src/commands/bootstrap/finish.ts
|
|
24447
|
-
import * as
|
|
25327
|
+
import * as fs28 from "fs/promises";
|
|
24448
25328
|
import * as os15 from "os";
|
|
24449
|
-
import * as
|
|
24450
|
-
import { Command as
|
|
25329
|
+
import * as path30 from "path";
|
|
25330
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
24451
25331
|
init_errors();
|
|
24452
25332
|
|
|
24453
25333
|
// src/lib/company-profile-seed.ts
|
|
24454
25334
|
import { spawn as spawn6 } from "child_process";
|
|
24455
|
-
import { closeSync, openSync, readFileSync as
|
|
25335
|
+
import { closeSync, openSync, readFileSync as readFileSync19 } from "fs";
|
|
24456
25336
|
import * as os14 from "os";
|
|
24457
|
-
import * as
|
|
25337
|
+
import * as path29 from "path";
|
|
24458
25338
|
init_errors();
|
|
24459
25339
|
|
|
24460
25340
|
// src/lib/onboarding-profile.ts
|
|
@@ -24635,9 +25515,9 @@ function composeFounderIdentityNote(id) {
|
|
|
24635
25515
|
|
|
24636
25516
|
// src/lib/company-profile-seed.ts
|
|
24637
25517
|
function readGithubAppCreds(credsPath) {
|
|
24638
|
-
const p = credsPath ??
|
|
25518
|
+
const p = credsPath ?? path29.join(os14.homedir(), ".m8t-stack", "github-app.json");
|
|
24639
25519
|
try {
|
|
24640
|
-
return JSON.parse(
|
|
25520
|
+
return JSON.parse(readFileSync19(p, "utf8"));
|
|
24641
25521
|
} catch {
|
|
24642
25522
|
return null;
|
|
24643
25523
|
}
|
|
@@ -24665,7 +25545,7 @@ async function resolveSeedContext(opts) {
|
|
|
24665
25545
|
async function applyProfileToBrain(args) {
|
|
24666
25546
|
const token = await mintInstallationTokenFromPem({
|
|
24667
25547
|
appId: args.appCreds.appId,
|
|
24668
|
-
privateKeyPem:
|
|
25548
|
+
privateKeyPem: readFileSync19(args.appCreds.pemPath, "utf8"),
|
|
24669
25549
|
installationId: args.appCreds.installationId,
|
|
24670
25550
|
fetchImpl: args.fetchImpl
|
|
24671
25551
|
});
|
|
@@ -24700,7 +25580,7 @@ async function applyProfileToBrain(args) {
|
|
|
24700
25580
|
});
|
|
24701
25581
|
}
|
|
24702
25582
|
function spawnDetachedSeedWatch() {
|
|
24703
|
-
const logPath =
|
|
25583
|
+
const logPath = path29.join(os14.homedir(), ".m8t-stack", "seed-profile.log");
|
|
24704
25584
|
const fd = openSync(logPath, "a");
|
|
24705
25585
|
try {
|
|
24706
25586
|
const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
|
|
@@ -24777,14 +25657,14 @@ function renderInstallSummary(args) {
|
|
|
24777
25657
|
// src/commands/bootstrap/finish.ts
|
|
24778
25658
|
var BootstrapFinishCommand = class extends M8tCommand {
|
|
24779
25659
|
static paths = [["bootstrap", "finish"]];
|
|
24780
|
-
static usage =
|
|
25660
|
+
static usage = Command52.Usage({
|
|
24781
25661
|
description: "Point your local tools at the now-live platform (repo-root marker, discovery cache, next steps).",
|
|
24782
25662
|
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.",
|
|
24783
25663
|
examples: [["Finish local", "$0 bootstrap finish --repo-root /path/to/m8t"]]
|
|
24784
25664
|
});
|
|
24785
|
-
repoRoot =
|
|
24786
|
-
subscription =
|
|
24787
|
-
resourceGroup =
|
|
25665
|
+
repoRoot = Option49.String("--repo-root");
|
|
25666
|
+
subscription = Option49.String("--subscription");
|
|
25667
|
+
resourceGroup = Option49.String("--resource-group");
|
|
24788
25668
|
async executeCommand() {
|
|
24789
25669
|
const state = await readBootstrapState();
|
|
24790
25670
|
if (!state) {
|
|
@@ -24799,9 +25679,9 @@ var BootstrapFinishCommand = class extends M8tCommand {
|
|
|
24799
25679
|
});
|
|
24800
25680
|
}
|
|
24801
25681
|
const repoRoot = (typeof this.repoRoot === "string" ? this.repoRoot : void 0) ?? process.cwd();
|
|
24802
|
-
const markerDir =
|
|
24803
|
-
await
|
|
24804
|
-
await
|
|
25682
|
+
const markerDir = path30.join(os15.homedir(), ".m8t-stack");
|
|
25683
|
+
await fs28.mkdir(markerDir, { recursive: true });
|
|
25684
|
+
await fs28.writeFile(path30.join(markerDir, "repo-root"), `${repoRoot}
|
|
24805
25685
|
`, "utf8");
|
|
24806
25686
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? state.subscriptionId;
|
|
24807
25687
|
const resourceGroup = (typeof this.resourceGroup === "string" ? this.resourceGroup : void 0) ?? state.resourceGroup;
|
|
@@ -24837,7 +25717,7 @@ var BootstrapFinishCommand = class extends M8tCommand {
|
|
|
24837
25717
|
}
|
|
24838
25718
|
let brainOrg = null;
|
|
24839
25719
|
try {
|
|
24840
|
-
const credsRaw = await
|
|
25720
|
+
const credsRaw = await fs28.readFile(path30.join(markerDir, "github-app.json"), "utf8");
|
|
24841
25721
|
const creds = JSON.parse(credsRaw);
|
|
24842
25722
|
brainOrg = typeof creds.org === "string" ? creds.org : null;
|
|
24843
25723
|
} catch {
|
|
@@ -24868,18 +25748,18 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
|
|
|
24868
25748
|
};
|
|
24869
25749
|
|
|
24870
25750
|
// src/commands/bootstrap/ui.ts
|
|
24871
|
-
import * as
|
|
25751
|
+
import * as fs30 from "fs";
|
|
24872
25752
|
import * as os17 from "os";
|
|
24873
|
-
import * as
|
|
24874
|
-
import { Command as
|
|
24875
|
-
import { DefaultAzureCredential as
|
|
25753
|
+
import * as path32 from "path";
|
|
25754
|
+
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
25755
|
+
import { DefaultAzureCredential as DefaultAzureCredential20 } from "@azure/identity";
|
|
24876
25756
|
init_errors();
|
|
24877
25757
|
|
|
24878
25758
|
// src/lib/bootstrap-ui.ts
|
|
24879
|
-
import * as
|
|
25759
|
+
import * as fs29 from "fs";
|
|
24880
25760
|
import * as net from "net";
|
|
24881
25761
|
import * as os16 from "os";
|
|
24882
|
-
import * as
|
|
25762
|
+
import * as path31 from "path";
|
|
24883
25763
|
import { spawn as spawn7 } from "child_process";
|
|
24884
25764
|
init_errors();
|
|
24885
25765
|
init_rbac();
|
|
@@ -24946,9 +25826,9 @@ async function ensureFounderFoundryRole(args) {
|
|
|
24946
25826
|
});
|
|
24947
25827
|
}
|
|
24948
25828
|
function writeWebEnvLocal(args) {
|
|
24949
|
-
const envPath =
|
|
24950
|
-
if (
|
|
24951
|
-
|
|
25829
|
+
const envPath = path31.join(args.repoRoot, "apps", "web", ".env.local");
|
|
25830
|
+
if (fs29.existsSync(envPath)) {
|
|
25831
|
+
fs29.copyFileSync(envPath, envPath + ".bak");
|
|
24952
25832
|
}
|
|
24953
25833
|
const body = [
|
|
24954
25834
|
"# Written by `m8t bootstrap ui` \u2014 onboarding chat-only run (Simple Stacey).",
|
|
@@ -24962,7 +25842,7 @@ function writeWebEnvLocal(args) {
|
|
|
24962
25842
|
"NEXT_PUBLIC_M8T_ONBOARDING=1",
|
|
24963
25843
|
""
|
|
24964
25844
|
].join("\n");
|
|
24965
|
-
|
|
25845
|
+
fs29.writeFileSync(envPath, body, "utf8");
|
|
24966
25846
|
return envPath;
|
|
24967
25847
|
}
|
|
24968
25848
|
function assertNodeVersion(versionString = process.version) {
|
|
@@ -25004,10 +25884,10 @@ async function installWebDeps(repoRoot) {
|
|
|
25004
25884
|
await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
|
|
25005
25885
|
}
|
|
25006
25886
|
function onboardingUiPaths(home = os16.homedir()) {
|
|
25007
|
-
const dir =
|
|
25887
|
+
const dir = path31.join(home, ".m8t-stack");
|
|
25008
25888
|
return {
|
|
25009
|
-
logPath:
|
|
25010
|
-
pidPath:
|
|
25889
|
+
logPath: path31.join(dir, "onboarding-ui.log"),
|
|
25890
|
+
pidPath: path31.join(dir, "onboarding-ui.pid")
|
|
25011
25891
|
};
|
|
25012
25892
|
}
|
|
25013
25893
|
async function serveOnboardingUiDetached(args) {
|
|
@@ -25031,8 +25911,8 @@ async function serveOnboardingUiDetached(args) {
|
|
|
25031
25911
|
if (await isPortOpen()) {
|
|
25032
25912
|
return { alreadyRunning: true, logPath };
|
|
25033
25913
|
}
|
|
25034
|
-
|
|
25035
|
-
const fd =
|
|
25914
|
+
fs29.mkdirSync(path31.dirname(logPath), { recursive: true });
|
|
25915
|
+
const fd = fs29.openSync(logPath, "a");
|
|
25036
25916
|
const child = spawn7("pnpm", ["--filter", "web", "dev"], {
|
|
25037
25917
|
cwd: args.repoRoot,
|
|
25038
25918
|
detached: true,
|
|
@@ -25040,7 +25920,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
25040
25920
|
env: { ...process.env, PORT: args.port },
|
|
25041
25921
|
shell: false
|
|
25042
25922
|
});
|
|
25043
|
-
|
|
25923
|
+
fs29.closeSync(fd);
|
|
25044
25924
|
if (child.pid == null) {
|
|
25045
25925
|
throw new LocalCliError({
|
|
25046
25926
|
code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
|
|
@@ -25049,7 +25929,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
25049
25929
|
});
|
|
25050
25930
|
}
|
|
25051
25931
|
child.unref();
|
|
25052
|
-
|
|
25932
|
+
fs29.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
25053
25933
|
const deadline = Date.now() + 45e3;
|
|
25054
25934
|
const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
25055
25935
|
while (Date.now() < deadline) {
|
|
@@ -25072,7 +25952,7 @@ function stopOnboardingUi(home = os16.homedir()) {
|
|
|
25072
25952
|
const { pidPath } = onboardingUiPaths(home);
|
|
25073
25953
|
let pidStr;
|
|
25074
25954
|
try {
|
|
25075
|
-
pidStr =
|
|
25955
|
+
pidStr = fs29.readFileSync(pidPath, "utf8").trim();
|
|
25076
25956
|
} catch {
|
|
25077
25957
|
return false;
|
|
25078
25958
|
}
|
|
@@ -25085,7 +25965,7 @@ function stopOnboardingUi(home = os16.homedir()) {
|
|
|
25085
25965
|
}
|
|
25086
25966
|
}
|
|
25087
25967
|
try {
|
|
25088
|
-
|
|
25968
|
+
fs29.unlinkSync(pidPath);
|
|
25089
25969
|
} catch {
|
|
25090
25970
|
}
|
|
25091
25971
|
return true;
|
|
@@ -25142,7 +26022,7 @@ async function deploySimpleStacey(args) {
|
|
|
25142
26022
|
// src/commands/bootstrap/ui.ts
|
|
25143
26023
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
25144
26024
|
static paths = [["bootstrap", "ui"]];
|
|
25145
|
-
static usage =
|
|
26025
|
+
static usage = Command53.Usage({
|
|
25146
26026
|
description: "Deploy Simple Stacey + start the local onboarding chat UI in the background (returns immediately).",
|
|
25147
26027
|
details: [
|
|
25148
26028
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -25160,15 +26040,15 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
25160
26040
|
["Foreground (blocking, old behaviour)", "$0 bootstrap ui --repo-root /path/to/m8t --foreground"]
|
|
25161
26041
|
]
|
|
25162
26042
|
});
|
|
25163
|
-
repoRoot =
|
|
25164
|
-
port =
|
|
25165
|
-
endpoint =
|
|
26043
|
+
repoRoot = Option50.String("--repo-root");
|
|
26044
|
+
port = Option50.String("--port", "3000");
|
|
26045
|
+
endpoint = Option50.String("--endpoint", {
|
|
25166
26046
|
description: "Foundry project endpoint to target \u2014 disambiguates when the subscription has multiple projects."
|
|
25167
26047
|
});
|
|
25168
|
-
prepOnly =
|
|
25169
|
-
skipInstall =
|
|
25170
|
-
stop =
|
|
25171
|
-
foreground =
|
|
26048
|
+
prepOnly = Option50.Boolean("--prep-only", false);
|
|
26049
|
+
skipInstall = Option50.Boolean("--skip-install", false);
|
|
26050
|
+
stop = Option50.Boolean("--stop", false);
|
|
26051
|
+
foreground = Option50.Boolean("--foreground", false);
|
|
25172
26052
|
async executeCommand() {
|
|
25173
26053
|
if (this.stop === true) {
|
|
25174
26054
|
const stopped = stopOnboardingUi();
|
|
@@ -25201,10 +26081,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
25201
26081
|
this.context.stderr.write(` ${colors.dim(m)}
|
|
25202
26082
|
`);
|
|
25203
26083
|
};
|
|
25204
|
-
if (
|
|
26084
|
+
if (fs30.existsSync(path32.join(os17.homedir(), ".m8t-stack", "config.yaml"))) {
|
|
25205
26085
|
out(colors.error("\u26A0 an existing ~/.m8t-stack/config.yaml will override the onboarding app registration in apps/web \u2014 if Microsoft sign-in fails, move it aside and retry."));
|
|
25206
26086
|
}
|
|
25207
|
-
const credential2 = new
|
|
26087
|
+
const credential2 = new DefaultAzureCredential20();
|
|
25208
26088
|
const account = await getAzAccount();
|
|
25209
26089
|
assertNodeVersion();
|
|
25210
26090
|
out("waiting for Foundry (the installer's foundry-create phase)\u2026");
|
|
@@ -25275,10 +26155,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
25275
26155
|
};
|
|
25276
26156
|
|
|
25277
26157
|
// src/commands/bootstrap/seed-profile.ts
|
|
25278
|
-
import { Command as
|
|
26158
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
25279
26159
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
25280
26160
|
static paths = [["bootstrap", "seed-profile"]];
|
|
25281
|
-
static usage =
|
|
26161
|
+
static usage = Command54.Usage({
|
|
25282
26162
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
|
|
25283
26163
|
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.",
|
|
25284
26164
|
examples: [
|
|
@@ -25286,11 +26166,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
25286
26166
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
25287
26167
|
]
|
|
25288
26168
|
});
|
|
25289
|
-
endpoint =
|
|
25290
|
-
brain =
|
|
25291
|
-
watch =
|
|
25292
|
-
timeout =
|
|
25293
|
-
githubAppCreds =
|
|
26169
|
+
endpoint = Option51.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
26170
|
+
brain = Option51.String("--brain", { description: "Override the brain repo (default <org>/stacey-brain)." });
|
|
26171
|
+
watch = Option51.Boolean("--watch", false, { description: "Poll until the questionnaire completes (or --timeout)." });
|
|
26172
|
+
timeout = Option51.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
26173
|
+
githubAppCreds = Option51.String("--github-app-creds");
|
|
25294
26174
|
async executeCommand() {
|
|
25295
26175
|
const ctx = await resolveSeedContext({
|
|
25296
26176
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -25375,7 +26255,9 @@ cli.register(CoderTeardownCommand);
|
|
|
25375
26255
|
cli.register(AzureExecDeployCommand);
|
|
25376
26256
|
cli.register(PlatformStatusCommand);
|
|
25377
26257
|
cli.register(PlatformUpdateCommand);
|
|
26258
|
+
cli.register(PlatformConvergeCommand);
|
|
25378
26259
|
cli.register(PlatformEnableCostReportCommand);
|
|
26260
|
+
cli.register(PlatformEnableAutoUpdateCommand);
|
|
25379
26261
|
cli.register(DeployCommand);
|
|
25380
26262
|
cli.register(AgentDeployAdvisorCommand);
|
|
25381
26263
|
cli.register(AgentRemoveCommand);
|