@m8t-stack/cli 0.2.71 → 0.2.72
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 +1371 -846
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1370,7 +1370,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1370
1370
|
import { Builtins, Cli } from "clipanion";
|
|
1371
1371
|
|
|
1372
1372
|
// src/lib/package-version.ts
|
|
1373
|
-
var CLI_VERSION = "0.2.
|
|
1373
|
+
var CLI_VERSION = "0.2.72";
|
|
1374
1374
|
|
|
1375
1375
|
// src/lib/render-error.ts
|
|
1376
1376
|
init_errors();
|
|
@@ -1917,7 +1917,7 @@ var safeJSON = (text) => {
|
|
|
1917
1917
|
};
|
|
1918
1918
|
|
|
1919
1919
|
// ../../node_modules/.pnpm/openai@6.49.0_ws@8.21.1_zod@4.4.3/node_modules/openai/internal/utils/sleep.mjs
|
|
1920
|
-
var sleep = (ms) => new Promise((
|
|
1920
|
+
var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
1921
1921
|
|
|
1922
1922
|
// ../../node_modules/.pnpm/openai@6.49.0_ws@8.21.1_zod@4.4.3/node_modules/openai/version.mjs
|
|
1923
1923
|
var VERSION = "6.49.0";
|
|
@@ -3028,8 +3028,8 @@ function addRequestID(value, response) {
|
|
|
3028
3028
|
var _APIPromise_client;
|
|
3029
3029
|
var APIPromise = class _APIPromise extends Promise {
|
|
3030
3030
|
constructor(client, responsePromise, parseResponse2 = defaultParseResponse) {
|
|
3031
|
-
super((
|
|
3032
|
-
|
|
3031
|
+
super((resolve6) => {
|
|
3032
|
+
resolve6(null);
|
|
3033
3033
|
});
|
|
3034
3034
|
this.responsePromise = responsePromise;
|
|
3035
3035
|
this.parseResponse = parseResponse2;
|
|
@@ -3704,12 +3704,12 @@ function encodeURIPath(str4) {
|
|
|
3704
3704
|
return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
3705
3705
|
}
|
|
3706
3706
|
var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
3707
|
-
var createPathTagFunction = (pathEncoder = encodeURIPath) => function
|
|
3707
|
+
var createPathTagFunction = (pathEncoder = encodeURIPath) => function path48(statics, ...params) {
|
|
3708
3708
|
if (statics.length === 1)
|
|
3709
3709
|
return statics[0];
|
|
3710
3710
|
let postPath = false;
|
|
3711
3711
|
const invalidSegments = [];
|
|
3712
|
-
const
|
|
3712
|
+
const path49 = statics.reduce((previousValue, currentValue, index) => {
|
|
3713
3713
|
if (/[?#]/.test(currentValue)) {
|
|
3714
3714
|
postPath = true;
|
|
3715
3715
|
}
|
|
@@ -3726,7 +3726,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path43(sta
|
|
|
3726
3726
|
}
|
|
3727
3727
|
return previousValue + currentValue + (index === params.length ? "" : encoded);
|
|
3728
3728
|
}, "");
|
|
3729
|
-
const pathOnly =
|
|
3729
|
+
const pathOnly = path49.split(/[?#]/, 1)[0];
|
|
3730
3730
|
const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
|
|
3731
3731
|
let match;
|
|
3732
3732
|
while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
|
|
@@ -3747,10 +3747,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path43(sta
|
|
|
3747
3747
|
}, "");
|
|
3748
3748
|
throw new OpenAIError(`Path parameters result in path with invalid segments:
|
|
3749
3749
|
${invalidSegments.map((e) => e.error).join("\n")}
|
|
3750
|
-
${
|
|
3750
|
+
${path49}
|
|
3751
3751
|
${underline}`);
|
|
3752
3752
|
}
|
|
3753
|
-
return
|
|
3753
|
+
return path49;
|
|
3754
3754
|
};
|
|
3755
3755
|
var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
|
|
3756
3756
|
|
|
@@ -3926,12 +3926,12 @@ var EventStream = class {
|
|
|
3926
3926
|
_EventStream_errored.set(this, false);
|
|
3927
3927
|
_EventStream_aborted.set(this, false);
|
|
3928
3928
|
_EventStream_catchingPromiseCreated.set(this, false);
|
|
3929
|
-
__classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((
|
|
3930
|
-
__classPrivateFieldSet(this, _EventStream_resolveConnectedPromise,
|
|
3929
|
+
__classPrivateFieldSet(this, _EventStream_connectedPromise, new Promise((resolve6, reject) => {
|
|
3930
|
+
__classPrivateFieldSet(this, _EventStream_resolveConnectedPromise, resolve6, "f");
|
|
3931
3931
|
__classPrivateFieldSet(this, _EventStream_rejectConnectedPromise, reject, "f");
|
|
3932
3932
|
}), "f");
|
|
3933
|
-
__classPrivateFieldSet(this, _EventStream_endPromise, new Promise((
|
|
3934
|
-
__classPrivateFieldSet(this, _EventStream_resolveEndPromise,
|
|
3933
|
+
__classPrivateFieldSet(this, _EventStream_endPromise, new Promise((resolve6, reject) => {
|
|
3934
|
+
__classPrivateFieldSet(this, _EventStream_resolveEndPromise, resolve6, "f");
|
|
3935
3935
|
__classPrivateFieldSet(this, _EventStream_rejectEndPromise, reject, "f");
|
|
3936
3936
|
}), "f");
|
|
3937
3937
|
__classPrivateFieldGet(this, _EventStream_connectedPromise, "f").catch(() => {
|
|
@@ -4031,11 +4031,11 @@ var EventStream = class {
|
|
|
4031
4031
|
* const message = await stream.emitted('message') // rejects if the stream errors
|
|
4032
4032
|
*/
|
|
4033
4033
|
emitted(event) {
|
|
4034
|
-
return new Promise((
|
|
4034
|
+
return new Promise((resolve6, reject) => {
|
|
4035
4035
|
__classPrivateFieldSet(this, _EventStream_catchingPromiseCreated, true, "f");
|
|
4036
4036
|
if (event !== "error")
|
|
4037
4037
|
this.once("error", reject);
|
|
4038
|
-
this.once(event,
|
|
4038
|
+
this.once(event, resolve6);
|
|
4039
4039
|
});
|
|
4040
4040
|
}
|
|
4041
4041
|
/**
|
|
@@ -4118,8 +4118,8 @@ var EventStream = class {
|
|
|
4118
4118
|
}
|
|
4119
4119
|
if (ended)
|
|
4120
4120
|
return Promise.resolve(doneResult());
|
|
4121
|
-
return new Promise((
|
|
4122
|
-
readQueue.push({ resolve:
|
|
4121
|
+
return new Promise((resolve6, reject) => {
|
|
4122
|
+
readQueue.push({ resolve: resolve6, reject });
|
|
4123
4123
|
});
|
|
4124
4124
|
},
|
|
4125
4125
|
return: () => {
|
|
@@ -5210,7 +5210,7 @@ var ChatCompletionStream = class _ChatCompletionStream extends AbstractChatCompl
|
|
|
5210
5210
|
if (done) {
|
|
5211
5211
|
return { value: void 0, done: true };
|
|
5212
5212
|
}
|
|
5213
|
-
return new Promise((
|
|
5213
|
+
return new Promise((resolve6, reject) => readQueue.push({ resolve: resolve6, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
5214
5214
|
}
|
|
5215
5215
|
const chunk = pushQueue.shift();
|
|
5216
5216
|
return { value: chunk, done: false };
|
|
@@ -5392,7 +5392,7 @@ var ChatCompletionStreamingRunner = class _ChatCompletionStreamingRunner extends
|
|
|
5392
5392
|
if (done) {
|
|
5393
5393
|
return { value: void 0, done: true };
|
|
5394
5394
|
}
|
|
5395
|
-
return new Promise((
|
|
5395
|
+
return new Promise((resolve6, reject) => readQueue.push({ resolve: resolve6, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
|
|
5396
5396
|
}
|
|
5397
5397
|
const event = pushQueue.shift();
|
|
5398
5398
|
if (!event) {
|
|
@@ -8674,7 +8674,7 @@ var AssistantStream = class extends EventStream {
|
|
|
8674
8674
|
if (done) {
|
|
8675
8675
|
return { value: void 0, done: true };
|
|
8676
8676
|
}
|
|
8677
|
-
return new Promise((
|
|
8677
|
+
return new Promise((resolve6, reject) => readQueue.push({ resolve: resolve6, reject })).then((chunk2) => chunk2 ? { value: chunk2, done: false } : { value: void 0, done: true });
|
|
8678
8678
|
}
|
|
8679
8679
|
const chunk = pushQueue.shift();
|
|
8680
8680
|
return { value: chunk, done: false };
|
|
@@ -11051,7 +11051,7 @@ var ResponseStream = class _ResponseStream extends EventStream {
|
|
|
11051
11051
|
if (done) {
|
|
11052
11052
|
return { value: void 0, done: true };
|
|
11053
11053
|
}
|
|
11054
|
-
return new Promise((
|
|
11054
|
+
return new Promise((resolve6, reject) => readQueue.push({ resolve: resolve6, reject })).then((event2) => event2 ? { value: event2, done: false } : { value: void 0, done: true });
|
|
11055
11055
|
}
|
|
11056
11056
|
const event = pushQueue.shift();
|
|
11057
11057
|
return { value: event, done: false };
|
|
@@ -12201,9 +12201,9 @@ var OpenAI = class {
|
|
|
12201
12201
|
this.apiKey = token;
|
|
12202
12202
|
return true;
|
|
12203
12203
|
}
|
|
12204
|
-
buildURL(
|
|
12204
|
+
buildURL(path48, query, defaultBaseURL) {
|
|
12205
12205
|
const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
|
|
12206
|
-
const url = isAbsoluteURL(
|
|
12206
|
+
const url = isAbsoluteURL(path48) ? new URL(path48) : new URL(baseURL + (baseURL.endsWith("/") && path48.startsWith("/") ? path48.slice(1) : path48));
|
|
12207
12207
|
const defaultQuery = this.defaultQuery();
|
|
12208
12208
|
const pathQuery = Object.fromEntries(url.searchParams);
|
|
12209
12209
|
if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
|
|
@@ -12233,24 +12233,24 @@ var OpenAI = class {
|
|
|
12233
12233
|
*/
|
|
12234
12234
|
async prepareRequest(request, { url, options }) {
|
|
12235
12235
|
}
|
|
12236
|
-
get(
|
|
12237
|
-
return this.methodRequest("get",
|
|
12236
|
+
get(path48, opts) {
|
|
12237
|
+
return this.methodRequest("get", path48, opts);
|
|
12238
12238
|
}
|
|
12239
|
-
post(
|
|
12240
|
-
return this.methodRequest("post",
|
|
12239
|
+
post(path48, opts) {
|
|
12240
|
+
return this.methodRequest("post", path48, opts);
|
|
12241
12241
|
}
|
|
12242
|
-
patch(
|
|
12243
|
-
return this.methodRequest("patch",
|
|
12242
|
+
patch(path48, opts) {
|
|
12243
|
+
return this.methodRequest("patch", path48, opts);
|
|
12244
12244
|
}
|
|
12245
|
-
put(
|
|
12246
|
-
return this.methodRequest("put",
|
|
12245
|
+
put(path48, opts) {
|
|
12246
|
+
return this.methodRequest("put", path48, opts);
|
|
12247
12247
|
}
|
|
12248
|
-
delete(
|
|
12249
|
-
return this.methodRequest("delete",
|
|
12248
|
+
delete(path48, opts) {
|
|
12249
|
+
return this.methodRequest("delete", path48, opts);
|
|
12250
12250
|
}
|
|
12251
|
-
methodRequest(method,
|
|
12251
|
+
methodRequest(method, path48, opts) {
|
|
12252
12252
|
return this.request(Promise.resolve(opts).then((opts2) => {
|
|
12253
|
-
return { method, path:
|
|
12253
|
+
return { method, path: path48, ...opts2 };
|
|
12254
12254
|
}));
|
|
12255
12255
|
}
|
|
12256
12256
|
request(options, remainingRetries = null) {
|
|
@@ -12375,8 +12375,8 @@ var OpenAI = class {
|
|
|
12375
12375
|
}));
|
|
12376
12376
|
return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
|
|
12377
12377
|
}
|
|
12378
|
-
getAPIList(
|
|
12379
|
-
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path:
|
|
12378
|
+
getAPIList(path48, Page2, opts) {
|
|
12379
|
+
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path48, ...opts2 })) : { method: "get", path: path48, ...opts });
|
|
12380
12380
|
}
|
|
12381
12381
|
requestAPIList(Page2, options) {
|
|
12382
12382
|
const request = this.makeRequest(options, null, void 0);
|
|
@@ -12470,8 +12470,8 @@ var OpenAI = class {
|
|
|
12470
12470
|
}
|
|
12471
12471
|
async buildRequest(inputOptions, { retryCount = 0 } = {}) {
|
|
12472
12472
|
const options = { ...inputOptions };
|
|
12473
|
-
const { method, path:
|
|
12474
|
-
const url = this.buildURL(
|
|
12473
|
+
const { method, path: path48, query, defaultBaseURL } = options;
|
|
12474
|
+
const url = this.buildURL(path48, query, defaultBaseURL);
|
|
12475
12475
|
if ("timeout" in options)
|
|
12476
12476
|
validatePositiveInteger("timeout", options.timeout);
|
|
12477
12477
|
options.timeout = options.timeout ?? this.timeout;
|
|
@@ -12839,7 +12839,7 @@ function redactAzArgs(args) {
|
|
|
12839
12839
|
return out;
|
|
12840
12840
|
}
|
|
12841
12841
|
function runAz(args) {
|
|
12842
|
-
return new Promise((
|
|
12842
|
+
return new Promise((resolve6, reject) => {
|
|
12843
12843
|
const proc = spawn("az", args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32" });
|
|
12844
12844
|
const out = [];
|
|
12845
12845
|
const err = [];
|
|
@@ -12879,7 +12879,7 @@ function runAz(args) {
|
|
|
12879
12879
|
);
|
|
12880
12880
|
return;
|
|
12881
12881
|
}
|
|
12882
|
-
|
|
12882
|
+
resolve6(Buffer.concat(out).toString("utf8"));
|
|
12883
12883
|
});
|
|
12884
12884
|
});
|
|
12885
12885
|
}
|
|
@@ -13473,8 +13473,8 @@ var BindCleanupCommand = class extends M8tCommand {
|
|
|
13473
13473
|
colors.error("\u26A0") + ` cleaned ${data.cleaned.length.toString()} binding(s); ${data.failed.length.toString()} failed:
|
|
13474
13474
|
`
|
|
13475
13475
|
);
|
|
13476
|
-
for (const
|
|
13477
|
-
this.context.stderr.write(` - ${
|
|
13476
|
+
for (const fail2 of data.failed) {
|
|
13477
|
+
this.context.stderr.write(` - ${fail2.bindingId} (${fail2.channel}): ${fail2.originalMessage}
|
|
13478
13478
|
`);
|
|
13479
13479
|
}
|
|
13480
13480
|
this.context.stderr.write(
|
|
@@ -13517,10 +13517,10 @@ var BindListCommand = class extends M8tCommand {
|
|
|
13517
13517
|
const qs = new URLSearchParams();
|
|
13518
13518
|
if (this.channel) qs.set("channel", this.channel);
|
|
13519
13519
|
if (this.status) qs.set("status", this.status);
|
|
13520
|
-
const
|
|
13520
|
+
const path48 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
|
|
13521
13521
|
const data = await apiCall(
|
|
13522
13522
|
{ gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
|
|
13523
|
-
{ method: "GET", path:
|
|
13523
|
+
{ method: "GET", path: path48 },
|
|
13524
13524
|
(msg) => this.context.stderr.write(msg + "\n")
|
|
13525
13525
|
);
|
|
13526
13526
|
if (mode === "json") {
|
|
@@ -14434,13 +14434,13 @@ async function createGitHubApp(args) {
|
|
|
14434
14434
|
const setupUrl = `${base}/setup`;
|
|
14435
14435
|
let createdApp = null;
|
|
14436
14436
|
let polledInstallationId = null;
|
|
14437
|
-
return new Promise((
|
|
14437
|
+
return new Promise((resolve6, reject) => {
|
|
14438
14438
|
let settled = false;
|
|
14439
14439
|
let server;
|
|
14440
14440
|
const complete = (installationId) => {
|
|
14441
14441
|
if (settled || !createdApp) return;
|
|
14442
14442
|
settled = true;
|
|
14443
|
-
|
|
14443
|
+
resolve6({ app: createdApp, installationId });
|
|
14444
14444
|
setTimeout(() => {
|
|
14445
14445
|
server.close();
|
|
14446
14446
|
}, 2500);
|
|
@@ -14589,7 +14589,7 @@ function outgoingSidecarToPreserve(home, pointerText, sidecarExists) {
|
|
|
14589
14589
|
// src/lib/gh-helpers.ts
|
|
14590
14590
|
init_errors();
|
|
14591
14591
|
import { spawn as spawn2 } from "child_process";
|
|
14592
|
-
var defaultGhExec = (cmd, args) => new Promise((
|
|
14592
|
+
var defaultGhExec = (cmd, args) => new Promise((resolve6) => {
|
|
14593
14593
|
const child = spawn2(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
14594
14594
|
let stdout = "";
|
|
14595
14595
|
let stderr = "";
|
|
@@ -14597,7 +14597,7 @@ var defaultGhExec = (cmd, args) => new Promise((resolve4) => {
|
|
|
14597
14597
|
const done = (r) => {
|
|
14598
14598
|
if (!settled) {
|
|
14599
14599
|
settled = true;
|
|
14600
|
-
|
|
14600
|
+
resolve6(r);
|
|
14601
14601
|
}
|
|
14602
14602
|
};
|
|
14603
14603
|
child.stdout.on("data", (d) => {
|
|
@@ -14766,8 +14766,8 @@ ${r.stderr}`;
|
|
|
14766
14766
|
message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
|
|
14767
14767
|
});
|
|
14768
14768
|
},
|
|
14769
|
-
getFile: async (
|
|
14770
|
-
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${
|
|
14769
|
+
getFile: async (path48) => {
|
|
14770
|
+
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path48}`]);
|
|
14771
14771
|
return r.exitCode === 0 ? r.stdout : null;
|
|
14772
14772
|
},
|
|
14773
14773
|
isEmpty: async () => {
|
|
@@ -15049,11 +15049,11 @@ var BrainCheckAppCommand = class extends M8tCommand {
|
|
|
15049
15049
|
// src/commands/brain/create.ts
|
|
15050
15050
|
init_esm2();
|
|
15051
15051
|
import { Command as Command18, Option as Option17 } from "clipanion";
|
|
15052
|
-
import { execFileSync, spawnSync } from "child_process";
|
|
15053
|
-
import { readFileSync as
|
|
15054
|
-
import * as
|
|
15055
|
-
import * as
|
|
15056
|
-
import * as
|
|
15052
|
+
import { execFileSync as execFileSync3, spawnSync } from "child_process";
|
|
15053
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
15054
|
+
import * as fs14 from "fs";
|
|
15055
|
+
import * as os5 from "os";
|
|
15056
|
+
import * as path14 from "path";
|
|
15057
15057
|
import * as readline2 from "readline/promises";
|
|
15058
15058
|
import { DefaultAzureCredential as DefaultAzureCredential4 } from "@azure/identity";
|
|
15059
15059
|
init_errors();
|
|
@@ -15556,8 +15556,107 @@ async function createBrainEnabledVersion(args) {
|
|
|
15556
15556
|
|
|
15557
15557
|
// src/lib/brain-seed.ts
|
|
15558
15558
|
init_errors();
|
|
15559
|
-
import * as
|
|
15560
|
-
import * as
|
|
15559
|
+
import * as fs13 from "fs";
|
|
15560
|
+
import * as path13 from "path";
|
|
15561
|
+
|
|
15562
|
+
// src/lib/agent-content-sync.ts
|
|
15563
|
+
init_errors();
|
|
15564
|
+
import * as fs12 from "fs";
|
|
15565
|
+
import * as path12 from "path";
|
|
15566
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
15567
|
+
|
|
15568
|
+
// src/lib/release-pin.ts
|
|
15569
|
+
init_errors();
|
|
15570
|
+
var PIN_SCHEMA_VERSION = 2;
|
|
15571
|
+
var REF_RE = /^[0-9a-f]{40}$/;
|
|
15572
|
+
var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
|
|
15573
|
+
var REPO_RE = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
|
15574
|
+
function fail(source, detail, hint) {
|
|
15575
|
+
throw new LocalCliError({
|
|
15576
|
+
code: "RELEASE_PIN_INVALID",
|
|
15577
|
+
message: `${source}: ${detail}`,
|
|
15578
|
+
...hint ? { hint } : {}
|
|
15579
|
+
});
|
|
15580
|
+
}
|
|
15581
|
+
function parseAgent(source, key2, raw) {
|
|
15582
|
+
if (typeof raw !== "object" || raw === null) fail(source, `agents.${key2} is not an object.`);
|
|
15583
|
+
const a = raw;
|
|
15584
|
+
if (typeof a.repo !== "string" || !REPO_RE.test(a.repo)) {
|
|
15585
|
+
fail(source, `agents.${key2}.repo must be "owner/name", got ${JSON.stringify(a.repo)}.`);
|
|
15586
|
+
}
|
|
15587
|
+
if (typeof a.ref !== "string" || !REF_RE.test(a.ref)) {
|
|
15588
|
+
fail(
|
|
15589
|
+
source,
|
|
15590
|
+
`agents.${key2}.ref must be a 40-hex commit sha, got ${JSON.stringify(a.ref)}.`,
|
|
15591
|
+
"A tag or branch can move, so it is not a pin. Use `m8t agent-content pin <key>` to resolve one."
|
|
15592
|
+
);
|
|
15593
|
+
}
|
|
15594
|
+
if (a.visibility !== "public" && a.visibility !== "private") {
|
|
15595
|
+
fail(source, `agents.${key2}.visibility must be "public" or "private", got ${JSON.stringify(a.visibility)}.`);
|
|
15596
|
+
}
|
|
15597
|
+
if (typeof a.persona !== "string" || a.persona.length === 0) {
|
|
15598
|
+
fail(source, `agents.${key2}.persona must be a non-empty persona slug.`);
|
|
15599
|
+
}
|
|
15600
|
+
if (typeof a.contentSha !== "string" || !DIGEST_RE.test(a.contentSha)) {
|
|
15601
|
+
fail(source, `agents.${key2}.contentSha must be "sha256:<64-hex>", got ${JSON.stringify(a.contentSha)}.`);
|
|
15602
|
+
}
|
|
15603
|
+
return { repo: a.repo, ref: a.ref, visibility: a.visibility, persona: a.persona, contentSha: a.contentSha };
|
|
15604
|
+
}
|
|
15605
|
+
function parseReleasePin(json, source) {
|
|
15606
|
+
let doc;
|
|
15607
|
+
try {
|
|
15608
|
+
doc = JSON.parse(json);
|
|
15609
|
+
} catch (e) {
|
|
15610
|
+
fail(source, `not valid JSON: ${e.message}`);
|
|
15611
|
+
}
|
|
15612
|
+
if (typeof doc !== "object" || doc === null) fail(source, "top level is not an object.");
|
|
15613
|
+
const d = doc;
|
|
15614
|
+
if (d.schemaVersion !== PIN_SCHEMA_VERSION) {
|
|
15615
|
+
fail(
|
|
15616
|
+
source,
|
|
15617
|
+
`schemaVersion ${JSON.stringify(d.schemaVersion)} is not the expected ${PIN_SCHEMA_VERSION.toString()}.`,
|
|
15618
|
+
"This CLI is older or newer than the pin it is reading. Guessing at an unknown shape is how a stale pin ships as if it were current."
|
|
15619
|
+
);
|
|
15620
|
+
}
|
|
15621
|
+
if (typeof d.platformVersion !== "string") fail(source, "platformVersion is missing.");
|
|
15622
|
+
const rawAgents = d.agents ?? {};
|
|
15623
|
+
if (typeof rawAgents !== "object" || rawAgents === null || Array.isArray(rawAgents)) {
|
|
15624
|
+
fail(source, "agents must be an object keyed by agent name.");
|
|
15625
|
+
}
|
|
15626
|
+
const agents = {};
|
|
15627
|
+
const personaOwner = /* @__PURE__ */ new Map();
|
|
15628
|
+
for (const [key2, raw] of Object.entries(rawAgents)) {
|
|
15629
|
+
const agent = parseAgent(source, key2, raw);
|
|
15630
|
+
const existing = personaOwner.get(agent.persona);
|
|
15631
|
+
if (existing !== void 0) {
|
|
15632
|
+
fail(
|
|
15633
|
+
source,
|
|
15634
|
+
`agents.${existing} and agents.${key2} both claim persona '${agent.persona}'.`,
|
|
15635
|
+
"Two sources for one persona means hydration order decides which wins \u2014 silently."
|
|
15636
|
+
);
|
|
15637
|
+
}
|
|
15638
|
+
personaOwner.set(agent.persona, key2);
|
|
15639
|
+
agents[key2] = agent;
|
|
15640
|
+
}
|
|
15641
|
+
const personas = d.personas ?? [];
|
|
15642
|
+
if (!Array.isArray(personas) || personas.some((p) => typeof p !== "string")) {
|
|
15643
|
+
fail(source, "personas must be an array of strings.");
|
|
15644
|
+
}
|
|
15645
|
+
return {
|
|
15646
|
+
schemaVersion: d.schemaVersion,
|
|
15647
|
+
platformVersion: d.platformVersion,
|
|
15648
|
+
agents,
|
|
15649
|
+
personas
|
|
15650
|
+
};
|
|
15651
|
+
}
|
|
15652
|
+
|
|
15653
|
+
// src/lib/agent-content.ts
|
|
15654
|
+
init_errors();
|
|
15655
|
+
import * as fs11 from "fs";
|
|
15656
|
+
import * as os4 from "os";
|
|
15657
|
+
import * as path11 from "path";
|
|
15658
|
+
import { createHash } from "crypto";
|
|
15659
|
+
import { parse as parseYaml5 } from "yaml";
|
|
15561
15660
|
|
|
15562
15661
|
// src/lib/seed-manifest.ts
|
|
15563
15662
|
import * as fs9 from "fs";
|
|
@@ -15612,22 +15711,322 @@ function deriveOwnedPaths(seedDir) {
|
|
|
15612
15711
|
return [...owned].sort((a, b) => a.localeCompare(b));
|
|
15613
15712
|
}
|
|
15614
15713
|
|
|
15714
|
+
// src/lib/tarball-cache.ts
|
|
15715
|
+
init_errors();
|
|
15716
|
+
import * as fs10 from "fs";
|
|
15717
|
+
import * as path10 from "path";
|
|
15718
|
+
import { execFileSync } from "child_process";
|
|
15719
|
+
function extractTarGz(tgz, into) {
|
|
15720
|
+
execFileSync("tar", ["-xzf", tgz, "-C", into, "--strip-components=1"]);
|
|
15721
|
+
}
|
|
15722
|
+
function publishAtomically(args) {
|
|
15723
|
+
try {
|
|
15724
|
+
fs10.renameSync(args.from, args.dest);
|
|
15725
|
+
} catch (e) {
|
|
15726
|
+
const err = e;
|
|
15727
|
+
const isBenignRace = (err.code === "EEXIST" || err.code === "ENOTEMPTY") && fs10.existsSync(path10.join(args.dest, args.sentinel));
|
|
15728
|
+
if (!isBenignRace) {
|
|
15729
|
+
throw new LocalCliError({
|
|
15730
|
+
code: args.errorCode,
|
|
15731
|
+
message: `Could not publish cache to ${args.dest}: ${err.message}`
|
|
15732
|
+
});
|
|
15733
|
+
}
|
|
15734
|
+
}
|
|
15735
|
+
}
|
|
15736
|
+
|
|
15737
|
+
// src/lib/agent-content.ts
|
|
15738
|
+
var AGENT_DIR = "agent";
|
|
15739
|
+
var AGENT_SEED_DIRS = [...SEED_PATHS, "memory"];
|
|
15740
|
+
var AGENT_SEED_FILES = ["NOTICE"];
|
|
15741
|
+
var PERSONA_REL = `${AGENT_DIR}/persona.md`;
|
|
15742
|
+
function walk(base, dir, out) {
|
|
15743
|
+
for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
15744
|
+
const abs = path11.join(dir, entry.name);
|
|
15745
|
+
if (entry.isDirectory()) walk(base, abs, out);
|
|
15746
|
+
else if (entry.isFile()) out.push(path11.relative(base, abs).split(path11.sep).join("/"));
|
|
15747
|
+
}
|
|
15748
|
+
}
|
|
15749
|
+
function collectAgentContent(repoDir) {
|
|
15750
|
+
const personaAbs = path11.join(repoDir, AGENT_DIR, "persona.md");
|
|
15751
|
+
if (!fs11.existsSync(personaAbs)) {
|
|
15752
|
+
throw new LocalCliError({
|
|
15753
|
+
code: "AGENT_CONTENT_INVALID",
|
|
15754
|
+
message: `'${repoDir}' is not an agent repo: no ${PERSONA_REL}.`,
|
|
15755
|
+
hint: "An agent repo declares its agent at agent/persona.md \u2014 see docs/architecture/AGENT-REPO-CONTRACT.md."
|
|
15756
|
+
});
|
|
15757
|
+
}
|
|
15758
|
+
const rels = [];
|
|
15759
|
+
walk(repoDir, path11.join(repoDir, AGENT_DIR), rels);
|
|
15760
|
+
for (const dir of AGENT_SEED_DIRS) {
|
|
15761
|
+
const abs = path11.join(repoDir, dir);
|
|
15762
|
+
if (fs11.existsSync(abs) && fs11.statSync(abs).isDirectory()) walk(repoDir, abs, rels);
|
|
15763
|
+
}
|
|
15764
|
+
for (const file of AGENT_SEED_FILES) {
|
|
15765
|
+
const abs = path11.join(repoDir, file);
|
|
15766
|
+
if (fs11.existsSync(abs) && fs11.statSync(abs).isFile()) rels.push(file);
|
|
15767
|
+
}
|
|
15768
|
+
const files = /* @__PURE__ */ new Map();
|
|
15769
|
+
for (const rel of rels) files.set(rel, fs11.readFileSync(path11.join(repoDir, rel)));
|
|
15770
|
+
return { files };
|
|
15771
|
+
}
|
|
15772
|
+
function digestAgentContent(content) {
|
|
15773
|
+
const records = [...content.files.entries()].map(([rel, bytes]) => [rel.split("\\").join("/"), createHash("sha256").update(bytes).digest("hex")]).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).map(([rel, sha]) => `${sha} ${rel}`);
|
|
15774
|
+
return "sha256:" + createHash("sha256").update(records.join("\0")).digest("hex");
|
|
15775
|
+
}
|
|
15776
|
+
function readDeclaredPersona(content) {
|
|
15777
|
+
const raw = content.files.get(PERSONA_REL);
|
|
15778
|
+
if (!raw) {
|
|
15779
|
+
throw new LocalCliError({
|
|
15780
|
+
code: "AGENT_CONTENT_INVALID",
|
|
15781
|
+
message: `Agent content carries no ${PERSONA_REL}.`
|
|
15782
|
+
});
|
|
15783
|
+
}
|
|
15784
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw.toString("utf8"));
|
|
15785
|
+
const name = fm ? parseYaml5(fm[1])?.name : void 0;
|
|
15786
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
15787
|
+
throw new LocalCliError({
|
|
15788
|
+
code: "AGENT_CONTENT_INVALID",
|
|
15789
|
+
message: `${PERSONA_REL} declares no frontmatter 'name'.`,
|
|
15790
|
+
hint: "Layer 1 of the persona schema requires `name` \u2014 see docs/architecture/SCHEMA.md."
|
|
15791
|
+
});
|
|
15792
|
+
}
|
|
15793
|
+
return name;
|
|
15794
|
+
}
|
|
15795
|
+
function hydrateAgentContent(content, destRoot, persona) {
|
|
15796
|
+
const personaDir = path11.join(destRoot, "personas", persona);
|
|
15797
|
+
const seedDir = path11.join(destRoot, "brain-seeds", persona);
|
|
15798
|
+
fs11.mkdirSync(destRoot, { recursive: true });
|
|
15799
|
+
const stage = fs11.mkdtempSync(path11.join(destRoot, `.hydrate-${persona}-`));
|
|
15800
|
+
try {
|
|
15801
|
+
for (const [rel, bytes] of content.files) {
|
|
15802
|
+
const dest = rel.startsWith(`${AGENT_DIR}/`) ? path11.join(stage, "personas", rel.slice(AGENT_DIR.length + 1)) : path11.join(stage, "seed", rel);
|
|
15803
|
+
fs11.mkdirSync(path11.dirname(dest), { recursive: true });
|
|
15804
|
+
fs11.writeFileSync(dest, bytes);
|
|
15805
|
+
}
|
|
15806
|
+
swapInto(path11.join(stage, "personas"), personaDir);
|
|
15807
|
+
swapInto(path11.join(stage, "seed"), seedDir);
|
|
15808
|
+
} finally {
|
|
15809
|
+
fs11.rmSync(stage, { recursive: true, force: true });
|
|
15810
|
+
}
|
|
15811
|
+
}
|
|
15812
|
+
function swapInto(from, dest) {
|
|
15813
|
+
if (!fs11.existsSync(from)) {
|
|
15814
|
+
fs11.rmSync(dest, { recursive: true, force: true });
|
|
15815
|
+
return;
|
|
15816
|
+
}
|
|
15817
|
+
fs11.mkdirSync(path11.dirname(dest), { recursive: true });
|
|
15818
|
+
const old = `${dest}.old-${process.pid.toString()}`;
|
|
15819
|
+
fs11.rmSync(old, { recursive: true, force: true });
|
|
15820
|
+
if (fs11.existsSync(dest)) fs11.renameSync(dest, old);
|
|
15821
|
+
fs11.renameSync(from, dest);
|
|
15822
|
+
fs11.rmSync(old, { recursive: true, force: true });
|
|
15823
|
+
}
|
|
15824
|
+
async function resolveToken(deps) {
|
|
15825
|
+
const env = deps.env ?? process.env;
|
|
15826
|
+
const fromEnv = [env.AGENT_CONTENT_TOKEN, env.GH_TOKEN, env.GITHUB_TOKEN].find((t) => t);
|
|
15827
|
+
if (fromEnv) return fromEnv;
|
|
15828
|
+
try {
|
|
15829
|
+
const tok = await (deps.ghToken ?? getGhToken)();
|
|
15830
|
+
return tok || void 0;
|
|
15831
|
+
} catch {
|
|
15832
|
+
return void 0;
|
|
15833
|
+
}
|
|
15834
|
+
}
|
|
15835
|
+
function assertMatchesPin(content, pin) {
|
|
15836
|
+
const actual = digestAgentContent(content);
|
|
15837
|
+
if (actual !== pin.contentSha) {
|
|
15838
|
+
throw new LocalCliError({
|
|
15839
|
+
code: "AGENT_CONTENT_DIGEST_MISMATCH",
|
|
15840
|
+
message: `${pin.repo}@${pin.ref}: content digest ${actual} does not match the pinned ${pin.contentSha}.`,
|
|
15841
|
+
hint: "The pin and the ref disagree. Re-derive with `m8t agent-content pin <key>`, or point the pin at the right commit."
|
|
15842
|
+
});
|
|
15843
|
+
}
|
|
15844
|
+
const declared = readDeclaredPersona(content);
|
|
15845
|
+
if (declared !== pin.persona) {
|
|
15846
|
+
throw new LocalCliError({
|
|
15847
|
+
code: "AGENT_CONTENT_PERSONA_MISMATCH",
|
|
15848
|
+
message: `${pin.repo}@${pin.ref} declares persona '${declared}', but the pin expects '${pin.persona}'.`,
|
|
15849
|
+
hint: "Align the pin's `persona` with the frontmatter `name` in the repo's agent/persona.md."
|
|
15850
|
+
});
|
|
15851
|
+
}
|
|
15852
|
+
}
|
|
15853
|
+
async function downloadAgentRepo(pin, tmpDir, deps) {
|
|
15854
|
+
const token = await resolveToken(deps);
|
|
15855
|
+
if (pin.visibility === "private" && !token) {
|
|
15856
|
+
throw new LocalCliError({
|
|
15857
|
+
code: "AGENT_CONTENT_TOKEN_MISSING",
|
|
15858
|
+
message: `${pin.repo} is a private agent repo and no read token is available.`,
|
|
15859
|
+
hint: "Set AGENT_CONTENT_TOKEN (contents:read on that repo), or GH_TOKEN / GITHUB_TOKEN, or run `gh auth login`."
|
|
15860
|
+
});
|
|
15861
|
+
}
|
|
15862
|
+
const url = `https://api.github.com/repos/${pin.repo}/tarball/${pin.ref}`;
|
|
15863
|
+
const res = await (deps.fetchImpl ?? fetch)(url, {
|
|
15864
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
15865
|
+
});
|
|
15866
|
+
if (!res.ok) {
|
|
15867
|
+
const status = res.status.toString();
|
|
15868
|
+
throw new LocalCliError({
|
|
15869
|
+
code: "AGENT_CONTENT_FETCH_FAILED",
|
|
15870
|
+
message: `GET ${url} \u2192 HTTP ${status} (agent '${pin.repo}' at ref ${pin.ref}).`,
|
|
15871
|
+
hint: res.status === 404 && pin.visibility === "private" ? "A 404 on a private repo usually means the token lacks access, not that the ref is wrong. Check AGENT_CONTENT_TOKEN's permission on that repo." : res.status === 404 ? "Check that the pinned ref exists on that repo." : "Transient GitHub failures are retryable; a persistent one is not."
|
|
15872
|
+
});
|
|
15873
|
+
}
|
|
15874
|
+
const tgz = path11.join(tmpDir, "src.tar.gz");
|
|
15875
|
+
fs11.writeFileSync(tgz, Buffer.from(await res.arrayBuffer()));
|
|
15876
|
+
const unpacked = path11.join(tmpDir, "unpacked");
|
|
15877
|
+
fs11.mkdirSync(unpacked);
|
|
15878
|
+
extractTarGz(tgz, unpacked);
|
|
15879
|
+
return unpacked;
|
|
15880
|
+
}
|
|
15881
|
+
async function resolveAgentContent(key2, pin, deps = {}) {
|
|
15882
|
+
const cacheRoot = deps.cacheRoot ?? path11.join(os4.homedir(), ".m8t", "cache");
|
|
15883
|
+
const digestTag = pin.contentSha.replace(/^sha256:/, "").slice(0, 16);
|
|
15884
|
+
const dest = path11.join(cacheRoot, `agent-${key2}-${digestTag}`);
|
|
15885
|
+
if (fs11.existsSync(path11.join(dest, PERSONA_REL))) {
|
|
15886
|
+
try {
|
|
15887
|
+
const cached = collectAgentContent(dest);
|
|
15888
|
+
assertMatchesPin(cached, pin);
|
|
15889
|
+
return cached;
|
|
15890
|
+
} catch {
|
|
15891
|
+
deps.onNotice?.(
|
|
15892
|
+
`agent content cache for '${key2}' failed verification \u2014 discarding ${dest} and re-fetching.`
|
|
15893
|
+
);
|
|
15894
|
+
fs11.rmSync(dest, { recursive: true, force: true });
|
|
15895
|
+
}
|
|
15896
|
+
}
|
|
15897
|
+
fs11.mkdirSync(cacheRoot, { recursive: true });
|
|
15898
|
+
fs11.rmSync(dest, { recursive: true, force: true });
|
|
15899
|
+
const tmpDir = fs11.mkdtempSync(path11.join(cacheRoot, `.dl-${key2}-`));
|
|
15900
|
+
try {
|
|
15901
|
+
const unpacked = await downloadAgentRepo(pin, tmpDir, deps);
|
|
15902
|
+
const content = collectAgentContent(unpacked);
|
|
15903
|
+
assertMatchesPin(content, pin);
|
|
15904
|
+
publishAtomically({
|
|
15905
|
+
from: unpacked,
|
|
15906
|
+
dest,
|
|
15907
|
+
sentinel: PERSONA_REL,
|
|
15908
|
+
errorCode: "AGENT_CONTENT_CACHE_PUBLISH_FAILED"
|
|
15909
|
+
});
|
|
15910
|
+
return content;
|
|
15911
|
+
} finally {
|
|
15912
|
+
fs11.rmSync(tmpDir, { recursive: true, force: true });
|
|
15913
|
+
}
|
|
15914
|
+
}
|
|
15915
|
+
async function fetchAgentContentForPinning(source, deps = {}) {
|
|
15916
|
+
const tmpDir = fs11.mkdtempSync(path11.join(os4.tmpdir(), "m8t-agent-pin-"));
|
|
15917
|
+
try {
|
|
15918
|
+
return collectAgentContent(await downloadAgentRepo(source, tmpDir, deps));
|
|
15919
|
+
} finally {
|
|
15920
|
+
fs11.rmSync(tmpDir, { recursive: true, force: true });
|
|
15921
|
+
}
|
|
15922
|
+
}
|
|
15923
|
+
function collectHydratedContent(destRoot, pin) {
|
|
15924
|
+
const personaDir = path11.join(destRoot, "personas", pin.persona);
|
|
15925
|
+
const seedDir = path11.join(destRoot, "brain-seeds", pin.persona);
|
|
15926
|
+
if (!fs11.existsSync(path11.join(personaDir, "persona.md"))) return null;
|
|
15927
|
+
const files = /* @__PURE__ */ new Map();
|
|
15928
|
+
const rels = [];
|
|
15929
|
+
walk(personaDir, personaDir, rels);
|
|
15930
|
+
for (const rel of rels) files.set(`${AGENT_DIR}/${rel}`, fs11.readFileSync(path11.join(personaDir, rel)));
|
|
15931
|
+
if (fs11.existsSync(seedDir)) {
|
|
15932
|
+
const seedRels = [];
|
|
15933
|
+
walk(seedDir, seedDir, seedRels);
|
|
15934
|
+
for (const rel of seedRels) files.set(rel, fs11.readFileSync(path11.join(seedDir, rel)));
|
|
15935
|
+
}
|
|
15936
|
+
const content = { files };
|
|
15937
|
+
return digestAgentContent(content) === pin.contentSha ? content : null;
|
|
15938
|
+
}
|
|
15939
|
+
async function hydrateAgentsInto(destRoot, agents, deps = {}) {
|
|
15940
|
+
const resolved = [];
|
|
15941
|
+
for (const [key2, pin] of Object.entries(agents)) {
|
|
15942
|
+
if (collectHydratedContent(destRoot, pin)) continue;
|
|
15943
|
+
resolved.push({ pin, content: await resolveAgentContent(key2, pin, deps) });
|
|
15944
|
+
}
|
|
15945
|
+
for (const { pin, content } of resolved) hydrateAgentContent(content, destRoot, pin.persona);
|
|
15946
|
+
}
|
|
15947
|
+
|
|
15948
|
+
// src/lib/agent-content-sync.ts
|
|
15949
|
+
var PIN_REL_PATH = "installer/release-pin.json";
|
|
15950
|
+
async function syncAgentContent(args) {
|
|
15951
|
+
const pinPath = path12.join(args.root, PIN_REL_PATH);
|
|
15952
|
+
if (!fs12.existsSync(pinPath)) {
|
|
15953
|
+
throw new LocalCliError({
|
|
15954
|
+
code: "RELEASE_PIN_MISSING",
|
|
15955
|
+
message: `No ${PIN_REL_PATH} under '${args.root}'.`,
|
|
15956
|
+
hint: "Run from an m8t checkout, or pass --root pointing at one."
|
|
15957
|
+
});
|
|
15958
|
+
}
|
|
15959
|
+
const pin = parseReleasePin(fs12.readFileSync(pinPath, "utf8"), pinPath);
|
|
15960
|
+
await hydrateAgentsInto(args.root, pin.agents, args.deps);
|
|
15961
|
+
return Object.values(pin.agents).map((a) => ({ persona: a.persona, repo: a.repo, ref: a.ref }));
|
|
15962
|
+
}
|
|
15963
|
+
function agentContentHint(root, persona) {
|
|
15964
|
+
try {
|
|
15965
|
+
const pin = parseReleasePin(fs12.readFileSync(path12.join(root, PIN_REL_PATH), "utf8"), PIN_REL_PATH);
|
|
15966
|
+
const agent = Object.values(pin.agents).find((a) => a.persona === persona);
|
|
15967
|
+
if (!agent) return void 0;
|
|
15968
|
+
return `'${persona}' lives in ${agent.repo}, not in this repo \u2014 its content is generated, not committed. Run \`pnpm agent-content:sync\` to materialize it.`;
|
|
15969
|
+
} catch {
|
|
15970
|
+
return void 0;
|
|
15971
|
+
}
|
|
15972
|
+
}
|
|
15973
|
+
function resolveRemoteRef(repo, branch) {
|
|
15974
|
+
let out;
|
|
15975
|
+
try {
|
|
15976
|
+
out = execFileSync2("git", ["ls-remote", `https://github.com/${repo}.git`, branch], {
|
|
15977
|
+
encoding: "utf8",
|
|
15978
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
15979
|
+
});
|
|
15980
|
+
} catch (e) {
|
|
15981
|
+
throw new LocalCliError({
|
|
15982
|
+
code: "AGENT_REF_RESOLVE_FAILED",
|
|
15983
|
+
message: `Could not resolve ${repo}#${branch}: ${e.message}`,
|
|
15984
|
+
hint: "Check the repo name, the branch, and that your git can read it (`gh auth setup-git` for private repos)."
|
|
15985
|
+
});
|
|
15986
|
+
}
|
|
15987
|
+
const sha = /^([0-9a-f]{40})\s/.exec(out.trim())?.[1];
|
|
15988
|
+
if (!sha) {
|
|
15989
|
+
throw new LocalCliError({
|
|
15990
|
+
code: "AGENT_REF_RESOLVE_FAILED",
|
|
15991
|
+
message: `${repo} has no branch '${branch}'.`
|
|
15992
|
+
});
|
|
15993
|
+
}
|
|
15994
|
+
return sha;
|
|
15995
|
+
}
|
|
15996
|
+
async function derivePin(args) {
|
|
15997
|
+
const ref = (args.resolveRef ?? resolveRemoteRef)(args.current.repo, args.branch);
|
|
15998
|
+
const content = await fetchAgentContentForPinning(
|
|
15999
|
+
{ repo: args.current.repo, ref, visibility: args.current.visibility },
|
|
16000
|
+
args.deps
|
|
16001
|
+
);
|
|
16002
|
+
return {
|
|
16003
|
+
...args.current,
|
|
16004
|
+
ref,
|
|
16005
|
+
persona: readDeclaredPersona(content),
|
|
16006
|
+
contentSha: digestAgentContent(content)
|
|
16007
|
+
};
|
|
16008
|
+
}
|
|
16009
|
+
|
|
15615
16010
|
// src/lib/brain-seed.ts
|
|
15616
16011
|
function resolveSeed(name) {
|
|
15617
|
-
const
|
|
15618
|
-
|
|
16012
|
+
const root = resolveRepoRoot();
|
|
16013
|
+
const dir = path13.join(root, "brain-seeds", name);
|
|
16014
|
+
if (!fs13.existsSync(dir)) {
|
|
15619
16015
|
throw new LocalCliError({
|
|
15620
16016
|
code: "SEED_NOT_FOUND",
|
|
15621
16017
|
message: `--seed "${name}": no brain-seeds/${name}/ directory found.`,
|
|
15622
|
-
|
|
16018
|
+
// A product agent's seed is generated from its pinned repo rather than
|
|
16019
|
+
// committed here, so "check the name" is the wrong advice for the most
|
|
16020
|
+
// likely cause — an un-hydrated checkout.
|
|
16021
|
+
hint: agentContentHint(root, name) ?? "Seeds live under brain-seeds/. Check the name, or omit --seed for an unseeded brain."
|
|
15623
16022
|
});
|
|
15624
16023
|
}
|
|
15625
16024
|
return dir;
|
|
15626
16025
|
}
|
|
15627
16026
|
function materializeBrainTree(opts) {
|
|
15628
|
-
|
|
16027
|
+
fs13.cpSync(opts.templateSrc, opts.destDir, { recursive: true });
|
|
15629
16028
|
if (opts.seedDir) {
|
|
15630
|
-
|
|
16029
|
+
fs13.cpSync(opts.seedDir, opts.destDir, { recursive: true });
|
|
15631
16030
|
}
|
|
15632
16031
|
}
|
|
15633
16032
|
function writeSeedMarker(args) {
|
|
@@ -15637,21 +16036,21 @@ function writeSeedMarker(args) {
|
|
|
15637
16036
|
refreshedToVersion: args.version,
|
|
15638
16037
|
ownedPaths: deriveOwnedPaths(args.seedDir)
|
|
15639
16038
|
}]);
|
|
15640
|
-
const dest =
|
|
15641
|
-
|
|
15642
|
-
|
|
16039
|
+
const dest = path13.join(args.destDir, SEED_MANIFEST_PATH);
|
|
16040
|
+
fs13.mkdirSync(path13.dirname(dest), { recursive: true });
|
|
16041
|
+
fs13.writeFileSync(dest, serializeSeedManifest(manifest));
|
|
15643
16042
|
}
|
|
15644
16043
|
|
|
15645
16044
|
// src/lib/github-app-repo.ts
|
|
15646
16045
|
init_errors();
|
|
15647
16046
|
init_esm2();
|
|
15648
16047
|
import { spawn as spawn3 } from "child_process";
|
|
15649
|
-
import { parse as
|
|
16048
|
+
import { parse as parseYaml6 } from "yaml";
|
|
15650
16049
|
function isM8tBrainMarker(yamlText) {
|
|
15651
16050
|
if (!yamlText.trim()) return false;
|
|
15652
16051
|
let parsed;
|
|
15653
16052
|
try {
|
|
15654
|
-
parsed =
|
|
16053
|
+
parsed = parseYaml6(yamlText);
|
|
15655
16054
|
} catch {
|
|
15656
16055
|
return false;
|
|
15657
16056
|
}
|
|
@@ -15683,7 +16082,7 @@ async function createBlankRepo(args) {
|
|
|
15683
16082
|
return { cloneUrl: j.clone_url, defaultBranch: j.default_branch ?? "main" };
|
|
15684
16083
|
}
|
|
15685
16084
|
function run(cmd, cmdArgs, cwd, env) {
|
|
15686
|
-
return new Promise((
|
|
16085
|
+
return new Promise((resolve6, reject) => {
|
|
15687
16086
|
const c = spawn3(cmd, cmdArgs, { cwd, env: { ...process.env, ...env }, stdio: ["ignore", "pipe", "pipe"] });
|
|
15688
16087
|
let err = "";
|
|
15689
16088
|
c.stderr.on("data", (d) => {
|
|
@@ -15699,7 +16098,7 @@ function run(cmd, cmdArgs, cwd, env) {
|
|
|
15699
16098
|
});
|
|
15700
16099
|
c.on("close", (code) => {
|
|
15701
16100
|
if (code === 0) {
|
|
15702
|
-
|
|
16101
|
+
resolve6();
|
|
15703
16102
|
} else {
|
|
15704
16103
|
reject(
|
|
15705
16104
|
new LocalCliError({
|
|
@@ -15883,7 +16282,7 @@ function appRepoProbe(args) {
|
|
|
15883
16282
|
const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
15884
16283
|
return res.status;
|
|
15885
16284
|
},
|
|
15886
|
-
getFile: (
|
|
16285
|
+
getFile: (path48) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path48, fetchImpl: args.fetchImpl }),
|
|
15887
16286
|
isEmpty: async () => {
|
|
15888
16287
|
const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
15889
16288
|
return res.status === 404;
|
|
@@ -16097,7 +16496,7 @@ function resolveScaffoldCommit(repoRoot, env) {
|
|
|
16097
16496
|
const baked = env.M8T_BUILD_SHA?.trim();
|
|
16098
16497
|
if (baked) return baked;
|
|
16099
16498
|
try {
|
|
16100
|
-
return
|
|
16499
|
+
return execFileSync3("git", ["rev-parse", "HEAD"], {
|
|
16101
16500
|
cwd: repoRoot,
|
|
16102
16501
|
encoding: "utf8",
|
|
16103
16502
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -16180,7 +16579,7 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
16180
16579
|
if (useAppPath) {
|
|
16181
16580
|
await assertInstallationPermissions({
|
|
16182
16581
|
appId: appIdOpt,
|
|
16183
|
-
privateKeyPem:
|
|
16582
|
+
privateKeyPem: readFileSync9(appPemOpt, "utf8"),
|
|
16184
16583
|
installationId: installationIdOpt,
|
|
16185
16584
|
org: owner,
|
|
16186
16585
|
onUnverified: (m) => this.context.stderr.write(` ${colors.dim(`warning: ${m}`)}
|
|
@@ -16212,15 +16611,15 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
16212
16611
|
agentEndpoint
|
|
16213
16612
|
});
|
|
16214
16613
|
const repoRoot = resolveRepoRoot();
|
|
16215
|
-
const templateSrc =
|
|
16216
|
-
if (!
|
|
16614
|
+
const templateSrc = path14.join(repoRoot, "brain-template");
|
|
16615
|
+
if (!fs14.existsSync(templateSrc)) {
|
|
16217
16616
|
throw new LocalCliError({
|
|
16218
16617
|
code: "TEMPLATE_MISSING",
|
|
16219
16618
|
message: `brain-template/ missing at ${templateSrc}`,
|
|
16220
16619
|
hint: "Ensure brain-template/ exists at the repo root (same level as apps/)."
|
|
16221
16620
|
});
|
|
16222
16621
|
}
|
|
16223
|
-
const tmpDir =
|
|
16622
|
+
const tmpDir = fs14.mkdtempSync(path14.join(os5.tmpdir(), `m8t-brain-create-${worker}-`));
|
|
16224
16623
|
let result2;
|
|
16225
16624
|
let resolvedInstallationId = null;
|
|
16226
16625
|
const force = this.reuse === true ? "reuse" : this.newName === true ? "new" : void 0;
|
|
@@ -16230,7 +16629,7 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
16230
16629
|
if (useAppPath) {
|
|
16231
16630
|
token = await mintInstallationTokenFromPem({
|
|
16232
16631
|
appId: appIdOpt,
|
|
16233
|
-
privateKeyPem:
|
|
16632
|
+
privateKeyPem: readFileSync9(appPemOpt, "utf8"),
|
|
16234
16633
|
installationId: installationIdOpt
|
|
16235
16634
|
});
|
|
16236
16635
|
const tok = token;
|
|
@@ -16352,7 +16751,7 @@ var BrainCreateCommand = class extends M8tCommand {
|
|
|
16352
16751
|
project
|
|
16353
16752
|
});
|
|
16354
16753
|
} finally {
|
|
16355
|
-
|
|
16754
|
+
fs14.rmSync(tmpDir, { recursive: true, force: true });
|
|
16356
16755
|
}
|
|
16357
16756
|
if (mode === "json") {
|
|
16358
16757
|
this.context.stdout.write(
|
|
@@ -16804,9 +17203,9 @@ init_errors();
|
|
|
16804
17203
|
// src/lib/brain-unlink.ts
|
|
16805
17204
|
init_esm2();
|
|
16806
17205
|
init_errors();
|
|
16807
|
-
import * as
|
|
16808
|
-
import * as
|
|
16809
|
-
import * as
|
|
17206
|
+
import * as fs15 from "fs";
|
|
17207
|
+
import * as os6 from "os";
|
|
17208
|
+
import * as path15 from "path";
|
|
16810
17209
|
init_foundry_agent_get();
|
|
16811
17210
|
init_foundry_agent_version();
|
|
16812
17211
|
var FOUNDRY_ARM_API2 = "2025-04-01-preview";
|
|
@@ -16837,15 +17236,15 @@ async function unlinkBrain(args) {
|
|
|
16837
17236
|
if (yaml?.personaPath) {
|
|
16838
17237
|
let personaPath = yaml.personaPath;
|
|
16839
17238
|
if (!personaPath.startsWith("/")) {
|
|
16840
|
-
const marker =
|
|
16841
|
-
if (!
|
|
17239
|
+
const marker = path15.join(args.home ?? os6.homedir(), ".m8t", "repo-root");
|
|
17240
|
+
if (!fs15.existsSync(marker)) {
|
|
16842
17241
|
throw new LocalCliError({
|
|
16843
17242
|
code: "PERSONA_PATH_NOT_ABSOLUTE",
|
|
16844
17243
|
message: `personaPath '${personaPath}' is relative and the repo-root marker is missing.`,
|
|
16845
17244
|
hint: "Re-run 'm8t install' from a fresh m8t checkout (writes ~/.m8t/repo-root) \u2014 or edit the personaPath to absolute."
|
|
16846
17245
|
});
|
|
16847
17246
|
}
|
|
16848
|
-
personaPath =
|
|
17247
|
+
personaPath = path15.join(fs15.readFileSync(marker, "utf8").trim(), personaPath);
|
|
16849
17248
|
}
|
|
16850
17249
|
strippedInstructions = renderPersonaBody(personaPath, yaml.fillableFieldValues ?? {}, args.agentName);
|
|
16851
17250
|
} else {
|
|
@@ -17066,38 +17465,41 @@ import { DefaultAzureCredential as DefaultAzureCredential9 } from "@azure/identi
|
|
|
17066
17465
|
init_errors();
|
|
17067
17466
|
|
|
17068
17467
|
// src/lib/deploy-prompt-advisor.ts
|
|
17069
|
-
import * as
|
|
17070
|
-
import * as
|
|
17071
|
-
import { parse as
|
|
17468
|
+
import * as fs16 from "fs";
|
|
17469
|
+
import * as path16 from "path";
|
|
17470
|
+
import { parse as parseYaml7 } from "yaml";
|
|
17072
17471
|
init_foundry_agents();
|
|
17073
17472
|
init_errors();
|
|
17074
17473
|
init_esm();
|
|
17075
17474
|
function findPersonasRoot(hint) {
|
|
17076
|
-
let dir =
|
|
17077
|
-
const fsRoot =
|
|
17475
|
+
let dir = path16.resolve(hint);
|
|
17476
|
+
const fsRoot = path16.parse(dir).root;
|
|
17078
17477
|
for (; ; ) {
|
|
17079
|
-
if (
|
|
17080
|
-
const parent =
|
|
17478
|
+
if (fs16.existsSync(path16.join(dir, "personas"))) return dir;
|
|
17479
|
+
const parent = path16.dirname(dir);
|
|
17081
17480
|
if (parent === dir || dir === fsRoot) return null;
|
|
17082
17481
|
dir = parent;
|
|
17083
17482
|
}
|
|
17084
17483
|
}
|
|
17085
17484
|
async function deployPromptAdvisor(args) {
|
|
17086
17485
|
const resolvedRoot = findPersonasRoot(args.repoRoot);
|
|
17087
|
-
const personaPath = resolvedRoot ?
|
|
17486
|
+
const personaPath = resolvedRoot ? path16.join(resolvedRoot, "personas", args.persona, "persona.md") : path16.join(args.repoRoot, "personas", args.persona, "persona.md");
|
|
17088
17487
|
let raw;
|
|
17089
17488
|
try {
|
|
17090
|
-
raw =
|
|
17489
|
+
raw = fs16.readFileSync(personaPath, "utf8");
|
|
17091
17490
|
} catch (e) {
|
|
17092
17491
|
throw new LocalCliError({
|
|
17093
17492
|
code: "ADVISOR_PERSONA_MISSING",
|
|
17094
17493
|
message: `Could not read the persona at '${personaPath}'.`,
|
|
17095
|
-
|
|
17494
|
+
// A product agent's persona is generated from its pinned repo rather than
|
|
17495
|
+
// committed here, so an un-hydrated checkout is the likeliest cause and
|
|
17496
|
+
// "run from your checkout" would send the reader the wrong way.
|
|
17497
|
+
hint: agentContentHint(resolvedRoot ?? args.repoRoot, args.persona) ?? "Run from your m8t repo checkout, or pass --repo-root.",
|
|
17096
17498
|
cause: e
|
|
17097
17499
|
});
|
|
17098
17500
|
}
|
|
17099
17501
|
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
17100
|
-
const fm = fmMatch ?
|
|
17502
|
+
const fm = fmMatch ? parseYaml7(fmMatch[1]) : {};
|
|
17101
17503
|
const model = args.model ?? fm.targets?.foundry?.model;
|
|
17102
17504
|
if (!model) {
|
|
17103
17505
|
throw new LocalCliError({
|
|
@@ -17271,14 +17673,14 @@ init_errors();
|
|
|
17271
17673
|
// src/lib/agent-remove.ts
|
|
17272
17674
|
init_errors();
|
|
17273
17675
|
init_foundry_agent_get();
|
|
17274
|
-
import * as
|
|
17676
|
+
import * as fs18 from "fs";
|
|
17275
17677
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
17276
17678
|
|
|
17277
17679
|
// src/lib/a2a-enable.ts
|
|
17278
17680
|
init_errors();
|
|
17279
17681
|
init_foundry_agent_get();
|
|
17280
17682
|
init_foundry_agent_version();
|
|
17281
|
-
import { randomBytes as randomBytes2, createHash } from "crypto";
|
|
17683
|
+
import { randomBytes as randomBytes2, createHash as createHash2 } from "crypto";
|
|
17282
17684
|
|
|
17283
17685
|
// src/lib/data-plane-ready.ts
|
|
17284
17686
|
init_errors();
|
|
@@ -17386,18 +17788,18 @@ async function awaitAgentQueryable(args) {
|
|
|
17386
17788
|
// src/lib/persona-a2a.ts
|
|
17387
17789
|
init_errors();
|
|
17388
17790
|
init_esm();
|
|
17389
|
-
import * as
|
|
17390
|
-
import { parse as
|
|
17791
|
+
import * as fs17 from "fs";
|
|
17792
|
+
import { parse as parseYaml8 } from "yaml";
|
|
17391
17793
|
function readPersonaA2aCard(personaPath) {
|
|
17392
17794
|
let raw;
|
|
17393
17795
|
try {
|
|
17394
|
-
raw =
|
|
17796
|
+
raw = fs17.readFileSync(personaPath, "utf8");
|
|
17395
17797
|
} catch (e) {
|
|
17396
17798
|
throw new LocalCliError({ code: "PERSONA_READ_FAILED", message: `Could not read persona '${personaPath}': ${e.message}` });
|
|
17397
17799
|
}
|
|
17398
17800
|
const fm = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
17399
17801
|
if (!fm) throw new LocalCliError({ code: "PERSONA_NO_FRONTMATTER", message: `Persona '${personaPath}' has no frontmatter.` });
|
|
17400
|
-
const front =
|
|
17802
|
+
const front = parseYaml8(fm[1]);
|
|
17401
17803
|
const role = typeof front.role === "string" ? front.role : "";
|
|
17402
17804
|
const cardYaml = front.targets?.foundry?.["a2a-card"];
|
|
17403
17805
|
if (!cardYaml || typeof cardYaml !== "object") {
|
|
@@ -17459,7 +17861,7 @@ async function enableA2a(args) {
|
|
|
17459
17861
|
});
|
|
17460
17862
|
}
|
|
17461
17863
|
const bearer2 = `a2a_${randomBytes2(32).toString("base64url")}`;
|
|
17462
|
-
const bearerHash =
|
|
17864
|
+
const bearerHash = createHash2("sha256").update(bearer2).digest("hex");
|
|
17463
17865
|
progress("Provisioning the A2A connection\u2026");
|
|
17464
17866
|
await putA2aConnection({ credential: args.credential, projectArmId: args.projectArmId, connectionName, target: args.bridgeUrl, bearer: bearer2 });
|
|
17465
17867
|
progress("Deploying the a2a-enabled agent version\u2026");
|
|
@@ -17716,7 +18118,7 @@ function defaultRemoveAgentDeps(args) {
|
|
|
17716
18118
|
},
|
|
17717
18119
|
removeLocalYaml(agentName) {
|
|
17718
18120
|
const p = agentYamlPath(agentName, home);
|
|
17719
|
-
if (
|
|
18121
|
+
if (fs18.existsSync(p)) fs18.rmSync(p);
|
|
17720
18122
|
}
|
|
17721
18123
|
};
|
|
17722
18124
|
}
|
|
@@ -17858,17 +18260,17 @@ ${colors.success("\u2713")} removed ${colors.field(name)}.
|
|
|
17858
18260
|
};
|
|
17859
18261
|
|
|
17860
18262
|
// src/commands/a2a/enable.ts
|
|
17861
|
-
import * as
|
|
18263
|
+
import * as path18 from "path";
|
|
17862
18264
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
17863
18265
|
import { DefaultAzureCredential as DefaultAzureCredential11 } from "@azure/identity";
|
|
17864
18266
|
init_errors();
|
|
17865
18267
|
|
|
17866
18268
|
// src/lib/bridge-url.ts
|
|
17867
18269
|
init_errors();
|
|
17868
|
-
import * as
|
|
17869
|
-
import * as
|
|
17870
|
-
import * as
|
|
17871
|
-
import { parse as
|
|
18270
|
+
import * as fs19 from "fs";
|
|
18271
|
+
import * as os7 from "os";
|
|
18272
|
+
import * as path17 from "path";
|
|
18273
|
+
import { parse as parseYaml9 } from "yaml";
|
|
17872
18274
|
var A2A_PATH = "/api/a2a/mcp";
|
|
17873
18275
|
function resolveBridgeUrl(opts) {
|
|
17874
18276
|
const env = opts.env ?? process.env;
|
|
@@ -17889,10 +18291,10 @@ function clean(v) {
|
|
|
17889
18291
|
return t.length ? t : void 0;
|
|
17890
18292
|
}
|
|
17891
18293
|
function readConfigGatewayUrl(home) {
|
|
17892
|
-
const cfg =
|
|
17893
|
-
if (!
|
|
18294
|
+
const cfg = path17.join(home ?? os7.homedir(), ".m8t", "config.yaml");
|
|
18295
|
+
if (!fs19.existsSync(cfg)) return void 0;
|
|
17894
18296
|
try {
|
|
17895
|
-
const o =
|
|
18297
|
+
const o = parseYaml9(fs19.readFileSync(cfg, "utf8"));
|
|
17896
18298
|
return typeof o.gatewayUrl === "string" ? o.gatewayUrl : void 0;
|
|
17897
18299
|
} catch {
|
|
17898
18300
|
return void 0;
|
|
@@ -17961,11 +18363,11 @@ var A2aEnableCommand = class extends M8tCommand {
|
|
|
17961
18363
|
resolvePersonaPath() {
|
|
17962
18364
|
if (typeof this.persona === "string" && this.persona.length > 0) {
|
|
17963
18365
|
if (this.persona.includes("/") || this.persona.endsWith(".md")) return this.persona;
|
|
17964
|
-
return
|
|
18366
|
+
return path18.join(resolveRepoRoot(), "personas", this.persona, "persona.md");
|
|
17965
18367
|
}
|
|
17966
18368
|
const yaml = readAgentYaml(this.worker);
|
|
17967
18369
|
if (yaml?.personaPath) {
|
|
17968
|
-
return yaml.personaPath.startsWith("/") ? yaml.personaPath :
|
|
18370
|
+
return yaml.personaPath.startsWith("/") ? yaml.personaPath : path18.join(resolveRepoRoot(), yaml.personaPath);
|
|
17969
18371
|
}
|
|
17970
18372
|
throw new LocalCliError({ code: "A2A_NO_PERSONA", message: `Could not resolve a persona for '${this.worker}'. Pass --persona <name>.` });
|
|
17971
18373
|
}
|
|
@@ -18020,13 +18422,13 @@ var A2aDisableCommand = class extends M8tCommand {
|
|
|
18020
18422
|
import { Command as Command28 } from "clipanion";
|
|
18021
18423
|
|
|
18022
18424
|
// src/lib/architect-version.ts
|
|
18023
|
-
import { createHash as
|
|
18024
|
-
import * as
|
|
18025
|
-
import * as
|
|
18026
|
-
import * as
|
|
18425
|
+
import { createHash as createHash3 } from "crypto";
|
|
18426
|
+
import * as fs20 from "fs";
|
|
18427
|
+
import * as os8 from "os";
|
|
18428
|
+
import * as path19 from "path";
|
|
18027
18429
|
function readVersionFrontmatter(file) {
|
|
18028
|
-
if (!
|
|
18029
|
-
const text =
|
|
18430
|
+
if (!fs20.existsSync(file)) return "";
|
|
18431
|
+
const text = fs20.readFileSync(file, "utf8");
|
|
18030
18432
|
if (!text.startsWith("---")) return "";
|
|
18031
18433
|
const closeIdx = text.indexOf("\n---", 3);
|
|
18032
18434
|
if (closeIdx < 0) return "";
|
|
@@ -18035,27 +18437,27 @@ function readVersionFrontmatter(file) {
|
|
|
18035
18437
|
return m ? m[1].trim() : "";
|
|
18036
18438
|
}
|
|
18037
18439
|
function sha256File(filePath) {
|
|
18038
|
-
return
|
|
18440
|
+
return createHash3("sha256").update(fs20.readFileSync(filePath)).digest("hex");
|
|
18039
18441
|
}
|
|
18040
18442
|
var SIDECAR_FILENAME = "m8t-architect.m8t-skill.json";
|
|
18041
18443
|
var LEGACY_SIDECAR_FILENAME = ".m8t-skill.json";
|
|
18042
18444
|
function vsCodeUserDataDir() {
|
|
18043
18445
|
if (process.platform === "darwin") {
|
|
18044
|
-
return
|
|
18446
|
+
return path19.join(os8.homedir(), "Library", "Application Support", "Code", "User");
|
|
18045
18447
|
}
|
|
18046
18448
|
if (process.platform === "win32") {
|
|
18047
|
-
const appData = process.env.APPDATA ??
|
|
18048
|
-
return
|
|
18449
|
+
const appData = process.env.APPDATA ?? path19.join(os8.homedir(), "AppData", "Roaming");
|
|
18450
|
+
return path19.join(appData, "Code", "User");
|
|
18049
18451
|
}
|
|
18050
18452
|
return null;
|
|
18051
18453
|
}
|
|
18052
18454
|
function vsCodePromptsDir() {
|
|
18053
18455
|
const userData = vsCodeUserDataDir();
|
|
18054
18456
|
if (!userData) return null;
|
|
18055
|
-
const prompts =
|
|
18056
|
-
if (
|
|
18057
|
-
const promptFiles =
|
|
18058
|
-
if (
|
|
18457
|
+
const prompts = path19.join(userData, "prompts");
|
|
18458
|
+
if (fs20.existsSync(prompts)) return prompts;
|
|
18459
|
+
const promptFiles = path19.join(userData, "prompt-files");
|
|
18460
|
+
if (fs20.existsSync(promptFiles)) return promptFiles;
|
|
18059
18461
|
return prompts;
|
|
18060
18462
|
}
|
|
18061
18463
|
var COPILOT_CLI_SKILLS_ROOTS = [
|
|
@@ -18064,9 +18466,9 @@ var COPILOT_CLI_SKILLS_ROOTS = [
|
|
|
18064
18466
|
[".github", "copilot", "skills"]
|
|
18065
18467
|
];
|
|
18066
18468
|
function sidecarLocations() {
|
|
18067
|
-
const home =
|
|
18469
|
+
const home = os8.homedir();
|
|
18068
18470
|
const locations = [
|
|
18069
|
-
{ host: "Claude Code", dir:
|
|
18471
|
+
{ host: "Claude Code", dir: path19.join(home, ".claude", "skills", "m8t-architect") }
|
|
18070
18472
|
];
|
|
18071
18473
|
const vsCodeDir = vsCodePromptsDir();
|
|
18072
18474
|
if (vsCodeDir) {
|
|
@@ -18075,7 +18477,7 @@ function sidecarLocations() {
|
|
|
18075
18477
|
for (const segments of COPILOT_CLI_SKILLS_ROOTS) {
|
|
18076
18478
|
locations.push({
|
|
18077
18479
|
host: "GitHub Copilot CLI",
|
|
18078
|
-
dir:
|
|
18480
|
+
dir: path19.join(home, ...segments, "m8t-architect")
|
|
18079
18481
|
});
|
|
18080
18482
|
}
|
|
18081
18483
|
return locations;
|
|
@@ -18084,10 +18486,10 @@ function resolveSidecar() {
|
|
|
18084
18486
|
let best = null;
|
|
18085
18487
|
for (const loc of sidecarLocations()) {
|
|
18086
18488
|
for (const filename of [SIDECAR_FILENAME, LEGACY_SIDECAR_FILENAME]) {
|
|
18087
|
-
const candidate2 =
|
|
18489
|
+
const candidate2 = path19.join(loc.dir, filename);
|
|
18088
18490
|
let mtimeMs;
|
|
18089
18491
|
try {
|
|
18090
|
-
mtimeMs =
|
|
18492
|
+
mtimeMs = fs20.statSync(candidate2).mtimeMs;
|
|
18091
18493
|
} catch {
|
|
18092
18494
|
continue;
|
|
18093
18495
|
}
|
|
@@ -18099,13 +18501,13 @@ function resolveSidecar() {
|
|
|
18099
18501
|
return best;
|
|
18100
18502
|
}
|
|
18101
18503
|
function checkArchitectDrift() {
|
|
18102
|
-
const sourcePath =
|
|
18504
|
+
const sourcePath = path19.join(
|
|
18103
18505
|
resolveRepoRoot(),
|
|
18104
18506
|
"personas",
|
|
18105
18507
|
"m8t-architect",
|
|
18106
18508
|
"persona.md"
|
|
18107
18509
|
);
|
|
18108
|
-
if (!
|
|
18510
|
+
if (!fs20.existsSync(sourcePath)) {
|
|
18109
18511
|
return {
|
|
18110
18512
|
match: false,
|
|
18111
18513
|
sourceVersion: "",
|
|
@@ -18118,7 +18520,7 @@ function checkArchitectDrift() {
|
|
|
18118
18520
|
let sidecar = null;
|
|
18119
18521
|
if (resolved) {
|
|
18120
18522
|
try {
|
|
18121
|
-
const raw = JSON.parse(
|
|
18523
|
+
const raw = JSON.parse(fs20.readFileSync(resolved.path, "utf8"));
|
|
18122
18524
|
if (raw !== null && typeof raw === "object") {
|
|
18123
18525
|
const r = raw;
|
|
18124
18526
|
sidecar = {
|
|
@@ -18180,10 +18582,123 @@ var ArchitectCheckCommand = class extends M8tCommand {
|
|
|
18180
18582
|
}
|
|
18181
18583
|
};
|
|
18182
18584
|
|
|
18585
|
+
// src/commands/agent-content/sync.ts
|
|
18586
|
+
import { Command as Command29, Option as Option27 } from "clipanion";
|
|
18587
|
+
import * as path20 from "path";
|
|
18588
|
+
var AgentContentSyncCommand = class extends M8tCommand {
|
|
18589
|
+
static paths = [["agent-content", "sync"]];
|
|
18590
|
+
static usage = Command29.Usage({
|
|
18591
|
+
description: "Materialize the pinned agent repos into personas/ and brain-seeds/.",
|
|
18592
|
+
details: "Reads installer/release-pin.json, fetches each pinned agent repo at its pinned commit, verifies the content against the pin's contentSha, and writes personas/<persona>/ + brain-seeds/<persona>/ under the repo root. Those paths are git-ignored: the agent repos are the source of truth, and this is how a checkout gets a usable copy. Private agent repos need AGENT_CONTENT_TOKEN (or GH_TOKEN / GITHUB_TOKEN / an authed gh). See docs/architecture/AGENT-REPO-CONTRACT.md.",
|
|
18593
|
+
examples: [
|
|
18594
|
+
["Sync into the current checkout", "$0 agent-content sync"],
|
|
18595
|
+
["Sync into an explicit root", "$0 agent-content sync --root /opt/m8t/repo"]
|
|
18596
|
+
]
|
|
18597
|
+
});
|
|
18598
|
+
root = Option27.String("--root", { description: "Repo root to hydrate into. Defaults to the m8t checkout." });
|
|
18599
|
+
async executeCommand() {
|
|
18600
|
+
const root = (typeof this.root === "string" ? this.root : void 0) ?? tryResolveRepoRoot() ?? process.cwd();
|
|
18601
|
+
const synced = await syncAgentContent({
|
|
18602
|
+
root: path20.resolve(root),
|
|
18603
|
+
deps: { env: this.context.env, onNotice: (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
18604
|
+
`) }
|
|
18605
|
+
});
|
|
18606
|
+
if (synced.length === 0) {
|
|
18607
|
+
this.context.stdout.write(`${colors.hint("note:")} the release pin declares no agents \u2014 nothing to sync.
|
|
18608
|
+
`);
|
|
18609
|
+
return 0;
|
|
18610
|
+
}
|
|
18611
|
+
for (const a of synced) {
|
|
18612
|
+
this.context.stdout.write(
|
|
18613
|
+
`${colors.success("\u2713")} ${colors.field(a.persona)} \u2190 ${a.repo}@${a.ref.slice(0, 8)} (${colors.dim(`personas/${a.persona}/, brain-seeds/${a.persona}/`)})
|
|
18614
|
+
`
|
|
18615
|
+
);
|
|
18616
|
+
}
|
|
18617
|
+
return 0;
|
|
18618
|
+
}
|
|
18619
|
+
};
|
|
18620
|
+
|
|
18621
|
+
// src/commands/agent-content/pin.ts
|
|
18622
|
+
import { Command as Command30, Option as Option28 } from "clipanion";
|
|
18623
|
+
import * as fs21 from "fs";
|
|
18624
|
+
import * as path21 from "path";
|
|
18625
|
+
init_errors();
|
|
18626
|
+
var AgentContentPinCommand = class extends M8tCommand {
|
|
18627
|
+
static paths = [["agent-content", "pin"]];
|
|
18628
|
+
static usage = Command30.Usage({
|
|
18629
|
+
description: "Re-derive a pinned agent's ref + contentSha from its branch.",
|
|
18630
|
+
details: "Resolves <key>'s repo branch to an immutable commit, fetches THAT commit, and digests what it actually contains \u2014 so the recorded contentSha describes bytes that were read, not a branch that may have moved. Prints the new entry; --write applies it to installer/release-pin.json. Bumping a pin is a release act: it changes what a from-zero install deploys.",
|
|
18631
|
+
examples: [
|
|
18632
|
+
["Show what ezra's pin would become", "$0 agent-content pin ezra"],
|
|
18633
|
+
["Apply it", "$0 agent-content pin ezra --write"],
|
|
18634
|
+
["Pin a non-default branch", "$0 agent-content pin ezra --branch curated"]
|
|
18635
|
+
]
|
|
18636
|
+
});
|
|
18637
|
+
key = Option28.String();
|
|
18638
|
+
branch = Option28.String("--branch", { description: "Branch to resolve. Default: main." });
|
|
18639
|
+
write = Option28.Boolean("--write", false, { description: "Apply the new entry to installer/release-pin.json." });
|
|
18640
|
+
root = Option28.String("--root");
|
|
18641
|
+
output = Option28.String("--output");
|
|
18642
|
+
async executeCommand() {
|
|
18643
|
+
const key2 = typeof this.key === "string" ? this.key : void 0;
|
|
18644
|
+
if (!key2) throw new LocalCliError({ code: "USAGE", message: "<key> is required (an agents.<key> from the release pin)" });
|
|
18645
|
+
const branch = (typeof this.branch === "string" ? this.branch : void 0) ?? "main";
|
|
18646
|
+
const root = path21.resolve(
|
|
18647
|
+
(typeof this.root === "string" ? this.root : void 0) ?? tryResolveRepoRoot() ?? process.cwd()
|
|
18648
|
+
);
|
|
18649
|
+
const pinPath = path21.join(root, PIN_REL_PATH);
|
|
18650
|
+
const raw = fs21.readFileSync(pinPath, "utf8");
|
|
18651
|
+
const pin = parseReleasePin(raw, pinPath);
|
|
18652
|
+
if (!Object.hasOwn(pin.agents, key2)) {
|
|
18653
|
+
throw new LocalCliError({
|
|
18654
|
+
code: "AGENT_PIN_UNKNOWN",
|
|
18655
|
+
message: `No agents.${key2} in ${PIN_REL_PATH}.`,
|
|
18656
|
+
hint: `Known keys: ${Object.keys(pin.agents).join(", ") || "(none)"}.`
|
|
18657
|
+
});
|
|
18658
|
+
}
|
|
18659
|
+
const current = pin.agents[key2];
|
|
18660
|
+
const next = await derivePin({
|
|
18661
|
+
current,
|
|
18662
|
+
branch,
|
|
18663
|
+
deps: { env: this.context.env, onNotice: (m) => this.context.stderr.write(` ${colors.dim(m)}
|
|
18664
|
+
`) }
|
|
18665
|
+
});
|
|
18666
|
+
if (this.output === "json") {
|
|
18667
|
+
this.context.stdout.write(renderJson({ key: key2, branch, from: current, to: next }) + "\n");
|
|
18668
|
+
} else if (next.ref === current.ref && next.contentSha === current.contentSha && next.persona === current.persona) {
|
|
18669
|
+
this.context.stdout.write(`${colors.success("\u2713")} ${key2} is already pinned to ${branch} (${next.ref.slice(0, 8)}).
|
|
18670
|
+
`);
|
|
18671
|
+
} else {
|
|
18672
|
+
this.context.stdout.write(`${colors.field(key2)} \u2192 ${branch}
|
|
18673
|
+
`);
|
|
18674
|
+
const line2 = (label, a, b) => {
|
|
18675
|
+
this.context.stdout.write(
|
|
18676
|
+
a === b ? ` ${colors.dim(`${label}: ${a} (unchanged)`)}
|
|
18677
|
+
` : ` ${label}: ${colors.dim(a)} \u2192 ${colors.field(b)}
|
|
18678
|
+
`
|
|
18679
|
+
);
|
|
18680
|
+
};
|
|
18681
|
+
line2("ref ", current.ref, next.ref);
|
|
18682
|
+
line2("contentSha", current.contentSha, next.contentSha);
|
|
18683
|
+
line2("persona ", current.persona, next.persona);
|
|
18684
|
+
}
|
|
18685
|
+
if (this.write === true) {
|
|
18686
|
+
const doc = JSON.parse(raw);
|
|
18687
|
+
doc.agents[key2] = { repo: next.repo, ref: next.ref, visibility: next.visibility, persona: next.persona, contentSha: next.contentSha };
|
|
18688
|
+
fs21.writeFileSync(pinPath, JSON.stringify(doc, null, 2) + "\n");
|
|
18689
|
+
this.context.stdout.write(`${colors.success("\u2713")} wrote ${PIN_REL_PATH}
|
|
18690
|
+
`);
|
|
18691
|
+
this.context.stdout.write(` ${colors.hint("note:")} bumping a pin is a release act \u2014 it changes what a from-zero install deploys.
|
|
18692
|
+
`);
|
|
18693
|
+
}
|
|
18694
|
+
return 0;
|
|
18695
|
+
}
|
|
18696
|
+
};
|
|
18697
|
+
|
|
18183
18698
|
// src/commands/coder/deploy.ts
|
|
18184
18699
|
init_esm2();
|
|
18185
|
-
import * as
|
|
18186
|
-
import { Command as
|
|
18700
|
+
import * as path23 from "path";
|
|
18701
|
+
import { Command as Command31, Option as Option29 } from "clipanion";
|
|
18187
18702
|
import { DefaultAzureCredential as DefaultAzureCredential13 } from "@azure/identity";
|
|
18188
18703
|
init_errors();
|
|
18189
18704
|
init_foundry_agent_get();
|
|
@@ -18449,14 +18964,14 @@ async function deployHostedWorker(args) {
|
|
|
18449
18964
|
}
|
|
18450
18965
|
|
|
18451
18966
|
// src/lib/persona.ts
|
|
18452
|
-
import * as
|
|
18453
|
-
import * as
|
|
18454
|
-
import { parse as
|
|
18967
|
+
import * as fs22 from "fs";
|
|
18968
|
+
import * as path22 from "path";
|
|
18969
|
+
import { parse as parseYaml10 } from "yaml";
|
|
18455
18970
|
function findRepoRoot(startDir) {
|
|
18456
|
-
let dir =
|
|
18971
|
+
let dir = path22.resolve(startDir);
|
|
18457
18972
|
for (; ; ) {
|
|
18458
|
-
if (
|
|
18459
|
-
const parent =
|
|
18973
|
+
if (fs22.existsSync(path22.join(dir, "personas"))) return dir;
|
|
18974
|
+
const parent = path22.dirname(dir);
|
|
18460
18975
|
if (parent === dir) return null;
|
|
18461
18976
|
dir = parent;
|
|
18462
18977
|
}
|
|
@@ -18464,10 +18979,10 @@ function findRepoRoot(startDir) {
|
|
|
18464
18979
|
function resolvePersona(persona, cwd = process.cwd()) {
|
|
18465
18980
|
const root = findRepoRoot(cwd);
|
|
18466
18981
|
if (!root) return { persona, personaVersion: null };
|
|
18467
|
-
const file =
|
|
18982
|
+
const file = path22.join(root, "personas", persona, "persona.md");
|
|
18468
18983
|
let raw;
|
|
18469
18984
|
try {
|
|
18470
|
-
raw =
|
|
18985
|
+
raw = fs22.readFileSync(file, "utf-8");
|
|
18471
18986
|
} catch {
|
|
18472
18987
|
return { persona, personaVersion: null };
|
|
18473
18988
|
}
|
|
@@ -18475,7 +18990,7 @@ function resolvePersona(persona, cwd = process.cwd()) {
|
|
|
18475
18990
|
if (!match) return { persona, personaVersion: null };
|
|
18476
18991
|
let fm = null;
|
|
18477
18992
|
try {
|
|
18478
|
-
fm =
|
|
18993
|
+
fm = parseYaml10(match[1]);
|
|
18479
18994
|
} catch {
|
|
18480
18995
|
return { persona, personaVersion: null };
|
|
18481
18996
|
}
|
|
@@ -18611,7 +19126,7 @@ var DEFAULT_MODEL = "gpt-4.1-mini";
|
|
|
18611
19126
|
var NAME_RE = /^[a-z0-9-]+$/;
|
|
18612
19127
|
var CoderDeployCommand = class extends M8tCommand {
|
|
18613
19128
|
static paths = [["coder", "deploy"]];
|
|
18614
|
-
static usage =
|
|
19129
|
+
static usage = Command31.Usage({
|
|
18615
19130
|
description: "Deploy the curated coding agent as a hosted Foundry worker.",
|
|
18616
19131
|
details: "Creates a hosted agent version from a container image (defaults to the public GHCR image ghcr.io/m8t-labs/m8t-coding-agent). Post-2026-06-25 Foundry projects require an authenticated pull, so a public image is staged into the customer's Azure Container Registry first (created if missing, then `az acr import` \u2014 a server-side copy, no local build) and the agent is deployed from that ACR ref. Re-running with a newer --image-tag imports the new tag and rolls out a new agent version \u2014 that is how you update a worker's image. Override --image with a private ACR ref to bring your own registry.",
|
|
18617
19132
|
examples: [
|
|
@@ -18620,22 +19135,22 @@ var CoderDeployCommand = class extends M8tCommand {
|
|
|
18620
19135
|
["Override the exec timeout", "$0 coder deploy my-coder --env M8T_CODER_EXEC_TIMEOUT_SECONDS=300"]
|
|
18621
19136
|
]
|
|
18622
19137
|
});
|
|
18623
|
-
name =
|
|
18624
|
-
persona =
|
|
18625
|
-
image =
|
|
18626
|
-
imageTag =
|
|
18627
|
-
size =
|
|
18628
|
-
modelDeployment =
|
|
18629
|
-
env =
|
|
18630
|
-
endpoint =
|
|
18631
|
-
subscription =
|
|
18632
|
-
output =
|
|
18633
|
-
brain =
|
|
18634
|
-
brainBranch =
|
|
18635
|
-
brainKv =
|
|
18636
|
-
allowNonReasoning =
|
|
18637
|
-
skipQuotaCheck =
|
|
18638
|
-
gatewayUrl =
|
|
19138
|
+
name = Option29.String();
|
|
19139
|
+
persona = Option29.String("--persona");
|
|
19140
|
+
image = Option29.String("--image");
|
|
19141
|
+
imageTag = Option29.String("--image-tag");
|
|
19142
|
+
size = Option29.String("--size");
|
|
19143
|
+
modelDeployment = Option29.String("--model-deployment");
|
|
19144
|
+
env = Option29.Array("--env");
|
|
19145
|
+
endpoint = Option29.String("--endpoint");
|
|
19146
|
+
subscription = Option29.String("--subscription");
|
|
19147
|
+
output = Option29.String("--output");
|
|
19148
|
+
brain = Option29.String("--brain");
|
|
19149
|
+
brainBranch = Option29.String("--branch");
|
|
19150
|
+
brainKv = Option29.String("--brain-kv");
|
|
19151
|
+
allowNonReasoning = Option29.Boolean("--allow-non-reasoning", false);
|
|
19152
|
+
skipQuotaCheck = Option29.Boolean("--skip-quota-check", false);
|
|
19153
|
+
gatewayUrl = Option29.String("--gateway-url", { description: "Base gateway URL for the a2a bridge (e.g. https://<gateway-fqdn>); enables this coder as an a2a target." });
|
|
18639
19154
|
async executeCommand() {
|
|
18640
19155
|
if (!NAME_RE.test(this.name)) {
|
|
18641
19156
|
throw new LocalCliError({
|
|
@@ -18824,7 +19339,7 @@ var CoderDeployCommand = class extends M8tCommand {
|
|
|
18824
19339
|
projectEndpoint: project.endpoint,
|
|
18825
19340
|
projectArmId: `${project.accountScope}/projects/${project.projectName}`,
|
|
18826
19341
|
agentName: this.name,
|
|
18827
|
-
personaPath:
|
|
19342
|
+
personaPath: path23.join(resolveRepoRoot(), "personas", personaName, "persona.md"),
|
|
18828
19343
|
bridgeUrl,
|
|
18829
19344
|
onProgress
|
|
18830
19345
|
});
|
|
@@ -18869,13 +19384,13 @@ var CoderDeployCommand = class extends M8tCommand {
|
|
|
18869
19384
|
|
|
18870
19385
|
// src/commands/coder/teardown.ts
|
|
18871
19386
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
18872
|
-
import { Command as
|
|
19387
|
+
import { Command as Command32, Option as Option30 } from "clipanion";
|
|
18873
19388
|
import { DefaultAzureCredential as DefaultAzureCredential14 } from "@azure/identity";
|
|
18874
19389
|
init_foundry_agents();
|
|
18875
19390
|
init_errors();
|
|
18876
19391
|
var CoderTeardownCommand = class extends M8tCommand {
|
|
18877
19392
|
static paths = [["coder", "teardown"]];
|
|
18878
|
-
static usage =
|
|
19393
|
+
static usage = Command32.Usage({
|
|
18879
19394
|
description: "Delete a deployed hosted coder (removes its container + identity + role assignment).",
|
|
18880
19395
|
details: "Idempotent: tearing down a missing coder reports 'already gone'. Only the named agent is touched. Discovery self-heals on the next worker list.",
|
|
18881
19396
|
examples: [
|
|
@@ -18883,11 +19398,11 @@ var CoderTeardownCommand = class extends M8tCommand {
|
|
|
18883
19398
|
["Skip the confirm (scripts)", "$0 coder teardown my-coder --yes"]
|
|
18884
19399
|
]
|
|
18885
19400
|
});
|
|
18886
|
-
name =
|
|
18887
|
-
yes =
|
|
18888
|
-
endpoint =
|
|
18889
|
-
subscription =
|
|
18890
|
-
output =
|
|
19401
|
+
name = Option30.String();
|
|
19402
|
+
yes = Option30.Boolean("--yes", false);
|
|
19403
|
+
endpoint = Option30.String("--endpoint");
|
|
19404
|
+
subscription = Option30.String("--subscription");
|
|
19405
|
+
output = Option30.String("--output");
|
|
18891
19406
|
async executeCommand() {
|
|
18892
19407
|
const mode = resolveOutputMode(
|
|
18893
19408
|
this.output,
|
|
@@ -18951,8 +19466,8 @@ var CoderTeardownCommand = class extends M8tCommand {
|
|
|
18951
19466
|
|
|
18952
19467
|
// src/commands/azure-exec/deploy.ts
|
|
18953
19468
|
init_esm2();
|
|
18954
|
-
import * as
|
|
18955
|
-
import { Command as
|
|
19469
|
+
import * as path24 from "path";
|
|
19470
|
+
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
18956
19471
|
import { DefaultAzureCredential as DefaultAzureCredential15 } from "@azure/identity";
|
|
18957
19472
|
init_errors();
|
|
18958
19473
|
init_foundry_agent_get();
|
|
@@ -19014,7 +19529,7 @@ var DEFAULT_MODEL2 = "gpt-5-mini";
|
|
|
19014
19529
|
var NAME_RE2 = /^[a-z0-9-]+$/;
|
|
19015
19530
|
var AzureExecDeployCommand = class extends M8tCommand {
|
|
19016
19531
|
static paths = [["azure-exec", "deploy"]];
|
|
19017
|
-
static usage =
|
|
19532
|
+
static usage = Command33.Usage({
|
|
19018
19533
|
description: "Deploy the Azure executor as a hosted Foundry worker (az CLI + tiered ops).",
|
|
19019
19534
|
details: "Creates a hosted agent version from a container image (defaults to the public GHCR image ghcr.io/m8t-labs/m8t-azure-executor \u2014 no local build needed), grants its identity Foundry User + Contributor (at --scope) + Key Vault Secrets User (brain KV), polls to active, and a2a-enables it as a target. Override with --image/--image-tag for a bring-your-own-registry image (e.g. a private ACR ref, which must be pushed first). Contributor scope is REQUIRED \u2014 pass --scope or --resource-group. Pass --grant-access-admin to additionally grant User Access Administrator (enables human-approved Tier-2 role/delete ops). Post-2026-06-25 Foundry projects require an authenticated pull, so the public image (default ghcr.io/m8t-labs/m8t-azure-executor) is staged into the customer's Azure Container Registry first (created if missing, then `az acr import` \u2014 server-side, no local build) and deployed from that ACR ref; re-running with a newer --image-tag updates the image.",
|
|
19020
19535
|
examples: [
|
|
@@ -19024,23 +19539,23 @@ var AzureExecDeployCommand = class extends M8tCommand {
|
|
|
19024
19539
|
]
|
|
19025
19540
|
]
|
|
19026
19541
|
});
|
|
19027
|
-
name =
|
|
19028
|
-
image =
|
|
19029
|
-
imageTag =
|
|
19030
|
-
size =
|
|
19031
|
-
scope =
|
|
19032
|
-
resourceGroup =
|
|
19033
|
-
modelDeployment =
|
|
19034
|
-
env =
|
|
19035
|
-
brain =
|
|
19036
|
-
kvUri =
|
|
19037
|
-
gatewayUrl =
|
|
19038
|
-
endpoint =
|
|
19039
|
-
subscription =
|
|
19040
|
-
output =
|
|
19041
|
-
skipQuotaCheck =
|
|
19042
|
-
grantAccessAdmin =
|
|
19043
|
-
enableEmail =
|
|
19542
|
+
name = Option31.String();
|
|
19543
|
+
image = Option31.String("--image");
|
|
19544
|
+
imageTag = Option31.String("--image-tag");
|
|
19545
|
+
size = Option31.String("--size");
|
|
19546
|
+
scope = Option31.String("--scope");
|
|
19547
|
+
resourceGroup = Option31.String("--resource-group");
|
|
19548
|
+
modelDeployment = Option31.String("--model-deployment");
|
|
19549
|
+
env = Option31.Array("--env");
|
|
19550
|
+
brain = Option31.String("--brain");
|
|
19551
|
+
kvUri = Option31.String("--kv-uri");
|
|
19552
|
+
gatewayUrl = Option31.String("--gateway-url");
|
|
19553
|
+
endpoint = Option31.String("--endpoint");
|
|
19554
|
+
subscription = Option31.String("--subscription");
|
|
19555
|
+
output = Option31.String("--output");
|
|
19556
|
+
skipQuotaCheck = Option31.Boolean("--skip-quota-check", false);
|
|
19557
|
+
grantAccessAdmin = Option31.Boolean("--grant-access-admin", false);
|
|
19558
|
+
enableEmail = Option31.Boolean("--enable-email", false);
|
|
19044
19559
|
async executeCommand() {
|
|
19045
19560
|
if (!NAME_RE2.test(this.name)) {
|
|
19046
19561
|
throw new LocalCliError({
|
|
@@ -19227,7 +19742,7 @@ var AzureExecDeployCommand = class extends M8tCommand {
|
|
|
19227
19742
|
kvUri
|
|
19228
19743
|
});
|
|
19229
19744
|
onProgress?.("a2a-enabling as a target\u2026");
|
|
19230
|
-
const personaPath =
|
|
19745
|
+
const personaPath = path24.join(resolveRepoRoot(), "personas", "azure-executor", "persona.md");
|
|
19231
19746
|
const bridgeUrl = resolveBridgeUrl({
|
|
19232
19747
|
flag: typeof this.gatewayUrl === "string" ? this.gatewayUrl : void 0,
|
|
19233
19748
|
env: processEnv
|
|
@@ -19285,7 +19800,7 @@ var AzureExecDeployCommand = class extends M8tCommand {
|
|
|
19285
19800
|
};
|
|
19286
19801
|
|
|
19287
19802
|
// src/commands/platform/status.ts
|
|
19288
|
-
import { Command as
|
|
19803
|
+
import { Command as Command34, Option as Option32 } from "clipanion";
|
|
19289
19804
|
import { DefaultAzureCredential as DefaultAzureCredential16 } from "@azure/identity";
|
|
19290
19805
|
|
|
19291
19806
|
// src/lib/platform-update.ts
|
|
@@ -19393,8 +19908,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
19393
19908
|
hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
|
|
19394
19909
|
});
|
|
19395
19910
|
}
|
|
19396
|
-
const
|
|
19397
|
-
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${
|
|
19911
|
+
const path48 = repo.replace(/^ghcr\.io\//, "");
|
|
19912
|
+
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path48}:pull`);
|
|
19398
19913
|
if (!tokenRes.ok) {
|
|
19399
19914
|
throw new LocalCliError({
|
|
19400
19915
|
code: "PLATFORM_GHCR_TOKEN_FAILED",
|
|
@@ -19410,7 +19925,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
19410
19925
|
hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
|
|
19411
19926
|
});
|
|
19412
19927
|
}
|
|
19413
|
-
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${
|
|
19928
|
+
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path48}/tags/list`, {
|
|
19414
19929
|
headers: { Authorization: `Bearer ${token}` }
|
|
19415
19930
|
});
|
|
19416
19931
|
if (!tagsRes.ok) {
|
|
@@ -19424,7 +19939,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
19424
19939
|
}
|
|
19425
19940
|
|
|
19426
19941
|
// src/lib/release-channel.ts
|
|
19427
|
-
import * as
|
|
19942
|
+
import * as fs23 from "fs";
|
|
19428
19943
|
|
|
19429
19944
|
// ../../packages/platform-release/dist/esm/manifest.js
|
|
19430
19945
|
var SEVERITIES = ["critical", "recommended", "optional"];
|
|
@@ -19647,7 +20162,7 @@ function manifestSourceForVersionTag(channelUrl, version) {
|
|
|
19647
20162
|
return { url: custom };
|
|
19648
20163
|
}
|
|
19649
20164
|
async function fetchManifest(source, deps = {}) {
|
|
19650
|
-
const readFile9 = deps.readFile ?? ((p) =>
|
|
20165
|
+
const readFile9 = deps.readFile ?? ((p) => fs23.readFileSync(p, "utf8"));
|
|
19651
20166
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
19652
20167
|
const ghToken = deps.ghToken ?? getGhToken;
|
|
19653
20168
|
let raw;
|
|
@@ -19693,10 +20208,9 @@ async function fetchManifest(source, deps = {}) {
|
|
|
19693
20208
|
}
|
|
19694
20209
|
|
|
19695
20210
|
// src/lib/release-content.ts
|
|
19696
|
-
import * as
|
|
19697
|
-
import * as
|
|
19698
|
-
import * as
|
|
19699
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
20211
|
+
import * as fs24 from "fs";
|
|
20212
|
+
import * as os9 from "os";
|
|
20213
|
+
import * as path25 from "path";
|
|
19700
20214
|
init_errors();
|
|
19701
20215
|
var OWNER_REPO = "m8t-labs/m8t";
|
|
19702
20216
|
function parseTreeResponse(body) {
|
|
@@ -19725,8 +20239,13 @@ async function fetchRepoTree(commit, deps = {}) {
|
|
|
19725
20239
|
function verifyPersonaTrees(manifest, tree) {
|
|
19726
20240
|
const out = [];
|
|
19727
20241
|
for (const [name, item] of Object.entries(manifest.components.personas.items)) {
|
|
20242
|
+
if (item.source) continue;
|
|
19728
20243
|
const actual = tree.treeShaOf(item.path);
|
|
19729
|
-
if (actual
|
|
20244
|
+
if (actual === void 0) {
|
|
20245
|
+
out.push(
|
|
20246
|
+
`persona '${name}' (${item.path}): the manifest claims it is in the platform repo at ${manifest.platform.commit}, but that commit's tree has no such path`
|
|
20247
|
+
);
|
|
20248
|
+
} else if (actual !== item.treeSha) {
|
|
19730
20249
|
out.push(`persona '${name}' (${item.path}): manifest pins ${item.treeSha} but commit tree has ${actual}`);
|
|
19731
20250
|
}
|
|
19732
20251
|
}
|
|
@@ -19734,15 +20253,15 @@ function verifyPersonaTrees(manifest, tree) {
|
|
|
19734
20253
|
}
|
|
19735
20254
|
async function resolveReleaseContent(manifest, opts = {}) {
|
|
19736
20255
|
if (opts.contentDir) {
|
|
19737
|
-
if (!
|
|
20256
|
+
if (!fs24.existsSync(path25.join(opts.contentDir, "personas"))) {
|
|
19738
20257
|
throw new LocalCliError({ code: "PLATFORM_CONTENT_DIR_INVALID", message: `--content-dir '${opts.contentDir}' has no personas/ \u2014 not a repo checkout.` });
|
|
19739
20258
|
}
|
|
19740
20259
|
return opts.contentDir;
|
|
19741
20260
|
}
|
|
19742
20261
|
const commit = manifest.platform.commit;
|
|
19743
|
-
const cacheRoot = opts.cacheRoot ??
|
|
19744
|
-
const dest =
|
|
19745
|
-
if (
|
|
20262
|
+
const cacheRoot = opts.cacheRoot ?? path25.join(os9.homedir(), ".m8t", "cache");
|
|
20263
|
+
const dest = path25.join(cacheRoot, `release-${commit}`);
|
|
20264
|
+
if (fs24.existsSync(path25.join(dest, "personas"))) return dest;
|
|
19746
20265
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
19747
20266
|
const ghToken = opts.ghToken ?? getGhToken;
|
|
19748
20267
|
let token;
|
|
@@ -19756,33 +20275,29 @@ async function resolveReleaseContent(manifest, opts = {}) {
|
|
|
19756
20275
|
if (!res.ok) {
|
|
19757
20276
|
throw new LocalCliError({ code: "PLATFORM_TARBALL_FETCH_FAILED", message: `GET ${url} \u2192 HTTP ${res.status.toString()}.` });
|
|
19758
20277
|
}
|
|
19759
|
-
|
|
19760
|
-
const tmp =
|
|
20278
|
+
fs24.mkdirSync(cacheRoot, { recursive: true });
|
|
20279
|
+
const tmp = fs24.mkdtempSync(path25.join(cacheRoot, `.dl-${commit.slice(0, 8)}-`));
|
|
19761
20280
|
try {
|
|
19762
|
-
const tgz =
|
|
19763
|
-
|
|
19764
|
-
const unpacked =
|
|
19765
|
-
|
|
20281
|
+
const tgz = path25.join(tmp, "src.tar.gz");
|
|
20282
|
+
fs24.writeFileSync(tgz, Buffer.from(await res.arrayBuffer()));
|
|
20283
|
+
const unpacked = path25.join(tmp, "unpacked");
|
|
20284
|
+
fs24.mkdirSync(unpacked);
|
|
19766
20285
|
extractTarGz(tgz, unpacked);
|
|
19767
|
-
|
|
19768
|
-
|
|
19769
|
-
|
|
19770
|
-
|
|
19771
|
-
|
|
19772
|
-
|
|
19773
|
-
throw new LocalCliError({
|
|
19774
|
-
code: "PLATFORM_CACHE_PUBLISH_FAILED",
|
|
19775
|
-
message: `Could not publish release cache to ${dest}: ${err.message}`
|
|
19776
|
-
});
|
|
19777
|
-
}
|
|
19778
|
-
}
|
|
20286
|
+
publishAtomically({
|
|
20287
|
+
from: unpacked,
|
|
20288
|
+
dest,
|
|
20289
|
+
sentinel: "personas",
|
|
20290
|
+
errorCode: "PLATFORM_CACHE_PUBLISH_FAILED"
|
|
20291
|
+
});
|
|
19779
20292
|
} finally {
|
|
19780
|
-
|
|
20293
|
+
fs24.rmSync(tmp, { recursive: true, force: true });
|
|
19781
20294
|
}
|
|
19782
20295
|
return dest;
|
|
19783
20296
|
}
|
|
19784
|
-
function
|
|
19785
|
-
|
|
20297
|
+
async function resolveReleaseContentWithAgents(manifest, opts = {}) {
|
|
20298
|
+
const contentDir = await resolveReleaseContent(manifest, opts);
|
|
20299
|
+
await hydrateAgentsInto(contentDir, manifest.components.agents ?? {}, opts.agentDeps);
|
|
20300
|
+
return contentDir;
|
|
19786
20301
|
}
|
|
19787
20302
|
|
|
19788
20303
|
// src/lib/platform-stamp.ts
|
|
@@ -19934,14 +20449,14 @@ async function readStamp(opts) {
|
|
|
19934
20449
|
|
|
19935
20450
|
// src/lib/platform-converge.ts
|
|
19936
20451
|
init_esm();
|
|
19937
|
-
import * as
|
|
19938
|
-
import * as
|
|
19939
|
-
import { parse as
|
|
20452
|
+
import * as fs26 from "fs";
|
|
20453
|
+
import * as path27 from "path";
|
|
20454
|
+
import { parse as parseYaml11 } from "yaml";
|
|
19940
20455
|
|
|
19941
20456
|
// src/lib/deploy.ts
|
|
19942
|
-
import * as
|
|
19943
|
-
import * as
|
|
19944
|
-
import * as
|
|
20457
|
+
import * as fs25 from "fs/promises";
|
|
20458
|
+
import * as os10 from "os";
|
|
20459
|
+
import * as path26 from "path";
|
|
19945
20460
|
init_errors();
|
|
19946
20461
|
var FOUNDRY_TRACING_MODES = ["project", "account", "skip"];
|
|
19947
20462
|
function parseFoundryTracingMode(v) {
|
|
@@ -19999,10 +20514,10 @@ function buildBicepParams(p) {
|
|
|
19999
20514
|
if (p.examApiBase) params.push(`examApiBase=${p.examApiBase}`);
|
|
20000
20515
|
return params;
|
|
20001
20516
|
}
|
|
20002
|
-
async function resolveRepoRoot2(home =
|
|
20003
|
-
const marker =
|
|
20517
|
+
async function resolveRepoRoot2(home = os10.homedir()) {
|
|
20518
|
+
const marker = path26.join(home, ".m8t", "repo-root");
|
|
20004
20519
|
try {
|
|
20005
|
-
const root = (await
|
|
20520
|
+
const root = (await fs25.readFile(marker, "utf8")).trim();
|
|
20006
20521
|
if (!root) throw new Error("empty");
|
|
20007
20522
|
return root;
|
|
20008
20523
|
} catch {
|
|
@@ -20088,7 +20603,7 @@ function buildDeployResult(args) {
|
|
|
20088
20603
|
};
|
|
20089
20604
|
}
|
|
20090
20605
|
async function runBicepDeployment(opts) {
|
|
20091
|
-
const bicepPath =
|
|
20606
|
+
const bicepPath = path26.join(opts.repoRoot, "deploy", "main.bicep");
|
|
20092
20607
|
const result2 = JSON.parse(
|
|
20093
20608
|
await runAz([
|
|
20094
20609
|
"deployment",
|
|
@@ -20455,14 +20970,14 @@ async function applyAgentImage(a, ctx, opts) {
|
|
|
20455
20970
|
function declaredFillableFields(personaPath) {
|
|
20456
20971
|
let raw;
|
|
20457
20972
|
try {
|
|
20458
|
-
raw =
|
|
20973
|
+
raw = fs26.readFileSync(personaPath, "utf8");
|
|
20459
20974
|
} catch {
|
|
20460
20975
|
return [];
|
|
20461
20976
|
}
|
|
20462
20977
|
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
20463
20978
|
if (!fmMatch) return [];
|
|
20464
20979
|
try {
|
|
20465
|
-
const fm =
|
|
20980
|
+
const fm = parseYaml11(fmMatch[1]);
|
|
20466
20981
|
return fm["fillable-fields"] ?? [];
|
|
20467
20982
|
} catch {
|
|
20468
20983
|
return [];
|
|
@@ -20481,7 +20996,7 @@ function injectAndRender(personaPath, values, agentName) {
|
|
|
20481
20996
|
function loaderText(loaderPath) {
|
|
20482
20997
|
if (!loaderPath) return null;
|
|
20483
20998
|
try {
|
|
20484
|
-
return
|
|
20999
|
+
return fs26.readFileSync(loaderPath, "utf8");
|
|
20485
21000
|
} catch {
|
|
20486
21001
|
return null;
|
|
20487
21002
|
}
|
|
@@ -20498,7 +21013,7 @@ function brainRepoOf(current) {
|
|
|
20498
21013
|
}
|
|
20499
21014
|
async function applyPersona(a, ctx, opts) {
|
|
20500
21015
|
const current = await getAgentVersion({ credential: ctx.credential, projectEndpoint: opts.project.endpoint, agentName: opts.agentName });
|
|
20501
|
-
const personaPath =
|
|
21016
|
+
const personaPath = path27.join(opts.personaDir, "persona.md");
|
|
20502
21017
|
const yamlValues = readAgentYaml(opts.agentName)?.fillableFieldValues ?? null;
|
|
20503
21018
|
const values = resolveFillableValues(current, yamlValues);
|
|
20504
21019
|
const declared = declaredFillableFields(personaPath);
|
|
@@ -20633,30 +21148,30 @@ async function readGatewayVersion(gatewayUrl, fetchImpl = fetch) {
|
|
|
20633
21148
|
// src/commands/platform/status.ts
|
|
20634
21149
|
var PlatformStatusCommand = class extends M8tCommand {
|
|
20635
21150
|
static paths = [["platform", "status"]];
|
|
20636
|
-
static usage =
|
|
21151
|
+
static usage = Command34.Usage({
|
|
20637
21152
|
description: "Show per-component platform drift against a release manifest.",
|
|
20638
21153
|
details: "Fetches a release manifest (the current channel by default), reads the installed-state stamp, and reports each component's installed vs target version and drift status. Read-only \u2014 never writes the stamp, never converges anything (use 'm8t platform update' for that). --verify additionally probes the live gateway (image tag + /api/version) best-effort."
|
|
20639
21154
|
});
|
|
20640
|
-
subscription =
|
|
20641
|
-
resourceGroup =
|
|
21155
|
+
subscription = Option32.String("--subscription");
|
|
21156
|
+
resourceGroup = Option32.String("--resource-group", {
|
|
20642
21157
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
20643
21158
|
});
|
|
20644
|
-
to =
|
|
21159
|
+
to = Option32.String("--to", {
|
|
20645
21160
|
description: "Check status against a pinned platform version (defaults to the current release channel)."
|
|
20646
21161
|
});
|
|
20647
|
-
contentDir =
|
|
21162
|
+
contentDir = Option32.String("--content-dir", {
|
|
20648
21163
|
description: "Path to a local m8t repo checkout (skips the tarball tree-SHA fetch)."
|
|
20649
21164
|
});
|
|
20650
|
-
manifestUrl =
|
|
21165
|
+
manifestUrl = Option32.String("--manifest-url", {
|
|
20651
21166
|
description: "Fetch the release manifest from this URL instead of the release channel."
|
|
20652
21167
|
});
|
|
20653
|
-
manifestFile =
|
|
21168
|
+
manifestFile = Option32.String("--manifest-file", {
|
|
20654
21169
|
description: "Read the release manifest from this local file instead of the release channel."
|
|
20655
21170
|
});
|
|
20656
|
-
verify =
|
|
21171
|
+
verify = Option32.Boolean("--verify", false, {
|
|
20657
21172
|
description: "Additionally probe the live gateway (image tag + /api/version) and report stamp-vs-live."
|
|
20658
21173
|
});
|
|
20659
|
-
output =
|
|
21174
|
+
output = Option32.String("--output");
|
|
20660
21175
|
async executeCommand() {
|
|
20661
21176
|
const mode = resolveOutputMode(
|
|
20662
21177
|
this.output,
|
|
@@ -20769,12 +21284,12 @@ var PlatformStatusCommand = class extends M8tCommand {
|
|
|
20769
21284
|
};
|
|
20770
21285
|
|
|
20771
21286
|
// src/commands/platform/update.ts
|
|
20772
|
-
import { Command as
|
|
21287
|
+
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
20773
21288
|
import { confirm as confirm5 } from "@inquirer/prompts";
|
|
20774
21289
|
import { DefaultAzureCredential as DefaultAzureCredential17 } from "@azure/identity";
|
|
20775
21290
|
|
|
20776
21291
|
// src/lib/platform-converge-cli.ts
|
|
20777
|
-
import * as
|
|
21292
|
+
import * as path31 from "path";
|
|
20778
21293
|
|
|
20779
21294
|
// src/lib/platform-infra-params.ts
|
|
20780
21295
|
import { TableClient as TableClient3 } from "@azure/data-tables";
|
|
@@ -20859,17 +21374,18 @@ init_errors();
|
|
|
20859
21374
|
|
|
20860
21375
|
// src/lib/seed-refresh-driver.ts
|
|
20861
21376
|
init_esm2();
|
|
20862
|
-
import * as
|
|
21377
|
+
import * as fs29 from "fs";
|
|
21378
|
+
import * as path30 from "path";
|
|
20863
21379
|
|
|
20864
21380
|
// src/lib/seed-refresh.ts
|
|
20865
21381
|
init_errors();
|
|
20866
|
-
import * as
|
|
20867
|
-
import * as
|
|
21382
|
+
import * as fs28 from "fs";
|
|
21383
|
+
import * as path29 from "path";
|
|
20868
21384
|
|
|
20869
21385
|
// src/lib/seed-merge.ts
|
|
20870
|
-
import * as
|
|
20871
|
-
import * as
|
|
20872
|
-
import * as
|
|
21386
|
+
import * as fs27 from "fs";
|
|
21387
|
+
import * as os11 from "os";
|
|
21388
|
+
import * as path28 from "path";
|
|
20873
21389
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
20874
21390
|
function isProbablyText(s) {
|
|
20875
21391
|
return !s.includes("\0");
|
|
@@ -20879,14 +21395,14 @@ function mergeThreeWay(ours, base, theirs) {
|
|
|
20879
21395
|
return { ok: false, merged: "" };
|
|
20880
21396
|
}
|
|
20881
21397
|
const lf = (s) => s.replace(/\r\n/g, "\n");
|
|
20882
|
-
const dir =
|
|
21398
|
+
const dir = fs27.mkdtempSync(path28.join(os11.tmpdir(), "seed-merge-"));
|
|
20883
21399
|
try {
|
|
20884
|
-
const o =
|
|
20885
|
-
const b =
|
|
20886
|
-
const t =
|
|
20887
|
-
|
|
20888
|
-
|
|
20889
|
-
|
|
21400
|
+
const o = path28.join(dir, "ours");
|
|
21401
|
+
const b = path28.join(dir, "base");
|
|
21402
|
+
const t = path28.join(dir, "theirs");
|
|
21403
|
+
fs27.writeFileSync(o, lf(ours));
|
|
21404
|
+
fs27.writeFileSync(b, lf(base));
|
|
21405
|
+
fs27.writeFileSync(t, lf(theirs));
|
|
20890
21406
|
const res = spawnSync3(
|
|
20891
21407
|
"git",
|
|
20892
21408
|
["merge-file", "-p", "--diff3", o, b, t],
|
|
@@ -20900,28 +21416,28 @@ function mergeThreeWay(ours, base, theirs) {
|
|
|
20900
21416
|
}
|
|
20901
21417
|
return { ok: false, merged: "" };
|
|
20902
21418
|
} finally {
|
|
20903
|
-
|
|
21419
|
+
fs27.rmSync(dir, { recursive: true, force: true });
|
|
20904
21420
|
}
|
|
20905
21421
|
}
|
|
20906
|
-
var skip = (
|
|
21422
|
+
var skip = (path48, skipReason) => ({ path: path48, kind: "skip", skipReason });
|
|
20907
21423
|
function classifyCandidate(c) {
|
|
20908
|
-
const { path:
|
|
20909
|
-
if (theirs === base) return { path:
|
|
21424
|
+
const { path: path48, base, ours, theirs } = c;
|
|
21425
|
+
if (theirs === base) return { path: path48, kind: "noop" };
|
|
20910
21426
|
if (base === null) {
|
|
20911
|
-
if (theirs === null) return { path:
|
|
20912
|
-
if (ours === null) return { path:
|
|
20913
|
-
if (ours === theirs) return { path:
|
|
20914
|
-
return skip(
|
|
21427
|
+
if (theirs === null) return { path: path48, kind: "noop" };
|
|
21428
|
+
if (ours === null) return { path: path48, kind: "add", content: theirs };
|
|
21429
|
+
if (ours === theirs) return { path: path48, kind: "noop" };
|
|
21430
|
+
return skip(path48, "you already have a file at this path");
|
|
20915
21431
|
}
|
|
20916
21432
|
if (ours === null) {
|
|
20917
|
-
return theirs === null ? { path:
|
|
21433
|
+
return theirs === null ? { path: path48, kind: "noop" } : skip(path48, "you removed this file; upstream changed it");
|
|
20918
21434
|
}
|
|
20919
21435
|
if (theirs === null) {
|
|
20920
|
-
return ours === base ? { path:
|
|
21436
|
+
return ours === base ? { path: path48, kind: "delete" } : skip(path48, "you edited this file; upstream removed it");
|
|
20921
21437
|
}
|
|
20922
|
-
if (ours === base) return { path:
|
|
21438
|
+
if (ours === base) return { path: path48, kind: "update", content: theirs };
|
|
20923
21439
|
const m = mergeThreeWay(ours, base, theirs);
|
|
20924
|
-
return m.ok ? { path:
|
|
21440
|
+
return m.ok ? { path: path48, kind: "update", content: m.merged } : skip(path48, "your edits conflict with the update");
|
|
20925
21441
|
}
|
|
20926
21442
|
|
|
20927
21443
|
// src/lib/seed-pr-body.ts
|
|
@@ -20956,8 +21472,8 @@ function composeSeedPrBody(s) {
|
|
|
20956
21472
|
// src/lib/seed-refresh.ts
|
|
20957
21473
|
function readIf(dir, rel) {
|
|
20958
21474
|
if (!dir) return null;
|
|
20959
|
-
const abs =
|
|
20960
|
-
return
|
|
21475
|
+
const abs = path29.join(dir, rel);
|
|
21476
|
+
return fs28.existsSync(abs) ? fs28.readFileSync(abs, "utf8") : null;
|
|
20961
21477
|
}
|
|
20962
21478
|
async function assembleCandidates(args) {
|
|
20963
21479
|
const paths = /* @__PURE__ */ new Set([...args.lineage?.ownedPaths ?? [], ...args.trees.ownedAtTheirs]);
|
|
@@ -21089,7 +21605,8 @@ function dedupeBrainRepos(agents) {
|
|
|
21089
21605
|
function seedContentDir(contentRoot, manifest, seedName) {
|
|
21090
21606
|
const items = manifest.components.brainSeeds.items;
|
|
21091
21607
|
if (!Object.hasOwn(items, seedName)) return null;
|
|
21092
|
-
|
|
21608
|
+
const dir = path30.join(contentRoot, items[seedName].path);
|
|
21609
|
+
return fs29.existsSync(dir) ? dir : null;
|
|
21093
21610
|
}
|
|
21094
21611
|
function bindRepoApi(token, repo) {
|
|
21095
21612
|
return {
|
|
@@ -21106,12 +21623,12 @@ function bindRepoApi(token, repo) {
|
|
|
21106
21623
|
openPr: (head, base, title, body) => openPullRequestViaApp({ token, repo, base, head, title, body }),
|
|
21107
21624
|
updatePr: (n, title, body) => updatePullRequestViaApp({ token, repo, number: n, title, body }),
|
|
21108
21625
|
closePr: (n, comment) => closePullRequestViaApp({ token, repo, number: n, comment }),
|
|
21109
|
-
readFileAtRef: (
|
|
21626
|
+
readFileAtRef: (path48, ref) => readRepoFileViaApp({ token, repo, path: path48, ref })
|
|
21110
21627
|
};
|
|
21111
21628
|
}
|
|
21112
21629
|
async function runSeedRefresh(args) {
|
|
21113
21630
|
const targetCommit = args.manifest.platform.commit;
|
|
21114
|
-
const theirsRoot = await
|
|
21631
|
+
const theirsRoot = await resolveReleaseContentWithAgents(args.manifest, {});
|
|
21115
21632
|
const repos = dedupeBrainRepos(args.agents);
|
|
21116
21633
|
const results = [];
|
|
21117
21634
|
let delivered = 0;
|
|
@@ -21148,6 +21665,7 @@ async function runSeedRefresh(args) {
|
|
|
21148
21665
|
const advisorLabel = ADVISOR_LABEL[seedName] ?? seedName;
|
|
21149
21666
|
let lineage = marker?.seeds.find((s) => s.name === seedName) ?? null;
|
|
21150
21667
|
let baseDir = null;
|
|
21668
|
+
const agentSourced = Boolean(args.manifest.components.agents && Object.values(args.manifest.components.agents).some((a) => a.persona === seedName));
|
|
21151
21669
|
if (lineage) {
|
|
21152
21670
|
try {
|
|
21153
21671
|
const baseRoot = await resolveReleaseContent(
|
|
@@ -21175,6 +21693,11 @@ async function runSeedRefresh(args) {
|
|
|
21175
21693
|
}
|
|
21176
21694
|
}
|
|
21177
21695
|
}
|
|
21696
|
+
if (agentSourced && !baseDir) {
|
|
21697
|
+
args.onWarn?.(
|
|
21698
|
+
`${ref.repo}: seed '${seedName}' comes from an agent repo and no merge base could be resolved \u2014 new files are delivered, files you already have are skipped and never overwritten.`
|
|
21699
|
+
);
|
|
21700
|
+
}
|
|
21178
21701
|
const trees = { baseDir, theirsDir, ownedAtTheirs: deriveOwnedPaths(theirsDir) };
|
|
21179
21702
|
const source = { readOurs: (p) => readRepoFileViaApp({ token, repo: ref.repo, path: p }) };
|
|
21180
21703
|
const res = await refreshBrainRepo({
|
|
@@ -21219,7 +21742,7 @@ async function buildConvergeDeps(args) {
|
|
|
21219
21742
|
const persona = a.metadata.persona;
|
|
21220
21743
|
if (persona) discovered[persona] = a.name;
|
|
21221
21744
|
}
|
|
21222
|
-
const loaderPath =
|
|
21745
|
+
const loaderPath = path31.join(args.contentDir, "targets/foundry/brain-loader.md");
|
|
21223
21746
|
return {
|
|
21224
21747
|
async applyPersona(a, ctx) {
|
|
21225
21748
|
const personaName = a.personaName;
|
|
@@ -21235,7 +21758,7 @@ async function buildConvergeDeps(args) {
|
|
|
21235
21758
|
return applyPersona(a, ctx, {
|
|
21236
21759
|
agentName,
|
|
21237
21760
|
project: args.project,
|
|
21238
|
-
personaDir:
|
|
21761
|
+
personaDir: path31.join(ctx.contentDir, personaItem.path),
|
|
21239
21762
|
loaderPath
|
|
21240
21763
|
});
|
|
21241
21764
|
},
|
|
@@ -21417,40 +21940,40 @@ function assertCliVersionOk(manifest, runningVersion, onWarn = () => {
|
|
|
21417
21940
|
// src/commands/platform/update.ts
|
|
21418
21941
|
var PlatformUpdateCommand = class extends M8tCommand {
|
|
21419
21942
|
static paths = [["platform", "update"]];
|
|
21420
|
-
static usage =
|
|
21943
|
+
static usage = Command35.Usage({
|
|
21421
21944
|
description: "Converge the whole platform (gateway, hosted agents, personas, infra) to a release manifest.",
|
|
21422
21945
|
details: "Fetches a release manifest (the current channel by default), diffs it against the installed-state stamp, and converges each changed component via its own executor: rolls the gateway/hosted-agent images, re-versions prompt personas (compose-preserving), and records infra state. A deploy/ tree drift is reported but NOT applied unless --force-infra is passed. Use --check to preview, --only to converge a single component, --yes to skip the confirmation prompt."
|
|
21423
21946
|
});
|
|
21424
|
-
subscription =
|
|
21425
|
-
resourceGroup =
|
|
21947
|
+
subscription = Option33.String("--subscription");
|
|
21948
|
+
resourceGroup = Option33.String("--resource-group", {
|
|
21426
21949
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
21427
21950
|
});
|
|
21428
|
-
to =
|
|
21951
|
+
to = Option33.String("--to", {
|
|
21429
21952
|
description: "Pin the platform version to converge to (defaults to the current release channel)."
|
|
21430
21953
|
});
|
|
21431
|
-
only =
|
|
21954
|
+
only = Option33.String("--only", {
|
|
21432
21955
|
description: "Converge only one component: infra | gateway | codingAgent | azureExecutor | personas | brainSeeds."
|
|
21433
21956
|
});
|
|
21434
|
-
check =
|
|
21435
|
-
yes =
|
|
21436
|
-
skipInfra =
|
|
21437
|
-
forceInfra =
|
|
21438
|
-
suffix =
|
|
21957
|
+
check = Option33.Boolean("--check", false);
|
|
21958
|
+
yes = Option33.Boolean("--yes", false);
|
|
21959
|
+
skipInfra = Option33.Boolean("--skip-infra", false);
|
|
21960
|
+
forceInfra = Option33.Boolean("--force-infra", false);
|
|
21961
|
+
suffix = Option33.String("--suffix", {
|
|
21439
21962
|
description: "Required with --force-infra \u2014 the existing deployment's resource-name suffix (avoids provisioning duplicates)."
|
|
21440
21963
|
});
|
|
21441
|
-
contentDir =
|
|
21964
|
+
contentDir = Option33.String("--content-dir", {
|
|
21442
21965
|
description: "Path to a local m8t repo checkout (skips the tarball fetch + tree-drift check)."
|
|
21443
21966
|
});
|
|
21444
|
-
manifestUrl =
|
|
21967
|
+
manifestUrl = Option33.String("--manifest-url", {
|
|
21445
21968
|
description: "Fetch the release manifest from this URL instead of the release channel."
|
|
21446
21969
|
});
|
|
21447
|
-
manifestFile =
|
|
21970
|
+
manifestFile = Option33.String("--manifest-file", {
|
|
21448
21971
|
description: "Read the release manifest from this local file instead of the release channel."
|
|
21449
21972
|
});
|
|
21450
|
-
endpoint =
|
|
21973
|
+
endpoint = Option33.String("--endpoint", {
|
|
21451
21974
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
21452
21975
|
});
|
|
21453
|
-
output =
|
|
21976
|
+
output = Option33.String("--output");
|
|
21454
21977
|
async executeCommand() {
|
|
21455
21978
|
const mode = resolveOutputMode(
|
|
21456
21979
|
this.output,
|
|
@@ -21494,7 +22017,7 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
21494
22017
|
assertCliVersionOk(manifest, CLI_VERSION, onWarn);
|
|
21495
22018
|
const stamp = await readStamp({ credential: credential2, subscriptionId, resourceGroup });
|
|
21496
22019
|
onProgress("resolving release content\u2026");
|
|
21497
|
-
const contentDir = await
|
|
22020
|
+
const contentDir = await resolveReleaseContentWithAgents(manifest, { contentDir: contentDirFlag });
|
|
21498
22021
|
const tree = contentDirFlag ? { treeShaOf: () => void 0 } : await fetchRepoTree(manifest.platform.commit);
|
|
21499
22022
|
if (!contentDirFlag) {
|
|
21500
22023
|
const mismatches = verifyPersonaTrees(manifest, tree);
|
|
@@ -21578,7 +22101,7 @@ var PlatformUpdateCommand = class extends M8tCommand {
|
|
|
21578
22101
|
};
|
|
21579
22102
|
|
|
21580
22103
|
// src/commands/platform/converge.ts
|
|
21581
|
-
import { Command as
|
|
22104
|
+
import { Command as Command36 } from "clipanion";
|
|
21582
22105
|
|
|
21583
22106
|
// src/lib/platform-converge-mi.ts
|
|
21584
22107
|
import { ManagedIdentityCredential } from "@azure/identity";
|
|
@@ -21669,15 +22192,15 @@ async function patchApplyRequest(client, mutate) {
|
|
|
21669
22192
|
}
|
|
21670
22193
|
|
|
21671
22194
|
// src/lib/platform-self-update.ts
|
|
21672
|
-
import * as
|
|
21673
|
-
import * as
|
|
22195
|
+
import * as fs30 from "fs";
|
|
22196
|
+
import * as path32 from "path";
|
|
21674
22197
|
function toVTag2(v) {
|
|
21675
22198
|
return v.startsWith("v") ? v : `v${v}`;
|
|
21676
22199
|
}
|
|
21677
22200
|
function readBakedReleaseVersion(repoRoot) {
|
|
21678
22201
|
if (!repoRoot) return null;
|
|
21679
22202
|
try {
|
|
21680
|
-
const raw =
|
|
22203
|
+
const raw = fs30.readFileSync(path32.join(repoRoot, "installer", "version.json"), "utf8");
|
|
21681
22204
|
const v = JSON.parse(raw).version;
|
|
21682
22205
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
21683
22206
|
} catch {
|
|
@@ -21882,7 +22405,7 @@ async function failIfStillInFlight(client, now, error) {
|
|
|
21882
22405
|
}
|
|
21883
22406
|
var PlatformConvergeCommand = class extends M8tCommand {
|
|
21884
22407
|
static paths = [["platform", "converge"]];
|
|
21885
|
-
static usage =
|
|
22408
|
+
static usage = Command36.Usage({
|
|
21886
22409
|
category: "Platform",
|
|
21887
22410
|
description: "Headless updater-job driver: claim a pending apply-request and converge the platform to it.",
|
|
21888
22411
|
details: "The Managed-Identity-authenticated entry point run by the updater Container Apps job. One invocation is one tick: it claims the pending apply-request row (if any), refuses a downgrade against the installed stamp, self-fetches and validates the target manifest, self-updates its own engine image when the manifest requires a newer CLI, then drives the converge engine behind the health-gate + auto-rollback. Reads its subscription/RG/endpoint/channel from env (MI_CLIENT_ID, SUBSCRIPTION_ID, RESOURCE_GROUP, FOUNDRY_ENDPOINT, M8T_UPDATE_CHANNEL_URL). Not intended for interactive use \u2014 the founder-facing path is 'm8t platform update'."
|
|
@@ -21964,7 +22487,7 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
21964
22487
|
interactive: false,
|
|
21965
22488
|
endpoint: ctx.endpoint
|
|
21966
22489
|
});
|
|
21967
|
-
const contentDir = await
|
|
22490
|
+
const contentDir = await resolveReleaseContentWithAgents(manifest, repoRoot ? { contentDir: repoRoot } : {});
|
|
21968
22491
|
const tree = manifest.components.infra?.treeSha ? { treeShaOf: () => void 0 } : await fetchRepoTree(manifest.platform.commit);
|
|
21969
22492
|
const suffix = await this.recoverSuffix(ctx);
|
|
21970
22493
|
const gatewayResourceId = await this.resolveGatewayResourceId(ctx, suffix);
|
|
@@ -22097,8 +22620,8 @@ function targetManifestSource(ctx, target) {
|
|
|
22097
22620
|
}
|
|
22098
22621
|
|
|
22099
22622
|
// src/commands/platform/seed-stamp.ts
|
|
22100
|
-
import { readFileSync as
|
|
22101
|
-
import { Command as
|
|
22623
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
22624
|
+
import { Command as Command37, Option as Option34 } from "clipanion";
|
|
22102
22625
|
import { DefaultAzureCredential as DefaultAzureCredential18, ManagedIdentityCredential as ManagedIdentityCredential2 } from "@azure/identity";
|
|
22103
22626
|
init_errors();
|
|
22104
22627
|
|
|
@@ -22184,7 +22707,7 @@ function buildSeedStamp(d, cliVersion, nowIso) {
|
|
|
22184
22707
|
|
|
22185
22708
|
// src/commands/platform/seed-stamp.ts
|
|
22186
22709
|
async function seedStamp2(args) {
|
|
22187
|
-
const readFileImpl = args.readFileImpl ?? ((p) =>
|
|
22710
|
+
const readFileImpl = args.readFileImpl ?? ((p) => readFileSync21(p, "utf8"));
|
|
22188
22711
|
const readStampImpl = args.readStampImpl ?? readStamp;
|
|
22189
22712
|
const writeStampImpl = args.writeStampImpl ?? ((stamp2, o) => writeStamp({ ...o, stamp: stamp2 }));
|
|
22190
22713
|
const discoverImpl = args.discoverImpl ?? discoverStampStorage;
|
|
@@ -22230,15 +22753,15 @@ async function seedStamp2(args) {
|
|
|
22230
22753
|
}
|
|
22231
22754
|
var PlatformSeedStampCommand = class extends M8tCommand {
|
|
22232
22755
|
static paths = [["platform", "seed-stamp"]];
|
|
22233
|
-
static usage =
|
|
22756
|
+
static usage = Command37.Usage({
|
|
22234
22757
|
description: "INTERNAL: write the initial installed-state stamp from a build-time install descriptor.",
|
|
22235
22758
|
details: "Used by the cloud installer at the end of a from-zero install so the platform records which release it is running. Writes the stamp ONLY IF ABSENT \u2014 it never overwrites a stamp written by the converge engine, so a re-run or a container restart is harmless. Not a founder-facing command: to change what is installed, use 'm8t platform update'."
|
|
22236
22759
|
});
|
|
22237
|
-
descriptor =
|
|
22238
|
-
subscription =
|
|
22239
|
-
resourceGroup =
|
|
22240
|
-
miClientId =
|
|
22241
|
-
output =
|
|
22760
|
+
descriptor = Option34.String("--descriptor", { description: "Path to install-descriptor.json (baked into the installer image)." });
|
|
22761
|
+
subscription = Option34.String("--subscription");
|
|
22762
|
+
resourceGroup = Option34.String("--resource-group");
|
|
22763
|
+
miClientId = Option34.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
|
|
22764
|
+
output = Option34.String("--output");
|
|
22242
22765
|
async executeCommand() {
|
|
22243
22766
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
22244
22767
|
const need = (v, flag) => {
|
|
@@ -22270,7 +22793,7 @@ var PlatformSeedStampCommand = class extends M8tCommand {
|
|
|
22270
22793
|
};
|
|
22271
22794
|
|
|
22272
22795
|
// src/commands/platform/stamp-build.ts
|
|
22273
|
-
import { Command as
|
|
22796
|
+
import { Command as Command38, Option as Option35 } from "clipanion";
|
|
22274
22797
|
import { DefaultAzureCredential as DefaultAzureCredential19, ManagedIdentityCredential as ManagedIdentityCredential3 } from "@azure/identity";
|
|
22275
22798
|
init_errors();
|
|
22276
22799
|
|
|
@@ -22304,17 +22827,17 @@ function buildRunningBuildStamp(input) {
|
|
|
22304
22827
|
// src/commands/platform/stamp-build.ts
|
|
22305
22828
|
var PlatformStampBuildCommand = class extends M8tCommand {
|
|
22306
22829
|
static paths = [["platform", "stamp-build"]];
|
|
22307
|
-
static usage =
|
|
22830
|
+
static usage = Command38.Usage({
|
|
22308
22831
|
description: "Record the currently running build as this install's platform version.",
|
|
22309
22832
|
details: "For installs that are deployed continuously rather than from a published release. Unlike the install-time record, this overwrites on every run \u2014 the running build is the truth. Only components this writer deploys are reported; the rest are marked as managed elsewhere."
|
|
22310
22833
|
});
|
|
22311
|
-
version =
|
|
22312
|
-
gatewayTag =
|
|
22313
|
-
gatewayDigest =
|
|
22314
|
-
subscription =
|
|
22315
|
-
resourceGroup =
|
|
22316
|
-
miClientId =
|
|
22317
|
-
output =
|
|
22834
|
+
version = Option35.String("--version", { description: "The running build's own version, bare X.Y.Z." });
|
|
22835
|
+
gatewayTag = Option35.String("--gateway-tag", { description: "The tag of the gateway image this build deployed." });
|
|
22836
|
+
gatewayDigest = Option35.String("--gateway-digest", { description: "The digest of the gateway image this build deployed." });
|
|
22837
|
+
subscription = Option35.String("--subscription");
|
|
22838
|
+
resourceGroup = Option35.String("--resource-group");
|
|
22839
|
+
miClientId = Option35.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
|
|
22840
|
+
output = Option35.String("--output");
|
|
22318
22841
|
async executeCommand() {
|
|
22319
22842
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
22320
22843
|
const need = (v, flag) => {
|
|
@@ -22366,7 +22889,7 @@ var PlatformStampBuildCommand = class extends M8tCommand {
|
|
|
22366
22889
|
};
|
|
22367
22890
|
|
|
22368
22891
|
// src/commands/platform/policy.ts
|
|
22369
|
-
import { Command as
|
|
22892
|
+
import { Command as Command39, Option as Option36 } from "clipanion";
|
|
22370
22893
|
import { DefaultAzureCredential as DefaultAzureCredential20, ManagedIdentityCredential as ManagedIdentityCredential4 } from "@azure/identity";
|
|
22371
22894
|
init_errors();
|
|
22372
22895
|
|
|
@@ -22422,16 +22945,16 @@ async function readPolicy(opts) {
|
|
|
22422
22945
|
// src/commands/platform/policy.ts
|
|
22423
22946
|
var PlatformPolicyCommand = class extends M8tCommand {
|
|
22424
22947
|
static paths = [["platform", "policy"]];
|
|
22425
|
-
static usage =
|
|
22948
|
+
static usage = Command39.Usage({
|
|
22426
22949
|
description: "Show or set how this install handles available platform updates.",
|
|
22427
22950
|
details: "Modes: 'notify-only' shows updates but never applies them; 'auto-critical' (the default when unset) applies critical releases without asking; 'auto-all' applies every release. Use 'notify-only' where something else already controls what is deployed.",
|
|
22428
22951
|
examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
|
|
22429
22952
|
});
|
|
22430
|
-
set =
|
|
22431
|
-
subscription =
|
|
22432
|
-
resourceGroup =
|
|
22433
|
-
miClientId =
|
|
22434
|
-
output =
|
|
22953
|
+
set = Option36.String("--set", { description: "notify-only | auto-critical | auto-all" });
|
|
22954
|
+
subscription = Option36.String("--subscription");
|
|
22955
|
+
resourceGroup = Option36.String("--resource-group");
|
|
22956
|
+
miClientId = Option36.String("--mi-client-id");
|
|
22957
|
+
output = Option36.String("--output");
|
|
22435
22958
|
async executeCommand() {
|
|
22436
22959
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
22437
22960
|
const need = (v, flag) => {
|
|
@@ -22477,7 +23000,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
22477
23000
|
};
|
|
22478
23001
|
|
|
22479
23002
|
// src/commands/platform/enable-cost-report.ts
|
|
22480
|
-
import { Command as
|
|
23003
|
+
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
22481
23004
|
|
|
22482
23005
|
// src/lib/wire-gateway-acs.ts
|
|
22483
23006
|
init_rbac();
|
|
@@ -22517,18 +23040,18 @@ async function wireGatewayForAcs(args) {
|
|
|
22517
23040
|
init_errors();
|
|
22518
23041
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
22519
23042
|
static paths = [["platform", "enable-cost-report"]];
|
|
22520
|
-
static usage =
|
|
23043
|
+
static usage = Command40.Usage({
|
|
22521
23044
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
22522
23045
|
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')."
|
|
22523
23046
|
});
|
|
22524
|
-
subscription =
|
|
22525
|
-
resourceGroup =
|
|
23047
|
+
subscription = Option37.String("--subscription");
|
|
23048
|
+
resourceGroup = Option37.String("--resource-group", {
|
|
22526
23049
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
22527
23050
|
});
|
|
22528
|
-
acsEndpoint =
|
|
22529
|
-
acsSender =
|
|
22530
|
-
acsResourceId =
|
|
22531
|
-
output =
|
|
23051
|
+
acsEndpoint = Option37.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
|
|
23052
|
+
acsSender = Option37.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
|
|
23053
|
+
acsResourceId = Option37.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
|
|
23054
|
+
output = Option37.String("--output");
|
|
22532
23055
|
async executeCommand() {
|
|
22533
23056
|
const mode = resolveOutputMode(
|
|
22534
23057
|
this.output,
|
|
@@ -22602,45 +23125,45 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
22602
23125
|
};
|
|
22603
23126
|
|
|
22604
23127
|
// src/commands/platform/enable-auto-update.ts
|
|
22605
|
-
import { Command as
|
|
23128
|
+
import { Command as Command41, Option as Option38 } from "clipanion";
|
|
22606
23129
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
22607
23130
|
import { DefaultAzureCredential as DefaultAzureCredential21 } from "@azure/identity";
|
|
22608
23131
|
init_errors();
|
|
22609
23132
|
var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
22610
23133
|
static paths = [["platform", "enable-auto-update"]];
|
|
22611
|
-
static usage =
|
|
23134
|
+
static usage = Command41.Usage({
|
|
22612
23135
|
category: "Platform",
|
|
22613
23136
|
description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
|
|
22614
23137
|
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."
|
|
22615
23138
|
});
|
|
22616
|
-
subscription =
|
|
22617
|
-
resourceGroup =
|
|
23139
|
+
subscription = Option38.String("--subscription");
|
|
23140
|
+
resourceGroup = Option38.String("--resource-group", {
|
|
22618
23141
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
22619
23142
|
});
|
|
22620
|
-
suffix =
|
|
23143
|
+
suffix = Option38.String("--suffix", {
|
|
22621
23144
|
description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
|
|
22622
23145
|
});
|
|
22623
|
-
installerImage =
|
|
23146
|
+
installerImage = Option38.String("--installer-image", {
|
|
22624
23147
|
required: true,
|
|
22625
23148
|
description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
|
|
22626
23149
|
});
|
|
22627
|
-
updateCron =
|
|
23150
|
+
updateCron = Option38.String("--update-cron", {
|
|
22628
23151
|
description: "Cron schedule for the updater job (bicep default applies when omitted)."
|
|
22629
23152
|
});
|
|
22630
|
-
channelUrl =
|
|
23153
|
+
channelUrl = Option38.String("--channel-url", {
|
|
22631
23154
|
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
22632
23155
|
});
|
|
22633
|
-
location =
|
|
23156
|
+
location = Option38.String("--location", {
|
|
22634
23157
|
description: "Region for the updater identity + job. Defaults to the resource group's existing resources."
|
|
22635
23158
|
});
|
|
22636
|
-
foundryTracing =
|
|
23159
|
+
foundryTracing = Option38.String("--foundry-tracing", {
|
|
22637
23160
|
description: "project | account | skip. Pass the value the install was deployed with \u2014 omitting it lets the bicep default (project) switch tracing on."
|
|
22638
23161
|
});
|
|
22639
|
-
endpoint =
|
|
23162
|
+
endpoint = Option38.String("--endpoint", {
|
|
22640
23163
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
22641
23164
|
});
|
|
22642
|
-
yes =
|
|
22643
|
-
output =
|
|
23165
|
+
yes = Option38.Boolean("--yes", false);
|
|
23166
|
+
output = Option38.String("--output");
|
|
22644
23167
|
async executeCommand() {
|
|
22645
23168
|
const mode = resolveOutputMode(
|
|
22646
23169
|
this.output,
|
|
@@ -22777,7 +23300,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
22777
23300
|
};
|
|
22778
23301
|
|
|
22779
23302
|
// src/commands/deploy.ts
|
|
22780
|
-
import { Command as
|
|
23303
|
+
import { Command as Command42, Option as Option39 } from "clipanion";
|
|
22781
23304
|
|
|
22782
23305
|
// src/lib/app-reg.ts
|
|
22783
23306
|
init_errors();
|
|
@@ -23062,7 +23585,7 @@ async function patchRedirectUris(appObjectId, fqdn) {
|
|
|
23062
23585
|
init_errors();
|
|
23063
23586
|
|
|
23064
23587
|
// src/lib/whatif.ts
|
|
23065
|
-
import * as
|
|
23588
|
+
import * as path33 from "path";
|
|
23066
23589
|
|
|
23067
23590
|
// src/lib/identifier-patterns.ts
|
|
23068
23591
|
var IDENTIFIER_PATTERNS = [
|
|
@@ -23118,7 +23641,7 @@ function deriveResourceType(resourceId) {
|
|
|
23118
23641
|
return parts.join("/");
|
|
23119
23642
|
}
|
|
23120
23643
|
async function runWhatIf(opts) {
|
|
23121
|
-
const bicepPath =
|
|
23644
|
+
const bicepPath = path33.join(opts.repoRoot, "deploy", "main.bicep");
|
|
23122
23645
|
const out = await runAz([
|
|
23123
23646
|
"deployment",
|
|
23124
23647
|
"group",
|
|
@@ -23216,9 +23739,9 @@ function flattenDelta(delta, prefix = "") {
|
|
|
23216
23739
|
const out = [];
|
|
23217
23740
|
for (const d of delta) {
|
|
23218
23741
|
const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
|
|
23219
|
-
const
|
|
23220
|
-
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children,
|
|
23221
|
-
else out.push({ ...d, path:
|
|
23742
|
+
const path48 = prefix ? `${prefix}${segment}` : d.path;
|
|
23743
|
+
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path48));
|
|
23744
|
+
else out.push({ ...d, path: path48 });
|
|
23222
23745
|
}
|
|
23223
23746
|
return out;
|
|
23224
23747
|
}
|
|
@@ -23268,52 +23791,52 @@ function classifyWhatIf(changes) {
|
|
|
23268
23791
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
|
|
23269
23792
|
var DeployCommand = class extends M8tCommand {
|
|
23270
23793
|
static paths = [["deploy"]];
|
|
23271
|
-
static usage =
|
|
23794
|
+
static usage = Command42.Usage({
|
|
23272
23795
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
23273
23796
|
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/config.yaml, ensures the resource group, and runs deploy/main.bicep. The repo is located via ~/.m8t/repo-root."
|
|
23274
23797
|
});
|
|
23275
|
-
subscription =
|
|
23276
|
-
resourceGroup =
|
|
23277
|
-
location =
|
|
23278
|
-
suffix =
|
|
23279
|
-
imageRef =
|
|
23280
|
-
acrPullIdentity =
|
|
23281
|
-
acrResourceId =
|
|
23282
|
-
foundryEndpoint =
|
|
23283
|
-
foundryResourceId =
|
|
23284
|
-
foundryTracing =
|
|
23798
|
+
subscription = Option39.String("--subscription");
|
|
23799
|
+
resourceGroup = Option39.String("--resource-group", "rg-m8t-stack");
|
|
23800
|
+
location = Option39.String("--location", "eastus");
|
|
23801
|
+
suffix = Option39.String("--suffix", "");
|
|
23802
|
+
imageRef = Option39.String("--image-ref", DEFAULT_IMAGE_REF);
|
|
23803
|
+
acrPullIdentity = Option39.String("--acrpull-identity");
|
|
23804
|
+
acrResourceId = Option39.String("--acr-resource-id");
|
|
23805
|
+
foundryEndpoint = Option39.String("--foundry-endpoint");
|
|
23806
|
+
foundryResourceId = Option39.String("--foundry-resource-id");
|
|
23807
|
+
foundryTracing = Option39.String("--foundry-tracing");
|
|
23285
23808
|
// project | account | skip (bicep default: project)
|
|
23286
|
-
clientId =
|
|
23287
|
-
whatIf =
|
|
23809
|
+
clientId = Option39.String("--client-id");
|
|
23810
|
+
whatIf = Option39.Boolean("--what-if", false);
|
|
23288
23811
|
// Only meaningful with --what-if. Routes the comparison through the
|
|
23289
23812
|
// value-free renderers (see ./lib/whatif-redact.js) instead of the default
|
|
23290
23813
|
// before/after renderer. Defaults false so a local, interactive run keeps
|
|
23291
23814
|
// showing values — that is the whole diagnostic point of --what-if.
|
|
23292
23815
|
// Automation that forwards this output anywhere non-private (a CI log, an
|
|
23293
23816
|
// issue) MUST pass --redact.
|
|
23294
|
-
redact =
|
|
23295
|
-
output =
|
|
23817
|
+
redact = Option39.Boolean("--redact", false);
|
|
23818
|
+
output = Option39.String("--output");
|
|
23296
23819
|
// Subscription-scoped role assignments. Omitted ⇒ the template default (true).
|
|
23297
23820
|
// Pass false when deploying as a principal scoped to the resource group only:
|
|
23298
23821
|
// it cannot deploy at subscription scope, and those assignments persist
|
|
23299
23822
|
// idempotently from the initial deployment anyway.
|
|
23300
|
-
assignSubscriptionRoles =
|
|
23823
|
+
assignSubscriptionRoles = Option39.String("--assign-subscription-roles");
|
|
23301
23824
|
// Referee — all optional, undefined by default ⇒ the bicep defaults
|
|
23302
23825
|
// apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
|
|
23303
23826
|
// empty). Only pass these when explicitly enabling the referee exam stack.
|
|
23304
|
-
gatewayCpu =
|
|
23305
|
-
gatewayMemory =
|
|
23306
|
-
refereeEnabled =
|
|
23307
|
-
refereeBrainRepos =
|
|
23308
|
-
refereeFeedRepo =
|
|
23309
|
-
refereeInstallationId =
|
|
23310
|
-
refereeWebhookHmacKvUri =
|
|
23311
|
-
examKvUri =
|
|
23312
|
-
examLaWorkspaceId =
|
|
23313
|
-
brainEvalDeployment =
|
|
23314
|
-
brainAppLogin =
|
|
23315
|
-
refereeCheckpointDir =
|
|
23316
|
-
examApiBase =
|
|
23827
|
+
gatewayCpu = Option39.String("--gateway-cpu");
|
|
23828
|
+
gatewayMemory = Option39.String("--gateway-memory");
|
|
23829
|
+
refereeEnabled = Option39.String("--referee-enabled");
|
|
23830
|
+
refereeBrainRepos = Option39.String("--referee-brain-repos");
|
|
23831
|
+
refereeFeedRepo = Option39.String("--referee-feed-repo");
|
|
23832
|
+
refereeInstallationId = Option39.String("--referee-installation-id");
|
|
23833
|
+
refereeWebhookHmacKvUri = Option39.String("--referee-webhook-hmac-kv-uri");
|
|
23834
|
+
examKvUri = Option39.String("--exam-kv-uri");
|
|
23835
|
+
examLaWorkspaceId = Option39.String("--exam-la-workspace-id");
|
|
23836
|
+
brainEvalDeployment = Option39.String("--brain-eval-deployment");
|
|
23837
|
+
brainAppLogin = Option39.String("--brain-app-login");
|
|
23838
|
+
refereeCheckpointDir = Option39.String("--referee-checkpoint-dir");
|
|
23839
|
+
examApiBase = Option39.String("--exam-api-base");
|
|
23317
23840
|
async executeCommand() {
|
|
23318
23841
|
const mode = resolveOutputMode(
|
|
23319
23842
|
this.output,
|
|
@@ -23492,7 +24015,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
23492
24015
|
|
|
23493
24016
|
// src/commands/eval/skill.ts
|
|
23494
24017
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
23495
|
-
import { Command as
|
|
24018
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
23496
24019
|
init_errors();
|
|
23497
24020
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
23498
24021
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -23517,14 +24040,14 @@ function parseVerdict(stdout) {
|
|
|
23517
24040
|
}
|
|
23518
24041
|
var EvalSkillCommand = class extends M8tCommand {
|
|
23519
24042
|
static paths = [["eval", "skill"]];
|
|
23520
|
-
static usage =
|
|
24043
|
+
static usage = Command43.Usage({
|
|
23521
24044
|
description: "Vet one inbox skill candidate: promote / reject / needs_review. Shells out to the Python `brain-eval` core (override its path with $BRAIN_EVAL_BIN)."
|
|
23522
24045
|
});
|
|
23523
|
-
candidate =
|
|
23524
|
-
skillsDir =
|
|
23525
|
-
noJudge =
|
|
23526
|
-
deployment =
|
|
23527
|
-
output =
|
|
24046
|
+
candidate = Option40.String();
|
|
24047
|
+
skillsDir = Option40.String("--skills-dir");
|
|
24048
|
+
noJudge = Option40.Boolean("--no-judge", false);
|
|
24049
|
+
deployment = Option40.String("--deployment");
|
|
24050
|
+
output = Option40.String("--output");
|
|
23528
24051
|
executeCommand() {
|
|
23529
24052
|
return Promise.resolve(this._runCommand());
|
|
23530
24053
|
}
|
|
@@ -23581,9 +24104,9 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
23581
24104
|
|
|
23582
24105
|
// src/commands/eval/exam.ts
|
|
23583
24106
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
23584
|
-
import { writeFileSync as
|
|
23585
|
-
import { join as
|
|
23586
|
-
import { Command as
|
|
24107
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync22, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
|
|
24108
|
+
import { join as join31 } from "path";
|
|
24109
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
23587
24110
|
init_errors();
|
|
23588
24111
|
init_esm();
|
|
23589
24112
|
function parseArmToken(tok, opts) {
|
|
@@ -23764,11 +24287,11 @@ function resolveRefereeHome(refereeHomeOut, env) {
|
|
|
23764
24287
|
return null;
|
|
23765
24288
|
}
|
|
23766
24289
|
function resolveTaskSetDir(base, worker, version) {
|
|
23767
|
-
if (
|
|
24290
|
+
if (existsSync20(join31(base, version))) return version;
|
|
23768
24291
|
const prefixed = `${worker}-${version}`;
|
|
23769
|
-
if (
|
|
23770
|
-
if (version === "latest" &&
|
|
23771
|
-
const dirs =
|
|
24292
|
+
if (existsSync20(join31(base, prefixed))) return prefixed;
|
|
24293
|
+
if (version === "latest" && existsSync20(base)) {
|
|
24294
|
+
const dirs = readdirSync3(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
|
|
23772
24295
|
if (dirs.length > 0) return dirs[dirs.length - 1];
|
|
23773
24296
|
}
|
|
23774
24297
|
return version;
|
|
@@ -23782,11 +24305,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
|
|
|
23782
24305
|
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)"
|
|
23783
24306
|
});
|
|
23784
24307
|
}
|
|
23785
|
-
const versionDir = resolveTaskSetDir(
|
|
23786
|
-
const manifestPath =
|
|
24308
|
+
const versionDir = resolveTaskSetDir(join31(home, "tasksets", worker), worker, version);
|
|
24309
|
+
const manifestPath = join31(home, "tasksets", worker, versionDir, "manifest.yaml");
|
|
23787
24310
|
let text;
|
|
23788
24311
|
try {
|
|
23789
|
-
text =
|
|
24312
|
+
text = readFileSync22(manifestPath, "utf-8");
|
|
23790
24313
|
} catch (e) {
|
|
23791
24314
|
throw new LocalCliError({
|
|
23792
24315
|
code: "EXAM_REDACTION_UNSAFE",
|
|
@@ -23813,24 +24336,24 @@ function buildPlan(args) {
|
|
|
23813
24336
|
}
|
|
23814
24337
|
var EvalExamCommand = class extends M8tCommand {
|
|
23815
24338
|
static paths = [["eval", "exam"]];
|
|
23816
|
-
static usage =
|
|
24339
|
+
static usage = Command44.Usage({
|
|
23817
24340
|
description: "Run a brain exam: impact A/B + dream-delta. Resolves the plan, then shells out to the Python `brain-exam` orchestrator (override its path with $BRAIN_EXAM_BIN). Renders an ExamVerdict: three-valued verdict + power note + per-task flips."
|
|
23818
24341
|
});
|
|
23819
|
-
worker =
|
|
23820
|
-
arms =
|
|
23821
|
-
taskSet =
|
|
23822
|
-
examType =
|
|
23823
|
-
skill =
|
|
23824
|
-
reps =
|
|
23825
|
-
probes =
|
|
23826
|
-
pool =
|
|
23827
|
-
out =
|
|
23828
|
-
dryRun =
|
|
23829
|
-
keepArms =
|
|
23830
|
-
allowStub =
|
|
23831
|
-
deployment =
|
|
23832
|
-
output =
|
|
23833
|
-
observeWaitS =
|
|
24342
|
+
worker = Option41.String();
|
|
24343
|
+
arms = Option41.String("--arms");
|
|
24344
|
+
taskSet = Option41.String("--task-set");
|
|
24345
|
+
examType = Option41.String("--exam-type");
|
|
24346
|
+
skill = Option41.String("--skill");
|
|
24347
|
+
reps = Option41.String("-n,--reps");
|
|
24348
|
+
probes = Option41.String("--probes");
|
|
24349
|
+
pool = Option41.String("--pool");
|
|
24350
|
+
out = Option41.String("--out");
|
|
24351
|
+
dryRun = Option41.Boolean("--dry-run", false);
|
|
24352
|
+
keepArms = Option41.Boolean("--keep-arms", false);
|
|
24353
|
+
allowStub = Option41.Boolean("--allow-stub", false);
|
|
24354
|
+
deployment = Option41.String("--deployment");
|
|
24355
|
+
output = Option41.String("--output");
|
|
24356
|
+
observeWaitS = Option41.String("--observe-wait-s");
|
|
23834
24357
|
async executeCommand() {
|
|
23835
24358
|
await Promise.resolve();
|
|
23836
24359
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -23906,10 +24429,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
23906
24429
|
const verdict = parseExamVerdict(res.stdout);
|
|
23907
24430
|
if (out !== void 0) {
|
|
23908
24431
|
const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
|
|
23909
|
-
|
|
23910
|
-
|
|
24432
|
+
mkdirSync6(out, { recursive: true });
|
|
24433
|
+
writeFileSync8(join31(out, "verdict.json"), JSON.stringify(verdict, null, 2));
|
|
23911
24434
|
const feed = redactForFeed(verdict, sealedTaskIds);
|
|
23912
|
-
|
|
24435
|
+
writeFileSync8(join31(out, "feed.json"), JSON.stringify(feed, null, 2));
|
|
23913
24436
|
}
|
|
23914
24437
|
if (mode === "json") {
|
|
23915
24438
|
this.context.stdout.write(renderJson(verdict) + "\n");
|
|
@@ -23945,10 +24468,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
23945
24468
|
};
|
|
23946
24469
|
|
|
23947
24470
|
// src/commands/version.ts
|
|
23948
|
-
import { Command as
|
|
24471
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
23949
24472
|
var VersionCommand = class extends M8tCommand {
|
|
23950
24473
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
23951
|
-
static usage =
|
|
24474
|
+
static usage = Command45.Usage({
|
|
23952
24475
|
description: "Print the CLI version.",
|
|
23953
24476
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
23954
24477
|
examples: [
|
|
@@ -23956,8 +24479,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
23956
24479
|
["Print as JSON", "$0 version --output json"]
|
|
23957
24480
|
]
|
|
23958
24481
|
});
|
|
23959
|
-
output =
|
|
23960
|
-
verbose =
|
|
24482
|
+
output = Option42.String("--output", { description: "pretty | json | auto (default)" });
|
|
24483
|
+
verbose = Option42.Boolean("--verbose", false);
|
|
23961
24484
|
executeCommand() {
|
|
23962
24485
|
const mode = resolveOutputMode(
|
|
23963
24486
|
this.output ?? "auto",
|
|
@@ -23988,18 +24511,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
23988
24511
|
};
|
|
23989
24512
|
|
|
23990
24513
|
// src/commands/whoami.ts
|
|
23991
|
-
import { Command as
|
|
24514
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
23992
24515
|
var WhoamiCommand = class extends M8tCommand {
|
|
23993
24516
|
static paths = [["whoami"]];
|
|
23994
|
-
static usage =
|
|
24517
|
+
static usage = Command46.Usage({
|
|
23995
24518
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
23996
24519
|
});
|
|
23997
|
-
output =
|
|
23998
|
-
verbose =
|
|
23999
|
-
subscription =
|
|
24520
|
+
output = Option43.String("--output");
|
|
24521
|
+
verbose = Option43.Boolean("--verbose", false);
|
|
24522
|
+
subscription = Option43.String("--subscription", {
|
|
24000
24523
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
24001
24524
|
});
|
|
24002
|
-
resourceGroup =
|
|
24525
|
+
resourceGroup = Option43.String("--resource-group", {
|
|
24003
24526
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24004
24527
|
});
|
|
24005
24528
|
async executeCommand() {
|
|
@@ -24064,13 +24587,13 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
24064
24587
|
};
|
|
24065
24588
|
|
|
24066
24589
|
// src/commands/status.ts
|
|
24067
|
-
import { Command as
|
|
24590
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
24068
24591
|
|
|
24069
24592
|
// src/lib/azd.ts
|
|
24070
24593
|
init_errors();
|
|
24071
24594
|
import { spawn as spawn4 } from "child_process";
|
|
24072
24595
|
function runAzd(args) {
|
|
24073
|
-
return new Promise((
|
|
24596
|
+
return new Promise((resolve6, reject) => {
|
|
24074
24597
|
const proc = spawn4("azd", args, { stdio: ["ignore", "pipe", "pipe"], shell: process.platform === "win32" });
|
|
24075
24598
|
const out = [];
|
|
24076
24599
|
const err = [];
|
|
@@ -24099,7 +24622,7 @@ function runAzd(args) {
|
|
|
24099
24622
|
);
|
|
24100
24623
|
return;
|
|
24101
24624
|
}
|
|
24102
|
-
|
|
24625
|
+
resolve6(Buffer.concat(out).toString("utf8"));
|
|
24103
24626
|
});
|
|
24104
24627
|
});
|
|
24105
24628
|
}
|
|
@@ -24129,10 +24652,10 @@ async function resolveLocalContext() {
|
|
|
24129
24652
|
// src/commands/status.ts
|
|
24130
24653
|
var StatusCommand = class extends M8tCommand {
|
|
24131
24654
|
static paths = [["status"]];
|
|
24132
|
-
static usage =
|
|
24655
|
+
static usage = Command47.Usage({
|
|
24133
24656
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
24134
24657
|
});
|
|
24135
|
-
output =
|
|
24658
|
+
output = Option44.String("--output");
|
|
24136
24659
|
async executeCommand() {
|
|
24137
24660
|
const mode = resolveOutputMode(
|
|
24138
24661
|
this.output,
|
|
@@ -24170,11 +24693,11 @@ var StatusCommand = class extends M8tCommand {
|
|
|
24170
24693
|
};
|
|
24171
24694
|
|
|
24172
24695
|
// src/commands/doctor.ts
|
|
24173
|
-
import { Command as
|
|
24696
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
24174
24697
|
import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
|
|
24175
|
-
import * as
|
|
24176
|
-
import * as
|
|
24177
|
-
import * as
|
|
24698
|
+
import * as fs31 from "fs";
|
|
24699
|
+
import * as os12 from "os";
|
|
24700
|
+
import * as path34 from "path";
|
|
24178
24701
|
|
|
24179
24702
|
// src/lib/doctor-checks.ts
|
|
24180
24703
|
function checkAzSignedIn(az) {
|
|
@@ -24653,40 +25176,40 @@ async function resolveAccountLocation(accountId) {
|
|
|
24653
25176
|
}
|
|
24654
25177
|
}
|
|
24655
25178
|
function probeRepoRoot() {
|
|
24656
|
-
const marker =
|
|
24657
|
-
if (!
|
|
25179
|
+
const marker = path34.join(os12.homedir(), ".m8t", "repo-root");
|
|
25180
|
+
if (!fs31.existsSync(marker)) {
|
|
24658
25181
|
return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
|
|
24659
25182
|
}
|
|
24660
25183
|
let repoPath;
|
|
24661
25184
|
try {
|
|
24662
|
-
repoPath =
|
|
25185
|
+
repoPath = fs31.readFileSync(marker, "utf8").trim();
|
|
24663
25186
|
} catch {
|
|
24664
25187
|
return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
24665
25188
|
}
|
|
24666
25189
|
if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
24667
25190
|
let isDir = false;
|
|
24668
25191
|
try {
|
|
24669
|
-
isDir =
|
|
25192
|
+
isDir = fs31.statSync(repoPath).isDirectory();
|
|
24670
25193
|
} catch {
|
|
24671
25194
|
isDir = false;
|
|
24672
25195
|
}
|
|
24673
|
-
const isGitCheckout = isDir &&
|
|
25196
|
+
const isGitCheckout = isDir && fs31.existsSync(path34.join(repoPath, ".git"));
|
|
24674
25197
|
return { markerExists: true, path: repoPath, isDir, isGitCheckout };
|
|
24675
25198
|
}
|
|
24676
25199
|
function probeLegacyStateDir() {
|
|
24677
25200
|
return {
|
|
24678
|
-
legacyExists:
|
|
24679
|
-
currentExists:
|
|
25201
|
+
legacyExists: fs31.existsSync(path34.join(os12.homedir(), ".m8t-stack")),
|
|
25202
|
+
currentExists: fs31.existsSync(path34.join(os12.homedir(), ".m8t"))
|
|
24680
25203
|
};
|
|
24681
25204
|
}
|
|
24682
25205
|
var DoctorCommand = class extends M8tCommand {
|
|
24683
25206
|
static paths = [["doctor"]];
|
|
24684
|
-
static usage =
|
|
25207
|
+
static usage = Command48.Usage({
|
|
24685
25208
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
24686
25209
|
});
|
|
24687
|
-
output =
|
|
24688
|
-
agent =
|
|
24689
|
-
resourceGroup =
|
|
25210
|
+
output = Option45.String("--output");
|
|
25211
|
+
agent = Option45.String("--agent");
|
|
25212
|
+
resourceGroup = Option45.String("--resource-group", {
|
|
24690
25213
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24691
25214
|
});
|
|
24692
25215
|
async executeCommand() {
|
|
@@ -24822,7 +25345,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
24822
25345
|
};
|
|
24823
25346
|
|
|
24824
25347
|
// src/commands/prereqs.ts
|
|
24825
|
-
import { Command as
|
|
25348
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
24826
25349
|
init_errors();
|
|
24827
25350
|
|
|
24828
25351
|
// src/lib/prereq-deps.ts
|
|
@@ -25528,7 +26051,7 @@ function renderVerdict(v) {
|
|
|
25528
26051
|
}
|
|
25529
26052
|
var PrereqsCommand = class extends M8tCommand {
|
|
25530
26053
|
static paths = [["prereqs"]];
|
|
25531
|
-
static usage =
|
|
26054
|
+
static usage = Command49.Usage({
|
|
25532
26055
|
description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
|
|
25533
26056
|
details: "Runs one of two phases. With no platform discoverable it checks INSTALL prerequisites: your Azure sign-in, subscription and directory rights, resource-provider registrations, soft-deleted Cognitive Services accounts (they hold their model quota until purged), and model quota. With a platform live it checks USAGE prerequisites for the signed-in account: the sign-in redirect URI, Foundry data-plane access, and Key Vault secrets access. Pass --fix to repair what is repairable, and --fix --for <upn> to set a teammate up (usage phase only). Read-only without --fix.",
|
|
25534
26057
|
examples: [
|
|
@@ -25539,15 +26062,15 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
25539
26062
|
["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
|
|
25540
26063
|
]
|
|
25541
26064
|
});
|
|
25542
|
-
fix =
|
|
25543
|
-
for_ =
|
|
25544
|
-
phase =
|
|
25545
|
-
region =
|
|
25546
|
-
model =
|
|
25547
|
-
clientId =
|
|
25548
|
-
subscription =
|
|
25549
|
-
resourceGroup =
|
|
25550
|
-
output =
|
|
26065
|
+
fix = Option46.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
|
|
26066
|
+
for_ = Option46.String("--for", { description: "UPN or object id of another person. Usage phase only." });
|
|
26067
|
+
phase = Option46.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
|
|
26068
|
+
region = Option46.String("--region", { description: "Target region. Enables the install-phase quota check, and judges soft-deleted accounts against the region you will install into (without it, they only warn)." });
|
|
26069
|
+
model = Option46.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
|
|
26070
|
+
clientId = Option46.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
|
|
26071
|
+
subscription = Option46.String("--subscription");
|
|
26072
|
+
resourceGroup = Option46.String("--resource-group");
|
|
26073
|
+
output = Option46.String("--output");
|
|
25551
26074
|
async executeCommand() {
|
|
25552
26075
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
25553
26076
|
const json = mode === "json";
|
|
@@ -25603,18 +26126,18 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
25603
26126
|
};
|
|
25604
26127
|
|
|
25605
26128
|
// src/commands/switch.ts
|
|
25606
|
-
import { Command as
|
|
26129
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
25607
26130
|
|
|
25608
26131
|
// src/lib/profiles.ts
|
|
25609
|
-
import * as
|
|
25610
|
-
import * as
|
|
25611
|
-
import { parse as
|
|
26132
|
+
import * as fs32 from "fs/promises";
|
|
26133
|
+
import * as path35 from "path";
|
|
26134
|
+
import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
|
|
25612
26135
|
function profilesDir() {
|
|
25613
26136
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
25614
|
-
return
|
|
26137
|
+
return path35.join(home, ".m8t", "profiles");
|
|
25615
26138
|
}
|
|
25616
26139
|
function getProfilePath(name) {
|
|
25617
|
-
return
|
|
26140
|
+
return path35.join(profilesDir(), `${path35.basename(name)}.yaml`);
|
|
25618
26141
|
}
|
|
25619
26142
|
function slugifyTenant(domainOrId) {
|
|
25620
26143
|
const base = domainOrId.split(".")[0].toLowerCase();
|
|
@@ -25623,7 +26146,7 @@ function slugifyTenant(domainOrId) {
|
|
|
25623
26146
|
}
|
|
25624
26147
|
async function listProfiles() {
|
|
25625
26148
|
try {
|
|
25626
|
-
const entries = await
|
|
26149
|
+
const entries = await fs32.readdir(profilesDir());
|
|
25627
26150
|
return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
|
|
25628
26151
|
} catch (e) {
|
|
25629
26152
|
if (e.code === "ENOENT") return [];
|
|
@@ -25632,8 +26155,8 @@ async function listProfiles() {
|
|
|
25632
26155
|
}
|
|
25633
26156
|
async function readProfile(name) {
|
|
25634
26157
|
try {
|
|
25635
|
-
const raw = await
|
|
25636
|
-
const parsed =
|
|
26158
|
+
const raw = await fs32.readFile(getProfilePath(name), "utf8");
|
|
26159
|
+
const parsed = parseYaml12(raw);
|
|
25637
26160
|
if (!parsed || typeof parsed !== "object") return null;
|
|
25638
26161
|
return parsed;
|
|
25639
26162
|
} catch (e) {
|
|
@@ -25643,14 +26166,14 @@ async function readProfile(name) {
|
|
|
25643
26166
|
}
|
|
25644
26167
|
async function exists(p) {
|
|
25645
26168
|
try {
|
|
25646
|
-
await
|
|
26169
|
+
await fs32.stat(p);
|
|
25647
26170
|
return true;
|
|
25648
26171
|
} catch {
|
|
25649
26172
|
return false;
|
|
25650
26173
|
}
|
|
25651
26174
|
}
|
|
25652
26175
|
async function saveProfile(p) {
|
|
25653
|
-
await
|
|
26176
|
+
await fs32.mkdir(profilesDir(), { recursive: true });
|
|
25654
26177
|
let name = p.name;
|
|
25655
26178
|
let n = 2;
|
|
25656
26179
|
while (await exists(getProfilePath(name))) {
|
|
@@ -25659,8 +26182,8 @@ async function saveProfile(p) {
|
|
|
25659
26182
|
}
|
|
25660
26183
|
const target = getProfilePath(name);
|
|
25661
26184
|
const tmp = `${target}.tmp`;
|
|
25662
|
-
await
|
|
25663
|
-
await
|
|
26185
|
+
await fs32.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
|
|
26186
|
+
await fs32.rename(tmp, target);
|
|
25664
26187
|
return name;
|
|
25665
26188
|
}
|
|
25666
26189
|
|
|
@@ -25743,14 +26266,14 @@ async function profileSwitch(name, asName) {
|
|
|
25743
26266
|
init_errors();
|
|
25744
26267
|
var SwitchCommand = class extends M8tCommand {
|
|
25745
26268
|
static paths = [["switch"]];
|
|
25746
|
-
static usage =
|
|
26269
|
+
static usage = Command50.Usage({
|
|
25747
26270
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
25748
26271
|
});
|
|
25749
|
-
profile =
|
|
25750
|
-
subscription =
|
|
25751
|
-
list =
|
|
25752
|
-
as =
|
|
25753
|
-
output =
|
|
26272
|
+
profile = Option47.String({ required: false });
|
|
26273
|
+
subscription = Option47.String("--subscription");
|
|
26274
|
+
list = Option47.Boolean("--list", false);
|
|
26275
|
+
as = Option47.String("--as");
|
|
26276
|
+
output = Option47.String("--output");
|
|
25754
26277
|
async executeCommand() {
|
|
25755
26278
|
const mode = resolveOutputMode(
|
|
25756
26279
|
this.output,
|
|
@@ -25807,7 +26330,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
25807
26330
|
|
|
25808
26331
|
// src/commands/open.ts
|
|
25809
26332
|
import { spawn as spawn5 } from "child_process";
|
|
25810
|
-
import { Command as
|
|
26333
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
25811
26334
|
|
|
25812
26335
|
// src/lib/open-targets.ts
|
|
25813
26336
|
init_errors();
|
|
@@ -25853,14 +26376,14 @@ function openUrl(url) {
|
|
|
25853
26376
|
}
|
|
25854
26377
|
var OpenCommand = class extends M8tCommand {
|
|
25855
26378
|
static paths = [["open"]];
|
|
25856
|
-
static usage =
|
|
26379
|
+
static usage = Command51.Usage({
|
|
25857
26380
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
25858
26381
|
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)."
|
|
25859
26382
|
});
|
|
25860
|
-
target =
|
|
25861
|
-
print =
|
|
25862
|
-
output =
|
|
25863
|
-
resourceGroup =
|
|
26383
|
+
target = Option48.String({ required: false });
|
|
26384
|
+
print = Option48.Boolean("--print", false);
|
|
26385
|
+
output = Option48.String("--output");
|
|
26386
|
+
resourceGroup = Option48.String("--resource-group", {
|
|
25864
26387
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
25865
26388
|
});
|
|
25866
26389
|
async executeCommand() {
|
|
@@ -25904,7 +26427,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
25904
26427
|
};
|
|
25905
26428
|
|
|
25906
26429
|
// src/commands/dream/run.ts
|
|
25907
|
-
import { Command as
|
|
26430
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
25908
26431
|
import { AzureCliCredential } from "@azure/identity";
|
|
25909
26432
|
import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
25910
26433
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
@@ -26890,7 +27413,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
26890
27413
|
// ../../packages/brain/engine/dist/esm/dream/writer.js
|
|
26891
27414
|
var API2 = "https://api.github.com";
|
|
26892
27415
|
var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
|
|
26893
|
-
var isAllowedWritePath = (
|
|
27416
|
+
var isAllowedWritePath = (path48, allowedPrefix) => allowedPrefix.test(path48) && !path48.split("/").includes("..");
|
|
26894
27417
|
var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
26895
27418
|
function parseFrontmatter(content) {
|
|
26896
27419
|
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
@@ -26933,8 +27456,8 @@ function makeGitDataWriter(args) {
|
|
|
26933
27456
|
if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
|
|
26934
27457
|
throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
|
|
26935
27458
|
}
|
|
26936
|
-
async function gh(token, method,
|
|
26937
|
-
const res = await doFetch(`${API2}/repos/${repoPath}${
|
|
27459
|
+
async function gh(token, method, path48, body) {
|
|
27460
|
+
const res = await doFetch(`${API2}/repos/${repoPath}${path48}`, {
|
|
26938
27461
|
method,
|
|
26939
27462
|
headers: {
|
|
26940
27463
|
Authorization: `Bearer ${token}`,
|
|
@@ -26947,8 +27470,8 @@ function makeGitDataWriter(args) {
|
|
|
26947
27470
|
const text = await res.text();
|
|
26948
27471
|
return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
|
|
26949
27472
|
}
|
|
26950
|
-
async function readFileWith(token,
|
|
26951
|
-
const encodedPath =
|
|
27473
|
+
async function readFileWith(token, path48) {
|
|
27474
|
+
const encodedPath = path48.split("/").map(encodeURIComponent).join("/");
|
|
26952
27475
|
const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
|
|
26953
27476
|
if (r.status === 404)
|
|
26954
27477
|
return null;
|
|
@@ -26964,9 +27487,9 @@ function makeGitDataWriter(args) {
|
|
|
26964
27487
|
throw new Error(`fetchTip: HTTP ${String(r.status)}`);
|
|
26965
27488
|
return r.json.object.sha;
|
|
26966
27489
|
},
|
|
26967
|
-
async readFile(
|
|
27490
|
+
async readFile(path48) {
|
|
26968
27491
|
const token = await args.mintToken();
|
|
26969
|
-
return readFileWith(token,
|
|
27492
|
+
return readFileWith(token, path48);
|
|
26970
27493
|
},
|
|
26971
27494
|
async listMemory() {
|
|
26972
27495
|
const token = await args.mintToken();
|
|
@@ -26975,9 +27498,9 @@ function makeGitDataWriter(args) {
|
|
|
26975
27498
|
return [];
|
|
26976
27499
|
const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
|
|
26977
27500
|
const out = [];
|
|
26978
|
-
for (const
|
|
26979
|
-
const content = await readFileWith(token,
|
|
26980
|
-
out.push({ path:
|
|
27501
|
+
for (const path48 of paths) {
|
|
27502
|
+
const content = await readFileWith(token, path48);
|
|
27503
|
+
out.push({ path: path48, frontmatter: content ? parseFrontmatter(content) : {} });
|
|
26981
27504
|
}
|
|
26982
27505
|
return out;
|
|
26983
27506
|
},
|
|
@@ -27416,7 +27939,7 @@ async function loadMemoryContext(brain) {
|
|
|
27416
27939
|
return {
|
|
27417
27940
|
files,
|
|
27418
27941
|
indexEntries,
|
|
27419
|
-
originOf: (
|
|
27942
|
+
originOf: (path48) => originByPath.get(path48) ?? "worker"
|
|
27420
27943
|
};
|
|
27421
27944
|
}
|
|
27422
27945
|
|
|
@@ -27437,14 +27960,14 @@ function checkEvidenceMembership(delta, harvested) {
|
|
|
27437
27960
|
}
|
|
27438
27961
|
var BatchAbort = class extends Error {
|
|
27439
27962
|
path;
|
|
27440
|
-
constructor(
|
|
27963
|
+
constructor(path48, message) {
|
|
27441
27964
|
super(message);
|
|
27442
|
-
this.path =
|
|
27965
|
+
this.path = path48;
|
|
27443
27966
|
this.name = "BatchAbort";
|
|
27444
27967
|
}
|
|
27445
27968
|
};
|
|
27446
|
-
var isIndexFile = (
|
|
27447
|
-
var isMemoryIndex = (
|
|
27969
|
+
var isIndexFile = (path48) => /(^|\/)[^/]*_index\.md$/.test(path48);
|
|
27970
|
+
var isMemoryIndex = (path48) => path48 === "memory/MEMORY.md";
|
|
27448
27971
|
function targetPath(delta) {
|
|
27449
27972
|
switch (delta.verb) {
|
|
27450
27973
|
case "new":
|
|
@@ -27461,29 +27984,29 @@ function targetPath(delta) {
|
|
|
27461
27984
|
}
|
|
27462
27985
|
async function checkOriginTier(delta, lookup) {
|
|
27463
27986
|
const evidence = evidenceOf(delta) ?? [];
|
|
27464
|
-
const
|
|
27465
|
-
if (
|
|
27987
|
+
const path48 = targetPath(delta);
|
|
27988
|
+
if (path48 === null)
|
|
27466
27989
|
return { ok: true };
|
|
27467
|
-
if (isIndexFile(
|
|
27468
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
27990
|
+
if (isIndexFile(path48) && !isMemoryIndex(path48)) {
|
|
27991
|
+
return { ok: false, flagged: { kind: "flagged", path: path48, tension: `refused: ${path48} is an index file (only memory/MEMORY.md is editable)`, evidence } };
|
|
27469
27992
|
}
|
|
27470
|
-
const target = await lookup(
|
|
27471
|
-
const isStructural =
|
|
27993
|
+
const target = await lookup(path48);
|
|
27994
|
+
const isStructural = path48.startsWith("references/");
|
|
27472
27995
|
const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
|
|
27473
27996
|
const editable = origin === "worker" || origin === "dream";
|
|
27474
27997
|
if (editable)
|
|
27475
27998
|
return { ok: true };
|
|
27476
27999
|
const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
|
|
27477
28000
|
if (removesOperatorTarget) {
|
|
27478
|
-
throw new BatchAbort(
|
|
28001
|
+
throw new BatchAbort(path48, `supersede/retract OLD target ${path48} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
|
|
27479
28002
|
}
|
|
27480
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
28003
|
+
return { ok: false, flagged: { kind: "flagged", path: path48, tension: `operator-origin (${origin}); flag-only`, evidence } };
|
|
27481
28004
|
}
|
|
27482
28005
|
var ALLOWED_MEMORY = /^memory\/.+\.md$/;
|
|
27483
28006
|
var ALLOWED_INBOX = /^inbox\//;
|
|
27484
28007
|
var ALLOWED_QUARANTINE = /^quarantine\//;
|
|
27485
28008
|
var ALLOWED_FLAG = /^memory\//;
|
|
27486
|
-
var hasTraversal = (
|
|
28009
|
+
var hasTraversal = (path48) => path48.split("/").includes("..");
|
|
27487
28010
|
function checkPathAllowed(delta) {
|
|
27488
28011
|
const evidence = evidenceOf(delta) ?? [];
|
|
27489
28012
|
const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
|
|
@@ -27590,7 +28113,7 @@ ${inject}
|
|
|
27590
28113
|
---
|
|
27591
28114
|
` + body.slice(m[0].length);
|
|
27592
28115
|
}
|
|
27593
|
-
var indexLine = (
|
|
28116
|
+
var indexLine = (path48, title, date, tags) => `- \`${path48}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
|
|
27594
28117
|
function splitIndex(index) {
|
|
27595
28118
|
const all = index.split("\n");
|
|
27596
28119
|
const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
|
|
@@ -27763,16 +28286,16 @@ function defaultDreamSeams(opts = {}) {
|
|
|
27763
28286
|
const mem = await memory(deps.brain);
|
|
27764
28287
|
const harvested = harvestedIds(input);
|
|
27765
28288
|
const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
|
|
27766
|
-
const lookup = (
|
|
28289
|
+
const lookup = (path48) => Promise.resolve(mem.files.find((f) => f.path === path48) ?? null);
|
|
27767
28290
|
const digest = [...pendingRejected];
|
|
27768
28291
|
let changes = [];
|
|
27769
28292
|
let indexEdit;
|
|
27770
28293
|
try {
|
|
27771
28294
|
for (const raw of deltas) {
|
|
27772
28295
|
const delta = forcePolicyQuarantine(raw);
|
|
27773
|
-
const
|
|
27774
|
-
if (!
|
|
27775
|
-
digest.push(
|
|
28296
|
+
const path48 = checkPathAllowed(delta);
|
|
28297
|
+
if (!path48.ok) {
|
|
28298
|
+
digest.push(path48.rejected);
|
|
27776
28299
|
continue;
|
|
27777
28300
|
}
|
|
27778
28301
|
const ev = checkEvidenceMembership(delta, harvested);
|
|
@@ -27986,7 +28509,7 @@ async function emitDigest(input, deps, digest) {
|
|
|
27986
28509
|
}
|
|
27987
28510
|
|
|
27988
28511
|
// ../../packages/brain/engine/dist/esm/dream/config.js
|
|
27989
|
-
import { parse as
|
|
28512
|
+
import { parse as parseYaml13 } from "yaml";
|
|
27990
28513
|
var DEFAULT_DREAM_MODEL = "gpt-5.4";
|
|
27991
28514
|
function asCadence(v) {
|
|
27992
28515
|
return v === "nightly" ? "nightly" : "off";
|
|
@@ -27994,7 +28517,7 @@ function asCadence(v) {
|
|
|
27994
28517
|
function parseDreamerConfig(rawYaml, env) {
|
|
27995
28518
|
let parsed;
|
|
27996
28519
|
try {
|
|
27997
|
-
parsed =
|
|
28520
|
+
parsed = parseYaml13(rawYaml);
|
|
27998
28521
|
} catch {
|
|
27999
28522
|
return { enabled: false, cadence: "off", model: env.M8T_DREAM_MODEL ?? DEFAULT_DREAM_MODEL };
|
|
28000
28523
|
}
|
|
@@ -28008,16 +28531,16 @@ function parseDreamerConfig(rawYaml, env) {
|
|
|
28008
28531
|
}
|
|
28009
28532
|
|
|
28010
28533
|
// ../../packages/brain/engine/dist/esm/cost-report/config.js
|
|
28011
|
-
import { parse as
|
|
28534
|
+
import { parse as parseYaml14 } from "yaml";
|
|
28012
28535
|
|
|
28013
28536
|
// ../../packages/brain/engine/dist/esm/librarian/codify/persistence-guard.js
|
|
28014
28537
|
var ZERO_WIDTH_RE = new RegExp("\u200B|\u200C|\u200D|\uFEFF", "g");
|
|
28015
28538
|
|
|
28016
28539
|
// ../../packages/brain/engine/dist/esm/librarian/janitor/frontmatter.js
|
|
28017
|
-
import { parse as
|
|
28540
|
+
import { parse as parseYaml15, stringify as stringifyYaml5 } from "yaml";
|
|
28018
28541
|
|
|
28019
28542
|
// ../../packages/brain/engine/dist/esm/librarian/config.js
|
|
28020
|
-
import { parse as
|
|
28543
|
+
import { parse as parseYaml16 } from "yaml";
|
|
28021
28544
|
|
|
28022
28545
|
// src/lib/dream-discovery.ts
|
|
28023
28546
|
init_http();
|
|
@@ -28131,25 +28654,25 @@ function redactTranscripts(input) {
|
|
|
28131
28654
|
}
|
|
28132
28655
|
var DreamRunCommand = class extends M8tCommand {
|
|
28133
28656
|
static paths = [["dream", "run"]];
|
|
28134
|
-
static usage =
|
|
28657
|
+
static usage = Command52.Usage({
|
|
28135
28658
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
|
|
28136
28659
|
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.\n\nThe bare command is READ-ONLY. --live takes the other branch: a real model call and a commit to the worker's brain repo through a minted GitHub App token."
|
|
28137
28660
|
});
|
|
28138
|
-
worker =
|
|
28661
|
+
worker = Option49.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
28139
28662
|
// Opting IN to the side effects, rather than opting out of them. This command's
|
|
28140
28663
|
// own help has always described a dry run, but the bare invocation used to take
|
|
28141
28664
|
// the live branch — a real model call and a commit to the brain repo — so anyone
|
|
28142
28665
|
// acting on `--help` got the opposite of what they read.
|
|
28143
|
-
live =
|
|
28144
|
-
dryRun =
|
|
28145
|
-
since =
|
|
28146
|
-
reset =
|
|
28147
|
-
showTranscripts =
|
|
28666
|
+
live = Option49.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
|
|
28667
|
+
dryRun = Option49.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
|
|
28668
|
+
since = Option49.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
|
|
28669
|
+
reset = Option49.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
|
|
28670
|
+
showTranscripts = Option49.Boolean("--show-transcripts", false, {
|
|
28148
28671
|
description: "Print transcript bodies (default: metadata only)."
|
|
28149
28672
|
});
|
|
28150
|
-
subscription =
|
|
28151
|
-
endpoint =
|
|
28152
|
-
output =
|
|
28673
|
+
subscription = Option49.String("--subscription");
|
|
28674
|
+
endpoint = Option49.String("--endpoint");
|
|
28675
|
+
output = Option49.String("--output");
|
|
28153
28676
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
28154
28677
|
deps;
|
|
28155
28678
|
async executeCommand() {
|
|
@@ -28497,13 +29020,13 @@ function defaultDeps(overrides) {
|
|
|
28497
29020
|
}
|
|
28498
29021
|
|
|
28499
29022
|
// src/commands/foundry/create.ts
|
|
28500
|
-
import { Command as
|
|
29023
|
+
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
28501
29024
|
|
|
28502
29025
|
// src/lib/foundry-create.ts
|
|
28503
29026
|
init_errors();
|
|
28504
|
-
import { createHash as
|
|
29027
|
+
import { createHash as createHash4 } from "crypto";
|
|
28505
29028
|
function deriveAccountName(subscriptionId) {
|
|
28506
|
-
const h =
|
|
29029
|
+
const h = createHash4("sha256").update(subscriptionId).digest("hex").slice(0, 12);
|
|
28507
29030
|
return `m8t${h}`;
|
|
28508
29031
|
}
|
|
28509
29032
|
function assertHostedRegion(location) {
|
|
@@ -28566,8 +29089,8 @@ async function ensureAccount(args) {
|
|
|
28566
29089
|
);
|
|
28567
29090
|
} catch (e) {
|
|
28568
29091
|
const msg = e instanceof Error ? e.message : String(e);
|
|
28569
|
-
const
|
|
28570
|
-
const stderr = msg.includes(
|
|
29092
|
+
const sep4 = "' failed: ";
|
|
29093
|
+
const stderr = msg.includes(sep4) ? msg.slice(msg.indexOf(sep4) + sep4.length) : msg;
|
|
28571
29094
|
if (/CustomDomainInUse|subdomain name .* is not available|already used by a resource/i.test(stderr)) {
|
|
28572
29095
|
throw new LocalCliError({
|
|
28573
29096
|
code: "FOUNDRY_ACCOUNT_SUBDOMAIN_CONFLICT",
|
|
@@ -28735,7 +29258,7 @@ async function createFoundryProject(args) {
|
|
|
28735
29258
|
init_errors();
|
|
28736
29259
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
28737
29260
|
static paths = [["foundry", "create"]];
|
|
28738
|
-
static usage =
|
|
29261
|
+
static usage = Command53.Usage({
|
|
28739
29262
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
28740
29263
|
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).",
|
|
28741
29264
|
examples: [
|
|
@@ -28744,16 +29267,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
28744
29267
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
28745
29268
|
]
|
|
28746
29269
|
});
|
|
28747
|
-
resourceGroup =
|
|
28748
|
-
location =
|
|
28749
|
-
account =
|
|
28750
|
-
project =
|
|
28751
|
-
model =
|
|
28752
|
-
modelVersion =
|
|
28753
|
-
capacity =
|
|
28754
|
-
subscription =
|
|
28755
|
-
skipQuotaCheck =
|
|
28756
|
-
output =
|
|
29270
|
+
resourceGroup = Option50.String("--resource-group");
|
|
29271
|
+
location = Option50.String("--location");
|
|
29272
|
+
account = Option50.String("--account");
|
|
29273
|
+
project = Option50.String("--project", "m8t");
|
|
29274
|
+
model = Option50.String("--model", "gpt-4.1-mini");
|
|
29275
|
+
modelVersion = Option50.String("--model-version", "2025-04-14");
|
|
29276
|
+
capacity = Option50.String("--capacity", "50");
|
|
29277
|
+
subscription = Option50.String("--subscription");
|
|
29278
|
+
skipQuotaCheck = Option50.Boolean("--skip-quota-check", false);
|
|
29279
|
+
output = Option50.String("--output");
|
|
28757
29280
|
async executeCommand() {
|
|
28758
29281
|
const mode = resolveOutputMode(
|
|
28759
29282
|
this.output,
|
|
@@ -28825,22 +29348,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
28825
29348
|
};
|
|
28826
29349
|
|
|
28827
29350
|
// src/commands/foundry/await-ready.ts
|
|
28828
|
-
import { Command as
|
|
29351
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
28829
29352
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
28830
29353
|
init_errors();
|
|
28831
29354
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
28832
29355
|
static paths = [["foundry", "await-ready"]];
|
|
28833
|
-
static usage =
|
|
29356
|
+
static usage = Command54.Usage({
|
|
28834
29357
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
28835
29358
|
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.",
|
|
28836
29359
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
28837
29360
|
});
|
|
28838
|
-
endpoint =
|
|
28839
|
-
consecutive =
|
|
28840
|
-
attempts =
|
|
28841
|
-
interval =
|
|
28842
|
-
subscription =
|
|
28843
|
-
output =
|
|
29361
|
+
endpoint = Option51.String("--endpoint");
|
|
29362
|
+
consecutive = Option51.String("--consecutive", "3");
|
|
29363
|
+
attempts = Option51.String("--attempts", "60");
|
|
29364
|
+
interval = Option51.String("--interval", "5");
|
|
29365
|
+
subscription = Option51.String("--subscription");
|
|
29366
|
+
output = Option51.String("--output");
|
|
28844
29367
|
async executeCommand() {
|
|
28845
29368
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
28846
29369
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -28874,7 +29397,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
28874
29397
|
};
|
|
28875
29398
|
|
|
28876
29399
|
// src/commands/bootstrap/preflight.ts
|
|
28877
|
-
import { Command as
|
|
29400
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
28878
29401
|
|
|
28879
29402
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
28880
29403
|
var EVENT_TIERS = {
|
|
@@ -28900,9 +29423,9 @@ var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t rece
|
|
|
28900
29423
|
|
|
28901
29424
|
// ../../packages/telemetry-contract/src/endpoints.ts
|
|
28902
29425
|
var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
|
|
28903
|
-
function ingestUrl(
|
|
29426
|
+
function ingestUrl(path48) {
|
|
28904
29427
|
const base = INGEST_BASE_URL.replace(/\/+$/, "");
|
|
28905
|
-
return `${base}${
|
|
29428
|
+
return `${base}${path48.startsWith("/") ? path48 : `/${path48}`}`;
|
|
28906
29429
|
}
|
|
28907
29430
|
|
|
28908
29431
|
// src/commands/bootstrap/preflight.ts
|
|
@@ -28926,7 +29449,7 @@ function preflightRenderable(results) {
|
|
|
28926
29449
|
}
|
|
28927
29450
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
28928
29451
|
static paths = [["bootstrap", "preflight"]];
|
|
28929
|
-
static usage =
|
|
29452
|
+
static usage = Command55.Usage({
|
|
28930
29453
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
28931
29454
|
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), every Azure resource provider the install uses (registering any that are missing), soft-deleted Cognitive Services accounts (they keep their model quota until purged, so quota can read free and the model deployment still fail), and \u2014 with --location \u2014 model quota in the target region. Exits non-zero with the exact failing check + remedy. `m8t prereqs` runs the same substrate checks on their own.",
|
|
28932
29455
|
examples: [
|
|
@@ -28935,9 +29458,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
28935
29458
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
28936
29459
|
]
|
|
28937
29460
|
});
|
|
28938
|
-
clientId =
|
|
28939
|
-
subscription =
|
|
28940
|
-
location =
|
|
29461
|
+
clientId = Option52.String("--client-id");
|
|
29462
|
+
subscription = Option52.String("--subscription");
|
|
29463
|
+
location = Option52.String("--location", {
|
|
28941
29464
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
28942
29465
|
});
|
|
28943
29466
|
async executeCommand() {
|
|
@@ -29034,10 +29557,10 @@ ${colors.error(" " + why)}
|
|
|
29034
29557
|
}
|
|
29035
29558
|
|
|
29036
29559
|
// src/commands/bootstrap/launch.ts
|
|
29037
|
-
import * as
|
|
29038
|
-
import * as
|
|
29039
|
-
import * as
|
|
29040
|
-
import { Command as
|
|
29560
|
+
import * as fs35 from "fs";
|
|
29561
|
+
import * as os15 from "os";
|
|
29562
|
+
import * as path38 from "path";
|
|
29563
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
29041
29564
|
init_errors();
|
|
29042
29565
|
|
|
29043
29566
|
// src/lib/bootstrap-mi.ts
|
|
@@ -29192,15 +29715,15 @@ async function kickInstaller(s) {
|
|
|
29192
29715
|
}
|
|
29193
29716
|
|
|
29194
29717
|
// src/lib/bootstrap-status.ts
|
|
29195
|
-
import { createHash as
|
|
29196
|
-
import * as
|
|
29197
|
-
import * as
|
|
29198
|
-
import * as
|
|
29718
|
+
import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
|
|
29719
|
+
import * as fs33 from "fs/promises";
|
|
29720
|
+
import * as os13 from "os";
|
|
29721
|
+
import * as path36 from "path";
|
|
29199
29722
|
init_errors();
|
|
29200
29723
|
var STATUS_CONTAINER = "status";
|
|
29201
29724
|
var STATUS_BLOB = "status.json";
|
|
29202
29725
|
function deriveStatusSaName(resourceGroup, subscriptionId) {
|
|
29203
|
-
const hex =
|
|
29726
|
+
const hex = createHash5("sha256").update(resourceGroup + subscriptionId).digest("hex").slice(0, 12);
|
|
29204
29727
|
return `m8tinst${hex}`;
|
|
29205
29728
|
}
|
|
29206
29729
|
function statusBlobUrl(saName) {
|
|
@@ -29234,7 +29757,7 @@ async function readStatusBlob(opts) {
|
|
|
29234
29757
|
cause: e
|
|
29235
29758
|
});
|
|
29236
29759
|
}
|
|
29237
|
-
const tmpFile =
|
|
29760
|
+
const tmpFile = path36.join(os13.tmpdir(), `m8t-status-${randomUUID2()}.json`);
|
|
29238
29761
|
try {
|
|
29239
29762
|
await runAz([
|
|
29240
29763
|
"storage",
|
|
@@ -29253,7 +29776,7 @@ async function readStatusBlob(opts) {
|
|
|
29253
29776
|
"--no-progress",
|
|
29254
29777
|
"--only-show-errors"
|
|
29255
29778
|
]);
|
|
29256
|
-
const raw = await
|
|
29779
|
+
const raw = await fs33.readFile(tmpFile, "utf8");
|
|
29257
29780
|
return JSON.parse(raw.trim());
|
|
29258
29781
|
} catch (e) {
|
|
29259
29782
|
throw new LocalCliError({
|
|
@@ -29263,34 +29786,34 @@ async function readStatusBlob(opts) {
|
|
|
29263
29786
|
cause: e
|
|
29264
29787
|
});
|
|
29265
29788
|
} finally {
|
|
29266
|
-
await
|
|
29789
|
+
await fs33.rm(tmpFile, { force: true });
|
|
29267
29790
|
}
|
|
29268
29791
|
}
|
|
29269
29792
|
|
|
29270
29793
|
// src/lib/bootstrap-state.ts
|
|
29271
|
-
import * as
|
|
29272
|
-
import * as
|
|
29273
|
-
import * as
|
|
29794
|
+
import * as fs34 from "fs/promises";
|
|
29795
|
+
import * as os14 from "os";
|
|
29796
|
+
import * as path37 from "path";
|
|
29274
29797
|
function stateDir(home) {
|
|
29275
|
-
return
|
|
29798
|
+
return path37.join(home, ".m8t");
|
|
29276
29799
|
}
|
|
29277
29800
|
function statePath(home) {
|
|
29278
|
-
return
|
|
29801
|
+
return path37.join(stateDir(home), "bootstrap.json");
|
|
29279
29802
|
}
|
|
29280
|
-
async function readBootstrapState(home =
|
|
29803
|
+
async function readBootstrapState(home = os14.homedir()) {
|
|
29281
29804
|
try {
|
|
29282
|
-
const raw = await
|
|
29805
|
+
const raw = await fs34.readFile(statePath(home), "utf8");
|
|
29283
29806
|
return JSON.parse(raw);
|
|
29284
29807
|
} catch (e) {
|
|
29285
29808
|
if (e.code === "ENOENT") return null;
|
|
29286
29809
|
throw e;
|
|
29287
29810
|
}
|
|
29288
29811
|
}
|
|
29289
|
-
async function writeBootstrapState(state, home =
|
|
29290
|
-
await
|
|
29812
|
+
async function writeBootstrapState(state, home = os14.homedir()) {
|
|
29813
|
+
await fs34.mkdir(stateDir(home), { recursive: true });
|
|
29291
29814
|
const tmp = `${statePath(home)}.tmp`;
|
|
29292
|
-
await
|
|
29293
|
-
await
|
|
29815
|
+
await fs34.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
29816
|
+
await fs34.rename(tmp, statePath(home));
|
|
29294
29817
|
}
|
|
29295
29818
|
|
|
29296
29819
|
// src/lib/bootstrap-occupancy.ts
|
|
@@ -29402,12 +29925,12 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
|
|
|
29402
29925
|
// src/commands/bootstrap/launch.ts
|
|
29403
29926
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
29404
29927
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
29405
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
29928
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.58";
|
|
29406
29929
|
var ACI_NAME = "m8t-installer";
|
|
29407
29930
|
var MI_NAME = "m8t-installer-mi";
|
|
29408
29931
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
29409
29932
|
static paths = [["bootstrap", "launch"]];
|
|
29410
|
-
static usage =
|
|
29933
|
+
static usage = Command56.Usage({
|
|
29411
29934
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
29412
29935
|
details: "Step 2 of `m8t bootstrap` (run after `preflight`). Ensures the resource group, creates the m8t 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/bootstrap.json for `status` and `reap`, and threads your Azure object id so the installer can grant you the platform's data-plane roles itself.",
|
|
29413
29936
|
examples: [
|
|
@@ -29418,26 +29941,26 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
29418
29941
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
29419
29942
|
]
|
|
29420
29943
|
});
|
|
29421
|
-
location =
|
|
29422
|
-
resourceGroup =
|
|
29423
|
-
clientId =
|
|
29424
|
-
subscription =
|
|
29425
|
-
installerTag =
|
|
29944
|
+
location = Option53.String("--location");
|
|
29945
|
+
resourceGroup = Option53.String("--resource-group");
|
|
29946
|
+
clientId = Option53.String("--client-id");
|
|
29947
|
+
subscription = Option53.String("--subscription");
|
|
29948
|
+
installerTag = Option53.String("--installer-tag");
|
|
29426
29949
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
29427
29950
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
29428
29951
|
// Wins over --installer-tag / the pinned default.
|
|
29429
|
-
installerImage =
|
|
29430
|
-
gatewayImageRef =
|
|
29431
|
-
githubAppCreds =
|
|
29432
|
-
contactEmail =
|
|
29433
|
-
company =
|
|
29952
|
+
installerImage = Option53.String("--installer-image");
|
|
29953
|
+
gatewayImageRef = Option53.String("--gateway-image-ref");
|
|
29954
|
+
githubAppCreds = Option53.String("--github-app-creds");
|
|
29955
|
+
contactEmail = Option53.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
29956
|
+
company = Option53.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
29434
29957
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
29435
29958
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
29436
29959
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
29437
29960
|
// the target; --resource-group is what CHOOSES it.
|
|
29438
|
-
reinstallInto =
|
|
29439
|
-
org =
|
|
29440
|
-
noBrains =
|
|
29961
|
+
reinstallInto = Option53.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
|
|
29962
|
+
org = Option53.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
|
|
29963
|
+
noBrains = Option53.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
|
|
29441
29964
|
async executeCommand() {
|
|
29442
29965
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
29443
29966
|
if (!location) {
|
|
@@ -29476,13 +29999,13 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
29476
29999
|
`);
|
|
29477
30000
|
return 0;
|
|
29478
30001
|
}
|
|
29479
|
-
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds :
|
|
30002
|
+
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path38.join(os15.homedir(), ".m8t", "github-app.json");
|
|
29480
30003
|
const skipBrains = this.noBrains === true;
|
|
29481
30004
|
const requestedOrg = typeof this.org === "string" ? this.org : void 0;
|
|
29482
30005
|
let githubApp;
|
|
29483
30006
|
if (skipBrains) {
|
|
29484
30007
|
out("installing without brain-backed workers; no GitHub App credentials will be used");
|
|
29485
|
-
} else if (!
|
|
30008
|
+
} else if (!fs35.existsSync(credsPath)) {
|
|
29486
30009
|
this.context.stderr.write(buildMissingCredsRefusal(credsPath));
|
|
29487
30010
|
throw new LocalCliError({
|
|
29488
30011
|
code: "GITHUB_APP_CREDS_MISSING",
|
|
@@ -29492,7 +30015,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
29492
30015
|
} else {
|
|
29493
30016
|
let parsed;
|
|
29494
30017
|
try {
|
|
29495
|
-
parsed = JSON.parse(
|
|
30018
|
+
parsed = JSON.parse(fs35.readFileSync(credsPath, "utf8"));
|
|
29496
30019
|
} catch {
|
|
29497
30020
|
throw new LocalCliError({
|
|
29498
30021
|
code: "GITHUB_APP_CREDS_INVALID",
|
|
@@ -29510,7 +30033,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
29510
30033
|
}
|
|
29511
30034
|
let pem;
|
|
29512
30035
|
try {
|
|
29513
|
-
pem =
|
|
30036
|
+
pem = fs35.readFileSync(parsed.pemPath, "utf8");
|
|
29514
30037
|
} catch {
|
|
29515
30038
|
throw new LocalCliError({
|
|
29516
30039
|
code: "GITHUB_APP_PEM_MISSING",
|
|
@@ -29598,7 +30121,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
29598
30121
|
};
|
|
29599
30122
|
|
|
29600
30123
|
// src/commands/bootstrap/status.ts
|
|
29601
|
-
import { Command as
|
|
30124
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
29602
30125
|
init_errors();
|
|
29603
30126
|
|
|
29604
30127
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -29624,15 +30147,15 @@ async function getAciState(opts) {
|
|
|
29624
30147
|
}
|
|
29625
30148
|
|
|
29626
30149
|
// src/lib/bootstrap-finalize.ts
|
|
29627
|
-
import * as
|
|
29628
|
-
import * as
|
|
29629
|
-
import * as
|
|
30150
|
+
import * as fs39 from "fs/promises";
|
|
30151
|
+
import * as os19 from "os";
|
|
30152
|
+
import * as path44 from "path";
|
|
29630
30153
|
|
|
29631
30154
|
// src/lib/company-profile-seed.ts
|
|
29632
30155
|
import { spawn as spawn6 } from "child_process";
|
|
29633
|
-
import { closeSync, openSync, readFileSync as
|
|
29634
|
-
import * as
|
|
29635
|
-
import * as
|
|
30156
|
+
import { closeSync, openSync, readFileSync as readFileSync25 } from "fs";
|
|
30157
|
+
import * as os16 from "os";
|
|
30158
|
+
import * as path39 from "path";
|
|
29636
30159
|
init_errors();
|
|
29637
30160
|
|
|
29638
30161
|
// src/lib/onboarding-profile.ts
|
|
@@ -30357,9 +30880,9 @@ function composeFounderIdentityNote(id) {
|
|
|
30357
30880
|
|
|
30358
30881
|
// src/lib/company-profile-seed.ts
|
|
30359
30882
|
function readGithubAppCreds(credsPath) {
|
|
30360
|
-
const p = credsPath ??
|
|
30883
|
+
const p = credsPath ?? path39.join(os16.homedir(), ".m8t", "github-app.json");
|
|
30361
30884
|
try {
|
|
30362
|
-
return JSON.parse(
|
|
30885
|
+
return JSON.parse(readFileSync25(p, "utf8"));
|
|
30363
30886
|
} catch {
|
|
30364
30887
|
return null;
|
|
30365
30888
|
}
|
|
@@ -30425,7 +30948,7 @@ function upsertMemoryIndexOnce(existing, line2, targetPath2) {
|
|
|
30425
30948
|
async function applyProfileToBrain(args) {
|
|
30426
30949
|
const token = await mintInstallationTokenFromPem({
|
|
30427
30950
|
appId: args.appCreds.appId,
|
|
30428
|
-
privateKeyPem:
|
|
30951
|
+
privateKeyPem: readFileSync25(args.appCreds.pemPath, "utf8"),
|
|
30429
30952
|
installationId: args.appCreds.installationId,
|
|
30430
30953
|
fetchImpl: args.fetchImpl
|
|
30431
30954
|
});
|
|
@@ -30515,7 +31038,7 @@ async function applyProfileToBrains(args) {
|
|
|
30515
31038
|
}
|
|
30516
31039
|
}
|
|
30517
31040
|
function spawnDetachedSeedWatch() {
|
|
30518
|
-
const logPath =
|
|
31041
|
+
const logPath = path39.join(os16.homedir(), ".m8t", "seed-profile.log");
|
|
30519
31042
|
const fd = openSync(logPath, "a");
|
|
30520
31043
|
try {
|
|
30521
31044
|
const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
|
|
@@ -30589,16 +31112,16 @@ function renderInstallSummary(args) {
|
|
|
30589
31112
|
|
|
30590
31113
|
// src/lib/companion-install.ts
|
|
30591
31114
|
import { constants as constants2 } from "fs";
|
|
30592
|
-
import * as
|
|
30593
|
-
import * as
|
|
31115
|
+
import * as fs37 from "fs/promises";
|
|
31116
|
+
import * as path41 from "path";
|
|
30594
31117
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
30595
31118
|
import { execFile, spawn as spawn7 } from "child_process";
|
|
30596
31119
|
|
|
30597
31120
|
// src/lib/companion-artifact.ts
|
|
30598
|
-
import { createHash as
|
|
31121
|
+
import { createHash as createHash6 } from "crypto";
|
|
30599
31122
|
import { constants } from "fs";
|
|
30600
|
-
import * as
|
|
30601
|
-
import * as
|
|
31123
|
+
import * as fs36 from "fs/promises";
|
|
31124
|
+
import * as path40 from "path";
|
|
30602
31125
|
var SHA256 = /^[a-f0-9]{64}$/u;
|
|
30603
31126
|
var VERSION2 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u;
|
|
30604
31127
|
function exactKeys(value, expected) {
|
|
@@ -30606,23 +31129,23 @@ function exactKeys(value, expected) {
|
|
|
30606
31129
|
return actual.length === expected.length && actual.every((key2, index) => key2 === [...expected].sort()[index]);
|
|
30607
31130
|
}
|
|
30608
31131
|
function normalizedRelative(value) {
|
|
30609
|
-
if (value.length === 0 || value.includes("\\") || value.includes("\0") ||
|
|
31132
|
+
if (value.length === 0 || value.includes("\\") || value.includes("\0") || path40.posix.isAbsolute(value)) {
|
|
30610
31133
|
throw new Error("Artifact path must be a normalized relative POSIX path");
|
|
30611
31134
|
}
|
|
30612
|
-
const normalized =
|
|
31135
|
+
const normalized = path40.posix.normalize(value);
|
|
30613
31136
|
if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../")) {
|
|
30614
31137
|
throw new Error("Artifact path escapes the payload root");
|
|
30615
31138
|
}
|
|
30616
31139
|
return value;
|
|
30617
31140
|
}
|
|
30618
31141
|
function validateLink(entryPath, target) {
|
|
30619
|
-
if (target.length === 0 || target.includes("\\") || target.includes("\0") ||
|
|
31142
|
+
if (target.length === 0 || target.includes("\\") || target.includes("\0") || path40.posix.isAbsolute(target)) {
|
|
30620
31143
|
throw new Error("Artifact symlink target must be relative");
|
|
30621
31144
|
}
|
|
30622
|
-
const resolved =
|
|
30623
|
-
|
|
31145
|
+
const resolved = path40.posix.normalize(
|
|
31146
|
+
path40.posix.join(path40.posix.dirname(entryPath), target)
|
|
30624
31147
|
);
|
|
30625
|
-
if (resolved === ".." || resolved.startsWith("../") ||
|
|
31148
|
+
if (resolved === ".." || resolved.startsWith("../") || path40.posix.isAbsolute(resolved)) {
|
|
30626
31149
|
throw new Error("Artifact symlink target escapes the payload root");
|
|
30627
31150
|
}
|
|
30628
31151
|
return target;
|
|
@@ -30633,7 +31156,7 @@ function canonicalEntry(entry) {
|
|
|
30633
31156
|
`;
|
|
30634
31157
|
}
|
|
30635
31158
|
function artifactTreeSha256(entries) {
|
|
30636
|
-
const hash =
|
|
31159
|
+
const hash = createHash6("sha256");
|
|
30637
31160
|
const ordered = [...entries].sort(
|
|
30638
31161
|
(left, right) => left.path.localeCompare(right.path)
|
|
30639
31162
|
);
|
|
@@ -30703,19 +31226,19 @@ function parseArtifactManifest(value) {
|
|
|
30703
31226
|
};
|
|
30704
31227
|
}
|
|
30705
31228
|
async function sha256File2(filePath) {
|
|
30706
|
-
return
|
|
31229
|
+
return createHash6("sha256").update(await fs36.readFile(filePath)).digest("hex");
|
|
30707
31230
|
}
|
|
30708
|
-
async function
|
|
30709
|
-
const directory =
|
|
30710
|
-
const children = await
|
|
31231
|
+
async function walk2(root, relative4 = "") {
|
|
31232
|
+
const directory = path40.join(root, ...relative4.split("/").filter(Boolean));
|
|
31233
|
+
const children = await fs36.readdir(directory, { withFileTypes: true });
|
|
30711
31234
|
const entries = [];
|
|
30712
31235
|
for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
30713
|
-
const childRelative =
|
|
31236
|
+
const childRelative = relative4 ? `${relative4}/${child.name}` : child.name;
|
|
30714
31237
|
normalizedRelative(childRelative);
|
|
30715
|
-
const childPath =
|
|
30716
|
-
const stat5 = await
|
|
31238
|
+
const childPath = path40.join(directory, child.name);
|
|
31239
|
+
const stat5 = await fs36.lstat(childPath);
|
|
30717
31240
|
if (stat5.isDirectory()) {
|
|
30718
|
-
entries.push(...await
|
|
31241
|
+
entries.push(...await walk2(root, childRelative));
|
|
30719
31242
|
} else if (stat5.isFile()) {
|
|
30720
31243
|
entries.push({
|
|
30721
31244
|
type: "file",
|
|
@@ -30725,7 +31248,7 @@ async function walk(root, relative3 = "") {
|
|
|
30725
31248
|
sha256: await sha256File2(childPath)
|
|
30726
31249
|
});
|
|
30727
31250
|
} else if (stat5.isSymbolicLink()) {
|
|
30728
|
-
const target = await
|
|
31251
|
+
const target = await fs36.readlink(childPath);
|
|
30729
31252
|
entries.push({
|
|
30730
31253
|
type: "symlink",
|
|
30731
31254
|
path: childRelative,
|
|
@@ -30738,19 +31261,19 @@ async function walk(root, relative3 = "") {
|
|
|
30738
31261
|
return entries;
|
|
30739
31262
|
}
|
|
30740
31263
|
async function ensureRealDirectory(root) {
|
|
30741
|
-
const stat5 = await
|
|
31264
|
+
const stat5 = await fs36.lstat(root);
|
|
30742
31265
|
if (stat5.isSymbolicLink()) throw new Error("Artifact root is a symbolic link");
|
|
30743
31266
|
if (!stat5.isDirectory()) throw new Error("Artifact root is not a directory");
|
|
30744
31267
|
}
|
|
30745
31268
|
async function validateResolvedLinks(root, entries) {
|
|
30746
|
-
const realRoot = await
|
|
31269
|
+
const realRoot = await fs36.realpath(root);
|
|
30747
31270
|
for (const entry of entries) {
|
|
30748
31271
|
if (entry.type !== "symlink") continue;
|
|
30749
31272
|
try {
|
|
30750
|
-
const linkPath =
|
|
30751
|
-
const resolved = await
|
|
30752
|
-
const
|
|
30753
|
-
if (
|
|
31273
|
+
const linkPath = path40.join(root, ...entry.path.split("/"));
|
|
31274
|
+
const resolved = await fs36.realpath(linkPath);
|
|
31275
|
+
const relative4 = path40.relative(realRoot, resolved);
|
|
31276
|
+
if (relative4 === ".." || relative4.startsWith(`..${path40.sep}`) || path40.isAbsolute(relative4)) {
|
|
30754
31277
|
throw new Error("Artifact symlink chain escapes the payload root");
|
|
30755
31278
|
}
|
|
30756
31279
|
} catch (error) {
|
|
@@ -30763,7 +31286,7 @@ async function validateResolvedLinks(root, entries) {
|
|
|
30763
31286
|
}
|
|
30764
31287
|
async function buildArtifactManifest(payloadRoot, input) {
|
|
30765
31288
|
await ensureRealDirectory(payloadRoot);
|
|
30766
|
-
const entries = await
|
|
31289
|
+
const entries = await walk2(payloadRoot);
|
|
30767
31290
|
await validateResolvedLinks(payloadRoot, entries);
|
|
30768
31291
|
const manifest = {
|
|
30769
31292
|
schemaVersion: 1,
|
|
@@ -30780,13 +31303,13 @@ async function buildArtifactManifest(payloadRoot, input) {
|
|
|
30780
31303
|
return manifest;
|
|
30781
31304
|
}
|
|
30782
31305
|
async function readArtifactManifest(manifestPath) {
|
|
30783
|
-
const before = await
|
|
31306
|
+
const before = await fs36.lstat(manifestPath);
|
|
30784
31307
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
30785
31308
|
throw new Error("Artifact manifest is a symbolic link or non-file");
|
|
30786
31309
|
}
|
|
30787
31310
|
let handle;
|
|
30788
31311
|
try {
|
|
30789
|
-
handle = await
|
|
31312
|
+
handle = await fs36.open(
|
|
30790
31313
|
manifestPath,
|
|
30791
31314
|
constants.O_RDONLY | constants.O_NOFOLLOW
|
|
30792
31315
|
);
|
|
@@ -30803,16 +31326,16 @@ async function readArtifactManifest(manifestPath) {
|
|
|
30803
31326
|
}
|
|
30804
31327
|
}
|
|
30805
31328
|
function ensureContainedEntry(root, entryPath) {
|
|
30806
|
-
const absolute =
|
|
30807
|
-
const
|
|
30808
|
-
if (
|
|
31329
|
+
const absolute = path40.join(root, ...entryPath.split("/"));
|
|
31330
|
+
const relative4 = path40.relative(root, absolute);
|
|
31331
|
+
if (relative4.startsWith("..") || path40.isAbsolute(relative4)) {
|
|
30809
31332
|
throw new Error("Artifact entry escapes payload root");
|
|
30810
31333
|
}
|
|
30811
31334
|
return absolute;
|
|
30812
31335
|
}
|
|
30813
31336
|
async function verifyArtifactSource(payloadRoot, manifest) {
|
|
30814
31337
|
await ensureRealDirectory(payloadRoot);
|
|
30815
|
-
const actual = await
|
|
31338
|
+
const actual = await walk2(payloadRoot);
|
|
30816
31339
|
await validateResolvedLinks(payloadRoot, actual);
|
|
30817
31340
|
const declared = new Map(manifest.entries.map((entry) => [entry.path, entry]));
|
|
30818
31341
|
for (const entry of actual) {
|
|
@@ -30829,7 +31352,7 @@ async function verifyArtifactSource(payloadRoot, manifest) {
|
|
|
30829
31352
|
}
|
|
30830
31353
|
async function verifyArtifactPayload(payloadRoot, manifest) {
|
|
30831
31354
|
await ensureRealDirectory(payloadRoot);
|
|
30832
|
-
const actual = await
|
|
31355
|
+
const actual = await walk2(payloadRoot);
|
|
30833
31356
|
await validateResolvedLinks(payloadRoot, actual);
|
|
30834
31357
|
if (actual.length !== manifest.entries.length || artifactTreeSha256(actual) !== manifest.treeSha256) {
|
|
30835
31358
|
throw new Error("Artifact payload digest or size does not match manifest");
|
|
@@ -30844,27 +31367,27 @@ async function verifyArtifactPayload(payloadRoot, manifest) {
|
|
|
30844
31367
|
async function copyArtifactPayload(sourceRoot, targetRoot, manifest) {
|
|
30845
31368
|
await verifyArtifactSource(sourceRoot, manifest);
|
|
30846
31369
|
try {
|
|
30847
|
-
const targetStat = await
|
|
31370
|
+
const targetStat = await fs36.lstat(targetRoot);
|
|
30848
31371
|
if (targetStat.isSymbolicLink()) {
|
|
30849
31372
|
throw new Error("Install target is a symbolic link");
|
|
30850
31373
|
}
|
|
30851
31374
|
if (!targetStat.isDirectory()) throw new Error("Install target is not a directory");
|
|
30852
|
-
if ((await
|
|
31375
|
+
if ((await fs36.readdir(targetRoot)).length > 0) {
|
|
30853
31376
|
throw new Error("Install staging target is not empty");
|
|
30854
31377
|
}
|
|
30855
31378
|
} catch (error) {
|
|
30856
31379
|
if (error.code !== "ENOENT") throw error;
|
|
30857
|
-
await
|
|
31380
|
+
await fs36.mkdir(targetRoot, { recursive: false, mode: 448 });
|
|
30858
31381
|
}
|
|
30859
31382
|
for (const entry of manifest.entries) {
|
|
30860
31383
|
const source = ensureContainedEntry(sourceRoot, entry.path);
|
|
30861
31384
|
const target = ensureContainedEntry(targetRoot, entry.path);
|
|
30862
|
-
await
|
|
31385
|
+
await fs36.mkdir(path40.dirname(target), { recursive: true, mode: 448 });
|
|
30863
31386
|
if (entry.type === "file") {
|
|
30864
|
-
await
|
|
30865
|
-
await
|
|
31387
|
+
await fs36.copyFile(source, target);
|
|
31388
|
+
await fs36.chmod(target, entry.mode);
|
|
30866
31389
|
} else {
|
|
30867
|
-
await
|
|
31390
|
+
await fs36.symlink(entry.target, target);
|
|
30868
31391
|
}
|
|
30869
31392
|
}
|
|
30870
31393
|
await verifyArtifactPayload(targetRoot, manifest);
|
|
@@ -30881,39 +31404,39 @@ function commandError(error, message) {
|
|
|
30881
31404
|
return wrapped;
|
|
30882
31405
|
}
|
|
30883
31406
|
function execFileAsync(file, args) {
|
|
30884
|
-
return new Promise((
|
|
31407
|
+
return new Promise((resolve6, reject) => {
|
|
30885
31408
|
execFile(file, [...args], { windowsHide: true }, (error) => {
|
|
30886
31409
|
if (error) reject(commandError(error, "Login-item command failed"));
|
|
30887
|
-
else
|
|
31410
|
+
else resolve6();
|
|
30888
31411
|
});
|
|
30889
31412
|
});
|
|
30890
31413
|
}
|
|
30891
31414
|
function execFileOutput(file, args) {
|
|
30892
|
-
return new Promise((
|
|
31415
|
+
return new Promise((resolve6, reject) => {
|
|
30893
31416
|
execFile(
|
|
30894
31417
|
file,
|
|
30895
31418
|
[...args],
|
|
30896
31419
|
{ windowsHide: true },
|
|
30897
31420
|
(error, stdout) => {
|
|
30898
31421
|
if (error) reject(commandError(error, "Login-item query failed"));
|
|
30899
|
-
else
|
|
31422
|
+
else resolve6(stdout);
|
|
30900
31423
|
}
|
|
30901
31424
|
);
|
|
30902
31425
|
});
|
|
30903
31426
|
}
|
|
30904
31427
|
async function setCompanionStartAtLogin(input) {
|
|
30905
31428
|
if (input.platform === "darwin") {
|
|
30906
|
-
const launchAgents =
|
|
30907
|
-
const registration =
|
|
31429
|
+
const launchAgents = path41.join(input.homeDirectory, "Library", "LaunchAgents");
|
|
31430
|
+
const registration = path41.join(
|
|
30908
31431
|
launchAgents,
|
|
30909
31432
|
"com.m8t.companion.plist"
|
|
30910
31433
|
);
|
|
30911
31434
|
await assertNotSymlink(registration, "Start-at-login registration");
|
|
30912
31435
|
if (!input.enabled) {
|
|
30913
|
-
await
|
|
31436
|
+
await fs37.rm(registration, { force: true });
|
|
30914
31437
|
return;
|
|
30915
31438
|
}
|
|
30916
|
-
await
|
|
31439
|
+
await fs37.mkdir(launchAgents, { recursive: true, mode: 448 });
|
|
30917
31440
|
const plist = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict><key>Label</key><string>com.m8t.companion</string><key>ProgramArguments</key><array><string>' + xmlEscape(input.executable) + "</string></array><key>RunAtLoad</key><true/></dict></plist>\n";
|
|
30918
31441
|
await atomicWriteText(registration, plist, 384);
|
|
30919
31442
|
return;
|
|
@@ -30965,7 +31488,7 @@ async function setCompanionStartAtLogin(input) {
|
|
|
30965
31488
|
}
|
|
30966
31489
|
async function getCompanionStartAtLogin(input) {
|
|
30967
31490
|
if (input.platform === "darwin") {
|
|
30968
|
-
const registration =
|
|
31491
|
+
const registration = path41.join(
|
|
30969
31492
|
input.homeDirectory,
|
|
30970
31493
|
"Library",
|
|
30971
31494
|
"LaunchAgents",
|
|
@@ -31027,26 +31550,26 @@ function assertSupported(options) {
|
|
|
31027
31550
|
}
|
|
31028
31551
|
function companionInstallPaths(options) {
|
|
31029
31552
|
assertSupported(options);
|
|
31030
|
-
const companionState =
|
|
31553
|
+
const companionState = path41.join(
|
|
31031
31554
|
options.homeDirectory,
|
|
31032
31555
|
".m8t",
|
|
31033
31556
|
"companion"
|
|
31034
31557
|
);
|
|
31035
|
-
const targetRoot = options.platform === "darwin" ?
|
|
31558
|
+
const targetRoot = options.platform === "darwin" ? path41.join(
|
|
31036
31559
|
options.homeDirectory,
|
|
31037
31560
|
"Applications",
|
|
31038
31561
|
"m8t Companion.app"
|
|
31039
|
-
) :
|
|
31040
|
-
options.localAppData ??
|
|
31562
|
+
) : path41.join(
|
|
31563
|
+
options.localAppData ?? path41.join(options.homeDirectory, "AppData", "Local"),
|
|
31041
31564
|
"m8t",
|
|
31042
31565
|
"companion",
|
|
31043
31566
|
"app"
|
|
31044
31567
|
);
|
|
31045
31568
|
return {
|
|
31046
31569
|
targetRoot,
|
|
31047
|
-
installManifest:
|
|
31048
|
-
runtimeBinding:
|
|
31049
|
-
preferences:
|
|
31570
|
+
installManifest: path41.join(companionState, "install-v1.json"),
|
|
31571
|
+
runtimeBinding: path41.join(companionState, "runtime-v1.json"),
|
|
31572
|
+
preferences: path41.join(companionState, "preferences-v1.json")
|
|
31050
31573
|
};
|
|
31051
31574
|
}
|
|
31052
31575
|
function exactKeys2(value, keys) {
|
|
@@ -31057,7 +31580,7 @@ function parseInstallManifest(value) {
|
|
|
31057
31580
|
throw new Error("Install manifest schema is invalid");
|
|
31058
31581
|
}
|
|
31059
31582
|
const record = value;
|
|
31060
|
-
if (!exactKeys2(record, INSTALL_KEYS) || record.schemaVersion !== 1 || typeof record.version !== "string" || record.platform !== "darwin" && record.platform !== "win32" || record.architecture !== "arm64" && record.architecture !== "x64" || typeof record.entryRelativePath !== "string" || typeof record.artifactTreeSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(record.artifactTreeSha256) || typeof record.ownedTargetRoot !== "string" || !
|
|
31583
|
+
if (!exactKeys2(record, INSTALL_KEYS) || record.schemaVersion !== 1 || typeof record.version !== "string" || record.platform !== "darwin" && record.platform !== "win32" || record.architecture !== "arm64" && record.architecture !== "x64" || typeof record.entryRelativePath !== "string" || typeof record.artifactTreeSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(record.artifactTreeSha256) || typeof record.ownedTargetRoot !== "string" || !path41.isAbsolute(record.ownedTargetRoot)) {
|
|
31061
31584
|
throw new Error("Install manifest schema is invalid");
|
|
31062
31585
|
}
|
|
31063
31586
|
return record;
|
|
@@ -31067,7 +31590,7 @@ function parseRuntimeBinding(value, platform) {
|
|
|
31067
31590
|
throw new Error("Runtime binding schema is invalid");
|
|
31068
31591
|
}
|
|
31069
31592
|
const record = value;
|
|
31070
|
-
const paths = platform === "win32" ?
|
|
31593
|
+
const paths = platform === "win32" ? path41.win32 : path41.posix;
|
|
31071
31594
|
if (!exactKeys2(record, RUNTIME_KEYS) || record.schemaVersion !== 1 || typeof record.nodeExecutable !== "string" || !paths.isAbsolute(record.nodeExecutable) || typeof record.cliEntry !== "string" || !paths.isAbsolute(record.cliEntry) || typeof record.gatewayOrigin !== "string") {
|
|
31072
31595
|
throw new Error("Runtime binding schema is invalid");
|
|
31073
31596
|
}
|
|
@@ -31092,7 +31615,7 @@ function validateGatewayOrigin(value) {
|
|
|
31092
31615
|
}
|
|
31093
31616
|
async function assertNotSymlink(filePath, kind) {
|
|
31094
31617
|
try {
|
|
31095
|
-
if ((await
|
|
31618
|
+
if ((await fs37.lstat(filePath)).isSymbolicLink()) {
|
|
31096
31619
|
throw new Error(`${kind} is a symbolic link`);
|
|
31097
31620
|
}
|
|
31098
31621
|
} catch (error) {
|
|
@@ -31101,20 +31624,20 @@ async function assertNotSymlink(filePath, kind) {
|
|
|
31101
31624
|
}
|
|
31102
31625
|
}
|
|
31103
31626
|
async function assertOwnedDirectoryChain(anchor, targetDirectory) {
|
|
31104
|
-
const
|
|
31105
|
-
if (
|
|
31627
|
+
const relative4 = path41.relative(anchor, targetDirectory);
|
|
31628
|
+
if (relative4 === ".." || relative4.startsWith(`..${path41.sep}`) || path41.isAbsolute(relative4)) {
|
|
31106
31629
|
throw new Error("Companion owned directory escapes its trusted anchor");
|
|
31107
31630
|
}
|
|
31108
|
-
const segments =
|
|
31631
|
+
const segments = relative4.split(path41.sep).filter(Boolean);
|
|
31109
31632
|
let current = anchor;
|
|
31110
|
-
const anchorStatus = await
|
|
31633
|
+
const anchorStatus = await fs37.lstat(anchor);
|
|
31111
31634
|
if (anchorStatus.isSymbolicLink() || !anchorStatus.isDirectory()) {
|
|
31112
31635
|
throw new Error("Companion owned directory anchor is unsafe");
|
|
31113
31636
|
}
|
|
31114
31637
|
for (const segment of segments) {
|
|
31115
|
-
current =
|
|
31638
|
+
current = path41.join(current, segment);
|
|
31116
31639
|
try {
|
|
31117
|
-
const status = await
|
|
31640
|
+
const status = await fs37.lstat(current);
|
|
31118
31641
|
if (status.isSymbolicLink() || !status.isDirectory()) {
|
|
31119
31642
|
throw new Error("Companion owned directory contains a symbolic link");
|
|
31120
31643
|
}
|
|
@@ -31127,19 +31650,19 @@ async function assertOwnedDirectoryChain(anchor, targetDirectory) {
|
|
|
31127
31650
|
async function assertOwnedParents(options, paths) {
|
|
31128
31651
|
await assertOwnedDirectoryChain(
|
|
31129
31652
|
options.homeDirectory,
|
|
31130
|
-
|
|
31653
|
+
path41.dirname(paths.installManifest)
|
|
31131
31654
|
);
|
|
31132
|
-
const targetAnchor = options.platform === "win32" ? options.localAppData ??
|
|
31133
|
-
await assertOwnedDirectoryChain(targetAnchor,
|
|
31655
|
+
const targetAnchor = options.platform === "win32" ? options.localAppData ?? path41.join(options.homeDirectory, "AppData", "Local") : options.homeDirectory;
|
|
31656
|
+
await assertOwnedDirectoryChain(targetAnchor, path41.dirname(paths.targetRoot));
|
|
31134
31657
|
}
|
|
31135
31658
|
async function readRegularText(filePath, maxBytes) {
|
|
31136
|
-
const before = await
|
|
31659
|
+
const before = await fs37.lstat(filePath);
|
|
31137
31660
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
31138
31661
|
throw new Error("Companion state file is not a regular file");
|
|
31139
31662
|
}
|
|
31140
31663
|
let handle;
|
|
31141
31664
|
try {
|
|
31142
|
-
handle = await
|
|
31665
|
+
handle = await fs37.open(
|
|
31143
31666
|
filePath,
|
|
31144
31667
|
constants2.O_RDONLY | constants2.O_NOFOLLOW
|
|
31145
31668
|
);
|
|
@@ -31162,27 +31685,27 @@ async function atomicWriteJson(filePath, value) {
|
|
|
31162
31685
|
}
|
|
31163
31686
|
async function atomicWriteText(filePath, contents, mode) {
|
|
31164
31687
|
await assertNotSymlink(filePath, "Companion state file");
|
|
31165
|
-
await
|
|
31688
|
+
await fs37.mkdir(path41.dirname(filePath), {
|
|
31166
31689
|
recursive: true,
|
|
31167
31690
|
mode: 448
|
|
31168
31691
|
});
|
|
31169
31692
|
const temporary = `${filePath}.${randomUUID3()}.tmp`;
|
|
31170
31693
|
try {
|
|
31171
|
-
await
|
|
31694
|
+
await fs37.writeFile(temporary, contents, {
|
|
31172
31695
|
mode,
|
|
31173
31696
|
flag: "wx"
|
|
31174
31697
|
});
|
|
31175
|
-
await
|
|
31176
|
-
await
|
|
31698
|
+
await fs37.rename(temporary, filePath);
|
|
31699
|
+
await fs37.chmod(filePath, mode).catch(() => void 0);
|
|
31177
31700
|
} finally {
|
|
31178
|
-
await
|
|
31701
|
+
await fs37.rm(temporary, { force: true }).catch(() => void 0);
|
|
31179
31702
|
}
|
|
31180
31703
|
}
|
|
31181
31704
|
async function realRegularFile(filePath, executable) {
|
|
31182
|
-
const real = await
|
|
31183
|
-
const stat5 = await
|
|
31705
|
+
const real = await fs37.realpath(filePath);
|
|
31706
|
+
const stat5 = await fs37.stat(real);
|
|
31184
31707
|
if (!stat5.isFile()) throw new Error("Companion launch target is not a file");
|
|
31185
|
-
await
|
|
31708
|
+
await fs37.access(real, executable ? constants2.X_OK : constants2.R_OK);
|
|
31186
31709
|
return real;
|
|
31187
31710
|
}
|
|
31188
31711
|
function defaultLaunch(executable) {
|
|
@@ -31198,7 +31721,7 @@ async function companionIsRunning(platform, executable) {
|
|
|
31198
31721
|
if (platform !== "win32") return false;
|
|
31199
31722
|
let handle;
|
|
31200
31723
|
try {
|
|
31201
|
-
handle = await
|
|
31724
|
+
handle = await fs37.open(executable, "r+");
|
|
31202
31725
|
} catch (error) {
|
|
31203
31726
|
const code = error.code;
|
|
31204
31727
|
if (code === "ENOENT") return false;
|
|
@@ -31279,7 +31802,7 @@ async function statusCompanion(options) {
|
|
|
31279
31802
|
realRegularFile(runtime.nodeExecutable, options.platform !== "win32"),
|
|
31280
31803
|
realRegularFile(runtime.cliEntry, false)
|
|
31281
31804
|
]);
|
|
31282
|
-
const executable =
|
|
31805
|
+
const executable = path41.join(
|
|
31283
31806
|
paths.targetRoot,
|
|
31284
31807
|
...install.entryRelativePath.split("/")
|
|
31285
31808
|
);
|
|
@@ -31312,7 +31835,7 @@ async function snapshotFile(filePath) {
|
|
|
31312
31835
|
}
|
|
31313
31836
|
async function restoreFile(filePath, bytes) {
|
|
31314
31837
|
if (bytes === null) {
|
|
31315
|
-
await
|
|
31838
|
+
await fs37.rm(filePath, { force: true });
|
|
31316
31839
|
} else {
|
|
31317
31840
|
await atomicWriteJson(filePath, JSON.parse(bytes.toString("utf8")));
|
|
31318
31841
|
}
|
|
@@ -31327,8 +31850,8 @@ async function converge(options, force) {
|
|
|
31327
31850
|
if (artifactManifest.platform !== options.platform || artifactManifest.architecture !== options.architecture) {
|
|
31328
31851
|
throw new Error("Companion artifact does not match this OS and architecture");
|
|
31329
31852
|
}
|
|
31330
|
-
const payloadRoot =
|
|
31331
|
-
|
|
31853
|
+
const payloadRoot = path41.join(
|
|
31854
|
+
path41.dirname(options.artifactManifestPath),
|
|
31332
31855
|
"payload"
|
|
31333
31856
|
);
|
|
31334
31857
|
await verifyArtifactSource(payloadRoot, artifactManifest);
|
|
@@ -31348,7 +31871,7 @@ async function converge(options, force) {
|
|
|
31348
31871
|
if (priorInstalled) {
|
|
31349
31872
|
await assertCompanionNotRunning(
|
|
31350
31873
|
options,
|
|
31351
|
-
|
|
31874
|
+
path41.join(
|
|
31352
31875
|
priorInstalled.paths.targetRoot,
|
|
31353
31876
|
...priorInstalled.install.entryRelativePath.split("/")
|
|
31354
31877
|
)
|
|
@@ -31356,7 +31879,7 @@ async function converge(options, force) {
|
|
|
31356
31879
|
}
|
|
31357
31880
|
if (current.state === "not-installed") {
|
|
31358
31881
|
try {
|
|
31359
|
-
await
|
|
31882
|
+
await fs37.lstat(paths.targetRoot);
|
|
31360
31883
|
throw new Error(
|
|
31361
31884
|
"The fixed companion target exists without an owned install manifest"
|
|
31362
31885
|
);
|
|
@@ -31367,7 +31890,7 @@ async function converge(options, force) {
|
|
|
31367
31890
|
const stage = `${paths.targetRoot}.m8t-stage-${randomUUID3()}`;
|
|
31368
31891
|
const backup = `${paths.targetRoot}.m8t-backup-${randomUUID3()}`;
|
|
31369
31892
|
const copy = options.copyPayload ?? copyArtifactPayload;
|
|
31370
|
-
await
|
|
31893
|
+
await fs37.mkdir(path41.dirname(paths.targetRoot), {
|
|
31371
31894
|
recursive: true,
|
|
31372
31895
|
mode: 448
|
|
31373
31896
|
});
|
|
@@ -31384,12 +31907,12 @@ async function converge(options, force) {
|
|
|
31384
31907
|
try {
|
|
31385
31908
|
await copy(payloadRoot, stage, artifactManifest);
|
|
31386
31909
|
try {
|
|
31387
|
-
await
|
|
31910
|
+
await fs37.rename(paths.targetRoot, backup);
|
|
31388
31911
|
movedPrior = true;
|
|
31389
31912
|
} catch (error) {
|
|
31390
31913
|
if (error.code !== "ENOENT") throw error;
|
|
31391
31914
|
}
|
|
31392
|
-
await
|
|
31915
|
+
await fs37.rename(stage, paths.targetRoot);
|
|
31393
31916
|
installedStage = true;
|
|
31394
31917
|
const nodeExecutable = await realRegularFile(
|
|
31395
31918
|
options.nodeExecutable,
|
|
@@ -31431,7 +31954,7 @@ async function converge(options, force) {
|
|
|
31431
31954
|
await readClosedJson(paths.runtimeBinding),
|
|
31432
31955
|
options.platform
|
|
31433
31956
|
);
|
|
31434
|
-
const executable =
|
|
31957
|
+
const executable = path41.join(
|
|
31435
31958
|
paths.targetRoot,
|
|
31436
31959
|
...artifactManifest.entryRelativePath.split("/")
|
|
31437
31960
|
);
|
|
@@ -31441,7 +31964,7 @@ async function converge(options, force) {
|
|
|
31441
31964
|
platform: options.platform,
|
|
31442
31965
|
executable: ownedExecutable
|
|
31443
31966
|
}));
|
|
31444
|
-
priorLoginExecutable = priorInstalled ?
|
|
31967
|
+
priorLoginExecutable = priorInstalled ? path41.join(
|
|
31445
31968
|
priorInstalled.paths.targetRoot,
|
|
31446
31969
|
...priorInstalled.install.entryRelativePath.split("/")
|
|
31447
31970
|
) : executable;
|
|
@@ -31452,7 +31975,7 @@ async function converge(options, force) {
|
|
|
31452
31975
|
);
|
|
31453
31976
|
loginChanged = true;
|
|
31454
31977
|
await (options.launch ?? defaultLaunch)(executable);
|
|
31455
|
-
await
|
|
31978
|
+
await fs37.rm(backup, { recursive: true, force: true });
|
|
31456
31979
|
return {
|
|
31457
31980
|
state: "installed",
|
|
31458
31981
|
version: artifactManifest.version,
|
|
@@ -31466,15 +31989,15 @@ async function converge(options, force) {
|
|
|
31466
31989
|
() => void 0
|
|
31467
31990
|
);
|
|
31468
31991
|
}
|
|
31469
|
-
await
|
|
31992
|
+
await fs37.rm(stage, { recursive: true, force: true }).catch(() => void 0);
|
|
31470
31993
|
if (installedStage) {
|
|
31471
|
-
await
|
|
31994
|
+
await fs37.rm(paths.targetRoot, {
|
|
31472
31995
|
recursive: true,
|
|
31473
31996
|
force: true
|
|
31474
31997
|
}).catch(() => void 0);
|
|
31475
31998
|
}
|
|
31476
31999
|
if (movedPrior) {
|
|
31477
|
-
await
|
|
32000
|
+
await fs37.rename(backup, paths.targetRoot).catch(() => void 0);
|
|
31478
32001
|
}
|
|
31479
32002
|
await Promise.all([
|
|
31480
32003
|
restoreFile(paths.installManifest, snapshots[0]),
|
|
@@ -31500,14 +32023,14 @@ async function uninstallCompanion(options) {
|
|
|
31500
32023
|
if (install.ownedTargetRoot !== paths.targetRoot) {
|
|
31501
32024
|
throw new Error("Refusing to remove a non-owned companion target");
|
|
31502
32025
|
}
|
|
31503
|
-
const executable =
|
|
32026
|
+
const executable = path41.join(
|
|
31504
32027
|
paths.targetRoot,
|
|
31505
32028
|
...install.entryRelativePath.split("/")
|
|
31506
32029
|
);
|
|
31507
32030
|
await assertCompanionNotRunning(options, executable);
|
|
31508
32031
|
await (options.setStartAtLogin ?? (() => Promise.resolve()))(executable, false);
|
|
31509
32032
|
const remove = options.removeOwnedPath ?? (async (ownedPath, recursive) => {
|
|
31510
|
-
await
|
|
32033
|
+
await fs37.rm(ownedPath, { recursive, force: true });
|
|
31511
32034
|
});
|
|
31512
32035
|
await remove(paths.targetRoot, true);
|
|
31513
32036
|
await remove(paths.installManifest, false);
|
|
@@ -31517,9 +32040,9 @@ async function uninstallCompanion(options) {
|
|
|
31517
32040
|
}
|
|
31518
32041
|
|
|
31519
32042
|
// src/commands/companion/install.ts
|
|
31520
|
-
import * as
|
|
31521
|
-
import * as
|
|
31522
|
-
import { Command as
|
|
32043
|
+
import * as os18 from "os";
|
|
32044
|
+
import * as path43 from "path";
|
|
32045
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
31523
32046
|
|
|
31524
32047
|
// src/lib/companion-channel.ts
|
|
31525
32048
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -31534,18 +32057,18 @@ async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps =
|
|
|
31534
32057
|
}
|
|
31535
32058
|
|
|
31536
32059
|
// src/lib/companion-download.ts
|
|
31537
|
-
import { createHash as
|
|
32060
|
+
import { createHash as createHash7 } from "crypto";
|
|
31538
32061
|
import { execFile as execFile2 } from "child_process";
|
|
31539
|
-
import * as
|
|
31540
|
-
import * as
|
|
31541
|
-
import * as
|
|
32062
|
+
import * as fs38 from "fs/promises";
|
|
32063
|
+
import * as os17 from "os";
|
|
32064
|
+
import * as path42 from "path";
|
|
31542
32065
|
init_errors();
|
|
31543
32066
|
var MAX_ASSET_BYTES = 512 * 1024 * 1024;
|
|
31544
32067
|
function extractArchive(archive, into) {
|
|
31545
|
-
return new Promise((
|
|
32068
|
+
return new Promise((resolve6, reject) => {
|
|
31546
32069
|
execFile2("tar", ["-xzf", archive, "-C", into], { windowsHide: true }, (error) => {
|
|
31547
32070
|
if (error) reject(new Error(`Could not unpack the companion build: ${error.message}`));
|
|
31548
|
-
else
|
|
32071
|
+
else resolve6();
|
|
31549
32072
|
});
|
|
31550
32073
|
});
|
|
31551
32074
|
}
|
|
@@ -31583,7 +32106,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
31583
32106
|
message: `${pinned.asset} is larger than this command will accept.`
|
|
31584
32107
|
});
|
|
31585
32108
|
}
|
|
31586
|
-
const digest =
|
|
32109
|
+
const digest = createHash7("sha256").update(bytes).digest("hex");
|
|
31587
32110
|
if (digest !== pinned.sha256) {
|
|
31588
32111
|
throw new LocalCliError({
|
|
31589
32112
|
code: "COMPANION_ASSET_DIGEST_MISMATCH",
|
|
@@ -31591,20 +32114,20 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
31591
32114
|
hint: "Nothing was unpacked. This is what a corrupted download or a substituted file looks like \u2014 retry, and report it if it persists."
|
|
31592
32115
|
});
|
|
31593
32116
|
}
|
|
31594
|
-
const root = await (deps.makeTemporaryDirectory ?? (() =>
|
|
32117
|
+
const root = await (deps.makeTemporaryDirectory ?? (() => fs38.mkdtemp(path42.join(os17.tmpdir(), "m8t-companion-"))))();
|
|
31595
32118
|
const dispose = async () => {
|
|
31596
|
-
await
|
|
32119
|
+
await fs38.rm(root, { recursive: true, force: true }).catch(() => void 0);
|
|
31597
32120
|
};
|
|
31598
32121
|
try {
|
|
31599
|
-
const archive =
|
|
31600
|
-
await
|
|
31601
|
-
const unpacked =
|
|
31602
|
-
await
|
|
32122
|
+
const archive = path42.join(root, pinned.asset);
|
|
32123
|
+
await fs38.writeFile(archive, bytes, { mode: 384 });
|
|
32124
|
+
const unpacked = path42.join(root, "unpacked");
|
|
32125
|
+
await fs38.mkdir(unpacked, { recursive: false, mode: 448 });
|
|
31603
32126
|
await (deps.extract ?? extractArchive)(archive, unpacked);
|
|
31604
|
-
await
|
|
32127
|
+
await fs38.rm(archive, { force: true });
|
|
31605
32128
|
return {
|
|
31606
32129
|
version: component.version,
|
|
31607
|
-
artifactManifestPath:
|
|
32130
|
+
artifactManifestPath: path42.join(unpacked, "artifact-v1.json"),
|
|
31608
32131
|
dispose
|
|
31609
32132
|
};
|
|
31610
32133
|
} catch (error) {
|
|
@@ -31617,7 +32140,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
31617
32140
|
function defaultLocalCompanionInstallOptions() {
|
|
31618
32141
|
const cliEntry = process.argv[1];
|
|
31619
32142
|
if (!cliEntry) throw new Error("Cannot resolve the installed CLI entry");
|
|
31620
|
-
const homeDirectory =
|
|
32143
|
+
const homeDirectory = os18.homedir();
|
|
31621
32144
|
const platform = process.platform;
|
|
31622
32145
|
return {
|
|
31623
32146
|
homeDirectory,
|
|
@@ -31635,7 +32158,7 @@ async function convergeCompanionFromChannel(converge2, options = {}) {
|
|
|
31635
32158
|
let artifactManifestPath;
|
|
31636
32159
|
let dispose = () => Promise.resolve();
|
|
31637
32160
|
if (stagedDirectory !== void 0) {
|
|
31638
|
-
artifactManifestPath =
|
|
32161
|
+
artifactManifestPath = path43.resolve(stagedDirectory, "artifact-v1.json");
|
|
31639
32162
|
} else {
|
|
31640
32163
|
const release = await readCompanionRelease(
|
|
31641
32164
|
platformVersion !== void 0 ? { channel: true, version: platformTag(platformVersion) } : { url: CHANNEL_LATEST_URL }
|
|
@@ -31677,7 +32200,7 @@ Version: ${state.version}
|
|
|
31677
32200
|
}
|
|
31678
32201
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
31679
32202
|
static paths = [["companion", "install"]];
|
|
31680
|
-
static usage =
|
|
32203
|
+
static usage = Command57.Usage({
|
|
31681
32204
|
description: "Install the desktop companions for this user from the release channel.",
|
|
31682
32205
|
examples: [
|
|
31683
32206
|
["Install the released build", "$0 companion install"],
|
|
@@ -31687,10 +32210,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
31687
32210
|
]
|
|
31688
32211
|
]
|
|
31689
32212
|
});
|
|
31690
|
-
from =
|
|
32213
|
+
from = Option54.String("--from", {
|
|
31691
32214
|
description: "A locally staged build directory instead of the released one."
|
|
31692
32215
|
});
|
|
31693
|
-
resourceGroup =
|
|
32216
|
+
resourceGroup = Option54.String("--resource-group", {
|
|
31694
32217
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
31695
32218
|
});
|
|
31696
32219
|
async executeCommand() {
|
|
@@ -31713,7 +32236,7 @@ function resolveRepoRootMarker(args) {
|
|
|
31713
32236
|
}
|
|
31714
32237
|
async function looksLikeCheckout(dir) {
|
|
31715
32238
|
try {
|
|
31716
|
-
return (await
|
|
32239
|
+
return (await fs39.stat(path44.join(dir, "brain-template"))).isDirectory();
|
|
31717
32240
|
} catch {
|
|
31718
32241
|
return false;
|
|
31719
32242
|
}
|
|
@@ -31729,21 +32252,21 @@ var defaultDeps2 = {
|
|
|
31729
32252
|
convergeCompanion: (platformVersion) => convergeCompanionFromChannel(installCompanion, {
|
|
31730
32253
|
...platformVersion !== void 0 ? { platformVersion } : {}
|
|
31731
32254
|
}),
|
|
31732
|
-
homedir: () =>
|
|
32255
|
+
homedir: () => os19.homedir()
|
|
31733
32256
|
};
|
|
31734
32257
|
async function finalizeInstall(args, deps = defaultDeps2) {
|
|
31735
|
-
const markerDir =
|
|
31736
|
-
const markerPath =
|
|
32258
|
+
const markerDir = path44.join(deps.homedir(), ".m8t");
|
|
32259
|
+
const markerPath = path44.join(markerDir, "repo-root");
|
|
31737
32260
|
const cwd = process.cwd();
|
|
31738
|
-
const existing = await
|
|
32261
|
+
const existing = await fs39.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
|
|
31739
32262
|
const repoRoot = resolveRepoRootMarker({
|
|
31740
32263
|
...args.repoRoot !== void 0 ? { explicit: args.repoRoot } : {},
|
|
31741
32264
|
cwd,
|
|
31742
32265
|
cwdIsCheckout: await looksLikeCheckout(cwd),
|
|
31743
32266
|
existing
|
|
31744
32267
|
});
|
|
31745
|
-
await
|
|
31746
|
-
await
|
|
32268
|
+
await fs39.mkdir(markerDir, { recursive: true });
|
|
32269
|
+
await fs39.writeFile(markerPath, `${repoRoot}
|
|
31747
32270
|
`, "utf8");
|
|
31748
32271
|
let webappUrl;
|
|
31749
32272
|
try {
|
|
@@ -31821,7 +32344,7 @@ async function finalizeInstall(args, deps = defaultDeps2) {
|
|
|
31821
32344
|
}
|
|
31822
32345
|
let brainOrg = null;
|
|
31823
32346
|
try {
|
|
31824
|
-
const credsRaw = await
|
|
32347
|
+
const credsRaw = await fs39.readFile(path44.join(markerDir, "github-app.json"), "utf8");
|
|
31825
32348
|
const creds = JSON.parse(credsRaw);
|
|
31826
32349
|
brainOrg = typeof creds.org === "string" ? creds.org : null;
|
|
31827
32350
|
} catch {
|
|
@@ -31903,7 +32426,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
31903
32426
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
31904
32427
|
// through — it prints a deprecation notice and does the right thing.
|
|
31905
32428
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
31906
|
-
static usage =
|
|
32429
|
+
static usage = Command58.Usage({
|
|
31907
32430
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
31908
32431
|
details: "Reads the durable status blob written by the installer. --watch polls until the install reaches done or failed.\n\nOn reaching done under --watch this also completes the local half of the install, which nothing else in the bootstrap path can do: it registers the webapp's sign-in redirect URI (the installer runs as a managed identity with no directory role, and at launch time the gateway FQDN did not exist yet), writes ~/.m8t/repo-root and the gateway discovery cache, and seeds your advisors' brains from the onboarding intake. Re-runnable \u2014 `m8t bootstrap finish` is a deprecated alias that does exactly this on an already-done install.",
|
|
31909
32432
|
examples: [
|
|
@@ -31912,12 +32435,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
31912
32435
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
31913
32436
|
]
|
|
31914
32437
|
});
|
|
31915
|
-
watch =
|
|
31916
|
-
output =
|
|
31917
|
-
repoRoot =
|
|
31918
|
-
finalize =
|
|
31919
|
-
subscription =
|
|
31920
|
-
resourceGroup =
|
|
32438
|
+
watch = Option55.Boolean("--watch", false);
|
|
32439
|
+
output = Option55.String("--output");
|
|
32440
|
+
repoRoot = Option55.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
|
|
32441
|
+
finalize = Option55.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
|
|
32442
|
+
subscription = Option55.String("--subscription");
|
|
32443
|
+
resourceGroup = Option55.String("--resource-group");
|
|
31921
32444
|
async executeCommand() {
|
|
31922
32445
|
const state = await readBootstrapState();
|
|
31923
32446
|
if (!state) {
|
|
@@ -32050,7 +32573,7 @@ function formatStatus(d) {
|
|
|
32050
32573
|
}
|
|
32051
32574
|
|
|
32052
32575
|
// src/commands/bootstrap/reap.ts
|
|
32053
|
-
import { Command as
|
|
32576
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
32054
32577
|
init_errors();
|
|
32055
32578
|
|
|
32056
32579
|
// src/lib/bootstrap-reap.ts
|
|
@@ -32144,7 +32667,7 @@ async function reapInstaller(opts) {
|
|
|
32144
32667
|
// src/commands/bootstrap/reap.ts
|
|
32145
32668
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
32146
32669
|
static paths = [["bootstrap", "reap"]];
|
|
32147
|
-
static usage =
|
|
32670
|
+
static usage = Command59.Usage({
|
|
32148
32671
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
32149
32672
|
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.\n\n--sweep-orphans is a DIFFERENT and much broader mode: instead of reaping this install, it scans the WHOLE SUBSCRIPTION for m8t role assignments left behind by installs whose resources are gone \u2014 orphaned installer Owner-at-subscription-scope grants and orphaned gateway subscription-scope roles. It is a dry run that only lists what it found unless you also pass --yes, which deletes them.",
|
|
32150
32673
|
examples: [
|
|
@@ -32154,9 +32677,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
32154
32677
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
32155
32678
|
]
|
|
32156
32679
|
});
|
|
32157
|
-
force =
|
|
32158
|
-
sweepOrphans =
|
|
32159
|
-
yes =
|
|
32680
|
+
force = Option56.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
|
|
32681
|
+
sweepOrphans = Option56.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
|
|
32682
|
+
yes = Option56.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
|
|
32160
32683
|
async executeCommand() {
|
|
32161
32684
|
if (this.sweepOrphans === true) {
|
|
32162
32685
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -32250,18 +32773,18 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
32250
32773
|
};
|
|
32251
32774
|
|
|
32252
32775
|
// src/commands/bootstrap/ui.ts
|
|
32253
|
-
import * as
|
|
32254
|
-
import * as
|
|
32255
|
-
import * as
|
|
32256
|
-
import { Command as
|
|
32776
|
+
import * as fs41 from "fs";
|
|
32777
|
+
import * as os21 from "os";
|
|
32778
|
+
import * as path46 from "path";
|
|
32779
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
32257
32780
|
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
32258
32781
|
init_errors();
|
|
32259
32782
|
|
|
32260
32783
|
// src/lib/bootstrap-ui.ts
|
|
32261
|
-
import * as
|
|
32784
|
+
import * as fs40 from "fs";
|
|
32262
32785
|
import * as net from "net";
|
|
32263
|
-
import * as
|
|
32264
|
-
import * as
|
|
32786
|
+
import * as os20 from "os";
|
|
32787
|
+
import * as path45 from "path";
|
|
32265
32788
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
32266
32789
|
import { spawn as spawn8, spawnSync as spawnSync6 } from "child_process";
|
|
32267
32790
|
init_errors();
|
|
@@ -32319,7 +32842,7 @@ async function deployIntakeAgent(args) {
|
|
|
32319
32842
|
|
|
32320
32843
|
// src/lib/bootstrap-ui.ts
|
|
32321
32844
|
function sleep3(ms) {
|
|
32322
|
-
return new Promise((
|
|
32845
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
32323
32846
|
}
|
|
32324
32847
|
function isAuthorizationShapedError(error) {
|
|
32325
32848
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -32459,11 +32982,11 @@ async function ensureFounderFoundryRole(args) {
|
|
|
32459
32982
|
});
|
|
32460
32983
|
}
|
|
32461
32984
|
function writeWebEnvLocal(args) {
|
|
32462
|
-
const envPath =
|
|
32985
|
+
const envPath = path45.join(args.repoRoot, "apps", "web", ".env.local");
|
|
32463
32986
|
let prior = "";
|
|
32464
|
-
if (
|
|
32465
|
-
prior =
|
|
32466
|
-
|
|
32987
|
+
if (fs40.existsSync(envPath)) {
|
|
32988
|
+
prior = fs40.readFileSync(envPath, "utf8");
|
|
32989
|
+
fs40.copyFileSync(envPath, envPath + ".bak");
|
|
32467
32990
|
}
|
|
32468
32991
|
const priorSecret = readValidInternalSecret(prior);
|
|
32469
32992
|
const internalSecret = priorSecret ?? (args.randomBytesImpl ?? randomBytes3)(32).toString("base64url");
|
|
@@ -32489,9 +33012,9 @@ function writeWebEnvLocal(args) {
|
|
|
32489
33012
|
"NEXT_PUBLIC_M8T_ONBOARDING=1",
|
|
32490
33013
|
""
|
|
32491
33014
|
].join("\n");
|
|
32492
|
-
|
|
33015
|
+
fs40.writeFileSync(envPath, body, { encoding: "utf8", mode: 384 });
|
|
32493
33016
|
try {
|
|
32494
|
-
|
|
33017
|
+
fs40.chmodSync(envPath, 384);
|
|
32495
33018
|
} catch {
|
|
32496
33019
|
}
|
|
32497
33020
|
return envPath;
|
|
@@ -32507,10 +33030,10 @@ function readValidInternalSecret(envText) {
|
|
|
32507
33030
|
return isValidInternalSecret(values[0]) ? values[0] : void 0;
|
|
32508
33031
|
}
|
|
32509
33032
|
function readWebInternalSecret(repoRoot) {
|
|
32510
|
-
const envPath =
|
|
33033
|
+
const envPath = path45.join(repoRoot, "apps", "web", ".env.local");
|
|
32511
33034
|
let secret;
|
|
32512
33035
|
try {
|
|
32513
|
-
secret = readValidInternalSecret(
|
|
33036
|
+
secret = readValidInternalSecret(fs40.readFileSync(envPath, "utf8"));
|
|
32514
33037
|
} catch {
|
|
32515
33038
|
secret = void 0;
|
|
32516
33039
|
}
|
|
@@ -32534,14 +33057,14 @@ function assertNodeVersion(versionString = process.version) {
|
|
|
32534
33057
|
}
|
|
32535
33058
|
}
|
|
32536
33059
|
function runInherit(cmd, cmdArgs, cwd, failCode) {
|
|
32537
|
-
return new Promise((
|
|
33060
|
+
return new Promise((resolve6, reject) => {
|
|
32538
33061
|
const child = spawn8(cmd, cmdArgs, { cwd, stdio: "inherit", shell: process.platform === "win32" });
|
|
32539
33062
|
child.on("error", (e) => {
|
|
32540
33063
|
reject(new LocalCliError({ code: failCode, message: `Failed to start '${cmd}': ${e.message}` }));
|
|
32541
33064
|
});
|
|
32542
33065
|
child.on("close", (code) => {
|
|
32543
33066
|
if (code === 0) {
|
|
32544
|
-
|
|
33067
|
+
resolve6();
|
|
32545
33068
|
} else {
|
|
32546
33069
|
const opts = {
|
|
32547
33070
|
code: failCode,
|
|
@@ -32561,40 +33084,40 @@ async function installWebDeps(repoRoot) {
|
|
|
32561
33084
|
});
|
|
32562
33085
|
await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
|
|
32563
33086
|
}
|
|
32564
|
-
function onboardingUiPaths(home =
|
|
32565
|
-
const dir =
|
|
33087
|
+
function onboardingUiPaths(home = os20.homedir()) {
|
|
33088
|
+
const dir = path45.join(home, ".m8t");
|
|
32566
33089
|
return {
|
|
32567
|
-
logPath:
|
|
32568
|
-
pidPath:
|
|
33090
|
+
logPath: path45.join(dir, "onboarding-ui.log"),
|
|
33091
|
+
pidPath: path45.join(dir, "onboarding-ui.pid")
|
|
32569
33092
|
};
|
|
32570
33093
|
}
|
|
32571
|
-
function onboardingRelayPaths(home =
|
|
32572
|
-
const dir =
|
|
33094
|
+
function onboardingRelayPaths(home = os20.homedir()) {
|
|
33095
|
+
const dir = path45.join(home, ".m8t");
|
|
32573
33096
|
return {
|
|
32574
|
-
logPath:
|
|
32575
|
-
pidPath:
|
|
33097
|
+
logPath: path45.join(dir, "onboarding-relay.log"),
|
|
33098
|
+
pidPath: path45.join(dir, "onboarding-relay.pid")
|
|
32576
33099
|
};
|
|
32577
33100
|
}
|
|
32578
33101
|
function isLocalPortOpen(port) {
|
|
32579
|
-
return new Promise((
|
|
33102
|
+
return new Promise((resolve6) => {
|
|
32580
33103
|
const s = net.createConnection({ host: "127.0.0.1", port });
|
|
32581
33104
|
s.setTimeout(1e3);
|
|
32582
33105
|
s.on("connect", () => {
|
|
32583
33106
|
s.destroy();
|
|
32584
|
-
|
|
33107
|
+
resolve6(true);
|
|
32585
33108
|
});
|
|
32586
33109
|
s.on("error", () => {
|
|
32587
|
-
|
|
33110
|
+
resolve6(false);
|
|
32588
33111
|
});
|
|
32589
33112
|
s.on("timeout", () => {
|
|
32590
33113
|
s.destroy();
|
|
32591
|
-
|
|
33114
|
+
resolve6(false);
|
|
32592
33115
|
});
|
|
32593
33116
|
});
|
|
32594
33117
|
}
|
|
32595
33118
|
function removePidFile(pidPath) {
|
|
32596
33119
|
try {
|
|
32597
|
-
|
|
33120
|
+
fs40.unlinkSync(pidPath);
|
|
32598
33121
|
} catch {
|
|
32599
33122
|
}
|
|
32600
33123
|
}
|
|
@@ -32607,7 +33130,7 @@ function parseManagedPid(text) {
|
|
|
32607
33130
|
function readLiveManagedPid(pidPath) {
|
|
32608
33131
|
let text;
|
|
32609
33132
|
try {
|
|
32610
|
-
text =
|
|
33133
|
+
text = fs40.readFileSync(pidPath, "utf8").trim();
|
|
32611
33134
|
} catch {
|
|
32612
33135
|
return null;
|
|
32613
33136
|
}
|
|
@@ -32646,8 +33169,8 @@ async function serveOnboardingUiDetached(args) {
|
|
|
32646
33169
|
});
|
|
32647
33170
|
}
|
|
32648
33171
|
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
32649
|
-
|
|
32650
|
-
const fd =
|
|
33172
|
+
fs40.mkdirSync(path45.dirname(logPath), { recursive: true });
|
|
33173
|
+
const fd = fs40.openSync(logPath, "a");
|
|
32651
33174
|
const child = spawn8("pnpm", ["--filter", "web", "dev"], {
|
|
32652
33175
|
cwd: args.repoRoot,
|
|
32653
33176
|
detached: true,
|
|
@@ -32656,7 +33179,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
32656
33179
|
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
32657
33180
|
shell: process.platform === "win32"
|
|
32658
33181
|
});
|
|
32659
|
-
|
|
33182
|
+
fs40.closeSync(fd);
|
|
32660
33183
|
if (child.pid == null) {
|
|
32661
33184
|
throw new LocalCliError({
|
|
32662
33185
|
code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
|
|
@@ -32665,7 +33188,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
32665
33188
|
});
|
|
32666
33189
|
}
|
|
32667
33190
|
child.unref();
|
|
32668
|
-
|
|
33191
|
+
fs40.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
32669
33192
|
const deadline = Date.now() + 45e3;
|
|
32670
33193
|
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
32671
33194
|
while (Date.now() < deadline) {
|
|
@@ -32697,8 +33220,8 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
32697
33220
|
});
|
|
32698
33221
|
}
|
|
32699
33222
|
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
32700
|
-
|
|
32701
|
-
const fd =
|
|
33223
|
+
fs40.mkdirSync(path45.dirname(logPath), { recursive: true });
|
|
33224
|
+
const fd = fs40.openSync(logPath, "a");
|
|
32702
33225
|
const child = spawn8("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
|
|
32703
33226
|
cwd: args.repoRoot,
|
|
32704
33227
|
detached: true,
|
|
@@ -32716,7 +33239,7 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
32716
33239
|
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
32717
33240
|
shell: process.platform === "win32"
|
|
32718
33241
|
});
|
|
32719
|
-
|
|
33242
|
+
fs40.closeSync(fd);
|
|
32720
33243
|
if (child.pid == null) {
|
|
32721
33244
|
throw new LocalCliError({
|
|
32722
33245
|
code: "BOOTSTRAP_UI_RELAY_SPAWN_FAILED",
|
|
@@ -32725,7 +33248,7 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
32725
33248
|
});
|
|
32726
33249
|
}
|
|
32727
33250
|
child.unref();
|
|
32728
|
-
|
|
33251
|
+
fs40.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
32729
33252
|
const deadline = Date.now() + 45e3;
|
|
32730
33253
|
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
32731
33254
|
while (Date.now() < deadline) {
|
|
@@ -32747,7 +33270,7 @@ function autoOpenOnboardingUi(url, open3 = tryOpenUrl) {
|
|
|
32747
33270
|
function stopPidFile(pidPath) {
|
|
32748
33271
|
let pidStr;
|
|
32749
33272
|
try {
|
|
32750
|
-
pidStr =
|
|
33273
|
+
pidStr = fs40.readFileSync(pidPath, "utf8").trim();
|
|
32751
33274
|
} catch {
|
|
32752
33275
|
return false;
|
|
32753
33276
|
}
|
|
@@ -32764,12 +33287,12 @@ function stopPidFile(pidPath) {
|
|
|
32764
33287
|
}
|
|
32765
33288
|
}
|
|
32766
33289
|
try {
|
|
32767
|
-
|
|
33290
|
+
fs40.unlinkSync(pidPath);
|
|
32768
33291
|
} catch {
|
|
32769
33292
|
}
|
|
32770
33293
|
return true;
|
|
32771
33294
|
}
|
|
32772
|
-
function stopOnboardingUi(home =
|
|
33295
|
+
function stopOnboardingUi(home = os20.homedir()) {
|
|
32773
33296
|
const uiStopped = stopPidFile(onboardingUiPaths(home).pidPath);
|
|
32774
33297
|
const relayStopped = stopPidFile(onboardingRelayPaths(home).pidPath);
|
|
32775
33298
|
return uiStopped || relayStopped;
|
|
@@ -33058,9 +33581,9 @@ async function resolveSubscriptionInventory(args) {
|
|
|
33058
33581
|
const scan2 = scanFn(args.subscriptionId);
|
|
33059
33582
|
void scan2.catch(() => {
|
|
33060
33583
|
});
|
|
33061
|
-
const timeout = new Promise((
|
|
33584
|
+
const timeout = new Promise((resolve6) => {
|
|
33062
33585
|
timer = setTimeout(() => {
|
|
33063
|
-
|
|
33586
|
+
resolve6("timeout");
|
|
33064
33587
|
}, deadlineMs);
|
|
33065
33588
|
});
|
|
33066
33589
|
const raced = await Promise.race([scan2, timeout]);
|
|
@@ -33091,7 +33614,7 @@ function renderDeployFailure(error) {
|
|
|
33091
33614
|
}
|
|
33092
33615
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
33093
33616
|
static paths = [["bootstrap", "ui"]];
|
|
33094
|
-
static usage =
|
|
33617
|
+
static usage = Command60.Usage({
|
|
33095
33618
|
description: "Deploy Ezra + start the local onboarding chat UI in the background (returns immediately).",
|
|
33096
33619
|
details: [
|
|
33097
33620
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -33112,16 +33635,16 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
33112
33635
|
["Experimental: start the voice relay (no effect on the text-only intake)", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
|
|
33113
33636
|
]
|
|
33114
33637
|
});
|
|
33115
|
-
repoRoot =
|
|
33116
|
-
port =
|
|
33117
|
-
endpoint =
|
|
33638
|
+
repoRoot = Option57.String("--repo-root");
|
|
33639
|
+
port = Option57.String("--port", "3000");
|
|
33640
|
+
endpoint = Option57.String("--endpoint", {
|
|
33118
33641
|
description: "Foundry project endpoint to target \u2014 overrides discovery when this install's resource group holds more than one Foundry project."
|
|
33119
33642
|
});
|
|
33120
|
-
prepOnly =
|
|
33121
|
-
skipInstall =
|
|
33122
|
-
stop =
|
|
33123
|
-
foreground =
|
|
33124
|
-
voice =
|
|
33643
|
+
prepOnly = Option57.Boolean("--prep-only", false);
|
|
33644
|
+
skipInstall = Option57.Boolean("--skip-install", false);
|
|
33645
|
+
stop = Option57.Boolean("--stop", false);
|
|
33646
|
+
foreground = Option57.Boolean("--foreground", false);
|
|
33647
|
+
voice = Option57.Boolean("--voice", false, {
|
|
33125
33648
|
description: "Experimental: when serving, starts the voice relay and writes the intake voice env var. The onboarding intake is text-only and is unaffected by this flag \u2014 no voice worker is registered for it."
|
|
33126
33649
|
});
|
|
33127
33650
|
async executeCommand() {
|
|
@@ -33156,7 +33679,7 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
33156
33679
|
this.context.stderr.write(` ${colors.dim(m)}
|
|
33157
33680
|
`);
|
|
33158
33681
|
};
|
|
33159
|
-
if (
|
|
33682
|
+
if (fs41.existsSync(path46.join(os21.homedir(), ".m8t", "config.yaml"))) {
|
|
33160
33683
|
out(colors.error("\u26A0 an existing ~/.m8t/config.yaml will override the onboarding app registration in apps/web \u2014 if Microsoft sign-in fails, move it aside and retry."));
|
|
33161
33684
|
}
|
|
33162
33685
|
const credential2 = new DefaultAzureCredential24();
|
|
@@ -33288,10 +33811,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
33288
33811
|
};
|
|
33289
33812
|
|
|
33290
33813
|
// src/commands/bootstrap/seed-profile.ts
|
|
33291
|
-
import { Command as
|
|
33814
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
33292
33815
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
33293
33816
|
static paths = [["bootstrap", "seed-profile"]];
|
|
33294
|
-
static usage =
|
|
33817
|
+
static usage = Command61.Usage({
|
|
33295
33818
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding intake.",
|
|
33296
33819
|
details: "Reads the latest onboarding conversation, renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines), and commits them to both <org>/stacey-brain and <org>/ezra-brain via the GitHub App. Idempotent. --watch polls until the founder finishes the intake.",
|
|
33297
33820
|
examples: [
|
|
@@ -33299,11 +33822,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
33299
33822
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
33300
33823
|
]
|
|
33301
33824
|
});
|
|
33302
|
-
endpoint =
|
|
33303
|
-
brain =
|
|
33304
|
-
watch =
|
|
33305
|
-
timeout =
|
|
33306
|
-
githubAppCreds =
|
|
33825
|
+
endpoint = Option58.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
33826
|
+
brain = Option58.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
33827
|
+
watch = Option58.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
33828
|
+
timeout = Option58.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
33829
|
+
githubAppCreds = Option58.String("--github-app-creds");
|
|
33307
33830
|
async executeCommand() {
|
|
33308
33831
|
const ctx = await resolveSeedContext({
|
|
33309
33832
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -33363,10 +33886,10 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
33363
33886
|
};
|
|
33364
33887
|
|
|
33365
33888
|
// src/commands/telemetry/enroll.ts
|
|
33366
|
-
import * as
|
|
33367
|
-
import * as
|
|
33368
|
-
import * as
|
|
33369
|
-
import { Command as
|
|
33889
|
+
import * as fs42 from "fs";
|
|
33890
|
+
import * as os22 from "os";
|
|
33891
|
+
import * as path47 from "path";
|
|
33892
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
33370
33893
|
init_errors();
|
|
33371
33894
|
|
|
33372
33895
|
// src/lib/telemetry-enroll.ts
|
|
@@ -33448,7 +33971,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
33448
33971
|
}
|
|
33449
33972
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
33450
33973
|
static paths = [["telemetry", "enroll"]];
|
|
33451
|
-
static usage =
|
|
33974
|
+
static usage = Command62.Usage({
|
|
33452
33975
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
33453
33976
|
details: "Generates an instance id + ingest key from the m8t telemetry ingest and stores the key in your platform Key Vault, the same place the installer writes it on a fresh install. Your installation is identified only by that random instance id \u2014 no contact, company, or subscription details are sent unless you pass them explicitly. Idempotent: refuses if a key already exists.",
|
|
33454
33977
|
examples: [
|
|
@@ -33456,11 +33979,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
33456
33979
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
33457
33980
|
]
|
|
33458
33981
|
});
|
|
33459
|
-
company =
|
|
33460
|
-
contactEmail =
|
|
33461
|
-
subscription =
|
|
33462
|
-
resourceGroup =
|
|
33463
|
-
force =
|
|
33982
|
+
company = Option59.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
33983
|
+
contactEmail = Option59.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
33984
|
+
subscription = Option59.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
33985
|
+
resourceGroup = Option59.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
33986
|
+
force = Option59.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
33464
33987
|
async executeCommand() {
|
|
33465
33988
|
const account = await getAzAccount();
|
|
33466
33989
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -33493,13 +34016,13 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
33493
34016
|
...company ? { company } : {},
|
|
33494
34017
|
...contactEmail ? { contactEmail } : {}
|
|
33495
34018
|
});
|
|
33496
|
-
const keyDir =
|
|
33497
|
-
const keyFile =
|
|
34019
|
+
const keyDir = fs42.mkdtempSync(path47.join(os22.tmpdir(), "m8t-ingest-key-"));
|
|
34020
|
+
const keyFile = path47.join(keyDir, "key");
|
|
33498
34021
|
try {
|
|
33499
|
-
|
|
34022
|
+
fs42.writeFileSync(keyFile, ingestKey, { mode: 384 });
|
|
33500
34023
|
await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--file", keyFile, "--only-show-errors"]);
|
|
33501
34024
|
} finally {
|
|
33502
|
-
|
|
34025
|
+
fs42.rmSync(keyDir, { recursive: true, force: true });
|
|
33503
34026
|
}
|
|
33504
34027
|
this.context.stdout.write(
|
|
33505
34028
|
`${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
|
|
@@ -33510,7 +34033,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
33510
34033
|
};
|
|
33511
34034
|
|
|
33512
34035
|
// src/commands/companion/bridge.ts
|
|
33513
|
-
import { Command as
|
|
34036
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
33514
34037
|
|
|
33515
34038
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
33516
34039
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -34628,14 +35151,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps3) {
|
|
|
34628
35151
|
return 3;
|
|
34629
35152
|
}
|
|
34630
35153
|
}
|
|
34631
|
-
var CompanionBridgeCommand = class extends
|
|
35154
|
+
var CompanionBridgeCommand = class extends Command63 {
|
|
34632
35155
|
static paths = [["companion", "_bridge"]];
|
|
34633
35156
|
/**
|
|
34634
35157
|
* One process serving many requests instead of one per request, so the
|
|
34635
35158
|
* session keeps its authenticated context between them. A CLI predating the
|
|
34636
35159
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
34637
35160
|
*/
|
|
34638
|
-
serve =
|
|
35161
|
+
serve = Option60.Boolean("--serve", false);
|
|
34639
35162
|
async execute() {
|
|
34640
35163
|
if (this.serve) {
|
|
34641
35164
|
return runCompanionBridgeServe(
|
|
@@ -34653,7 +35176,7 @@ var CompanionBridgeCommand = class extends Command61 {
|
|
|
34653
35176
|
};
|
|
34654
35177
|
|
|
34655
35178
|
// src/commands/companion/status.ts
|
|
34656
|
-
import { Command as
|
|
35179
|
+
import { Command as Command64 } from "clipanion";
|
|
34657
35180
|
async function withTimeout(work, ms) {
|
|
34658
35181
|
let timer;
|
|
34659
35182
|
try {
|
|
@@ -34725,7 +35248,7 @@ Run: m8t companion install
|
|
|
34725
35248
|
}
|
|
34726
35249
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
34727
35250
|
static paths = [["companion", "status"]];
|
|
34728
|
-
static usage =
|
|
35251
|
+
static usage = Command64.Usage({
|
|
34729
35252
|
description: "Verify the installed desktop companion without launching it."
|
|
34730
35253
|
});
|
|
34731
35254
|
async executeCommand() {
|
|
@@ -34739,7 +35262,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
34739
35262
|
};
|
|
34740
35263
|
|
|
34741
35264
|
// src/commands/companion/repair.ts
|
|
34742
|
-
import { Command as
|
|
35265
|
+
import { Command as Command65, Option as Option61 } from "clipanion";
|
|
34743
35266
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
34744
35267
|
const state = await repair();
|
|
34745
35268
|
if (state.state === "not-released") {
|
|
@@ -34758,10 +35281,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
34758
35281
|
}
|
|
34759
35282
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
34760
35283
|
static paths = [["companion", "repair"]];
|
|
34761
|
-
static usage =
|
|
35284
|
+
static usage = Command65.Usage({
|
|
34762
35285
|
description: "Restore the desktop companions and start-at-login state."
|
|
34763
35286
|
});
|
|
34764
|
-
resourceGroup =
|
|
35287
|
+
resourceGroup = Option61.String("--resource-group", {
|
|
34765
35288
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
34766
35289
|
});
|
|
34767
35290
|
async executeCommand() {
|
|
@@ -34775,7 +35298,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
34775
35298
|
};
|
|
34776
35299
|
|
|
34777
35300
|
// src/commands/companion/uninstall.ts
|
|
34778
|
-
import { Command as
|
|
35301
|
+
import { Command as Command66 } from "clipanion";
|
|
34779
35302
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
34780
35303
|
const state = await uninstall();
|
|
34781
35304
|
if (state.state !== "not-installed") {
|
|
@@ -34787,7 +35310,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
34787
35310
|
}
|
|
34788
35311
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
34789
35312
|
static paths = [["companion", "uninstall"]];
|
|
34790
|
-
static usage =
|
|
35313
|
+
static usage = Command66.Usage({
|
|
34791
35314
|
description: "Remove only this user's desktop companion installation."
|
|
34792
35315
|
});
|
|
34793
35316
|
async executeCommand() {
|
|
@@ -34836,6 +35359,8 @@ cli.register(BrainOrgsCommand);
|
|
|
34836
35359
|
cli.register(BrainShowCommand);
|
|
34837
35360
|
cli.register(BrainUnlinkCommand);
|
|
34838
35361
|
cli.register(ArchitectCheckCommand);
|
|
35362
|
+
cli.register(AgentContentSyncCommand);
|
|
35363
|
+
cli.register(AgentContentPinCommand);
|
|
34839
35364
|
cli.register(CoderDeployCommand);
|
|
34840
35365
|
cli.register(CoderTeardownCommand);
|
|
34841
35366
|
cli.register(AzureExecDeployCommand);
|