@m8t-stack/cli 0.2.18 → 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 +1186 -316
- 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) {
|
|
@@ -17601,12 +17617,49 @@ function entityToStamp(e) {
|
|
|
17601
17617
|
|
|
17602
17618
|
// ../../packages/platform-release/dist/esm/channel-url.js
|
|
17603
17619
|
var CHANNEL_LATEST_URL = "https://github.com/m8t-run/m8t/releases/latest/download/manifest.json";
|
|
17604
|
-
function
|
|
17605
|
-
|
|
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);
|
|
17606
17650
|
}
|
|
17607
17651
|
|
|
17608
17652
|
// src/lib/release-channel.ts
|
|
17609
17653
|
init_errors();
|
|
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 };
|
|
17662
|
+
}
|
|
17610
17663
|
async function fetchManifest(source, deps = {}) {
|
|
17611
17664
|
const readFile8 = deps.readFile ?? ((p) => fs18.readFileSync(p, "utf8"));
|
|
17612
17665
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -17899,6 +17952,24 @@ function buildBicepParams(p) {
|
|
|
17899
17952
|
`acrResourceId=${p.acrResourceId}`
|
|
17900
17953
|
];
|
|
17901
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}`);
|
|
17902
17973
|
return params;
|
|
17903
17974
|
}
|
|
17904
17975
|
async function resolveRepoRoot2(home = os9.homedir()) {
|
|
@@ -18154,22 +18225,22 @@ async function applyPlan(plan, deps, ctx) {
|
|
|
18154
18225
|
stamp.components.gateway = await deps.applyGateway(a, ctx);
|
|
18155
18226
|
} else if (a.component === "codingAgent" || a.component === "azureExecutor") {
|
|
18156
18227
|
const r = await deps.applyAgent(a, ctx);
|
|
18157
|
-
if (r
|
|
18158
|
-
|
|
18159
|
-
|
|
18160
|
-
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;
|
|
18161
18231
|
}
|
|
18232
|
+
stamp.components[a.component] = { ...r, state: "managed" };
|
|
18162
18233
|
} else {
|
|
18163
18234
|
if (!a.personaName) {
|
|
18164
18235
|
throw new LocalCliError({ code: "PLATFORM_PERSONA_NO_NAME", message: "persona action missing personaName" });
|
|
18165
18236
|
}
|
|
18166
18237
|
const r = await deps.applyPersona(a, ctx);
|
|
18167
18238
|
if (r === "absent") {
|
|
18168
|
-
ctx.onProgress?.(`persona '${a.personaName}': not deployed on this install \u2014
|
|
18169
|
-
|
|
18170
|
-
const prev = Object.hasOwn(stamp.components.personas, a.personaName) ? stamp.components.personas[a.personaName] : void 0;
|
|
18171
|
-
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;
|
|
18172
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;
|
|
18173
18244
|
}
|
|
18174
18245
|
applied.push(a);
|
|
18175
18246
|
stamp.lastResult = "partial";
|
|
@@ -18186,6 +18257,9 @@ async function applyPlan(plan, deps, ctx) {
|
|
|
18186
18257
|
}
|
|
18187
18258
|
stamp.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18188
18259
|
await deps.writeStamp(stamp);
|
|
18260
|
+
if (!isTargeted && ctx.suffix && deps.writeInfraParams) {
|
|
18261
|
+
await deps.writeInfraParams(ctx, ctx.suffix);
|
|
18262
|
+
}
|
|
18189
18263
|
return { stamp, applied };
|
|
18190
18264
|
} catch (e) {
|
|
18191
18265
|
stamp.lastResult = "failed";
|
|
@@ -18277,7 +18351,8 @@ async function resolveBicepParamsForConverge(ctx, opts = {}) {
|
|
|
18277
18351
|
foundryProjectEndpoint: opts.foundryEndpoint ?? "",
|
|
18278
18352
|
acrPullIdentityResourceId,
|
|
18279
18353
|
acrResourceId,
|
|
18280
|
-
foundryTracingMode: opts.foundryTracingMode
|
|
18354
|
+
foundryTracingMode: opts.foundryTracingMode,
|
|
18355
|
+
...opts.assignSubscriptionRoles !== void 0 ? { assignSubscriptionRoles: opts.assignSubscriptionRoles } : {}
|
|
18281
18356
|
};
|
|
18282
18357
|
}
|
|
18283
18358
|
function swapHostedImage(def, newImage) {
|
|
@@ -18544,6 +18619,59 @@ import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/ident
|
|
|
18544
18619
|
|
|
18545
18620
|
// src/lib/platform-converge-cli.ts
|
|
18546
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
|
|
18547
18675
|
init_errors();
|
|
18548
18676
|
var HOSTED_AGENT_PERSONA_DIR = {
|
|
18549
18677
|
codingAgent: "coding-agent",
|
|
@@ -18603,7 +18731,23 @@ async function buildConvergeDeps(args) {
|
|
|
18603
18731
|
},
|
|
18604
18732
|
async applyInfra(a, ctx) {
|
|
18605
18733
|
const target = infraTargetSha(ctx.manifest, args.tree);
|
|
18606
|
-
|
|
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);
|
|
18607
18751
|
},
|
|
18608
18752
|
async healthGate(_ctx, applied) {
|
|
18609
18753
|
for (const a of applied) {
|
|
@@ -18644,6 +18788,22 @@ async function buildConvergeDeps(args) {
|
|
|
18644
18788
|
stamp: { ...s, cli: CLI_VERSION },
|
|
18645
18789
|
onProgress: args.onProgress
|
|
18646
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
|
+
});
|
|
18647
18807
|
}
|
|
18648
18808
|
};
|
|
18649
18809
|
}
|
|
@@ -18799,7 +18959,7 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
18799
18959
|
onProgress,
|
|
18800
18960
|
onWarn
|
|
18801
18961
|
});
|
|
18802
|
-
await applyPlan(plan, deps, { credential: credential2, subscriptionId, resourceGroup, contentDir, manifest, onProgress });
|
|
18962
|
+
await applyPlan(plan, deps, { credential: credential2, subscriptionId, resourceGroup, contentDir, manifest, onProgress, suffix });
|
|
18803
18963
|
if (mode === "json") this.context.stdout.write(renderJson({ ...plan, applied: true }) + "\n");
|
|
18804
18964
|
else log(colors.success(`converged \u2192 ${plan.targetVersion} \u2713`));
|
|
18805
18965
|
return 0;
|
|
@@ -18831,8 +18991,497 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
18831
18991
|
}
|
|
18832
18992
|
};
|
|
18833
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
|
+
|
|
18834
19483
|
// src/commands/platform/enable-cost-report.ts
|
|
18835
|
-
import { Command as
|
|
19484
|
+
import { Command as Command34, Option as Option31 } from "clipanion";
|
|
18836
19485
|
|
|
18837
19486
|
// src/lib/wire-gateway-acs.ts
|
|
18838
19487
|
init_rbac();
|
|
@@ -18872,7 +19521,7 @@ async function wireGatewayForAcs(args) {
|
|
|
18872
19521
|
init_errors();
|
|
18873
19522
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
18874
19523
|
static paths = [["platform", "enable-cost-report"]];
|
|
18875
|
-
static usage =
|
|
19524
|
+
static usage = Command34.Usage({
|
|
18876
19525
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
18877
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')."
|
|
18878
19527
|
});
|
|
@@ -18956,8 +19605,161 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
18956
19605
|
}
|
|
18957
19606
|
};
|
|
18958
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
|
+
|
|
18959
19761
|
// src/commands/deploy.ts
|
|
18960
|
-
import { Command as
|
|
19762
|
+
import { Command as Command36, Option as Option33 } from "clipanion";
|
|
18961
19763
|
|
|
18962
19764
|
// src/lib/app-reg.ts
|
|
18963
19765
|
init_errors();
|
|
@@ -19218,7 +20020,7 @@ async function patchRedirectUris(appObjectId, fqdn) {
|
|
|
19218
20020
|
init_errors();
|
|
19219
20021
|
|
|
19220
20022
|
// src/lib/whatif.ts
|
|
19221
|
-
import * as
|
|
20023
|
+
import * as path23 from "path";
|
|
19222
20024
|
function deriveResourceType(resourceId) {
|
|
19223
20025
|
if (resourceId.startsWith("[")) return "";
|
|
19224
20026
|
const afterProviders = resourceId.split("/providers/")[1];
|
|
@@ -19229,7 +20031,7 @@ function deriveResourceType(resourceId) {
|
|
|
19229
20031
|
return parts.join("/");
|
|
19230
20032
|
}
|
|
19231
20033
|
async function runWhatIf(opts) {
|
|
19232
|
-
const bicepPath =
|
|
20034
|
+
const bicepPath = path23.join(opts.repoRoot, "deploy", "main.bicep");
|
|
19233
20035
|
const out = await runAz([
|
|
19234
20036
|
"deployment",
|
|
19235
20037
|
"group",
|
|
@@ -19294,9 +20096,9 @@ function flattenDelta(delta, prefix = "") {
|
|
|
19294
20096
|
const out = [];
|
|
19295
20097
|
for (const d of delta) {
|
|
19296
20098
|
const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
|
|
19297
|
-
const
|
|
19298
|
-
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children,
|
|
19299
|
-
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 });
|
|
19300
20102
|
}
|
|
19301
20103
|
return out;
|
|
19302
20104
|
}
|
|
@@ -19346,24 +20148,40 @@ function classifyWhatIf(changes) {
|
|
|
19346
20148
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-run/m8t:latest";
|
|
19347
20149
|
var DeployCommand = class extends M8tCommand {
|
|
19348
20150
|
static paths = [["deploy"]];
|
|
19349
|
-
static usage =
|
|
20151
|
+
static usage = Command36.Usage({
|
|
19350
20152
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
19351
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."
|
|
19352
20154
|
});
|
|
19353
|
-
subscription =
|
|
19354
|
-
resourceGroup =
|
|
19355
|
-
location =
|
|
19356
|
-
suffix =
|
|
19357
|
-
imageRef =
|
|
19358
|
-
acrPullIdentity =
|
|
19359
|
-
acrResourceId =
|
|
19360
|
-
foundryEndpoint =
|
|
19361
|
-
foundryResourceId =
|
|
19362
|
-
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");
|
|
19363
20165
|
// project | account | skip (bicep default: project)
|
|
19364
|
-
clientId =
|
|
19365
|
-
whatIf =
|
|
19366
|
-
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");
|
|
19367
20185
|
async executeCommand() {
|
|
19368
20186
|
const mode = resolveOutputMode(
|
|
19369
20187
|
this.output,
|
|
@@ -19407,7 +20225,20 @@ var DeployCommand = class extends M8tCommand {
|
|
|
19407
20225
|
foundryProjectEndpoint: foundryEndpoint,
|
|
19408
20226
|
acrPullIdentityResourceId: acrPullIdentityResourceId2,
|
|
19409
20227
|
acrResourceId: acrResourceId2,
|
|
19410
|
-
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
|
|
19411
20242
|
});
|
|
19412
20243
|
const changes = await runWhatIf({ resourceGroup: this.resourceGroup, repoRoot: repoRoot2, params: params2 });
|
|
19413
20244
|
const classified = classifyWhatIf(changes);
|
|
@@ -19442,7 +20273,20 @@ var DeployCommand = class extends M8tCommand {
|
|
|
19442
20273
|
foundryProjectEndpoint: foundryEndpoint,
|
|
19443
20274
|
acrPullIdentityResourceId,
|
|
19444
20275
|
acrResourceId,
|
|
19445
|
-
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
|
|
19446
20290
|
});
|
|
19447
20291
|
log("running Bicep deployment (~3-5 min)\u2026");
|
|
19448
20292
|
const outputs = await runBicepDeployment({
|
|
@@ -19477,7 +20321,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
19477
20321
|
|
|
19478
20322
|
// src/commands/eval/skill.ts
|
|
19479
20323
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
19480
|
-
import { Command as
|
|
20324
|
+
import { Command as Command37, Option as Option34 } from "clipanion";
|
|
19481
20325
|
init_errors();
|
|
19482
20326
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
19483
20327
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -19502,14 +20346,14 @@ function parseVerdict(stdout) {
|
|
|
19502
20346
|
}
|
|
19503
20347
|
var EvalSkillCommand = class extends M8tCommand {
|
|
19504
20348
|
static paths = [["eval", "skill"]];
|
|
19505
|
-
static usage =
|
|
20349
|
+
static usage = Command37.Usage({
|
|
19506
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)."
|
|
19507
20351
|
});
|
|
19508
|
-
candidate =
|
|
19509
|
-
skillsDir =
|
|
19510
|
-
noJudge =
|
|
19511
|
-
deployment =
|
|
19512
|
-
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");
|
|
19513
20357
|
executeCommand() {
|
|
19514
20358
|
return Promise.resolve(this._runCommand());
|
|
19515
20359
|
}
|
|
@@ -19566,9 +20410,9 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
19566
20410
|
|
|
19567
20411
|
// src/commands/eval/exam.ts
|
|
19568
20412
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
19569
|
-
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, readFileSync as
|
|
19570
|
-
import { join as
|
|
19571
|
-
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";
|
|
19572
20416
|
init_errors();
|
|
19573
20417
|
init_esm();
|
|
19574
20418
|
function parseArmToken(tok, opts) {
|
|
@@ -19656,7 +20500,7 @@ function parseExamVerdict(stdout) {
|
|
|
19656
20500
|
}
|
|
19657
20501
|
return v;
|
|
19658
20502
|
}
|
|
19659
|
-
var DEFAULT_REPS = { impact: 3, dream_delta: 5 };
|
|
20503
|
+
var DEFAULT_REPS = { impact: 3, dream_delta: 5, skill_abstraction: 5 };
|
|
19660
20504
|
function inferExamType(arms, explicit) {
|
|
19661
20505
|
if (explicit !== void 0) {
|
|
19662
20506
|
if (!EXAM_TYPES.has(explicit)) {
|
|
@@ -19677,6 +20521,22 @@ function inferExamType(arms, explicit) {
|
|
|
19677
20521
|
message: "could not infer --exam-type from arms \u2014 pass --exam-type impact|dream_delta|tournament"
|
|
19678
20522
|
});
|
|
19679
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
|
+
}
|
|
19680
20540
|
function resolveJudgeDeployment(flag, env) {
|
|
19681
20541
|
if (typeof flag === "string" && flag.length > 0) return { deployment: flag };
|
|
19682
20542
|
const fromEnv = env.EXAM_JUDGE_DEPLOYMENT;
|
|
@@ -19733,9 +20593,9 @@ function resolveRefereeHome(refereeHomeOut, env) {
|
|
|
19733
20593
|
return null;
|
|
19734
20594
|
}
|
|
19735
20595
|
function resolveTaskSetDir(base, worker, version) {
|
|
19736
|
-
if (existsSync13(
|
|
20596
|
+
if (existsSync13(join23(base, version))) return version;
|
|
19737
20597
|
const prefixed = `${worker}-${version}`;
|
|
19738
|
-
if (existsSync13(
|
|
20598
|
+
if (existsSync13(join23(base, prefixed))) return prefixed;
|
|
19739
20599
|
if (version === "latest" && existsSync13(base)) {
|
|
19740
20600
|
const dirs = readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
|
|
19741
20601
|
if (dirs.length > 0) return dirs[dirs.length - 1];
|
|
@@ -19751,11 +20611,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
|
|
|
19751
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)"
|
|
19752
20612
|
});
|
|
19753
20613
|
}
|
|
19754
|
-
const versionDir = resolveTaskSetDir(
|
|
19755
|
-
const manifestPath =
|
|
20614
|
+
const versionDir = resolveTaskSetDir(join23(home, "tasksets", worker), worker, version);
|
|
20615
|
+
const manifestPath = join23(home, "tasksets", worker, versionDir, "manifest.yaml");
|
|
19756
20616
|
let text;
|
|
19757
20617
|
try {
|
|
19758
|
-
text =
|
|
20618
|
+
text = readFileSync16(manifestPath, "utf-8");
|
|
19759
20619
|
} catch (e) {
|
|
19760
20620
|
throw new LocalCliError({
|
|
19761
20621
|
code: "EXAM_REDACTION_UNSAFE",
|
|
@@ -19782,23 +20642,24 @@ function buildPlan(args) {
|
|
|
19782
20642
|
}
|
|
19783
20643
|
var EvalExamCommand = class extends M8tCommand {
|
|
19784
20644
|
static paths = [["eval", "exam"]];
|
|
19785
|
-
static usage =
|
|
20645
|
+
static usage = Command38.Usage({
|
|
19786
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."
|
|
19787
20647
|
});
|
|
19788
|
-
worker =
|
|
19789
|
-
arms =
|
|
19790
|
-
taskSet =
|
|
19791
|
-
examType =
|
|
19792
|
-
|
|
19793
|
-
|
|
19794
|
-
|
|
19795
|
-
|
|
19796
|
-
|
|
19797
|
-
|
|
19798
|
-
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
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");
|
|
19802
20663
|
async executeCommand() {
|
|
19803
20664
|
await Promise.resolve();
|
|
19804
20665
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -19807,6 +20668,7 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
19807
20668
|
if (!armsRaw) throw new LocalCliError({ code: "USAGE", message: "--arms <spec> is required" });
|
|
19808
20669
|
const arms = parseArmSpec(armsRaw, "git", { allowStub: this.allowStub === true });
|
|
19809
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);
|
|
19810
20672
|
let reps = DEFAULT_REPS[examType] ?? 3;
|
|
19811
20673
|
if (typeof this.reps === "string") {
|
|
19812
20674
|
const parsed = Number.parseInt(this.reps, 10);
|
|
@@ -19874,9 +20736,9 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
19874
20736
|
if (out !== void 0) {
|
|
19875
20737
|
const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
|
|
19876
20738
|
mkdirSync4(out, { recursive: true });
|
|
19877
|
-
writeFileSync4(
|
|
20739
|
+
writeFileSync4(join23(out, "verdict.json"), JSON.stringify(verdict, null, 2));
|
|
19878
20740
|
const feed = redactForFeed(verdict, sealedTaskIds);
|
|
19879
|
-
writeFileSync4(
|
|
20741
|
+
writeFileSync4(join23(out, "feed.json"), JSON.stringify(feed, null, 2));
|
|
19880
20742
|
}
|
|
19881
20743
|
if (mode === "json") {
|
|
19882
20744
|
this.context.stdout.write(renderJson(verdict) + "\n");
|
|
@@ -19912,10 +20774,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
19912
20774
|
};
|
|
19913
20775
|
|
|
19914
20776
|
// src/commands/version.ts
|
|
19915
|
-
import { Command as
|
|
20777
|
+
import { Command as Command39, Option as Option36 } from "clipanion";
|
|
19916
20778
|
var VersionCommand = class extends M8tCommand {
|
|
19917
20779
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
19918
|
-
static usage =
|
|
20780
|
+
static usage = Command39.Usage({
|
|
19919
20781
|
description: "Print the CLI version.",
|
|
19920
20782
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
19921
20783
|
examples: [
|
|
@@ -19923,8 +20785,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
19923
20785
|
["Print as JSON", "$0 version --output json"]
|
|
19924
20786
|
]
|
|
19925
20787
|
});
|
|
19926
|
-
output =
|
|
19927
|
-
verbose =
|
|
20788
|
+
output = Option36.String("--output", { description: "pretty | json | auto (default)" });
|
|
20789
|
+
verbose = Option36.Boolean("--verbose", false);
|
|
19928
20790
|
executeCommand() {
|
|
19929
20791
|
const mode = resolveOutputMode(
|
|
19930
20792
|
this.output ?? "auto",
|
|
@@ -19955,18 +20817,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
19955
20817
|
};
|
|
19956
20818
|
|
|
19957
20819
|
// src/commands/whoami.ts
|
|
19958
|
-
import { Command as
|
|
20820
|
+
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
19959
20821
|
var WhoamiCommand = class extends M8tCommand {
|
|
19960
20822
|
static paths = [["whoami"]];
|
|
19961
|
-
static usage =
|
|
20823
|
+
static usage = Command40.Usage({
|
|
19962
20824
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
19963
20825
|
});
|
|
19964
|
-
output =
|
|
19965
|
-
verbose =
|
|
19966
|
-
subscription =
|
|
20826
|
+
output = Option37.String("--output");
|
|
20827
|
+
verbose = Option37.Boolean("--verbose", false);
|
|
20828
|
+
subscription = Option37.String("--subscription", {
|
|
19967
20829
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
19968
20830
|
});
|
|
19969
|
-
resourceGroup =
|
|
20831
|
+
resourceGroup = Option37.String("--resource-group", {
|
|
19970
20832
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
19971
20833
|
});
|
|
19972
20834
|
async executeCommand() {
|
|
@@ -20031,7 +20893,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
20031
20893
|
};
|
|
20032
20894
|
|
|
20033
20895
|
// src/commands/status.ts
|
|
20034
|
-
import { Command as
|
|
20896
|
+
import { Command as Command41, Option as Option38 } from "clipanion";
|
|
20035
20897
|
|
|
20036
20898
|
// src/lib/azd.ts
|
|
20037
20899
|
init_errors();
|
|
@@ -20096,10 +20958,10 @@ async function resolveLocalContext() {
|
|
|
20096
20958
|
// src/commands/status.ts
|
|
20097
20959
|
var StatusCommand = class extends M8tCommand {
|
|
20098
20960
|
static paths = [["status"]];
|
|
20099
|
-
static usage =
|
|
20961
|
+
static usage = Command41.Usage({
|
|
20100
20962
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
20101
20963
|
});
|
|
20102
|
-
output =
|
|
20964
|
+
output = Option38.String("--output");
|
|
20103
20965
|
async executeCommand() {
|
|
20104
20966
|
const mode = resolveOutputMode(
|
|
20105
20967
|
this.output,
|
|
@@ -20137,11 +20999,11 @@ var StatusCommand = class extends M8tCommand {
|
|
|
20137
20999
|
};
|
|
20138
21000
|
|
|
20139
21001
|
// src/commands/doctor.ts
|
|
20140
|
-
import { Command as
|
|
20141
|
-
import { DefaultAzureCredential as
|
|
20142
|
-
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";
|
|
20143
21005
|
import * as os10 from "os";
|
|
20144
|
-
import * as
|
|
21006
|
+
import * as path24 from "path";
|
|
20145
21007
|
|
|
20146
21008
|
// src/lib/doctor-checks.ts
|
|
20147
21009
|
function checkAzSignedIn(az) {
|
|
@@ -20366,34 +21228,34 @@ async function resolveAccountLocation(accountId) {
|
|
|
20366
21228
|
}
|
|
20367
21229
|
}
|
|
20368
21230
|
function probeRepoRoot() {
|
|
20369
|
-
const marker =
|
|
20370
|
-
if (!
|
|
21231
|
+
const marker = path24.join(os10.homedir(), ".m8t-stack", "repo-root");
|
|
21232
|
+
if (!fs23.existsSync(marker)) {
|
|
20371
21233
|
return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
|
|
20372
21234
|
}
|
|
20373
21235
|
let repoPath;
|
|
20374
21236
|
try {
|
|
20375
|
-
repoPath =
|
|
21237
|
+
repoPath = fs23.readFileSync(marker, "utf8").trim();
|
|
20376
21238
|
} catch {
|
|
20377
21239
|
return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
20378
21240
|
}
|
|
20379
21241
|
if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
20380
21242
|
let isDir = false;
|
|
20381
21243
|
try {
|
|
20382
|
-
isDir =
|
|
21244
|
+
isDir = fs23.statSync(repoPath).isDirectory();
|
|
20383
21245
|
} catch {
|
|
20384
21246
|
isDir = false;
|
|
20385
21247
|
}
|
|
20386
|
-
const isGitCheckout = isDir &&
|
|
21248
|
+
const isGitCheckout = isDir && fs23.existsSync(path24.join(repoPath, ".git"));
|
|
20387
21249
|
return { markerExists: true, path: repoPath, isDir, isGitCheckout };
|
|
20388
21250
|
}
|
|
20389
21251
|
var DoctorCommand = class extends M8tCommand {
|
|
20390
21252
|
static paths = [["doctor"]];
|
|
20391
|
-
static usage =
|
|
21253
|
+
static usage = Command42.Usage({
|
|
20392
21254
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
20393
21255
|
});
|
|
20394
|
-
output =
|
|
20395
|
-
agent =
|
|
20396
|
-
resourceGroup =
|
|
21256
|
+
output = Option39.String("--output");
|
|
21257
|
+
agent = Option39.String("--agent");
|
|
21258
|
+
resourceGroup = Option39.String("--resource-group", {
|
|
20397
21259
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
20398
21260
|
});
|
|
20399
21261
|
async executeCommand() {
|
|
@@ -20466,7 +21328,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
20466
21328
|
if (typeof this.agent === "string" && this.agent) {
|
|
20467
21329
|
checking(`delivery grant for ${this.agent}`);
|
|
20468
21330
|
try {
|
|
20469
|
-
const credential2 = new
|
|
21331
|
+
const credential2 = new DefaultAzureCredential19();
|
|
20470
21332
|
const cur = await getAgentVersion({
|
|
20471
21333
|
credential: credential2,
|
|
20472
21334
|
projectEndpoint: foundry.projectEndpoint,
|
|
@@ -20509,18 +21371,18 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
20509
21371
|
};
|
|
20510
21372
|
|
|
20511
21373
|
// src/commands/switch.ts
|
|
20512
|
-
import { Command as
|
|
21374
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
20513
21375
|
|
|
20514
21376
|
// src/lib/profiles.ts
|
|
20515
|
-
import * as
|
|
20516
|
-
import * as
|
|
21377
|
+
import * as fs24 from "fs/promises";
|
|
21378
|
+
import * as path25 from "path";
|
|
20517
21379
|
import { parse as parseYaml11, stringify as stringifyYaml4 } from "yaml";
|
|
20518
21380
|
function profilesDir() {
|
|
20519
21381
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
20520
|
-
return
|
|
21382
|
+
return path25.join(home, ".m8t-stack", "profiles");
|
|
20521
21383
|
}
|
|
20522
21384
|
function getProfilePath(name) {
|
|
20523
|
-
return
|
|
21385
|
+
return path25.join(profilesDir(), `${path25.basename(name)}.yaml`);
|
|
20524
21386
|
}
|
|
20525
21387
|
function slugifyTenant(domainOrId) {
|
|
20526
21388
|
const base = domainOrId.split(".")[0].toLowerCase();
|
|
@@ -20529,7 +21391,7 @@ function slugifyTenant(domainOrId) {
|
|
|
20529
21391
|
}
|
|
20530
21392
|
async function listProfiles() {
|
|
20531
21393
|
try {
|
|
20532
|
-
const entries = await
|
|
21394
|
+
const entries = await fs24.readdir(profilesDir());
|
|
20533
21395
|
return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
|
|
20534
21396
|
} catch (e) {
|
|
20535
21397
|
if (e.code === "ENOENT") return [];
|
|
@@ -20538,7 +21400,7 @@ async function listProfiles() {
|
|
|
20538
21400
|
}
|
|
20539
21401
|
async function readProfile(name) {
|
|
20540
21402
|
try {
|
|
20541
|
-
const raw = await
|
|
21403
|
+
const raw = await fs24.readFile(getProfilePath(name), "utf8");
|
|
20542
21404
|
const parsed = parseYaml11(raw);
|
|
20543
21405
|
if (!parsed || typeof parsed !== "object") return null;
|
|
20544
21406
|
return parsed;
|
|
@@ -20549,14 +21411,14 @@ async function readProfile(name) {
|
|
|
20549
21411
|
}
|
|
20550
21412
|
async function exists(p) {
|
|
20551
21413
|
try {
|
|
20552
|
-
await
|
|
21414
|
+
await fs24.stat(p);
|
|
20553
21415
|
return true;
|
|
20554
21416
|
} catch {
|
|
20555
21417
|
return false;
|
|
20556
21418
|
}
|
|
20557
21419
|
}
|
|
20558
21420
|
async function saveProfile(p) {
|
|
20559
|
-
await
|
|
21421
|
+
await fs24.mkdir(profilesDir(), { recursive: true });
|
|
20560
21422
|
let name = p.name;
|
|
20561
21423
|
let n = 2;
|
|
20562
21424
|
while (await exists(getProfilePath(name))) {
|
|
@@ -20565,8 +21427,8 @@ async function saveProfile(p) {
|
|
|
20565
21427
|
}
|
|
20566
21428
|
const target = getProfilePath(name);
|
|
20567
21429
|
const tmp = `${target}.tmp`;
|
|
20568
|
-
await
|
|
20569
|
-
await
|
|
21430
|
+
await fs24.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
|
|
21431
|
+
await fs24.rename(tmp, target);
|
|
20570
21432
|
return name;
|
|
20571
21433
|
}
|
|
20572
21434
|
|
|
@@ -20649,14 +21511,14 @@ async function profileSwitch(name, asName) {
|
|
|
20649
21511
|
init_errors();
|
|
20650
21512
|
var SwitchCommand = class extends M8tCommand {
|
|
20651
21513
|
static paths = [["switch"]];
|
|
20652
|
-
static usage =
|
|
21514
|
+
static usage = Command43.Usage({
|
|
20653
21515
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
20654
21516
|
});
|
|
20655
|
-
profile =
|
|
20656
|
-
subscription =
|
|
20657
|
-
list =
|
|
20658
|
-
as =
|
|
20659
|
-
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");
|
|
20660
21522
|
async executeCommand() {
|
|
20661
21523
|
const mode = resolveOutputMode(
|
|
20662
21524
|
this.output,
|
|
@@ -20713,7 +21575,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
20713
21575
|
|
|
20714
21576
|
// src/commands/open.ts
|
|
20715
21577
|
import { spawn as spawn5 } from "child_process";
|
|
20716
|
-
import { Command as
|
|
21578
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
20717
21579
|
|
|
20718
21580
|
// src/lib/open-targets.ts
|
|
20719
21581
|
init_errors();
|
|
@@ -20759,14 +21621,14 @@ function openUrl(url) {
|
|
|
20759
21621
|
}
|
|
20760
21622
|
var OpenCommand = class extends M8tCommand {
|
|
20761
21623
|
static paths = [["open"]];
|
|
20762
|
-
static usage =
|
|
21624
|
+
static usage = Command44.Usage({
|
|
20763
21625
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
20764
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)."
|
|
20765
21627
|
});
|
|
20766
|
-
target =
|
|
20767
|
-
print =
|
|
20768
|
-
output =
|
|
20769
|
-
resourceGroup =
|
|
21628
|
+
target = Option41.String({ required: false });
|
|
21629
|
+
print = Option41.Boolean("--print", false);
|
|
21630
|
+
output = Option41.String("--output");
|
|
21631
|
+
resourceGroup = Option41.String("--resource-group", {
|
|
20770
21632
|
description: "m8t-stack resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
20771
21633
|
});
|
|
20772
21634
|
async executeCommand() {
|
|
@@ -20810,9 +21672,9 @@ var OpenCommand = class extends M8tCommand {
|
|
|
20810
21672
|
};
|
|
20811
21673
|
|
|
20812
21674
|
// src/commands/dream/run.ts
|
|
20813
|
-
import { Command as
|
|
21675
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
20814
21676
|
import { AzureCliCredential } from "@azure/identity";
|
|
20815
|
-
import { TableClient as
|
|
21677
|
+
import { TableClient as TableClient5 } from "@azure/data-tables";
|
|
20816
21678
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
20817
21679
|
import { LogsQueryClient as LogsQueryClient3, LogsQueryResultStatus as LogsQueryResultStatus3 } from "@azure/monitor-query";
|
|
20818
21680
|
|
|
@@ -21119,7 +21981,7 @@ var ConvFetchError = class extends Error {
|
|
|
21119
21981
|
};
|
|
21120
21982
|
|
|
21121
21983
|
// ../../packages/brain/engine/dist/esm/cursor.js
|
|
21122
|
-
import { TableClient as
|
|
21984
|
+
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
21123
21985
|
|
|
21124
21986
|
// ../../packages/agent-ledger/dist/esm/row-key.js
|
|
21125
21987
|
var MAX_MS = 864e13;
|
|
@@ -21241,7 +22103,7 @@ async function commitCursorAdvance(plan, cursor) {
|
|
|
21241
22103
|
}
|
|
21242
22104
|
var CURSOR_TABLE_NAME = "BrainDreamCursor";
|
|
21243
22105
|
function makeTableCursor(credential2, tableEndpoint) {
|
|
21244
|
-
const client = new
|
|
22106
|
+
const client = new TableClient4(tableEndpoint, CURSOR_TABLE_NAME, credential2);
|
|
21245
22107
|
return new TableCursor(client);
|
|
21246
22108
|
}
|
|
21247
22109
|
function formatCursorPreview(cursor) {
|
|
@@ -21735,7 +22597,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
21735
22597
|
// ../../packages/brain/engine/dist/esm/dream/writer.js
|
|
21736
22598
|
var API2 = "https://api.github.com";
|
|
21737
22599
|
var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
|
|
21738
|
-
var isAllowedWritePath = (
|
|
22600
|
+
var isAllowedWritePath = (path33, allowedPrefix) => allowedPrefix.test(path33) && !path33.split("/").includes("..");
|
|
21739
22601
|
var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
21740
22602
|
function parseFrontmatter(content) {
|
|
21741
22603
|
const m = /^---\n([\s\S]*?)\n---/.exec(content);
|
|
@@ -21774,8 +22636,12 @@ function parseFrontmatter(content) {
|
|
|
21774
22636
|
function makeGitDataWriter(args) {
|
|
21775
22637
|
const doFetch = args.fetchImpl ?? fetch;
|
|
21776
22638
|
const repoPath = args.repository;
|
|
21777
|
-
|
|
21778
|
-
|
|
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}`, {
|
|
21779
22645
|
method,
|
|
21780
22646
|
headers: {
|
|
21781
22647
|
Authorization: `Bearer ${token}`,
|
|
@@ -21788,8 +22654,8 @@ function makeGitDataWriter(args) {
|
|
|
21788
22654
|
const text = await res.text();
|
|
21789
22655
|
return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
|
|
21790
22656
|
}
|
|
21791
|
-
async function readFileWith(token,
|
|
21792
|
-
const encodedPath =
|
|
22657
|
+
async function readFileWith(token, path33) {
|
|
22658
|
+
const encodedPath = path33.split("/").map(encodeURIComponent).join("/");
|
|
21793
22659
|
const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
|
|
21794
22660
|
if (r.status === 404)
|
|
21795
22661
|
return null;
|
|
@@ -21805,9 +22671,9 @@ function makeGitDataWriter(args) {
|
|
|
21805
22671
|
throw new Error(`fetchTip: HTTP ${String(r.status)}`);
|
|
21806
22672
|
return r.json.object.sha;
|
|
21807
22673
|
},
|
|
21808
|
-
async readFile(
|
|
22674
|
+
async readFile(path33) {
|
|
21809
22675
|
const token = await args.mintToken();
|
|
21810
|
-
return readFileWith(token,
|
|
22676
|
+
return readFileWith(token, path33);
|
|
21811
22677
|
},
|
|
21812
22678
|
async listMemory() {
|
|
21813
22679
|
const token = await args.mintToken();
|
|
@@ -21816,15 +22682,15 @@ function makeGitDataWriter(args) {
|
|
|
21816
22682
|
return [];
|
|
21817
22683
|
const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
|
|
21818
22684
|
const out = [];
|
|
21819
|
-
for (const
|
|
21820
|
-
const content = await readFileWith(token,
|
|
21821
|
-
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) : {} });
|
|
21822
22688
|
}
|
|
21823
22689
|
return out;
|
|
21824
22690
|
},
|
|
21825
22691
|
async commitBatch(a) {
|
|
21826
22692
|
try {
|
|
21827
|
-
const escaping = a.changes.filter((c) => !isAllowedWritePath(c.path));
|
|
22693
|
+
const escaping = a.changes.filter((c) => !isAllowedWritePath(c.path, allowedPathPrefix));
|
|
21828
22694
|
if (escaping.length > 0) {
|
|
21829
22695
|
return {
|
|
21830
22696
|
ok: false,
|
|
@@ -21876,9 +22742,11 @@ function makeFoundryModelClient(openai, model) {
|
|
|
21876
22742
|
const create = openai.responses.create;
|
|
21877
22743
|
const stream = await create({
|
|
21878
22744
|
model,
|
|
22745
|
+
// Explicit `type: "message"` — the Responses API rejects untyped input
|
|
22746
|
+
// items (400 `Invalid value: ''`).
|
|
21879
22747
|
input: [
|
|
21880
|
-
{ role: "system", content: system },
|
|
21881
|
-
{ role: "user", content: user }
|
|
22748
|
+
{ type: "message", role: "system", content: system },
|
|
22749
|
+
{ type: "message", role: "user", content: user }
|
|
21882
22750
|
],
|
|
21883
22751
|
stream: true
|
|
21884
22752
|
}, signal ? { signal } : void 0);
|
|
@@ -22255,7 +23123,7 @@ async function loadMemoryContext(brain) {
|
|
|
22255
23123
|
return {
|
|
22256
23124
|
files,
|
|
22257
23125
|
indexEntries,
|
|
22258
|
-
originOf: (
|
|
23126
|
+
originOf: (path33) => originByPath.get(path33) ?? "worker"
|
|
22259
23127
|
};
|
|
22260
23128
|
}
|
|
22261
23129
|
|
|
@@ -22276,14 +23144,14 @@ function checkEvidenceMembership(delta, harvested) {
|
|
|
22276
23144
|
}
|
|
22277
23145
|
var BatchAbort = class extends Error {
|
|
22278
23146
|
path;
|
|
22279
|
-
constructor(
|
|
23147
|
+
constructor(path33, message) {
|
|
22280
23148
|
super(message);
|
|
22281
|
-
this.path =
|
|
23149
|
+
this.path = path33;
|
|
22282
23150
|
this.name = "BatchAbort";
|
|
22283
23151
|
}
|
|
22284
23152
|
};
|
|
22285
|
-
var isIndexFile = (
|
|
22286
|
-
var isMemoryIndex = (
|
|
23153
|
+
var isIndexFile = (path33) => /(^|\/)[^/]*_index\.md$/.test(path33);
|
|
23154
|
+
var isMemoryIndex = (path33) => path33 === "memory/MEMORY.md";
|
|
22287
23155
|
function targetPath(delta) {
|
|
22288
23156
|
switch (delta.verb) {
|
|
22289
23157
|
case "new":
|
|
@@ -22300,29 +23168,29 @@ function targetPath(delta) {
|
|
|
22300
23168
|
}
|
|
22301
23169
|
async function checkOriginTier(delta, lookup) {
|
|
22302
23170
|
const evidence = evidenceOf(delta) ?? [];
|
|
22303
|
-
const
|
|
22304
|
-
if (
|
|
23171
|
+
const path33 = targetPath(delta);
|
|
23172
|
+
if (path33 === null)
|
|
22305
23173
|
return { ok: true };
|
|
22306
|
-
if (isIndexFile(
|
|
22307
|
-
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 } };
|
|
22308
23176
|
}
|
|
22309
|
-
const target = await lookup(
|
|
22310
|
-
const isStructural =
|
|
23177
|
+
const target = await lookup(path33);
|
|
23178
|
+
const isStructural = path33.startsWith("references/");
|
|
22311
23179
|
const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
|
|
22312
23180
|
const editable = origin === "worker" || origin === "dream";
|
|
22313
23181
|
if (editable)
|
|
22314
23182
|
return { ok: true };
|
|
22315
23183
|
const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
|
|
22316
23184
|
if (removesOperatorTarget) {
|
|
22317
|
-
throw new BatchAbort(
|
|
23185
|
+
throw new BatchAbort(path33, `supersede/retract OLD target ${path33} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
|
|
22318
23186
|
}
|
|
22319
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
23187
|
+
return { ok: false, flagged: { kind: "flagged", path: path33, tension: `operator-origin (${origin}); flag-only`, evidence } };
|
|
22320
23188
|
}
|
|
22321
23189
|
var ALLOWED_MEMORY = /^memory\/.+\.md$/;
|
|
22322
23190
|
var ALLOWED_INBOX = /^inbox\//;
|
|
22323
23191
|
var ALLOWED_QUARANTINE = /^quarantine\//;
|
|
22324
23192
|
var ALLOWED_FLAG = /^memory\//;
|
|
22325
|
-
var hasTraversal = (
|
|
23193
|
+
var hasTraversal = (path33) => path33.split("/").includes("..");
|
|
22326
23194
|
function checkPathAllowed(delta) {
|
|
22327
23195
|
const evidence = evidenceOf(delta) ?? [];
|
|
22328
23196
|
const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
|
|
@@ -22429,7 +23297,7 @@ ${inject}
|
|
|
22429
23297
|
---
|
|
22430
23298
|
`);
|
|
22431
23299
|
}
|
|
22432
|
-
var indexLine = (
|
|
23300
|
+
var indexLine = (path33, title, date, tags) => `- \`${path33}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
|
|
22433
23301
|
function splitIndex(index) {
|
|
22434
23302
|
const all = index.split("\n");
|
|
22435
23303
|
const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
|
|
@@ -22602,16 +23470,16 @@ function defaultDreamSeams(opts = {}) {
|
|
|
22602
23470
|
const mem = await memory(deps.brain);
|
|
22603
23471
|
const harvested = harvestedIds(input);
|
|
22604
23472
|
const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
|
|
22605
|
-
const lookup = (
|
|
23473
|
+
const lookup = (path33) => Promise.resolve(mem.files.find((f) => f.path === path33) ?? null);
|
|
22606
23474
|
const digest = [...pendingRejected];
|
|
22607
23475
|
let changes = [];
|
|
22608
23476
|
let indexEdit;
|
|
22609
23477
|
try {
|
|
22610
23478
|
for (const raw of deltas) {
|
|
22611
23479
|
const delta = forcePolicyQuarantine(raw);
|
|
22612
|
-
const
|
|
22613
|
-
if (!
|
|
22614
|
-
digest.push(
|
|
23480
|
+
const path33 = checkPathAllowed(delta);
|
|
23481
|
+
if (!path33.ok) {
|
|
23482
|
+
digest.push(path33.rejected);
|
|
22615
23483
|
continue;
|
|
22616
23484
|
}
|
|
22617
23485
|
const ev = checkEvidenceMembership(delta, harvested);
|
|
@@ -22954,20 +23822,20 @@ function redactTranscripts(input) {
|
|
|
22954
23822
|
}
|
|
22955
23823
|
var DreamRunCommand = class extends M8tCommand {
|
|
22956
23824
|
static paths = [["dream", "run"]];
|
|
22957
|
-
static usage =
|
|
23825
|
+
static usage = Command45.Usage({
|
|
22958
23826
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes).",
|
|
22959
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."
|
|
22960
23828
|
});
|
|
22961
|
-
worker =
|
|
22962
|
-
dryRun =
|
|
22963
|
-
since =
|
|
22964
|
-
reset =
|
|
22965
|
-
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, {
|
|
22966
23834
|
description: "Print transcript bodies (default: metadata only)."
|
|
22967
23835
|
});
|
|
22968
|
-
subscription =
|
|
22969
|
-
endpoint =
|
|
22970
|
-
output =
|
|
23836
|
+
subscription = Option42.String("--subscription");
|
|
23837
|
+
endpoint = Option42.String("--endpoint");
|
|
23838
|
+
output = Option42.String("--output");
|
|
22971
23839
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
22972
23840
|
deps;
|
|
22973
23841
|
async executeCommand() {
|
|
@@ -23162,7 +24030,7 @@ AppDependencies
|
|
|
23162
24030
|
};
|
|
23163
24031
|
}
|
|
23164
24032
|
function buildLiveSources(context, credential2) {
|
|
23165
|
-
const ledgerClient = new
|
|
24033
|
+
const ledgerClient = new TableClient5(context.ledgerTableEndpoint, LEDGER_TABLE_NAME, credential2);
|
|
23166
24034
|
const ledger = makeLedgerSource(
|
|
23167
24035
|
async (pk, sinceTs) => fetchLedgerRows(
|
|
23168
24036
|
{ workers: [pk], source: "all", from: sinceTs, to: "9999-12-31T23:59:59.999Z" },
|
|
@@ -23315,7 +24183,7 @@ function defaultDeps(overrides) {
|
|
|
23315
24183
|
}
|
|
23316
24184
|
|
|
23317
24185
|
// src/commands/foundry/create.ts
|
|
23318
|
-
import { Command as
|
|
24186
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
23319
24187
|
|
|
23320
24188
|
// src/lib/foundry-create.ts
|
|
23321
24189
|
init_errors();
|
|
@@ -23553,7 +24421,7 @@ async function createFoundryProject(args) {
|
|
|
23553
24421
|
init_errors();
|
|
23554
24422
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
23555
24423
|
static paths = [["foundry", "create"]];
|
|
23556
|
-
static usage =
|
|
24424
|
+
static usage = Command46.Usage({
|
|
23557
24425
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
23558
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).",
|
|
23559
24427
|
examples: [
|
|
@@ -23562,16 +24430,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
23562
24430
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
23563
24431
|
]
|
|
23564
24432
|
});
|
|
23565
|
-
resourceGroup =
|
|
23566
|
-
location =
|
|
23567
|
-
account =
|
|
23568
|
-
project =
|
|
23569
|
-
model =
|
|
23570
|
-
modelVersion =
|
|
23571
|
-
capacity =
|
|
23572
|
-
subscription =
|
|
23573
|
-
skipQuotaCheck =
|
|
23574
|
-
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");
|
|
23575
24443
|
async executeCommand() {
|
|
23576
24444
|
const mode = resolveOutputMode(
|
|
23577
24445
|
this.output,
|
|
@@ -23643,22 +24511,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
23643
24511
|
};
|
|
23644
24512
|
|
|
23645
24513
|
// src/commands/foundry/await-ready.ts
|
|
23646
|
-
import { Command as
|
|
24514
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
23647
24515
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
23648
24516
|
init_errors();
|
|
23649
24517
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
23650
24518
|
static paths = [["foundry", "await-ready"]];
|
|
23651
|
-
static usage =
|
|
24519
|
+
static usage = Command47.Usage({
|
|
23652
24520
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
23653
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.",
|
|
23654
24522
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
23655
24523
|
});
|
|
23656
|
-
endpoint =
|
|
23657
|
-
consecutive =
|
|
23658
|
-
attempts =
|
|
23659
|
-
interval =
|
|
23660
|
-
subscription =
|
|
23661
|
-
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");
|
|
23662
24530
|
async executeCommand() {
|
|
23663
24531
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23664
24532
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -23692,7 +24560,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
23692
24560
|
};
|
|
23693
24561
|
|
|
23694
24562
|
// src/commands/bootstrap/preflight.ts
|
|
23695
|
-
import { Command as
|
|
24563
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
23696
24564
|
init_errors();
|
|
23697
24565
|
|
|
23698
24566
|
// src/lib/bootstrap-preflight.ts
|
|
@@ -23766,7 +24634,7 @@ function buildPreflightBanner(who) {
|
|
|
23766
24634
|
// src/commands/bootstrap/preflight.ts
|
|
23767
24635
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
23768
24636
|
static paths = [["bootstrap", "preflight"]];
|
|
23769
|
-
static usage =
|
|
24637
|
+
static usage = Command48.Usage({
|
|
23770
24638
|
description: "Loudly verify you can install m8t-stack (Owner/UAA + directory admin) and hard-stop if not.",
|
|
23771
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.",
|
|
23772
24640
|
examples: [
|
|
@@ -23774,8 +24642,8 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
23774
24642
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
23775
24643
|
]
|
|
23776
24644
|
});
|
|
23777
|
-
clientId =
|
|
23778
|
-
subscription =
|
|
24645
|
+
clientId = Option45.String("--client-id");
|
|
24646
|
+
subscription = Option45.String("--subscription");
|
|
23779
24647
|
async executeCommand() {
|
|
23780
24648
|
const clientId = typeof this.clientId === "string" ? this.clientId : void 0;
|
|
23781
24649
|
const account = await getAzAccount();
|
|
@@ -23831,10 +24699,10 @@ ${colors.error(" " + why)}
|
|
|
23831
24699
|
}
|
|
23832
24700
|
|
|
23833
24701
|
// src/commands/bootstrap/launch.ts
|
|
23834
|
-
import * as
|
|
24702
|
+
import * as fs27 from "fs";
|
|
23835
24703
|
import * as os13 from "os";
|
|
23836
|
-
import * as
|
|
23837
|
-
import { Command as
|
|
24704
|
+
import * as path28 from "path";
|
|
24705
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
23838
24706
|
init_errors();
|
|
23839
24707
|
|
|
23840
24708
|
// src/lib/bootstrap-mi.ts
|
|
@@ -23979,9 +24847,9 @@ async function kickInstaller(s) {
|
|
|
23979
24847
|
|
|
23980
24848
|
// src/lib/bootstrap-status.ts
|
|
23981
24849
|
import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
|
|
23982
|
-
import * as
|
|
24850
|
+
import * as fs25 from "fs/promises";
|
|
23983
24851
|
import * as os11 from "os";
|
|
23984
|
-
import * as
|
|
24852
|
+
import * as path26 from "path";
|
|
23985
24853
|
init_errors();
|
|
23986
24854
|
var STATUS_CONTAINER = "status";
|
|
23987
24855
|
var STATUS_BLOB = "status.json";
|
|
@@ -24020,7 +24888,7 @@ async function readStatusBlob(opts) {
|
|
|
24020
24888
|
cause: e
|
|
24021
24889
|
});
|
|
24022
24890
|
}
|
|
24023
|
-
const tmpFile =
|
|
24891
|
+
const tmpFile = path26.join(os11.tmpdir(), `m8t-status-${randomUUID2()}.json`);
|
|
24024
24892
|
try {
|
|
24025
24893
|
await runAz([
|
|
24026
24894
|
"storage",
|
|
@@ -24039,7 +24907,7 @@ async function readStatusBlob(opts) {
|
|
|
24039
24907
|
"--no-progress",
|
|
24040
24908
|
"--only-show-errors"
|
|
24041
24909
|
]);
|
|
24042
|
-
const raw = await
|
|
24910
|
+
const raw = await fs25.readFile(tmpFile, "utf8");
|
|
24043
24911
|
return JSON.parse(raw.trim());
|
|
24044
24912
|
} catch (e) {
|
|
24045
24913
|
throw new LocalCliError({
|
|
@@ -24049,23 +24917,23 @@ async function readStatusBlob(opts) {
|
|
|
24049
24917
|
cause: e
|
|
24050
24918
|
});
|
|
24051
24919
|
} finally {
|
|
24052
|
-
await
|
|
24920
|
+
await fs25.rm(tmpFile, { force: true });
|
|
24053
24921
|
}
|
|
24054
24922
|
}
|
|
24055
24923
|
|
|
24056
24924
|
// src/lib/bootstrap-state.ts
|
|
24057
|
-
import * as
|
|
24925
|
+
import * as fs26 from "fs/promises";
|
|
24058
24926
|
import * as os12 from "os";
|
|
24059
|
-
import * as
|
|
24927
|
+
import * as path27 from "path";
|
|
24060
24928
|
function stateDir(home) {
|
|
24061
|
-
return
|
|
24929
|
+
return path27.join(home, ".m8t-stack");
|
|
24062
24930
|
}
|
|
24063
24931
|
function statePath(home) {
|
|
24064
|
-
return
|
|
24932
|
+
return path27.join(stateDir(home), "bootstrap.json");
|
|
24065
24933
|
}
|
|
24066
24934
|
async function readBootstrapState(home = os12.homedir()) {
|
|
24067
24935
|
try {
|
|
24068
|
-
const raw = await
|
|
24936
|
+
const raw = await fs26.readFile(statePath(home), "utf8");
|
|
24069
24937
|
return JSON.parse(raw);
|
|
24070
24938
|
} catch (e) {
|
|
24071
24939
|
if (e.code === "ENOENT") return null;
|
|
@@ -24073,10 +24941,10 @@ async function readBootstrapState(home = os12.homedir()) {
|
|
|
24073
24941
|
}
|
|
24074
24942
|
}
|
|
24075
24943
|
async function writeBootstrapState(state, home = os12.homedir()) {
|
|
24076
|
-
await
|
|
24944
|
+
await fs26.mkdir(stateDir(home), { recursive: true });
|
|
24077
24945
|
const tmp = `${statePath(home)}.tmp`;
|
|
24078
|
-
await
|
|
24079
|
-
await
|
|
24946
|
+
await fs26.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
24947
|
+
await fs26.rename(tmp, statePath(home));
|
|
24080
24948
|
}
|
|
24081
24949
|
|
|
24082
24950
|
// src/commands/bootstrap/launch.ts
|
|
@@ -24087,7 +24955,7 @@ var ACI_NAME = "m8t-installer";
|
|
|
24087
24955
|
var MI_NAME = "m8t-installer-mi";
|
|
24088
24956
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
24089
24957
|
static paths = [["bootstrap", "launch"]];
|
|
24090
|
-
static usage =
|
|
24958
|
+
static usage = Command49.Usage({
|
|
24091
24959
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
24092
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`.",
|
|
24093
24961
|
examples: [
|
|
@@ -24096,13 +24964,13 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24096
24964
|
["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.0"]
|
|
24097
24965
|
]
|
|
24098
24966
|
});
|
|
24099
|
-
location =
|
|
24100
|
-
resourceGroup =
|
|
24101
|
-
clientId =
|
|
24102
|
-
subscription =
|
|
24103
|
-
installerTag =
|
|
24104
|
-
gatewayImageRef =
|
|
24105
|
-
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");
|
|
24106
24974
|
async executeCommand() {
|
|
24107
24975
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
24108
24976
|
if (!location) {
|
|
@@ -24116,12 +24984,12 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24116
24984
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
24117
24985
|
const out = (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
24118
24986
|
`);
|
|
24119
|
-
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");
|
|
24120
24988
|
let githubApp;
|
|
24121
|
-
if (
|
|
24989
|
+
if (fs27.existsSync(credsPath)) {
|
|
24122
24990
|
let parsed;
|
|
24123
24991
|
try {
|
|
24124
|
-
parsed = JSON.parse(
|
|
24992
|
+
parsed = JSON.parse(fs27.readFileSync(credsPath, "utf8"));
|
|
24125
24993
|
} catch {
|
|
24126
24994
|
throw new LocalCliError({
|
|
24127
24995
|
code: "GITHUB_APP_CREDS_INVALID",
|
|
@@ -24131,7 +24999,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24131
24999
|
}
|
|
24132
25000
|
let pem;
|
|
24133
25001
|
try {
|
|
24134
|
-
pem =
|
|
25002
|
+
pem = fs27.readFileSync(parsed.pemPath, "utf8");
|
|
24135
25003
|
} catch {
|
|
24136
25004
|
throw new LocalCliError({
|
|
24137
25005
|
code: "GITHUB_APP_PEM_MISSING",
|
|
@@ -24203,7 +25071,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
24203
25071
|
};
|
|
24204
25072
|
|
|
24205
25073
|
// src/commands/bootstrap/status.ts
|
|
24206
|
-
import { Command as
|
|
25074
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
24207
25075
|
init_errors();
|
|
24208
25076
|
|
|
24209
25077
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -24231,13 +25099,13 @@ async function getAciState(opts) {
|
|
|
24231
25099
|
// src/commands/bootstrap/status.ts
|
|
24232
25100
|
var BootstrapStatusCommand = class extends M8tCommand {
|
|
24233
25101
|
static paths = [["bootstrap", "status"]];
|
|
24234
|
-
static usage =
|
|
25102
|
+
static usage = Command50.Usage({
|
|
24235
25103
|
description: "Show the cloud installer's live status (phase, progress, result).",
|
|
24236
25104
|
details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.",
|
|
24237
25105
|
examples: [["One read", "$0 bootstrap status"], ["Watch to completion", "$0 bootstrap status --watch"]]
|
|
24238
25106
|
});
|
|
24239
|
-
watch =
|
|
24240
|
-
output =
|
|
25107
|
+
watch = Option47.Boolean("--watch", false);
|
|
25108
|
+
output = Option47.String("--output");
|
|
24241
25109
|
async executeCommand() {
|
|
24242
25110
|
const state = await readBootstrapState();
|
|
24243
25111
|
if (!state) {
|
|
@@ -24308,7 +25176,7 @@ function formatStatus(d) {
|
|
|
24308
25176
|
}
|
|
24309
25177
|
|
|
24310
25178
|
// src/commands/bootstrap/reap.ts
|
|
24311
|
-
import { Command as
|
|
25179
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
24312
25180
|
init_errors();
|
|
24313
25181
|
|
|
24314
25182
|
// src/lib/bootstrap-reap.ts
|
|
@@ -24384,14 +25252,14 @@ async function reapInstaller(opts) {
|
|
|
24384
25252
|
// src/commands/bootstrap/reap.ts
|
|
24385
25253
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
24386
25254
|
static paths = [["bootstrap", "reap"]];
|
|
24387
|
-
static usage =
|
|
25255
|
+
static usage = Command51.Usage({
|
|
24388
25256
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
24389
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.",
|
|
24390
25258
|
examples: [["Reap after done", "$0 bootstrap reap"]]
|
|
24391
25259
|
});
|
|
24392
|
-
force =
|
|
24393
|
-
sweepOrphans =
|
|
24394
|
-
yes =
|
|
25260
|
+
force = Option48.Boolean("--force", false);
|
|
25261
|
+
sweepOrphans = Option48.Boolean("--sweep-orphans", false);
|
|
25262
|
+
yes = Option48.Boolean("--yes", false);
|
|
24395
25263
|
async executeCommand() {
|
|
24396
25264
|
if (this.sweepOrphans === true) {
|
|
24397
25265
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -24456,17 +25324,17 @@ Found ${String(found.length)} orphaned Owner@sub assignment(s) (dry-run). ${colo
|
|
|
24456
25324
|
};
|
|
24457
25325
|
|
|
24458
25326
|
// src/commands/bootstrap/finish.ts
|
|
24459
|
-
import * as
|
|
25327
|
+
import * as fs28 from "fs/promises";
|
|
24460
25328
|
import * as os15 from "os";
|
|
24461
|
-
import * as
|
|
24462
|
-
import { Command as
|
|
25329
|
+
import * as path30 from "path";
|
|
25330
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
24463
25331
|
init_errors();
|
|
24464
25332
|
|
|
24465
25333
|
// src/lib/company-profile-seed.ts
|
|
24466
25334
|
import { spawn as spawn6 } from "child_process";
|
|
24467
|
-
import { closeSync, openSync, readFileSync as
|
|
25335
|
+
import { closeSync, openSync, readFileSync as readFileSync19 } from "fs";
|
|
24468
25336
|
import * as os14 from "os";
|
|
24469
|
-
import * as
|
|
25337
|
+
import * as path29 from "path";
|
|
24470
25338
|
init_errors();
|
|
24471
25339
|
|
|
24472
25340
|
// src/lib/onboarding-profile.ts
|
|
@@ -24647,9 +25515,9 @@ function composeFounderIdentityNote(id) {
|
|
|
24647
25515
|
|
|
24648
25516
|
// src/lib/company-profile-seed.ts
|
|
24649
25517
|
function readGithubAppCreds(credsPath) {
|
|
24650
|
-
const p = credsPath ??
|
|
25518
|
+
const p = credsPath ?? path29.join(os14.homedir(), ".m8t-stack", "github-app.json");
|
|
24651
25519
|
try {
|
|
24652
|
-
return JSON.parse(
|
|
25520
|
+
return JSON.parse(readFileSync19(p, "utf8"));
|
|
24653
25521
|
} catch {
|
|
24654
25522
|
return null;
|
|
24655
25523
|
}
|
|
@@ -24677,7 +25545,7 @@ async function resolveSeedContext(opts) {
|
|
|
24677
25545
|
async function applyProfileToBrain(args) {
|
|
24678
25546
|
const token = await mintInstallationTokenFromPem({
|
|
24679
25547
|
appId: args.appCreds.appId,
|
|
24680
|
-
privateKeyPem:
|
|
25548
|
+
privateKeyPem: readFileSync19(args.appCreds.pemPath, "utf8"),
|
|
24681
25549
|
installationId: args.appCreds.installationId,
|
|
24682
25550
|
fetchImpl: args.fetchImpl
|
|
24683
25551
|
});
|
|
@@ -24712,7 +25580,7 @@ async function applyProfileToBrain(args) {
|
|
|
24712
25580
|
});
|
|
24713
25581
|
}
|
|
24714
25582
|
function spawnDetachedSeedWatch() {
|
|
24715
|
-
const logPath =
|
|
25583
|
+
const logPath = path29.join(os14.homedir(), ".m8t-stack", "seed-profile.log");
|
|
24716
25584
|
const fd = openSync(logPath, "a");
|
|
24717
25585
|
try {
|
|
24718
25586
|
const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
|
|
@@ -24789,14 +25657,14 @@ function renderInstallSummary(args) {
|
|
|
24789
25657
|
// src/commands/bootstrap/finish.ts
|
|
24790
25658
|
var BootstrapFinishCommand = class extends M8tCommand {
|
|
24791
25659
|
static paths = [["bootstrap", "finish"]];
|
|
24792
|
-
static usage =
|
|
25660
|
+
static usage = Command52.Usage({
|
|
24793
25661
|
description: "Point your local tools at the now-live platform (repo-root marker, discovery cache, next steps).",
|
|
24794
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.",
|
|
24795
25663
|
examples: [["Finish local", "$0 bootstrap finish --repo-root /path/to/m8t"]]
|
|
24796
25664
|
});
|
|
24797
|
-
repoRoot =
|
|
24798
|
-
subscription =
|
|
24799
|
-
resourceGroup =
|
|
25665
|
+
repoRoot = Option49.String("--repo-root");
|
|
25666
|
+
subscription = Option49.String("--subscription");
|
|
25667
|
+
resourceGroup = Option49.String("--resource-group");
|
|
24800
25668
|
async executeCommand() {
|
|
24801
25669
|
const state = await readBootstrapState();
|
|
24802
25670
|
if (!state) {
|
|
@@ -24811,9 +25679,9 @@ var BootstrapFinishCommand = class extends M8tCommand {
|
|
|
24811
25679
|
});
|
|
24812
25680
|
}
|
|
24813
25681
|
const repoRoot = (typeof this.repoRoot === "string" ? this.repoRoot : void 0) ?? process.cwd();
|
|
24814
|
-
const markerDir =
|
|
24815
|
-
await
|
|
24816
|
-
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}
|
|
24817
25685
|
`, "utf8");
|
|
24818
25686
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? state.subscriptionId;
|
|
24819
25687
|
const resourceGroup = (typeof this.resourceGroup === "string" ? this.resourceGroup : void 0) ?? state.resourceGroup;
|
|
@@ -24849,7 +25717,7 @@ var BootstrapFinishCommand = class extends M8tCommand {
|
|
|
24849
25717
|
}
|
|
24850
25718
|
let brainOrg = null;
|
|
24851
25719
|
try {
|
|
24852
|
-
const credsRaw = await
|
|
25720
|
+
const credsRaw = await fs28.readFile(path30.join(markerDir, "github-app.json"), "utf8");
|
|
24853
25721
|
const creds = JSON.parse(credsRaw);
|
|
24854
25722
|
brainOrg = typeof creds.org === "string" ? creds.org : null;
|
|
24855
25723
|
} catch {
|
|
@@ -24880,18 +25748,18 @@ ${colors.field("Then open a brand new chat/session")} \u2014 new skills + MCP se
|
|
|
24880
25748
|
};
|
|
24881
25749
|
|
|
24882
25750
|
// src/commands/bootstrap/ui.ts
|
|
24883
|
-
import * as
|
|
25751
|
+
import * as fs30 from "fs";
|
|
24884
25752
|
import * as os17 from "os";
|
|
24885
|
-
import * as
|
|
24886
|
-
import { Command as
|
|
24887
|
-
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";
|
|
24888
25756
|
init_errors();
|
|
24889
25757
|
|
|
24890
25758
|
// src/lib/bootstrap-ui.ts
|
|
24891
|
-
import * as
|
|
25759
|
+
import * as fs29 from "fs";
|
|
24892
25760
|
import * as net from "net";
|
|
24893
25761
|
import * as os16 from "os";
|
|
24894
|
-
import * as
|
|
25762
|
+
import * as path31 from "path";
|
|
24895
25763
|
import { spawn as spawn7 } from "child_process";
|
|
24896
25764
|
init_errors();
|
|
24897
25765
|
init_rbac();
|
|
@@ -24958,9 +25826,9 @@ async function ensureFounderFoundryRole(args) {
|
|
|
24958
25826
|
});
|
|
24959
25827
|
}
|
|
24960
25828
|
function writeWebEnvLocal(args) {
|
|
24961
|
-
const envPath =
|
|
24962
|
-
if (
|
|
24963
|
-
|
|
25829
|
+
const envPath = path31.join(args.repoRoot, "apps", "web", ".env.local");
|
|
25830
|
+
if (fs29.existsSync(envPath)) {
|
|
25831
|
+
fs29.copyFileSync(envPath, envPath + ".bak");
|
|
24964
25832
|
}
|
|
24965
25833
|
const body = [
|
|
24966
25834
|
"# Written by `m8t bootstrap ui` \u2014 onboarding chat-only run (Simple Stacey).",
|
|
@@ -24974,7 +25842,7 @@ function writeWebEnvLocal(args) {
|
|
|
24974
25842
|
"NEXT_PUBLIC_M8T_ONBOARDING=1",
|
|
24975
25843
|
""
|
|
24976
25844
|
].join("\n");
|
|
24977
|
-
|
|
25845
|
+
fs29.writeFileSync(envPath, body, "utf8");
|
|
24978
25846
|
return envPath;
|
|
24979
25847
|
}
|
|
24980
25848
|
function assertNodeVersion(versionString = process.version) {
|
|
@@ -25016,10 +25884,10 @@ async function installWebDeps(repoRoot) {
|
|
|
25016
25884
|
await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
|
|
25017
25885
|
}
|
|
25018
25886
|
function onboardingUiPaths(home = os16.homedir()) {
|
|
25019
|
-
const dir =
|
|
25887
|
+
const dir = path31.join(home, ".m8t-stack");
|
|
25020
25888
|
return {
|
|
25021
|
-
logPath:
|
|
25022
|
-
pidPath:
|
|
25889
|
+
logPath: path31.join(dir, "onboarding-ui.log"),
|
|
25890
|
+
pidPath: path31.join(dir, "onboarding-ui.pid")
|
|
25023
25891
|
};
|
|
25024
25892
|
}
|
|
25025
25893
|
async function serveOnboardingUiDetached(args) {
|
|
@@ -25043,8 +25911,8 @@ async function serveOnboardingUiDetached(args) {
|
|
|
25043
25911
|
if (await isPortOpen()) {
|
|
25044
25912
|
return { alreadyRunning: true, logPath };
|
|
25045
25913
|
}
|
|
25046
|
-
|
|
25047
|
-
const fd =
|
|
25914
|
+
fs29.mkdirSync(path31.dirname(logPath), { recursive: true });
|
|
25915
|
+
const fd = fs29.openSync(logPath, "a");
|
|
25048
25916
|
const child = spawn7("pnpm", ["--filter", "web", "dev"], {
|
|
25049
25917
|
cwd: args.repoRoot,
|
|
25050
25918
|
detached: true,
|
|
@@ -25052,7 +25920,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
25052
25920
|
env: { ...process.env, PORT: args.port },
|
|
25053
25921
|
shell: false
|
|
25054
25922
|
});
|
|
25055
|
-
|
|
25923
|
+
fs29.closeSync(fd);
|
|
25056
25924
|
if (child.pid == null) {
|
|
25057
25925
|
throw new LocalCliError({
|
|
25058
25926
|
code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
|
|
@@ -25061,7 +25929,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
25061
25929
|
});
|
|
25062
25930
|
}
|
|
25063
25931
|
child.unref();
|
|
25064
|
-
|
|
25932
|
+
fs29.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
25065
25933
|
const deadline = Date.now() + 45e3;
|
|
25066
25934
|
const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
25067
25935
|
while (Date.now() < deadline) {
|
|
@@ -25084,7 +25952,7 @@ function stopOnboardingUi(home = os16.homedir()) {
|
|
|
25084
25952
|
const { pidPath } = onboardingUiPaths(home);
|
|
25085
25953
|
let pidStr;
|
|
25086
25954
|
try {
|
|
25087
|
-
pidStr =
|
|
25955
|
+
pidStr = fs29.readFileSync(pidPath, "utf8").trim();
|
|
25088
25956
|
} catch {
|
|
25089
25957
|
return false;
|
|
25090
25958
|
}
|
|
@@ -25097,7 +25965,7 @@ function stopOnboardingUi(home = os16.homedir()) {
|
|
|
25097
25965
|
}
|
|
25098
25966
|
}
|
|
25099
25967
|
try {
|
|
25100
|
-
|
|
25968
|
+
fs29.unlinkSync(pidPath);
|
|
25101
25969
|
} catch {
|
|
25102
25970
|
}
|
|
25103
25971
|
return true;
|
|
@@ -25154,7 +26022,7 @@ async function deploySimpleStacey(args) {
|
|
|
25154
26022
|
// src/commands/bootstrap/ui.ts
|
|
25155
26023
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
25156
26024
|
static paths = [["bootstrap", "ui"]];
|
|
25157
|
-
static usage =
|
|
26025
|
+
static usage = Command53.Usage({
|
|
25158
26026
|
description: "Deploy Simple Stacey + start the local onboarding chat UI in the background (returns immediately).",
|
|
25159
26027
|
details: [
|
|
25160
26028
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -25172,15 +26040,15 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
25172
26040
|
["Foreground (blocking, old behaviour)", "$0 bootstrap ui --repo-root /path/to/m8t --foreground"]
|
|
25173
26041
|
]
|
|
25174
26042
|
});
|
|
25175
|
-
repoRoot =
|
|
25176
|
-
port =
|
|
25177
|
-
endpoint =
|
|
26043
|
+
repoRoot = Option50.String("--repo-root");
|
|
26044
|
+
port = Option50.String("--port", "3000");
|
|
26045
|
+
endpoint = Option50.String("--endpoint", {
|
|
25178
26046
|
description: "Foundry project endpoint to target \u2014 disambiguates when the subscription has multiple projects."
|
|
25179
26047
|
});
|
|
25180
|
-
prepOnly =
|
|
25181
|
-
skipInstall =
|
|
25182
|
-
stop =
|
|
25183
|
-
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);
|
|
25184
26052
|
async executeCommand() {
|
|
25185
26053
|
if (this.stop === true) {
|
|
25186
26054
|
const stopped = stopOnboardingUi();
|
|
@@ -25213,10 +26081,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
25213
26081
|
this.context.stderr.write(` ${colors.dim(m)}
|
|
25214
26082
|
`);
|
|
25215
26083
|
};
|
|
25216
|
-
if (
|
|
26084
|
+
if (fs30.existsSync(path32.join(os17.homedir(), ".m8t-stack", "config.yaml"))) {
|
|
25217
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."));
|
|
25218
26086
|
}
|
|
25219
|
-
const credential2 = new
|
|
26087
|
+
const credential2 = new DefaultAzureCredential20();
|
|
25220
26088
|
const account = await getAzAccount();
|
|
25221
26089
|
assertNodeVersion();
|
|
25222
26090
|
out("waiting for Foundry (the installer's foundry-create phase)\u2026");
|
|
@@ -25287,10 +26155,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
25287
26155
|
};
|
|
25288
26156
|
|
|
25289
26157
|
// src/commands/bootstrap/seed-profile.ts
|
|
25290
|
-
import { Command as
|
|
26158
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
25291
26159
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
25292
26160
|
static paths = [["bootstrap", "seed-profile"]];
|
|
25293
|
-
static usage =
|
|
26161
|
+
static usage = Command54.Usage({
|
|
25294
26162
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding questionnaire.",
|
|
25295
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.",
|
|
25296
26164
|
examples: [
|
|
@@ -25298,11 +26166,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
25298
26166
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
25299
26167
|
]
|
|
25300
26168
|
});
|
|
25301
|
-
endpoint =
|
|
25302
|
-
brain =
|
|
25303
|
-
watch =
|
|
25304
|
-
timeout =
|
|
25305
|
-
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");
|
|
25306
26174
|
async executeCommand() {
|
|
25307
26175
|
const ctx = await resolveSeedContext({
|
|
25308
26176
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -25387,7 +26255,9 @@ cli.register(CoderTeardownCommand);
|
|
|
25387
26255
|
cli.register(AzureExecDeployCommand);
|
|
25388
26256
|
cli.register(PlatformStatusCommand);
|
|
25389
26257
|
cli.register(PlatformUpdateCommand);
|
|
26258
|
+
cli.register(PlatformConvergeCommand);
|
|
25390
26259
|
cli.register(PlatformEnableCostReportCommand);
|
|
26260
|
+
cli.register(PlatformEnableAutoUpdateCommand);
|
|
25391
26261
|
cli.register(DeployCommand);
|
|
25392
26262
|
cli.register(AgentDeployAdvisorCommand);
|
|
25393
26263
|
cli.register(AgentRemoveCommand);
|