@m8t-stack/cli 0.2.33 → 0.2.35
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 +198 -149
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1247,7 +1247,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1247
1247
|
import { Builtins, Cli } from "clipanion";
|
|
1248
1248
|
|
|
1249
1249
|
// src/lib/package-version.ts
|
|
1250
|
-
var CLI_VERSION = "0.2.
|
|
1250
|
+
var CLI_VERSION = "0.2.35";
|
|
1251
1251
|
|
|
1252
1252
|
// src/lib/render-error.ts
|
|
1253
1253
|
init_errors();
|
|
@@ -1444,6 +1444,7 @@ var BACKEND_RULES = [
|
|
|
1444
1444
|
var LOCAL_RULES = {
|
|
1445
1445
|
AGENT_CREATE_VERSION_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1446
1446
|
AGENT_CREATE_VERSION_NO_VERSION: "Foundry returned 2xx but no version \u2014 likely an API regression. Re-try; if it persists, file an issue.",
|
|
1447
|
+
AGENT_ENDPOINT_AUTH_PATCH_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1447
1448
|
AGENT_GET_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
1448
1449
|
AGENT_NOT_FOUND: "Use `m8t brain create <name>` to create the worker first, OR run `m8t deploy <name>` to register an existing agent.",
|
|
1449
1450
|
AGENT_REDEPLOY_FAILED: "Re-run with `--verbose` for the full response body.",
|
|
@@ -3327,12 +3328,12 @@ function encodeURIPath(str4) {
|
|
|
3327
3328
|
return str4.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
3328
3329
|
}
|
|
3329
3330
|
var EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
3330
|
-
var createPathTagFunction = (pathEncoder = encodeURIPath) => function
|
|
3331
|
+
var createPathTagFunction = (pathEncoder = encodeURIPath) => function path38(statics, ...params) {
|
|
3331
3332
|
if (statics.length === 1)
|
|
3332
3333
|
return statics[0];
|
|
3333
3334
|
let postPath = false;
|
|
3334
3335
|
const invalidSegments = [];
|
|
3335
|
-
const
|
|
3336
|
+
const path39 = statics.reduce((previousValue, currentValue, index) => {
|
|
3336
3337
|
if (/[?#]/.test(currentValue)) {
|
|
3337
3338
|
postPath = true;
|
|
3338
3339
|
}
|
|
@@ -3349,7 +3350,7 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path37(sta
|
|
|
3349
3350
|
}
|
|
3350
3351
|
return previousValue + currentValue + (index === params.length ? "" : encoded);
|
|
3351
3352
|
}, "");
|
|
3352
|
-
const pathOnly =
|
|
3353
|
+
const pathOnly = path39.split(/[?#]/, 1)[0];
|
|
3353
3354
|
const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
|
|
3354
3355
|
let match;
|
|
3355
3356
|
while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
|
|
@@ -3370,10 +3371,10 @@ var createPathTagFunction = (pathEncoder = encodeURIPath) => function path37(sta
|
|
|
3370
3371
|
}, "");
|
|
3371
3372
|
throw new OpenAIError(`Path parameters result in path with invalid segments:
|
|
3372
3373
|
${invalidSegments.map((e) => e.error).join("\n")}
|
|
3373
|
-
${
|
|
3374
|
+
${path39}
|
|
3374
3375
|
${underline}`);
|
|
3375
3376
|
}
|
|
3376
|
-
return
|
|
3377
|
+
return path39;
|
|
3377
3378
|
};
|
|
3378
3379
|
var path = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
|
|
3379
3380
|
|
|
@@ -10890,9 +10891,9 @@ var OpenAI = class {
|
|
|
10890
10891
|
this.apiKey = token;
|
|
10891
10892
|
return true;
|
|
10892
10893
|
}
|
|
10893
|
-
buildURL(
|
|
10894
|
+
buildURL(path38, query, defaultBaseURL) {
|
|
10894
10895
|
const baseURL = !__classPrivateFieldGet(this, _OpenAI_instances, "m", _OpenAI_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
|
|
10895
|
-
const url = isAbsoluteURL(
|
|
10896
|
+
const url = isAbsoluteURL(path38) ? new URL(path38) : new URL(baseURL + (baseURL.endsWith("/") && path38.startsWith("/") ? path38.slice(1) : path38));
|
|
10896
10897
|
const defaultQuery = this.defaultQuery();
|
|
10897
10898
|
const pathQuery = Object.fromEntries(url.searchParams);
|
|
10898
10899
|
if (!isEmptyObj(defaultQuery) || !isEmptyObj(pathQuery)) {
|
|
@@ -10920,24 +10921,24 @@ var OpenAI = class {
|
|
|
10920
10921
|
*/
|
|
10921
10922
|
async prepareRequest(request, { url, options }) {
|
|
10922
10923
|
}
|
|
10923
|
-
get(
|
|
10924
|
-
return this.methodRequest("get",
|
|
10924
|
+
get(path38, opts) {
|
|
10925
|
+
return this.methodRequest("get", path38, opts);
|
|
10925
10926
|
}
|
|
10926
|
-
post(
|
|
10927
|
-
return this.methodRequest("post",
|
|
10927
|
+
post(path38, opts) {
|
|
10928
|
+
return this.methodRequest("post", path38, opts);
|
|
10928
10929
|
}
|
|
10929
|
-
patch(
|
|
10930
|
-
return this.methodRequest("patch",
|
|
10930
|
+
patch(path38, opts) {
|
|
10931
|
+
return this.methodRequest("patch", path38, opts);
|
|
10931
10932
|
}
|
|
10932
|
-
put(
|
|
10933
|
-
return this.methodRequest("put",
|
|
10933
|
+
put(path38, opts) {
|
|
10934
|
+
return this.methodRequest("put", path38, opts);
|
|
10934
10935
|
}
|
|
10935
|
-
delete(
|
|
10936
|
-
return this.methodRequest("delete",
|
|
10936
|
+
delete(path38, opts) {
|
|
10937
|
+
return this.methodRequest("delete", path38, opts);
|
|
10937
10938
|
}
|
|
10938
|
-
methodRequest(method,
|
|
10939
|
+
methodRequest(method, path38, opts) {
|
|
10939
10940
|
return this.request(Promise.resolve(opts).then((opts2) => {
|
|
10940
|
-
return { method, path:
|
|
10941
|
+
return { method, path: path38, ...opts2 };
|
|
10941
10942
|
}));
|
|
10942
10943
|
}
|
|
10943
10944
|
request(options, remainingRetries = null) {
|
|
@@ -11059,8 +11060,8 @@ var OpenAI = class {
|
|
|
11059
11060
|
}));
|
|
11060
11061
|
return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
|
|
11061
11062
|
}
|
|
11062
|
-
getAPIList(
|
|
11063
|
-
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path:
|
|
11063
|
+
getAPIList(path38, Page2, opts) {
|
|
11064
|
+
return this.requestAPIList(Page2, opts && "then" in opts ? opts.then((opts2) => ({ method: "get", path: path38, ...opts2 })) : { method: "get", path: path38, ...opts });
|
|
11064
11065
|
}
|
|
11065
11066
|
requestAPIList(Page2, options) {
|
|
11066
11067
|
const request = this.makeRequest(options, null, void 0);
|
|
@@ -11154,8 +11155,8 @@ var OpenAI = class {
|
|
|
11154
11155
|
}
|
|
11155
11156
|
async buildRequest(inputOptions, { retryCount = 0 } = {}) {
|
|
11156
11157
|
const options = { ...inputOptions };
|
|
11157
|
-
const { method, path:
|
|
11158
|
-
const url = this.buildURL(
|
|
11158
|
+
const { method, path: path38, query, defaultBaseURL } = options;
|
|
11159
|
+
const url = this.buildURL(path38, query, defaultBaseURL);
|
|
11159
11160
|
if ("timeout" in options)
|
|
11160
11161
|
validatePositiveInteger("timeout", options.timeout);
|
|
11161
11162
|
options.timeout = options.timeout ?? this.timeout;
|
|
@@ -12182,10 +12183,10 @@ var BindListCommand = class extends M8tCommand {
|
|
|
12182
12183
|
const qs = new URLSearchParams();
|
|
12183
12184
|
if (this.channel) qs.set("channel", this.channel);
|
|
12184
12185
|
if (this.status) qs.set("status", this.status);
|
|
12185
|
-
const
|
|
12186
|
+
const path38 = qs.toString().length > 0 ? `/api/bindings?${qs.toString()}` : "/api/bindings";
|
|
12186
12187
|
const data = await apiCall(
|
|
12187
12188
|
{ gatewayUrl: ctx.gatewayUrl, gatewayClientId: ctx.gatewayClientId },
|
|
12188
|
-
{ method: "GET", path:
|
|
12189
|
+
{ method: "GET", path: path38 },
|
|
12189
12190
|
(msg) => this.context.stderr.write(msg + "\n")
|
|
12190
12191
|
);
|
|
12191
12192
|
if (mode === "json") {
|
|
@@ -13396,8 +13397,8 @@ ${r.stderr}`;
|
|
|
13396
13397
|
message: `gh api /repos/${slug} failed (exit ${r.exitCode.toString()}): ${(r.stderr || r.stdout).slice(0, 200)}`
|
|
13397
13398
|
});
|
|
13398
13399
|
},
|
|
13399
|
-
getFile: async (
|
|
13400
|
-
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${
|
|
13400
|
+
getFile: async (path38) => {
|
|
13401
|
+
const r = await exec("gh", ["api", "-H", "Accept: application/vnd.github.raw+json", `/repos/${slug}/contents/${path38}`]);
|
|
13401
13402
|
return r.exitCode === 0 ? r.stdout : null;
|
|
13402
13403
|
},
|
|
13403
13404
|
isEmpty: async () => {
|
|
@@ -13917,8 +13918,27 @@ ${text.slice(0, 500)}` });
|
|
|
13917
13918
|
}
|
|
13918
13919
|
const data = await res.json();
|
|
13919
13920
|
if (!data.version) throw new LocalCliError({ code: "AGENT_CREATE_VERSION_NO_VERSION", message: "createVersion returned no version" });
|
|
13921
|
+
await ensureAgentEndpointEntraIsolation(
|
|
13922
|
+
{ credential: args.credential, projectEndpoint: args.projectEndpoint, agentName: args.agentName, extraHeaders: args.extraHeaders },
|
|
13923
|
+
fetchImpl
|
|
13924
|
+
);
|
|
13920
13925
|
return data.version;
|
|
13921
13926
|
}
|
|
13927
|
+
async function ensureAgentEndpointEntraIsolation(args, fetchImpl = fetch) {
|
|
13928
|
+
const token = await args.credential.getToken(FOUNDRY_DATA_SCOPE);
|
|
13929
|
+
if (!token?.token) throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
|
|
13930
|
+
const url = `${args.projectEndpoint}/agents/${args.agentName}?api-version=v1`;
|
|
13931
|
+
const res = await fetchImpl(url, {
|
|
13932
|
+
method: "PATCH",
|
|
13933
|
+
headers: { Authorization: `Bearer ${token.token}`, "Content-Type": "application/json", ...args.extraHeaders ?? {} },
|
|
13934
|
+
body: JSON.stringify({ agent_endpoint: { authorization_schemes: [{ type: "Entra", isolation_key_source: { kind: "Entra" } }] } })
|
|
13935
|
+
});
|
|
13936
|
+
if (!res.ok) {
|
|
13937
|
+
const text = await res.text();
|
|
13938
|
+
throw new LocalCliError({ code: "AGENT_ENDPOINT_AUTH_PATCH_FAILED", message: `PATCH ${url}: HTTP ${res.status.toString()}
|
|
13939
|
+
${text.slice(0, 500)}` });
|
|
13940
|
+
}
|
|
13941
|
+
}
|
|
13922
13942
|
|
|
13923
13943
|
// src/lib/brain-link.ts
|
|
13924
13944
|
init_brain_yaml_mirror();
|
|
@@ -14487,7 +14507,7 @@ function appRepoProbe(args) {
|
|
|
14487
14507
|
const res = await doFetch(`${GH_API}/repos/${args.repo}`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
14488
14508
|
return res.status;
|
|
14489
14509
|
},
|
|
14490
|
-
getFile: (
|
|
14510
|
+
getFile: (path38) => readRepoFileViaApp({ token: args.token, repo: args.repo, path: path38, fetchImpl: args.fetchImpl }),
|
|
14491
14511
|
isEmpty: async () => {
|
|
14492
14512
|
const res = await doFetch(`${GH_API}/repos/${args.repo}/contents`, { headers: H, signal: AbortSignal.timeout(GH_REQUEST_TIMEOUT_MS) });
|
|
14493
14513
|
return res.status === 404;
|
|
@@ -15223,7 +15243,6 @@ import * as os5 from "os";
|
|
|
15223
15243
|
import * as path11 from "path";
|
|
15224
15244
|
init_foundry_agent_get();
|
|
15225
15245
|
var FOUNDRY_ARM_API2 = "2025-04-01-preview";
|
|
15226
|
-
var FOUNDRY_DATA_SCOPE2 = "https://ai.azure.com/.default";
|
|
15227
15246
|
var ARM_SCOPE5 = "https://management.azure.com/.default";
|
|
15228
15247
|
var GITHUB_APP_API = "https://api.github.com";
|
|
15229
15248
|
async function unlinkBrain(args) {
|
|
@@ -15309,10 +15328,6 @@ function parseBrainMeta(raw) {
|
|
|
15309
15328
|
}
|
|
15310
15329
|
}
|
|
15311
15330
|
async function createBrainStrippedVersion(args) {
|
|
15312
|
-
const fndToken = await args.credential.getToken(FOUNDRY_DATA_SCOPE2);
|
|
15313
|
-
if (!fndToken?.token) {
|
|
15314
|
-
throw new LocalCliError({ code: "FOUNDRY_AUTH", message: "Could not acquire Foundry data-plane token" });
|
|
15315
|
-
}
|
|
15316
15331
|
const otherTools = (args.currentDefinition.tools ?? []).filter(
|
|
15317
15332
|
(t) => !(t.type === "mcp" && t.server_label === "brain")
|
|
15318
15333
|
);
|
|
@@ -15325,28 +15340,13 @@ async function createBrainStrippedVersion(args) {
|
|
|
15325
15340
|
...args.currentMetadata,
|
|
15326
15341
|
brain: ""
|
|
15327
15342
|
};
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15343
|
+
return createAgentVersion({
|
|
15344
|
+
credential: args.credential,
|
|
15345
|
+
projectEndpoint: args.projectEndpoint,
|
|
15346
|
+
agentName: args.agentName,
|
|
15347
|
+
definition,
|
|
15348
|
+
metadata
|
|
15333
15349
|
});
|
|
15334
|
-
if (!res.ok) {
|
|
15335
|
-
const text = await res.text();
|
|
15336
|
-
throw new LocalCliError({
|
|
15337
|
-
code: "AGENT_CREATE_VERSION_FAILED",
|
|
15338
|
-
message: `POST ${url}: HTTP ${res.status.toString()}
|
|
15339
|
-
${text.slice(0, 500)}`
|
|
15340
|
-
});
|
|
15341
|
-
}
|
|
15342
|
-
const data = await res.json();
|
|
15343
|
-
if (!data.version) {
|
|
15344
|
-
throw new LocalCliError({
|
|
15345
|
-
code: "AGENT_REDEPLOY_NO_VERSION",
|
|
15346
|
-
message: "createVersion returned no version"
|
|
15347
|
-
});
|
|
15348
|
-
}
|
|
15349
|
-
return data.version;
|
|
15350
15350
|
}
|
|
15351
15351
|
async function deleteConnection(args) {
|
|
15352
15352
|
const armToken = await args.credential.getToken(ARM_SCOPE5);
|
|
@@ -15695,7 +15695,7 @@ async function awaitDataPlaneReady(opts) {
|
|
|
15695
15695
|
const consecutive = opts.consecutive ?? 3;
|
|
15696
15696
|
const attempts = opts.attempts ?? 60;
|
|
15697
15697
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
15698
|
-
const
|
|
15698
|
+
const sleep5 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15699
15699
|
let streak = 0;
|
|
15700
15700
|
for (let i = 1; i <= attempts; i++) {
|
|
15701
15701
|
const r = await opts.probe();
|
|
@@ -15717,7 +15717,7 @@ async function awaitDataPlaneReady(opts) {
|
|
|
15717
15717
|
streak = 0;
|
|
15718
15718
|
opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
|
|
15719
15719
|
}
|
|
15720
|
-
if (i < attempts) await
|
|
15720
|
+
if (i < attempts) await sleep5(intervalMs);
|
|
15721
15721
|
}
|
|
15722
15722
|
return { ready: false, attempts };
|
|
15723
15723
|
}
|
|
@@ -16801,7 +16801,7 @@ init_errors();
|
|
|
16801
16801
|
async function deployHostedWorker(args) {
|
|
16802
16802
|
const onProgress = args.onProgress ?? ((_m) => {
|
|
16803
16803
|
});
|
|
16804
|
-
const
|
|
16804
|
+
const sleep5 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
16805
16805
|
const now = args.now ?? (() => Date.now());
|
|
16806
16806
|
const timeout = args.pollTimeoutMs ?? 8 * 60 * 1e3;
|
|
16807
16807
|
const interval = args.pollIntervalMs ?? 1e4;
|
|
@@ -16853,7 +16853,7 @@ async function deployHostedWorker(args) {
|
|
|
16853
16853
|
hint: "Provisioning can take 2\u20135 min; re-run with the same name to resume, or check the version status in the Foundry portal."
|
|
16854
16854
|
});
|
|
16855
16855
|
}
|
|
16856
|
-
await
|
|
16856
|
+
await sleep5(interval);
|
|
16857
16857
|
}
|
|
16858
16858
|
}
|
|
16859
16859
|
|
|
@@ -17751,8 +17751,8 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
17751
17751
|
hint: "Pass a ghcr.io/<owner>/<name> repo, or use the BYOC private-ACR deploy flow."
|
|
17752
17752
|
});
|
|
17753
17753
|
}
|
|
17754
|
-
const
|
|
17755
|
-
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${
|
|
17754
|
+
const path38 = repo.replace(/^ghcr\.io\//, "");
|
|
17755
|
+
const tokenRes = await fetchImpl(`https://ghcr.io/token?scope=repository:${path38}:pull`);
|
|
17756
17756
|
if (!tokenRes.ok) {
|
|
17757
17757
|
throw new LocalCliError({
|
|
17758
17758
|
code: "PLATFORM_GHCR_TOKEN_FAILED",
|
|
@@ -17768,7 +17768,7 @@ async function fetchPublicTags(repo, fetchImpl = fetch) {
|
|
|
17768
17768
|
hint: "Make the package public (deploy/ghcr-package-visibility.md), then retry."
|
|
17769
17769
|
});
|
|
17770
17770
|
}
|
|
17771
|
-
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${
|
|
17771
|
+
const tagsRes = await fetchImpl(`https://ghcr.io/v2/${path38}/tags/list`, {
|
|
17772
17772
|
headers: { Authorization: `Bearer ${token}` }
|
|
17773
17773
|
});
|
|
17774
17774
|
if (!tagsRes.ok) {
|
|
@@ -19101,25 +19101,25 @@ function mergeThreeWay(ours, base, theirs) {
|
|
|
19101
19101
|
fs23.rmSync(dir, { recursive: true, force: true });
|
|
19102
19102
|
}
|
|
19103
19103
|
}
|
|
19104
|
-
var skip = (
|
|
19104
|
+
var skip = (path38, skipReason) => ({ path: path38, kind: "skip", skipReason });
|
|
19105
19105
|
function classifyCandidate(c) {
|
|
19106
|
-
const { path:
|
|
19107
|
-
if (theirs === base) return { path:
|
|
19106
|
+
const { path: path38, base, ours, theirs } = c;
|
|
19107
|
+
if (theirs === base) return { path: path38, kind: "noop" };
|
|
19108
19108
|
if (base === null) {
|
|
19109
|
-
if (theirs === null) return { path:
|
|
19110
|
-
if (ours === null) return { path:
|
|
19111
|
-
if (ours === theirs) return { path:
|
|
19112
|
-
return skip(
|
|
19109
|
+
if (theirs === null) return { path: path38, kind: "noop" };
|
|
19110
|
+
if (ours === null) return { path: path38, kind: "add", content: theirs };
|
|
19111
|
+
if (ours === theirs) return { path: path38, kind: "noop" };
|
|
19112
|
+
return skip(path38, "you already have a file at this path");
|
|
19113
19113
|
}
|
|
19114
19114
|
if (ours === null) {
|
|
19115
|
-
return theirs === null ? { path:
|
|
19115
|
+
return theirs === null ? { path: path38, kind: "noop" } : skip(path38, "you removed this file; upstream changed it");
|
|
19116
19116
|
}
|
|
19117
19117
|
if (theirs === null) {
|
|
19118
|
-
return ours === base ? { path:
|
|
19118
|
+
return ours === base ? { path: path38, kind: "delete" } : skip(path38, "you edited this file; upstream removed it");
|
|
19119
19119
|
}
|
|
19120
|
-
if (ours === base) return { path:
|
|
19120
|
+
if (ours === base) return { path: path38, kind: "update", content: theirs };
|
|
19121
19121
|
const m = mergeThreeWay(ours, base, theirs);
|
|
19122
|
-
return m.ok ? { path:
|
|
19122
|
+
return m.ok ? { path: path38, kind: "update", content: m.merged } : skip(path38, "your edits conflict with the update");
|
|
19123
19123
|
}
|
|
19124
19124
|
|
|
19125
19125
|
// src/lib/seed-pr-body.ts
|
|
@@ -19304,7 +19304,7 @@ function bindRepoApi(token, repo) {
|
|
|
19304
19304
|
openPr: (head, base, title, body) => openPullRequestViaApp({ token, repo, base, head, title, body }),
|
|
19305
19305
|
updatePr: (n, title, body) => updatePullRequestViaApp({ token, repo, number: n, title, body }),
|
|
19306
19306
|
closePr: (n, comment) => closePullRequestViaApp({ token, repo, number: n, comment }),
|
|
19307
|
-
readFileAtRef: (
|
|
19307
|
+
readFileAtRef: (path38, ref) => readRepoFileViaApp({ token, repo, path: path38, ref })
|
|
19308
19308
|
};
|
|
19309
19309
|
}
|
|
19310
19310
|
async function runSeedRefresh(args) {
|
|
@@ -21084,9 +21084,9 @@ function flattenDelta(delta, prefix = "") {
|
|
|
21084
21084
|
const out = [];
|
|
21085
21085
|
for (const d of delta) {
|
|
21086
21086
|
const segment = /^[0-9]+$/.test(d.path) ? `[${d.path}]` : prefix ? `.${d.path}` : d.path;
|
|
21087
|
-
const
|
|
21088
|
-
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children,
|
|
21089
|
-
else out.push({ ...d, path:
|
|
21087
|
+
const path38 = prefix ? `${prefix}${segment}` : d.path;
|
|
21088
|
+
if (d.children && d.children.length > 0) out.push(...flattenDelta(d.children, path38));
|
|
21089
|
+
else out.push({ ...d, path: path38 });
|
|
21090
21090
|
}
|
|
21091
21091
|
return out;
|
|
21092
21092
|
}
|
|
@@ -23243,7 +23243,7 @@ function mapItems(raw) {
|
|
|
23243
23243
|
return acc.reverse();
|
|
23244
23244
|
}
|
|
23245
23245
|
function makeConversationSource(client, opts = {}) {
|
|
23246
|
-
const
|
|
23246
|
+
const sleep5 = opts.sleep ?? defaultSleep;
|
|
23247
23247
|
return {
|
|
23248
23248
|
async items(conversationId) {
|
|
23249
23249
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
@@ -23261,7 +23261,7 @@ function makeConversationSource(client, opts = {}) {
|
|
|
23261
23261
|
throw new ConvFetchError("auth", `auth failed reading ${conversationId}`);
|
|
23262
23262
|
const retryable = status === void 0 || status === TRANSIENT;
|
|
23263
23263
|
if (retryable && attempt < MAX_ATTEMPTS - 1) {
|
|
23264
|
-
await
|
|
23264
|
+
await sleep5(attempt);
|
|
23265
23265
|
continue;
|
|
23266
23266
|
}
|
|
23267
23267
|
throw new ConvFetchError("terminal", `exhausted ${String(MAX_ATTEMPTS)} retries reading ${conversationId}: ${e?.message ?? String(e)}`);
|
|
@@ -23672,7 +23672,7 @@ function buildAdvancePlan(worker, cursor, physicalPks, consumedRowsByPk, failedT
|
|
|
23672
23672
|
// ../../packages/brain/engine/dist/esm/dream/writer.js
|
|
23673
23673
|
var API2 = "https://api.github.com";
|
|
23674
23674
|
var WRITER_ALLOWED_PREFIX = /^(memory|inbox|quarantine|artifacts)\//;
|
|
23675
|
-
var isAllowedWritePath = (
|
|
23675
|
+
var isAllowedWritePath = (path38, allowedPrefix) => allowedPrefix.test(path38) && !path38.split("/").includes("..");
|
|
23676
23676
|
var unquote = (s) => s.trim().replace(/^["']|["']$/g, "");
|
|
23677
23677
|
function parseFrontmatter(content) {
|
|
23678
23678
|
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
@@ -23715,8 +23715,8 @@ function makeGitDataWriter(args) {
|
|
|
23715
23715
|
if (args.allowedPathPrefix && !args.allowedPathPrefix.source.startsWith("^")) {
|
|
23716
23716
|
throw new Error(`makeGitDataWriter: allowedPathPrefix must be anchored with "^" (got /${args.allowedPathPrefix.source}/) \u2014 an unanchored regex would widen the B1 write-path allowlist`);
|
|
23717
23717
|
}
|
|
23718
|
-
async function gh(token, method,
|
|
23719
|
-
const res = await doFetch(`${API2}/repos/${repoPath}${
|
|
23718
|
+
async function gh(token, method, path38, body) {
|
|
23719
|
+
const res = await doFetch(`${API2}/repos/${repoPath}${path38}`, {
|
|
23720
23720
|
method,
|
|
23721
23721
|
headers: {
|
|
23722
23722
|
Authorization: `Bearer ${token}`,
|
|
@@ -23729,8 +23729,8 @@ function makeGitDataWriter(args) {
|
|
|
23729
23729
|
const text = await res.text();
|
|
23730
23730
|
return { status: res.status, ok: res.ok, json: text ? JSON.parse(text) : {} };
|
|
23731
23731
|
}
|
|
23732
|
-
async function readFileWith(token,
|
|
23733
|
-
const encodedPath =
|
|
23732
|
+
async function readFileWith(token, path38) {
|
|
23733
|
+
const encodedPath = path38.split("/").map(encodeURIComponent).join("/");
|
|
23734
23734
|
const r = await gh(token, "GET", `/contents/${encodedPath}?ref=${args.branch}`);
|
|
23735
23735
|
if (r.status === 404)
|
|
23736
23736
|
return null;
|
|
@@ -23746,9 +23746,9 @@ function makeGitDataWriter(args) {
|
|
|
23746
23746
|
throw new Error(`fetchTip: HTTP ${String(r.status)}`);
|
|
23747
23747
|
return r.json.object.sha;
|
|
23748
23748
|
},
|
|
23749
|
-
async readFile(
|
|
23749
|
+
async readFile(path38) {
|
|
23750
23750
|
const token = await args.mintToken();
|
|
23751
|
-
return readFileWith(token,
|
|
23751
|
+
return readFileWith(token, path38);
|
|
23752
23752
|
},
|
|
23753
23753
|
async listMemory() {
|
|
23754
23754
|
const token = await args.mintToken();
|
|
@@ -23757,9 +23757,9 @@ function makeGitDataWriter(args) {
|
|
|
23757
23757
|
return [];
|
|
23758
23758
|
const paths = r.json.tree.filter((e) => e.type === "blob" && e.path.startsWith("memory/") && e.path.endsWith(".md")).map((e) => e.path);
|
|
23759
23759
|
const out = [];
|
|
23760
|
-
for (const
|
|
23761
|
-
const content = await readFileWith(token,
|
|
23762
|
-
out.push({ path:
|
|
23760
|
+
for (const path38 of paths) {
|
|
23761
|
+
const content = await readFileWith(token, path38);
|
|
23762
|
+
out.push({ path: path38, frontmatter: content ? parseFrontmatter(content) : {} });
|
|
23763
23763
|
}
|
|
23764
23764
|
return out;
|
|
23765
23765
|
},
|
|
@@ -24063,7 +24063,7 @@ function extractJson(text) {
|
|
|
24063
24063
|
return JSON.parse(candidate2.slice(start, end + 1));
|
|
24064
24064
|
}
|
|
24065
24065
|
async function propose(model, system, user, opts = {}) {
|
|
24066
|
-
const
|
|
24066
|
+
const sleep5 = opts.sleep ?? defaultSleep2;
|
|
24067
24067
|
const modelName = opts.model ?? MODEL_NAME_FALLBACK;
|
|
24068
24068
|
let inputTokens = 0;
|
|
24069
24069
|
let outputTokens = 0;
|
|
@@ -24079,7 +24079,7 @@ async function propose(model, system, user, opts = {}) {
|
|
|
24079
24079
|
} catch (e) {
|
|
24080
24080
|
lastErr = `parse: ${e.message}`;
|
|
24081
24081
|
if (attempt < MAX_MODEL_ATTEMPTS - 1) {
|
|
24082
|
-
await
|
|
24082
|
+
await sleep5(attempt);
|
|
24083
24083
|
continue;
|
|
24084
24084
|
}
|
|
24085
24085
|
break;
|
|
@@ -24088,7 +24088,7 @@ async function propose(model, system, user, opts = {}) {
|
|
|
24088
24088
|
if (!Array.isArray(rawDeltas)) {
|
|
24089
24089
|
lastErr = "schema: top-level { deltas: [] } missing";
|
|
24090
24090
|
if (attempt < MAX_MODEL_ATTEMPTS - 1) {
|
|
24091
|
-
await
|
|
24091
|
+
await sleep5(attempt);
|
|
24092
24092
|
continue;
|
|
24093
24093
|
}
|
|
24094
24094
|
break;
|
|
@@ -24198,7 +24198,7 @@ async function loadMemoryContext(brain) {
|
|
|
24198
24198
|
return {
|
|
24199
24199
|
files,
|
|
24200
24200
|
indexEntries,
|
|
24201
|
-
originOf: (
|
|
24201
|
+
originOf: (path38) => originByPath.get(path38) ?? "worker"
|
|
24202
24202
|
};
|
|
24203
24203
|
}
|
|
24204
24204
|
|
|
@@ -24219,14 +24219,14 @@ function checkEvidenceMembership(delta, harvested) {
|
|
|
24219
24219
|
}
|
|
24220
24220
|
var BatchAbort = class extends Error {
|
|
24221
24221
|
path;
|
|
24222
|
-
constructor(
|
|
24222
|
+
constructor(path38, message) {
|
|
24223
24223
|
super(message);
|
|
24224
|
-
this.path =
|
|
24224
|
+
this.path = path38;
|
|
24225
24225
|
this.name = "BatchAbort";
|
|
24226
24226
|
}
|
|
24227
24227
|
};
|
|
24228
|
-
var isIndexFile = (
|
|
24229
|
-
var isMemoryIndex = (
|
|
24228
|
+
var isIndexFile = (path38) => /(^|\/)[^/]*_index\.md$/.test(path38);
|
|
24229
|
+
var isMemoryIndex = (path38) => path38 === "memory/MEMORY.md";
|
|
24230
24230
|
function targetPath(delta) {
|
|
24231
24231
|
switch (delta.verb) {
|
|
24232
24232
|
case "new":
|
|
@@ -24243,29 +24243,29 @@ function targetPath(delta) {
|
|
|
24243
24243
|
}
|
|
24244
24244
|
async function checkOriginTier(delta, lookup) {
|
|
24245
24245
|
const evidence = evidenceOf(delta) ?? [];
|
|
24246
|
-
const
|
|
24247
|
-
if (
|
|
24246
|
+
const path38 = targetPath(delta);
|
|
24247
|
+
if (path38 === null)
|
|
24248
24248
|
return { ok: true };
|
|
24249
|
-
if (isIndexFile(
|
|
24250
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
24249
|
+
if (isIndexFile(path38) && !isMemoryIndex(path38)) {
|
|
24250
|
+
return { ok: false, flagged: { kind: "flagged", path: path38, tension: `refused: ${path38} is an index file (only memory/MEMORY.md is editable)`, evidence } };
|
|
24251
24251
|
}
|
|
24252
|
-
const target = await lookup(
|
|
24253
|
-
const isStructural =
|
|
24252
|
+
const target = await lookup(path38);
|
|
24253
|
+
const isStructural = path38.startsWith("references/");
|
|
24254
24254
|
const origin = target?.frontmatter.origin ?? (isStructural ? "operator" : "worker");
|
|
24255
24255
|
const editable = origin === "worker" || origin === "dream";
|
|
24256
24256
|
if (editable)
|
|
24257
24257
|
return { ok: true };
|
|
24258
24258
|
const removesOperatorTarget = delta.verb === "retract" || delta.verb === "supersede" && delta.oldPath !== delta.newPath;
|
|
24259
24259
|
if (removesOperatorTarget) {
|
|
24260
|
-
throw new BatchAbort(
|
|
24260
|
+
throw new BatchAbort(path38, `supersede/retract OLD target ${path38} is origin: ${origin} \u2014 aborting batch (never half-apply)`);
|
|
24261
24261
|
}
|
|
24262
|
-
return { ok: false, flagged: { kind: "flagged", path:
|
|
24262
|
+
return { ok: false, flagged: { kind: "flagged", path: path38, tension: `operator-origin (${origin}); flag-only`, evidence } };
|
|
24263
24263
|
}
|
|
24264
24264
|
var ALLOWED_MEMORY = /^memory\/.+\.md$/;
|
|
24265
24265
|
var ALLOWED_INBOX = /^inbox\//;
|
|
24266
24266
|
var ALLOWED_QUARANTINE = /^quarantine\//;
|
|
24267
24267
|
var ALLOWED_FLAG = /^memory\//;
|
|
24268
|
-
var hasTraversal = (
|
|
24268
|
+
var hasTraversal = (path38) => path38.split("/").includes("..");
|
|
24269
24269
|
function checkPathAllowed(delta) {
|
|
24270
24270
|
const evidence = evidenceOf(delta) ?? [];
|
|
24271
24271
|
const reject = (detail) => ({ ok: false, rejected: { kind: "rejected", detail, evidence } });
|
|
@@ -24372,7 +24372,7 @@ ${inject}
|
|
|
24372
24372
|
---
|
|
24373
24373
|
` + body.slice(m[0].length);
|
|
24374
24374
|
}
|
|
24375
|
-
var indexLine = (
|
|
24375
|
+
var indexLine = (path38, title, date, tags) => `- \`${path38}\` \u2014 **${title}**: ${title}. (${date} \xB7 ${tags.join(", ")})`;
|
|
24376
24376
|
function splitIndex(index) {
|
|
24377
24377
|
const all = index.split("\n");
|
|
24378
24378
|
const firstLineIdx = all.findIndex((l) => l.startsWith("- `memory/"));
|
|
@@ -24545,16 +24545,16 @@ function defaultDreamSeams(opts = {}) {
|
|
|
24545
24545
|
const mem = await memory(deps.brain);
|
|
24546
24546
|
const harvested = harvestedIds(input);
|
|
24547
24547
|
const ctx = { readFile: (p) => deps.brain.readFile(p), at: deps.clock() };
|
|
24548
|
-
const lookup = (
|
|
24548
|
+
const lookup = (path38) => Promise.resolve(mem.files.find((f) => f.path === path38) ?? null);
|
|
24549
24549
|
const digest = [...pendingRejected];
|
|
24550
24550
|
let changes = [];
|
|
24551
24551
|
let indexEdit;
|
|
24552
24552
|
try {
|
|
24553
24553
|
for (const raw of deltas) {
|
|
24554
24554
|
const delta = forcePolicyQuarantine(raw);
|
|
24555
|
-
const
|
|
24556
|
-
if (!
|
|
24557
|
-
digest.push(
|
|
24555
|
+
const path38 = checkPathAllowed(delta);
|
|
24556
|
+
if (!path38.ok) {
|
|
24557
|
+
digest.push(path38.rejected);
|
|
24558
24558
|
continue;
|
|
24559
24559
|
}
|
|
24560
24560
|
const ev = checkEvidenceMembership(delta, harvested);
|
|
@@ -25668,13 +25668,13 @@ var EVENT_TIERS = {
|
|
|
25668
25668
|
var EVENT_ENUM = Object.keys(EVENT_TIERS);
|
|
25669
25669
|
|
|
25670
25670
|
// ../../packages/telemetry-contract/src/disclosure.ts
|
|
25671
|
-
var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t receives limited operational data linked to it \u2014 installation and update events, installed versions, and service health signals
|
|
25671
|
+
var DISCLOSURE_TIER1 = "Operational data. To support your installation, m8t receives limited operational data linked to it \u2014 installation and update events, installed versions, and service health signals. Your installation is identified only by a random installation ID we generate at setup \u2014 an opaque value with no information encoded in it. No name, email, company, or subscription information is sent in the enrollment record unless you explicitly choose to share contact details for support. At enrollment, we also verify a cloud identity token to confirm the request comes from a real cloud account; that token names the account making the request, and is otherwise discarded immediately and never stored. This is service data used only to operate and support the platform; it contains none of your content. Details: TELEMETRY.md.";
|
|
25672
25672
|
|
|
25673
25673
|
// ../../packages/telemetry-contract/src/endpoints.ts
|
|
25674
25674
|
var INGEST_BASE_URL = (process.env.M8T_INGEST_BASE_URL ?? "").trim() || "https://m8t-admin.wonderfuldesert-721e332f.eastus2.azurecontainerapps.io";
|
|
25675
|
-
function ingestUrl(
|
|
25675
|
+
function ingestUrl(path38) {
|
|
25676
25676
|
const base = INGEST_BASE_URL.replace(/\/+$/, "");
|
|
25677
|
-
return `${base}${
|
|
25677
|
+
return `${base}${path38.startsWith("/") ? path38 : `/${path38}`}`;
|
|
25678
25678
|
}
|
|
25679
25679
|
|
|
25680
25680
|
// src/commands/bootstrap/preflight.ts
|
|
@@ -25928,6 +25928,7 @@ function buildAciCreateArgs(s) {
|
|
|
25928
25928
|
if (s.foundryTracing) env.push(`FOUNDRY_TRACING=${s.foundryTracing}`);
|
|
25929
25929
|
if (s.updateChannelUrl) env.push(`M8T_UPDATE_CHANNEL_URL=${s.updateChannelUrl}`);
|
|
25930
25930
|
if (s.enrollContactEmail) env.push(`M8T_ENROLL_CONTACT_EMAIL=${s.enrollContactEmail}`);
|
|
25931
|
+
if (s.enrollCompany) env.push(`M8T_ENROLL_COMPANY=${s.enrollCompany}`);
|
|
25931
25932
|
if (s.githubApp) {
|
|
25932
25933
|
const g = s.githubApp;
|
|
25933
25934
|
env.push(
|
|
@@ -26078,7 +26079,7 @@ async function writeBootstrapState(state, home = os13.homedir()) {
|
|
|
26078
26079
|
// src/commands/bootstrap/launch.ts
|
|
26079
26080
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
26080
26081
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
26081
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
26082
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.41";
|
|
26082
26083
|
var ACI_NAME = "m8t-installer";
|
|
26083
26084
|
var MI_NAME = "m8t-installer-mi";
|
|
26084
26085
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
@@ -26090,7 +26091,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26090
26091
|
["Launch in eastus2", "$0 bootstrap launch --location eastus2"],
|
|
26091
26092
|
["BYO app registration", "$0 bootstrap launch --location eastus2 --client-id <appId>"],
|
|
26092
26093
|
["Pin a specific installer tag", "$0 bootstrap launch --location eastus2 --installer-tag v0.1.33"],
|
|
26093
|
-
["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.33"]
|
|
26094
|
+
["Override the full installer image ref", "$0 bootstrap launch --location eastus2 --installer-image ghcr.io/m8t-labs/m8t-installer:v0.1.33"],
|
|
26095
|
+
["Share a support contact", "$0 bootstrap launch --location eastus2 --contact-email you@example.com --company 'Acme'"]
|
|
26094
26096
|
]
|
|
26095
26097
|
});
|
|
26096
26098
|
location = Option47.String("--location");
|
|
@@ -26104,6 +26106,8 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26104
26106
|
installerImage = Option47.String("--installer-image");
|
|
26105
26107
|
gatewayImageRef = Option47.String("--gateway-image-ref");
|
|
26106
26108
|
githubAppCreds = Option47.String("--github-app-creds");
|
|
26109
|
+
contactEmail = Option47.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
26110
|
+
company = Option47.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
26107
26111
|
async executeCommand() {
|
|
26108
26112
|
const location = typeof this.location === "string" ? this.location : void 0;
|
|
26109
26113
|
if (!location) {
|
|
@@ -26195,7 +26199,11 @@ var BootstrapLaunchCommand = class extends M8tCommand {
|
|
|
26195
26199
|
gatewayImageRef,
|
|
26196
26200
|
foundryTracing: "skip",
|
|
26197
26201
|
githubApp,
|
|
26198
|
-
|
|
26202
|
+
// Opt-in only. The signed-in UPN was previously seeded here automatically;
|
|
26203
|
+
// an installation is identified by its random instance id, and contact
|
|
26204
|
+
// details are sent only when the operator asks for them.
|
|
26205
|
+
...typeof this.contactEmail === "string" ? { enrollContactEmail: this.contactEmail } : {},
|
|
26206
|
+
...typeof this.company === "string" ? { enrollCompany: this.company } : {},
|
|
26199
26207
|
...process.env.M8T_UPDATE_CHANNEL_URL ? { updateChannelUrl: process.env.M8T_UPDATE_CHANNEL_URL } : {}
|
|
26200
26208
|
});
|
|
26201
26209
|
this.context.stdout.write(
|
|
@@ -26256,7 +26264,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
26256
26264
|
typeof this.output === "string" ? this.output : void 0,
|
|
26257
26265
|
this.context.stdout
|
|
26258
26266
|
);
|
|
26259
|
-
const
|
|
26267
|
+
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
26260
26268
|
const watch = this.watch === true;
|
|
26261
26269
|
for (; ; ) {
|
|
26262
26270
|
let doc;
|
|
@@ -26266,7 +26274,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
26266
26274
|
if (watch && e instanceof LocalCliError && e.code === "BOOTSTRAP_STATUS_UNREADABLE") {
|
|
26267
26275
|
if (mode === "pretty") this.context.stderr.write(` ${colors.dim("waiting for the installer to start\u2026")}
|
|
26268
26276
|
`);
|
|
26269
|
-
await
|
|
26277
|
+
await sleep5(1e4);
|
|
26270
26278
|
continue;
|
|
26271
26279
|
}
|
|
26272
26280
|
throw e;
|
|
@@ -26291,7 +26299,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
26291
26299
|
);
|
|
26292
26300
|
return 1;
|
|
26293
26301
|
}
|
|
26294
|
-
await
|
|
26302
|
+
await sleep5(1e4);
|
|
26295
26303
|
}
|
|
26296
26304
|
}
|
|
26297
26305
|
};
|
|
@@ -27659,9 +27667,9 @@ async function serveOnboardingUiDetached(args) {
|
|
|
27659
27667
|
child.unref();
|
|
27660
27668
|
fs32.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
27661
27669
|
const deadline = Date.now() + 45e3;
|
|
27662
|
-
const
|
|
27670
|
+
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
27663
27671
|
while (Date.now() < deadline) {
|
|
27664
|
-
await
|
|
27672
|
+
await sleep5(500);
|
|
27665
27673
|
if (await isPortOpen()) break;
|
|
27666
27674
|
}
|
|
27667
27675
|
return { alreadyRunning: false, logPath };
|
|
@@ -27719,9 +27727,9 @@ async function serveOnboardingRelayDetached(args) {
|
|
|
27719
27727
|
child.unref();
|
|
27720
27728
|
fs32.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
27721
27729
|
const deadline = Date.now() + 45e3;
|
|
27722
|
-
const
|
|
27730
|
+
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
27723
27731
|
while (Date.now() < deadline) {
|
|
27724
|
-
await
|
|
27732
|
+
await sleep5(500);
|
|
27725
27733
|
if (await isLocalPortOpen(portNum)) break;
|
|
27726
27734
|
}
|
|
27727
27735
|
return { alreadyRunning: false, logPath };
|
|
@@ -27978,7 +27986,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
27978
27986
|
const parsedTimeout = Number(rawTimeout);
|
|
27979
27987
|
const timeoutMin = rawTimeout !== "" && Number.isFinite(parsedTimeout) ? parsedTimeout : 20;
|
|
27980
27988
|
const deadline = Date.now() + timeoutMin * 6e4;
|
|
27981
|
-
const
|
|
27989
|
+
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
27982
27990
|
for (; ; ) {
|
|
27983
27991
|
const token = await getFoundryToken();
|
|
27984
27992
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
|
|
@@ -28006,33 +28014,60 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
28006
28014
|
}
|
|
28007
28015
|
this.context.stderr.write(` ${colors.dim("waiting for the questionnaire to complete\u2026")}
|
|
28008
28016
|
`);
|
|
28009
|
-
await
|
|
28017
|
+
await sleep5(2e4);
|
|
28010
28018
|
}
|
|
28011
28019
|
}
|
|
28012
28020
|
};
|
|
28013
28021
|
|
|
28014
28022
|
// src/commands/telemetry/enroll.ts
|
|
28023
|
+
import * as fs34 from "fs";
|
|
28024
|
+
import * as os19 from "os";
|
|
28025
|
+
import * as path37 from "path";
|
|
28015
28026
|
import { Command as Command56, Option as Option53 } from "clipanion";
|
|
28016
28027
|
init_errors();
|
|
28017
28028
|
|
|
28018
28029
|
// src/lib/telemetry-enroll.ts
|
|
28019
28030
|
init_errors();
|
|
28031
|
+
async function defaultToken() {
|
|
28032
|
+
const out = await runAz([
|
|
28033
|
+
"account",
|
|
28034
|
+
"get-access-token",
|
|
28035
|
+
"--resource",
|
|
28036
|
+
"https://management.azure.com/",
|
|
28037
|
+
"--query",
|
|
28038
|
+
"accessToken",
|
|
28039
|
+
"-o",
|
|
28040
|
+
"tsv"
|
|
28041
|
+
]);
|
|
28042
|
+
const token = out.trim();
|
|
28043
|
+
return token.length > 0 ? token : null;
|
|
28044
|
+
}
|
|
28045
|
+
var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
28020
28046
|
async function enroll(args) {
|
|
28021
28047
|
const f = args.fetchImpl ?? fetch;
|
|
28022
28048
|
const url = args.baseUrl ? `${args.baseUrl.replace(/\/+$/, "")}/api/ingest/enroll` : ingestUrl("/api/ingest/enroll");
|
|
28049
|
+
const retryDelayMs = args.retryDelayMs ?? 1e3;
|
|
28050
|
+
const token = await (args.tokenImpl ?? defaultToken)().catch(() => null);
|
|
28051
|
+
const payload = { schemaVersion: 1 };
|
|
28052
|
+
const company = args.company?.trim();
|
|
28053
|
+
if (company) payload.company = company;
|
|
28054
|
+
const contactEmail = args.contactEmail?.trim();
|
|
28055
|
+
if (contactEmail) payload.contactEmail = contactEmail;
|
|
28056
|
+
payload.proof = token ? { type: "entra-jwt", token } : null;
|
|
28057
|
+
const body = JSON.stringify(payload);
|
|
28023
28058
|
let res;
|
|
28024
|
-
|
|
28025
|
-
|
|
28026
|
-
method: "POST",
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
}
|
|
28030
|
-
|
|
28031
|
-
|
|
28059
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
28060
|
+
try {
|
|
28061
|
+
res = await f(url, { method: "POST", headers: { "content-type": "application/json" }, body });
|
|
28062
|
+
} catch (e) {
|
|
28063
|
+
throw new LocalCliError({ code: "TELEMETRY_ENROLL_UNREACHABLE", message: `Could not reach the telemetry ingest to enroll: ${e instanceof Error ? e.message : String(e)}` });
|
|
28064
|
+
}
|
|
28065
|
+
if (res.status !== 503 || attempt === 3) break;
|
|
28066
|
+
await sleep4(retryDelayMs * attempt);
|
|
28032
28067
|
}
|
|
28033
|
-
if (!res
|
|
28034
|
-
const
|
|
28035
|
-
throw new LocalCliError({ code: "TELEMETRY_ENROLL_FAILED", message: `Enroll failed (HTTP ${res.status.toString()}): ${
|
|
28068
|
+
if (!res?.ok) {
|
|
28069
|
+
const detail = res ? await res.text().catch(() => "") : "";
|
|
28070
|
+
throw new LocalCliError({ code: "TELEMETRY_ENROLL_FAILED", message: `Enroll failed (HTTP ${res ? res.status.toString() : "?"}): ${detail}` });
|
|
28036
28071
|
}
|
|
28037
28072
|
const json = await res.json();
|
|
28038
28073
|
if (!json.instanceId || !json.ingestKey) {
|
|
@@ -28072,19 +28107,22 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
28072
28107
|
static paths = [["telemetry", "enroll"]];
|
|
28073
28108
|
static usage = Command56.Usage({
|
|
28074
28109
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
28075
|
-
details: "
|
|
28076
|
-
examples: [
|
|
28110
|
+
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.",
|
|
28111
|
+
examples: [
|
|
28112
|
+
["Enroll", "$0 telemetry enroll"],
|
|
28113
|
+
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
28114
|
+
]
|
|
28077
28115
|
});
|
|
28078
|
-
company = Option53.String("--company", { description: "
|
|
28079
|
-
contactEmail = Option53.String("--contact-email", { description: "
|
|
28080
|
-
subscription = Option53.String("--subscription", { description: "Azure subscription id
|
|
28116
|
+
company = Option53.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
28117
|
+
contactEmail = Option53.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
28118
|
+
subscription = Option53.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
28081
28119
|
resourceGroup = Option53.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
28082
|
-
force = Option53.Boolean("--force", false, { description: "
|
|
28120
|
+
force = Option53.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
28083
28121
|
async executeCommand() {
|
|
28084
28122
|
const account = await getAzAccount();
|
|
28085
28123
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
28086
|
-
const contactEmail =
|
|
28087
|
-
const company =
|
|
28124
|
+
const contactEmail = typeof this.contactEmail === "string" ? this.contactEmail : void 0;
|
|
28125
|
+
const company = typeof this.company === "string" ? this.company : void 0;
|
|
28088
28126
|
const d = await discoverGateway({
|
|
28089
28127
|
subscriptionId,
|
|
28090
28128
|
interactive: false,
|
|
@@ -28100,15 +28138,26 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
28100
28138
|
existing = "";
|
|
28101
28139
|
}
|
|
28102
28140
|
if (existing.trim()) {
|
|
28141
|
+
const wantedContact = contactEmail !== void 0 || company !== void 0;
|
|
28103
28142
|
throw new LocalCliError({
|
|
28104
28143
|
code: "TELEMETRY_ALREADY_ENROLLED",
|
|
28105
|
-
message: "This installation is already enrolled.",
|
|
28106
|
-
hint: "
|
|
28144
|
+
message: wantedContact ? "This installation is already enrolled, so contact details can't be attached from here." : "This installation is already enrolled.",
|
|
28145
|
+
hint: "To add or change the contact details on your record, reach out through the channels in SUPPORT.md and we'll update it for you."
|
|
28107
28146
|
});
|
|
28108
28147
|
}
|
|
28109
28148
|
}
|
|
28110
|
-
const { instanceId, ingestKey } = await enroll({
|
|
28111
|
-
|
|
28149
|
+
const { instanceId, ingestKey } = await enroll({
|
|
28150
|
+
...company ? { company } : {},
|
|
28151
|
+
...contactEmail ? { contactEmail } : {}
|
|
28152
|
+
});
|
|
28153
|
+
const keyDir = fs34.mkdtempSync(path37.join(os19.tmpdir(), "m8t-ingest-key-"));
|
|
28154
|
+
const keyFile = path37.join(keyDir, "key");
|
|
28155
|
+
try {
|
|
28156
|
+
fs34.writeFileSync(keyFile, ingestKey, { mode: 384 });
|
|
28157
|
+
await runAz(["keyvault", "secret", "set", "--vault-name", kvName, "--name", INGEST_KEY_SECRET, "--file", keyFile, "--only-show-errors"]);
|
|
28158
|
+
} finally {
|
|
28159
|
+
fs34.rmSync(keyDir, { recursive: true, force: true });
|
|
28160
|
+
}
|
|
28112
28161
|
this.context.stdout.write(
|
|
28113
28162
|
`${colors.success("\u2713")} enrolled (instance ${instanceId}); ingest key stored in Key Vault ${kvName}. The gateway picks it up on its next cycle.
|
|
28114
28163
|
`
|