@m8t-stack/cli 0.2.82 → 0.2.84
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 +975 -674
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1467,7 +1467,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1467
1467
|
import { Builtins, Cli } from "clipanion";
|
|
1468
1468
|
|
|
1469
1469
|
// src/lib/package-version.ts
|
|
1470
|
-
var CLI_VERSION = "0.2.
|
|
1470
|
+
var CLI_VERSION = "0.2.84";
|
|
1471
1471
|
|
|
1472
1472
|
// src/lib/render-error.ts
|
|
1473
1473
|
init_errors();
|
|
@@ -3801,12 +3801,12 @@ function encodeURIPath(str4) {
|
|
|
3801
3801
|
return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
3802
3802
|
}
|
|
3803
3803
|
var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
3804
|
-
var createPathTagFunction = (pathEncoder = encodeURIPath) => function
|
|
3804
|
+
var createPathTagFunction = (pathEncoder = encodeURIPath) => function path47(statics, ...params) {
|
|
3805
3805
|
if (statics.length === 1)
|
|
3806
3806
|
return statics[0];
|
|
3807
3807
|
let postPath = false;
|
|
3808
3808
|
const invalidSegments = [];
|
|
3809
|
-
const
|
|
3809
|
+
const path48 = statics.reduce((previousValue, currentValue, index) => {
|
|
3810
3810
|
if (/[?#]/.test(currentValue)) {
|
|
3811
3811
|
postPath = true;
|
|
3812
3812
|
}
|
|
@@ -3823,7 +3823,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path48(sta
|
|
|
3823
3823
|
}
|
|
3824
3824
|
return previousValue + currentValue + (index === params.length ? "" : encoded);
|
|
3825
3825
|
}, "");
|
|
3826
|
-
const pathOnly =
|
|
3826
|
+
const pathOnly = path48.split(/[?#]/, 1)[0];
|
|
3827
3827
|
const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
|
|
3828
3828
|
let match;
|
|
3829
3829
|
while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
|
|
@@ -3844,10 +3844,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path48(sta
|
|
|
3844
3844
|
}, "");
|
|
3845
3845
|
throw new OpenAIError(`Path parameters result in path with invalid segments:
|
|
3846
3846
|
${invalidSegments.map((e) => e.error).join("\n")}
|
|
3847
|
-
${
|
|
3847
|
+
${path48}
|
|
3848
3848
|
${underline}`);
|
|
3849
3849
|
}
|
|
3850
|
-
return
|
|
3850
|
+
return path48;
|
|
3851
3851
|
};
|
|
3852
3852
|
var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
|
|
3853
3853
|
|
|
@@ -12298,9 +12298,9 @@ var OpenAI = class {
|
|
|
12298
12298
|
this.apiKey = token;
|
|
12299
12299
|
return true;
|
|
12300
12300
|
}
|
|
12301
|
-
buildURL(
|
|
12301
|
+
buildURL(path47, query, defaultBaseURL) {
|
|
12302
12302
|
const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
|
|
12303
|
-
const url = isAbsoluteURL(
|
|
12303
|
+
const url = isAbsoluteURL(path47) ? new URL(path47) : new URL(baseURL + (baseURL.endsWith("/") && path47.startsWith("/") ? path47.slice(1) : path47));
|
|
12304
12304
|
const defaultQuery = this.defaultQuery();
|
|
12305
12305
|
const pathQuery = Object.fromEntries(url.searchParams);
|
|
12306
12306
|
if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
|
|
@@ -12330,24 +12330,24 @@ var OpenAI = class {
|
|
|
12330
12330
|
*/
|
|
12331
12331
|
async prepareRequest(request, { url, options }) {
|
|
12332
12332
|
}
|
|
12333
|
-
get(
|
|
12334
|
-
return this.methodRequest("get",
|
|
12333
|
+
get(path47, opts) {
|
|
12334
|
+
return this.methodRequest("get", path47, opts);
|
|
12335
12335
|
}
|
|
12336
|
-
post(
|
|
12337
|
-
return this.methodRequest("post",
|
|
12336
|
+
post(path47, opts) {
|
|
12337
|
+
return this.methodRequest("post", path47, opts);
|
|
12338
12338
|
}
|
|
12339
|
-
patch(
|
|
12340
|
-
return this.methodRequest("patch",
|
|
12339
|
+
patch(path47, opts) {
|
|
12340
|
+
return this.methodRequest("patch", path47, opts);
|
|
12341
12341
|
}
|
|
12342
|
-
put(
|
|
12343
|
-
return this.methodRequest("put",
|
|
12342
|
+
put(path47, opts) {
|
|
12343
|
+
return this.methodRequest("put", path47, opts);
|
|
12344
12344
|
}
|
|
12345
|
-
delete(
|
|
12346
|
-
return this.methodRequest("delete",
|
|
12345
|
+
delete(path47, opts) {
|
|
12346
|
+
return this.methodRequest("delete", path47, opts);
|
|
12347
12347
|
}
|
|
12348
|
-
methodRequest(method,
|
|
12348
|
+
methodRequest(method, path47, opts) {
|
|
12349
12349
|
return this.request(Promise.resolve(opts).then((opts2) => {
|
|
12350
|
-
return { method, path:
|
|
12350
|
+
return { method, path: path47, ...opts2 };
|
|
12351
12351
|
}));
|
|
12352
12352
|
}
|
|
12353
12353
|
request(options, remainingRetries = null) {
|
|
@@ -12472,8 +12472,8 @@ var OpenAI = class {
|
|
|
12472
12472
|
}));
|
|
12473
12473
|
return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
|
|
12474
12474
|
}
|
|
12475
|
-
getAPIList(
|
|
12476
|
-
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path:
|
|
12475
|
+
getAPIList(path47, Page2, opts) {
|
|
12476
|
+
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path47, ...opts2 })) : { method: "get", path: path47, ...opts });
|
|
12477
12477
|
}
|
|
12478
12478
|
requestAPIList(Page2, options) {
|
|
12479
12479
|
const request = this.makeRequest(options, null, void 0);
|
|
@@ -12567,8 +12567,8 @@ var OpenAI = class {
|
|
|
12567
12567
|
}
|
|
12568
12568
|
async buildRequest(inputOptions, { retryCount = 0 } = {}) {
|
|
12569
12569
|
const options = { ...inputOptions };
|
|
12570
|
-
const { method, path:
|
|
12571
|
-
const url = this.buildURL(
|
|
12570
|
+
const { method, path: path47, query, defaultBaseURL } = options;
|
|
12571
|
+
const url = this.buildURL(path47, query, defaultBaseURL);
|
|
12572
12572
|
if ("timeout" in options)
|
|
12573
12573
|
validatePositiveInteger("timeout", options.timeout);
|
|
12574
12574
|
options.timeout = options.timeout ?? this.timeout;
|
|
@@ -13633,10 +13633,10 @@ var BindListCommand = class extends M8tCommand {
|
|
|
13633
13633
|
const qs = new URLSearchParams();
|
|
13634
13634
|
if (this.channel) qs.set("channel", this.channel);
|
|
13635
13635
|
if (this.status) qs.set("status", this.status);
|
|
13636
|
-
const
|
|
13636
|
+
const path47 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
|
|
13637
13637
|
const data = await apiCall(
|
|
13638
13638
|
{ gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
|
|
13639
|
-
{ method: "GET", path:
|
|
13639
|
+
{ method: "GET", path: path47 },
|
|
13640
13640
|
(msg) => this.context.stderr.write(msg + "\n")
|
|
13641
13641
|
);
|
|
13642
13642
|
if (mode === "json") {
|
|
@@ -14882,8 +14882,8 @@ ${r.stderr}`;
|
|
|
14882
14882
|
message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
|
|
14883
14883
|
});
|
|
14884
14884
|
},
|
|
14885
|
-
getFile: async (
|
|
14886
|
-
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${
|
|
14885
|
+
getFile: async (path47) => {
|
|
14886
|
+
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path47}`]);
|
|
14887
14887
|
return r.exitCode === 0 ? r.stdout : null;
|
|
14888
14888
|
},
|
|
14889
14889
|
isEmpty: async () => {
|
|
@@ -16434,7 +16434,7 @@ function appRepoProbe(args) {
|
|
|
16434
16434
|
const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
16435
16435
|
return res.status;
|
|
16436
16436
|
},
|
|
16437
|
-
getFile: (
|
|
16437
|
+
getFile: (path47) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path47, fetchImpl: args.fetchImpl }),
|
|
16438
16438
|
isEmpty: async () => {
|
|
16439
16439
|
const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
16440
16440
|
return res.status === 404;
|
|
@@ -20001,6 +20001,7 @@ import { DefaultAzureCredential as DefaultAzureCredential16 } from "@azure/ident
|
|
|
20001
20001
|
|
|
20002
20002
|
// src/lib/platform-update.ts
|
|
20003
20003
|
init_errors();
|
|
20004
|
+
import semver from "semver";
|
|
20004
20005
|
var DEFAULT_IMAGE_REPO = "ghcr.io/m8t-labs/m8t";
|
|
20005
20006
|
function parseImageRef2(ref) {
|
|
20006
20007
|
let rest = ref;
|
|
@@ -20055,10 +20056,22 @@ function compareSemver(a, b) {
|
|
|
20055
20056
|
}
|
|
20056
20057
|
return 0;
|
|
20057
20058
|
}
|
|
20059
|
+
function comparePlatformVersion(a, b) {
|
|
20060
|
+
const bare = (v) => v.trim().replace(/^platform-/, "").replace(/^v/, "");
|
|
20061
|
+
const va = semver.valid(bare(a));
|
|
20062
|
+
const vb = semver.valid(bare(b));
|
|
20063
|
+
if (!va || !vb) {
|
|
20064
|
+
throw new LocalCliError({
|
|
20065
|
+
code: "PLATFORM_BAD_SEMVER",
|
|
20066
|
+
message: `comparePlatformVersion needs platform versions, got '${a}' / '${b}'.`
|
|
20067
|
+
});
|
|
20068
|
+
}
|
|
20069
|
+
return semver.compare(va, vb);
|
|
20070
|
+
}
|
|
20058
20071
|
function pickNewestSemver(tags) {
|
|
20059
|
-
const
|
|
20060
|
-
if (
|
|
20061
|
-
return
|
|
20072
|
+
const semver2 = tags.filter(isSemverTag);
|
|
20073
|
+
if (semver2.length === 0) return null;
|
|
20074
|
+
return semver2.reduce((best, t) => compareSemver(t, best) > 0 ? t : best);
|
|
20062
20075
|
}
|
|
20063
20076
|
function parseContainerAppResourceId(id) {
|
|
20064
20077
|
const m = /\/subscriptions\/([^/]+)\/resourceGroups\/([^/]+)\/providers\/Microsoft\.App\/containerApps\/([^/]+)/i.exec(
|
|
@@ -20104,8 +20117,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
20104
20117
|
hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
|
|
20105
20118
|
});
|
|
20106
20119
|
}
|
|
20107
|
-
const
|
|
20108
|
-
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${
|
|
20120
|
+
const path47 = repo.replace(/^ghcr\.io\//, "");
|
|
20121
|
+
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path47}:pull`);
|
|
20109
20122
|
if (!tokenRes.ok) {
|
|
20110
20123
|
throw new LocalCliError({
|
|
20111
20124
|
code: "PLATFORM_GHCR_TOKEN_FAILED",
|
|
@@ -20121,7 +20134,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
20121
20134
|
hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
|
|
20122
20135
|
});
|
|
20123
20136
|
}
|
|
20124
|
-
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${
|
|
20137
|
+
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path47}/tags/list`, {
|
|
20125
20138
|
headers: { Authorization: `Bearer ${token}` }
|
|
20126
20139
|
});
|
|
20127
20140
|
if (!tagsRes.ok) {
|
|
@@ -20298,6 +20311,8 @@ function entityToStamp(e) {
|
|
|
20298
20311
|
var RELEASES_REPO = "m8t-labs/m8t-releases";
|
|
20299
20312
|
var RELEASES_BASE = `https://github.com/${RELEASES_REPO}/releases`;
|
|
20300
20313
|
var CHANNEL_LATEST_URL = `${RELEASES_BASE}/latest/download/manifest.json`;
|
|
20314
|
+
var CANARY_POINTER_TAG = "canary";
|
|
20315
|
+
var CHANNEL_CANARY_URL = `${RELEASES_BASE}/download/${CANARY_POINTER_TAG}/manifest.json`;
|
|
20301
20316
|
function platformTag(version) {
|
|
20302
20317
|
const bare = version.trim().replace(/^platform-/, "").replace(/^v/, "");
|
|
20303
20318
|
return `platform-v${bare}`;
|
|
@@ -20309,13 +20324,46 @@ function releaseAssetUrl(tag, asset) {
|
|
|
20309
20324
|
return `${RELEASES_BASE}/download/${tag}/${asset}`;
|
|
20310
20325
|
}
|
|
20311
20326
|
|
|
20327
|
+
// ../../packages/platform-release/dist/esm/canary-version.js
|
|
20328
|
+
var CANARY_IDENTIFIER = "canary";
|
|
20329
|
+
var STABLE_CORE = String.raw`\d+\.\d+\.\d+`;
|
|
20330
|
+
var CANARY_SUFFIX = String.raw`-${CANARY_IDENTIFIER}\.(?:0|[1-9]\d*)`;
|
|
20331
|
+
var CANARY_VERSION_PATTERN = `^${STABLE_CORE}${CANARY_SUFFIX}$`;
|
|
20332
|
+
var PLATFORM_VERSION_PATTERN = `^${STABLE_CORE}(?:${CANARY_SUFFIX})?$`;
|
|
20333
|
+
var PLATFORM_TAG_PATTERN = `^platform-v${STABLE_CORE}(?:${CANARY_SUFFIX})?$`;
|
|
20334
|
+
var CANARY_VERSION_RE = new RegExp(CANARY_VERSION_PATTERN);
|
|
20335
|
+
var STABLE_CORE_RE = new RegExp(`^${STABLE_CORE}$`);
|
|
20336
|
+
|
|
20312
20337
|
// ../../packages/platform-release/dist/esm/apply-request.js
|
|
20313
20338
|
var APPLY_REQUEST_PK = "system";
|
|
20314
20339
|
var APPLY_REQUEST_RK = "apply-request";
|
|
20315
20340
|
var IN_FLIGHT = ["claimed", "applying"];
|
|
20341
|
+
var LEASE_MS = 30 * 60 * 1e3;
|
|
20342
|
+
var MAX_LEASE_TAKEOVERS = 2;
|
|
20343
|
+
var MAX_HEALTHY_IN_FLIGHT_MS = LEASE_MS * (MAX_LEASE_TAKEOVERS + 1);
|
|
20316
20344
|
function isInFlight(status) {
|
|
20317
20345
|
return IN_FLIGHT.includes(status);
|
|
20318
20346
|
}
|
|
20347
|
+
var BLOCKS_NEW_INTENT = ["pending", "claimed", "applying", "awaiting-engine-update"];
|
|
20348
|
+
function blocksNewIntent(status) {
|
|
20349
|
+
return BLOCKS_NEW_INTENT.includes(status);
|
|
20350
|
+
}
|
|
20351
|
+
function newIntent(target, provenance, nowIso) {
|
|
20352
|
+
return {
|
|
20353
|
+
schemaVersion: 1,
|
|
20354
|
+
target,
|
|
20355
|
+
provenance,
|
|
20356
|
+
status: "pending",
|
|
20357
|
+
phase: null,
|
|
20358
|
+
claimedBy: null,
|
|
20359
|
+
leaseUntil: null,
|
|
20360
|
+
attempt: 0,
|
|
20361
|
+
breaker: "none",
|
|
20362
|
+
result: null,
|
|
20363
|
+
createdAt: nowIso,
|
|
20364
|
+
updatedAt: nowIso
|
|
20365
|
+
};
|
|
20366
|
+
}
|
|
20319
20367
|
function applyRequestToEntity(r) {
|
|
20320
20368
|
return { partitionKey: APPLY_REQUEST_PK, rowKey: APPLY_REQUEST_RK, value: JSON.stringify(r), updatedAt: r.updatedAt };
|
|
20321
20369
|
}
|
|
@@ -20354,11 +20402,10 @@ function manifestSourceForVersionTag(channelUrl, version) {
|
|
|
20354
20402
|
const tag = platformTag(version);
|
|
20355
20403
|
const custom = channelUrl.trim();
|
|
20356
20404
|
if (!custom || custom === CHANNEL_LATEST_URL) return { channel: true, version: tag };
|
|
20357
|
-
|
|
20358
|
-
|
|
20359
|
-
}
|
|
20360
|
-
return { url: custom };
|
|
20405
|
+
const swapped = custom.replace(POINTER_SEGMENT, `/releases/download/${tag}/`);
|
|
20406
|
+
return { url: swapped };
|
|
20361
20407
|
}
|
|
20408
|
+
var POINTER_SEGMENT = /\/releases\/(?:latest\/download|download\/[^/]+)\//;
|
|
20362
20409
|
async function fetchManifest(source, deps = {}) {
|
|
20363
20410
|
const readFile9 = deps.readFile ?? ((p) => fs23.readFileSync(p, "utf8"));
|
|
20364
20411
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -21630,25 +21677,25 @@ function mergeThreeWay(ours, base, theirs) {
|
|
|
21630
21677
|
fs27.rmSync(dir, { recursive: true, force: true });
|
|
21631
21678
|
}
|
|
21632
21679
|
}
|
|
21633
|
-
var skip = (
|
|
21680
|
+
var skip = (path47, skipReason) => ({ path: path47, kind: "skip", skipReason });
|
|
21634
21681
|
function classifyCandidate(c) {
|
|
21635
|
-
const { path:
|
|
21636
|
-
if (theirs === base) return { path:
|
|
21682
|
+
const { path: path47, base, ours, theirs } = c;
|
|
21683
|
+
if (theirs === base) return { path: path47, kind: "noop" };
|
|
21637
21684
|
if (base === null) {
|
|
21638
|
-
if (theirs === null) return { path:
|
|
21639
|
-
if (ours === null) return { path:
|
|
21640
|
-
if (ours === theirs) return { path:
|
|
21641
|
-
return skip(
|
|
21685
|
+
if (theirs === null) return { path: path47, kind: "noop" };
|
|
21686
|
+
if (ours === null) return { path: path47, kind: "add", content: theirs };
|
|
21687
|
+
if (ours === theirs) return { path: path47, kind: "noop" };
|
|
21688
|
+
return skip(path47, "you already have a file at this path");
|
|
21642
21689
|
}
|
|
21643
21690
|
if (ours === null) {
|
|
21644
|
-
return theirs === null ? { path:
|
|
21691
|
+
return theirs === null ? { path: path47, kind: "noop" } : skip(path47, "you removed this file; upstream changed it");
|
|
21645
21692
|
}
|
|
21646
21693
|
if (theirs === null) {
|
|
21647
|
-
return ours === base ? { path:
|
|
21694
|
+
return ours === base ? { path: path47, kind: "delete" } : skip(path47, "you edited this file; upstream removed it");
|
|
21648
21695
|
}
|
|
21649
|
-
if (ours === base) return { path:
|
|
21696
|
+
if (ours === base) return { path: path47, kind: "update", content: theirs };
|
|
21650
21697
|
const m = mergeThreeWay(ours, base, theirs);
|
|
21651
|
-
return m.ok ? { path:
|
|
21698
|
+
return m.ok ? { path: path47, kind: "update", content: m.merged } : skip(path47, "your edits conflict with the update");
|
|
21652
21699
|
}
|
|
21653
21700
|
|
|
21654
21701
|
// src/lib/seed-pr-body.ts
|
|
@@ -21835,7 +21882,7 @@ function bindRepoApi(token, repo) {
|
|
|
21835
21882
|
openPr: (head, base, title, body) => openPullRequestViaApp({ token, repo, base, head, title, body }),
|
|
21836
21883
|
updatePr: (n, title, body) => updatePullRequestViaApp({ token, repo, number: n, title, body }),
|
|
21837
21884
|
closePr: (n, comment) => closePullRequestViaApp({ token, repo, number: n, comment }),
|
|
21838
|
-
readFileAtRef: (
|
|
21885
|
+
readFileAtRef: (path47, ref) => readRepoFileViaApp({ token, repo, path: path47, ref })
|
|
21839
21886
|
};
|
|
21840
21887
|
}
|
|
21841
21888
|
async function runSeedRefresh(args) {
|
|
@@ -22337,7 +22384,6 @@ function resolveHeadlessContextFromEnv(env) {
|
|
|
22337
22384
|
|
|
22338
22385
|
// src/lib/apply-request-store.ts
|
|
22339
22386
|
import { TableClient as TableClient4 } from "@azure/data-tables";
|
|
22340
|
-
var MAX_LEASE_TAKEOVERS = 2;
|
|
22341
22387
|
async function openApplyTable(opts) {
|
|
22342
22388
|
const { tableEndpoint } = await discoverStampStorage(opts);
|
|
22343
22389
|
return new TableClient4(tableEndpoint, "Metadata", opts.credential);
|
|
@@ -22423,32 +22469,59 @@ function assertForwardTarget(manifest) {
|
|
|
22423
22469
|
}
|
|
22424
22470
|
|
|
22425
22471
|
// src/lib/platform-self-update.ts
|
|
22426
|
-
import * as fs30 from "fs";
|
|
22427
|
-
import * as path32 from "path";
|
|
22428
22472
|
function toVTag2(v) {
|
|
22429
22473
|
return v.startsWith("v") ? v : `v${v}`;
|
|
22430
22474
|
}
|
|
22431
|
-
function
|
|
22432
|
-
if (!repoRoot) return null;
|
|
22433
|
-
try {
|
|
22434
|
-
const raw = fs30.readFileSync(path32.join(repoRoot, "installer", "version.json"), "utf8");
|
|
22435
|
-
const v = JSON.parse(raw).version;
|
|
22436
|
-
return typeof v === "string" && v.length > 0 ? v : null;
|
|
22437
|
-
} catch {
|
|
22438
|
-
return null;
|
|
22439
|
-
}
|
|
22440
|
-
}
|
|
22441
|
-
function needsEngineUpdate(manifest, runningVersion, bakedRelease) {
|
|
22475
|
+
function needsEngineUpdate(manifest, runningVersion, runningImage) {
|
|
22442
22476
|
if (runningVersion === "0.0.0-dev") return false;
|
|
22443
|
-
if (
|
|
22477
|
+
if (needsEngineImageHop(manifest, runningVersion, runningImage ?? null)) return true;
|
|
22444
22478
|
const running = toVTag2(runningVersion);
|
|
22445
22479
|
const min = toVTag2(manifest.components.cli.min);
|
|
22446
22480
|
return compareSemver(running, min) < 0;
|
|
22447
22481
|
}
|
|
22482
|
+
function needsPreClaimEngineUpdate(manifest, runningVersion, runningImage) {
|
|
22483
|
+
return needsEngineImageHop(manifest, runningVersion, runningImage ?? null);
|
|
22484
|
+
}
|
|
22485
|
+
function targetEngineImage(manifest) {
|
|
22486
|
+
const inst = manifest.components.installer;
|
|
22487
|
+
return `${inst.ref}:${inst.tag}`;
|
|
22488
|
+
}
|
|
22489
|
+
function ownJobName(env = process.env) {
|
|
22490
|
+
const name = (env.UPDATER_JOB_NAME ?? "").trim();
|
|
22491
|
+
return name.length > 0 ? name : null;
|
|
22492
|
+
}
|
|
22493
|
+
async function readOwnJobImage(opts) {
|
|
22494
|
+
const job = opts.jobName ?? ownJobName();
|
|
22495
|
+
if (!job) return null;
|
|
22496
|
+
const image = (await runAz([
|
|
22497
|
+
"containerapp",
|
|
22498
|
+
"job",
|
|
22499
|
+
"show",
|
|
22500
|
+
"-n",
|
|
22501
|
+
job,
|
|
22502
|
+
"-g",
|
|
22503
|
+
opts.resourceGroup,
|
|
22504
|
+
"--subscription",
|
|
22505
|
+
opts.subscriptionId,
|
|
22506
|
+
"--query",
|
|
22507
|
+
"properties.template.containers[0].image",
|
|
22508
|
+
"-o",
|
|
22509
|
+
"tsv",
|
|
22510
|
+
"--only-show-errors"
|
|
22511
|
+
])).trim();
|
|
22512
|
+
if (!image || image === "None") {
|
|
22513
|
+
throw new Error(`could not read the updater job's current image (job ${job}) \u2014 refusing to decide a self-update blind`);
|
|
22514
|
+
}
|
|
22515
|
+
return image;
|
|
22516
|
+
}
|
|
22517
|
+
function needsEngineImageHop(manifest, runningVersion, runningImage) {
|
|
22518
|
+
if (runningVersion === "0.0.0-dev") return false;
|
|
22519
|
+
if (runningImage === null || runningImage.trim().length === 0) return false;
|
|
22520
|
+
return runningImage.trim() !== targetEngineImage(manifest);
|
|
22521
|
+
}
|
|
22448
22522
|
async function updateOwnJobImage(opts) {
|
|
22449
|
-
const
|
|
22450
|
-
const
|
|
22451
|
-
const job = opts.jobName ?? process.env.UPDATER_JOB_NAME;
|
|
22523
|
+
const image = targetEngineImage(opts.manifest);
|
|
22524
|
+
const job = opts.jobName ?? ownJobName();
|
|
22452
22525
|
if (!job) throw new Error("UPDATER_JOB_NAME unset \u2014 cannot self-update the updater job image");
|
|
22453
22526
|
await runAz([
|
|
22454
22527
|
"containerapp",
|
|
@@ -22618,13 +22691,9 @@ async function readBreaker(client) {
|
|
|
22618
22691
|
|
|
22619
22692
|
// src/commands/platform/converge.ts
|
|
22620
22693
|
init_errors();
|
|
22621
|
-
function canonicalVersion(v) {
|
|
22622
|
-
const bare = v.replace(/^platform-/, "");
|
|
22623
|
-
return bare.startsWith("v") ? bare : `v${bare}`;
|
|
22624
|
-
}
|
|
22625
22694
|
function isForwardTarget(target, installed) {
|
|
22626
22695
|
if (!installed) return true;
|
|
22627
|
-
return
|
|
22696
|
+
return comparePlatformVersion(target, installed) >= 0;
|
|
22628
22697
|
}
|
|
22629
22698
|
async function failIfStillInFlight(client, now, error) {
|
|
22630
22699
|
const cur = await readApplyRequest(client);
|
|
@@ -22655,6 +22724,28 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
22655
22724
|
subscriptionId: ctx.subscriptionId,
|
|
22656
22725
|
resourceGroup: ctx.resourceGroup
|
|
22657
22726
|
});
|
|
22727
|
+
let runningImage = null;
|
|
22728
|
+
const outstanding = await readApplyRequest(client);
|
|
22729
|
+
if (outstanding && blocksNewIntent(outstanding.status)) {
|
|
22730
|
+
runningImage = await readOwnJobImage({
|
|
22731
|
+
subscriptionId: ctx.subscriptionId,
|
|
22732
|
+
resourceGroup: ctx.resourceGroup
|
|
22733
|
+
});
|
|
22734
|
+
if (runningImage) {
|
|
22735
|
+
const preManifest = await fetchManifest(targetManifestSource(ctx, outstanding.target));
|
|
22736
|
+
if (needsPreClaimEngineUpdate(preManifest, CLI_VERSION, runningImage)) {
|
|
22737
|
+
log(
|
|
22738
|
+
`self-updating BEFORE claiming (running ${runningImage} \u2192 target ${targetEngineImage(preManifest)}); the next tick claims with the new engine.`
|
|
22739
|
+
);
|
|
22740
|
+
await updateOwnJobImage({
|
|
22741
|
+
subscriptionId: ctx.subscriptionId,
|
|
22742
|
+
resourceGroup: ctx.resourceGroup,
|
|
22743
|
+
manifest: preManifest
|
|
22744
|
+
});
|
|
22745
|
+
return 0;
|
|
22746
|
+
}
|
|
22747
|
+
}
|
|
22748
|
+
}
|
|
22658
22749
|
const claimed = await claimApplyRequest(client, executionId, now(), 30 * 60 * 1e3);
|
|
22659
22750
|
if (!claimed) {
|
|
22660
22751
|
log("no pending apply-request \u2014 nothing to converge.");
|
|
@@ -22689,10 +22780,9 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
22689
22780
|
return 1;
|
|
22690
22781
|
}
|
|
22691
22782
|
const repoRoot = tryResolveRepoRoot();
|
|
22692
|
-
|
|
22693
|
-
if (needsEngineUpdate(manifest, CLI_VERSION, bakedRelease)) {
|
|
22783
|
+
if (needsEngineUpdate(manifest, CLI_VERSION, runningImage)) {
|
|
22694
22784
|
log(
|
|
22695
|
-
`self-updating the updater image (
|
|
22785
|
+
`self-updating the updater image (running ${runningImage ?? "(not the job)"} / engine ${CLI_VERSION} \u2192 target ${targetEngineImage(manifest)}, cli.min ${manifest.components.cli.min}).`
|
|
22696
22786
|
);
|
|
22697
22787
|
await updateOwnJobImage({
|
|
22698
22788
|
subscriptionId: ctx.subscriptionId,
|
|
@@ -22854,12 +22944,270 @@ function targetManifestSource(ctx, target) {
|
|
|
22854
22944
|
return manifestSourceForVersionTag(ctx.channelUrl, target);
|
|
22855
22945
|
}
|
|
22856
22946
|
|
|
22857
|
-
// src/commands/platform/
|
|
22858
|
-
import { readFileSync as readFileSync21 } from "fs";
|
|
22947
|
+
// src/commands/platform/clear-intent.ts
|
|
22859
22948
|
import { Command as Command37, Option as Option34 } from "clipanion";
|
|
22860
22949
|
import { DefaultAzureCredential as DefaultAzureCredential18, ManagedIdentityCredential as ManagedIdentityCredential2 } from "@azure/identity";
|
|
22861
22950
|
init_errors();
|
|
22862
22951
|
|
|
22952
|
+
// src/lib/apply-intent-clear.ts
|
|
22953
|
+
var CLEARED_BY_OPERATOR = "cleared by an operator via `m8t platform clear-intent`";
|
|
22954
|
+
function decideClear(row, nowIso, force) {
|
|
22955
|
+
if (!row) return { action: "none", reason: "there is no apply-request to clear." };
|
|
22956
|
+
if (!isInFlight(row.status) && row.status !== "pending" && row.status !== "awaiting-engine-update") {
|
|
22957
|
+
return { action: "none", reason: `the apply-request is already terminal (${row.status}) \u2014 a new intent can be written.` };
|
|
22958
|
+
}
|
|
22959
|
+
const leaseLive = isInFlight(row.status) && row.leaseUntil !== null && Date.parse(row.leaseUntil) > Date.parse(nowIso);
|
|
22960
|
+
if (leaseLive && !force) {
|
|
22961
|
+
return {
|
|
22962
|
+
action: "refuse",
|
|
22963
|
+
reason: `the apply-request is ${row.status} and its lease is still live until ${row.leaseUntil ?? "(unknown)"} \u2014 a converge may be running right now. Clearing it could start a second apply alongside the first. Wait for the lease to expire, or pass --force if you know the engine is gone.`
|
|
22964
|
+
};
|
|
22965
|
+
}
|
|
22966
|
+
const held = leaseLive ? " (lease still live \u2014 forced)" : "";
|
|
22967
|
+
return { action: "clear", reason: `clearing a ${row.status} apply-request for target ${row.target}${held}.` };
|
|
22968
|
+
}
|
|
22969
|
+
function clearedRow(row, nowIso) {
|
|
22970
|
+
return {
|
|
22971
|
+
...row,
|
|
22972
|
+
status: "failed",
|
|
22973
|
+
result: {
|
|
22974
|
+
appliedVersion: row.result?.appliedVersion ?? null,
|
|
22975
|
+
error: `${CLEARED_BY_OPERATOR} (was ${row.status}${row.phase ? `, phase "${row.phase}"` : ""})`
|
|
22976
|
+
},
|
|
22977
|
+
updatedAt: nowIso
|
|
22978
|
+
};
|
|
22979
|
+
}
|
|
22980
|
+
|
|
22981
|
+
// src/commands/platform/clear-intent.ts
|
|
22982
|
+
var PlatformClearIntentCommand = class extends M8tCommand {
|
|
22983
|
+
static paths = [["platform", "clear-intent"]];
|
|
22984
|
+
static usage = Command37.Usage({
|
|
22985
|
+
description: "Clear a stuck platform update request so a new one can be made.",
|
|
22986
|
+
details: "An update request that never settles blocks every later request, and the routines that would normally rescue it all run inside the updater itself \u2014 which is no help when the updater is what is stuck. This clears it from outside. Refuses while an update may still be running, unless --force.",
|
|
22987
|
+
examples: [
|
|
22988
|
+
["Clear a stuck request", "m8t platform clear-intent --resource-group rg-m8t --subscription <id>"],
|
|
22989
|
+
["Clear one whose lease is still live", "m8t platform clear-intent --resource-group rg-m8t --subscription <id> --force"]
|
|
22990
|
+
]
|
|
22991
|
+
});
|
|
22992
|
+
subscription = Option34.String("--subscription");
|
|
22993
|
+
resourceGroup = Option34.String("--resource-group");
|
|
22994
|
+
miClientId = Option34.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
|
|
22995
|
+
force = Option34.Boolean("--force", false, {
|
|
22996
|
+
description: "Clear even while the lease is live. Only when you know the engine that held it is gone."
|
|
22997
|
+
});
|
|
22998
|
+
output = Option34.String("--output");
|
|
22999
|
+
async executeCommand() {
|
|
23000
|
+
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23001
|
+
const need = (v, flag) => {
|
|
23002
|
+
if (typeof v !== "string" || v.trim().length === 0) {
|
|
23003
|
+
throw new LocalCliError({ code: "PLATFORM_CLEAR_INTENT_ARG_MISSING", message: `${flag} is required.` });
|
|
23004
|
+
}
|
|
23005
|
+
return v.trim();
|
|
23006
|
+
};
|
|
23007
|
+
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential2({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential18();
|
|
23008
|
+
const client = await openApplyTable({
|
|
23009
|
+
credential: credential2,
|
|
23010
|
+
subscriptionId: need(this.subscription, "--subscription"),
|
|
23011
|
+
resourceGroup: need(this.resourceGroup, "--resource-group")
|
|
23012
|
+
});
|
|
23013
|
+
const row = await readApplyRequest(client);
|
|
23014
|
+
const decision = decideClear(row, (/* @__PURE__ */ new Date()).toISOString(), this.force);
|
|
23015
|
+
if (decision.action === "refuse") {
|
|
23016
|
+
throw new LocalCliError({
|
|
23017
|
+
code: "PLATFORM_CLEAR_INTENT_REFUSED",
|
|
23018
|
+
message: decision.reason,
|
|
23019
|
+
hint: "Re-run with --force once you have confirmed no converge is running."
|
|
23020
|
+
});
|
|
23021
|
+
}
|
|
23022
|
+
if (decision.action === "none" || !row) {
|
|
23023
|
+
this.context.stdout.write(
|
|
23024
|
+
mode === "json" ? renderJson({ cleared: false, reason: decision.reason }) + "\n" : `${decision.reason}
|
|
23025
|
+
`
|
|
23026
|
+
);
|
|
23027
|
+
return 0;
|
|
23028
|
+
}
|
|
23029
|
+
const next = clearedRow(row, (/* @__PURE__ */ new Date()).toISOString());
|
|
23030
|
+
await client.updateEntity(applyRequestToEntity(next), "Merge");
|
|
23031
|
+
const result2 = { cleared: true, previousStatus: row.status, target: row.target, reason: decision.reason };
|
|
23032
|
+
this.context.stdout.write(
|
|
23033
|
+
mode === "json" ? renderJson(result2) + "\n" : `cleared: ${row.status} \u2192 failed (target ${row.target}). A new update can now be requested.
|
|
23034
|
+
`
|
|
23035
|
+
);
|
|
23036
|
+
return 0;
|
|
23037
|
+
}
|
|
23038
|
+
};
|
|
23039
|
+
|
|
23040
|
+
// src/commands/platform/request-update.ts
|
|
23041
|
+
import { Command as Command38, Option as Option35 } from "clipanion";
|
|
23042
|
+
import { DefaultAzureCredential as DefaultAzureCredential19, ManagedIdentityCredential as ManagedIdentityCredential3 } from "@azure/identity";
|
|
23043
|
+
init_errors();
|
|
23044
|
+
|
|
23045
|
+
// src/lib/request-update-follow.ts
|
|
23046
|
+
var TERMINAL = {
|
|
23047
|
+
success: true,
|
|
23048
|
+
failed: false,
|
|
23049
|
+
"rolled-back": false,
|
|
23050
|
+
held: false
|
|
23051
|
+
};
|
|
23052
|
+
function isTerminal(status) {
|
|
23053
|
+
return status in TERMINAL;
|
|
23054
|
+
}
|
|
23055
|
+
function classifyPolledRow(row, target) {
|
|
23056
|
+
if (!row) return null;
|
|
23057
|
+
if (row.target !== target) return { kind: "not-ours", otherTarget: row.target };
|
|
23058
|
+
if (isTerminal(row.status)) return { kind: "settled", row };
|
|
23059
|
+
return { kind: "in-progress", where: positionLabel(row.status, row.phase) };
|
|
23060
|
+
}
|
|
23061
|
+
function positionLabel(status, phase) {
|
|
23062
|
+
return phase ? `${status} (${phase})` : status;
|
|
23063
|
+
}
|
|
23064
|
+
function outcomeExitCode(settled) {
|
|
23065
|
+
if (!settled) return 1;
|
|
23066
|
+
return TERMINAL[settled.status] ? 0 : 1;
|
|
23067
|
+
}
|
|
23068
|
+
function outcomeStatus(settled) {
|
|
23069
|
+
return settled?.status ?? "timed-out";
|
|
23070
|
+
}
|
|
23071
|
+
|
|
23072
|
+
// src/commands/platform/request-update.ts
|
|
23073
|
+
var PlatformRequestUpdateCommand = class extends M8tCommand {
|
|
23074
|
+
static paths = [["platform", "request-update"]];
|
|
23075
|
+
static usage = Command38.Usage({
|
|
23076
|
+
description: "Ask this installation's updater to converge to a version, exactly as the in-app button does.",
|
|
23077
|
+
details: "Writes the update request the updater job claims on its next tick. This is the ONLY privilege the requester holds \u2014 it does not deploy anything itself; the install's own updater fetches the release, applies it, health-gates it and rolls back on failure. With --wait, follows the request to a terminal state and exits non-zero if it did not succeed.",
|
|
23078
|
+
examples: [
|
|
23079
|
+
["Request an update", "m8t platform request-update --version 0.7.2 --resource-group rg-m8t --subscription <id>"],
|
|
23080
|
+
["Request it and wait", "m8t platform request-update --version 0.7.2 --wait --resource-group rg-m8t --subscription <id>"]
|
|
23081
|
+
]
|
|
23082
|
+
});
|
|
23083
|
+
version = Option35.String("--version", { description: "The platform version to converge to." });
|
|
23084
|
+
subscription = Option35.String("--subscription");
|
|
23085
|
+
resourceGroup = Option35.String("--resource-group");
|
|
23086
|
+
source = Option35.String("--source", "cd", { description: "Who is asking: cd | button | policy." });
|
|
23087
|
+
identity = Option35.String("--identity", "", { description: "Recorded on the request, for the audit trail." });
|
|
23088
|
+
miClientId = Option35.String("--mi-client-id");
|
|
23089
|
+
wait = Option35.Boolean("--wait", false, { description: "Follow the request until it settles." });
|
|
23090
|
+
/**
|
|
23091
|
+
* Follow an EXISTING request without writing a new one.
|
|
23092
|
+
*
|
|
23093
|
+
* Lets a caller write the request in one step and wait in another, which is
|
|
23094
|
+
* what makes the ordering provable: the row is a fact before anything is
|
|
23095
|
+
* started against it, instead of a background write racing a sleep.
|
|
23096
|
+
*/
|
|
23097
|
+
waitOnly = Option35.Boolean("--wait-only", false, { description: "Follow the current request without creating one." });
|
|
23098
|
+
timeoutMinutes = Option35.String("--timeout-minutes", "45");
|
|
23099
|
+
pollSeconds = Option35.String("--poll-seconds", "20");
|
|
23100
|
+
output = Option35.String("--output");
|
|
23101
|
+
async executeCommand() {
|
|
23102
|
+
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23103
|
+
const need = (v, flag) => {
|
|
23104
|
+
if (typeof v !== "string" || v.trim().length === 0) {
|
|
23105
|
+
throw new LocalCliError({ code: "PLATFORM_REQUEST_UPDATE_ARG_MISSING", message: `${flag} is required.` });
|
|
23106
|
+
}
|
|
23107
|
+
return v.trim();
|
|
23108
|
+
};
|
|
23109
|
+
const target = need(this.version, "--version");
|
|
23110
|
+
const SOURCES2 = ["cd", "button", "policy"];
|
|
23111
|
+
if (!SOURCES2.includes(this.source)) {
|
|
23112
|
+
throw new LocalCliError({ code: "PLATFORM_REQUEST_UPDATE_BAD_SOURCE", message: `--source must be cd, button or policy; got '${this.source}'.` });
|
|
23113
|
+
}
|
|
23114
|
+
const source = this.source;
|
|
23115
|
+
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential3({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential19();
|
|
23116
|
+
const client = await openApplyTable({
|
|
23117
|
+
credential: credential2,
|
|
23118
|
+
subscriptionId: need(this.subscription, "--subscription"),
|
|
23119
|
+
resourceGroup: need(this.resourceGroup, "--resource-group")
|
|
23120
|
+
});
|
|
23121
|
+
const log = (m) => {
|
|
23122
|
+
this.context.stderr.write(`${m}
|
|
23123
|
+
`);
|
|
23124
|
+
};
|
|
23125
|
+
if (!this.waitOnly) {
|
|
23126
|
+
const existing = await readApplyRequest(client);
|
|
23127
|
+
if (existing && blocksNewIntent(existing.status)) {
|
|
23128
|
+
throw new LocalCliError({
|
|
23129
|
+
code: "PLATFORM_REQUEST_UPDATE_IN_PROGRESS",
|
|
23130
|
+
message: `an update request for ${existing.target} is already ${existing.status}.`,
|
|
23131
|
+
hint: "Wait for it to settle, or clear it with `m8t platform clear-intent` if nothing is advancing it."
|
|
23132
|
+
});
|
|
23133
|
+
}
|
|
23134
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23135
|
+
const intent = newIntent(target, { source, identity: this.identity || source, at: now }, now);
|
|
23136
|
+
await client.upsertEntity(applyRequestToEntity(intent), "Replace");
|
|
23137
|
+
log(`requested update to ${target} (source ${source}).`);
|
|
23138
|
+
} else {
|
|
23139
|
+
log(`following the existing request for ${target}.`);
|
|
23140
|
+
}
|
|
23141
|
+
if (!this.wait && !this.waitOnly) {
|
|
23142
|
+
this.context.stdout.write(
|
|
23143
|
+
mode === "json" ? renderJson({ requested: true, target, waited: false }) + "\n" : `requested update to ${target}.
|
|
23144
|
+
`
|
|
23145
|
+
);
|
|
23146
|
+
return 0;
|
|
23147
|
+
}
|
|
23148
|
+
const settled = await this.follow(client, target, log);
|
|
23149
|
+
const result2 = {
|
|
23150
|
+
requested: true,
|
|
23151
|
+
target,
|
|
23152
|
+
waited: true,
|
|
23153
|
+
status: outcomeStatus(settled),
|
|
23154
|
+
error: settled?.result?.error ?? null
|
|
23155
|
+
};
|
|
23156
|
+
this.context.stdout.write(
|
|
23157
|
+
mode === "json" ? renderJson(result2) + "\n" : `${target}: ${result2.status}${result2.error ? ` \u2014 ${result2.error}` : ""}
|
|
23158
|
+
`
|
|
23159
|
+
);
|
|
23160
|
+
return outcomeExitCode(settled);
|
|
23161
|
+
}
|
|
23162
|
+
/**
|
|
23163
|
+
* Follow the request until it settles.
|
|
23164
|
+
*
|
|
23165
|
+
* `awaiting-engine-update` is explicitly NOT a failure and not a stall: the
|
|
23166
|
+
* updater parks a request there while it replaces its own image, so a converge
|
|
23167
|
+
* legitimately spans two ticks. Treating that as stuck would fail a build for
|
|
23168
|
+
* doing exactly what it is supposed to do.
|
|
23169
|
+
*/
|
|
23170
|
+
async follow(client, target, log) {
|
|
23171
|
+
const timeoutMs = Number(this.timeoutMinutes) * 60 * 1e3;
|
|
23172
|
+
const pollMs = Number(this.pollSeconds) * 1e3;
|
|
23173
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
23174
|
+
throw new LocalCliError({ code: "PLATFORM_REQUEST_UPDATE_BAD_TIMEOUT", message: `--timeout-minutes must be a positive number, got '${this.timeoutMinutes}'.` });
|
|
23175
|
+
}
|
|
23176
|
+
const deadline = Date.now() + timeoutMs;
|
|
23177
|
+
let lastReported = "";
|
|
23178
|
+
let consecutiveAbsent = 0;
|
|
23179
|
+
while (Date.now() < deadline) {
|
|
23180
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
23181
|
+
const verdict = classifyPolledRow(await readApplyRequest(client), target);
|
|
23182
|
+
if (!verdict) {
|
|
23183
|
+
consecutiveAbsent += 1;
|
|
23184
|
+
if (consecutiveAbsent >= 3) {
|
|
23185
|
+
throw new LocalCliError({
|
|
23186
|
+
code: "PLATFORM_REQUEST_UPDATE_ROW_ABSENT",
|
|
23187
|
+
message: `no update request found for ${target} after ${String(consecutiveAbsent)} polls.`,
|
|
23188
|
+
hint: "Something removed the row after it was written. Check for a concurrent `clear-intent` or a competing requester."
|
|
23189
|
+
});
|
|
23190
|
+
}
|
|
23191
|
+
continue;
|
|
23192
|
+
}
|
|
23193
|
+
consecutiveAbsent = 0;
|
|
23194
|
+
if (verdict.kind === "settled") return verdict.row;
|
|
23195
|
+
const line2 = verdict.kind === "not-ours" ? ` (ignoring a request for ${verdict.otherTarget} \u2014 not the one we made)` : ` ${verdict.where}`;
|
|
23196
|
+
if (line2 !== lastReported) {
|
|
23197
|
+
log(line2);
|
|
23198
|
+
lastReported = line2;
|
|
23199
|
+
}
|
|
23200
|
+
}
|
|
23201
|
+
return null;
|
|
23202
|
+
}
|
|
23203
|
+
};
|
|
23204
|
+
|
|
23205
|
+
// src/commands/platform/seed-stamp.ts
|
|
23206
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
23207
|
+
import { Command as Command39, Option as Option36 } from "clipanion";
|
|
23208
|
+
import { DefaultAzureCredential as DefaultAzureCredential20, ManagedIdentityCredential as ManagedIdentityCredential4 } from "@azure/identity";
|
|
23209
|
+
init_errors();
|
|
23210
|
+
|
|
22863
23211
|
// src/lib/platform-stamp-seed.ts
|
|
22864
23212
|
var IMAGE_COMPONENTS = ["gateway", "codingAgent", "azureExecutor"];
|
|
22865
23213
|
var BARE_SEMVER = /^\d+\.\d+\.\d+$/;
|
|
@@ -22942,7 +23290,7 @@ function buildSeedStamp(d, cliVersion, nowIso) {
|
|
|
22942
23290
|
|
|
22943
23291
|
// src/commands/platform/seed-stamp.ts
|
|
22944
23292
|
async function seedStamp2(args) {
|
|
22945
|
-
const readFileImpl = args.readFileImpl ?? ((p) =>
|
|
23293
|
+
const readFileImpl = args.readFileImpl ?? ((p) => readFileSync20(p, "utf8"));
|
|
22946
23294
|
const readStampImpl = args.readStampImpl ?? readStamp;
|
|
22947
23295
|
const writeStampImpl = args.writeStampImpl ?? ((stamp2, o) => writeStamp({ ...o, stamp: stamp2 }));
|
|
22948
23296
|
const discoverImpl = args.discoverImpl ?? discoverStampStorage;
|
|
@@ -22988,15 +23336,15 @@ async function seedStamp2(args) {
|
|
|
22988
23336
|
}
|
|
22989
23337
|
var PlatformSeedStampCommand = class extends M8tCommand {
|
|
22990
23338
|
static paths = [["platform", "seed-stamp"]];
|
|
22991
|
-
static usage =
|
|
23339
|
+
static usage = Command39.Usage({
|
|
22992
23340
|
description: "INTERNAL: write the initial installed-state stamp from a build-time install descriptor.",
|
|
22993
23341
|
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'."
|
|
22994
23342
|
});
|
|
22995
|
-
descriptor =
|
|
22996
|
-
subscription =
|
|
22997
|
-
resourceGroup =
|
|
22998
|
-
miClientId =
|
|
22999
|
-
output =
|
|
23343
|
+
descriptor = Option36.String("--descriptor", { description: "Path to install-descriptor.json (baked into the installer image)." });
|
|
23344
|
+
subscription = Option36.String("--subscription");
|
|
23345
|
+
resourceGroup = Option36.String("--resource-group");
|
|
23346
|
+
miClientId = Option36.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
|
|
23347
|
+
output = Option36.String("--output");
|
|
23000
23348
|
async executeCommand() {
|
|
23001
23349
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23002
23350
|
const need = (v, flag) => {
|
|
@@ -23009,7 +23357,7 @@ var PlatformSeedStampCommand = class extends M8tCommand {
|
|
|
23009
23357
|
this.context.stderr.write(`${m}
|
|
23010
23358
|
`);
|
|
23011
23359
|
};
|
|
23012
|
-
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new
|
|
23360
|
+
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential4({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential20();
|
|
23013
23361
|
const result2 = await seedStamp2({
|
|
23014
23362
|
descriptorPath: need(this.descriptor, "--descriptor"),
|
|
23015
23363
|
credential: credential2,
|
|
@@ -23027,105 +23375,9 @@ var PlatformSeedStampCommand = class extends M8tCommand {
|
|
|
23027
23375
|
}
|
|
23028
23376
|
};
|
|
23029
23377
|
|
|
23030
|
-
// src/commands/platform/stamp-build.ts
|
|
23031
|
-
import { Command as Command38, Option as Option35 } from "clipanion";
|
|
23032
|
-
import { DefaultAzureCredential as DefaultAzureCredential19, ManagedIdentityCredential as ManagedIdentityCredential3 } from "@azure/identity";
|
|
23033
|
-
init_errors();
|
|
23034
|
-
|
|
23035
|
-
// src/lib/platform-stamp-build.ts
|
|
23036
|
-
var BARE_SEMVER2 = /^\d+\.\d+\.\d+$/;
|
|
23037
|
-
function buildRunningBuildStamp(input) {
|
|
23038
|
-
if (!BARE_SEMVER2.test(input.version)) {
|
|
23039
|
-
throw new Error(
|
|
23040
|
-
`platform version must be bare X.Y.Z, got "${input.version}" \u2014 a build tag cannot be represented as a version.`
|
|
23041
|
-
);
|
|
23042
|
-
}
|
|
23043
|
-
return {
|
|
23044
|
-
schemaVersion: 1,
|
|
23045
|
-
platformVersion: platformTag(input.version),
|
|
23046
|
-
previousPlatformVersion: input.previousPlatformVersion,
|
|
23047
|
-
updatedAt: input.nowIso,
|
|
23048
|
-
lastResult: "success",
|
|
23049
|
-
cli: input.cliVersion,
|
|
23050
|
-
origin: "cd",
|
|
23051
|
-
components: {
|
|
23052
|
-
gateway: { tag: input.gatewayTag, digest: input.gatewayDigest, state: "managed" },
|
|
23053
|
-
codingAgent: { tag: "", digest: "", state: "external" },
|
|
23054
|
-
azureExecutor: { tag: "", digest: "", state: "external" },
|
|
23055
|
-
personas: input.personas,
|
|
23056
|
-
infra: { treeSha: input.infraTreeSha },
|
|
23057
|
-
...input.brainSeeds ? { brainSeeds: input.brainSeeds } : {}
|
|
23058
|
-
}
|
|
23059
|
-
};
|
|
23060
|
-
}
|
|
23061
|
-
|
|
23062
|
-
// src/commands/platform/stamp-build.ts
|
|
23063
|
-
var PlatformStampBuildCommand = class extends M8tCommand {
|
|
23064
|
-
static paths = [["platform", "stamp-build"]];
|
|
23065
|
-
static usage = Command38.Usage({
|
|
23066
|
-
description: "Record the currently running build as this install's platform version.",
|
|
23067
|
-
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."
|
|
23068
|
-
});
|
|
23069
|
-
version = Option35.String("--version", { description: "The running build's own version, bare X.Y.Z." });
|
|
23070
|
-
gatewayTag = Option35.String("--gateway-tag", { description: "The tag of the gateway image this build deployed." });
|
|
23071
|
-
gatewayDigest = Option35.String("--gateway-digest", { description: "The digest of the gateway image this build deployed." });
|
|
23072
|
-
subscription = Option35.String("--subscription");
|
|
23073
|
-
resourceGroup = Option35.String("--resource-group");
|
|
23074
|
-
miClientId = Option35.String("--mi-client-id", { description: "Authenticate as this user-assigned managed identity instead of the ambient credential." });
|
|
23075
|
-
output = Option35.String("--output");
|
|
23076
|
-
async executeCommand() {
|
|
23077
|
-
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23078
|
-
const need = (v, flag) => {
|
|
23079
|
-
if (typeof v !== "string" || v.trim().length === 0) {
|
|
23080
|
-
throw new LocalCliError({ code: "PLATFORM_STAMP_BUILD_ARG_MISSING", message: `${flag} is required.` });
|
|
23081
|
-
}
|
|
23082
|
-
return v.trim();
|
|
23083
|
-
};
|
|
23084
|
-
const onProgress = (m) => {
|
|
23085
|
-
this.context.stderr.write(`${m}
|
|
23086
|
-
`);
|
|
23087
|
-
};
|
|
23088
|
-
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential3({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential19();
|
|
23089
|
-
const ctx = {
|
|
23090
|
-
credential: credential2,
|
|
23091
|
-
subscriptionId: need(this.subscription, "--subscription"),
|
|
23092
|
-
resourceGroup: need(this.resourceGroup, "--resource-group")
|
|
23093
|
-
};
|
|
23094
|
-
const { accountName } = await discoverStampStorage(ctx);
|
|
23095
|
-
const existing = await readStampOutcome(ctx);
|
|
23096
|
-
if (existing.source === "unreadable") {
|
|
23097
|
-
throw new LocalCliError({
|
|
23098
|
-
code: "PLATFORM_STAMP_BUILD_PRIOR_UNREADABLE",
|
|
23099
|
-
message: "Could not read the existing platform stamp; refusing to overwrite it blind.",
|
|
23100
|
-
hint: "The running build is the truth about the CURRENT version, not about whether a previous one existed \u2014 retry once the prior record can be read."
|
|
23101
|
-
});
|
|
23102
|
-
}
|
|
23103
|
-
const previousPlatformVersion = existing.source === "explicit" ? existing.stamp.platformVersion : null;
|
|
23104
|
-
const priorComponents = existing.source === "explicit" ? existing.stamp.components : void 0;
|
|
23105
|
-
const stamp = buildRunningBuildStamp({
|
|
23106
|
-
version: need(this.version, "--version"),
|
|
23107
|
-
gatewayTag: need(this.gatewayTag, "--gateway-tag"),
|
|
23108
|
-
gatewayDigest: this.gatewayDigest ?? "",
|
|
23109
|
-
previousPlatformVersion,
|
|
23110
|
-
cliVersion: CLI_VERSION,
|
|
23111
|
-
nowIso: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23112
|
-
personas: priorComponents?.personas ?? {},
|
|
23113
|
-
infraTreeSha: priorComponents?.infra.treeSha ?? "",
|
|
23114
|
-
...priorComponents?.brainSeeds ? { brainSeeds: priorComponents.brainSeeds } : {}
|
|
23115
|
-
});
|
|
23116
|
-
await writeStamp({ ...ctx, stamp, onProgress });
|
|
23117
|
-
const result2 = { platformVersion: stamp.platformVersion, previous: previousPlatformVersion, storageAccount: accountName };
|
|
23118
|
-
this.context.stdout.write(
|
|
23119
|
-
mode === "json" ? renderJson(result2) + "\n" : `platform stamp: ${result2.platformVersion} (previous ${result2.previous ?? "none"}, storage ${result2.storageAccount})
|
|
23120
|
-
`
|
|
23121
|
-
);
|
|
23122
|
-
return 0;
|
|
23123
|
-
}
|
|
23124
|
-
};
|
|
23125
|
-
|
|
23126
23378
|
// src/commands/platform/policy.ts
|
|
23127
|
-
import { Command as
|
|
23128
|
-
import { DefaultAzureCredential as
|
|
23379
|
+
import { Command as Command40, Option as Option37 } from "clipanion";
|
|
23380
|
+
import { DefaultAzureCredential as DefaultAzureCredential21, ManagedIdentityCredential as ManagedIdentityCredential5 } from "@azure/identity";
|
|
23129
23381
|
init_errors();
|
|
23130
23382
|
|
|
23131
23383
|
// src/lib/platform-policy.ts
|
|
@@ -23180,16 +23432,16 @@ async function readPolicy(opts) {
|
|
|
23180
23432
|
// src/commands/platform/policy.ts
|
|
23181
23433
|
var PlatformPolicyCommand = class extends M8tCommand {
|
|
23182
23434
|
static paths = [["platform", "policy"]];
|
|
23183
|
-
static usage =
|
|
23435
|
+
static usage = Command40.Usage({
|
|
23184
23436
|
description: "Show or set how this install handles available platform updates.",
|
|
23185
23437
|
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.",
|
|
23186
23438
|
examples: [["Show the current mode", "m8t platform policy"], ["Never apply automatically", "m8t platform policy --set notify-only"]]
|
|
23187
23439
|
});
|
|
23188
|
-
set =
|
|
23189
|
-
subscription =
|
|
23190
|
-
resourceGroup =
|
|
23191
|
-
miClientId =
|
|
23192
|
-
output =
|
|
23440
|
+
set = Option37.String("--set", { description: "notify-only | auto-critical | auto-all" });
|
|
23441
|
+
subscription = Option37.String("--subscription");
|
|
23442
|
+
resourceGroup = Option37.String("--resource-group");
|
|
23443
|
+
miClientId = Option37.String("--mi-client-id");
|
|
23444
|
+
output = Option37.String("--output");
|
|
23193
23445
|
async executeCommand() {
|
|
23194
23446
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
23195
23447
|
const need = (v, flag) => {
|
|
@@ -23202,7 +23454,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
23202
23454
|
this.context.stderr.write(`${m}
|
|
23203
23455
|
`);
|
|
23204
23456
|
};
|
|
23205
|
-
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new
|
|
23457
|
+
const credential2 = typeof this.miClientId === "string" && this.miClientId.trim().length > 0 ? new ManagedIdentityCredential5({ clientId: this.miClientId.trim() }) : new DefaultAzureCredential21();
|
|
23206
23458
|
const ctx = {
|
|
23207
23459
|
credential: credential2,
|
|
23208
23460
|
subscriptionId: need(this.subscription, "--subscription"),
|
|
@@ -23235,7 +23487,7 @@ var PlatformPolicyCommand = class extends M8tCommand {
|
|
|
23235
23487
|
};
|
|
23236
23488
|
|
|
23237
23489
|
// src/commands/platform/enable-cost-report.ts
|
|
23238
|
-
import { Command as
|
|
23490
|
+
import { Command as Command41, Option as Option38 } from "clipanion";
|
|
23239
23491
|
|
|
23240
23492
|
// src/lib/wire-gateway-acs.ts
|
|
23241
23493
|
init_rbac();
|
|
@@ -23275,18 +23527,18 @@ async function wireGatewayForAcs(args) {
|
|
|
23275
23527
|
init_errors();
|
|
23276
23528
|
var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
23277
23529
|
static paths = [["platform", "enable-cost-report"]];
|
|
23278
|
-
static usage =
|
|
23530
|
+
static usage = Command41.Usage({
|
|
23279
23531
|
description: "Wire the deployed gateway to send the bi-weekly cost report via ACS Email.",
|
|
23280
23532
|
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')."
|
|
23281
23533
|
});
|
|
23282
|
-
subscription =
|
|
23283
|
-
resourceGroup =
|
|
23534
|
+
subscription = Option38.String("--subscription");
|
|
23535
|
+
resourceGroup = Option38.String("--resource-group", {
|
|
23284
23536
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
23285
23537
|
});
|
|
23286
|
-
acsEndpoint =
|
|
23287
|
-
acsSender =
|
|
23288
|
-
acsResourceId =
|
|
23289
|
-
output =
|
|
23538
|
+
acsEndpoint = Option38.String("--acs-endpoint", { required: true, description: "ACS data-plane endpoint, e.g. https://acs-<name>.communication.azure.com/" });
|
|
23539
|
+
acsSender = Option38.String("--acs-sender", { required: true, description: "Managed-domain sender address, e.g. DoNotReply@<guid>.azurecomm.net" });
|
|
23540
|
+
acsResourceId = Option38.String("--acs-resource-id", { required: true, description: "ARM resource id of the acs-<name> Communication Service \u2014 the role-grant scope." });
|
|
23541
|
+
output = Option38.String("--output");
|
|
23290
23542
|
async executeCommand() {
|
|
23291
23543
|
const mode = resolveOutputMode(
|
|
23292
23544
|
this.output,
|
|
@@ -23360,45 +23612,45 @@ var PlatformEnableCostReportCommand = class extends M8tCommand {
|
|
|
23360
23612
|
};
|
|
23361
23613
|
|
|
23362
23614
|
// src/commands/platform/enable-auto-update.ts
|
|
23363
|
-
import { Command as
|
|
23615
|
+
import { Command as Command42, Option as Option39 } from "clipanion";
|
|
23364
23616
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
23365
|
-
import { DefaultAzureCredential as
|
|
23617
|
+
import { DefaultAzureCredential as DefaultAzureCredential22 } from "@azure/identity";
|
|
23366
23618
|
init_errors();
|
|
23367
23619
|
var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
23368
23620
|
static paths = [["platform", "enable-auto-update"]];
|
|
23369
|
-
static usage =
|
|
23621
|
+
static usage = Command42.Usage({
|
|
23370
23622
|
category: "Platform",
|
|
23371
23623
|
description: "Retrofit the auto-updater (MI + cron job + role assignments) onto an existing install.",
|
|
23372
23624
|
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."
|
|
23373
23625
|
});
|
|
23374
|
-
subscription =
|
|
23375
|
-
resourceGroup =
|
|
23626
|
+
subscription = Option39.String("--subscription");
|
|
23627
|
+
resourceGroup = Option39.String("--resource-group", {
|
|
23376
23628
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
23377
23629
|
});
|
|
23378
|
-
suffix =
|
|
23630
|
+
suffix = Option39.String("--suffix", {
|
|
23379
23631
|
description: "The existing deployment's resource-name suffix. Required when it cannot be recovered from system/infra-params or the live gateway."
|
|
23380
23632
|
});
|
|
23381
|
-
installerImage =
|
|
23633
|
+
installerImage = Option39.String("--installer-image", {
|
|
23382
23634
|
required: true,
|
|
23383
23635
|
description: "Updater CA-Job image ref (the converge engine at the current release). Required \u2014 an empty image would skip provisioning."
|
|
23384
23636
|
});
|
|
23385
|
-
updateCron =
|
|
23637
|
+
updateCron = Option39.String("--update-cron", {
|
|
23386
23638
|
description: "Cron schedule for the updater job (bicep default applies when omitted)."
|
|
23387
23639
|
});
|
|
23388
|
-
channelUrl =
|
|
23640
|
+
channelUrl = Option39.String("--channel-url", {
|
|
23389
23641
|
description: "Release-channel URL the updater job polls (bicep default applies when omitted)."
|
|
23390
23642
|
});
|
|
23391
|
-
location =
|
|
23643
|
+
location = Option39.String("--location", {
|
|
23392
23644
|
description: "Region for the updater identity + job. Defaults to the resource group's existing resources."
|
|
23393
23645
|
});
|
|
23394
|
-
foundryTracing =
|
|
23646
|
+
foundryTracing = Option39.String("--foundry-tracing", {
|
|
23395
23647
|
description: "project | account | skip. Pass the value the install was deployed with \u2014 omitting it lets the bicep default (project) switch tracing on."
|
|
23396
23648
|
});
|
|
23397
|
-
endpoint =
|
|
23649
|
+
endpoint = Option39.String("--endpoint", {
|
|
23398
23650
|
description: "Foundry project endpoint URL. Disambiguates the project in a multi-project subscription."
|
|
23399
23651
|
});
|
|
23400
|
-
yes =
|
|
23401
|
-
output =
|
|
23652
|
+
yes = Option39.Boolean("--yes", false);
|
|
23653
|
+
output = Option39.String("--output");
|
|
23402
23654
|
async executeCommand() {
|
|
23403
23655
|
const mode = resolveOutputMode(
|
|
23404
23656
|
this.output,
|
|
@@ -23417,7 +23669,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
23417
23669
|
resourceGroup: this.resourceGroup
|
|
23418
23670
|
});
|
|
23419
23671
|
const { resourceGroup, name: gatewayName } = parseContainerAppResourceId(gw.containerAppResourceId);
|
|
23420
|
-
const credential2 = new
|
|
23672
|
+
const credential2 = new DefaultAzureCredential22();
|
|
23421
23673
|
const account = await getAzAccount();
|
|
23422
23674
|
const subscriptionId = this.subscription ?? account.subscriptionId;
|
|
23423
23675
|
const explicitSuffix = typeof this.suffix === "string" && this.suffix.length > 0 ? this.suffix : void 0;
|
|
@@ -23535,7 +23787,7 @@ var PlatformEnableAutoUpdateCommand = class extends M8tCommand {
|
|
|
23535
23787
|
};
|
|
23536
23788
|
|
|
23537
23789
|
// src/commands/deploy.ts
|
|
23538
|
-
import { Command as
|
|
23790
|
+
import { Command as Command43, Option as Option40 } from "clipanion";
|
|
23539
23791
|
|
|
23540
23792
|
// src/lib/app-reg.ts
|
|
23541
23793
|
init_esm();
|
|
@@ -23869,7 +24121,7 @@ async function patchRedirectUris(appObjectId, fqdn) {
|
|
|
23869
24121
|
init_errors();
|
|
23870
24122
|
|
|
23871
24123
|
// src/lib/whatif.ts
|
|
23872
|
-
import * as
|
|
24124
|
+
import * as path32 from "path";
|
|
23873
24125
|
|
|
23874
24126
|
// src/lib/identifier-patterns.ts
|
|
23875
24127
|
var IDENTIFIER_PATTERNS = [
|
|
@@ -23925,7 +24177,7 @@ function deriveResourceType(resourceId) {
|
|
|
23925
24177
|
return parts.join("/");
|
|
23926
24178
|
}
|
|
23927
24179
|
async function runWhatIf(opts) {
|
|
23928
|
-
const bicepPath =
|
|
24180
|
+
const bicepPath = path32.join(opts.repoRoot, "deploy", "main.bicep");
|
|
23929
24181
|
const out = await runAz([
|
|
23930
24182
|
"deployment",
|
|
23931
24183
|
"group",
|
|
@@ -24023,9 +24275,9 @@ function flattenDelta(delta, prefix = "") {
|
|
|
24023
24275
|
const out = [];
|
|
24024
24276
|
for (const d of delta) {
|
|
24025
24277
|
const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
|
|
24026
|
-
const
|
|
24027
|
-
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children,
|
|
24028
|
-
else out.push({ ...d, path:
|
|
24278
|
+
const path47 = prefix ? `${prefix}${segment}` : d.path;
|
|
24279
|
+
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path47));
|
|
24280
|
+
else out.push({ ...d, path: path47 });
|
|
24029
24281
|
}
|
|
24030
24282
|
return out;
|
|
24031
24283
|
}
|
|
@@ -24075,52 +24327,63 @@ function classifyWhatIf(changes) {
|
|
|
24075
24327
|
var DEFAULT_IMAGE_REF = "ghcr.io/m8t-labs/m8t:latest";
|
|
24076
24328
|
var DeployCommand = class extends M8tCommand {
|
|
24077
24329
|
static paths = [["deploy"]];
|
|
24078
|
-
static usage =
|
|
24330
|
+
static usage = Command43.Usage({
|
|
24079
24331
|
description: "Deploy (or update) the m8t gateway/webapp stack via Bicep.",
|
|
24080
24332
|
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."
|
|
24081
24333
|
});
|
|
24082
|
-
subscription =
|
|
24083
|
-
resourceGroup =
|
|
24084
|
-
location =
|
|
24085
|
-
suffix =
|
|
24086
|
-
imageRef =
|
|
24087
|
-
acrPullIdentity =
|
|
24088
|
-
acrResourceId =
|
|
24089
|
-
foundryEndpoint =
|
|
24090
|
-
foundryResourceId =
|
|
24091
|
-
foundryTracing =
|
|
24334
|
+
subscription = Option40.String("--subscription");
|
|
24335
|
+
resourceGroup = Option40.String("--resource-group", "rg-m8t-stack");
|
|
24336
|
+
location = Option40.String("--location", "eastus");
|
|
24337
|
+
suffix = Option40.String("--suffix", "");
|
|
24338
|
+
imageRef = Option40.String("--image-ref", DEFAULT_IMAGE_REF);
|
|
24339
|
+
acrPullIdentity = Option40.String("--acrpull-identity");
|
|
24340
|
+
acrResourceId = Option40.String("--acr-resource-id");
|
|
24341
|
+
foundryEndpoint = Option40.String("--foundry-endpoint");
|
|
24342
|
+
foundryResourceId = Option40.String("--foundry-resource-id");
|
|
24343
|
+
foundryTracing = Option40.String("--foundry-tracing");
|
|
24092
24344
|
// project | account | skip (bicep default: project)
|
|
24093
|
-
clientId =
|
|
24094
|
-
whatIf =
|
|
24345
|
+
clientId = Option40.String("--client-id");
|
|
24346
|
+
whatIf = Option40.Boolean("--what-if", false);
|
|
24095
24347
|
// Only meaningful with --what-if. Routes the comparison through the
|
|
24096
24348
|
// value-free renderers (see ./lib/whatif-redact.js) instead of the default
|
|
24097
24349
|
// before/after renderer. Defaults false so a local, interactive run keeps
|
|
24098
24350
|
// showing values — that is the whole diagnostic point of --what-if.
|
|
24099
24351
|
// Automation that forwards this output anywhere non-private (a CI log, an
|
|
24100
24352
|
// issue) MUST pass --redact.
|
|
24101
|
-
redact =
|
|
24102
|
-
output =
|
|
24353
|
+
redact = Option40.Boolean("--redact", false);
|
|
24354
|
+
output = Option40.String("--output");
|
|
24103
24355
|
// Subscription-scoped role assignments. Omitted ⇒ the template default (true).
|
|
24104
24356
|
// Pass false when deploying as a principal scoped to the resource group only:
|
|
24105
24357
|
// it cannot deploy at subscription scope, and those assignments persist
|
|
24106
24358
|
// idempotently from the initial deployment anyway.
|
|
24107
|
-
assignSubscriptionRoles =
|
|
24359
|
+
assignSubscriptionRoles = Option40.String("--assign-subscription-roles");
|
|
24360
|
+
/**
|
|
24361
|
+
* The installer image the updater Container-Apps Job runs.
|
|
24362
|
+
*
|
|
24363
|
+
* main.bicep only provisions the updater when an image is supplied
|
|
24364
|
+
* (`provisionUpdaterEffective = provisionUpdater && !empty(installerImage)`),
|
|
24365
|
+
* so a run that omits this describes a deployment WITHOUT an updater. For a
|
|
24366
|
+
* real deploy that is merely a skip; for a `--what-if` comparison it is worse
|
|
24367
|
+
* than that — the comparison proposes REMOVING an updater job that exists and
|
|
24368
|
+
* should, and reports it as drift on every single run.
|
|
24369
|
+
*/
|
|
24370
|
+
installerImage = Option40.String("--installer-image");
|
|
24108
24371
|
// Referee — all optional, undefined by default ⇒ the bicep defaults
|
|
24109
24372
|
// apply (dormant: gatewayCpu=0.25, gatewayMemory=0.5Gi, every referee/exam var
|
|
24110
24373
|
// empty). Only pass these when explicitly enabling the referee exam stack.
|
|
24111
|
-
gatewayCpu =
|
|
24112
|
-
gatewayMemory =
|
|
24113
|
-
refereeEnabled =
|
|
24114
|
-
refereeBrainRepos =
|
|
24115
|
-
refereeFeedRepo =
|
|
24116
|
-
refereeInstallationId =
|
|
24117
|
-
refereeWebhookHmacKvUri =
|
|
24118
|
-
examKvUri =
|
|
24119
|
-
examLaWorkspaceId =
|
|
24120
|
-
brainEvalDeployment =
|
|
24121
|
-
brainAppLogin =
|
|
24122
|
-
refereeCheckpointDir =
|
|
24123
|
-
examApiBase =
|
|
24374
|
+
gatewayCpu = Option40.String("--gateway-cpu");
|
|
24375
|
+
gatewayMemory = Option40.String("--gateway-memory");
|
|
24376
|
+
refereeEnabled = Option40.String("--referee-enabled");
|
|
24377
|
+
refereeBrainRepos = Option40.String("--referee-brain-repos");
|
|
24378
|
+
refereeFeedRepo = Option40.String("--referee-feed-repo");
|
|
24379
|
+
refereeInstallationId = Option40.String("--referee-installation-id");
|
|
24380
|
+
refereeWebhookHmacKvUri = Option40.String("--referee-webhook-hmac-kv-uri");
|
|
24381
|
+
examKvUri = Option40.String("--exam-kv-uri");
|
|
24382
|
+
examLaWorkspaceId = Option40.String("--exam-la-workspace-id");
|
|
24383
|
+
brainEvalDeployment = Option40.String("--brain-eval-deployment");
|
|
24384
|
+
brainAppLogin = Option40.String("--brain-app-login");
|
|
24385
|
+
refereeCheckpointDir = Option40.String("--referee-checkpoint-dir");
|
|
24386
|
+
examApiBase = Option40.String("--exam-api-base");
|
|
24124
24387
|
async executeCommand() {
|
|
24125
24388
|
const mode = resolveOutputMode(
|
|
24126
24389
|
this.output,
|
|
@@ -24182,7 +24445,8 @@ var DeployCommand = class extends M8tCommand {
|
|
|
24182
24445
|
brainAppLogin: this.brainAppLogin,
|
|
24183
24446
|
refereeCheckpointDir: this.refereeCheckpointDir,
|
|
24184
24447
|
examApiBase: this.examApiBase,
|
|
24185
|
-
...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) }
|
|
24448
|
+
...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) },
|
|
24449
|
+
...this.installerImage ? { installerImage: this.installerImage } : {}
|
|
24186
24450
|
});
|
|
24187
24451
|
const changes = await runWhatIf({ resourceGroup: this.resourceGroup, repoRoot: repoRoot2, params: params2 });
|
|
24188
24452
|
const classified = classifyWhatIf(changes);
|
|
@@ -24250,7 +24514,8 @@ var DeployCommand = class extends M8tCommand {
|
|
|
24250
24514
|
brainAppLogin: this.brainAppLogin,
|
|
24251
24515
|
refereeCheckpointDir: this.refereeCheckpointDir,
|
|
24252
24516
|
examApiBase: this.examApiBase,
|
|
24253
|
-
...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) }
|
|
24517
|
+
...parseAssignSubscriptionRoles(this.assignSubscriptionRoles) === void 0 ? {} : { assignSubscriptionRoles: parseAssignSubscriptionRoles(this.assignSubscriptionRoles) },
|
|
24518
|
+
...this.installerImage ? { installerImage: this.installerImage } : {}
|
|
24254
24519
|
});
|
|
24255
24520
|
log("running Bicep deployment (~3-5 min)\u2026");
|
|
24256
24521
|
const outputs = await runBicepDeployment({
|
|
@@ -24299,7 +24564,7 @@ var DeployCommand = class extends M8tCommand {
|
|
|
24299
24564
|
|
|
24300
24565
|
// src/commands/eval/skill.ts
|
|
24301
24566
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
24302
|
-
import { Command as
|
|
24567
|
+
import { Command as Command44, Option as Option41 } from "clipanion";
|
|
24303
24568
|
init_errors();
|
|
24304
24569
|
var DECISIONS = /* @__PURE__ */ new Set(["promote", "reject", "needs_review"]);
|
|
24305
24570
|
var JUDGE_STATUSES = /* @__PURE__ */ new Set(["ok", "skipped", "unavailable"]);
|
|
@@ -24324,14 +24589,14 @@ function parseVerdict(stdout) {
|
|
|
24324
24589
|
}
|
|
24325
24590
|
var EvalSkillCommand = class extends M8tCommand {
|
|
24326
24591
|
static paths = [["eval", "skill"]];
|
|
24327
|
-
static usage =
|
|
24592
|
+
static usage = Command44.Usage({
|
|
24328
24593
|
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)."
|
|
24329
24594
|
});
|
|
24330
|
-
candidate =
|
|
24331
|
-
skillsDir =
|
|
24332
|
-
noJudge =
|
|
24333
|
-
deployment =
|
|
24334
|
-
output =
|
|
24595
|
+
candidate = Option41.String();
|
|
24596
|
+
skillsDir = Option41.String("--skills-dir");
|
|
24597
|
+
noJudge = Option41.Boolean("--no-judge", false);
|
|
24598
|
+
deployment = Option41.String("--deployment");
|
|
24599
|
+
output = Option41.String("--output");
|
|
24335
24600
|
executeCommand() {
|
|
24336
24601
|
return Promise.resolve(this._runCommand());
|
|
24337
24602
|
}
|
|
@@ -24388,9 +24653,9 @@ var EvalSkillCommand = class extends M8tCommand {
|
|
|
24388
24653
|
|
|
24389
24654
|
// src/commands/eval/exam.ts
|
|
24390
24655
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
24391
|
-
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as
|
|
24392
|
-
import { join as
|
|
24393
|
-
import { Command as
|
|
24656
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, readdirSync as readdirSync3 } from "fs";
|
|
24657
|
+
import { join as join30 } from "path";
|
|
24658
|
+
import { Command as Command45, Option as Option42 } from "clipanion";
|
|
24394
24659
|
init_errors();
|
|
24395
24660
|
init_esm();
|
|
24396
24661
|
function parseArmToken(tok, opts) {
|
|
@@ -24571,9 +24836,9 @@ function resolveRefereeHome(refereeHomeOut, env) {
|
|
|
24571
24836
|
return null;
|
|
24572
24837
|
}
|
|
24573
24838
|
function resolveTaskSetDir(base, worker, version) {
|
|
24574
|
-
if (existsSync20(
|
|
24839
|
+
if (existsSync20(join30(base, version))) return version;
|
|
24575
24840
|
const prefixed = `${worker}-${version}`;
|
|
24576
|
-
if (existsSync20(
|
|
24841
|
+
if (existsSync20(join30(base, prefixed))) return prefixed;
|
|
24577
24842
|
if (version === "latest" && existsSync20(base)) {
|
|
24578
24843
|
const dirs = readdirSync3(base, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith(`${worker}-`)).map((d) => d.name).sort();
|
|
24579
24844
|
if (dirs.length > 0) return dirs[dirs.length - 1];
|
|
@@ -24589,11 +24854,11 @@ function readSealedTaskIds(taskSetVersion, refereeHomeOut, env = process.env) {
|
|
|
24589
24854
|
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)"
|
|
24590
24855
|
});
|
|
24591
24856
|
}
|
|
24592
|
-
const versionDir = resolveTaskSetDir(
|
|
24593
|
-
const manifestPath =
|
|
24857
|
+
const versionDir = resolveTaskSetDir(join30(home, "tasksets", worker), worker, version);
|
|
24858
|
+
const manifestPath = join30(home, "tasksets", worker, versionDir, "manifest.yaml");
|
|
24594
24859
|
let text;
|
|
24595
24860
|
try {
|
|
24596
|
-
text =
|
|
24861
|
+
text = readFileSync21(manifestPath, "utf-8");
|
|
24597
24862
|
} catch (e) {
|
|
24598
24863
|
throw new LocalCliError({
|
|
24599
24864
|
code: "EXAM_REDACTION_UNSAFE",
|
|
@@ -24620,24 +24885,24 @@ function buildPlan(args) {
|
|
|
24620
24885
|
}
|
|
24621
24886
|
var EvalExamCommand = class extends M8tCommand {
|
|
24622
24887
|
static paths = [["eval", "exam"]];
|
|
24623
|
-
static usage =
|
|
24888
|
+
static usage = Command45.Usage({
|
|
24624
24889
|
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."
|
|
24625
24890
|
});
|
|
24626
|
-
worker =
|
|
24627
|
-
arms =
|
|
24628
|
-
taskSet =
|
|
24629
|
-
examType =
|
|
24630
|
-
skill =
|
|
24631
|
-
reps =
|
|
24632
|
-
probes =
|
|
24633
|
-
pool =
|
|
24634
|
-
out =
|
|
24635
|
-
dryRun =
|
|
24636
|
-
keepArms =
|
|
24637
|
-
allowStub =
|
|
24638
|
-
deployment =
|
|
24639
|
-
output =
|
|
24640
|
-
observeWaitS =
|
|
24891
|
+
worker = Option42.String();
|
|
24892
|
+
arms = Option42.String("--arms");
|
|
24893
|
+
taskSet = Option42.String("--task-set");
|
|
24894
|
+
examType = Option42.String("--exam-type");
|
|
24895
|
+
skill = Option42.String("--skill");
|
|
24896
|
+
reps = Option42.String("-n,--reps");
|
|
24897
|
+
probes = Option42.String("--probes");
|
|
24898
|
+
pool = Option42.String("--pool");
|
|
24899
|
+
out = Option42.String("--out");
|
|
24900
|
+
dryRun = Option42.Boolean("--dry-run", false);
|
|
24901
|
+
keepArms = Option42.Boolean("--keep-arms", false);
|
|
24902
|
+
allowStub = Option42.Boolean("--allow-stub", false);
|
|
24903
|
+
deployment = Option42.String("--deployment");
|
|
24904
|
+
output = Option42.String("--output");
|
|
24905
|
+
observeWaitS = Option42.String("--observe-wait-s");
|
|
24641
24906
|
async executeCommand() {
|
|
24642
24907
|
await Promise.resolve();
|
|
24643
24908
|
const worker = typeof this.worker === "string" ? this.worker : void 0;
|
|
@@ -24714,9 +24979,9 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
24714
24979
|
if (out !== void 0) {
|
|
24715
24980
|
const sealedTaskIds = taskSetVersion ? readSealedTaskIds(taskSetVersion, out) : /* @__PURE__ */ new Set();
|
|
24716
24981
|
mkdirSync6(out, { recursive: true });
|
|
24717
|
-
writeFileSync8(
|
|
24982
|
+
writeFileSync8(join30(out, "verdict.json"), JSON.stringify(verdict, null, 2));
|
|
24718
24983
|
const feed = redactForFeed(verdict, sealedTaskIds);
|
|
24719
|
-
writeFileSync8(
|
|
24984
|
+
writeFileSync8(join30(out, "feed.json"), JSON.stringify(feed, null, 2));
|
|
24720
24985
|
}
|
|
24721
24986
|
if (mode === "json") {
|
|
24722
24987
|
this.context.stdout.write(renderJson(verdict) + "\n");
|
|
@@ -24752,10 +25017,10 @@ var EvalExamCommand = class extends M8tCommand {
|
|
|
24752
25017
|
};
|
|
24753
25018
|
|
|
24754
25019
|
// src/commands/version.ts
|
|
24755
|
-
import { Command as
|
|
25020
|
+
import { Command as Command46, Option as Option43 } from "clipanion";
|
|
24756
25021
|
var VersionCommand = class extends M8tCommand {
|
|
24757
25022
|
static paths = [["version"], ["--version"], ["-v"]];
|
|
24758
|
-
static usage =
|
|
25023
|
+
static usage = Command46.Usage({
|
|
24759
25024
|
description: "Print the CLI version.",
|
|
24760
25025
|
details: "Prints the m8t CLI version. With --verbose, also prints Node version and platform.",
|
|
24761
25026
|
examples: [
|
|
@@ -24763,8 +25028,8 @@ var VersionCommand = class extends M8tCommand {
|
|
|
24763
25028
|
["Print as JSON", "$0 version --output json"]
|
|
24764
25029
|
]
|
|
24765
25030
|
});
|
|
24766
|
-
output =
|
|
24767
|
-
verbose =
|
|
25031
|
+
output = Option43.String("--output", { description: "pretty | json | auto (default)" });
|
|
25032
|
+
verbose = Option43.Boolean("--verbose", false);
|
|
24768
25033
|
executeCommand() {
|
|
24769
25034
|
const mode = resolveOutputMode(
|
|
24770
25035
|
this.output ?? "auto",
|
|
@@ -24795,18 +25060,18 @@ var VersionCommand = class extends M8tCommand {
|
|
|
24795
25060
|
};
|
|
24796
25061
|
|
|
24797
25062
|
// src/commands/whoami.ts
|
|
24798
|
-
import { Command as
|
|
25063
|
+
import { Command as Command47, Option as Option44 } from "clipanion";
|
|
24799
25064
|
var WhoamiCommand = class extends M8tCommand {
|
|
24800
25065
|
static paths = [["whoami"]];
|
|
24801
|
-
static usage =
|
|
25066
|
+
static usage = Command47.Usage({
|
|
24802
25067
|
description: "Show your identity + the gateway you'll talk to. Probes the backend."
|
|
24803
25068
|
});
|
|
24804
|
-
output =
|
|
24805
|
-
verbose =
|
|
24806
|
-
subscription =
|
|
25069
|
+
output = Option44.String("--output");
|
|
25070
|
+
verbose = Option44.Boolean("--verbose", false);
|
|
25071
|
+
subscription = Option44.String("--subscription", {
|
|
24807
25072
|
description: "Azure subscription ID to discover gateway in (defaults to active az subscription)."
|
|
24808
25073
|
});
|
|
24809
|
-
resourceGroup =
|
|
25074
|
+
resourceGroup = Option44.String("--resource-group", {
|
|
24810
25075
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
24811
25076
|
});
|
|
24812
25077
|
async executeCommand() {
|
|
@@ -24871,7 +25136,7 @@ var WhoamiCommand = class extends M8tCommand {
|
|
|
24871
25136
|
};
|
|
24872
25137
|
|
|
24873
25138
|
// src/commands/status.ts
|
|
24874
|
-
import { Command as
|
|
25139
|
+
import { Command as Command48, Option as Option45 } from "clipanion";
|
|
24875
25140
|
|
|
24876
25141
|
// src/lib/azd.ts
|
|
24877
25142
|
init_errors();
|
|
@@ -24936,10 +25201,10 @@ async function resolveLocalContext() {
|
|
|
24936
25201
|
// src/commands/status.ts
|
|
24937
25202
|
var StatusCommand = class extends M8tCommand {
|
|
24938
25203
|
static paths = [["status"]];
|
|
24939
|
-
static usage =
|
|
25204
|
+
static usage = Command48.Usage({
|
|
24940
25205
|
description: "Show the local m8t context: identity, config.yaml, gateway cache, azd mode."
|
|
24941
25206
|
});
|
|
24942
|
-
output =
|
|
25207
|
+
output = Option45.String("--output");
|
|
24943
25208
|
async executeCommand() {
|
|
24944
25209
|
const mode = resolveOutputMode(
|
|
24945
25210
|
this.output,
|
|
@@ -24977,11 +25242,11 @@ var StatusCommand = class extends M8tCommand {
|
|
|
24977
25242
|
};
|
|
24978
25243
|
|
|
24979
25244
|
// src/commands/doctor.ts
|
|
24980
|
-
import { Command as
|
|
24981
|
-
import { DefaultAzureCredential as
|
|
24982
|
-
import * as
|
|
25245
|
+
import { Command as Command49, Option as Option46 } from "clipanion";
|
|
25246
|
+
import { DefaultAzureCredential as DefaultAzureCredential23 } from "@azure/identity";
|
|
25247
|
+
import * as fs30 from "fs";
|
|
24983
25248
|
import * as os12 from "os";
|
|
24984
|
-
import * as
|
|
25249
|
+
import * as path33 from "path";
|
|
24985
25250
|
|
|
24986
25251
|
// src/lib/doctor-checks.ts
|
|
24987
25252
|
function checkAzSignedIn(az) {
|
|
@@ -25460,40 +25725,40 @@ async function resolveAccountLocation(accountId) {
|
|
|
25460
25725
|
}
|
|
25461
25726
|
}
|
|
25462
25727
|
function probeRepoRoot() {
|
|
25463
|
-
const marker =
|
|
25464
|
-
if (!
|
|
25728
|
+
const marker = path33.join(os12.homedir(), ".m8t", "repo-root");
|
|
25729
|
+
if (!fs30.existsSync(marker)) {
|
|
25465
25730
|
return { markerExists: false, path: null, isDir: false, isGitCheckout: false };
|
|
25466
25731
|
}
|
|
25467
25732
|
let repoPath;
|
|
25468
25733
|
try {
|
|
25469
|
-
repoPath =
|
|
25734
|
+
repoPath = fs30.readFileSync(marker, "utf8").trim();
|
|
25470
25735
|
} catch {
|
|
25471
25736
|
return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
25472
25737
|
}
|
|
25473
25738
|
if (!repoPath) return { markerExists: true, path: null, isDir: false, isGitCheckout: false };
|
|
25474
25739
|
let isDir = false;
|
|
25475
25740
|
try {
|
|
25476
|
-
isDir =
|
|
25741
|
+
isDir = fs30.statSync(repoPath).isDirectory();
|
|
25477
25742
|
} catch {
|
|
25478
25743
|
isDir = false;
|
|
25479
25744
|
}
|
|
25480
|
-
const isGitCheckout = isDir &&
|
|
25745
|
+
const isGitCheckout = isDir && fs30.existsSync(path33.join(repoPath, ".git"));
|
|
25481
25746
|
return { markerExists: true, path: repoPath, isDir, isGitCheckout };
|
|
25482
25747
|
}
|
|
25483
25748
|
function probeLegacyStateDir() {
|
|
25484
25749
|
return {
|
|
25485
|
-
legacyExists:
|
|
25486
|
-
currentExists:
|
|
25750
|
+
legacyExists: fs30.existsSync(path33.join(os12.homedir(), ".m8t-stack")),
|
|
25751
|
+
currentExists: fs30.existsSync(path33.join(os12.homedir(), ".m8t"))
|
|
25487
25752
|
};
|
|
25488
25753
|
}
|
|
25489
25754
|
var DoctorCommand = class extends M8tCommand {
|
|
25490
25755
|
static paths = [["doctor"]];
|
|
25491
|
-
static usage =
|
|
25756
|
+
static usage = Command49.Usage({
|
|
25492
25757
|
description: "Diagnose the local m8t setup: az login, config.yaml, gateway, Foundry data-plane."
|
|
25493
25758
|
});
|
|
25494
|
-
output =
|
|
25495
|
-
agent =
|
|
25496
|
-
resourceGroup =
|
|
25759
|
+
output = Option46.String("--output");
|
|
25760
|
+
agent = Option46.String("--agent");
|
|
25761
|
+
resourceGroup = Option46.String("--resource-group", {
|
|
25497
25762
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
25498
25763
|
});
|
|
25499
25764
|
async executeCommand() {
|
|
@@ -25566,7 +25831,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
25566
25831
|
let kvStatus = 0;
|
|
25567
25832
|
if (kv) {
|
|
25568
25833
|
try {
|
|
25569
|
-
const token = await new
|
|
25834
|
+
const token = await new DefaultAzureCredential23().getToken("https://vault.azure.net/.default");
|
|
25570
25835
|
const res = await fetch(`${kv.uri.replace(/\/$/, "")}/secrets?api-version=7.4&maxresults=1`, {
|
|
25571
25836
|
headers: { Authorization: `Bearer ${token.token}` }
|
|
25572
25837
|
});
|
|
@@ -25586,7 +25851,7 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
25586
25851
|
if (typeof this.agent === "string" && this.agent) {
|
|
25587
25852
|
checking(`delivery grant for ${this.agent}`);
|
|
25588
25853
|
try {
|
|
25589
|
-
const credential2 = new
|
|
25854
|
+
const credential2 = new DefaultAzureCredential23();
|
|
25590
25855
|
const cur = await getAgentVersion({
|
|
25591
25856
|
credential: credential2,
|
|
25592
25857
|
projectEndpoint: foundry.projectEndpoint,
|
|
@@ -25629,11 +25894,11 @@ var DoctorCommand = class extends M8tCommand {
|
|
|
25629
25894
|
};
|
|
25630
25895
|
|
|
25631
25896
|
// src/commands/prereqs.ts
|
|
25632
|
-
import { Command as
|
|
25897
|
+
import { Command as Command50, Option as Option47 } from "clipanion";
|
|
25633
25898
|
init_errors();
|
|
25634
25899
|
|
|
25635
25900
|
// src/lib/prereq-deps.ts
|
|
25636
|
-
import { DefaultAzureCredential as
|
|
25901
|
+
import { DefaultAzureCredential as DefaultAzureCredential24 } from "@azure/identity";
|
|
25637
25902
|
|
|
25638
25903
|
// src/lib/bootstrap-preflight.ts
|
|
25639
25904
|
var ADMIN_DIRECTORY_ROLES = ["Global Administrator", "Application Administrator", "Cloud Application Administrator"];
|
|
@@ -25745,7 +26010,7 @@ function buildPrereqDeps(opts = {}) {
|
|
|
25745
26010
|
probeRedirectUri,
|
|
25746
26011
|
fixFoundryAccess,
|
|
25747
26012
|
fixKeyVaultAccess,
|
|
25748
|
-
credential: () => credentialSingleton2 ??= new
|
|
26013
|
+
credential: () => credentialSingleton2 ??= new DefaultAzureCredential24()
|
|
25749
26014
|
};
|
|
25750
26015
|
}
|
|
25751
26016
|
|
|
@@ -26335,7 +26600,7 @@ function renderVerdict(v) {
|
|
|
26335
26600
|
}
|
|
26336
26601
|
var PrereqsCommand = class extends M8tCommand {
|
|
26337
26602
|
static paths = [["prereqs"]];
|
|
26338
|
-
static usage =
|
|
26603
|
+
static usage = Command50.Usage({
|
|
26339
26604
|
description: "Check (and optionally fix) everything m8t needs \u2014 to install, or for you to use it.",
|
|
26340
26605
|
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.",
|
|
26341
26606
|
examples: [
|
|
@@ -26346,15 +26611,15 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
26346
26611
|
["Set a teammate up (needs role-assignment rights)", "$0 prereqs --fix --for alice@contoso.com"]
|
|
26347
26612
|
]
|
|
26348
26613
|
});
|
|
26349
|
-
fix =
|
|
26350
|
-
for_ =
|
|
26351
|
-
phase =
|
|
26352
|
-
region =
|
|
26353
|
-
model =
|
|
26354
|
-
clientId =
|
|
26355
|
-
subscription =
|
|
26356
|
-
resourceGroup =
|
|
26357
|
-
output =
|
|
26614
|
+
fix = Option47.Boolean("--fix", false, { description: "Repair what is repairable. Without it the command only reports." });
|
|
26615
|
+
for_ = Option47.String("--for", { description: "UPN or object id of another person. Usage phase only." });
|
|
26616
|
+
phase = Option47.String("--phase", { description: "Force 'install' or 'usage' instead of auto-detecting." });
|
|
26617
|
+
region = Option47.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)." });
|
|
26618
|
+
model = Option47.String("--model", { description: `Model whose quota the install needs (default ${INSTALL_REASONING_MODEL}).` });
|
|
26619
|
+
clientId = Option47.String("--client-id", { description: "A ready app registration, in place of directory-admin rights." });
|
|
26620
|
+
subscription = Option47.String("--subscription");
|
|
26621
|
+
resourceGroup = Option47.String("--resource-group");
|
|
26622
|
+
output = Option47.String("--output");
|
|
26358
26623
|
async executeCommand() {
|
|
26359
26624
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
26360
26625
|
const json = mode === "json";
|
|
@@ -26410,18 +26675,18 @@ var PrereqsCommand = class extends M8tCommand {
|
|
|
26410
26675
|
};
|
|
26411
26676
|
|
|
26412
26677
|
// src/commands/switch.ts
|
|
26413
|
-
import { Command as
|
|
26678
|
+
import { Command as Command51, Option as Option48 } from "clipanion";
|
|
26414
26679
|
|
|
26415
26680
|
// src/lib/profiles.ts
|
|
26416
|
-
import * as
|
|
26417
|
-
import * as
|
|
26681
|
+
import * as fs31 from "fs/promises";
|
|
26682
|
+
import * as path34 from "path";
|
|
26418
26683
|
import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
|
|
26419
26684
|
function profilesDir() {
|
|
26420
26685
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
26421
|
-
return
|
|
26686
|
+
return path34.join(home, ".m8t", "profiles");
|
|
26422
26687
|
}
|
|
26423
26688
|
function getProfilePath(name) {
|
|
26424
|
-
return
|
|
26689
|
+
return path34.join(profilesDir(), `${path34.basename(name)}.yaml`);
|
|
26425
26690
|
}
|
|
26426
26691
|
function slugifyTenant(domainOrId) {
|
|
26427
26692
|
const base = domainOrId.split(".")[0].toLowerCase();
|
|
@@ -26430,7 +26695,7 @@ function slugifyTenant(domainOrId) {
|
|
|
26430
26695
|
}
|
|
26431
26696
|
async function listProfiles() {
|
|
26432
26697
|
try {
|
|
26433
|
-
const entries = await
|
|
26698
|
+
const entries = await fs31.readdir(profilesDir());
|
|
26434
26699
|
return entries.filter((e) => e.endsWith(".yaml")).map((e) => e.replace(/\.yaml$/, "")).sort();
|
|
26435
26700
|
} catch (e) {
|
|
26436
26701
|
if (e.code === "ENOENT") return [];
|
|
@@ -26439,7 +26704,7 @@ async function listProfiles() {
|
|
|
26439
26704
|
}
|
|
26440
26705
|
async function readProfile(name) {
|
|
26441
26706
|
try {
|
|
26442
|
-
const raw = await
|
|
26707
|
+
const raw = await fs31.readFile(getProfilePath(name), "utf8");
|
|
26443
26708
|
const parsed = parseYaml12(raw);
|
|
26444
26709
|
if (!parsed || typeof parsed !== "object") return null;
|
|
26445
26710
|
return parsed;
|
|
@@ -26450,14 +26715,14 @@ async function readProfile(name) {
|
|
|
26450
26715
|
}
|
|
26451
26716
|
async function exists(p) {
|
|
26452
26717
|
try {
|
|
26453
|
-
await
|
|
26718
|
+
await fs31.stat(p);
|
|
26454
26719
|
return true;
|
|
26455
26720
|
} catch {
|
|
26456
26721
|
return false;
|
|
26457
26722
|
}
|
|
26458
26723
|
}
|
|
26459
26724
|
async function saveProfile(p) {
|
|
26460
|
-
await
|
|
26725
|
+
await fs31.mkdir(profilesDir(), { recursive: true });
|
|
26461
26726
|
let name = p.name;
|
|
26462
26727
|
let n = 2;
|
|
26463
26728
|
while (await exists(getProfilePath(name))) {
|
|
@@ -26466,8 +26731,8 @@ async function saveProfile(p) {
|
|
|
26466
26731
|
}
|
|
26467
26732
|
const target = getProfilePath(name);
|
|
26468
26733
|
const tmp = `${target}.tmp`;
|
|
26469
|
-
await
|
|
26470
|
-
await
|
|
26734
|
+
await fs31.writeFile(tmp, stringifyYaml4({ ...p, name }, { indent: 2 }), "utf8");
|
|
26735
|
+
await fs31.rename(tmp, target);
|
|
26471
26736
|
return name;
|
|
26472
26737
|
}
|
|
26473
26738
|
|
|
@@ -26550,14 +26815,14 @@ async function profileSwitch(name, asName) {
|
|
|
26550
26815
|
init_errors();
|
|
26551
26816
|
var SwitchCommand = class extends M8tCommand {
|
|
26552
26817
|
static paths = [["switch"]];
|
|
26553
|
-
static usage =
|
|
26818
|
+
static usage = Command51.Usage({
|
|
26554
26819
|
description: "Re-point local config at another deployment: --subscription <id|name> (discovery) or <profile>."
|
|
26555
26820
|
});
|
|
26556
|
-
profile =
|
|
26557
|
-
subscription =
|
|
26558
|
-
list =
|
|
26559
|
-
as =
|
|
26560
|
-
output =
|
|
26821
|
+
profile = Option48.String({ required: false });
|
|
26822
|
+
subscription = Option48.String("--subscription");
|
|
26823
|
+
list = Option48.Boolean("--list", false);
|
|
26824
|
+
as = Option48.String("--as");
|
|
26825
|
+
output = Option48.String("--output");
|
|
26561
26826
|
async executeCommand() {
|
|
26562
26827
|
const mode = resolveOutputMode(
|
|
26563
26828
|
this.output,
|
|
@@ -26614,7 +26879,7 @@ var SwitchCommand = class extends M8tCommand {
|
|
|
26614
26879
|
|
|
26615
26880
|
// src/commands/open.ts
|
|
26616
26881
|
import { spawn as spawn5 } from "child_process";
|
|
26617
|
-
import { Command as
|
|
26882
|
+
import { Command as Command52, Option as Option49 } from "clipanion";
|
|
26618
26883
|
|
|
26619
26884
|
// src/lib/open-targets.ts
|
|
26620
26885
|
init_errors();
|
|
@@ -26660,14 +26925,14 @@ function openUrl(url) {
|
|
|
26660
26925
|
}
|
|
26661
26926
|
var OpenCommand = class extends M8tCommand {
|
|
26662
26927
|
static paths = [["open"]];
|
|
26663
|
-
static usage =
|
|
26928
|
+
static usage = Command52.Usage({
|
|
26664
26929
|
description: "Open the deployed webapp (default), the Foundry portal, or the resource group.",
|
|
26665
26930
|
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)."
|
|
26666
26931
|
});
|
|
26667
|
-
target =
|
|
26668
|
-
print =
|
|
26669
|
-
output =
|
|
26670
|
-
resourceGroup =
|
|
26932
|
+
target = Option49.String({ required: false });
|
|
26933
|
+
print = Option49.Boolean("--print", false);
|
|
26934
|
+
output = Option49.String("--output");
|
|
26935
|
+
resourceGroup = Option49.String("--resource-group", {
|
|
26671
26936
|
description: "m8t resource group to disambiguate the gateway (multi-deployment subscriptions)."
|
|
26672
26937
|
});
|
|
26673
26938
|
async executeCommand() {
|
|
@@ -26711,7 +26976,7 @@ var OpenCommand = class extends M8tCommand {
|
|
|
26711
26976
|
};
|
|
26712
26977
|
|
|
26713
26978
|
// src/commands/dream/run.ts
|
|
26714
|
-
import { Command as
|
|
26979
|
+
import { Command as Command53, Option as Option50 } from "clipanion";
|
|
26715
26980
|
import { AzureCliCredential } from "@azure/identity";
|
|
26716
26981
|
import { TableClient as TableClient7 } from "@azure/data-tables";
|
|
26717
26982
|
import { AIProjectClient as AIProjectClient3 } from "@azure/ai-projects";
|
|
@@ -27756,7 +28021,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
27756
28021
|
// ../../packages/brain/engine/dist/esm/dream/writer.js
|
|
27757
28022
|
var API2 = "https://api.github.com";
|
|
27758
28023
|
var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
|
|
27759
|
-
var isAllowedWritePath = (
|
|
28024
|
+
var isAllowedWritePath = (path47, allowedPrefix) => allowedPrefix.test(path47) && !path47.split("/").includes("..");
|
|
27760
28025
|
var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
27761
28026
|
function parseFrontmatter(content) {
|
|
27762
28027
|
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
@@ -27799,8 +28064,8 @@ function makeGitDataWriter(args) {
|
|
|
27799
28064
|
if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
|
|
27800
28065
|
throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
|
|
27801
28066
|
}
|
|
27802
|
-
async function gh(token, method,
|
|
27803
|
-
const res = await doFetch(`${API2}/repos/${repoPath}${
|
|
28067
|
+
async function gh(token, method, path47, body) {
|
|
28068
|
+
const res = await doFetch(`${API2}/repos/${repoPath}${path47}`, {
|
|
27804
28069
|
method,
|
|
27805
28070
|
headers: {
|
|
27806
28071
|
Authorization: `Bearer ${token}`,
|
|
@@ -27813,8 +28078,8 @@ function makeGitDataWriter(args) {
|
|
|
27813
28078
|
const text = await res.text();
|
|
27814
28079
|
return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
|
|
27815
28080
|
}
|
|
27816
|
-
async function readFileWith(token,
|
|
27817
|
-
const encodedPath =
|
|
28081
|
+
async function readFileWith(token, path47) {
|
|
28082
|
+
const encodedPath = path47.split("/").map(encodeURIComponent).join("/");
|
|
27818
28083
|
const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
|
|
27819
28084
|
if (r.status === 404)
|
|
27820
28085
|
return null;
|
|
@@ -27830,9 +28095,9 @@ function makeGitDataWriter(args) {
|
|
|
27830
28095
|
throw new Error(`fetchTip: HTTP ${String(r.status)}`);
|
|
27831
28096
|
return r.json.object.sha;
|
|
27832
28097
|
},
|
|
27833
|
-
async readFile(
|
|
28098
|
+
async readFile(path47) {
|
|
27834
28099
|
const token = await args.mintToken();
|
|
27835
|
-
return readFileWith(token,
|
|
28100
|
+
return readFileWith(token, path47);
|
|
27836
28101
|
},
|
|
27837
28102
|
async listMemory() {
|
|
27838
28103
|
const token = await args.mintToken();
|
|
@@ -27841,9 +28106,9 @@ function makeGitDataWriter(args) {
|
|
|
27841
28106
|
return [];
|
|
27842
28107
|
const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
|
|
27843
28108
|
const out = [];
|
|
27844
|
-
for (const
|
|
27845
|
-
const content = await readFileWith(token,
|
|
27846
|
-
out.push({ path:
|
|
28109
|
+
for (const path47 of paths) {
|
|
28110
|
+
const content = await readFileWith(token, path47);
|
|
28111
|
+
out.push({ path: path47, frontmatter: content ? parseFrontmatter(content) : {} });
|
|
27847
28112
|
}
|
|
27848
28113
|
return out;
|
|
27849
28114
|
},
|
|
@@ -28282,7 +28547,7 @@ async function loadMemoryContext(brain) {
|
|
|
28282
28547
|
return {
|
|
28283
28548
|
files,
|
|
28284
28549
|
indexEntries,
|
|
28285
|
-
originOf: (
|
|
28550
|
+
originOf: (path47) => originByPath.get(path47) ?? "worker"
|
|
28286
28551
|
};
|
|
28287
28552
|
}
|
|
28288
28553
|
|
|
@@ -28303,14 +28568,14 @@ function checkEvidenceMembership(delta, harvested) {
|
|
|
28303
28568
|
}
|
|
28304
28569
|
var BatchAbort = class extends Error {
|
|
28305
28570
|
path;
|
|
28306
|
-
constructor(
|
|
28571
|
+
constructor(path47, message) {
|
|
28307
28572
|
super(message);
|
|
28308
|
-
this.path =
|
|
28573
|
+
this.path = path47;
|
|
28309
28574
|
this.name = "BatchAbort";
|
|
28310
28575
|
}
|
|
28311
28576
|
};
|
|
28312
|
-
var isIndexFile = (
|
|
28313
|
-
var isMemoryIndex = (
|
|
28577
|
+
var isIndexFile = (path47) => /(^|\/)[^/]*_index\.md$/.test(path47);
|
|
28578
|
+
var isMemoryIndex = (path47) => path47 === "memory/MEMORY.md";
|
|
28314
28579
|
function targetPath(delta) {
|
|
28315
28580
|
switch (delta.verb) {
|
|
28316
28581
|
case "new":
|
|
@@ -28327,29 +28592,29 @@ function targetPath(delta) {
|
|
|
28327
28592
|
}
|
|
28328
28593
|
async function checkOriginTier(delta, lookup) {
|
|
28329
28594
|
const evidence = evidenceOf(delta) ?? [];
|
|
28330
|
-
const
|
|
28331
|
-
if (
|
|
28595
|
+
const path47 = targetPath(delta);
|
|
28596
|
+
if (path47 === null)
|
|
28332
28597
|
return { ok: true };
|
|
28333
|
-
if (isIndexFile(
|
|
28334
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
28598
|
+
if (isIndexFile(path47) && !isMemoryIndex(path47)) {
|
|
28599
|
+
return { ok: false, flagged: { kind: "flagged", path: path47, tension: `refused: ${path47} is an index file (only memory/MEMORY.md is editable)`, evidence } };
|
|
28335
28600
|
}
|
|
28336
|
-
const target = await lookup(
|
|
28337
|
-
const isStructural =
|
|
28601
|
+
const target = await lookup(path47);
|
|
28602
|
+
const isStructural = path47.startsWith("references/");
|
|
28338
28603
|
const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
|
|
28339
28604
|
const editable = origin === "worker" || origin === "dream";
|
|
28340
28605
|
if (editable)
|
|
28341
28606
|
return { ok: true };
|
|
28342
28607
|
const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
|
|
28343
28608
|
if (removesOperatorTarget) {
|
|
28344
|
-
throw new BatchAbort(
|
|
28609
|
+
throw new BatchAbort(path47, `supersede/retract OLD target ${path47} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
|
|
28345
28610
|
}
|
|
28346
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
28611
|
+
return { ok: false, flagged: { kind: "flagged", path: path47, tension: `operator-origin (${origin}); flag-only`, evidence } };
|
|
28347
28612
|
}
|
|
28348
28613
|
var ALLOWED_MEMORY = /^memory\/.+\.md$/;
|
|
28349
28614
|
var ALLOWED_INBOX = /^inbox\//;
|
|
28350
28615
|
var ALLOWED_QUARANTINE = /^quarantine\//;
|
|
28351
28616
|
var ALLOWED_FLAG = /^memory\//;
|
|
28352
|
-
var hasTraversal = (
|
|
28617
|
+
var hasTraversal = (path47) => path47.split("/").includes("..");
|
|
28353
28618
|
function checkPathAllowed(delta) {
|
|
28354
28619
|
const evidence = evidenceOf(delta) ?? [];
|
|
28355
28620
|
const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
|
|
@@ -28456,7 +28721,7 @@ ${inject}
|
|
|
28456
28721
|
---
|
|
28457
28722
|
` + body.slice(m[0].length);
|
|
28458
28723
|
}
|
|
28459
|
-
var indexLine = (
|
|
28724
|
+
var indexLine = (path47, title, date, tags) => `- \`${path47}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
|
|
28460
28725
|
function splitIndex(index) {
|
|
28461
28726
|
const all = index.split("\n");
|
|
28462
28727
|
const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
|
|
@@ -28629,16 +28894,16 @@ function defaultDreamSeams(opts = {}) {
|
|
|
28629
28894
|
const mem = await memory(deps.brain);
|
|
28630
28895
|
const harvested = harvestedIds(input);
|
|
28631
28896
|
const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
|
|
28632
|
-
const lookup = (
|
|
28897
|
+
const lookup = (path47) => Promise.resolve(mem.files.find((f) => f.path === path47) ?? null);
|
|
28633
28898
|
const digest = [...pendingRejected];
|
|
28634
28899
|
let changes = [];
|
|
28635
28900
|
let indexEdit;
|
|
28636
28901
|
try {
|
|
28637
28902
|
for (const raw of deltas) {
|
|
28638
28903
|
const delta = forcePolicyQuarantine(raw);
|
|
28639
|
-
const
|
|
28640
|
-
if (!
|
|
28641
|
-
digest.push(
|
|
28904
|
+
const path47 = checkPathAllowed(delta);
|
|
28905
|
+
if (!path47.ok) {
|
|
28906
|
+
digest.push(path47.rejected);
|
|
28642
28907
|
continue;
|
|
28643
28908
|
}
|
|
28644
28909
|
const ev = checkEvidenceMembership(delta, harvested);
|
|
@@ -28892,21 +29157,28 @@ var ARM4 = "https://management.azure.com";
|
|
|
28892
29157
|
var ARM_SCOPE8 = "https://management.azure.com/.default";
|
|
28893
29158
|
var STORAGE_API2 = "2023-05-01";
|
|
28894
29159
|
var LA_API = "2023-09-01";
|
|
29160
|
+
var tagOf = (a, key2) => a.tags?.[key2] ?? a.properties?.tags?.[key2];
|
|
29161
|
+
function selectLedgerAccount(accounts, storageAccount) {
|
|
29162
|
+
if (storageAccount) return accounts.find((a) => a.name === storageAccount);
|
|
29163
|
+
const managed = accounts.filter((a) => tagOf(a, "managedBy") === "m8t");
|
|
29164
|
+
return managed.find((a) => tagOf(a, "m8t") === "storage") ?? managed.at(0) ?? accounts.find((a) => tagOf(a, "m8t:role") === "ledger") ?? (accounts.length === 1 ? accounts[0] : void 0);
|
|
29165
|
+
}
|
|
28895
29166
|
async function discoverLedgerResources(opts) {
|
|
28896
|
-
const { credential: credential2, subscriptionId, resourceGroup } = opts;
|
|
29167
|
+
const { credential: credential2, subscriptionId, resourceGroup, storageAccount } = opts;
|
|
28897
29168
|
const storage = await authedJsonPagedValues({
|
|
28898
29169
|
credential: credential2,
|
|
28899
29170
|
scope: ARM_SCOPE8,
|
|
28900
29171
|
url: `${ARM4}/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Storage/storageAccounts?api-version=${STORAGE_API2}`
|
|
28901
29172
|
});
|
|
28902
29173
|
const accounts = storage.filter((a) => a.properties?.primaryEndpoints?.table);
|
|
28903
|
-
const ledgerAcct =
|
|
29174
|
+
const ledgerAcct = selectLedgerAccount(accounts, storageAccount);
|
|
28904
29175
|
const ledgerTableEndpoint = ledgerAcct?.properties?.primaryEndpoints?.table?.replace(/\/$/, "");
|
|
28905
29176
|
if (!ledgerTableEndpoint) {
|
|
29177
|
+
const found = accounts.map((a) => a.name).join(", ") || "(none)";
|
|
28906
29178
|
throw new LocalCliError({
|
|
28907
29179
|
code: "DREAM_LEDGER_STORAGE_NOT_FOUND",
|
|
28908
|
-
message: `No AgentLedger storage account found in resource group ${resourceGroup}.`,
|
|
28909
|
-
hint: `Found: ${
|
|
29180
|
+
message: storageAccount ? `Storage account '${storageAccount}' was not found in resource group ${resourceGroup}, or has no table endpoint.` : `No AgentLedger storage account found in resource group ${resourceGroup}.`,
|
|
29181
|
+
hint: `Found: ${found}. Pass --storage-account <name> to select one explicitly.`
|
|
28910
29182
|
});
|
|
28911
29183
|
}
|
|
28912
29184
|
const ws = await authedJsonPagedValues({
|
|
@@ -28948,6 +29220,7 @@ async function resolveDreamContext(opts) {
|
|
|
28948
29220
|
});
|
|
28949
29221
|
const resourceGroup = rgFromScope(project.accountScope);
|
|
28950
29222
|
const { ledgerTableEndpoint, workspaceId } = await discoverLedgerResources({
|
|
29223
|
+
storageAccount: opts.storageAccount,
|
|
28951
29224
|
credential: credential2,
|
|
28952
29225
|
subscriptionId,
|
|
28953
29226
|
resourceGroup
|
|
@@ -28997,25 +29270,28 @@ function redactTranscripts(input) {
|
|
|
28997
29270
|
}
|
|
28998
29271
|
var DreamRunCommand = class extends M8tCommand {
|
|
28999
29272
|
static paths = [["dream", "run"]];
|
|
29000
|
-
static usage =
|
|
29273
|
+
static usage = Command53.Usage({
|
|
29001
29274
|
description: "Dry-run the brain consumption pipeline for one worker (no model call, no writes). Pass --live to actually run it.",
|
|
29002
29275
|
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."
|
|
29003
29276
|
});
|
|
29004
|
-
worker =
|
|
29277
|
+
worker = Option50.String("--worker", { description: "Worker (canonical name) to harvest. Required." });
|
|
29005
29278
|
// Opting IN to the side effects, rather than opting out of them. This command's
|
|
29006
29279
|
// own help has always described a dry run, but the bare invocation used to take
|
|
29007
29280
|
// the live branch — a real model call and a commit to the brain repo — so anyone
|
|
29008
29281
|
// acting on `--help` got the opposite of what they read.
|
|
29009
|
-
live =
|
|
29010
|
-
dryRun =
|
|
29011
|
-
since =
|
|
29012
|
-
reset =
|
|
29013
|
-
showTranscripts =
|
|
29282
|
+
live = Option50.Boolean("--live", false, { description: "Actually run it: model call + commit to the brain repo." });
|
|
29283
|
+
dryRun = Option50.Boolean("--dry-run", false, { description: "Read-only harvest. This is the default; the flag is accepted for compatibility." });
|
|
29284
|
+
since = Option50.String("--since", { description: "ISO-8601 start override (rejected if malformed or future)." });
|
|
29285
|
+
reset = Option50.Boolean("--reset", false, { description: "Ignore the stored cursor; read from the beginning." });
|
|
29286
|
+
showTranscripts = Option50.Boolean("--show-transcripts", false, {
|
|
29014
29287
|
description: "Print transcript bodies (default: metadata only)."
|
|
29015
29288
|
});
|
|
29016
|
-
subscription =
|
|
29017
|
-
endpoint =
|
|
29018
|
-
|
|
29289
|
+
subscription = Option50.String("--subscription");
|
|
29290
|
+
endpoint = Option50.String("--endpoint");
|
|
29291
|
+
storageAccount = Option50.String("--storage-account", {
|
|
29292
|
+
description: "Ledger storage account name (skips tag-based discovery)"
|
|
29293
|
+
});
|
|
29294
|
+
output = Option50.String("--output");
|
|
29019
29295
|
// Resolution seam — overridden by tests; built lazily at runtime otherwise.
|
|
29020
29296
|
deps;
|
|
29021
29297
|
async executeCommand() {
|
|
@@ -29032,7 +29308,8 @@ var DreamRunCommand = class extends M8tCommand {
|
|
|
29032
29308
|
const liveContext = await liveDeps.resolveContext({
|
|
29033
29309
|
worker,
|
|
29034
29310
|
subscription: typeof this.subscription === "string" ? this.subscription : void 0,
|
|
29035
|
-
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
|
|
29311
|
+
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
29312
|
+
storageAccount: typeof this.storageAccount === "string" ? this.storageAccount : void 0
|
|
29036
29313
|
});
|
|
29037
29314
|
const liveCursor = await liveDeps.readCursor(liveContext.worker);
|
|
29038
29315
|
const result2 = await liveDeps.runDream({
|
|
@@ -29079,7 +29356,8 @@ var DreamRunCommand = class extends M8tCommand {
|
|
|
29079
29356
|
const context = await deps.resolveContext({
|
|
29080
29357
|
worker,
|
|
29081
29358
|
subscription: typeof this.subscription === "string" ? this.subscription : void 0,
|
|
29082
|
-
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0
|
|
29359
|
+
endpoint: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
29360
|
+
storageAccount: typeof this.storageAccount === "string" ? this.storageAccount : void 0
|
|
29083
29361
|
});
|
|
29084
29362
|
const now = /* @__PURE__ */ new Date();
|
|
29085
29363
|
const cursor = await deps.readCursor(context.worker);
|
|
@@ -29364,7 +29642,7 @@ function defaultDeps(overrides) {
|
|
|
29364
29642
|
|
|
29365
29643
|
// src/commands/conversations/sweep.ts
|
|
29366
29644
|
import { createHash as createHash4 } from "crypto";
|
|
29367
|
-
import { Command as
|
|
29645
|
+
import { Command as Command54, Option as Option51 } from "clipanion";
|
|
29368
29646
|
import { AzureCliCredential as AzureCliCredential2 } from "@azure/identity";
|
|
29369
29647
|
import { TableClient as TableClient8 } from "@azure/data-tables";
|
|
29370
29648
|
import { AIProjectClient as AIProjectClient4 } from "@azure/ai-projects";
|
|
@@ -29429,13 +29707,28 @@ function defaultDeps2() {
|
|
|
29429
29707
|
const { ledgerTableEndpoint } = await discoverLedgerResources({
|
|
29430
29708
|
credential: credential2,
|
|
29431
29709
|
subscriptionId,
|
|
29432
|
-
resourceGroup: m[1]
|
|
29710
|
+
resourceGroup: m[1],
|
|
29711
|
+
storageAccount: opts.storageAccount
|
|
29433
29712
|
});
|
|
29434
29713
|
return { projectEndpoint: project.endpoint, ledgerTableEndpoint };
|
|
29435
29714
|
},
|
|
29436
29715
|
async fetchRows(ctx, fromIso, toIso) {
|
|
29437
29716
|
const client = new TableClient8(ctx.ledgerTableEndpoint, LEDGER_TABLE_NAME2, credential2);
|
|
29438
|
-
|
|
29717
|
+
try {
|
|
29718
|
+
return await fetchLedgerRows({ workers: [], source: "all", from: fromIso, to: toIso }, client);
|
|
29719
|
+
} catch (e) {
|
|
29720
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
29721
|
+
if (statusOf2(e) === 403 || msg.includes("AuthorizationPermissionMismatch")) {
|
|
29722
|
+
const account = /https:\/\/([^.]+)\./.exec(ctx.ledgerTableEndpoint)?.[1] ?? "the ledger account";
|
|
29723
|
+
throw new LocalCliError({
|
|
29724
|
+
code: "sweep_ledger_forbidden",
|
|
29725
|
+
message: `Not authorized to read the ledger table on '${account}'.`,
|
|
29726
|
+
hint: `Reading table rows needs a data-plane role: grant 'Storage Table Data Reader' on that storage account to the signed-in identity (owning the account is not enough).`,
|
|
29727
|
+
cause: e
|
|
29728
|
+
});
|
|
29729
|
+
}
|
|
29730
|
+
throw e;
|
|
29731
|
+
}
|
|
29439
29732
|
},
|
|
29440
29733
|
async retrieveMetadata(ctx, conversationId) {
|
|
29441
29734
|
try {
|
|
@@ -29475,7 +29768,7 @@ function defaultDeps2() {
|
|
|
29475
29768
|
}
|
|
29476
29769
|
var ConversationsSweepCommand = class extends M8tCommand {
|
|
29477
29770
|
static paths = [["conversations", "sweep"]];
|
|
29478
|
-
static usage =
|
|
29771
|
+
static usage = Command54.Usage({
|
|
29479
29772
|
category: "Conversations",
|
|
29480
29773
|
description: "Delete expired public-visitor conversations (dry run by default)",
|
|
29481
29774
|
details: `
|
|
@@ -29491,21 +29784,24 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
29491
29784
|
["Delete, capped at 50", "m8t conversations sweep --delete --max 50"]
|
|
29492
29785
|
]
|
|
29493
29786
|
});
|
|
29494
|
-
doDelete =
|
|
29787
|
+
doDelete = Option51.Boolean("--delete", false, {
|
|
29495
29788
|
description: "Perform deletions (without this flag the command only reports)"
|
|
29496
29789
|
});
|
|
29497
|
-
principal =
|
|
29790
|
+
principal = Option51.String("--principal", {
|
|
29498
29791
|
description: "Service-principal oid whose end-user keys are known-ephemeral (required with --delete; a dry run without it reports candidates grouped by principal)"
|
|
29499
29792
|
});
|
|
29500
|
-
max =
|
|
29501
|
-
graceDays =
|
|
29793
|
+
max = Option51.String("--max", "200", { description: "Maximum deletions per run" });
|
|
29794
|
+
graceDays = Option51.String("--grace-days", "14", {
|
|
29502
29795
|
description: "Days past the 30-day key life before a conversation is eligible"
|
|
29503
29796
|
});
|
|
29504
|
-
lookbackDays =
|
|
29797
|
+
lookbackDays = Option51.String("--lookback-days", "180", {
|
|
29505
29798
|
description: "How far back to scan ledger activity for candidates"
|
|
29506
29799
|
});
|
|
29507
|
-
subscription =
|
|
29508
|
-
endpoint =
|
|
29800
|
+
subscription = Option51.String("--subscription", { description: "Azure subscription id override" });
|
|
29801
|
+
endpoint = Option51.String("--endpoint", { description: "Foundry project endpoint override" });
|
|
29802
|
+
storageAccount = Option51.String("--storage-account", {
|
|
29803
|
+
description: "Ledger storage account name (skips tag-based discovery)"
|
|
29804
|
+
});
|
|
29509
29805
|
deps = defaultDeps2();
|
|
29510
29806
|
async executeCommand() {
|
|
29511
29807
|
const out = this.context.stdout;
|
|
@@ -29522,7 +29818,11 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
29522
29818
|
const cutoffIso = new Date(now - retentionDays * DAY_MS).toISOString();
|
|
29523
29819
|
const fromIso = new Date(now - lookback * DAY_MS).toISOString();
|
|
29524
29820
|
const toIso = new Date(now).toISOString();
|
|
29525
|
-
const ctx = await this.deps.resolveContext({
|
|
29821
|
+
const ctx = await this.deps.resolveContext({
|
|
29822
|
+
subscription: this.subscription,
|
|
29823
|
+
endpoint: this.endpoint,
|
|
29824
|
+
storageAccount: this.storageAccount
|
|
29825
|
+
});
|
|
29526
29826
|
out.write(`Scanning ledger activity ${fromIso} \u2192 ${toIso}; retention cutoff ${cutoffIso}.
|
|
29527
29827
|
`);
|
|
29528
29828
|
const rows = await this.deps.fetchRows(ctx, fromIso, toIso);
|
|
@@ -29627,7 +29927,7 @@ var ConversationsSweepCommand = class extends M8tCommand {
|
|
|
29627
29927
|
};
|
|
29628
29928
|
|
|
29629
29929
|
// src/commands/foundry/create.ts
|
|
29630
|
-
import { Command as
|
|
29930
|
+
import { Command as Command55, Option as Option52 } from "clipanion";
|
|
29631
29931
|
|
|
29632
29932
|
// src/lib/foundry-create.ts
|
|
29633
29933
|
init_errors();
|
|
@@ -29865,7 +30165,7 @@ async function createFoundryProject(args) {
|
|
|
29865
30165
|
init_errors();
|
|
29866
30166
|
var FoundryCreateCommand = class extends M8tCommand {
|
|
29867
30167
|
static paths = [["foundry", "create"]];
|
|
29868
|
-
static usage =
|
|
30168
|
+
static usage = Command55.Usage({
|
|
29869
30169
|
description: "Create an AI Foundry (AIServices) account + project + model deployment from scratch.",
|
|
29870
30170
|
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).",
|
|
29871
30171
|
examples: [
|
|
@@ -29874,16 +30174,16 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
29874
30174
|
["Higher capacity for a reasoning model", "$0 foundry create --resource-group rg-m8t-stack --location eastus2 --model gpt-5-mini --capacity 250"]
|
|
29875
30175
|
]
|
|
29876
30176
|
});
|
|
29877
|
-
resourceGroup =
|
|
29878
|
-
location =
|
|
29879
|
-
account =
|
|
29880
|
-
project =
|
|
29881
|
-
model =
|
|
29882
|
-
modelVersion =
|
|
29883
|
-
capacity =
|
|
29884
|
-
subscription =
|
|
29885
|
-
skipQuotaCheck =
|
|
29886
|
-
output =
|
|
30177
|
+
resourceGroup = Option52.String("--resource-group");
|
|
30178
|
+
location = Option52.String("--location");
|
|
30179
|
+
account = Option52.String("--account");
|
|
30180
|
+
project = Option52.String("--project", "m8t");
|
|
30181
|
+
model = Option52.String("--model", "gpt-4.1-mini");
|
|
30182
|
+
modelVersion = Option52.String("--model-version", "2025-04-14");
|
|
30183
|
+
capacity = Option52.String("--capacity", "50");
|
|
30184
|
+
subscription = Option52.String("--subscription");
|
|
30185
|
+
skipQuotaCheck = Option52.Boolean("--skip-quota-check", false);
|
|
30186
|
+
output = Option52.String("--output");
|
|
29887
30187
|
async executeCommand() {
|
|
29888
30188
|
const mode = resolveOutputMode(
|
|
29889
30189
|
this.output,
|
|
@@ -29955,22 +30255,22 @@ var FoundryCreateCommand = class extends M8tCommand {
|
|
|
29955
30255
|
};
|
|
29956
30256
|
|
|
29957
30257
|
// src/commands/foundry/await-ready.ts
|
|
29958
|
-
import { Command as
|
|
30258
|
+
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
29959
30259
|
import { AzureCliCredential as AzureCliCredential3 } from "@azure/identity";
|
|
29960
30260
|
init_errors();
|
|
29961
30261
|
var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
29962
30262
|
static paths = [["foundry", "await-ready"]];
|
|
29963
|
-
static usage =
|
|
30263
|
+
static usage = Command56.Usage({
|
|
29964
30264
|
description: "Wait until a freshly-created Foundry project's data plane reliably serves it.",
|
|
29965
30265
|
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.",
|
|
29966
30266
|
examples: [["Wait for a project to be ready", "$0 foundry await-ready --endpoint https://acc.services.ai.azure.com/api/projects/m8t"]]
|
|
29967
30267
|
});
|
|
29968
|
-
endpoint =
|
|
29969
|
-
consecutive =
|
|
29970
|
-
attempts =
|
|
29971
|
-
interval =
|
|
29972
|
-
subscription =
|
|
29973
|
-
output =
|
|
30268
|
+
endpoint = Option53.String("--endpoint");
|
|
30269
|
+
consecutive = Option53.String("--consecutive", "3");
|
|
30270
|
+
attempts = Option53.String("--attempts", "60");
|
|
30271
|
+
interval = Option53.String("--interval", "5");
|
|
30272
|
+
subscription = Option53.String("--subscription");
|
|
30273
|
+
output = Option53.String("--output");
|
|
29974
30274
|
async executeCommand() {
|
|
29975
30275
|
const mode = resolveOutputMode(this.output, this.context.stdout);
|
|
29976
30276
|
const endpoint = typeof this.endpoint === "string" ? this.endpoint : void 0;
|
|
@@ -30004,7 +30304,7 @@ var FoundryAwaitReadyCommand = class extends M8tCommand {
|
|
|
30004
30304
|
};
|
|
30005
30305
|
|
|
30006
30306
|
// src/commands/bootstrap/preflight.ts
|
|
30007
|
-
import { Command as
|
|
30307
|
+
import { Command as Command57, Option as Option54 } from "clipanion";
|
|
30008
30308
|
|
|
30009
30309
|
// ../../packages/telemetry-contract/artifact/tier-map.ts
|
|
30010
30310
|
var EVENT_TIERS = {
|
|
@@ -30030,9 +30330,9 @@ var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t rece
|
|
|
30030
30330
|
|
|
30031
30331
|
// ../../packages/telemetry-contract/src/endpoints.ts
|
|
30032
30332
|
var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
|
|
30033
|
-
function ingestUrl(
|
|
30333
|
+
function ingestUrl(path47) {
|
|
30034
30334
|
const base = INGEST_BASE_URL.replace(/\/+$/, "");
|
|
30035
|
-
return `${base}${
|
|
30335
|
+
return `${base}${path47.startsWith("/") ? path47 : `/${path47}`}`;
|
|
30036
30336
|
}
|
|
30037
30337
|
|
|
30038
30338
|
// src/commands/bootstrap/preflight.ts
|
|
@@ -30056,7 +30356,7 @@ function preflightRenderable(results) {
|
|
|
30056
30356
|
}
|
|
30057
30357
|
var BootstrapPreflightCommand = class extends M8tCommand {
|
|
30058
30358
|
static paths = [["bootstrap", "preflight"]];
|
|
30059
|
-
static usage =
|
|
30359
|
+
static usage = Command57.Usage({
|
|
30060
30360
|
description: "Loudly verify you can install m8t (Owner/UAA + directory admin) and hard-stop if not.",
|
|
30061
30361
|
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.",
|
|
30062
30362
|
examples: [
|
|
@@ -30065,9 +30365,9 @@ var BootstrapPreflightCommand = class extends M8tCommand {
|
|
|
30065
30365
|
["BYO app registration (directory guests)", "$0 bootstrap preflight --client-id <appId>"]
|
|
30066
30366
|
]
|
|
30067
30367
|
});
|
|
30068
|
-
clientId =
|
|
30069
|
-
subscription =
|
|
30070
|
-
location =
|
|
30368
|
+
clientId = Option54.String("--client-id");
|
|
30369
|
+
subscription = Option54.String("--subscription");
|
|
30370
|
+
location = Option54.String("--location", {
|
|
30071
30371
|
description: "Target region. Enables the model-quota check \u2014 without it, quota is not verified."
|
|
30072
30372
|
});
|
|
30073
30373
|
async executeCommand() {
|
|
@@ -30164,10 +30464,10 @@ ${colors.error(" " + why)}
|
|
|
30164
30464
|
}
|
|
30165
30465
|
|
|
30166
30466
|
// src/commands/bootstrap/launch.ts
|
|
30167
|
-
import * as
|
|
30467
|
+
import * as fs34 from "fs";
|
|
30168
30468
|
import * as os15 from "os";
|
|
30169
|
-
import * as
|
|
30170
|
-
import { Command as
|
|
30469
|
+
import * as path37 from "path";
|
|
30470
|
+
import { Command as Command58, Option as Option55 } from "clipanion";
|
|
30171
30471
|
init_errors();
|
|
30172
30472
|
|
|
30173
30473
|
// src/lib/bootstrap-mi.ts
|
|
@@ -30323,9 +30623,9 @@ async function kickInstaller(s) {
|
|
|
30323
30623
|
|
|
30324
30624
|
// src/lib/bootstrap-status.ts
|
|
30325
30625
|
import { createHash as createHash6, randomUUID as randomUUID2 } from "crypto";
|
|
30326
|
-
import * as
|
|
30626
|
+
import * as fs32 from "fs/promises";
|
|
30327
30627
|
import * as os13 from "os";
|
|
30328
|
-
import * as
|
|
30628
|
+
import * as path35 from "path";
|
|
30329
30629
|
init_errors();
|
|
30330
30630
|
var STATUS_CONTAINER = "status";
|
|
30331
30631
|
var STATUS_BLOB = "status.json";
|
|
@@ -30364,7 +30664,7 @@ async function readStatusBlob(opts) {
|
|
|
30364
30664
|
cause: e
|
|
30365
30665
|
});
|
|
30366
30666
|
}
|
|
30367
|
-
const tmpFile =
|
|
30667
|
+
const tmpFile = path35.join(os13.tmpdir(), `m8t-status-${randomUUID2()}.json`);
|
|
30368
30668
|
try {
|
|
30369
30669
|
await runAz([
|
|
30370
30670
|
"storage",
|
|
@@ -30383,7 +30683,7 @@ async function readStatusBlob(opts) {
|
|
|
30383
30683
|
"--no-progress",
|
|
30384
30684
|
"--only-show-errors"
|
|
30385
30685
|
]);
|
|
30386
|
-
const raw = await
|
|
30686
|
+
const raw = await fs32.readFile(tmpFile, "utf8");
|
|
30387
30687
|
return JSON.parse(raw.trim());
|
|
30388
30688
|
} catch (e) {
|
|
30389
30689
|
throw new LocalCliError({
|
|
@@ -30393,23 +30693,23 @@ async function readStatusBlob(opts) {
|
|
|
30393
30693
|
cause: e
|
|
30394
30694
|
});
|
|
30395
30695
|
} finally {
|
|
30396
|
-
await
|
|
30696
|
+
await fs32.rm(tmpFile, { force: true });
|
|
30397
30697
|
}
|
|
30398
30698
|
}
|
|
30399
30699
|
|
|
30400
30700
|
// src/lib/bootstrap-state.ts
|
|
30401
|
-
import * as
|
|
30701
|
+
import * as fs33 from "fs/promises";
|
|
30402
30702
|
import * as os14 from "os";
|
|
30403
|
-
import * as
|
|
30703
|
+
import * as path36 from "path";
|
|
30404
30704
|
function stateDir(home) {
|
|
30405
|
-
return
|
|
30705
|
+
return path36.join(home, ".m8t");
|
|
30406
30706
|
}
|
|
30407
30707
|
function statePath(home) {
|
|
30408
|
-
return
|
|
30708
|
+
return path36.join(stateDir(home), "bootstrap.json");
|
|
30409
30709
|
}
|
|
30410
30710
|
async function readBootstrapState(home = os14.homedir()) {
|
|
30411
30711
|
try {
|
|
30412
|
-
const raw = await
|
|
30712
|
+
const raw = await fs33.readFile(statePath(home), "utf8");
|
|
30413
30713
|
return JSON.parse(raw);
|
|
30414
30714
|
} catch (e) {
|
|
30415
30715
|
if (e.code === "ENOENT") return null;
|
|
@@ -30417,10 +30717,10 @@ async function readBootstrapState(home = os14.homedir()) {
|
|
|
30417
30717
|
}
|
|
30418
30718
|
}
|
|
30419
30719
|
async function writeBootstrapState(state, home = os14.homedir()) {
|
|
30420
|
-
await
|
|
30720
|
+
await fs33.mkdir(stateDir(home), { recursive: true });
|
|
30421
30721
|
const tmp = `${statePath(home)}.tmp`;
|
|
30422
|
-
await
|
|
30423
|
-
await
|
|
30722
|
+
await fs33.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
30723
|
+
await fs33.rename(tmp, statePath(home));
|
|
30424
30724
|
}
|
|
30425
30725
|
|
|
30426
30726
|
// src/lib/bootstrap-occupancy.ts
|
|
@@ -30537,7 +30837,7 @@ var ACI_NAME = "m8t-installer";
|
|
|
30537
30837
|
var MI_NAME = "m8t-installer-mi";
|
|
30538
30838
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
30539
30839
|
static paths = [["bootstrap", "launch"]];
|
|
30540
|
-
static usage =
|
|
30840
|
+
static usage = Command58.Usage({
|
|
30541
30841
|
description: "Create + authorize the installer managed identity, then kick the cloud installer.",
|
|
30542
30842
|
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.",
|
|
30543
30843
|
examples: [
|
|
@@ -30548,26 +30848,26 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30548
30848
|
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
30549
30849
|
]
|
|
30550
30850
|
});
|
|
30551
|
-
location =
|
|
30552
|
-
resourceGroup =
|
|
30553
|
-
clientId =
|
|
30554
|
-
subscription =
|
|
30555
|
-
installerTag =
|
|
30851
|
+
location = Option55.String("--location");
|
|
30852
|
+
resourceGroup = Option55.String("--resource-group");
|
|
30853
|
+
clientId = Option55.String("--client-id");
|
|
30854
|
+
subscription = Option55.String("--subscription");
|
|
30855
|
+
installerTag = Option55.String("--installer-tag");
|
|
30556
30856
|
// Full image ref override (registry + repo + tag) — an escape hatch when the
|
|
30557
30857
|
// default org/tag is wrong for the current CLI (e.g. a stale published build).
|
|
30558
30858
|
// Wins over --installer-tag / the pinned default.
|
|
30559
|
-
installerImage =
|
|
30560
|
-
gatewayImageRef =
|
|
30561
|
-
githubAppCreds =
|
|
30562
|
-
contactEmail =
|
|
30563
|
-
company =
|
|
30859
|
+
installerImage = Option55.String("--installer-image");
|
|
30860
|
+
gatewayImageRef = Option55.String("--gateway-image-ref");
|
|
30861
|
+
githubAppCreds = Option55.String("--github-app-creds");
|
|
30862
|
+
contactEmail = Option55.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
30863
|
+
company = Option55.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
30564
30864
|
// Value-carrying on purpose: a bare --force would be cargo-culted into
|
|
30565
30865
|
// runbooks and harness prompts and erode the protection, whereas a faithful
|
|
30566
30866
|
// paste can never accidentally carry the victim group's name. It AUTHORIZES
|
|
30567
30867
|
// the target; --resource-group is what CHOOSES it.
|
|
30568
|
-
reinstallInto =
|
|
30569
|
-
org =
|
|
30570
|
-
noBrains =
|
|
30868
|
+
reinstallInto = Option55.String("--reinstall-into", { description: "Consent to installing into --resource-group even though it already holds resources. Must match the target group's name." });
|
|
30869
|
+
org = Option55.String("--org", { description: "Assert the GitHub App on disk is installed on this org; refuses on a mismatch." });
|
|
30870
|
+
noBrains = Option55.Boolean("--no-brains", false, { description: "Install without brain-backed workers. For test and CI rigs; the supported install creates brains." });
|
|
30571
30871
|
async executeCommand() {
|
|
30572
30872
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
30573
30873
|
if (!location) {
|
|
@@ -30606,13 +30906,13 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30606
30906
|
`);
|
|
30607
30907
|
return 0;
|
|
30608
30908
|
}
|
|
30609
|
-
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds :
|
|
30909
|
+
const credsPath = typeof this.githubAppCreds === "string" ? this.githubAppCreds : path37.join(os15.homedir(), ".m8t", "github-app.json");
|
|
30610
30910
|
const skipBrains = this.noBrains === true;
|
|
30611
30911
|
const requestedOrg = typeof this.org === "string" ? this.org : void 0;
|
|
30612
30912
|
let githubApp;
|
|
30613
30913
|
if (skipBrains) {
|
|
30614
30914
|
out("installing without brain-backed workers; no GitHub App credentials will be used");
|
|
30615
|
-
} else if (!
|
|
30915
|
+
} else if (!fs34.existsSync(credsPath)) {
|
|
30616
30916
|
this.context.stderr.write(buildMissingCredsRefusal(credsPath));
|
|
30617
30917
|
throw new LocalCliError({
|
|
30618
30918
|
code: "GITHUB_APP_CREDS_MISSING",
|
|
@@ -30622,7 +30922,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30622
30922
|
} else {
|
|
30623
30923
|
let parsed;
|
|
30624
30924
|
try {
|
|
30625
|
-
parsed = JSON.parse(
|
|
30925
|
+
parsed = JSON.parse(fs34.readFileSync(credsPath, "utf8"));
|
|
30626
30926
|
} catch {
|
|
30627
30927
|
throw new LocalCliError({
|
|
30628
30928
|
code: "GITHUB_APP_CREDS_INVALID",
|
|
@@ -30640,7 +30940,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30640
30940
|
}
|
|
30641
30941
|
let pem;
|
|
30642
30942
|
try {
|
|
30643
|
-
pem =
|
|
30943
|
+
pem = fs34.readFileSync(parsed.pemPath, "utf8");
|
|
30644
30944
|
} catch {
|
|
30645
30945
|
throw new LocalCliError({
|
|
30646
30946
|
code: "GITHUB_APP_PEM_MISSING",
|
|
@@ -30728,7 +31028,7 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
30728
31028
|
};
|
|
30729
31029
|
|
|
30730
31030
|
// src/commands/bootstrap/status.ts
|
|
30731
|
-
import { Command as
|
|
31031
|
+
import { Command as Command60, Option as Option57 } from "clipanion";
|
|
30732
31032
|
init_errors();
|
|
30733
31033
|
|
|
30734
31034
|
// src/lib/bootstrap-aci-state.ts
|
|
@@ -30754,15 +31054,15 @@ async function getAciState(opts) {
|
|
|
30754
31054
|
}
|
|
30755
31055
|
|
|
30756
31056
|
// src/lib/bootstrap-finalize.ts
|
|
30757
|
-
import * as
|
|
31057
|
+
import * as fs38 from "fs/promises";
|
|
30758
31058
|
import * as os19 from "os";
|
|
30759
|
-
import * as
|
|
31059
|
+
import * as path43 from "path";
|
|
30760
31060
|
|
|
30761
31061
|
// src/lib/company-profile-seed.ts
|
|
30762
31062
|
import { spawn as spawn6 } from "child_process";
|
|
30763
|
-
import { closeSync, openSync, readFileSync as
|
|
31063
|
+
import { closeSync, openSync, readFileSync as readFileSync24 } from "fs";
|
|
30764
31064
|
import * as os16 from "os";
|
|
30765
|
-
import * as
|
|
31065
|
+
import * as path38 from "path";
|
|
30766
31066
|
init_errors();
|
|
30767
31067
|
|
|
30768
31068
|
// src/lib/onboarding-profile.ts
|
|
@@ -31487,9 +31787,9 @@ function composeFounderIdentityNote(id) {
|
|
|
31487
31787
|
|
|
31488
31788
|
// src/lib/company-profile-seed.ts
|
|
31489
31789
|
function readGithubAppCreds(credsPath) {
|
|
31490
|
-
const p = credsPath ??
|
|
31790
|
+
const p = credsPath ?? path38.join(os16.homedir(), ".m8t", "github-app.json");
|
|
31491
31791
|
try {
|
|
31492
|
-
return JSON.parse(
|
|
31792
|
+
return JSON.parse(readFileSync24(p, "utf8"));
|
|
31493
31793
|
} catch {
|
|
31494
31794
|
return null;
|
|
31495
31795
|
}
|
|
@@ -31558,7 +31858,7 @@ function upsertMemoryIndexOnce(existing, line2, targetPath2) {
|
|
|
31558
31858
|
async function applyProfileToBrain(args) {
|
|
31559
31859
|
const token = await mintInstallationTokenFromPem({
|
|
31560
31860
|
appId: args.appCreds.appId,
|
|
31561
|
-
privateKeyPem:
|
|
31861
|
+
privateKeyPem: readFileSync24(args.appCreds.pemPath, "utf8"),
|
|
31562
31862
|
installationId: args.appCreds.installationId,
|
|
31563
31863
|
fetchImpl: args.fetchImpl
|
|
31564
31864
|
});
|
|
@@ -31648,7 +31948,7 @@ async function applyProfileToBrains(args) {
|
|
|
31648
31948
|
}
|
|
31649
31949
|
}
|
|
31650
31950
|
function spawnDetachedSeedWatch() {
|
|
31651
|
-
const logPath =
|
|
31951
|
+
const logPath = path38.join(os16.homedir(), ".m8t", "seed-profile.log");
|
|
31652
31952
|
const fd = openSync(logPath, "a");
|
|
31653
31953
|
try {
|
|
31654
31954
|
const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
|
|
@@ -31726,16 +32026,16 @@ function renderInstallSummary(args) {
|
|
|
31726
32026
|
|
|
31727
32027
|
// src/lib/companion-install.ts
|
|
31728
32028
|
import { constants as constants2 } from "fs";
|
|
31729
|
-
import * as
|
|
31730
|
-
import * as
|
|
32029
|
+
import * as fs36 from "fs/promises";
|
|
32030
|
+
import * as path40 from "path";
|
|
31731
32031
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
31732
32032
|
import { execFile, spawn as spawn7 } from "child_process";
|
|
31733
32033
|
|
|
31734
32034
|
// src/lib/companion-artifact.ts
|
|
31735
32035
|
import { createHash as createHash7 } from "crypto";
|
|
31736
32036
|
import { constants } from "fs";
|
|
31737
|
-
import * as
|
|
31738
|
-
import * as
|
|
32037
|
+
import * as fs35 from "fs/promises";
|
|
32038
|
+
import * as path39 from "path";
|
|
31739
32039
|
var SHA256 = /^[a-f0-9]{64}$/u;
|
|
31740
32040
|
var VERSION2 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u;
|
|
31741
32041
|
function exactKeys(value, expected) {
|
|
@@ -31743,23 +32043,23 @@ function exactKeys(value, expected) {
|
|
|
31743
32043
|
return actual.length === expected.length && actual.every((key2, index) => key2 === [...expected].sort()[index]);
|
|
31744
32044
|
}
|
|
31745
32045
|
function normalizedRelative(value) {
|
|
31746
|
-
if (value.length === 0 || value.includes("\\") || value.includes("\0") ||
|
|
32046
|
+
if (value.length === 0 || value.includes("\\") || value.includes("\0") || path39.posix.isAbsolute(value)) {
|
|
31747
32047
|
throw new Error("Artifact path must be a normalized relative POSIX path");
|
|
31748
32048
|
}
|
|
31749
|
-
const normalized =
|
|
32049
|
+
const normalized = path39.posix.normalize(value);
|
|
31750
32050
|
if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../")) {
|
|
31751
32051
|
throw new Error("Artifact path escapes the payload root");
|
|
31752
32052
|
}
|
|
31753
32053
|
return value;
|
|
31754
32054
|
}
|
|
31755
32055
|
function validateLink(entryPath, target) {
|
|
31756
|
-
if (target.length === 0 || target.includes("\\") || target.includes("\0") ||
|
|
32056
|
+
if (target.length === 0 || target.includes("\\") || target.includes("\0") || path39.posix.isAbsolute(target)) {
|
|
31757
32057
|
throw new Error("Artifact symlink target must be relative");
|
|
31758
32058
|
}
|
|
31759
|
-
const resolved =
|
|
31760
|
-
|
|
32059
|
+
const resolved = path39.posix.normalize(
|
|
32060
|
+
path39.posix.join(path39.posix.dirname(entryPath), target)
|
|
31761
32061
|
);
|
|
31762
|
-
if (resolved === ".." || resolved.startsWith("../") ||
|
|
32062
|
+
if (resolved === ".." || resolved.startsWith("../") || path39.posix.isAbsolute(resolved)) {
|
|
31763
32063
|
throw new Error("Artifact symlink target escapes the payload root");
|
|
31764
32064
|
}
|
|
31765
32065
|
return target;
|
|
@@ -31840,17 +32140,17 @@ function parseArtifactManifest(value) {
|
|
|
31840
32140
|
};
|
|
31841
32141
|
}
|
|
31842
32142
|
async function sha256File2(filePath) {
|
|
31843
|
-
return createHash7("sha256").update(await
|
|
32143
|
+
return createHash7("sha256").update(await fs35.readFile(filePath)).digest("hex");
|
|
31844
32144
|
}
|
|
31845
32145
|
async function walk2(root, relative4 = "") {
|
|
31846
|
-
const directory =
|
|
31847
|
-
const children = await
|
|
32146
|
+
const directory = path39.join(root, ...relative4.split("/").filter(Boolean));
|
|
32147
|
+
const children = await fs35.readdir(directory, { withFileTypes: true });
|
|
31848
32148
|
const entries = [];
|
|
31849
32149
|
for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
31850
32150
|
const childRelative = relative4 ? `${relative4}/${child.name}` : child.name;
|
|
31851
32151
|
normalizedRelative(childRelative);
|
|
31852
|
-
const childPath =
|
|
31853
|
-
const stat5 = await
|
|
32152
|
+
const childPath = path39.join(directory, child.name);
|
|
32153
|
+
const stat5 = await fs35.lstat(childPath);
|
|
31854
32154
|
if (stat5.isDirectory()) {
|
|
31855
32155
|
entries.push(...await walk2(root, childRelative));
|
|
31856
32156
|
} else if (stat5.isFile()) {
|
|
@@ -31862,7 +32162,7 @@ async function walk2(root, relative4 = "") {
|
|
|
31862
32162
|
sha256: await sha256File2(childPath)
|
|
31863
32163
|
});
|
|
31864
32164
|
} else if (stat5.isSymbolicLink()) {
|
|
31865
|
-
const target = await
|
|
32165
|
+
const target = await fs35.readlink(childPath);
|
|
31866
32166
|
entries.push({
|
|
31867
32167
|
type: "symlink",
|
|
31868
32168
|
path: childRelative,
|
|
@@ -31875,19 +32175,19 @@ async function walk2(root, relative4 = "") {
|
|
|
31875
32175
|
return entries;
|
|
31876
32176
|
}
|
|
31877
32177
|
async function ensureRealDirectory(root) {
|
|
31878
|
-
const stat5 = await
|
|
32178
|
+
const stat5 = await fs35.lstat(root);
|
|
31879
32179
|
if (stat5.isSymbolicLink()) throw new Error("Artifact root is a symbolic link");
|
|
31880
32180
|
if (!stat5.isDirectory()) throw new Error("Artifact root is not a directory");
|
|
31881
32181
|
}
|
|
31882
32182
|
async function validateResolvedLinks(root, entries) {
|
|
31883
|
-
const realRoot = await
|
|
32183
|
+
const realRoot = await fs35.realpath(root);
|
|
31884
32184
|
for (const entry of entries) {
|
|
31885
32185
|
if (entry.type !== "symlink") continue;
|
|
31886
32186
|
try {
|
|
31887
|
-
const linkPath =
|
|
31888
|
-
const resolved = await
|
|
31889
|
-
const relative4 =
|
|
31890
|
-
if (relative4 === ".." || relative4.startsWith(`..${
|
|
32187
|
+
const linkPath = path39.join(root, ...entry.path.split("/"));
|
|
32188
|
+
const resolved = await fs35.realpath(linkPath);
|
|
32189
|
+
const relative4 = path39.relative(realRoot, resolved);
|
|
32190
|
+
if (relative4 === ".." || relative4.startsWith(`..${path39.sep}`) || path39.isAbsolute(relative4)) {
|
|
31891
32191
|
throw new Error("Artifact symlink chain escapes the payload root");
|
|
31892
32192
|
}
|
|
31893
32193
|
} catch (error) {
|
|
@@ -31917,13 +32217,13 @@ async function buildArtifactManifest(payloadRoot, input) {
|
|
|
31917
32217
|
return manifest;
|
|
31918
32218
|
}
|
|
31919
32219
|
async function readArtifactManifest(manifestPath) {
|
|
31920
|
-
const before = await
|
|
32220
|
+
const before = await fs35.lstat(manifestPath);
|
|
31921
32221
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
31922
32222
|
throw new Error("Artifact manifest is a symbolic link or non-file");
|
|
31923
32223
|
}
|
|
31924
32224
|
let handle;
|
|
31925
32225
|
try {
|
|
31926
|
-
handle = await
|
|
32226
|
+
handle = await fs35.open(
|
|
31927
32227
|
manifestPath,
|
|
31928
32228
|
constants.O_RDONLY | constants.O_NOFOLLOW
|
|
31929
32229
|
);
|
|
@@ -31940,9 +32240,9 @@ async function readArtifactManifest(manifestPath) {
|
|
|
31940
32240
|
}
|
|
31941
32241
|
}
|
|
31942
32242
|
function ensureContainedEntry(root, entryPath) {
|
|
31943
|
-
const absolute =
|
|
31944
|
-
const relative4 =
|
|
31945
|
-
if (relative4.startsWith("..") ||
|
|
32243
|
+
const absolute = path39.join(root, ...entryPath.split("/"));
|
|
32244
|
+
const relative4 = path39.relative(root, absolute);
|
|
32245
|
+
if (relative4.startsWith("..") || path39.isAbsolute(relative4)) {
|
|
31946
32246
|
throw new Error("Artifact entry escapes payload root");
|
|
31947
32247
|
}
|
|
31948
32248
|
return absolute;
|
|
@@ -31981,27 +32281,27 @@ async function verifyArtifactPayload(payloadRoot, manifest) {
|
|
|
31981
32281
|
async function copyArtifactPayload(sourceRoot, targetRoot, manifest) {
|
|
31982
32282
|
await verifyArtifactSource(sourceRoot, manifest);
|
|
31983
32283
|
try {
|
|
31984
|
-
const targetStat = await
|
|
32284
|
+
const targetStat = await fs35.lstat(targetRoot);
|
|
31985
32285
|
if (targetStat.isSymbolicLink()) {
|
|
31986
32286
|
throw new Error("Install target is a symbolic link");
|
|
31987
32287
|
}
|
|
31988
32288
|
if (!targetStat.isDirectory()) throw new Error("Install target is not a directory");
|
|
31989
|
-
if ((await
|
|
32289
|
+
if ((await fs35.readdir(targetRoot)).length > 0) {
|
|
31990
32290
|
throw new Error("Install staging target is not empty");
|
|
31991
32291
|
}
|
|
31992
32292
|
} catch (error) {
|
|
31993
32293
|
if (error.code !== "ENOENT") throw error;
|
|
31994
|
-
await
|
|
32294
|
+
await fs35.mkdir(targetRoot, { recursive: false, mode: 448 });
|
|
31995
32295
|
}
|
|
31996
32296
|
for (const entry of manifest.entries) {
|
|
31997
32297
|
const source = ensureContainedEntry(sourceRoot, entry.path);
|
|
31998
32298
|
const target = ensureContainedEntry(targetRoot, entry.path);
|
|
31999
|
-
await
|
|
32299
|
+
await fs35.mkdir(path39.dirname(target), { recursive: true, mode: 448 });
|
|
32000
32300
|
if (entry.type === "file") {
|
|
32001
|
-
await
|
|
32002
|
-
await
|
|
32301
|
+
await fs35.copyFile(source, target);
|
|
32302
|
+
await fs35.chmod(target, entry.mode);
|
|
32003
32303
|
} else {
|
|
32004
|
-
await
|
|
32304
|
+
await fs35.symlink(entry.target, target);
|
|
32005
32305
|
}
|
|
32006
32306
|
}
|
|
32007
32307
|
await verifyArtifactPayload(targetRoot, manifest);
|
|
@@ -32040,17 +32340,17 @@ function execFileOutput(file, args) {
|
|
|
32040
32340
|
}
|
|
32041
32341
|
async function setCompanionStartAtLogin(input) {
|
|
32042
32342
|
if (input.platform === "darwin") {
|
|
32043
|
-
const launchAgents =
|
|
32044
|
-
const registration =
|
|
32343
|
+
const launchAgents = path40.join(input.homeDirectory, "Library", "LaunchAgents");
|
|
32344
|
+
const registration = path40.join(
|
|
32045
32345
|
launchAgents,
|
|
32046
32346
|
"com.m8t.companion.plist"
|
|
32047
32347
|
);
|
|
32048
32348
|
await assertNotSymlink(registration, "Start-at-login registration");
|
|
32049
32349
|
if (!input.enabled) {
|
|
32050
|
-
await
|
|
32350
|
+
await fs36.rm(registration, { force: true });
|
|
32051
32351
|
return;
|
|
32052
32352
|
}
|
|
32053
|
-
await
|
|
32353
|
+
await fs36.mkdir(launchAgents, { recursive: true, mode: 448 });
|
|
32054
32354
|
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";
|
|
32055
32355
|
await atomicWriteText(registration, plist, 384);
|
|
32056
32356
|
return;
|
|
@@ -32102,7 +32402,7 @@ async function setCompanionStartAtLogin(input) {
|
|
|
32102
32402
|
}
|
|
32103
32403
|
async function getCompanionStartAtLogin(input) {
|
|
32104
32404
|
if (input.platform === "darwin") {
|
|
32105
|
-
const registration =
|
|
32405
|
+
const registration = path40.join(
|
|
32106
32406
|
input.homeDirectory,
|
|
32107
32407
|
"Library",
|
|
32108
32408
|
"LaunchAgents",
|
|
@@ -32164,26 +32464,26 @@ function assertSupported(options) {
|
|
|
32164
32464
|
}
|
|
32165
32465
|
function companionInstallPaths(options) {
|
|
32166
32466
|
assertSupported(options);
|
|
32167
|
-
const companionState =
|
|
32467
|
+
const companionState = path40.join(
|
|
32168
32468
|
options.homeDirectory,
|
|
32169
32469
|
".m8t",
|
|
32170
32470
|
"companion"
|
|
32171
32471
|
);
|
|
32172
|
-
const targetRoot = options.platform === "darwin" ?
|
|
32472
|
+
const targetRoot = options.platform === "darwin" ? path40.join(
|
|
32173
32473
|
options.homeDirectory,
|
|
32174
32474
|
"Applications",
|
|
32175
32475
|
"m8t Companion.app"
|
|
32176
|
-
) :
|
|
32177
|
-
options.localAppData ??
|
|
32476
|
+
) : path40.join(
|
|
32477
|
+
options.localAppData ?? path40.join(options.homeDirectory, "AppData", "Local"),
|
|
32178
32478
|
"m8t",
|
|
32179
32479
|
"companion",
|
|
32180
32480
|
"app"
|
|
32181
32481
|
);
|
|
32182
32482
|
return {
|
|
32183
32483
|
targetRoot,
|
|
32184
|
-
installManifest:
|
|
32185
|
-
runtimeBinding:
|
|
32186
|
-
preferences:
|
|
32484
|
+
installManifest: path40.join(companionState, "install-v1.json"),
|
|
32485
|
+
runtimeBinding: path40.join(companionState, "runtime-v1.json"),
|
|
32486
|
+
preferences: path40.join(companionState, "preferences-v1.json")
|
|
32187
32487
|
};
|
|
32188
32488
|
}
|
|
32189
32489
|
function exactKeys2(value, keys) {
|
|
@@ -32194,7 +32494,7 @@ function parseInstallManifest(value) {
|
|
|
32194
32494
|
throw new Error("Install manifest schema is invalid");
|
|
32195
32495
|
}
|
|
32196
32496
|
const record = value;
|
|
32197
|
-
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" || !
|
|
32497
|
+
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" || !path40.isAbsolute(record.ownedTargetRoot)) {
|
|
32198
32498
|
throw new Error("Install manifest schema is invalid");
|
|
32199
32499
|
}
|
|
32200
32500
|
return record;
|
|
@@ -32204,7 +32504,7 @@ function parseRuntimeBinding(value, platform) {
|
|
|
32204
32504
|
throw new Error("Runtime binding schema is invalid");
|
|
32205
32505
|
}
|
|
32206
32506
|
const record = value;
|
|
32207
|
-
const paths = platform === "win32" ?
|
|
32507
|
+
const paths = platform === "win32" ? path40.win32 : path40.posix;
|
|
32208
32508
|
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") {
|
|
32209
32509
|
throw new Error("Runtime binding schema is invalid");
|
|
32210
32510
|
}
|
|
@@ -32229,7 +32529,7 @@ function validateGatewayOrigin(value) {
|
|
|
32229
32529
|
}
|
|
32230
32530
|
async function assertNotSymlink(filePath, kind) {
|
|
32231
32531
|
try {
|
|
32232
|
-
if ((await
|
|
32532
|
+
if ((await fs36.lstat(filePath)).isSymbolicLink()) {
|
|
32233
32533
|
throw new Error(`${kind} is a symbolic link`);
|
|
32234
32534
|
}
|
|
32235
32535
|
} catch (error) {
|
|
@@ -32238,20 +32538,20 @@ async function assertNotSymlink(filePath, kind) {
|
|
|
32238
32538
|
}
|
|
32239
32539
|
}
|
|
32240
32540
|
async function assertOwnedDirectoryChain(anchor, targetDirectory) {
|
|
32241
|
-
const relative4 =
|
|
32242
|
-
if (relative4 === ".." || relative4.startsWith(`..${
|
|
32541
|
+
const relative4 = path40.relative(anchor, targetDirectory);
|
|
32542
|
+
if (relative4 === ".." || relative4.startsWith(`..${path40.sep}`) || path40.isAbsolute(relative4)) {
|
|
32243
32543
|
throw new Error("Companion owned directory escapes its trusted anchor");
|
|
32244
32544
|
}
|
|
32245
|
-
const segments = relative4.split(
|
|
32545
|
+
const segments = relative4.split(path40.sep).filter(Boolean);
|
|
32246
32546
|
let current = anchor;
|
|
32247
|
-
const anchorStatus = await
|
|
32547
|
+
const anchorStatus = await fs36.lstat(anchor);
|
|
32248
32548
|
if (anchorStatus.isSymbolicLink() || !anchorStatus.isDirectory()) {
|
|
32249
32549
|
throw new Error("Companion owned directory anchor is unsafe");
|
|
32250
32550
|
}
|
|
32251
32551
|
for (const segment of segments) {
|
|
32252
|
-
current =
|
|
32552
|
+
current = path40.join(current, segment);
|
|
32253
32553
|
try {
|
|
32254
|
-
const status = await
|
|
32554
|
+
const status = await fs36.lstat(current);
|
|
32255
32555
|
if (status.isSymbolicLink() || !status.isDirectory()) {
|
|
32256
32556
|
throw new Error("Companion owned directory contains a symbolic link");
|
|
32257
32557
|
}
|
|
@@ -32264,19 +32564,19 @@ async function assertOwnedDirectoryChain(anchor, targetDirectory) {
|
|
|
32264
32564
|
async function assertOwnedParents(options, paths) {
|
|
32265
32565
|
await assertOwnedDirectoryChain(
|
|
32266
32566
|
options.homeDirectory,
|
|
32267
|
-
|
|
32567
|
+
path40.dirname(paths.installManifest)
|
|
32268
32568
|
);
|
|
32269
|
-
const targetAnchor = options.platform === "win32" ? options.localAppData ??
|
|
32270
|
-
await assertOwnedDirectoryChain(targetAnchor,
|
|
32569
|
+
const targetAnchor = options.platform === "win32" ? options.localAppData ?? path40.join(options.homeDirectory, "AppData", "Local") : options.homeDirectory;
|
|
32570
|
+
await assertOwnedDirectoryChain(targetAnchor, path40.dirname(paths.targetRoot));
|
|
32271
32571
|
}
|
|
32272
32572
|
async function readRegularText(filePath, maxBytes) {
|
|
32273
|
-
const before = await
|
|
32573
|
+
const before = await fs36.lstat(filePath);
|
|
32274
32574
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
32275
32575
|
throw new Error("Companion state file is not a regular file");
|
|
32276
32576
|
}
|
|
32277
32577
|
let handle;
|
|
32278
32578
|
try {
|
|
32279
|
-
handle = await
|
|
32579
|
+
handle = await fs36.open(
|
|
32280
32580
|
filePath,
|
|
32281
32581
|
constants2.O_RDONLY | constants2.O_NOFOLLOW
|
|
32282
32582
|
);
|
|
@@ -32299,27 +32599,27 @@ async function atomicWriteJson(filePath, value) {
|
|
|
32299
32599
|
}
|
|
32300
32600
|
async function atomicWriteText(filePath, contents, mode) {
|
|
32301
32601
|
await assertNotSymlink(filePath, "Companion state file");
|
|
32302
|
-
await
|
|
32602
|
+
await fs36.mkdir(path40.dirname(filePath), {
|
|
32303
32603
|
recursive: true,
|
|
32304
32604
|
mode: 448
|
|
32305
32605
|
});
|
|
32306
32606
|
const temporary = `${filePath}.${randomUUID3()}.tmp`;
|
|
32307
32607
|
try {
|
|
32308
|
-
await
|
|
32608
|
+
await fs36.writeFile(temporary, contents, {
|
|
32309
32609
|
mode,
|
|
32310
32610
|
flag: "wx"
|
|
32311
32611
|
});
|
|
32312
|
-
await
|
|
32313
|
-
await
|
|
32612
|
+
await fs36.rename(temporary, filePath);
|
|
32613
|
+
await fs36.chmod(filePath, mode).catch(() => void 0);
|
|
32314
32614
|
} finally {
|
|
32315
|
-
await
|
|
32615
|
+
await fs36.rm(temporary, { force: true }).catch(() => void 0);
|
|
32316
32616
|
}
|
|
32317
32617
|
}
|
|
32318
32618
|
async function realRegularFile(filePath, executable) {
|
|
32319
|
-
const real = await
|
|
32320
|
-
const stat5 = await
|
|
32619
|
+
const real = await fs36.realpath(filePath);
|
|
32620
|
+
const stat5 = await fs36.stat(real);
|
|
32321
32621
|
if (!stat5.isFile()) throw new Error("Companion launch target is not a file");
|
|
32322
|
-
await
|
|
32622
|
+
await fs36.access(real, executable ? constants2.X_OK : constants2.R_OK);
|
|
32323
32623
|
return real;
|
|
32324
32624
|
}
|
|
32325
32625
|
function defaultLaunch(executable) {
|
|
@@ -32335,7 +32635,7 @@ async function companionIsRunning(platform, executable) {
|
|
|
32335
32635
|
if (platform !== "win32") return false;
|
|
32336
32636
|
let handle;
|
|
32337
32637
|
try {
|
|
32338
|
-
handle = await
|
|
32638
|
+
handle = await fs36.open(executable, "r+");
|
|
32339
32639
|
} catch (error) {
|
|
32340
32640
|
const code = error.code;
|
|
32341
32641
|
if (code === "ENOENT") return false;
|
|
@@ -32416,7 +32716,7 @@ async function statusCompanion(options) {
|
|
|
32416
32716
|
realRegularFile(runtime.nodeExecutable, options.platform !== "win32"),
|
|
32417
32717
|
realRegularFile(runtime.cliEntry, false)
|
|
32418
32718
|
]);
|
|
32419
|
-
const executable =
|
|
32719
|
+
const executable = path40.join(
|
|
32420
32720
|
paths.targetRoot,
|
|
32421
32721
|
...install.entryRelativePath.split("/")
|
|
32422
32722
|
);
|
|
@@ -32449,7 +32749,7 @@ async function snapshotFile(filePath) {
|
|
|
32449
32749
|
}
|
|
32450
32750
|
async function restoreFile(filePath, bytes) {
|
|
32451
32751
|
if (bytes === null) {
|
|
32452
|
-
await
|
|
32752
|
+
await fs36.rm(filePath, { force: true });
|
|
32453
32753
|
} else {
|
|
32454
32754
|
await atomicWriteJson(filePath, JSON.parse(bytes.toString("utf8")));
|
|
32455
32755
|
}
|
|
@@ -32464,8 +32764,8 @@ async function converge(options, force) {
|
|
|
32464
32764
|
if (artifactManifest.platform !== options.platform || artifactManifest.architecture !== options.architecture) {
|
|
32465
32765
|
throw new Error("Companion artifact does not match this OS and architecture");
|
|
32466
32766
|
}
|
|
32467
|
-
const payloadRoot =
|
|
32468
|
-
|
|
32767
|
+
const payloadRoot = path40.join(
|
|
32768
|
+
path40.dirname(options.artifactManifestPath),
|
|
32469
32769
|
"payload"
|
|
32470
32770
|
);
|
|
32471
32771
|
await verifyArtifactSource(payloadRoot, artifactManifest);
|
|
@@ -32485,7 +32785,7 @@ async function converge(options, force) {
|
|
|
32485
32785
|
if (priorInstalled) {
|
|
32486
32786
|
await assertCompanionNotRunning(
|
|
32487
32787
|
options,
|
|
32488
|
-
|
|
32788
|
+
path40.join(
|
|
32489
32789
|
priorInstalled.paths.targetRoot,
|
|
32490
32790
|
...priorInstalled.install.entryRelativePath.split("/")
|
|
32491
32791
|
)
|
|
@@ -32493,7 +32793,7 @@ async function converge(options, force) {
|
|
|
32493
32793
|
}
|
|
32494
32794
|
if (current.state === "not-installed") {
|
|
32495
32795
|
try {
|
|
32496
|
-
await
|
|
32796
|
+
await fs36.lstat(paths.targetRoot);
|
|
32497
32797
|
throw new Error(
|
|
32498
32798
|
"The fixed companion target exists without an owned install manifest"
|
|
32499
32799
|
);
|
|
@@ -32504,7 +32804,7 @@ async function converge(options, force) {
|
|
|
32504
32804
|
const stage = `${paths.targetRoot}.m8t-stage-${randomUUID3()}`;
|
|
32505
32805
|
const backup = `${paths.targetRoot}.m8t-backup-${randomUUID3()}`;
|
|
32506
32806
|
const copy = options.copyPayload ?? copyArtifactPayload;
|
|
32507
|
-
await
|
|
32807
|
+
await fs36.mkdir(path40.dirname(paths.targetRoot), {
|
|
32508
32808
|
recursive: true,
|
|
32509
32809
|
mode: 448
|
|
32510
32810
|
});
|
|
@@ -32521,12 +32821,12 @@ async function converge(options, force) {
|
|
|
32521
32821
|
try {
|
|
32522
32822
|
await copy(payloadRoot, stage, artifactManifest);
|
|
32523
32823
|
try {
|
|
32524
|
-
await
|
|
32824
|
+
await fs36.rename(paths.targetRoot, backup);
|
|
32525
32825
|
movedPrior = true;
|
|
32526
32826
|
} catch (error) {
|
|
32527
32827
|
if (error.code !== "ENOENT") throw error;
|
|
32528
32828
|
}
|
|
32529
|
-
await
|
|
32829
|
+
await fs36.rename(stage, paths.targetRoot);
|
|
32530
32830
|
installedStage = true;
|
|
32531
32831
|
const nodeExecutable = await realRegularFile(
|
|
32532
32832
|
options.nodeExecutable,
|
|
@@ -32568,7 +32868,7 @@ async function converge(options, force) {
|
|
|
32568
32868
|
await readClosedJson(paths.runtimeBinding),
|
|
32569
32869
|
options.platform
|
|
32570
32870
|
);
|
|
32571
|
-
const executable =
|
|
32871
|
+
const executable = path40.join(
|
|
32572
32872
|
paths.targetRoot,
|
|
32573
32873
|
...artifactManifest.entryRelativePath.split("/")
|
|
32574
32874
|
);
|
|
@@ -32578,7 +32878,7 @@ async function converge(options, force) {
|
|
|
32578
32878
|
platform: options.platform,
|
|
32579
32879
|
executable: ownedExecutable
|
|
32580
32880
|
}));
|
|
32581
|
-
priorLoginExecutable = priorInstalled ?
|
|
32881
|
+
priorLoginExecutable = priorInstalled ? path40.join(
|
|
32582
32882
|
priorInstalled.paths.targetRoot,
|
|
32583
32883
|
...priorInstalled.install.entryRelativePath.split("/")
|
|
32584
32884
|
) : executable;
|
|
@@ -32589,7 +32889,7 @@ async function converge(options, force) {
|
|
|
32589
32889
|
);
|
|
32590
32890
|
loginChanged = true;
|
|
32591
32891
|
await (options.launch ?? defaultLaunch)(executable);
|
|
32592
|
-
await
|
|
32892
|
+
await fs36.rm(backup, { recursive: true, force: true });
|
|
32593
32893
|
return {
|
|
32594
32894
|
state: "installed",
|
|
32595
32895
|
version: artifactManifest.version,
|
|
@@ -32603,15 +32903,15 @@ async function converge(options, force) {
|
|
|
32603
32903
|
() => void 0
|
|
32604
32904
|
);
|
|
32605
32905
|
}
|
|
32606
|
-
await
|
|
32906
|
+
await fs36.rm(stage, { recursive: true, force: true }).catch(() => void 0);
|
|
32607
32907
|
if (installedStage) {
|
|
32608
|
-
await
|
|
32908
|
+
await fs36.rm(paths.targetRoot, {
|
|
32609
32909
|
recursive: true,
|
|
32610
32910
|
force: true
|
|
32611
32911
|
}).catch(() => void 0);
|
|
32612
32912
|
}
|
|
32613
32913
|
if (movedPrior) {
|
|
32614
|
-
await
|
|
32914
|
+
await fs36.rename(backup, paths.targetRoot).catch(() => void 0);
|
|
32615
32915
|
}
|
|
32616
32916
|
await Promise.all([
|
|
32617
32917
|
restoreFile(paths.installManifest, snapshots[0]),
|
|
@@ -32637,14 +32937,14 @@ async function uninstallCompanion(options) {
|
|
|
32637
32937
|
if (install.ownedTargetRoot !== paths.targetRoot) {
|
|
32638
32938
|
throw new Error("Refusing to remove a non-owned companion target");
|
|
32639
32939
|
}
|
|
32640
|
-
const executable =
|
|
32940
|
+
const executable = path40.join(
|
|
32641
32941
|
paths.targetRoot,
|
|
32642
32942
|
...install.entryRelativePath.split("/")
|
|
32643
32943
|
);
|
|
32644
32944
|
await assertCompanionNotRunning(options, executable);
|
|
32645
32945
|
await (options.setStartAtLogin ?? (() => Promise.resolve()))(executable, false);
|
|
32646
32946
|
const remove = options.removeOwnedPath ?? (async (ownedPath, recursive) => {
|
|
32647
|
-
await
|
|
32947
|
+
await fs36.rm(ownedPath, { recursive, force: true });
|
|
32648
32948
|
});
|
|
32649
32949
|
await remove(paths.targetRoot, true);
|
|
32650
32950
|
await remove(paths.installManifest, false);
|
|
@@ -32655,8 +32955,8 @@ async function uninstallCompanion(options) {
|
|
|
32655
32955
|
|
|
32656
32956
|
// src/commands/companion/install.ts
|
|
32657
32957
|
import * as os18 from "os";
|
|
32658
|
-
import * as
|
|
32659
|
-
import { Command as
|
|
32958
|
+
import * as path42 from "path";
|
|
32959
|
+
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
32660
32960
|
|
|
32661
32961
|
// src/lib/companion-channel.ts
|
|
32662
32962
|
async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps = {}) {
|
|
@@ -32673,9 +32973,9 @@ async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps =
|
|
|
32673
32973
|
// src/lib/companion-download.ts
|
|
32674
32974
|
import { createHash as createHash8 } from "crypto";
|
|
32675
32975
|
import { execFile as execFile2 } from "child_process";
|
|
32676
|
-
import * as
|
|
32976
|
+
import * as fs37 from "fs/promises";
|
|
32677
32977
|
import * as os17 from "os";
|
|
32678
|
-
import * as
|
|
32978
|
+
import * as path41 from "path";
|
|
32679
32979
|
init_errors();
|
|
32680
32980
|
var MAX_ASSET_BYTES = 512 * 1024 * 1024;
|
|
32681
32981
|
function extractArchive(archive, into) {
|
|
@@ -32728,20 +33028,20 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
32728
33028
|
hint: "Nothing was unpacked. This is what a corrupted download or a substituted file looks like \u2014 retry, and report it if it persists."
|
|
32729
33029
|
});
|
|
32730
33030
|
}
|
|
32731
|
-
const root = await (deps.makeTemporaryDirectory ?? (() =>
|
|
33031
|
+
const root = await (deps.makeTemporaryDirectory ?? (() => fs37.mkdtemp(path41.join(os17.tmpdir(), "m8t-companion-"))))();
|
|
32732
33032
|
const dispose = async () => {
|
|
32733
|
-
await
|
|
33033
|
+
await fs37.rm(root, { recursive: true, force: true }).catch(() => void 0);
|
|
32734
33034
|
};
|
|
32735
33035
|
try {
|
|
32736
|
-
const archive =
|
|
32737
|
-
await
|
|
32738
|
-
const unpacked =
|
|
32739
|
-
await
|
|
33036
|
+
const archive = path41.join(root, pinned.asset);
|
|
33037
|
+
await fs37.writeFile(archive, bytes, { mode: 384 });
|
|
33038
|
+
const unpacked = path41.join(root, "unpacked");
|
|
33039
|
+
await fs37.mkdir(unpacked, { recursive: false, mode: 448 });
|
|
32740
33040
|
await (deps.extract ?? extractArchive)(archive, unpacked);
|
|
32741
|
-
await
|
|
33041
|
+
await fs37.rm(archive, { force: true });
|
|
32742
33042
|
return {
|
|
32743
33043
|
version: component.version,
|
|
32744
|
-
artifactManifestPath:
|
|
33044
|
+
artifactManifestPath: path41.join(unpacked, "artifact-v1.json"),
|
|
32745
33045
|
dispose
|
|
32746
33046
|
};
|
|
32747
33047
|
} catch (error) {
|
|
@@ -32772,7 +33072,7 @@ async function convergeCompanionFromChannel(converge2, options = {}) {
|
|
|
32772
33072
|
let artifactManifestPath;
|
|
32773
33073
|
let dispose = () => Promise.resolve();
|
|
32774
33074
|
if (stagedDirectory !== void 0) {
|
|
32775
|
-
artifactManifestPath =
|
|
33075
|
+
artifactManifestPath = path42.resolve(stagedDirectory, "artifact-v1.json");
|
|
32776
33076
|
} else {
|
|
32777
33077
|
const release = await readCompanionRelease(
|
|
32778
33078
|
platformVersion !== void 0 ? { channel: true, version: platformTag(platformVersion) } : { url: CHANNEL_LATEST_URL }
|
|
@@ -32814,7 +33114,7 @@ Version: ${state.version}
|
|
|
32814
33114
|
}
|
|
32815
33115
|
var CompanionInstallCommand = class extends M8tCommand {
|
|
32816
33116
|
static paths = [["companion", "install"]];
|
|
32817
|
-
static usage =
|
|
33117
|
+
static usage = Command59.Usage({
|
|
32818
33118
|
description: "Install the desktop companions for this user from the release channel.",
|
|
32819
33119
|
examples: [
|
|
32820
33120
|
["Install the released build", "$0 companion install"],
|
|
@@ -32824,10 +33124,10 @@ var CompanionInstallCommand = class extends M8tCommand {
|
|
|
32824
33124
|
]
|
|
32825
33125
|
]
|
|
32826
33126
|
});
|
|
32827
|
-
from =
|
|
33127
|
+
from = Option56.String("--from", {
|
|
32828
33128
|
description: "A locally staged build directory instead of the released one."
|
|
32829
33129
|
});
|
|
32830
|
-
resourceGroup =
|
|
33130
|
+
resourceGroup = Option56.String("--resource-group", {
|
|
32831
33131
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
32832
33132
|
});
|
|
32833
33133
|
async executeCommand() {
|
|
@@ -32850,7 +33150,7 @@ function resolveRepoRootMarker(args) {
|
|
|
32850
33150
|
}
|
|
32851
33151
|
async function looksLikeCheckout(dir) {
|
|
32852
33152
|
try {
|
|
32853
|
-
return (await
|
|
33153
|
+
return (await fs38.stat(path43.join(dir, "brain-template"))).isDirectory();
|
|
32854
33154
|
} catch {
|
|
32855
33155
|
return false;
|
|
32856
33156
|
}
|
|
@@ -32869,18 +33169,18 @@ var defaultDeps3 = {
|
|
|
32869
33169
|
homedir: () => os19.homedir()
|
|
32870
33170
|
};
|
|
32871
33171
|
async function finalizeInstall(args, deps = defaultDeps3) {
|
|
32872
|
-
const markerDir =
|
|
32873
|
-
const markerPath =
|
|
33172
|
+
const markerDir = path43.join(deps.homedir(), ".m8t");
|
|
33173
|
+
const markerPath = path43.join(markerDir, "repo-root");
|
|
32874
33174
|
const cwd = process.cwd();
|
|
32875
|
-
const existing = await
|
|
33175
|
+
const existing = await fs38.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
|
|
32876
33176
|
const repoRoot = resolveRepoRootMarker({
|
|
32877
33177
|
...args.repoRoot !== void 0 ? { explicit: args.repoRoot } : {},
|
|
32878
33178
|
cwd,
|
|
32879
33179
|
cwdIsCheckout: await looksLikeCheckout(cwd),
|
|
32880
33180
|
existing
|
|
32881
33181
|
});
|
|
32882
|
-
await
|
|
32883
|
-
await
|
|
33182
|
+
await fs38.mkdir(markerDir, { recursive: true });
|
|
33183
|
+
await fs38.writeFile(markerPath, `${repoRoot}
|
|
32884
33184
|
`, "utf8");
|
|
32885
33185
|
let webappUrl;
|
|
32886
33186
|
try {
|
|
@@ -32958,7 +33258,7 @@ async function finalizeInstall(args, deps = defaultDeps3) {
|
|
|
32958
33258
|
}
|
|
32959
33259
|
let brainOrg = null;
|
|
32960
33260
|
try {
|
|
32961
|
-
const credsRaw = await
|
|
33261
|
+
const credsRaw = await fs38.readFile(path43.join(markerDir, "github-app.json"), "utf8");
|
|
32962
33262
|
const creds = JSON.parse(credsRaw);
|
|
32963
33263
|
brainOrg = typeof creds.org === "string" ? creds.org : null;
|
|
32964
33264
|
} catch {
|
|
@@ -33043,7 +33343,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
33043
33343
|
// runbooks and shakedown recipes that a founder may already be part-way
|
|
33044
33344
|
// through — it prints a deprecation notice and does the right thing.
|
|
33045
33345
|
static paths = [["bootstrap", "status"], ["bootstrap", "finish"]];
|
|
33046
|
-
static usage =
|
|
33346
|
+
static usage = Command60.Usage({
|
|
33047
33347
|
description: "Show the cloud installer's live status (phase, progress, result) \u2014 and finish the local setup when it lands.",
|
|
33048
33348
|
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.",
|
|
33049
33349
|
examples: [
|
|
@@ -33052,12 +33352,12 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
33052
33352
|
["Redo the local setup on a finished install", "$0 bootstrap status --finalize --repo-root /path/to/m8t"]
|
|
33053
33353
|
]
|
|
33054
33354
|
});
|
|
33055
|
-
watch =
|
|
33056
|
-
output =
|
|
33057
|
-
repoRoot =
|
|
33058
|
-
finalize =
|
|
33059
|
-
subscription =
|
|
33060
|
-
resourceGroup =
|
|
33355
|
+
watch = Option57.Boolean("--watch", false);
|
|
33356
|
+
output = Option57.String("--output");
|
|
33357
|
+
repoRoot = Option57.String("--repo-root", { description: "The m8t clone to point local tools at (default: the current directory)." });
|
|
33358
|
+
finalize = Option57.Boolean("--finalize", false, { description: "Complete the local setup against an already-done install, without watching." });
|
|
33359
|
+
subscription = Option57.String("--subscription");
|
|
33360
|
+
resourceGroup = Option57.String("--resource-group");
|
|
33061
33361
|
async executeCommand() {
|
|
33062
33362
|
const state = await readBootstrapState();
|
|
33063
33363
|
if (!state) {
|
|
@@ -33190,7 +33490,7 @@ function formatStatus(d) {
|
|
|
33190
33490
|
}
|
|
33191
33491
|
|
|
33192
33492
|
// src/commands/bootstrap/reap.ts
|
|
33193
|
-
import { Command as
|
|
33493
|
+
import { Command as Command61, Option as Option58 } from "clipanion";
|
|
33194
33494
|
init_errors();
|
|
33195
33495
|
|
|
33196
33496
|
// src/lib/bootstrap-reap.ts
|
|
@@ -33284,7 +33584,7 @@ async function reapInstaller(opts) {
|
|
|
33284
33584
|
// src/commands/bootstrap/reap.ts
|
|
33285
33585
|
var BootstrapReapCommand = class extends M8tCommand {
|
|
33286
33586
|
static paths = [["bootstrap", "reap"]];
|
|
33287
|
-
static usage =
|
|
33587
|
+
static usage = Command61.Usage({
|
|
33288
33588
|
description: "Tear down the installer scaffolding (ACI \u2192 MI \u2192 its role assignments) after a successful install.",
|
|
33289
33589
|
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.",
|
|
33290
33590
|
examples: [
|
|
@@ -33294,9 +33594,9 @@ var BootstrapReapCommand = class extends M8tCommand {
|
|
|
33294
33594
|
["\u2026and delete them", "$0 bootstrap reap --sweep-orphans --yes"]
|
|
33295
33595
|
]
|
|
33296
33596
|
});
|
|
33297
|
-
force =
|
|
33298
|
-
sweepOrphans =
|
|
33299
|
-
yes =
|
|
33597
|
+
force = Option58.Boolean("--force", false, { description: "Reap even if the install failed or never reported a status." });
|
|
33598
|
+
sweepOrphans = Option58.Boolean("--sweep-orphans", false, { description: "Subscription-wide: find m8t role assignments orphaned by earlier installs. Lists only, unless --yes." });
|
|
33599
|
+
yes = Option58.Boolean("--yes", false, { description: "With --sweep-orphans, actually delete what the sweep found." });
|
|
33300
33600
|
async executeCommand() {
|
|
33301
33601
|
if (this.sweepOrphans === true) {
|
|
33302
33602
|
const { subscriptionId: sub } = await getAzAccount();
|
|
@@ -33390,18 +33690,18 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
33390
33690
|
};
|
|
33391
33691
|
|
|
33392
33692
|
// src/commands/bootstrap/ui.ts
|
|
33393
|
-
import * as
|
|
33693
|
+
import * as fs40 from "fs";
|
|
33394
33694
|
import * as os21 from "os";
|
|
33395
|
-
import * as
|
|
33396
|
-
import { Command as
|
|
33397
|
-
import { DefaultAzureCredential as
|
|
33695
|
+
import * as path45 from "path";
|
|
33696
|
+
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
33697
|
+
import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
|
|
33398
33698
|
init_errors();
|
|
33399
33699
|
|
|
33400
33700
|
// src/lib/bootstrap-ui.ts
|
|
33401
|
-
import * as
|
|
33701
|
+
import * as fs39 from "fs";
|
|
33402
33702
|
import * as net from "net";
|
|
33403
33703
|
import * as os20 from "os";
|
|
33404
|
-
import * as
|
|
33704
|
+
import * as path44 from "path";
|
|
33405
33705
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
33406
33706
|
import { spawn as spawn8, spawnSync as spawnSync6 } from "child_process";
|
|
33407
33707
|
init_errors();
|
|
@@ -33589,11 +33889,11 @@ async function ensureFounderFoundryRole(args) {
|
|
|
33589
33889
|
});
|
|
33590
33890
|
}
|
|
33591
33891
|
function writeWebEnvLocal(args) {
|
|
33592
|
-
const envPath =
|
|
33892
|
+
const envPath = path44.join(args.repoRoot, "apps", "web", ".env.local");
|
|
33593
33893
|
let prior = "";
|
|
33594
|
-
if (
|
|
33595
|
-
prior =
|
|
33596
|
-
|
|
33894
|
+
if (fs39.existsSync(envPath)) {
|
|
33895
|
+
prior = fs39.readFileSync(envPath, "utf8");
|
|
33896
|
+
fs39.copyFileSync(envPath, envPath + ".bak");
|
|
33597
33897
|
}
|
|
33598
33898
|
const priorSecret = readValidInternalSecret(prior);
|
|
33599
33899
|
const internalSecret = priorSecret ?? (args.randomBytesImpl ?? randomBytes3)(32).toString("base64url");
|
|
@@ -33619,9 +33919,9 @@ function writeWebEnvLocal(args) {
|
|
|
33619
33919
|
"NEXT_PUBLIC_M8T_ONBOARDING=1",
|
|
33620
33920
|
""
|
|
33621
33921
|
].join("\n");
|
|
33622
|
-
|
|
33922
|
+
fs39.writeFileSync(envPath, body, { encoding: "utf8", mode: 384 });
|
|
33623
33923
|
try {
|
|
33624
|
-
|
|
33924
|
+
fs39.chmodSync(envPath, 384);
|
|
33625
33925
|
} catch {
|
|
33626
33926
|
}
|
|
33627
33927
|
return envPath;
|
|
@@ -33637,10 +33937,10 @@ function readValidInternalSecret(envText) {
|
|
|
33637
33937
|
return isValidInternalSecret(values[0]) ? values[0] : void 0;
|
|
33638
33938
|
}
|
|
33639
33939
|
function readWebInternalSecret(repoRoot) {
|
|
33640
|
-
const envPath =
|
|
33940
|
+
const envPath = path44.join(repoRoot, "apps", "web", ".env.local");
|
|
33641
33941
|
let secret;
|
|
33642
33942
|
try {
|
|
33643
|
-
secret = readValidInternalSecret(
|
|
33943
|
+
secret = readValidInternalSecret(fs39.readFileSync(envPath, "utf8"));
|
|
33644
33944
|
} catch {
|
|
33645
33945
|
secret = void 0;
|
|
33646
33946
|
}
|
|
@@ -33692,17 +33992,17 @@ async function installWebDeps(repoRoot) {
|
|
|
33692
33992
|
await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
|
|
33693
33993
|
}
|
|
33694
33994
|
function onboardingUiPaths(home = os20.homedir()) {
|
|
33695
|
-
const dir =
|
|
33995
|
+
const dir = path44.join(home, ".m8t");
|
|
33696
33996
|
return {
|
|
33697
|
-
logPath:
|
|
33698
|
-
pidPath:
|
|
33997
|
+
logPath: path44.join(dir, "onboarding-ui.log"),
|
|
33998
|
+
pidPath: path44.join(dir, "onboarding-ui.pid")
|
|
33699
33999
|
};
|
|
33700
34000
|
}
|
|
33701
34001
|
function onboardingRelayPaths(home = os20.homedir()) {
|
|
33702
|
-
const dir =
|
|
34002
|
+
const dir = path44.join(home, ".m8t");
|
|
33703
34003
|
return {
|
|
33704
|
-
logPath:
|
|
33705
|
-
pidPath:
|
|
34004
|
+
logPath: path44.join(dir, "onboarding-relay.log"),
|
|
34005
|
+
pidPath: path44.join(dir, "onboarding-relay.pid")
|
|
33706
34006
|
};
|
|
33707
34007
|
}
|
|
33708
34008
|
function isLocalPortOpen(port) {
|
|
@@ -33724,7 +34024,7 @@ function isLocalPortOpen(port) {
|
|
|
33724
34024
|
}
|
|
33725
34025
|
function removePidFile(pidPath) {
|
|
33726
34026
|
try {
|
|
33727
|
-
|
|
34027
|
+
fs39.unlinkSync(pidPath);
|
|
33728
34028
|
} catch {
|
|
33729
34029
|
}
|
|
33730
34030
|
}
|
|
@@ -33737,7 +34037,7 @@ function parseManagedPid(text) {
|
|
|
33737
34037
|
function readLiveManagedPid(pidPath) {
|
|
33738
34038
|
let text;
|
|
33739
34039
|
try {
|
|
33740
|
-
text =
|
|
34040
|
+
text = fs39.readFileSync(pidPath, "utf8").trim();
|
|
33741
34041
|
} catch {
|
|
33742
34042
|
return null;
|
|
33743
34043
|
}
|
|
@@ -33776,8 +34076,8 @@ async function serveOnboardingUiDetached(args) {
|
|
|
33776
34076
|
});
|
|
33777
34077
|
}
|
|
33778
34078
|
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
33779
|
-
|
|
33780
|
-
const fd =
|
|
34079
|
+
fs39.mkdirSync(path44.dirname(logPath), { recursive: true });
|
|
34080
|
+
const fd = fs39.openSync(logPath, "a");
|
|
33781
34081
|
const child = spawn8("pnpm", ["--filter", "web", "dev"], {
|
|
33782
34082
|
cwd: args.repoRoot,
|
|
33783
34083
|
detached: true,
|
|
@@ -33786,7 +34086,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
33786
34086
|
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
33787
34087
|
shell: process.platform === "win32"
|
|
33788
34088
|
});
|
|
33789
|
-
|
|
34089
|
+
fs39.closeSync(fd);
|
|
33790
34090
|
if (child.pid == null) {
|
|
33791
34091
|
throw new LocalCliError({
|
|
33792
34092
|
code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
|
|
@@ -33795,7 +34095,7 @@ async function serveOnboardingUiDetached(args) {
|
|
|
33795
34095
|
});
|
|
33796
34096
|
}
|
|
33797
34097
|
child.unref();
|
|
33798
|
-
|
|
34098
|
+
fs39.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
33799
34099
|
const deadline = Date.now() + 45e3;
|
|
33800
34100
|
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
33801
34101
|
while (Date.now() < deadline) {
|
|
@@ -33827,8 +34127,8 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
33827
34127
|
});
|
|
33828
34128
|
}
|
|
33829
34129
|
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
33830
|
-
|
|
33831
|
-
const fd =
|
|
34130
|
+
fs39.mkdirSync(path44.dirname(logPath), { recursive: true });
|
|
34131
|
+
const fd = fs39.openSync(logPath, "a");
|
|
33832
34132
|
const child = spawn8("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
|
|
33833
34133
|
cwd: args.repoRoot,
|
|
33834
34134
|
detached: true,
|
|
@@ -33846,7 +34146,7 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
33846
34146
|
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
33847
34147
|
shell: process.platform === "win32"
|
|
33848
34148
|
});
|
|
33849
|
-
|
|
34149
|
+
fs39.closeSync(fd);
|
|
33850
34150
|
if (child.pid == null) {
|
|
33851
34151
|
throw new LocalCliError({
|
|
33852
34152
|
code: "BOOTSTRAP_UI_RELAY_SPAWN_FAILED",
|
|
@@ -33855,7 +34155,7 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
33855
34155
|
});
|
|
33856
34156
|
}
|
|
33857
34157
|
child.unref();
|
|
33858
|
-
|
|
34158
|
+
fs39.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
33859
34159
|
const deadline = Date.now() + 45e3;
|
|
33860
34160
|
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
33861
34161
|
while (Date.now() < deadline) {
|
|
@@ -33877,7 +34177,7 @@ function autoOpenOnboardingUi(url, open3 = tryOpenUrl) {
|
|
|
33877
34177
|
function stopPidFile(pidPath) {
|
|
33878
34178
|
let pidStr;
|
|
33879
34179
|
try {
|
|
33880
|
-
pidStr =
|
|
34180
|
+
pidStr = fs39.readFileSync(pidPath, "utf8").trim();
|
|
33881
34181
|
} catch {
|
|
33882
34182
|
return false;
|
|
33883
34183
|
}
|
|
@@ -33894,7 +34194,7 @@ function stopPidFile(pidPath) {
|
|
|
33894
34194
|
}
|
|
33895
34195
|
}
|
|
33896
34196
|
try {
|
|
33897
|
-
|
|
34197
|
+
fs39.unlinkSync(pidPath);
|
|
33898
34198
|
} catch {
|
|
33899
34199
|
}
|
|
33900
34200
|
return true;
|
|
@@ -34221,7 +34521,7 @@ function renderDeployFailure(error) {
|
|
|
34221
34521
|
}
|
|
34222
34522
|
var BootstrapUiCommand = class extends M8tCommand {
|
|
34223
34523
|
static paths = [["bootstrap", "ui"]];
|
|
34224
|
-
static usage =
|
|
34524
|
+
static usage = Command62.Usage({
|
|
34225
34525
|
description: "Deploy Ezra + start the local onboarding chat UI in the background (returns immediately).",
|
|
34226
34526
|
details: [
|
|
34227
34527
|
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Waits for the cloud",
|
|
@@ -34242,16 +34542,16 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
34242
34542
|
["Experimental: start the voice relay (no effect on the text-only intake)", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
|
|
34243
34543
|
]
|
|
34244
34544
|
});
|
|
34245
|
-
repoRoot =
|
|
34246
|
-
port =
|
|
34247
|
-
endpoint =
|
|
34545
|
+
repoRoot = Option59.String("--repo-root");
|
|
34546
|
+
port = Option59.String("--port", "3000");
|
|
34547
|
+
endpoint = Option59.String("--endpoint", {
|
|
34248
34548
|
description: "Foundry project endpoint to target \u2014 overrides discovery when this install's resource group holds more than one Foundry project."
|
|
34249
34549
|
});
|
|
34250
|
-
prepOnly =
|
|
34251
|
-
skipInstall =
|
|
34252
|
-
stop =
|
|
34253
|
-
foreground =
|
|
34254
|
-
voice =
|
|
34550
|
+
prepOnly = Option59.Boolean("--prep-only", false);
|
|
34551
|
+
skipInstall = Option59.Boolean("--skip-install", false);
|
|
34552
|
+
stop = Option59.Boolean("--stop", false);
|
|
34553
|
+
foreground = Option59.Boolean("--foreground", false);
|
|
34554
|
+
voice = Option59.Boolean("--voice", false, {
|
|
34255
34555
|
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."
|
|
34256
34556
|
});
|
|
34257
34557
|
async executeCommand() {
|
|
@@ -34291,10 +34591,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
34291
34591
|
this.context.stderr.write(` ${colors.dim(m)}
|
|
34292
34592
|
`);
|
|
34293
34593
|
};
|
|
34294
|
-
if (
|
|
34594
|
+
if (fs40.existsSync(path45.join(os21.homedir(), ".m8t", "config.yaml"))) {
|
|
34295
34595
|
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."));
|
|
34296
34596
|
}
|
|
34297
|
-
const credential2 = new
|
|
34597
|
+
const credential2 = new DefaultAzureCredential25();
|
|
34298
34598
|
const account = await getAzAccount();
|
|
34299
34599
|
assertNodeVersion();
|
|
34300
34600
|
out("waiting for Foundry (the installer's foundry-create phase)\u2026");
|
|
@@ -34423,10 +34723,10 @@ var BootstrapUiCommand = class extends M8tCommand {
|
|
|
34423
34723
|
};
|
|
34424
34724
|
|
|
34425
34725
|
// src/commands/bootstrap/seed-profile.ts
|
|
34426
|
-
import { Command as
|
|
34726
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34427
34727
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
34428
34728
|
static paths = [["bootstrap", "seed-profile"]];
|
|
34429
|
-
static usage =
|
|
34729
|
+
static usage = Command63.Usage({
|
|
34430
34730
|
description: "Seed your advisors' brains with the founder + company profile from the onboarding intake.",
|
|
34431
34731
|
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.",
|
|
34432
34732
|
examples: [
|
|
@@ -34434,11 +34734,11 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34434
34734
|
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
34435
34735
|
]
|
|
34436
34736
|
});
|
|
34437
|
-
endpoint =
|
|
34438
|
-
brain =
|
|
34439
|
-
watch =
|
|
34440
|
-
timeout =
|
|
34441
|
-
githubAppCreds =
|
|
34737
|
+
endpoint = Option60.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
34738
|
+
brain = Option60.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
34739
|
+
watch = Option60.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
34740
|
+
timeout = Option60.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
34741
|
+
githubAppCreds = Option60.String("--github-app-creds");
|
|
34442
34742
|
async executeCommand() {
|
|
34443
34743
|
const ctx = await resolveSeedContext({
|
|
34444
34744
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -34498,10 +34798,10 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34498
34798
|
};
|
|
34499
34799
|
|
|
34500
34800
|
// src/commands/telemetry/enroll.ts
|
|
34501
|
-
import * as
|
|
34801
|
+
import * as fs41 from "fs";
|
|
34502
34802
|
import * as os22 from "os";
|
|
34503
|
-
import * as
|
|
34504
|
-
import { Command as
|
|
34803
|
+
import * as path46 from "path";
|
|
34804
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
34505
34805
|
init_errors();
|
|
34506
34806
|
|
|
34507
34807
|
// src/lib/telemetry-enroll.ts
|
|
@@ -34583,7 +34883,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
34583
34883
|
}
|
|
34584
34884
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
34585
34885
|
static paths = [["telemetry", "enroll"]];
|
|
34586
|
-
static usage =
|
|
34886
|
+
static usage = Command64.Usage({
|
|
34587
34887
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
34588
34888
|
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.",
|
|
34589
34889
|
examples: [
|
|
@@ -34591,11 +34891,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34591
34891
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
34592
34892
|
]
|
|
34593
34893
|
});
|
|
34594
|
-
company =
|
|
34595
|
-
contactEmail =
|
|
34596
|
-
subscription =
|
|
34597
|
-
resourceGroup =
|
|
34598
|
-
force =
|
|
34894
|
+
company = Option61.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
34895
|
+
contactEmail = Option61.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
34896
|
+
subscription = Option61.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
34897
|
+
resourceGroup = Option61.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
34898
|
+
force = Option61.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
34599
34899
|
async executeCommand() {
|
|
34600
34900
|
const account = await getAzAccount();
|
|
34601
34901
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -34628,13 +34928,13 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34628
34928
|
...company ? { company } : {},
|
|
34629
34929
|
...contactEmail ? { contactEmail } : {}
|
|
34630
34930
|
});
|
|
34631
|
-
const keyDir =
|
|
34632
|
-
const keyFile =
|
|
34931
|
+
const keyDir = fs41.mkdtempSync(path46.join(os22.tmpdir(), "m8t-ingest-key-"));
|
|
34932
|
+
const keyFile = path46.join(keyDir, "key");
|
|
34633
34933
|
try {
|
|
34634
|
-
|
|
34934
|
+
fs41.writeFileSync(keyFile, ingestKey, { mode: 384 });
|
|
34635
34935
|
await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--file", keyFile, "--only-show-errors"]);
|
|
34636
34936
|
} finally {
|
|
34637
|
-
|
|
34937
|
+
fs41.rmSync(keyDir, { recursive: true, force: true });
|
|
34638
34938
|
}
|
|
34639
34939
|
this.context.stdout.write(
|
|
34640
34940
|
`${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
|
|
@@ -34645,7 +34945,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34645
34945
|
};
|
|
34646
34946
|
|
|
34647
34947
|
// src/commands/companion/bridge.ts
|
|
34648
|
-
import { Command as
|
|
34948
|
+
import { Command as Command65, Option as Option62 } from "clipanion";
|
|
34649
34949
|
|
|
34650
34950
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
34651
34951
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -36101,14 +36401,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps4) {
|
|
|
36101
36401
|
return 3;
|
|
36102
36402
|
}
|
|
36103
36403
|
}
|
|
36104
|
-
var CompanionBridgeCommand = class extends
|
|
36404
|
+
var CompanionBridgeCommand = class extends Command65 {
|
|
36105
36405
|
static paths = [["companion", "_bridge"]];
|
|
36106
36406
|
/**
|
|
36107
36407
|
* One process serving many requests instead of one per request, so the
|
|
36108
36408
|
* session keeps its authenticated context between them. A CLI predating the
|
|
36109
36409
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
36110
36410
|
*/
|
|
36111
|
-
serve =
|
|
36411
|
+
serve = Option62.Boolean("--serve", false);
|
|
36112
36412
|
async execute() {
|
|
36113
36413
|
if (this.serve) {
|
|
36114
36414
|
return runCompanionBridgeServe(
|
|
@@ -36126,7 +36426,7 @@ var CompanionBridgeCommand = class extends Command64 {
|
|
|
36126
36426
|
};
|
|
36127
36427
|
|
|
36128
36428
|
// src/commands/companion/status.ts
|
|
36129
|
-
import { Command as
|
|
36429
|
+
import { Command as Command66 } from "clipanion";
|
|
36130
36430
|
async function withTimeout(work, ms) {
|
|
36131
36431
|
let timer;
|
|
36132
36432
|
try {
|
|
@@ -36198,7 +36498,7 @@ Run: m8t companion install
|
|
|
36198
36498
|
}
|
|
36199
36499
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
36200
36500
|
static paths = [["companion", "status"]];
|
|
36201
|
-
static usage =
|
|
36501
|
+
static usage = Command66.Usage({
|
|
36202
36502
|
description: "Verify the installed desktop companion without launching it."
|
|
36203
36503
|
});
|
|
36204
36504
|
async executeCommand() {
|
|
@@ -36212,7 +36512,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
36212
36512
|
};
|
|
36213
36513
|
|
|
36214
36514
|
// src/commands/companion/repair.ts
|
|
36215
|
-
import { Command as
|
|
36515
|
+
import { Command as Command67, Option as Option63 } from "clipanion";
|
|
36216
36516
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
36217
36517
|
const state = await repair();
|
|
36218
36518
|
if (state.state === "not-released") {
|
|
@@ -36231,10 +36531,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
36231
36531
|
}
|
|
36232
36532
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
36233
36533
|
static paths = [["companion", "repair"]];
|
|
36234
|
-
static usage =
|
|
36534
|
+
static usage = Command67.Usage({
|
|
36235
36535
|
description: "Restore the desktop companions and start-at-login state."
|
|
36236
36536
|
});
|
|
36237
|
-
resourceGroup =
|
|
36537
|
+
resourceGroup = Option63.String("--resource-group", {
|
|
36238
36538
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
36239
36539
|
});
|
|
36240
36540
|
async executeCommand() {
|
|
@@ -36248,7 +36548,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
36248
36548
|
};
|
|
36249
36549
|
|
|
36250
36550
|
// src/commands/companion/uninstall.ts
|
|
36251
|
-
import { Command as
|
|
36551
|
+
import { Command as Command68 } from "clipanion";
|
|
36252
36552
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
36253
36553
|
const state = await uninstall();
|
|
36254
36554
|
if (state.state !== "not-installed") {
|
|
@@ -36260,7 +36560,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
36260
36560
|
}
|
|
36261
36561
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
36262
36562
|
static paths = [["companion", "uninstall"]];
|
|
36263
|
-
static usage =
|
|
36563
|
+
static usage = Command68.Usage({
|
|
36264
36564
|
description: "Remove only this user's desktop companion installation."
|
|
36265
36565
|
});
|
|
36266
36566
|
async executeCommand() {
|
|
@@ -36317,8 +36617,9 @@ cli.register(AzureExecDeployCommand);
|
|
|
36317
36617
|
cli.register(PlatformStatusCommand);
|
|
36318
36618
|
cli.register(PlatformUpdateCommand);
|
|
36319
36619
|
cli.register(PlatformConvergeCommand);
|
|
36620
|
+
cli.register(PlatformClearIntentCommand);
|
|
36621
|
+
cli.register(PlatformRequestUpdateCommand);
|
|
36320
36622
|
cli.register(PlatformSeedStampCommand);
|
|
36321
|
-
cli.register(PlatformStampBuildCommand);
|
|
36322
36623
|
cli.register(PlatformPolicyCommand);
|
|
36323
36624
|
cli.register(PlatformEnableCostReportCommand);
|
|
36324
36625
|
cli.register(PlatformEnableAutoUpdateCommand);
|