@m8t-stack/cli 0.2.84 → 0.2.86
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 +822 -1418
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -10,11 +10,6 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/lib/errors.ts
|
|
13
|
-
var errors_exports = {};
|
|
14
|
-
__export(errors_exports, {
|
|
15
|
-
ApiCallError: () => ApiCallError,
|
|
16
|
-
LocalCliError: () => LocalCliError
|
|
17
|
-
});
|
|
18
13
|
var LocalCliError, ApiCallError;
|
|
19
14
|
var init_errors = __esm({
|
|
20
15
|
"src/lib/errors.ts"() {
|
|
@@ -1467,7 +1462,7 @@ var init_enable_hosted_brain = __esm({
|
|
|
1467
1462
|
import { Builtins, Cli } from "clipanion";
|
|
1468
1463
|
|
|
1469
1464
|
// src/lib/package-version.ts
|
|
1470
|
-
var CLI_VERSION = "0.2.
|
|
1465
|
+
var CLI_VERSION = "0.2.86";
|
|
1471
1466
|
|
|
1472
1467
|
// src/lib/render-error.ts
|
|
1473
1468
|
init_errors();
|
|
@@ -9940,17 +9935,17 @@ var Files2 = class extends APIResource {
|
|
|
9940
9935
|
async waitForProcessing(id, { pollInterval = 5e3, maxWait = 30 * 60 * 1e3 } = {}) {
|
|
9941
9936
|
const TERMINAL_STATES = /* @__PURE__ */ new Set(["processed", "error", "deleted"]);
|
|
9942
9937
|
const start = Date.now();
|
|
9943
|
-
let
|
|
9944
|
-
while (!
|
|
9938
|
+
let file2 = await this.retrieve(id);
|
|
9939
|
+
while (!file2.status || !TERMINAL_STATES.has(file2.status)) {
|
|
9945
9940
|
await sleep(pollInterval);
|
|
9946
|
-
|
|
9941
|
+
file2 = await this.retrieve(id);
|
|
9947
9942
|
if (Date.now() - start > maxWait) {
|
|
9948
9943
|
throw new APIConnectionTimeoutError({
|
|
9949
9944
|
message: `Giving up on waiting for file ${id} to finish processing after ${maxWait} milliseconds.`
|
|
9950
9945
|
});
|
|
9951
9946
|
}
|
|
9952
9947
|
}
|
|
9953
|
-
return
|
|
9948
|
+
return file2;
|
|
9954
9949
|
}
|
|
9955
9950
|
};
|
|
9956
9951
|
|
|
@@ -11742,8 +11737,8 @@ var Files3 = class extends APIResource {
|
|
|
11742
11737
|
* Attach a file to the given vector store and wait for it to be processed.
|
|
11743
11738
|
*/
|
|
11744
11739
|
async createAndPoll(vectorStoreId, body, options) {
|
|
11745
|
-
const
|
|
11746
|
-
return await this.poll(vectorStoreId,
|
|
11740
|
+
const file2 = await this.create(vectorStoreId, body, options);
|
|
11741
|
+
return await this.poll(vectorStoreId, file2.id, options);
|
|
11747
11742
|
}
|
|
11748
11743
|
/**
|
|
11749
11744
|
* Wait for the vector store file to finish processing.
|
|
@@ -11763,8 +11758,8 @@ var Files3 = class extends APIResource {
|
|
|
11763
11758
|
const fileResponse = await this.retrieve(fileID, {
|
|
11764
11759
|
vector_store_id: vectorStoreID
|
|
11765
11760
|
}, { ...options, headers }).withResponse();
|
|
11766
|
-
const
|
|
11767
|
-
switch (
|
|
11761
|
+
const file2 = fileResponse.data;
|
|
11762
|
+
switch (file2.status) {
|
|
11768
11763
|
case "in_progress":
|
|
11769
11764
|
let sleepInterval = 5e3;
|
|
11770
11765
|
if (options?.pollIntervalMs) {
|
|
@@ -11782,7 +11777,7 @@ var Files3 = class extends APIResource {
|
|
|
11782
11777
|
break;
|
|
11783
11778
|
case "failed":
|
|
11784
11779
|
case "completed":
|
|
11785
|
-
return
|
|
11780
|
+
return file2;
|
|
11786
11781
|
}
|
|
11787
11782
|
}
|
|
11788
11783
|
}
|
|
@@ -11792,15 +11787,15 @@ var Files3 = class extends APIResource {
|
|
|
11792
11787
|
* Note the file will be asynchronously processed (you can use the alternative
|
|
11793
11788
|
* polling helper method to wait for processing to complete).
|
|
11794
11789
|
*/
|
|
11795
|
-
async upload(vectorStoreId,
|
|
11796
|
-
const fileInfo = await this._client.files.create({ file, purpose: "assistants" }, options);
|
|
11790
|
+
async upload(vectorStoreId, file2, options) {
|
|
11791
|
+
const fileInfo = await this._client.files.create({ file: file2, purpose: "assistants" }, options);
|
|
11797
11792
|
return this.create(vectorStoreId, { file_id: fileInfo.id }, options);
|
|
11798
11793
|
}
|
|
11799
11794
|
/**
|
|
11800
11795
|
* Add a file to a vector store and poll until processing is complete.
|
|
11801
11796
|
*/
|
|
11802
|
-
async uploadAndPoll(vectorStoreId,
|
|
11803
|
-
const fileInfo = await this.upload(vectorStoreId,
|
|
11797
|
+
async uploadAndPoll(vectorStoreId, file2, options) {
|
|
11798
|
+
const fileInfo = await this.upload(vectorStoreId, file2, options);
|
|
11804
11799
|
return await this.poll(vectorStoreId, fileInfo.id, options);
|
|
11805
11800
|
}
|
|
11806
11801
|
/**
|
|
@@ -13205,9 +13200,9 @@ async function readConfig() {
|
|
|
13205
13200
|
}
|
|
13206
13201
|
}
|
|
13207
13202
|
async function writeConfig(cfg) {
|
|
13208
|
-
const
|
|
13203
|
+
const dir2 = configDir();
|
|
13209
13204
|
const configPath = getConfigPath();
|
|
13210
|
-
await fs.mkdir(
|
|
13205
|
+
await fs.mkdir(dir2, { recursive: true });
|
|
13211
13206
|
const yaml = stringifyYaml(cfg, { indent: 2 });
|
|
13212
13207
|
const tmp = `${configPath}.tmp`;
|
|
13213
13208
|
await fs.writeFile(tmp, yaml, "utf8");
|
|
@@ -14676,11 +14671,11 @@ function orgFileSegment(org) {
|
|
|
14676
14671
|
return org.replace(/[^A-Za-z0-9-]+/g, "-");
|
|
14677
14672
|
}
|
|
14678
14673
|
function brainAppFilePaths(home, org, appId) {
|
|
14679
|
-
const
|
|
14674
|
+
const dir2 = path4.join(home, ".m8t");
|
|
14680
14675
|
const stem = `github-app-${orgFileSegment(org)}-${appId}`;
|
|
14681
14676
|
return {
|
|
14682
|
-
pemPath: path4.join(
|
|
14683
|
-
sidecarPath: path4.join(
|
|
14677
|
+
pemPath: path4.join(dir2, `${stem}.pem`),
|
|
14678
|
+
sidecarPath: path4.join(dir2, `${stem}.json`)
|
|
14684
14679
|
};
|
|
14685
14680
|
}
|
|
14686
14681
|
function isPreservable(v) {
|
|
@@ -15290,15 +15285,15 @@ function agentYamlPath(agentName, home = os3.homedir()) {
|
|
|
15290
15285
|
return path7.join(home, ".m8t", "foundry", `${agentName}.yaml`);
|
|
15291
15286
|
}
|
|
15292
15287
|
function readAgentYaml(agentName, home) {
|
|
15293
|
-
const
|
|
15294
|
-
if (!fs6.existsSync(
|
|
15295
|
-
const text = fs6.readFileSync(
|
|
15288
|
+
const file2 = agentYamlPath(agentName, home);
|
|
15289
|
+
if (!fs6.existsSync(file2)) return null;
|
|
15290
|
+
const text = fs6.readFileSync(file2, "utf8");
|
|
15296
15291
|
return parseYaml3(text);
|
|
15297
15292
|
}
|
|
15298
15293
|
function writeAgentYaml(agentName, data, home) {
|
|
15299
|
-
const
|
|
15300
|
-
fs6.mkdirSync(path7.dirname(
|
|
15301
|
-
fs6.writeFileSync(
|
|
15294
|
+
const file2 = agentYamlPath(agentName, home);
|
|
15295
|
+
fs6.mkdirSync(path7.dirname(file2), { recursive: true });
|
|
15296
|
+
fs6.writeFileSync(file2, stringifyYaml3(data), { mode: 384 });
|
|
15302
15297
|
}
|
|
15303
15298
|
function patchAgentYaml(agentName, patch, home) {
|
|
15304
15299
|
const current = readAgentYaml(agentName, home);
|
|
@@ -15881,9 +15876,9 @@ var AGENT_DIR = "agent";
|
|
|
15881
15876
|
var AGENT_SEED_DIRS = [...SEED_PATHS, "memory"];
|
|
15882
15877
|
var AGENT_SEED_FILES = ["NOTICE"];
|
|
15883
15878
|
var PERSONA_REL = `${AGENT_DIR}/persona.md`;
|
|
15884
|
-
function walk(base,
|
|
15885
|
-
for (const entry of fs11.readdirSync(
|
|
15886
|
-
const abs = path11.join(
|
|
15879
|
+
function walk(base, dir2, out) {
|
|
15880
|
+
for (const entry of fs11.readdirSync(dir2, { withFileTypes: true })) {
|
|
15881
|
+
const abs = path11.join(dir2, entry.name);
|
|
15887
15882
|
if (entry.isDirectory()) walk(base, abs, out);
|
|
15888
15883
|
else if (entry.isFile()) out.push(path11.relative(base, abs).split(path11.sep).join("/"));
|
|
15889
15884
|
}
|
|
@@ -15899,13 +15894,13 @@ function collectAgentContent(repoDir) {
|
|
|
15899
15894
|
}
|
|
15900
15895
|
const rels = [];
|
|
15901
15896
|
walk(repoDir, path11.join(repoDir, AGENT_DIR), rels);
|
|
15902
|
-
for (const
|
|
15903
|
-
const abs = path11.join(repoDir,
|
|
15897
|
+
for (const dir2 of AGENT_SEED_DIRS) {
|
|
15898
|
+
const abs = path11.join(repoDir, dir2);
|
|
15904
15899
|
if (fs11.existsSync(abs) && fs11.statSync(abs).isDirectory()) walk(repoDir, abs, rels);
|
|
15905
15900
|
}
|
|
15906
|
-
for (const
|
|
15907
|
-
const abs = path11.join(repoDir,
|
|
15908
|
-
if (fs11.existsSync(abs) && fs11.statSync(abs).isFile()) rels.push(
|
|
15901
|
+
for (const file2 of AGENT_SEED_FILES) {
|
|
15902
|
+
const abs = path11.join(repoDir, file2);
|
|
15903
|
+
if (fs11.existsSync(abs) && fs11.statSync(abs).isFile()) rels.push(file2);
|
|
15909
15904
|
}
|
|
15910
15905
|
const files = /* @__PURE__ */ new Map();
|
|
15911
15906
|
for (const rel of rels) files.set(rel, fs11.readFileSync(path11.join(repoDir, rel)));
|
|
@@ -16161,8 +16156,8 @@ async function derivePin(args) {
|
|
|
16161
16156
|
// src/lib/brain-seed.ts
|
|
16162
16157
|
function resolveSeed(name) {
|
|
16163
16158
|
const root = resolvePlatformRepoRoot("Seeding a brain", ["brain-seeds"]);
|
|
16164
|
-
const
|
|
16165
|
-
if (!fs13.existsSync(
|
|
16159
|
+
const dir2 = path13.join(root, "brain-seeds", name);
|
|
16160
|
+
if (!fs13.existsSync(dir2)) {
|
|
16166
16161
|
throw new LocalCliError({
|
|
16167
16162
|
code: "SEED_NOT_FOUND",
|
|
16168
16163
|
message: `--seed "${name}": no brain-seeds/${name}/ directory found.`,
|
|
@@ -16172,7 +16167,7 @@ function resolveSeed(name) {
|
|
|
16172
16167
|
hint: agentContentHint(root, name) ?? "Seeds live under brain-seeds/. Check the name, or omit --seed for an unseeded brain."
|
|
16173
16168
|
});
|
|
16174
16169
|
}
|
|
16175
|
-
return
|
|
16170
|
+
return dir2;
|
|
16176
16171
|
}
|
|
16177
16172
|
function materializeBrainTree(opts) {
|
|
16178
16173
|
fs13.cpSync(opts.templateSrc, opts.destDir, { recursive: true });
|
|
@@ -16657,20 +16652,20 @@ function resolveScaffoldCommit(repoRoot, env) {
|
|
|
16657
16652
|
return "(scaffold)";
|
|
16658
16653
|
}
|
|
16659
16654
|
}
|
|
16660
|
-
function stageAsGitRepo(
|
|
16655
|
+
function stageAsGitRepo(dir2) {
|
|
16661
16656
|
const steps = [
|
|
16662
16657
|
{ args: ["init", "-b", "main"], label: "git init" },
|
|
16663
16658
|
{ args: ["add", "."], label: "git add" },
|
|
16664
16659
|
{ args: ["commit", "-m", "Initial commit from brain-template"], label: "git commit" }
|
|
16665
16660
|
];
|
|
16666
16661
|
for (const { args, label } of steps) {
|
|
16667
|
-
const r = spawnSync("git", args, { cwd:
|
|
16662
|
+
const r = spawnSync("git", args, { cwd: dir2, encoding: "utf8" });
|
|
16668
16663
|
if (r.status !== 0) {
|
|
16669
16664
|
const exitCode = r.status !== null ? r.status.toString() : "<null>";
|
|
16670
16665
|
const detail = r.stderr.trim() || r.stdout.trim() || `exit ${exitCode}`;
|
|
16671
16666
|
throw new LocalCliError({
|
|
16672
16667
|
code: "BRAIN_GIT_INIT_FAILED",
|
|
16673
|
-
message: `${label} failed in ${
|
|
16668
|
+
message: `${label} failed in ${dir2}: ${detail}`
|
|
16674
16669
|
});
|
|
16675
16670
|
}
|
|
16676
16671
|
}
|
|
@@ -17634,13 +17629,13 @@ import * as fs16 from "fs";
|
|
|
17634
17629
|
import * as path16 from "path";
|
|
17635
17630
|
import { parse as parseYaml7 } from "yaml";
|
|
17636
17631
|
function findAncestorContaining(hint, rel) {
|
|
17637
|
-
let
|
|
17638
|
-
const fsRoot = path16.parse(
|
|
17632
|
+
let dir2 = path16.resolve(hint);
|
|
17633
|
+
const fsRoot = path16.parse(dir2).root;
|
|
17639
17634
|
for (; ; ) {
|
|
17640
|
-
if (fs16.existsSync(path16.join(
|
|
17641
|
-
const parent = path16.dirname(
|
|
17642
|
-
if (parent ===
|
|
17643
|
-
|
|
17635
|
+
if (fs16.existsSync(path16.join(dir2, rel))) return dir2;
|
|
17636
|
+
const parent = path16.dirname(dir2);
|
|
17637
|
+
if (parent === dir2 || dir2 === fsRoot) return null;
|
|
17638
|
+
dir2 = parent;
|
|
17644
17639
|
}
|
|
17645
17640
|
}
|
|
17646
17641
|
function findPersonasRoot(hint) {
|
|
@@ -17884,7 +17879,7 @@ async function awaitDataPlaneReady(opts) {
|
|
|
17884
17879
|
const consecutive = opts.consecutive ?? 3;
|
|
17885
17880
|
const attempts = opts.attempts ?? 60;
|
|
17886
17881
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
17887
|
-
const
|
|
17882
|
+
const sleep4 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
17888
17883
|
let streak = 0;
|
|
17889
17884
|
for (let i = 1; i <= attempts; i++) {
|
|
17890
17885
|
const r = await opts.probe();
|
|
@@ -17906,7 +17901,7 @@ async function awaitDataPlaneReady(opts) {
|
|
|
17906
17901
|
streak = 0;
|
|
17907
17902
|
opts.onProgress?.(`data-plane not ready (${forcedRetryable ? `status ${(r.status ?? 0).toString()}` : cls.category}), waiting\u2026`);
|
|
17908
17903
|
}
|
|
17909
|
-
if (i < attempts) await
|
|
17904
|
+
if (i < attempts) await sleep4(intervalMs);
|
|
17910
17905
|
}
|
|
17911
17906
|
return { ready: false, attempts };
|
|
17912
17907
|
}
|
|
@@ -18622,9 +18617,9 @@ import { createHash as createHash3 } from "crypto";
|
|
|
18622
18617
|
import * as fs20 from "fs";
|
|
18623
18618
|
import * as os8 from "os";
|
|
18624
18619
|
import * as path19 from "path";
|
|
18625
|
-
function readVersionFrontmatter(
|
|
18626
|
-
if (!fs20.existsSync(
|
|
18627
|
-
const text = fs20.readFileSync(
|
|
18620
|
+
function readVersionFrontmatter(file2) {
|
|
18621
|
+
if (!fs20.existsSync(file2)) return "";
|
|
18622
|
+
const text = fs20.readFileSync(file2, "utf8");
|
|
18628
18623
|
if (!text.startsWith("---")) return "";
|
|
18629
18624
|
const closeIdx = text.indexOf("\n---", 3);
|
|
18630
18625
|
if (closeIdx < 0) return "";
|
|
@@ -19103,7 +19098,7 @@ init_errors();
|
|
|
19103
19098
|
async function deployHostedWorker(args) {
|
|
19104
19099
|
const onProgress = args.onProgress ?? ((_m) => {
|
|
19105
19100
|
});
|
|
19106
|
-
const
|
|
19101
|
+
const sleep4 = args.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
19107
19102
|
const now = args.now ?? (() => Date.now());
|
|
19108
19103
|
const timeout = args.pollTimeoutMs ?? 8 * 60 * 1e3;
|
|
19109
19104
|
const interval = args.pollIntervalMs ?? 1e4;
|
|
@@ -19155,7 +19150,7 @@ async function deployHostedWorker(args) {
|
|
|
19155
19150
|
hint: "Provisioning can take 2\u20135 min; re-run with the same name to resume, or check the version status in the Foundry portal."
|
|
19156
19151
|
});
|
|
19157
19152
|
}
|
|
19158
|
-
await
|
|
19153
|
+
await sleep4(interval);
|
|
19159
19154
|
}
|
|
19160
19155
|
}
|
|
19161
19156
|
|
|
@@ -19164,21 +19159,21 @@ import * as fs22 from "fs";
|
|
|
19164
19159
|
import * as path22 from "path";
|
|
19165
19160
|
import { parse as parseYaml10 } from "yaml";
|
|
19166
19161
|
function findRepoRoot(startDir) {
|
|
19167
|
-
let
|
|
19162
|
+
let dir2 = path22.resolve(startDir);
|
|
19168
19163
|
for (; ; ) {
|
|
19169
|
-
if (fs22.existsSync(path22.join(
|
|
19170
|
-
const parent = path22.dirname(
|
|
19171
|
-
if (parent ===
|
|
19172
|
-
|
|
19164
|
+
if (fs22.existsSync(path22.join(dir2, "personas"))) return dir2;
|
|
19165
|
+
const parent = path22.dirname(dir2);
|
|
19166
|
+
if (parent === dir2) return null;
|
|
19167
|
+
dir2 = parent;
|
|
19173
19168
|
}
|
|
19174
19169
|
}
|
|
19175
19170
|
function resolvePersona(persona, cwd = process.cwd()) {
|
|
19176
19171
|
const root = findRepoRoot(cwd);
|
|
19177
19172
|
if (!root) return { persona, personaVersion: null };
|
|
19178
|
-
const
|
|
19173
|
+
const file2 = path22.join(root, "personas", persona, "persona.md");
|
|
19179
19174
|
let raw;
|
|
19180
19175
|
try {
|
|
19181
|
-
raw = fs22.readFileSync(
|
|
19176
|
+
raw = fs22.readFileSync(file2, "utf-8");
|
|
19182
19177
|
} catch {
|
|
19183
19178
|
return { persona, personaVersion: null };
|
|
19184
19179
|
}
|
|
@@ -20407,13 +20402,13 @@ function manifestSourceForVersionTag(channelUrl, version) {
|
|
|
20407
20402
|
}
|
|
20408
20403
|
var POINTER_SEGMENT = /\/releases\/(?:latest\/download|download\/[^/]+)\//;
|
|
20409
20404
|
async function fetchManifest(source, deps = {}) {
|
|
20410
|
-
const
|
|
20405
|
+
const readFile10 = deps.readFile ?? ((p) => fs23.readFileSync(p, "utf8"));
|
|
20411
20406
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
20412
20407
|
const ghToken = deps.ghToken ?? getGhToken;
|
|
20413
20408
|
let raw;
|
|
20414
20409
|
if ("file" in source) {
|
|
20415
20410
|
try {
|
|
20416
|
-
raw =
|
|
20411
|
+
raw = readFile10(source.file);
|
|
20417
20412
|
} catch (e) {
|
|
20418
20413
|
throw new LocalCliError({ code: "PLATFORM_MANIFEST_READ_FAILED", message: `Could not read manifest file '${source.file}': ${e.message}` });
|
|
20419
20414
|
}
|
|
@@ -21653,11 +21648,11 @@ function mergeThreeWay(ours, base, theirs) {
|
|
|
21653
21648
|
return { ok: false, merged: "" };
|
|
21654
21649
|
}
|
|
21655
21650
|
const lf = (s) => s.replace(/\r\n/g, "\n");
|
|
21656
|
-
const
|
|
21651
|
+
const dir2 = fs27.mkdtempSync(path28.join(os11.tmpdir(), "seed-merge-"));
|
|
21657
21652
|
try {
|
|
21658
|
-
const o = path28.join(
|
|
21659
|
-
const b = path28.join(
|
|
21660
|
-
const t = path28.join(
|
|
21653
|
+
const o = path28.join(dir2, "ours");
|
|
21654
|
+
const b = path28.join(dir2, "base");
|
|
21655
|
+
const t = path28.join(dir2, "theirs");
|
|
21661
21656
|
fs27.writeFileSync(o, lf(ours));
|
|
21662
21657
|
fs27.writeFileSync(b, lf(base));
|
|
21663
21658
|
fs27.writeFileSync(t, lf(theirs));
|
|
@@ -21674,7 +21669,7 @@ function mergeThreeWay(ours, base, theirs) {
|
|
|
21674
21669
|
}
|
|
21675
21670
|
return { ok: false, merged: "" };
|
|
21676
21671
|
} finally {
|
|
21677
|
-
fs27.rmSync(
|
|
21672
|
+
fs27.rmSync(dir2, { recursive: true, force: true });
|
|
21678
21673
|
}
|
|
21679
21674
|
}
|
|
21680
21675
|
var skip = (path47, skipReason) => ({ path: path47, kind: "skip", skipReason });
|
|
@@ -21728,9 +21723,9 @@ function composeSeedPrBody(s) {
|
|
|
21728
21723
|
}
|
|
21729
21724
|
|
|
21730
21725
|
// src/lib/seed-refresh.ts
|
|
21731
|
-
function readIf(
|
|
21732
|
-
if (!
|
|
21733
|
-
const abs = path29.join(
|
|
21726
|
+
function readIf(dir2, rel) {
|
|
21727
|
+
if (!dir2) return null;
|
|
21728
|
+
const abs = path29.join(dir2, rel);
|
|
21734
21729
|
return fs28.existsSync(abs) ? fs28.readFileSync(abs, "utf8") : null;
|
|
21735
21730
|
}
|
|
21736
21731
|
async function assembleCandidates(args) {
|
|
@@ -21864,8 +21859,8 @@ function dedupeBrainRepos(agents) {
|
|
|
21864
21859
|
function seedContentDir(contentRoot, manifest, seedName) {
|
|
21865
21860
|
const items = manifest.components.brainSeeds.items;
|
|
21866
21861
|
if (!Object.hasOwn(items, seedName)) return null;
|
|
21867
|
-
const
|
|
21868
|
-
return fs29.existsSync(
|
|
21862
|
+
const dir2 = path30.join(contentRoot, items[seedName].path);
|
|
21863
|
+
return fs29.existsSync(dir2) ? dir2 : null;
|
|
21869
21864
|
}
|
|
21870
21865
|
function bindRepoApi(token, repo) {
|
|
21871
21866
|
return {
|
|
@@ -22028,8 +22023,8 @@ async function buildConvergeDeps(args) {
|
|
|
22028
22023
|
message: `applyAgent called with non-hosted component '${a.component}'`
|
|
22029
22024
|
});
|
|
22030
22025
|
}
|
|
22031
|
-
const
|
|
22032
|
-
const agentName =
|
|
22026
|
+
const dir2 = HOSTED_AGENT_PERSONA_DIR[a.component];
|
|
22027
|
+
const agentName = dir2 ? discovered[dir2] : void 0;
|
|
22033
22028
|
if (!agentName) {
|
|
22034
22029
|
warn(`hosted agent for component '${a.component}' is not deployed on this install \u2014 skipping.`);
|
|
22035
22030
|
return "absent";
|
|
@@ -22048,6 +22043,13 @@ async function buildConvergeDeps(args) {
|
|
|
22048
22043
|
const target = infraTargetSha(ctx.manifest, args.tree);
|
|
22049
22044
|
const infraTable = await openInfraParamsTable({ credential: args.credential, subscriptionId: args.subscriptionId, resourceGroup: args.resourceGroup });
|
|
22050
22045
|
const stamped = (await readInfraParams(infraTable))?.bicep;
|
|
22046
|
+
if (!stamped) {
|
|
22047
|
+
throw new LocalCliError({
|
|
22048
|
+
code: "PLATFORM_INFRA_PARAMS_MISSING",
|
|
22049
|
+
message: "This install has no stamped system/infra-params row, so the updater cannot converge infra: the row is the only place the real tenantId/clientId/Foundry endpoint survive, and they cannot be reconstructed from scratch.",
|
|
22050
|
+
hint: "Run `m8t platform enable-auto-update` against this deployment (it captures the full bicep parameter set), then let the next tick retry."
|
|
22051
|
+
});
|
|
22052
|
+
}
|
|
22051
22053
|
args.onProgress?.("subscription-scoped modules skipped: updater is RG-scoped; sub-level changes require a founder-run CLI converge.");
|
|
22052
22054
|
const gatewayName = args.suffix ? `m8t-gateway-${args.suffix}` : void 0;
|
|
22053
22055
|
const voiceInternalSecret = gatewayName ? await readLiveVoiceInternalSecret({
|
|
@@ -22063,17 +22065,18 @@ async function buildConvergeDeps(args) {
|
|
|
22063
22065
|
const opts = {
|
|
22064
22066
|
suffix: args.suffix,
|
|
22065
22067
|
assignSubscriptionRoles: false,
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
|
|
22069
|
-
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
|
|
22076
|
-
} : {},
|
|
22068
|
+
// Unconditional: the guard above has already refused an absent row. The
|
|
22069
|
+
// ternary that used to be here read as "these are optional" — which is
|
|
22070
|
+
// how the empty-endpoint death got written in the first place.
|
|
22071
|
+
tenantId: stamped.tenantId,
|
|
22072
|
+
clientId: stamped.clientId,
|
|
22073
|
+
foundryEndpoint: stamped.foundryProjectEndpoint,
|
|
22074
|
+
foundryResourceId: stamped.foundryResourceId,
|
|
22075
|
+
acrPullIdentity: stamped.acrPullIdentityResourceId,
|
|
22076
|
+
acrResourceId: stamped.acrResourceId,
|
|
22077
|
+
...stamped.foundryTracingMode ? { foundryTracingMode: stamped.foundryTracingMode } : {},
|
|
22078
|
+
...stamped.channelUrl ? { channelUrl: stamped.channelUrl } : {},
|
|
22079
|
+
...stamped.location ? { location: stamped.location } : {},
|
|
22077
22080
|
...voiceInternalSecret ? { voiceInternalSecret } : {}
|
|
22078
22081
|
};
|
|
22079
22082
|
return applyInfraDeploy(a, ctx, target, opts);
|
|
@@ -22084,8 +22087,8 @@ async function buildConvergeDeps(args) {
|
|
|
22084
22087
|
if (a.component === "personas" && a.personaName) {
|
|
22085
22088
|
agentName = discovered[a.personaName];
|
|
22086
22089
|
} else if (a.component === "codingAgent" || a.component === "azureExecutor") {
|
|
22087
|
-
const
|
|
22088
|
-
agentName =
|
|
22090
|
+
const dir2 = HOSTED_AGENT_PERSONA_DIR[a.component];
|
|
22091
|
+
agentName = dir2 ? discovered[dir2] : void 0;
|
|
22089
22092
|
}
|
|
22090
22093
|
if (!agentName) continue;
|
|
22091
22094
|
await awaitAgentQueryable({ credential: args.credential, projectEndpoint: args.project.endpoint, agentName, onProgress: args.onProgress });
|
|
@@ -22624,7 +22627,21 @@ async function rollbackOrHold(args, notify, error) {
|
|
|
22624
22627
|
return "held";
|
|
22625
22628
|
}
|
|
22626
22629
|
if (rollbackTarget) {
|
|
22627
|
-
|
|
22630
|
+
try {
|
|
22631
|
+
await reconvergeToPrevious(args, rollbackTarget);
|
|
22632
|
+
} catch (e) {
|
|
22633
|
+
const clip = (s) => s.length > 4e3 ? `${s.slice(0, 4e3)} \u2026[truncated]` : s;
|
|
22634
|
+
const combined = `${clip(error)} \u2014 and the rollback to ${rollbackTarget} failed: ${clip(e.message)} \u2014 the rollback may have partially applied; check the installed stamp's components before retrying.`;
|
|
22635
|
+
await patchApplyRequest(args.client, (r) => ({
|
|
22636
|
+
...r,
|
|
22637
|
+
status: "held",
|
|
22638
|
+
breaker: "held",
|
|
22639
|
+
result: { appliedVersion: r.result?.appliedVersion ?? null, error: combined },
|
|
22640
|
+
updatedAt: args.now().toISOString()
|
|
22641
|
+
}));
|
|
22642
|
+
await notify({ outcome: "held", target, error: combined });
|
|
22643
|
+
return "held";
|
|
22644
|
+
}
|
|
22628
22645
|
}
|
|
22629
22646
|
await patchApplyRequest(args.client, (r) => ({
|
|
22630
22647
|
...r,
|
|
@@ -22865,6 +22882,9 @@ var PlatformConvergeCommand = class extends M8tCommand {
|
|
|
22865
22882
|
channelUrl: ctx.channelUrl,
|
|
22866
22883
|
repoRoot,
|
|
22867
22884
|
now: () => /* @__PURE__ */ new Date()
|
|
22885
|
+
}).catch(async (e) => {
|
|
22886
|
+
await failIfStillInFlight(client, now, e.message);
|
|
22887
|
+
throw e;
|
|
22868
22888
|
});
|
|
22869
22889
|
log(`converge outcome: ${outcome}.`);
|
|
22870
22890
|
return outcome === "success" ? 0 : 1;
|
|
@@ -27361,9 +27381,9 @@ function isAbsentCursorError(e) {
|
|
|
27361
27381
|
const anyE = e;
|
|
27362
27382
|
return anyE.statusCode === 404 || anyE.code === "ResourceNotFound" || anyE.code === "TableNotFound" || anyE.details?.errorCode === "TableNotFound" || anyE.details?.errorCode === "ResourceNotFound";
|
|
27363
27383
|
}
|
|
27364
|
-
function absentCursor(
|
|
27384
|
+
function absentCursor(canonical2) {
|
|
27365
27385
|
return {
|
|
27366
|
-
worker:
|
|
27386
|
+
worker: canonical2,
|
|
27367
27387
|
watermarks: {},
|
|
27368
27388
|
safetyLagSeconds: DEFAULT_SAFETY_LAG_SECONDS,
|
|
27369
27389
|
engineVersion: ENGINE_VERSION
|
|
@@ -27382,19 +27402,19 @@ var TableCursor = class {
|
|
|
27382
27402
|
* Any other error (e.g. 403 RBAC) propagates raw.
|
|
27383
27403
|
*/
|
|
27384
27404
|
async read(worker) {
|
|
27385
|
-
const
|
|
27405
|
+
const canonical2 = canonicalWorker(worker);
|
|
27386
27406
|
let row;
|
|
27387
27407
|
try {
|
|
27388
|
-
row = await this.client.getEntity(CURSOR_PARTITION_KEY,
|
|
27408
|
+
row = await this.client.getEntity(CURSOR_PARTITION_KEY, canonical2);
|
|
27389
27409
|
} catch (e) {
|
|
27390
27410
|
if (isAbsentCursorError(e))
|
|
27391
|
-
return absentCursor(
|
|
27411
|
+
return absentCursor(canonical2);
|
|
27392
27412
|
throw e;
|
|
27393
27413
|
}
|
|
27394
27414
|
const watermarks = row.watermarks ? JSON.parse(row.watermarks) : {};
|
|
27395
27415
|
const fetchFailures = row.fetchFailures ? JSON.parse(row.fetchFailures) : void 0;
|
|
27396
27416
|
return {
|
|
27397
|
-
worker:
|
|
27417
|
+
worker: canonical2,
|
|
27398
27418
|
watermarks,
|
|
27399
27419
|
...fetchFailures && Object.keys(fetchFailures).length > 0 ? { fetchFailures } : {},
|
|
27400
27420
|
safetyLagSeconds: row.safetyLagSeconds ?? DEFAULT_SAFETY_LAG_SECONDS,
|
|
@@ -27410,11 +27430,11 @@ var TableCursor = class {
|
|
|
27410
27430
|
* (Table cells are scalar — same rationale as agent-ledger's `artifacts`).
|
|
27411
27431
|
*/
|
|
27412
27432
|
async advance(worker, cursor) {
|
|
27413
|
-
const
|
|
27433
|
+
const canonical2 = canonicalWorker(worker);
|
|
27414
27434
|
await this.ensureTable();
|
|
27415
27435
|
const entity = {
|
|
27416
27436
|
partitionKey: CURSOR_PARTITION_KEY,
|
|
27417
|
-
rowKey:
|
|
27437
|
+
rowKey: canonical2,
|
|
27418
27438
|
watermarks: JSON.stringify(cursor.watermarks),
|
|
27419
27439
|
safetyLagSeconds: cursor.safetyLagSeconds,
|
|
27420
27440
|
engineVersion: cursor.engineVersion
|
|
@@ -27499,10 +27519,10 @@ function formatCursorPreview(cursor) {
|
|
|
27499
27519
|
|
|
27500
27520
|
// ../../packages/brain/engine/dist/esm/ledger-source.js
|
|
27501
27521
|
function physicalPksFor(worker) {
|
|
27502
|
-
const
|
|
27503
|
-
const pks = /* @__PURE__ */ new Set([
|
|
27522
|
+
const canonical2 = canonicalWorker(worker);
|
|
27523
|
+
const pks = /* @__PURE__ */ new Set([canonical2]);
|
|
27504
27524
|
for (const [physical, mapped] of Object.entries(WORKER_ALIASES)) {
|
|
27505
|
-
if (mapped ===
|
|
27525
|
+
if (mapped === canonical2)
|
|
27506
27526
|
pks.add(physical);
|
|
27507
27527
|
}
|
|
27508
27528
|
return [...pks];
|
|
@@ -27590,7 +27610,7 @@ function mapItems(raw) {
|
|
|
27590
27610
|
return acc.reverse();
|
|
27591
27611
|
}
|
|
27592
27612
|
function makeConversationSource(client, opts = {}) {
|
|
27593
|
-
const
|
|
27613
|
+
const sleep4 = opts.sleep ?? defaultSleep;
|
|
27594
27614
|
return {
|
|
27595
27615
|
async items(conversationId) {
|
|
27596
27616
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
@@ -27608,7 +27628,7 @@ function makeConversationSource(client, opts = {}) {
|
|
|
27608
27628
|
throw new ConvFetchError("auth", `auth failed reading ${conversationId}`);
|
|
27609
27629
|
const retryable = status === void 0 || status === TRANSIENT;
|
|
27610
27630
|
if (retryable && attempt < MAX_ATTEMPTS - 1) {
|
|
27611
|
-
await
|
|
27631
|
+
await sleep4(attempt);
|
|
27612
27632
|
continue;
|
|
27613
27633
|
}
|
|
27614
27634
|
throw new ConvFetchError("terminal", `exhausted ${String(MAX_ATTEMPTS)} retries reading ${conversationId}: ${e?.message ?? String(e)}`);
|
|
@@ -28412,7 +28432,7 @@ function extractJson(text) {
|
|
|
28412
28432
|
return JSON.parse(candidate2.slice(start, end + 1));
|
|
28413
28433
|
}
|
|
28414
28434
|
async function propose(model, system, user, opts = {}) {
|
|
28415
|
-
const
|
|
28435
|
+
const sleep4 = opts.sleep ?? defaultSleep2;
|
|
28416
28436
|
const modelName = opts.model ?? MODEL_NAME_FALLBACK;
|
|
28417
28437
|
let inputTokens = 0;
|
|
28418
28438
|
let outputTokens = 0;
|
|
@@ -28428,7 +28448,7 @@ async function propose(model, system, user, opts = {}) {
|
|
|
28428
28448
|
} catch (e) {
|
|
28429
28449
|
lastErr = `parse: ${e.message}`;
|
|
28430
28450
|
if (attempt < MAX_MODEL_ATTEMPTS - 1) {
|
|
28431
|
-
await
|
|
28451
|
+
await sleep4(attempt);
|
|
28432
28452
|
continue;
|
|
28433
28453
|
}
|
|
28434
28454
|
break;
|
|
@@ -28437,7 +28457,7 @@ async function propose(model, system, user, opts = {}) {
|
|
|
28437
28457
|
if (!Array.isArray(rawDeltas)) {
|
|
28438
28458
|
lastErr = "schema: top-level { deltas: [] } missing";
|
|
28439
28459
|
if (attempt < MAX_MODEL_ATTEMPTS - 1) {
|
|
28440
|
-
await
|
|
28460
|
+
await sleep4(attempt);
|
|
28441
28461
|
continue;
|
|
28442
28462
|
}
|
|
28443
28463
|
break;
|
|
@@ -29198,9 +29218,9 @@ async function discoverLedgerResources(opts) {
|
|
|
29198
29218
|
}
|
|
29199
29219
|
|
|
29200
29220
|
// src/lib/dream-context.ts
|
|
29201
|
-
function physicalPksFor2(
|
|
29202
|
-
const aliases = Object.entries(WORKER_ALIASES).filter(([, c]) => c ===
|
|
29203
|
-
return [
|
|
29221
|
+
function physicalPksFor2(canonical2) {
|
|
29222
|
+
const aliases = Object.entries(WORKER_ALIASES).filter(([, c]) => c === canonical2).map(([physical]) => physical);
|
|
29223
|
+
return [canonical2, ...aliases];
|
|
29204
29224
|
}
|
|
29205
29225
|
function rgFromScope(scope) {
|
|
29206
29226
|
const m = /\/resourceGroups\/([^/]+)/i.exec(scope);
|
|
@@ -29209,7 +29229,7 @@ function rgFromScope(scope) {
|
|
|
29209
29229
|
}
|
|
29210
29230
|
async function resolveDreamContext(opts) {
|
|
29211
29231
|
const { credential: credential2 } = opts;
|
|
29212
|
-
const
|
|
29232
|
+
const canonical2 = opts.worker.toLowerCase();
|
|
29213
29233
|
const account = await getAzAccount();
|
|
29214
29234
|
const subscriptionId = opts.subscription ?? account.subscriptionId;
|
|
29215
29235
|
const project = await resolveFoundryProject({
|
|
@@ -29226,11 +29246,11 @@ async function resolveDreamContext(opts) {
|
|
|
29226
29246
|
resourceGroup
|
|
29227
29247
|
});
|
|
29228
29248
|
return {
|
|
29229
|
-
worker:
|
|
29249
|
+
worker: canonical2,
|
|
29230
29250
|
projectEndpoint: project.endpoint,
|
|
29231
29251
|
ledgerTableEndpoint,
|
|
29232
29252
|
workspaceId,
|
|
29233
|
-
physicalPks: physicalPksFor2(
|
|
29253
|
+
physicalPks: physicalPksFor2(canonical2)
|
|
29234
29254
|
};
|
|
29235
29255
|
}
|
|
29236
29256
|
|
|
@@ -30832,7 +30852,7 @@ ${colors.error("\u2717")} The GitHub App on disk is installed on ${colors.field(
|
|
|
30832
30852
|
// src/commands/bootstrap/launch.ts
|
|
30833
30853
|
var DEFAULT_RG = "rg-m8t-stack";
|
|
30834
30854
|
var DEFAULT_INSTALLER = "ghcr.io/m8t-labs/m8t-installer";
|
|
30835
|
-
var DEFAULT_INSTALLER_TAG = "v0.1.
|
|
30855
|
+
var DEFAULT_INSTALLER_TAG = "v0.1.63";
|
|
30836
30856
|
var ACI_NAME = "m8t-installer";
|
|
30837
30857
|
var MI_NAME = "m8t-installer-mi";
|
|
30838
30858
|
var BootstrapLaunchCommand = class extends M8tCommand {
|
|
@@ -31054,15 +31074,15 @@ async function getAciState(opts) {
|
|
|
31054
31074
|
}
|
|
31055
31075
|
|
|
31056
31076
|
// src/lib/bootstrap-finalize.ts
|
|
31057
|
-
import * as
|
|
31058
|
-
import * as
|
|
31059
|
-
import * as
|
|
31077
|
+
import * as fs39 from "fs/promises";
|
|
31078
|
+
import * as os20 from "os";
|
|
31079
|
+
import * as path44 from "path";
|
|
31060
31080
|
|
|
31061
31081
|
// src/lib/company-profile-seed.ts
|
|
31062
31082
|
import { spawn as spawn6 } from "child_process";
|
|
31063
31083
|
import { closeSync, openSync, readFileSync as readFileSync24 } from "fs";
|
|
31064
|
-
import * as
|
|
31065
|
-
import * as
|
|
31084
|
+
import * as os17 from "os";
|
|
31085
|
+
import * as path39 from "path";
|
|
31066
31086
|
init_errors();
|
|
31067
31087
|
|
|
31068
31088
|
// src/lib/onboarding-profile.ts
|
|
@@ -31497,7 +31517,11 @@ function renderCompanyProfile(block, now = (/* @__PURE__ */ new Date()).toISOStr
|
|
|
31497
31517
|
``,
|
|
31498
31518
|
`# Company profile`,
|
|
31499
31519
|
``,
|
|
31500
|
-
|
|
31520
|
+
// Named the questionnaire until the questionnaire stopped existing. There is no
|
|
31521
|
+
// intake conversation any more — `m8t bootstrap profile` confirms two contact
|
|
31522
|
+
// facts and nothing about the company — so the old line described a thing that
|
|
31523
|
+
// never happened to this founder.
|
|
31524
|
+
`_Seeded at install._`,
|
|
31501
31525
|
``,
|
|
31502
31526
|
...bullet("Company", block.company_name),
|
|
31503
31527
|
...bullet("Stage", block.company_stage),
|
|
@@ -31523,6 +31547,45 @@ function renderCompanyProfile(block, now = (/* @__PURE__ */ new Date()).toISOStr
|
|
|
31523
31547
|
var FOUNDER_RECORD_PATH = "memory/founder.md";
|
|
31524
31548
|
var NOT_CAPTURED = "_not captured yet \u2014 just tell me and I'll add it_";
|
|
31525
31549
|
var REGION_UNKNOWN_AT_INTAKE = "_not known at intake \u2014 filled in when the request is filed_";
|
|
31550
|
+
var FOUNDER_BULLET_LABELS = {
|
|
31551
|
+
name: "- **Founder:** ",
|
|
31552
|
+
email: "- **Founder email (company_email):** ",
|
|
31553
|
+
advisor: "- **Microsoft Startup Advisor (SA):** ",
|
|
31554
|
+
subscription: "- **Azure subscription:** "
|
|
31555
|
+
};
|
|
31556
|
+
function updateFounderContacts(existing, block, inputs) {
|
|
31557
|
+
if (!existing.includes(FOUNDER_BULLET_LABELS.email)) return null;
|
|
31558
|
+
const advisorName = (block.advisor_name ?? "").trim();
|
|
31559
|
+
const advisorEmail = (block.advisor_email ?? "").trim();
|
|
31560
|
+
const advisor = inputs.advisorCleared === true ? renderAdvisor(advisorName, advisorEmail, NOT_CAPTURED) : renderAdvisor(advisorName, advisorEmail, "");
|
|
31561
|
+
const replacements = [];
|
|
31562
|
+
const add = (prefix, value) => {
|
|
31563
|
+
if (value) replacements.push([prefix, prefix + value]);
|
|
31564
|
+
};
|
|
31565
|
+
add(FOUNDER_BULLET_LABELS.name, (block.founder_name ?? "").trim());
|
|
31566
|
+
add(FOUNDER_BULLET_LABELS.email, (block.founder_email ?? "").trim());
|
|
31567
|
+
add(FOUNDER_BULLET_LABELS.advisor, advisor);
|
|
31568
|
+
add(FOUNDER_BULLET_LABELS.subscription, (inputs.subscriptionId ?? "").trim());
|
|
31569
|
+
return existing.split("\n").map((line2) => {
|
|
31570
|
+
const cr = line2.endsWith("\r") ? "\r" : "";
|
|
31571
|
+
const bare = cr ? line2.slice(0, -1) : line2;
|
|
31572
|
+
const hit = replacements.find(([prefix]) => bare.startsWith(prefix));
|
|
31573
|
+
return hit ? hit[1] + cr : line2;
|
|
31574
|
+
}).join("\n");
|
|
31575
|
+
}
|
|
31576
|
+
function renderAdvisor(name, email, absent) {
|
|
31577
|
+
if (!name && !email) return absent;
|
|
31578
|
+
return [name, email ? `<${email}>` : ""].filter(Boolean).join(" ");
|
|
31579
|
+
}
|
|
31580
|
+
function readAdvisorFromRecord(existing) {
|
|
31581
|
+
const line2 = existing.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l).find((l) => l.startsWith(FOUNDER_BULLET_LABELS.advisor));
|
|
31582
|
+
if (line2 === void 0) return null;
|
|
31583
|
+
const value = line2.slice(FOUNDER_BULLET_LABELS.advisor.length).trim();
|
|
31584
|
+
if (!value || value === NOT_CAPTURED) return null;
|
|
31585
|
+
const email = /<([^>]+)>/.exec(value)?.[1]?.trim() ?? "";
|
|
31586
|
+
const name = value.replace(/<[^>]*>/, "").trim();
|
|
31587
|
+
return name || email ? { name, email } : null;
|
|
31588
|
+
}
|
|
31526
31589
|
function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
31527
31590
|
const pick = (...vals) => vals.map((v) => v?.trim()).find(Boolean) ?? "";
|
|
31528
31591
|
const founderName = pick(block.founder_name, inputs.azIdentity?.name);
|
|
@@ -31530,7 +31593,7 @@ function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).t
|
|
|
31530
31593
|
const advisorName = (block.advisor_name ?? "").trim();
|
|
31531
31594
|
const advisorEmail = (block.advisor_email ?? "").trim();
|
|
31532
31595
|
const subscription = (inputs.subscriptionId ?? "").trim();
|
|
31533
|
-
const advisorRendered = advisorName
|
|
31596
|
+
const advisorRendered = renderAdvisor(advisorName, advisorEmail, NOT_CAPTURED);
|
|
31534
31597
|
const request = block.pending_requests?.[0];
|
|
31535
31598
|
const requestLines2 = request === void 0 ? [] : (() => {
|
|
31536
31599
|
const modelLabel = request.model.trim() ? `\`${request.model.trim()}\`` : "(model not recorded)";
|
|
@@ -31565,7 +31628,7 @@ function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).t
|
|
|
31565
31628
|
``,
|
|
31566
31629
|
`# Founder & install context`,
|
|
31567
31630
|
``,
|
|
31568
|
-
`_Seeded at
|
|
31631
|
+
`_Seeded at install from your Azure identity + what you confirmed. Authoritative \u2014 read it; don't rewrite it (origin: operator)._`,
|
|
31569
31632
|
``,
|
|
31570
31633
|
`- **Founder:** ${founderName || NOT_CAPTURED}`,
|
|
31571
31634
|
`- **Founder email (company_email):** ${founderEmail || NOT_CAPTURED}`,
|
|
@@ -31581,168 +31644,7 @@ function renderFounderRecord(block, inputs, now = (/* @__PURE__ */ new Date()).t
|
|
|
31581
31644
|
return { founderMd, memoryIndexLine };
|
|
31582
31645
|
}
|
|
31583
31646
|
|
|
31584
|
-
// src/lib/model-cascade.ts
|
|
31585
|
-
var WHITELIST = [
|
|
31586
|
-
{ model: "gpt-5.6-luna", format: "OpenAI", version: "2026-07-09", capacity: 100 },
|
|
31587
|
-
{ model: "gpt-5.6-sol", format: "OpenAI", version: "2026-07-09", capacity: 100 },
|
|
31588
|
-
{ model: "gpt-5.6-terra", format: "OpenAI", version: "2026-07-09", capacity: 100 },
|
|
31589
|
-
{ model: "gpt-5.4", format: "OpenAI", version: "2026-03-05", capacity: 100 },
|
|
31590
|
-
{ model: "grok-4.3", format: "xAI", version: "1", capacity: 100 },
|
|
31591
|
-
{ model: "gpt-4.1-mini", format: "OpenAI", version: "2025-04-14", capacity: 50 }
|
|
31592
|
-
];
|
|
31593
|
-
var GLOBAL_STANDARD = "GlobalStandard";
|
|
31594
|
-
function planCascade(whitelist, catalog, quota) {
|
|
31595
|
-
if (!catalog.ok) {
|
|
31596
|
-
return {
|
|
31597
|
-
candidates: [],
|
|
31598
|
-
skipped: whitelist.map((r) => ({ model: r.model, outcome: "catalog-unverified", detail: catalog.error }))
|
|
31599
|
-
};
|
|
31600
|
-
}
|
|
31601
|
-
const rowsByName = /* @__PURE__ */ new Map();
|
|
31602
|
-
for (const row of catalog.rows) {
|
|
31603
|
-
const list = rowsByName.get(row.name);
|
|
31604
|
-
if (list) list.push(row);
|
|
31605
|
-
else rowsByName.set(row.name, [row]);
|
|
31606
|
-
}
|
|
31607
|
-
const candidates = [];
|
|
31608
|
-
const skipped = [];
|
|
31609
|
-
for (const rung of whitelist) {
|
|
31610
|
-
const rows = rowsByName.get(rung.model);
|
|
31611
|
-
if (!rows || rows.length === 0) {
|
|
31612
|
-
skipped.push({ model: rung.model, outcome: "not-in-catalog" });
|
|
31613
|
-
continue;
|
|
31614
|
-
}
|
|
31615
|
-
const agentRows = rows.filter((r) => r.agentsV2);
|
|
31616
|
-
if (agentRows.length === 0) {
|
|
31617
|
-
skipped.push({ model: rung.model, outcome: "not-agent-eligible" });
|
|
31618
|
-
continue;
|
|
31619
|
-
}
|
|
31620
|
-
if (!agentRows.some((r) => r.skus.includes(GLOBAL_STANDARD))) {
|
|
31621
|
-
skipped.push({ model: rung.model, outcome: "no-global-standard" });
|
|
31622
|
-
continue;
|
|
31623
|
-
}
|
|
31624
|
-
if (modelQuotaVerdict(quota, rung.model).verdict === "no_quota") {
|
|
31625
|
-
skipped.push({ model: rung.model, outcome: "no-quota" });
|
|
31626
|
-
continue;
|
|
31627
|
-
}
|
|
31628
|
-
candidates.push(rung);
|
|
31629
|
-
}
|
|
31630
|
-
return { candidates, skipped };
|
|
31631
|
-
}
|
|
31632
|
-
var QUOTA_RE = /insufficient\s*quota|not enough quota|quota limit|quota\b[^.]{0,40}exceed|exceed\w*\b[^.]{0,40}quota|capacity[^.]{0,40}exceed|exceed\w*\b[^.]{0,40}capacity/i;
|
|
31633
|
-
var REGION_RE = /not available in|not supported in|NotAvailableInRegion|InvalidResourceLocation/i;
|
|
31634
|
-
var CONFLICT_RE = /\bconflict\b|\b409\b/i;
|
|
31635
|
-
function classifyDeployError(message) {
|
|
31636
|
-
if (QUOTA_RE.test(message)) return "deploy-rejected-quota";
|
|
31637
|
-
if (REGION_RE.test(message)) return "deploy-rejected-region";
|
|
31638
|
-
return "deploy-unverified";
|
|
31639
|
-
}
|
|
31640
|
-
async function walkCascade(whitelist, plan, deploy, opts) {
|
|
31641
|
-
const outcomes = /* @__PURE__ */ new Map();
|
|
31642
|
-
for (const s of plan.skipped) outcomes.set(s.model, s);
|
|
31643
|
-
let chosen = null;
|
|
31644
|
-
let aborted = false;
|
|
31645
|
-
for (const rung of plan.candidates) {
|
|
31646
|
-
if (chosen) break;
|
|
31647
|
-
if (opts.now() >= opts.deadlineAt) {
|
|
31648
|
-
aborted = true;
|
|
31649
|
-
break;
|
|
31650
|
-
}
|
|
31651
|
-
let row;
|
|
31652
|
-
try {
|
|
31653
|
-
await deploy(rung);
|
|
31654
|
-
row = { model: rung.model, outcome: "deployed" };
|
|
31655
|
-
chosen = rung;
|
|
31656
|
-
} catch (e) {
|
|
31657
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
31658
|
-
if (CONFLICT_RE.test(msg)) {
|
|
31659
|
-
try {
|
|
31660
|
-
await deploy(rung);
|
|
31661
|
-
row = { model: rung.model, outcome: "deployed" };
|
|
31662
|
-
chosen = rung;
|
|
31663
|
-
} catch (e2) {
|
|
31664
|
-
const msg2 = e2 instanceof Error ? e2.message : String(e2);
|
|
31665
|
-
row = {
|
|
31666
|
-
model: rung.model,
|
|
31667
|
-
outcome: CONFLICT_RE.test(msg2) ? "deploy-unverified" : classifyDeployError(msg2),
|
|
31668
|
-
detail: msg2
|
|
31669
|
-
};
|
|
31670
|
-
}
|
|
31671
|
-
} else {
|
|
31672
|
-
row = { model: rung.model, outcome: classifyDeployError(msg), detail: msg };
|
|
31673
|
-
}
|
|
31674
|
-
}
|
|
31675
|
-
outcomes.set(rung.model, row);
|
|
31676
|
-
opts.onRung?.(row.model, row.outcome);
|
|
31677
|
-
}
|
|
31678
|
-
const trace = whitelist.map(
|
|
31679
|
-
(r) => outcomes.get(r.model) ?? { model: r.model, outcome: aborted ? "aborted-deadline" : "not-reached" }
|
|
31680
|
-
);
|
|
31681
|
-
return { chosen, trace };
|
|
31682
|
-
}
|
|
31683
|
-
var QUOTA_INVITATION = "You can offer to help request that quota.";
|
|
31684
|
-
var CERTAIN_UNAVAILABLE = /* @__PURE__ */ new Set([
|
|
31685
|
-
"not-in-catalog",
|
|
31686
|
-
"not-agent-eligible",
|
|
31687
|
-
"no-global-standard",
|
|
31688
|
-
"deploy-rejected-region"
|
|
31689
|
-
]);
|
|
31690
|
-
var QUOTA_BLOCKED = /* @__PURE__ */ new Set(["no-quota", "deploy-rejected-quota"]);
|
|
31691
|
-
function decideNote(whitelist, result2) {
|
|
31692
|
-
const chosenIdx = result2.chosen ? whitelist.findIndex((r) => r.model === result2.chosen?.model) : whitelist.length;
|
|
31693
|
-
const better = result2.trace.slice(0, Math.max(chosenIdx, 0));
|
|
31694
|
-
const runningModel = result2.chosen?.model ?? null;
|
|
31695
|
-
if (chosenIdx === 0) {
|
|
31696
|
-
return { status: "top", runningModel, pitchModel: null, unavailableAbovePitch: [] };
|
|
31697
|
-
}
|
|
31698
|
-
const pitch = better.find((t) => QUOTA_BLOCKED.has(t.outcome));
|
|
31699
|
-
if (pitch) {
|
|
31700
|
-
const pitchIdx = better.findIndex((t) => t.model === pitch.model);
|
|
31701
|
-
return {
|
|
31702
|
-
status: "lesser-quota",
|
|
31703
|
-
runningModel,
|
|
31704
|
-
pitchModel: pitch.model,
|
|
31705
|
-
unavailableAbovePitch: better.slice(0, pitchIdx).filter((t) => CERTAIN_UNAVAILABLE.has(t.outcome)).map((t) => t.model)
|
|
31706
|
-
};
|
|
31707
|
-
}
|
|
31708
|
-
const allCertain = better.length > 0 && better.every((t) => CERTAIN_UNAVAILABLE.has(t.outcome));
|
|
31709
|
-
return {
|
|
31710
|
-
status: allCertain ? "lesser-unavailable" : "lesser-unverified",
|
|
31711
|
-
runningModel,
|
|
31712
|
-
pitchModel: null,
|
|
31713
|
-
unavailableAbovePitch: []
|
|
31714
|
-
};
|
|
31715
|
-
}
|
|
31716
|
-
var listNames = (names) => names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
31717
|
-
function renderChosenModelNote(d) {
|
|
31718
|
-
const subject = d.runningModel ? `You are running on ${d.runningModel}.` : "You are running on this install's default model.";
|
|
31719
|
-
switch (d.status) {
|
|
31720
|
-
case "top":
|
|
31721
|
-
return `${subject} That is the best available model for this install.`;
|
|
31722
|
-
case "lesser-quota": {
|
|
31723
|
-
const aside = d.unavailableAbovePitch.length > 0 ? ` ${listNames(d.unavailableAbovePitch)} ${d.unavailableAbovePitch.length > 1 ? "are" : "is"} not offered for this install.` : "";
|
|
31724
|
-
return `${subject} A stronger model, ${d.pitchModel ?? ""}, is offered here, but this subscription has no quota for it.${aside} ${QUOTA_INVITATION}`;
|
|
31725
|
-
}
|
|
31726
|
-
case "lesser-unavailable":
|
|
31727
|
-
return `${subject} No stronger model is offered for this install right now.`;
|
|
31728
|
-
case "lesser-unverified":
|
|
31729
|
-
return `${subject} It was not possible to check which stronger models this subscription can run, so do not make claims about what is or is not available.`;
|
|
31730
|
-
}
|
|
31731
|
-
}
|
|
31732
|
-
|
|
31733
31647
|
// src/lib/founder-identity.ts
|
|
31734
|
-
var INVENTORY_INVITATION = "You can offer to walk the founder through these resources.";
|
|
31735
|
-
var NOTE_INVITATIONS = [QUOTA_INVITATION, INVENTORY_INVITATION];
|
|
31736
|
-
function escapeRegExp(value) {
|
|
31737
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31738
|
-
}
|
|
31739
|
-
function stripNoteInvitations(value) {
|
|
31740
|
-
let out = value;
|
|
31741
|
-
for (const sentence of NOTE_INVITATIONS) {
|
|
31742
|
-
out = out.replace(new RegExp(escapeRegExp(sentence), "gi"), " ");
|
|
31743
|
-
}
|
|
31744
|
-
return out.replace(/\s{2,}/g, " ").trim();
|
|
31745
|
-
}
|
|
31746
31648
|
function deriveEmailCandidate(raw) {
|
|
31747
31649
|
const mail = (raw.mail ?? "").trim();
|
|
31748
31650
|
if (mail) return mail;
|
|
@@ -31773,21 +31675,80 @@ async function getSignedInUserIdentity(runAzImpl = runAz) {
|
|
|
31773
31675
|
return { name: "", email: "" };
|
|
31774
31676
|
}
|
|
31775
31677
|
}
|
|
31776
|
-
|
|
31777
|
-
|
|
31778
|
-
|
|
31779
|
-
|
|
31780
|
-
|
|
31678
|
+
|
|
31679
|
+
// src/lib/onboarding-profile-store.ts
|
|
31680
|
+
import * as fs35 from "fs/promises";
|
|
31681
|
+
import * as os16 from "os";
|
|
31682
|
+
import * as path38 from "path";
|
|
31683
|
+
var ONBOARDING_PROFILE_FILE = "onboarding-profile.json";
|
|
31684
|
+
function dir(home) {
|
|
31685
|
+
return path38.join(home, ".m8t");
|
|
31686
|
+
}
|
|
31687
|
+
function file(home) {
|
|
31688
|
+
return path38.join(dir(home), ONBOARDING_PROFILE_FILE);
|
|
31689
|
+
}
|
|
31690
|
+
function isRecord3(value) {
|
|
31691
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31692
|
+
}
|
|
31693
|
+
function nonBlankString(value) {
|
|
31694
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
31695
|
+
}
|
|
31696
|
+
function canonical(value) {
|
|
31697
|
+
if (!isRecord3(value)) return null;
|
|
31698
|
+
if (value.schemaVersion !== 1) return null;
|
|
31699
|
+
if (!nonBlankString(value.founderEmail)) return null;
|
|
31700
|
+
if (typeof value.founderName !== "string") return null;
|
|
31701
|
+
if (typeof value.collectedAt !== "string") return null;
|
|
31702
|
+
if (value.advisor === void 0) return null;
|
|
31703
|
+
let advisor = null;
|
|
31704
|
+
if (value.advisor !== null) {
|
|
31705
|
+
if (!isRecord3(value.advisor)) return null;
|
|
31706
|
+
if (typeof value.advisor.name !== "string" || typeof value.advisor.email !== "string") return null;
|
|
31707
|
+
advisor = { name: value.advisor.name, email: value.advisor.email };
|
|
31781
31708
|
}
|
|
31782
|
-
|
|
31783
|
-
|
|
31709
|
+
return {
|
|
31710
|
+
schemaVersion: 1,
|
|
31711
|
+
collectedAt: value.collectedAt,
|
|
31712
|
+
founderName: value.founderName,
|
|
31713
|
+
founderEmail: value.founderEmail,
|
|
31714
|
+
advisor,
|
|
31715
|
+
...value.advisorCleared === true ? { advisorCleared: true } : {}
|
|
31716
|
+
};
|
|
31717
|
+
}
|
|
31718
|
+
async function readOnboardingProfile(home = os16.homedir()) {
|
|
31719
|
+
let raw;
|
|
31720
|
+
try {
|
|
31721
|
+
raw = await fs35.readFile(file(home), "utf8");
|
|
31722
|
+
} catch {
|
|
31723
|
+
return null;
|
|
31724
|
+
}
|
|
31725
|
+
try {
|
|
31726
|
+
return canonical(JSON.parse(raw));
|
|
31727
|
+
} catch {
|
|
31728
|
+
return null;
|
|
31784
31729
|
}
|
|
31785
|
-
|
|
31730
|
+
}
|
|
31731
|
+
async function writeOnboardingProfile(profile, home = os16.homedir()) {
|
|
31732
|
+
await fs35.mkdir(dir(home), { recursive: true });
|
|
31733
|
+
const target = file(home);
|
|
31734
|
+
const tmp = `${target}.tmp`;
|
|
31735
|
+
await fs35.writeFile(tmp, JSON.stringify(profile, null, 2), { encoding: "utf8", mode: 384 });
|
|
31736
|
+
await fs35.rename(tmp, target);
|
|
31737
|
+
}
|
|
31738
|
+
function toOnboardingBlock(profile) {
|
|
31739
|
+
return {
|
|
31740
|
+
schema_version: "3",
|
|
31741
|
+
context: "",
|
|
31742
|
+
founder_name: profile.founderName,
|
|
31743
|
+
founder_email: profile.founderEmail,
|
|
31744
|
+
advisor_name: profile.advisor?.name ?? "",
|
|
31745
|
+
advisor_email: profile.advisor?.email ?? ""
|
|
31746
|
+
};
|
|
31786
31747
|
}
|
|
31787
31748
|
|
|
31788
31749
|
// src/lib/company-profile-seed.ts
|
|
31789
31750
|
function readGithubAppCreds(credsPath) {
|
|
31790
|
-
const p = credsPath ??
|
|
31751
|
+
const p = credsPath ?? path39.join(os17.homedir(), ".m8t", "github-app.json");
|
|
31791
31752
|
try {
|
|
31792
31753
|
return JSON.parse(readFileSync24(p, "utf8"));
|
|
31793
31754
|
} catch {
|
|
@@ -31862,12 +31823,8 @@ async function applyProfileToBrain(args) {
|
|
|
31862
31823
|
installationId: args.appCreds.installationId,
|
|
31863
31824
|
fetchImpl: args.fetchImpl
|
|
31864
31825
|
});
|
|
31826
|
+
const contactsOnly = args.contactsOnly === true;
|
|
31865
31827
|
const { profileMd, memoryIndexLine: companyLine } = renderCompanyProfile(args.block, args.now);
|
|
31866
|
-
const { founderMd, memoryIndexLine: founderLine } = renderFounderRecord(
|
|
31867
|
-
args.block,
|
|
31868
|
-
{ subscriptionId: args.subscriptionId, azIdentity: args.azIdentity },
|
|
31869
|
-
args.now
|
|
31870
|
-
);
|
|
31871
31828
|
const read = (p) => readRepoFileViaApp({
|
|
31872
31829
|
token,
|
|
31873
31830
|
repo: args.brainRepo,
|
|
@@ -31875,13 +31832,25 @@ async function applyProfileToBrain(args) {
|
|
|
31875
31832
|
ref: args.branch,
|
|
31876
31833
|
fetchImpl: args.fetchImpl
|
|
31877
31834
|
});
|
|
31878
|
-
const existingProfile = await read(COMPANY_PROFILE_PATH);
|
|
31835
|
+
const existingProfile = contactsOnly ? null : await read(COMPANY_PROFILE_PATH);
|
|
31879
31836
|
const existingFounder = await read(FOUNDER_RECORD_PATH);
|
|
31880
31837
|
const existingIndex = await read(MEMORY_INDEX_PATH) ?? DEFAULT_MEMORY_INDEX_HEADER;
|
|
31881
|
-
|
|
31838
|
+
const keepsExistingAdvisor = contactsOnly && existingFounder !== null && args.advisorCleared !== true && !args.block.advisor_name.trim() && !args.block.advisor_email.trim();
|
|
31839
|
+
const recovered = keepsExistingAdvisor ? readAdvisorFromRecord(existingFounder) : null;
|
|
31840
|
+
const effectiveBlock = recovered ? { ...args.block, advisor_name: recovered.name, advisor_email: recovered.email } : args.block;
|
|
31841
|
+
const { founderMd: freshFounderMd, memoryIndexLine: founderLine } = renderFounderRecord(
|
|
31842
|
+
effectiveBlock,
|
|
31843
|
+
{ subscriptionId: args.subscriptionId, azIdentity: args.azIdentity },
|
|
31844
|
+
args.now
|
|
31845
|
+
);
|
|
31846
|
+
const founderMd = contactsOnly && existingFounder !== null ? updateFounderContacts(existingFounder, args.block, {
|
|
31847
|
+
subscriptionId: args.subscriptionId,
|
|
31848
|
+
advisorCleared: args.advisorCleared
|
|
31849
|
+
}) ?? freshFounderMd : freshFounderMd;
|
|
31850
|
+
let nextIndex = contactsOnly ? existingIndex : upsertMemoryIndexOnce(existingIndex, companyLine, COMPANY_PROFILE_PATH);
|
|
31882
31851
|
nextIndex = upsertMemoryIndexOnce(nextIndex, founderLine, FOUNDER_RECORD_PATH);
|
|
31883
31852
|
const files = [
|
|
31884
|
-
...!seededDocumentMatches(existingProfile, profileMd) ? [{ path: COMPANY_PROFILE_PATH, content: profileMd }] : [],
|
|
31853
|
+
...!contactsOnly && !seededDocumentMatches(existingProfile, profileMd) ? [{ path: COMPANY_PROFILE_PATH, content: profileMd }] : [],
|
|
31885
31854
|
...!seededDocumentMatches(existingFounder, founderMd) ? [{ path: FOUNDER_RECORD_PATH, content: founderMd }] : [],
|
|
31886
31855
|
...nextIndex !== existingIndex ? [{ path: MEMORY_INDEX_PATH, content: nextIndex }] : []
|
|
31887
31856
|
];
|
|
@@ -31890,17 +31859,17 @@ async function applyProfileToBrain(args) {
|
|
|
31890
31859
|
token,
|
|
31891
31860
|
repo: args.brainRepo,
|
|
31892
31861
|
branch: args.branch,
|
|
31893
|
-
message: "seed(brain): founder + company profile from onboarding",
|
|
31862
|
+
message: contactsOnly ? "seed(brain): how to reach the founder" : "seed(brain): founder + company profile from onboarding",
|
|
31894
31863
|
files,
|
|
31895
31864
|
fetchImpl: args.fetchImpl
|
|
31896
31865
|
});
|
|
31897
31866
|
const [verifiedProfile, verifiedFounder, verifiedIndex] = await Promise.all([
|
|
31898
|
-
read(COMPANY_PROFILE_PATH),
|
|
31867
|
+
contactsOnly ? Promise.resolve(null) : read(COMPANY_PROFILE_PATH),
|
|
31899
31868
|
read(FOUNDER_RECORD_PATH),
|
|
31900
31869
|
read(MEMORY_INDEX_PATH)
|
|
31901
31870
|
]);
|
|
31902
31871
|
const mismatches = [
|
|
31903
|
-
...!seededDocumentMatches(verifiedProfile, profileMd) ? [COMPANY_PROFILE_PATH] : [],
|
|
31872
|
+
...!contactsOnly && !seededDocumentMatches(verifiedProfile, profileMd) ? [COMPANY_PROFILE_PATH] : [],
|
|
31904
31873
|
...!seededDocumentMatches(verifiedFounder, founderMd) ? [FOUNDER_RECORD_PATH] : [],
|
|
31905
31874
|
...verifiedIndex !== nextIndex ? [MEMORY_INDEX_PATH] : []
|
|
31906
31875
|
];
|
|
@@ -31948,7 +31917,7 @@ async function applyProfileToBrains(args) {
|
|
|
31948
31917
|
}
|
|
31949
31918
|
}
|
|
31950
31919
|
function spawnDetachedSeedWatch() {
|
|
31951
|
-
const logPath =
|
|
31920
|
+
const logPath = path39.join(os17.homedir(), ".m8t", "seed-profile.log");
|
|
31952
31921
|
const fd = openSync(logPath, "a");
|
|
31953
31922
|
try {
|
|
31954
31923
|
const child = spawn6(process.execPath, [process.argv[1] ?? "", "bootstrap", "seed-profile", "--watch"], {
|
|
@@ -31960,6 +31929,8 @@ function spawnDetachedSeedWatch() {
|
|
|
31960
31929
|
closeSync(fd);
|
|
31961
31930
|
}
|
|
31962
31931
|
}
|
|
31932
|
+
var NO_PROFILE_LINE = `${colors.dim("\u2139 Your advisors don't know how to reach you yet \u2014 run 'm8t bootstrap profile'.")}
|
|
31933
|
+
`;
|
|
31963
31934
|
async function reactiveSeedOnInstallComplete(args) {
|
|
31964
31935
|
const ctx = await resolveSeedContext({
|
|
31965
31936
|
endpointOverride: args.endpoint,
|
|
@@ -31968,6 +31939,23 @@ async function reactiveSeedOnInstallComplete(args) {
|
|
|
31968
31939
|
brainsOverride: args.brains
|
|
31969
31940
|
});
|
|
31970
31941
|
if (!ctx) return;
|
|
31942
|
+
const local = await readOnboardingProfile(args.home);
|
|
31943
|
+
if (local) {
|
|
31944
|
+
await applyProfileToBrains({
|
|
31945
|
+
block: toOnboardingBlock(local),
|
|
31946
|
+
brainRepos: ctx.brainRepos,
|
|
31947
|
+
branch: "main",
|
|
31948
|
+
appCreds: ctx.appCreds,
|
|
31949
|
+
subscriptionId: ctx.subscriptionId,
|
|
31950
|
+
azIdentity: { name: local.founderName, email: local.founderEmail },
|
|
31951
|
+
contactsOnly: true,
|
|
31952
|
+
advisorCleared: local.advisorCleared === true,
|
|
31953
|
+
fetchImpl: args.fetchImpl
|
|
31954
|
+
});
|
|
31955
|
+
args.stdout(`${colors.success("\u2713")} Your advisors now know how to reach you (seeded ${ctx.brainRepos.join(", ")}).
|
|
31956
|
+
`);
|
|
31957
|
+
return;
|
|
31958
|
+
}
|
|
31971
31959
|
const token = await (args.getFoundryTokenImpl ?? getFoundryToken)();
|
|
31972
31960
|
const { hadIntake, block } = await findOnboardingProfile({ endpoint: ctx.endpoint, token, fetchImpl: args.fetchImpl });
|
|
31973
31961
|
if (block) {
|
|
@@ -31985,7 +31973,10 @@ async function reactiveSeedOnInstallComplete(args) {
|
|
|
31985
31973
|
`);
|
|
31986
31974
|
return;
|
|
31987
31975
|
}
|
|
31988
|
-
if (!hadIntake)
|
|
31976
|
+
if (!hadIntake) {
|
|
31977
|
+
args.stdout(NO_PROFILE_LINE);
|
|
31978
|
+
return;
|
|
31979
|
+
}
|
|
31989
31980
|
(args.spawnWatch ?? spawnDetachedSeedWatch)();
|
|
31990
31981
|
args.stdout(
|
|
31991
31982
|
`${colors.dim("\u2139 Your advisors will pick up your company profile when you finish the intake (watching in the background).")}
|
|
@@ -32026,16 +32017,16 @@ function renderInstallSummary(args) {
|
|
|
32026
32017
|
|
|
32027
32018
|
// src/lib/companion-install.ts
|
|
32028
32019
|
import { constants as constants2 } from "fs";
|
|
32029
|
-
import * as
|
|
32030
|
-
import * as
|
|
32020
|
+
import * as fs37 from "fs/promises";
|
|
32021
|
+
import * as path41 from "path";
|
|
32031
32022
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
32032
32023
|
import { execFile, spawn as spawn7 } from "child_process";
|
|
32033
32024
|
|
|
32034
32025
|
// src/lib/companion-artifact.ts
|
|
32035
32026
|
import { createHash as createHash7 } from "crypto";
|
|
32036
32027
|
import { constants } from "fs";
|
|
32037
|
-
import * as
|
|
32038
|
-
import * as
|
|
32028
|
+
import * as fs36 from "fs/promises";
|
|
32029
|
+
import * as path40 from "path";
|
|
32039
32030
|
var SHA256 = /^[a-f0-9]{64}$/u;
|
|
32040
32031
|
var VERSION2 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u;
|
|
32041
32032
|
function exactKeys(value, expected) {
|
|
@@ -32043,23 +32034,23 @@ function exactKeys(value, expected) {
|
|
|
32043
32034
|
return actual.length === expected.length && actual.every((key2, index) => key2 === [...expected].sort()[index]);
|
|
32044
32035
|
}
|
|
32045
32036
|
function normalizedRelative(value) {
|
|
32046
|
-
if (value.length === 0 || value.includes("\\") || value.includes("\0") ||
|
|
32037
|
+
if (value.length === 0 || value.includes("\\") || value.includes("\0") || path40.posix.isAbsolute(value)) {
|
|
32047
32038
|
throw new Error("Artifact path must be a normalized relative POSIX path");
|
|
32048
32039
|
}
|
|
32049
|
-
const normalized =
|
|
32040
|
+
const normalized = path40.posix.normalize(value);
|
|
32050
32041
|
if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../")) {
|
|
32051
32042
|
throw new Error("Artifact path escapes the payload root");
|
|
32052
32043
|
}
|
|
32053
32044
|
return value;
|
|
32054
32045
|
}
|
|
32055
32046
|
function validateLink(entryPath, target) {
|
|
32056
|
-
if (target.length === 0 || target.includes("\\") || target.includes("\0") ||
|
|
32047
|
+
if (target.length === 0 || target.includes("\\") || target.includes("\0") || path40.posix.isAbsolute(target)) {
|
|
32057
32048
|
throw new Error("Artifact symlink target must be relative");
|
|
32058
32049
|
}
|
|
32059
|
-
const resolved =
|
|
32060
|
-
|
|
32050
|
+
const resolved = path40.posix.normalize(
|
|
32051
|
+
path40.posix.join(path40.posix.dirname(entryPath), target)
|
|
32061
32052
|
);
|
|
32062
|
-
if (resolved === ".." || resolved.startsWith("../") ||
|
|
32053
|
+
if (resolved === ".." || resolved.startsWith("../") || path40.posix.isAbsolute(resolved)) {
|
|
32063
32054
|
throw new Error("Artifact symlink target escapes the payload root");
|
|
32064
32055
|
}
|
|
32065
32056
|
return target;
|
|
@@ -32140,17 +32131,17 @@ function parseArtifactManifest(value) {
|
|
|
32140
32131
|
};
|
|
32141
32132
|
}
|
|
32142
32133
|
async function sha256File2(filePath) {
|
|
32143
|
-
return createHash7("sha256").update(await
|
|
32134
|
+
return createHash7("sha256").update(await fs36.readFile(filePath)).digest("hex");
|
|
32144
32135
|
}
|
|
32145
32136
|
async function walk2(root, relative4 = "") {
|
|
32146
|
-
const directory =
|
|
32147
|
-
const children = await
|
|
32137
|
+
const directory = path40.join(root, ...relative4.split("/").filter(Boolean));
|
|
32138
|
+
const children = await fs36.readdir(directory, { withFileTypes: true });
|
|
32148
32139
|
const entries = [];
|
|
32149
32140
|
for (const child of children.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
32150
32141
|
const childRelative = relative4 ? `${relative4}/${child.name}` : child.name;
|
|
32151
32142
|
normalizedRelative(childRelative);
|
|
32152
|
-
const childPath =
|
|
32153
|
-
const stat5 = await
|
|
32143
|
+
const childPath = path40.join(directory, child.name);
|
|
32144
|
+
const stat5 = await fs36.lstat(childPath);
|
|
32154
32145
|
if (stat5.isDirectory()) {
|
|
32155
32146
|
entries.push(...await walk2(root, childRelative));
|
|
32156
32147
|
} else if (stat5.isFile()) {
|
|
@@ -32162,7 +32153,7 @@ async function walk2(root, relative4 = "") {
|
|
|
32162
32153
|
sha256: await sha256File2(childPath)
|
|
32163
32154
|
});
|
|
32164
32155
|
} else if (stat5.isSymbolicLink()) {
|
|
32165
|
-
const target = await
|
|
32156
|
+
const target = await fs36.readlink(childPath);
|
|
32166
32157
|
entries.push({
|
|
32167
32158
|
type: "symlink",
|
|
32168
32159
|
path: childRelative,
|
|
@@ -32175,19 +32166,19 @@ async function walk2(root, relative4 = "") {
|
|
|
32175
32166
|
return entries;
|
|
32176
32167
|
}
|
|
32177
32168
|
async function ensureRealDirectory(root) {
|
|
32178
|
-
const stat5 = await
|
|
32169
|
+
const stat5 = await fs36.lstat(root);
|
|
32179
32170
|
if (stat5.isSymbolicLink()) throw new Error("Artifact root is a symbolic link");
|
|
32180
32171
|
if (!stat5.isDirectory()) throw new Error("Artifact root is not a directory");
|
|
32181
32172
|
}
|
|
32182
32173
|
async function validateResolvedLinks(root, entries) {
|
|
32183
|
-
const realRoot = await
|
|
32174
|
+
const realRoot = await fs36.realpath(root);
|
|
32184
32175
|
for (const entry of entries) {
|
|
32185
32176
|
if (entry.type !== "symlink") continue;
|
|
32186
32177
|
try {
|
|
32187
|
-
const linkPath =
|
|
32188
|
-
const resolved = await
|
|
32189
|
-
const relative4 =
|
|
32190
|
-
if (relative4 === ".." || relative4.startsWith(`..${
|
|
32178
|
+
const linkPath = path40.join(root, ...entry.path.split("/"));
|
|
32179
|
+
const resolved = await fs36.realpath(linkPath);
|
|
32180
|
+
const relative4 = path40.relative(realRoot, resolved);
|
|
32181
|
+
if (relative4 === ".." || relative4.startsWith(`..${path40.sep}`) || path40.isAbsolute(relative4)) {
|
|
32191
32182
|
throw new Error("Artifact symlink chain escapes the payload root");
|
|
32192
32183
|
}
|
|
32193
32184
|
} catch (error) {
|
|
@@ -32217,13 +32208,13 @@ async function buildArtifactManifest(payloadRoot, input) {
|
|
|
32217
32208
|
return manifest;
|
|
32218
32209
|
}
|
|
32219
32210
|
async function readArtifactManifest(manifestPath) {
|
|
32220
|
-
const before = await
|
|
32211
|
+
const before = await fs36.lstat(manifestPath);
|
|
32221
32212
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
32222
32213
|
throw new Error("Artifact manifest is a symbolic link or non-file");
|
|
32223
32214
|
}
|
|
32224
32215
|
let handle;
|
|
32225
32216
|
try {
|
|
32226
|
-
handle = await
|
|
32217
|
+
handle = await fs36.open(
|
|
32227
32218
|
manifestPath,
|
|
32228
32219
|
constants.O_RDONLY | constants.O_NOFOLLOW
|
|
32229
32220
|
);
|
|
@@ -32240,9 +32231,9 @@ async function readArtifactManifest(manifestPath) {
|
|
|
32240
32231
|
}
|
|
32241
32232
|
}
|
|
32242
32233
|
function ensureContainedEntry(root, entryPath) {
|
|
32243
|
-
const absolute =
|
|
32244
|
-
const relative4 =
|
|
32245
|
-
if (relative4.startsWith("..") ||
|
|
32234
|
+
const absolute = path40.join(root, ...entryPath.split("/"));
|
|
32235
|
+
const relative4 = path40.relative(root, absolute);
|
|
32236
|
+
if (relative4.startsWith("..") || path40.isAbsolute(relative4)) {
|
|
32246
32237
|
throw new Error("Artifact entry escapes payload root");
|
|
32247
32238
|
}
|
|
32248
32239
|
return absolute;
|
|
@@ -32281,27 +32272,27 @@ async function verifyArtifactPayload(payloadRoot, manifest) {
|
|
|
32281
32272
|
async function copyArtifactPayload(sourceRoot, targetRoot, manifest) {
|
|
32282
32273
|
await verifyArtifactSource(sourceRoot, manifest);
|
|
32283
32274
|
try {
|
|
32284
|
-
const targetStat = await
|
|
32275
|
+
const targetStat = await fs36.lstat(targetRoot);
|
|
32285
32276
|
if (targetStat.isSymbolicLink()) {
|
|
32286
32277
|
throw new Error("Install target is a symbolic link");
|
|
32287
32278
|
}
|
|
32288
32279
|
if (!targetStat.isDirectory()) throw new Error("Install target is not a directory");
|
|
32289
|
-
if ((await
|
|
32280
|
+
if ((await fs36.readdir(targetRoot)).length > 0) {
|
|
32290
32281
|
throw new Error("Install staging target is not empty");
|
|
32291
32282
|
}
|
|
32292
32283
|
} catch (error) {
|
|
32293
32284
|
if (error.code !== "ENOENT") throw error;
|
|
32294
|
-
await
|
|
32285
|
+
await fs36.mkdir(targetRoot, { recursive: false, mode: 448 });
|
|
32295
32286
|
}
|
|
32296
32287
|
for (const entry of manifest.entries) {
|
|
32297
32288
|
const source = ensureContainedEntry(sourceRoot, entry.path);
|
|
32298
32289
|
const target = ensureContainedEntry(targetRoot, entry.path);
|
|
32299
|
-
await
|
|
32290
|
+
await fs36.mkdir(path40.dirname(target), { recursive: true, mode: 448 });
|
|
32300
32291
|
if (entry.type === "file") {
|
|
32301
|
-
await
|
|
32302
|
-
await
|
|
32292
|
+
await fs36.copyFile(source, target);
|
|
32293
|
+
await fs36.chmod(target, entry.mode);
|
|
32303
32294
|
} else {
|
|
32304
|
-
await
|
|
32295
|
+
await fs36.symlink(entry.target, target);
|
|
32305
32296
|
}
|
|
32306
32297
|
}
|
|
32307
32298
|
await verifyArtifactPayload(targetRoot, manifest);
|
|
@@ -32317,18 +32308,18 @@ function commandError(error, message) {
|
|
|
32317
32308
|
if (code !== void 0) wrapped.code = code;
|
|
32318
32309
|
return wrapped;
|
|
32319
32310
|
}
|
|
32320
|
-
function execFileAsync(
|
|
32311
|
+
function execFileAsync(file2, args) {
|
|
32321
32312
|
return new Promise((resolve6, reject) => {
|
|
32322
|
-
execFile(
|
|
32313
|
+
execFile(file2, [...args], { windowsHide: true }, (error) => {
|
|
32323
32314
|
if (error) reject(commandError(error, "Login-item command failed"));
|
|
32324
32315
|
else resolve6();
|
|
32325
32316
|
});
|
|
32326
32317
|
});
|
|
32327
32318
|
}
|
|
32328
|
-
function execFileOutput(
|
|
32319
|
+
function execFileOutput(file2, args) {
|
|
32329
32320
|
return new Promise((resolve6, reject) => {
|
|
32330
32321
|
execFile(
|
|
32331
|
-
|
|
32322
|
+
file2,
|
|
32332
32323
|
[...args],
|
|
32333
32324
|
{ windowsHide: true },
|
|
32334
32325
|
(error, stdout) => {
|
|
@@ -32340,25 +32331,25 @@ function execFileOutput(file, args) {
|
|
|
32340
32331
|
}
|
|
32341
32332
|
async function setCompanionStartAtLogin(input) {
|
|
32342
32333
|
if (input.platform === "darwin") {
|
|
32343
|
-
const launchAgents =
|
|
32344
|
-
const registration =
|
|
32334
|
+
const launchAgents = path41.join(input.homeDirectory, "Library", "LaunchAgents");
|
|
32335
|
+
const registration = path41.join(
|
|
32345
32336
|
launchAgents,
|
|
32346
32337
|
"com.m8t.companion.plist"
|
|
32347
32338
|
);
|
|
32348
32339
|
await assertNotSymlink(registration, "Start-at-login registration");
|
|
32349
32340
|
if (!input.enabled) {
|
|
32350
|
-
await
|
|
32341
|
+
await fs37.rm(registration, { force: true });
|
|
32351
32342
|
return;
|
|
32352
32343
|
}
|
|
32353
|
-
await
|
|
32344
|
+
await fs37.mkdir(launchAgents, { recursive: true, mode: 448 });
|
|
32354
32345
|
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";
|
|
32355
32346
|
await atomicWriteText(registration, plist, 384);
|
|
32356
32347
|
return;
|
|
32357
32348
|
}
|
|
32358
32349
|
if (input.platform === "win32") {
|
|
32359
32350
|
const key2 = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
32360
|
-
const run2 = input.runCommand ?? (async (
|
|
32361
|
-
await execFileAsync(
|
|
32351
|
+
const run2 = input.runCommand ?? (async (file2, args) => {
|
|
32352
|
+
await execFileAsync(file2, args);
|
|
32362
32353
|
return "";
|
|
32363
32354
|
});
|
|
32364
32355
|
if (input.enabled) {
|
|
@@ -32402,7 +32393,7 @@ async function setCompanionStartAtLogin(input) {
|
|
|
32402
32393
|
}
|
|
32403
32394
|
async function getCompanionStartAtLogin(input) {
|
|
32404
32395
|
if (input.platform === "darwin") {
|
|
32405
|
-
const registration =
|
|
32396
|
+
const registration = path41.join(
|
|
32406
32397
|
input.homeDirectory,
|
|
32407
32398
|
"Library",
|
|
32408
32399
|
"LaunchAgents",
|
|
@@ -32464,26 +32455,26 @@ function assertSupported(options) {
|
|
|
32464
32455
|
}
|
|
32465
32456
|
function companionInstallPaths(options) {
|
|
32466
32457
|
assertSupported(options);
|
|
32467
|
-
const companionState =
|
|
32458
|
+
const companionState = path41.join(
|
|
32468
32459
|
options.homeDirectory,
|
|
32469
32460
|
".m8t",
|
|
32470
32461
|
"companion"
|
|
32471
32462
|
);
|
|
32472
|
-
const targetRoot = options.platform === "darwin" ?
|
|
32463
|
+
const targetRoot = options.platform === "darwin" ? path41.join(
|
|
32473
32464
|
options.homeDirectory,
|
|
32474
32465
|
"Applications",
|
|
32475
32466
|
"m8t Companion.app"
|
|
32476
|
-
) :
|
|
32477
|
-
options.localAppData ??
|
|
32467
|
+
) : path41.join(
|
|
32468
|
+
options.localAppData ?? path41.join(options.homeDirectory, "AppData", "Local"),
|
|
32478
32469
|
"m8t",
|
|
32479
32470
|
"companion",
|
|
32480
32471
|
"app"
|
|
32481
32472
|
);
|
|
32482
32473
|
return {
|
|
32483
32474
|
targetRoot,
|
|
32484
|
-
installManifest:
|
|
32485
|
-
runtimeBinding:
|
|
32486
|
-
preferences:
|
|
32475
|
+
installManifest: path41.join(companionState, "install-v1.json"),
|
|
32476
|
+
runtimeBinding: path41.join(companionState, "runtime-v1.json"),
|
|
32477
|
+
preferences: path41.join(companionState, "preferences-v1.json")
|
|
32487
32478
|
};
|
|
32488
32479
|
}
|
|
32489
32480
|
function exactKeys2(value, keys) {
|
|
@@ -32494,7 +32485,7 @@ function parseInstallManifest(value) {
|
|
|
32494
32485
|
throw new Error("Install manifest schema is invalid");
|
|
32495
32486
|
}
|
|
32496
32487
|
const record = value;
|
|
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" || !
|
|
32488
|
+
if (!exactKeys2(record, INSTALL_KEYS) || record.schemaVersion !== 1 || typeof record.version !== "string" || record.platform !== "darwin" && record.platform !== "win32" || record.architecture !== "arm64" && record.architecture !== "x64" || typeof record.entryRelativePath !== "string" || typeof record.artifactTreeSha256 !== "string" || !/^[a-f0-9]{64}$/u.test(record.artifactTreeSha256) || typeof record.ownedTargetRoot !== "string" || !path41.isAbsolute(record.ownedTargetRoot)) {
|
|
32498
32489
|
throw new Error("Install manifest schema is invalid");
|
|
32499
32490
|
}
|
|
32500
32491
|
return record;
|
|
@@ -32504,7 +32495,7 @@ function parseRuntimeBinding(value, platform) {
|
|
|
32504
32495
|
throw new Error("Runtime binding schema is invalid");
|
|
32505
32496
|
}
|
|
32506
32497
|
const record = value;
|
|
32507
|
-
const paths = platform === "win32" ?
|
|
32498
|
+
const paths = platform === "win32" ? path41.win32 : path41.posix;
|
|
32508
32499
|
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") {
|
|
32509
32500
|
throw new Error("Runtime binding schema is invalid");
|
|
32510
32501
|
}
|
|
@@ -32529,7 +32520,7 @@ function validateGatewayOrigin(value) {
|
|
|
32529
32520
|
}
|
|
32530
32521
|
async function assertNotSymlink(filePath, kind) {
|
|
32531
32522
|
try {
|
|
32532
|
-
if ((await
|
|
32523
|
+
if ((await fs37.lstat(filePath)).isSymbolicLink()) {
|
|
32533
32524
|
throw new Error(`${kind} is a symbolic link`);
|
|
32534
32525
|
}
|
|
32535
32526
|
} catch (error) {
|
|
@@ -32538,20 +32529,20 @@ async function assertNotSymlink(filePath, kind) {
|
|
|
32538
32529
|
}
|
|
32539
32530
|
}
|
|
32540
32531
|
async function assertOwnedDirectoryChain(anchor, targetDirectory) {
|
|
32541
|
-
const relative4 =
|
|
32542
|
-
if (relative4 === ".." || relative4.startsWith(`..${
|
|
32532
|
+
const relative4 = path41.relative(anchor, targetDirectory);
|
|
32533
|
+
if (relative4 === ".." || relative4.startsWith(`..${path41.sep}`) || path41.isAbsolute(relative4)) {
|
|
32543
32534
|
throw new Error("Companion owned directory escapes its trusted anchor");
|
|
32544
32535
|
}
|
|
32545
|
-
const segments = relative4.split(
|
|
32536
|
+
const segments = relative4.split(path41.sep).filter(Boolean);
|
|
32546
32537
|
let current = anchor;
|
|
32547
|
-
const anchorStatus = await
|
|
32538
|
+
const anchorStatus = await fs37.lstat(anchor);
|
|
32548
32539
|
if (anchorStatus.isSymbolicLink() || !anchorStatus.isDirectory()) {
|
|
32549
32540
|
throw new Error("Companion owned directory anchor is unsafe");
|
|
32550
32541
|
}
|
|
32551
32542
|
for (const segment of segments) {
|
|
32552
|
-
current =
|
|
32543
|
+
current = path41.join(current, segment);
|
|
32553
32544
|
try {
|
|
32554
|
-
const status = await
|
|
32545
|
+
const status = await fs37.lstat(current);
|
|
32555
32546
|
if (status.isSymbolicLink() || !status.isDirectory()) {
|
|
32556
32547
|
throw new Error("Companion owned directory contains a symbolic link");
|
|
32557
32548
|
}
|
|
@@ -32564,19 +32555,19 @@ async function assertOwnedDirectoryChain(anchor, targetDirectory) {
|
|
|
32564
32555
|
async function assertOwnedParents(options, paths) {
|
|
32565
32556
|
await assertOwnedDirectoryChain(
|
|
32566
32557
|
options.homeDirectory,
|
|
32567
|
-
|
|
32558
|
+
path41.dirname(paths.installManifest)
|
|
32568
32559
|
);
|
|
32569
|
-
const targetAnchor = options.platform === "win32" ? options.localAppData ??
|
|
32570
|
-
await assertOwnedDirectoryChain(targetAnchor,
|
|
32560
|
+
const targetAnchor = options.platform === "win32" ? options.localAppData ?? path41.join(options.homeDirectory, "AppData", "Local") : options.homeDirectory;
|
|
32561
|
+
await assertOwnedDirectoryChain(targetAnchor, path41.dirname(paths.targetRoot));
|
|
32571
32562
|
}
|
|
32572
32563
|
async function readRegularText(filePath, maxBytes) {
|
|
32573
|
-
const before = await
|
|
32564
|
+
const before = await fs37.lstat(filePath);
|
|
32574
32565
|
if (before.isSymbolicLink() || !before.isFile()) {
|
|
32575
32566
|
throw new Error("Companion state file is not a regular file");
|
|
32576
32567
|
}
|
|
32577
32568
|
let handle;
|
|
32578
32569
|
try {
|
|
32579
|
-
handle = await
|
|
32570
|
+
handle = await fs37.open(
|
|
32580
32571
|
filePath,
|
|
32581
32572
|
constants2.O_RDONLY | constants2.O_NOFOLLOW
|
|
32582
32573
|
);
|
|
@@ -32599,27 +32590,27 @@ async function atomicWriteJson(filePath, value) {
|
|
|
32599
32590
|
}
|
|
32600
32591
|
async function atomicWriteText(filePath, contents, mode) {
|
|
32601
32592
|
await assertNotSymlink(filePath, "Companion state file");
|
|
32602
|
-
await
|
|
32593
|
+
await fs37.mkdir(path41.dirname(filePath), {
|
|
32603
32594
|
recursive: true,
|
|
32604
32595
|
mode: 448
|
|
32605
32596
|
});
|
|
32606
32597
|
const temporary = `${filePath}.${randomUUID3()}.tmp`;
|
|
32607
32598
|
try {
|
|
32608
|
-
await
|
|
32599
|
+
await fs37.writeFile(temporary, contents, {
|
|
32609
32600
|
mode,
|
|
32610
32601
|
flag: "wx"
|
|
32611
32602
|
});
|
|
32612
|
-
await
|
|
32613
|
-
await
|
|
32603
|
+
await fs37.rename(temporary, filePath);
|
|
32604
|
+
await fs37.chmod(filePath, mode).catch(() => void 0);
|
|
32614
32605
|
} finally {
|
|
32615
|
-
await
|
|
32606
|
+
await fs37.rm(temporary, { force: true }).catch(() => void 0);
|
|
32616
32607
|
}
|
|
32617
32608
|
}
|
|
32618
32609
|
async function realRegularFile(filePath, executable) {
|
|
32619
|
-
const real = await
|
|
32620
|
-
const stat5 = await
|
|
32610
|
+
const real = await fs37.realpath(filePath);
|
|
32611
|
+
const stat5 = await fs37.stat(real);
|
|
32621
32612
|
if (!stat5.isFile()) throw new Error("Companion launch target is not a file");
|
|
32622
|
-
await
|
|
32613
|
+
await fs37.access(real, executable ? constants2.X_OK : constants2.R_OK);
|
|
32623
32614
|
return real;
|
|
32624
32615
|
}
|
|
32625
32616
|
function defaultLaunch(executable) {
|
|
@@ -32635,7 +32626,7 @@ async function companionIsRunning(platform, executable) {
|
|
|
32635
32626
|
if (platform !== "win32") return false;
|
|
32636
32627
|
let handle;
|
|
32637
32628
|
try {
|
|
32638
|
-
handle = await
|
|
32629
|
+
handle = await fs37.open(executable, "r+");
|
|
32639
32630
|
} catch (error) {
|
|
32640
32631
|
const code = error.code;
|
|
32641
32632
|
if (code === "ENOENT") return false;
|
|
@@ -32716,7 +32707,7 @@ async function statusCompanion(options) {
|
|
|
32716
32707
|
realRegularFile(runtime.nodeExecutable, options.platform !== "win32"),
|
|
32717
32708
|
realRegularFile(runtime.cliEntry, false)
|
|
32718
32709
|
]);
|
|
32719
|
-
const executable =
|
|
32710
|
+
const executable = path41.join(
|
|
32720
32711
|
paths.targetRoot,
|
|
32721
32712
|
...install.entryRelativePath.split("/")
|
|
32722
32713
|
);
|
|
@@ -32749,7 +32740,7 @@ async function snapshotFile(filePath) {
|
|
|
32749
32740
|
}
|
|
32750
32741
|
async function restoreFile(filePath, bytes) {
|
|
32751
32742
|
if (bytes === null) {
|
|
32752
|
-
await
|
|
32743
|
+
await fs37.rm(filePath, { force: true });
|
|
32753
32744
|
} else {
|
|
32754
32745
|
await atomicWriteJson(filePath, JSON.parse(bytes.toString("utf8")));
|
|
32755
32746
|
}
|
|
@@ -32764,8 +32755,8 @@ async function converge(options, force) {
|
|
|
32764
32755
|
if (artifactManifest.platform !== options.platform || artifactManifest.architecture !== options.architecture) {
|
|
32765
32756
|
throw new Error("Companion artifact does not match this OS and architecture");
|
|
32766
32757
|
}
|
|
32767
|
-
const payloadRoot =
|
|
32768
|
-
|
|
32758
|
+
const payloadRoot = path41.join(
|
|
32759
|
+
path41.dirname(options.artifactManifestPath),
|
|
32769
32760
|
"payload"
|
|
32770
32761
|
);
|
|
32771
32762
|
await verifyArtifactSource(payloadRoot, artifactManifest);
|
|
@@ -32785,7 +32776,7 @@ async function converge(options, force) {
|
|
|
32785
32776
|
if (priorInstalled) {
|
|
32786
32777
|
await assertCompanionNotRunning(
|
|
32787
32778
|
options,
|
|
32788
|
-
|
|
32779
|
+
path41.join(
|
|
32789
32780
|
priorInstalled.paths.targetRoot,
|
|
32790
32781
|
...priorInstalled.install.entryRelativePath.split("/")
|
|
32791
32782
|
)
|
|
@@ -32793,7 +32784,7 @@ async function converge(options, force) {
|
|
|
32793
32784
|
}
|
|
32794
32785
|
if (current.state === "not-installed") {
|
|
32795
32786
|
try {
|
|
32796
|
-
await
|
|
32787
|
+
await fs37.lstat(paths.targetRoot);
|
|
32797
32788
|
throw new Error(
|
|
32798
32789
|
"The fixed companion target exists without an owned install manifest"
|
|
32799
32790
|
);
|
|
@@ -32804,7 +32795,7 @@ async function converge(options, force) {
|
|
|
32804
32795
|
const stage = `${paths.targetRoot}.m8t-stage-${randomUUID3()}`;
|
|
32805
32796
|
const backup = `${paths.targetRoot}.m8t-backup-${randomUUID3()}`;
|
|
32806
32797
|
const copy = options.copyPayload ?? copyArtifactPayload;
|
|
32807
|
-
await
|
|
32798
|
+
await fs37.mkdir(path41.dirname(paths.targetRoot), {
|
|
32808
32799
|
recursive: true,
|
|
32809
32800
|
mode: 448
|
|
32810
32801
|
});
|
|
@@ -32821,12 +32812,12 @@ async function converge(options, force) {
|
|
|
32821
32812
|
try {
|
|
32822
32813
|
await copy(payloadRoot, stage, artifactManifest);
|
|
32823
32814
|
try {
|
|
32824
|
-
await
|
|
32815
|
+
await fs37.rename(paths.targetRoot, backup);
|
|
32825
32816
|
movedPrior = true;
|
|
32826
32817
|
} catch (error) {
|
|
32827
32818
|
if (error.code !== "ENOENT") throw error;
|
|
32828
32819
|
}
|
|
32829
|
-
await
|
|
32820
|
+
await fs37.rename(stage, paths.targetRoot);
|
|
32830
32821
|
installedStage = true;
|
|
32831
32822
|
const nodeExecutable = await realRegularFile(
|
|
32832
32823
|
options.nodeExecutable,
|
|
@@ -32868,7 +32859,7 @@ async function converge(options, force) {
|
|
|
32868
32859
|
await readClosedJson(paths.runtimeBinding),
|
|
32869
32860
|
options.platform
|
|
32870
32861
|
);
|
|
32871
|
-
const executable =
|
|
32862
|
+
const executable = path41.join(
|
|
32872
32863
|
paths.targetRoot,
|
|
32873
32864
|
...artifactManifest.entryRelativePath.split("/")
|
|
32874
32865
|
);
|
|
@@ -32878,7 +32869,7 @@ async function converge(options, force) {
|
|
|
32878
32869
|
platform: options.platform,
|
|
32879
32870
|
executable: ownedExecutable
|
|
32880
32871
|
}));
|
|
32881
|
-
priorLoginExecutable = priorInstalled ?
|
|
32872
|
+
priorLoginExecutable = priorInstalled ? path41.join(
|
|
32882
32873
|
priorInstalled.paths.targetRoot,
|
|
32883
32874
|
...priorInstalled.install.entryRelativePath.split("/")
|
|
32884
32875
|
) : executable;
|
|
@@ -32889,7 +32880,7 @@ async function converge(options, force) {
|
|
|
32889
32880
|
);
|
|
32890
32881
|
loginChanged = true;
|
|
32891
32882
|
await (options.launch ?? defaultLaunch)(executable);
|
|
32892
|
-
await
|
|
32883
|
+
await fs37.rm(backup, { recursive: true, force: true });
|
|
32893
32884
|
return {
|
|
32894
32885
|
state: "installed",
|
|
32895
32886
|
version: artifactManifest.version,
|
|
@@ -32903,15 +32894,15 @@ async function converge(options, force) {
|
|
|
32903
32894
|
() => void 0
|
|
32904
32895
|
);
|
|
32905
32896
|
}
|
|
32906
|
-
await
|
|
32897
|
+
await fs37.rm(stage, { recursive: true, force: true }).catch(() => void 0);
|
|
32907
32898
|
if (installedStage) {
|
|
32908
|
-
await
|
|
32899
|
+
await fs37.rm(paths.targetRoot, {
|
|
32909
32900
|
recursive: true,
|
|
32910
32901
|
force: true
|
|
32911
32902
|
}).catch(() => void 0);
|
|
32912
32903
|
}
|
|
32913
32904
|
if (movedPrior) {
|
|
32914
|
-
await
|
|
32905
|
+
await fs37.rename(backup, paths.targetRoot).catch(() => void 0);
|
|
32915
32906
|
}
|
|
32916
32907
|
await Promise.all([
|
|
32917
32908
|
restoreFile(paths.installManifest, snapshots[0]),
|
|
@@ -32937,14 +32928,14 @@ async function uninstallCompanion(options) {
|
|
|
32937
32928
|
if (install.ownedTargetRoot !== paths.targetRoot) {
|
|
32938
32929
|
throw new Error("Refusing to remove a non-owned companion target");
|
|
32939
32930
|
}
|
|
32940
|
-
const executable =
|
|
32931
|
+
const executable = path41.join(
|
|
32941
32932
|
paths.targetRoot,
|
|
32942
32933
|
...install.entryRelativePath.split("/")
|
|
32943
32934
|
);
|
|
32944
32935
|
await assertCompanionNotRunning(options, executable);
|
|
32945
32936
|
await (options.setStartAtLogin ?? (() => Promise.resolve()))(executable, false);
|
|
32946
32937
|
const remove = options.removeOwnedPath ?? (async (ownedPath, recursive) => {
|
|
32947
|
-
await
|
|
32938
|
+
await fs37.rm(ownedPath, { recursive, force: true });
|
|
32948
32939
|
});
|
|
32949
32940
|
await remove(paths.targetRoot, true);
|
|
32950
32941
|
await remove(paths.installManifest, false);
|
|
@@ -32954,8 +32945,8 @@ async function uninstallCompanion(options) {
|
|
|
32954
32945
|
}
|
|
32955
32946
|
|
|
32956
32947
|
// src/commands/companion/install.ts
|
|
32957
|
-
import * as
|
|
32958
|
-
import * as
|
|
32948
|
+
import * as os19 from "os";
|
|
32949
|
+
import * as path43 from "path";
|
|
32959
32950
|
import { Command as Command59, Option as Option56 } from "clipanion";
|
|
32960
32951
|
|
|
32961
32952
|
// src/lib/companion-channel.ts
|
|
@@ -32973,9 +32964,9 @@ async function readCompanionRelease(source = { url: CHANNEL_LATEST_URL }, deps =
|
|
|
32973
32964
|
// src/lib/companion-download.ts
|
|
32974
32965
|
import { createHash as createHash8 } from "crypto";
|
|
32975
32966
|
import { execFile as execFile2 } from "child_process";
|
|
32976
|
-
import * as
|
|
32977
|
-
import * as
|
|
32978
|
-
import * as
|
|
32967
|
+
import * as fs38 from "fs/promises";
|
|
32968
|
+
import * as os18 from "os";
|
|
32969
|
+
import * as path42 from "path";
|
|
32979
32970
|
init_errors();
|
|
32980
32971
|
var MAX_ASSET_BYTES = 512 * 1024 * 1024;
|
|
32981
32972
|
function extractArchive(archive, into) {
|
|
@@ -33028,20 +33019,20 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
33028
33019
|
hint: "Nothing was unpacked. This is what a corrupted download or a substituted file looks like \u2014 retry, and report it if it persists."
|
|
33029
33020
|
});
|
|
33030
33021
|
}
|
|
33031
|
-
const root = await (deps.makeTemporaryDirectory ?? (() =>
|
|
33022
|
+
const root = await (deps.makeTemporaryDirectory ?? (() => fs38.mkdtemp(path42.join(os18.tmpdir(), "m8t-companion-"))))();
|
|
33032
33023
|
const dispose = async () => {
|
|
33033
|
-
await
|
|
33024
|
+
await fs38.rm(root, { recursive: true, force: true }).catch(() => void 0);
|
|
33034
33025
|
};
|
|
33035
33026
|
try {
|
|
33036
|
-
const archive =
|
|
33037
|
-
await
|
|
33038
|
-
const unpacked =
|
|
33039
|
-
await
|
|
33027
|
+
const archive = path42.join(root, pinned.asset);
|
|
33028
|
+
await fs38.writeFile(archive, bytes, { mode: 384 });
|
|
33029
|
+
const unpacked = path42.join(root, "unpacked");
|
|
33030
|
+
await fs38.mkdir(unpacked, { recursive: false, mode: 448 });
|
|
33040
33031
|
await (deps.extract ?? extractArchive)(archive, unpacked);
|
|
33041
|
-
await
|
|
33032
|
+
await fs38.rm(archive, { force: true });
|
|
33042
33033
|
return {
|
|
33043
33034
|
version: component.version,
|
|
33044
|
-
artifactManifestPath:
|
|
33035
|
+
artifactManifestPath: path42.join(unpacked, "artifact-v1.json"),
|
|
33045
33036
|
dispose
|
|
33046
33037
|
};
|
|
33047
33038
|
} catch (error) {
|
|
@@ -33054,7 +33045,7 @@ async function downloadCompanionArtifact(component, deps = {}) {
|
|
|
33054
33045
|
function defaultLocalCompanionInstallOptions() {
|
|
33055
33046
|
const cliEntry = process.argv[1];
|
|
33056
33047
|
if (!cliEntry) throw new Error("Cannot resolve the installed CLI entry");
|
|
33057
|
-
const homeDirectory =
|
|
33048
|
+
const homeDirectory = os19.homedir();
|
|
33058
33049
|
const platform = process.platform;
|
|
33059
33050
|
return {
|
|
33060
33051
|
homeDirectory,
|
|
@@ -33072,7 +33063,7 @@ async function convergeCompanionFromChannel(converge2, options = {}) {
|
|
|
33072
33063
|
let artifactManifestPath;
|
|
33073
33064
|
let dispose = () => Promise.resolve();
|
|
33074
33065
|
if (stagedDirectory !== void 0) {
|
|
33075
|
-
artifactManifestPath =
|
|
33066
|
+
artifactManifestPath = path43.resolve(stagedDirectory, "artifact-v1.json");
|
|
33076
33067
|
} else {
|
|
33077
33068
|
const release = await readCompanionRelease(
|
|
33078
33069
|
platformVersion !== void 0 ? { channel: true, version: platformTag(platformVersion) } : { url: CHANNEL_LATEST_URL }
|
|
@@ -33148,9 +33139,9 @@ function resolveRepoRootMarker(args) {
|
|
|
33148
33139
|
if (args.existing !== null && args.existing !== "") return args.existing;
|
|
33149
33140
|
return args.cwd;
|
|
33150
33141
|
}
|
|
33151
|
-
async function looksLikeCheckout(
|
|
33142
|
+
async function looksLikeCheckout(dir2) {
|
|
33152
33143
|
try {
|
|
33153
|
-
return (await
|
|
33144
|
+
return (await fs39.stat(path44.join(dir2, "brain-template"))).isDirectory();
|
|
33154
33145
|
} catch {
|
|
33155
33146
|
return false;
|
|
33156
33147
|
}
|
|
@@ -33166,21 +33157,21 @@ var defaultDeps3 = {
|
|
|
33166
33157
|
convergeCompanion: (platformVersion) => convergeCompanionFromChannel(installCompanion, {
|
|
33167
33158
|
...platformVersion !== void 0 ? { platformVersion } : {}
|
|
33168
33159
|
}),
|
|
33169
|
-
homedir: () =>
|
|
33160
|
+
homedir: () => os20.homedir()
|
|
33170
33161
|
};
|
|
33171
33162
|
async function finalizeInstall(args, deps = defaultDeps3) {
|
|
33172
|
-
const markerDir =
|
|
33173
|
-
const markerPath =
|
|
33163
|
+
const markerDir = path44.join(deps.homedir(), ".m8t");
|
|
33164
|
+
const markerPath = path44.join(markerDir, "repo-root");
|
|
33174
33165
|
const cwd = process.cwd();
|
|
33175
|
-
const existing = await
|
|
33166
|
+
const existing = await fs39.readFile(markerPath, "utf8").then((s) => s.trim()).catch(() => null);
|
|
33176
33167
|
const repoRoot = resolveRepoRootMarker({
|
|
33177
33168
|
...args.repoRoot !== void 0 ? { explicit: args.repoRoot } : {},
|
|
33178
33169
|
cwd,
|
|
33179
33170
|
cwdIsCheckout: await looksLikeCheckout(cwd),
|
|
33180
33171
|
existing
|
|
33181
33172
|
});
|
|
33182
|
-
await
|
|
33183
|
-
await
|
|
33173
|
+
await fs39.mkdir(markerDir, { recursive: true });
|
|
33174
|
+
await fs39.writeFile(markerPath, `${repoRoot}
|
|
33184
33175
|
`, "utf8");
|
|
33185
33176
|
let webappUrl;
|
|
33186
33177
|
try {
|
|
@@ -33258,7 +33249,7 @@ async function finalizeInstall(args, deps = defaultDeps3) {
|
|
|
33258
33249
|
}
|
|
33259
33250
|
let brainOrg = null;
|
|
33260
33251
|
try {
|
|
33261
|
-
const credsRaw = await
|
|
33252
|
+
const credsRaw = await fs39.readFile(path44.join(markerDir, "github-app.json"), "utf8");
|
|
33262
33253
|
const creds = JSON.parse(credsRaw);
|
|
33263
33254
|
brainOrg = typeof creds.org === "string" ? creds.org : null;
|
|
33264
33255
|
} catch {
|
|
@@ -33371,7 +33362,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
33371
33362
|
typeof this.output === "string" ? this.output : void 0,
|
|
33372
33363
|
this.context.stdout
|
|
33373
33364
|
);
|
|
33374
|
-
const
|
|
33365
|
+
const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
33375
33366
|
const watch = this.watch === true;
|
|
33376
33367
|
const invokedPath = this.path ?? [];
|
|
33377
33368
|
const viaFinishAlias = invokedPath[invokedPath.length - 1] === "finish";
|
|
@@ -33404,7 +33395,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
33404
33395
|
}
|
|
33405
33396
|
if (mode === "pretty") this.context.stderr.write(` ${colors.dim("waiting for the installer to start\u2026")}
|
|
33406
33397
|
`);
|
|
33407
|
-
await
|
|
33398
|
+
await sleep4(1e4);
|
|
33408
33399
|
continue;
|
|
33409
33400
|
}
|
|
33410
33401
|
throw e;
|
|
@@ -33440,7 +33431,7 @@ var BootstrapStatusCommand = class extends M8tCommand {
|
|
|
33440
33431
|
);
|
|
33441
33432
|
return 1;
|
|
33442
33433
|
}
|
|
33443
|
-
await
|
|
33434
|
+
await sleep4(1e4);
|
|
33444
33435
|
}
|
|
33445
33436
|
}
|
|
33446
33437
|
/**
|
|
@@ -33690,18 +33681,13 @@ Found ${String(total)} orphaned assignment(s) (${breakdown}) (dry-run). ${colors
|
|
|
33690
33681
|
};
|
|
33691
33682
|
|
|
33692
33683
|
// src/commands/bootstrap/ui.ts
|
|
33693
|
-
import * as fs40 from "fs";
|
|
33694
|
-
import * as os21 from "os";
|
|
33695
|
-
import * as path45 from "path";
|
|
33696
33684
|
import { Command as Command62, Option as Option59 } from "clipanion";
|
|
33697
|
-
import { DefaultAzureCredential as DefaultAzureCredential25 } from "@azure/identity";
|
|
33698
|
-
init_errors();
|
|
33699
33685
|
|
|
33700
33686
|
// src/lib/bootstrap-ui.ts
|
|
33701
|
-
import * as
|
|
33687
|
+
import * as fs40 from "fs";
|
|
33702
33688
|
import * as net from "net";
|
|
33703
|
-
import * as
|
|
33704
|
-
import * as
|
|
33689
|
+
import * as os21 from "os";
|
|
33690
|
+
import * as path45 from "path";
|
|
33705
33691
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
33706
33692
|
import { spawn as spawn8, spawnSync as spawnSync6 } from "child_process";
|
|
33707
33693
|
init_errors();
|
|
@@ -33709,475 +33695,32 @@ init_rbac();
|
|
|
33709
33695
|
|
|
33710
33696
|
// src/lib/intake-agent.ts
|
|
33711
33697
|
init_esm();
|
|
33712
|
-
var INTAKE_PERSONA = "ezra-intake";
|
|
33713
|
-
var INTAKE_AGENT = INTAKE_AGENT_NAME;
|
|
33714
|
-
var ADVISOR_TO_INTAKE_CODE = /* @__PURE__ */ new Map([
|
|
33715
|
-
["ADVISOR_PERSONA_MISSING", "INTAKE_PERSONA_MISSING"],
|
|
33716
|
-
["ADVISOR_PERSONA_EMPTY", "INTAKE_PERSONA_EMPTY"],
|
|
33717
|
-
["ADVISOR_NO_MODEL", "INTAKE_NO_MODEL"],
|
|
33718
|
-
["ADVISOR_BAD_EFFORT", "INTAKE_BAD_EFFORT"],
|
|
33719
|
-
["ADVISOR_BAD_TOOLS", "INTAKE_BAD_TOOLS"]
|
|
33720
|
-
]);
|
|
33721
|
-
async function deployIntakeAgent(args) {
|
|
33722
|
-
try {
|
|
33723
|
-
return await deployPromptAdvisor({
|
|
33724
|
-
credential: args.credential,
|
|
33725
|
-
endpoint: args.endpoint,
|
|
33726
|
-
repoRoot: args.repoRoot,
|
|
33727
|
-
persona: INTAKE_PERSONA,
|
|
33728
|
-
agentName: INTAKE_AGENT,
|
|
33729
|
-
model: args.model,
|
|
33730
|
-
fieldOverrides: args.fieldOverrides
|
|
33731
|
-
});
|
|
33732
|
-
} catch (e) {
|
|
33733
|
-
if (e && typeof e === "object" && "code" in e) {
|
|
33734
|
-
const err = e;
|
|
33735
|
-
const intakeCode = ADVISOR_TO_INTAKE_CODE.get(err.code);
|
|
33736
|
-
if (intakeCode) {
|
|
33737
|
-
const { LocalCliError: LocalCliError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
33738
|
-
throw new LocalCliError2({
|
|
33739
|
-
code: intakeCode,
|
|
33740
|
-
message: err.message,
|
|
33741
|
-
hint: err.hint,
|
|
33742
|
-
cause: err.cause
|
|
33743
|
-
});
|
|
33744
|
-
}
|
|
33745
|
-
}
|
|
33746
|
-
throw e;
|
|
33747
|
-
}
|
|
33748
|
-
}
|
|
33749
33698
|
|
|
33750
33699
|
// src/lib/bootstrap-ui.ts
|
|
33751
|
-
function
|
|
33752
|
-
|
|
33753
|
-
}
|
|
33754
|
-
function isAuthorizationShapedError(error) {
|
|
33755
|
-
const seen = /* @__PURE__ */ new Set();
|
|
33756
|
-
let current = error;
|
|
33757
|
-
while (current != null && !seen.has(current)) {
|
|
33758
|
-
seen.add(current);
|
|
33759
|
-
if (typeof current === "object") {
|
|
33760
|
-
const record = current;
|
|
33761
|
-
if (record.statusCode === 403) return true;
|
|
33762
|
-
if (typeof record.message === "string" && /403|Forbidden|PermissionDenied|not authorized/i.test(record.message)) return true;
|
|
33763
|
-
current = record.cause;
|
|
33764
|
-
continue;
|
|
33765
|
-
}
|
|
33766
|
-
if (typeof current === "string" && /403|Forbidden|PermissionDenied|not authorized/i.test(current)) return true;
|
|
33767
|
-
break;
|
|
33768
|
-
}
|
|
33769
|
-
return false;
|
|
33770
|
-
}
|
|
33771
|
-
async function deployIntakeAgentWithRetry(args) {
|
|
33772
|
-
const maxWaitMs = args.maxWaitMs ?? 3e5;
|
|
33773
|
-
const intervalMs = args.intervalMs ?? 1e4;
|
|
33774
|
-
const deadline = Date.now() + maxWaitMs;
|
|
33775
|
-
for (; ; ) {
|
|
33776
|
-
try {
|
|
33777
|
-
return await deployIntakeAgent({
|
|
33778
|
-
credential: args.credential,
|
|
33779
|
-
endpoint: args.endpoint,
|
|
33780
|
-
repoRoot: args.repoRoot,
|
|
33781
|
-
model: args.model,
|
|
33782
|
-
fieldOverrides: args.fieldOverrides
|
|
33783
|
-
});
|
|
33784
|
-
} catch (error) {
|
|
33785
|
-
if (!isAuthorizationShapedError(error)) throw error;
|
|
33786
|
-
const remainingMs = deadline - Date.now();
|
|
33787
|
-
if (remainingMs <= 0) {
|
|
33788
|
-
throw new LocalCliError({
|
|
33789
|
-
code: "BOOTSTRAP_UI_ROLE_PROPAGATION_TIMEOUT",
|
|
33790
|
-
message: "Timed out waiting for Azure role propagation before deploying the intake agent.",
|
|
33791
|
-
hint: "Azure role propagation can take a few minutes - re-run 'm8t bootstrap ui' (idempotent).",
|
|
33792
|
-
cause: error
|
|
33793
|
-
});
|
|
33794
|
-
}
|
|
33795
|
-
args.onWait?.("waiting for Azure role propagation...");
|
|
33796
|
-
await sleep3(Math.min(intervalMs, remainingMs));
|
|
33797
|
-
}
|
|
33798
|
-
}
|
|
33799
|
-
}
|
|
33800
|
-
function renderNotReadyHint(resourceGroup, excluded) {
|
|
33801
|
-
const lines = ["Check 'm8t bootstrap status --watch' \u2014 the install may have stalled in foundry-create."];
|
|
33802
|
-
if (resourceGroup) {
|
|
33803
|
-
if (excluded.length > 0) {
|
|
33804
|
-
const count = excluded.length === 1 ? "1 project" : `${excluded.length.toString()} projects`;
|
|
33805
|
-
const verb = excluded.length === 1 ? "was" : "were";
|
|
33806
|
-
lines.push(
|
|
33807
|
-
`Discovery is scoped to this install's resource group (${resourceGroup}) on purpose, so it can`,
|
|
33808
|
-
`never deploy into someone else's project. ${count} elsewhere in this subscription ${verb}`,
|
|
33809
|
-
"skipped:",
|
|
33810
|
-
...excluded.map((c) => ` ${c.endpoint}`)
|
|
33811
|
-
);
|
|
33812
|
-
}
|
|
33813
|
-
lines.push(
|
|
33814
|
-
"If ~/.m8t/bootstrap.json names a different install than the one you meant, re-run with",
|
|
33815
|
-
"--endpoint <projectEndpoint>."
|
|
33816
|
-
);
|
|
33817
|
-
}
|
|
33818
|
-
return lines.join("\n");
|
|
33819
|
-
}
|
|
33820
|
-
async function resolveFoundryEndpointWithWait(args, opts = {}) {
|
|
33821
|
-
const pollMs = opts.pollMs ?? 1e4;
|
|
33822
|
-
const timeoutMs = opts.timeoutMs ?? 15 * 6e4;
|
|
33823
|
-
const deadline = Date.now() + timeoutMs;
|
|
33824
|
-
let excluded = [];
|
|
33825
|
-
for (; ; ) {
|
|
33826
|
-
try {
|
|
33827
|
-
excluded = [];
|
|
33828
|
-
const p = await resolveFoundryProject({
|
|
33829
|
-
credential: args.credential,
|
|
33830
|
-
subscriptionId: args.subscriptionId,
|
|
33831
|
-
interactive: false,
|
|
33832
|
-
// When the install's own resource group holds multiple projects, an
|
|
33833
|
-
// explicit endpoint disambiguates instead of FOUNDRY_PROJECT_MULTIPLE.
|
|
33834
|
-
// Omitted → single-project auto-discovery within that group.
|
|
33835
|
-
endpoint: args.endpoint,
|
|
33836
|
-
resourceGroup: args.resourceGroup,
|
|
33837
|
-
onScopedOut: (dropped) => {
|
|
33838
|
-
excluded = dropped;
|
|
33839
|
-
}
|
|
33840
|
-
});
|
|
33841
|
-
return { endpoint: p.endpoint, accountScope: p.accountScope };
|
|
33842
|
-
} catch (e) {
|
|
33843
|
-
if (e instanceof LocalCliError && e.code === "FOUNDRY_PROJECT_MULTIPLE") {
|
|
33844
|
-
throw new LocalCliError({
|
|
33845
|
-
code: "BOOTSTRAP_UI_MULTIPLE_PROJECTS",
|
|
33846
|
-
message: `${e.message} bootstrap ui can't pick one safely (it must not deploy the intake agent into the wrong project).`,
|
|
33847
|
-
hint: [
|
|
33848
|
-
"Re-run naming the project you want:",
|
|
33849
|
-
'm8t bootstrap ui --repo-root "$(pwd)" --endpoint <projectEndpoint>',
|
|
33850
|
-
"'m8t bootstrap status' prints this install's endpoint (result.foundryEndpoint)."
|
|
33851
|
-
].join("\n"),
|
|
33852
|
-
cause: e
|
|
33853
|
-
});
|
|
33854
|
-
}
|
|
33855
|
-
if (!(e instanceof LocalCliError && e.code === "FOUNDRY_PROJECT_ZERO")) {
|
|
33856
|
-
throw e;
|
|
33857
|
-
}
|
|
33858
|
-
if (Date.now() >= deadline) {
|
|
33859
|
-
throw new LocalCliError({
|
|
33860
|
-
code: "BOOTSTRAP_UI_FOUNDRY_NOT_READY",
|
|
33861
|
-
message: args.resourceGroup ? `Foundry is not ready yet \u2014 no AI project appeared in resource group ${args.resourceGroup} before the timeout.` : "Foundry is not ready yet \u2014 no AI project appeared in your subscription before the timeout.",
|
|
33862
|
-
hint: renderNotReadyHint(args.resourceGroup, excluded),
|
|
33863
|
-
cause: e
|
|
33864
|
-
});
|
|
33865
|
-
}
|
|
33866
|
-
opts.onWait?.("waiting for Foundry-create to finish\u2026");
|
|
33867
|
-
await sleep3(pollMs);
|
|
33868
|
-
}
|
|
33869
|
-
}
|
|
33870
|
-
}
|
|
33871
|
-
async function getSignedInUserOid() {
|
|
33872
|
-
const oid = (await runAz(["ad", "signed-in-user", "show", "--query", "id", "-o", "tsv"])).trim();
|
|
33873
|
-
if (!oid) {
|
|
33874
|
-
throw new LocalCliError({
|
|
33875
|
-
code: "BOOTSTRAP_UI_NO_SIGNED_IN_USER",
|
|
33876
|
-
message: "Could not resolve the signed-in user (az ad signed-in-user show returned nothing).",
|
|
33877
|
-
hint: "Personal Microsoft accounts can't be resolved this way \u2014 sign in with a work/school account."
|
|
33878
|
-
});
|
|
33879
|
-
}
|
|
33880
|
-
return oid;
|
|
33881
|
-
}
|
|
33882
|
-
async function ensureFounderFoundryRole(args) {
|
|
33883
|
-
await grantFoundryUser({
|
|
33884
|
-
credential: args.credential,
|
|
33885
|
-
subscriptionId: args.subscriptionId,
|
|
33886
|
-
accountScope: args.accountScope,
|
|
33887
|
-
principalId: args.principalId,
|
|
33888
|
-
principalType: "User"
|
|
33889
|
-
});
|
|
33890
|
-
}
|
|
33891
|
-
function writeWebEnvLocal(args) {
|
|
33892
|
-
const envPath = path44.join(args.repoRoot, "apps", "web", ".env.local");
|
|
33893
|
-
let prior = "";
|
|
33894
|
-
if (fs39.existsSync(envPath)) {
|
|
33895
|
-
prior = fs39.readFileSync(envPath, "utf8");
|
|
33896
|
-
fs39.copyFileSync(envPath, envPath + ".bak");
|
|
33897
|
-
}
|
|
33898
|
-
const priorSecret = readValidInternalSecret(prior);
|
|
33899
|
-
const internalSecret = priorSecret ?? (args.randomBytesImpl ?? randomBytes3)(32).toString("base64url");
|
|
33900
|
-
if (!isValidInternalSecret(internalSecret)) {
|
|
33901
|
-
throw new LocalCliError({
|
|
33902
|
-
code: "BOOTSTRAP_UI_INTERNAL_SECRET_GENERATION_FAILED",
|
|
33903
|
-
message: "Could not generate a valid local internal service secret."
|
|
33904
|
-
});
|
|
33905
|
-
}
|
|
33906
|
-
const body = [
|
|
33907
|
-
"# Written by `m8t bootstrap ui` \u2014 local onboarding run (the onboarding intake agent).",
|
|
33908
|
-
"# Text-first: the intake voice stack is off unless the run passed --voice.",
|
|
33909
|
-
"# STORAGE unset \u2192 gateway boot skipped; the webapp forwards your MSAL token to Foundry.",
|
|
33910
|
-
`AZURE_TENANT_ID=${args.tenantId}`,
|
|
33911
|
-
`AZURE_CLIENT_ID=${args.clientId}`,
|
|
33912
|
-
`FOUNDRY_PROJECT_ENDPOINT=${args.foundryEndpoint}`,
|
|
33913
|
-
...args.voice ? ["VOICE_RELAY_PUBLIC_URL=ws://localhost:8790", "NEXT_PUBLIC_M8T_INTAKE_VOICE=1"] : [],
|
|
33914
|
-
"STORAGE_ACCOUNT_NAME=",
|
|
33915
|
-
`M8T_INTERNAL_SECRET=${internalSecret}`,
|
|
33916
|
-
"M8T_SINGLE_GATEWAY_PROCESS=1",
|
|
33917
|
-
"M8T_DISABLE_WORKER=1",
|
|
33918
|
-
"M8T_ONBOARDING=1",
|
|
33919
|
-
"NEXT_PUBLIC_M8T_ONBOARDING=1",
|
|
33920
|
-
""
|
|
33921
|
-
].join("\n");
|
|
33922
|
-
fs39.writeFileSync(envPath, body, { encoding: "utf8", mode: 384 });
|
|
33923
|
-
try {
|
|
33924
|
-
fs39.chmodSync(envPath, 384);
|
|
33925
|
-
} catch {
|
|
33926
|
-
}
|
|
33927
|
-
return envPath;
|
|
33928
|
-
}
|
|
33929
|
-
function isValidInternalSecret(value) {
|
|
33930
|
-
if (!value || !/^[A-Za-z0-9_-]{43}$/.test(value)) return false;
|
|
33931
|
-
const decoded = Buffer.from(value, "base64url");
|
|
33932
|
-
return decoded.length === 32 && decoded.toString("base64url") === value;
|
|
33933
|
-
}
|
|
33934
|
-
function readValidInternalSecret(envText) {
|
|
33935
|
-
const values = envText.split(/\r?\n/).filter((line2) => line2.startsWith("M8T_INTERNAL_SECRET=")).map((line2) => line2.slice("M8T_INTERNAL_SECRET=".length));
|
|
33936
|
-
if (values.length !== 1) return void 0;
|
|
33937
|
-
return isValidInternalSecret(values[0]) ? values[0] : void 0;
|
|
33938
|
-
}
|
|
33939
|
-
function readWebInternalSecret(repoRoot) {
|
|
33940
|
-
const envPath = path44.join(repoRoot, "apps", "web", ".env.local");
|
|
33941
|
-
let secret;
|
|
33942
|
-
try {
|
|
33943
|
-
secret = readValidInternalSecret(fs39.readFileSync(envPath, "utf8"));
|
|
33944
|
-
} catch {
|
|
33945
|
-
secret = void 0;
|
|
33946
|
-
}
|
|
33947
|
-
if (!secret) {
|
|
33948
|
-
throw new LocalCliError({
|
|
33949
|
-
code: "BOOTSTRAP_UI_INTERNAL_SECRET_MISSING",
|
|
33950
|
-
message: "The local web environment has no valid internal service secret.",
|
|
33951
|
-
hint: "Re-run 'm8t bootstrap ui' so the web app and voice relay receive the same secret."
|
|
33952
|
-
});
|
|
33953
|
-
}
|
|
33954
|
-
return secret;
|
|
33955
|
-
}
|
|
33956
|
-
function assertNodeVersion(versionString = process.version) {
|
|
33957
|
-
const major = Number(/^v?(\d+)\./.exec(versionString)?.[1] ?? "0");
|
|
33958
|
-
if (major < 20) {
|
|
33959
|
-
throw new LocalCliError({
|
|
33960
|
-
code: "BOOTSTRAP_UI_NODE_TOO_OLD",
|
|
33961
|
-
message: `apps/web needs Node 20+, but this is ${versionString}.`,
|
|
33962
|
-
hint: "Install Node 20+ (see install/prereqs-*.md), then re-run 'm8t bootstrap ui'."
|
|
33963
|
-
});
|
|
33964
|
-
}
|
|
33965
|
-
}
|
|
33966
|
-
function runInherit(cmd, cmdArgs, cwd, failCode) {
|
|
33967
|
-
return new Promise((resolve6, reject) => {
|
|
33968
|
-
const child = spawn8(cmd, cmdArgs, { cwd, stdio: "inherit", shell: process.platform === "win32" });
|
|
33969
|
-
child.on("error", (e) => {
|
|
33970
|
-
reject(new LocalCliError({ code: failCode, message: `Failed to start '${cmd}': ${e.message}` }));
|
|
33971
|
-
});
|
|
33972
|
-
child.on("close", (code) => {
|
|
33973
|
-
if (code === 0) {
|
|
33974
|
-
resolve6();
|
|
33975
|
-
} else {
|
|
33976
|
-
const opts = {
|
|
33977
|
-
code: failCode,
|
|
33978
|
-
message: `'${cmd} ${cmdArgs.join(" ")}' exited with code ${String(code)}.`
|
|
33979
|
-
};
|
|
33980
|
-
if (cmd === "pnpm") {
|
|
33981
|
-
opts.hint = "Fix the pnpm error above, then re-run 'm8t bootstrap ui' (it's idempotent).";
|
|
33982
|
-
}
|
|
33983
|
-
reject(new LocalCliError(opts));
|
|
33984
|
-
}
|
|
33985
|
-
});
|
|
33986
|
-
});
|
|
33987
|
-
}
|
|
33988
|
-
async function installWebDeps(repoRoot) {
|
|
33989
|
-
assertNodeVersion();
|
|
33990
|
-
await runInherit("corepack", ["enable"], repoRoot, "BOOTSTRAP_UI_COREPACK_FAILED").catch(() => {
|
|
33991
|
-
});
|
|
33992
|
-
await runInherit("pnpm", ["install"], repoRoot, "BOOTSTRAP_UI_INSTALL_FAILED");
|
|
33993
|
-
}
|
|
33994
|
-
function onboardingUiPaths(home = os20.homedir()) {
|
|
33995
|
-
const dir = path44.join(home, ".m8t");
|
|
33700
|
+
function onboardingUiPaths(home = os21.homedir()) {
|
|
33701
|
+
const dir2 = path45.join(home, ".m8t");
|
|
33996
33702
|
return {
|
|
33997
|
-
logPath:
|
|
33998
|
-
pidPath:
|
|
33703
|
+
logPath: path45.join(dir2, "onboarding-ui.log"),
|
|
33704
|
+
pidPath: path45.join(dir2, "onboarding-ui.pid")
|
|
33999
33705
|
};
|
|
34000
33706
|
}
|
|
34001
|
-
function onboardingRelayPaths(home =
|
|
34002
|
-
const
|
|
33707
|
+
function onboardingRelayPaths(home = os21.homedir()) {
|
|
33708
|
+
const dir2 = path45.join(home, ".m8t");
|
|
34003
33709
|
return {
|
|
34004
|
-
logPath:
|
|
34005
|
-
pidPath:
|
|
33710
|
+
logPath: path45.join(dir2, "onboarding-relay.log"),
|
|
33711
|
+
pidPath: path45.join(dir2, "onboarding-relay.pid")
|
|
34006
33712
|
};
|
|
34007
33713
|
}
|
|
34008
|
-
function isLocalPortOpen(port) {
|
|
34009
|
-
return new Promise((resolve6) => {
|
|
34010
|
-
const s = net.createConnection({ host: "127.0.0.1", port });
|
|
34011
|
-
s.setTimeout(1e3);
|
|
34012
|
-
s.on("connect", () => {
|
|
34013
|
-
s.destroy();
|
|
34014
|
-
resolve6(true);
|
|
34015
|
-
});
|
|
34016
|
-
s.on("error", () => {
|
|
34017
|
-
resolve6(false);
|
|
34018
|
-
});
|
|
34019
|
-
s.on("timeout", () => {
|
|
34020
|
-
s.destroy();
|
|
34021
|
-
resolve6(false);
|
|
34022
|
-
});
|
|
34023
|
-
});
|
|
34024
|
-
}
|
|
34025
|
-
function removePidFile(pidPath) {
|
|
34026
|
-
try {
|
|
34027
|
-
fs39.unlinkSync(pidPath);
|
|
34028
|
-
} catch {
|
|
34029
|
-
}
|
|
34030
|
-
}
|
|
34031
33714
|
function parseManagedPid(text) {
|
|
34032
33715
|
if (!/^[1-9]\d*$/.test(text)) return null;
|
|
34033
33716
|
const pid = Number(text);
|
|
34034
33717
|
if (!Number.isSafeInteger(pid) || pid < 2 || pid === process.pid || pid === process.ppid) return null;
|
|
34035
33718
|
return pid;
|
|
34036
33719
|
}
|
|
34037
|
-
function readLiveManagedPid(pidPath) {
|
|
34038
|
-
let text;
|
|
34039
|
-
try {
|
|
34040
|
-
text = fs39.readFileSync(pidPath, "utf8").trim();
|
|
34041
|
-
} catch {
|
|
34042
|
-
return null;
|
|
34043
|
-
}
|
|
34044
|
-
const pid = parseManagedPid(text);
|
|
34045
|
-
if (pid == null) {
|
|
34046
|
-
removePidFile(pidPath);
|
|
34047
|
-
return null;
|
|
34048
|
-
}
|
|
34049
|
-
try {
|
|
34050
|
-
process.kill(pid, 0);
|
|
34051
|
-
return pid;
|
|
34052
|
-
} catch (error) {
|
|
34053
|
-
const code = error.code;
|
|
34054
|
-
if (code === "ESRCH") {
|
|
34055
|
-
removePidFile(pidPath);
|
|
34056
|
-
return null;
|
|
34057
|
-
}
|
|
34058
|
-
if (code === "EPERM") return pid;
|
|
34059
|
-
throw error;
|
|
34060
|
-
}
|
|
34061
|
-
}
|
|
34062
|
-
async function serveOnboardingUiDetached(args) {
|
|
34063
|
-
const { logPath, pidPath } = onboardingUiPaths();
|
|
34064
|
-
const portNum = Number(args.port);
|
|
34065
|
-
const isPortOpen = () => isLocalPortOpen(portNum);
|
|
34066
|
-
const portOpen = await isPortOpen();
|
|
34067
|
-
const managedPid = readLiveManagedPid(pidPath);
|
|
34068
|
-
if (portOpen && managedPid != null) {
|
|
34069
|
-
return { alreadyRunning: true, logPath };
|
|
34070
|
-
}
|
|
34071
|
-
if (portOpen) {
|
|
34072
|
-
throw new LocalCliError({
|
|
34073
|
-
code: "BOOTSTRAP_UI_PORT_IN_USE",
|
|
34074
|
-
message: `Port ${args.port} is occupied by a process not started by m8t bootstrap.`,
|
|
34075
|
-
hint: `Stop the process using port ${args.port}, then re-run 'm8t bootstrap ui'.`
|
|
34076
|
-
});
|
|
34077
|
-
}
|
|
34078
|
-
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
34079
|
-
fs39.mkdirSync(path44.dirname(logPath), { recursive: true });
|
|
34080
|
-
const fd = fs39.openSync(logPath, "a");
|
|
34081
|
-
const child = spawn8("pnpm", ["--filter", "web", "dev"], {
|
|
34082
|
-
cwd: args.repoRoot,
|
|
34083
|
-
detached: true,
|
|
34084
|
-
stdio: ["ignore", fd, fd],
|
|
34085
|
-
env: { ...process.env, PORT: args.port, M8T_SINGLE_GATEWAY_PROCESS: "1" },
|
|
34086
|
-
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
34087
|
-
shell: process.platform === "win32"
|
|
34088
|
-
});
|
|
34089
|
-
fs39.closeSync(fd);
|
|
34090
|
-
if (child.pid == null) {
|
|
34091
|
-
throw new LocalCliError({
|
|
34092
|
-
code: "BOOTSTRAP_UI_DEV_SPAWN_FAILED",
|
|
34093
|
-
message: "Could not start the web dev server (pnpm did not spawn).",
|
|
34094
|
-
hint: "Check that pnpm + Node 20+ are installed; see the log at " + logPath + "."
|
|
34095
|
-
});
|
|
34096
|
-
}
|
|
34097
|
-
child.unref();
|
|
34098
|
-
fs39.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
34099
|
-
const deadline = Date.now() + 45e3;
|
|
34100
|
-
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
34101
|
-
while (Date.now() < deadline) {
|
|
34102
|
-
await sleep5(500);
|
|
34103
|
-
if (await isPortOpen()) break;
|
|
34104
|
-
}
|
|
34105
|
-
return { alreadyRunning: false, logPath };
|
|
34106
|
-
}
|
|
34107
|
-
async function serveOnboardingRelayDetached(args) {
|
|
34108
|
-
const { logPath, pidPath } = onboardingRelayPaths();
|
|
34109
|
-
const portNum = 8790;
|
|
34110
|
-
if (args.webInternalBaseUrl !== "http://localhost:3000") {
|
|
34111
|
-
throw new LocalCliError({
|
|
34112
|
-
code: "BOOTSTRAP_UI_INTERNAL_BASE_URL_INVALID",
|
|
34113
|
-
message: "The local voice relay internal base URL must be http://localhost:3000."
|
|
34114
|
-
});
|
|
34115
|
-
}
|
|
34116
|
-
const internalSecret = readWebInternalSecret(args.repoRoot);
|
|
34117
|
-
const portOpen = await isLocalPortOpen(portNum);
|
|
34118
|
-
const managedPid = readLiveManagedPid(pidPath);
|
|
34119
|
-
if (portOpen && managedPid != null) {
|
|
34120
|
-
return { alreadyRunning: true, logPath };
|
|
34121
|
-
}
|
|
34122
|
-
if (portOpen) {
|
|
34123
|
-
throw new LocalCliError({
|
|
34124
|
-
code: "BOOTSTRAP_UI_RELAY_PORT_IN_USE",
|
|
34125
|
-
message: "Port 8790 is occupied by a process not started by m8t bootstrap.",
|
|
34126
|
-
hint: "Stop the process using port 8790, then re-run 'm8t bootstrap ui'."
|
|
34127
|
-
});
|
|
34128
|
-
}
|
|
34129
|
-
if (managedPid != null) return { alreadyRunning: true, logPath };
|
|
34130
|
-
fs39.mkdirSync(path44.dirname(logPath), { recursive: true });
|
|
34131
|
-
const fd = fs39.openSync(logPath, "a");
|
|
34132
|
-
const child = spawn8("pnpm", ["--filter", "web", "exec", "tsx", "voice-relay-entry.ts"], {
|
|
34133
|
-
cwd: args.repoRoot,
|
|
34134
|
-
detached: true,
|
|
34135
|
-
stdio: ["ignore", fd, fd],
|
|
34136
|
-
env: {
|
|
34137
|
-
...process.env,
|
|
34138
|
-
FOUNDRY_PROJECT_ENDPOINT: args.foundryEndpoint,
|
|
34139
|
-
AZURE_TENANT_ID: args.tenantId,
|
|
34140
|
-
AZURE_CLIENT_ID: args.clientId,
|
|
34141
|
-
RELAY_ALLOWED_ORIGINS: "http://localhost:3000",
|
|
34142
|
-
RELAY_PORT: "8790",
|
|
34143
|
-
M8T_INTERNAL_SECRET: internalSecret,
|
|
34144
|
-
WEB_INTERNAL_BASE_URL: args.webInternalBaseUrl
|
|
34145
|
-
},
|
|
34146
|
-
// pnpm resolves to pnpm.cmd on Windows, which needs a shell to launch.
|
|
34147
|
-
shell: process.platform === "win32"
|
|
34148
|
-
});
|
|
34149
|
-
fs39.closeSync(fd);
|
|
34150
|
-
if (child.pid == null) {
|
|
34151
|
-
throw new LocalCliError({
|
|
34152
|
-
code: "BOOTSTRAP_UI_RELAY_SPAWN_FAILED",
|
|
34153
|
-
message: "Could not start the voice relay (pnpm did not spawn).",
|
|
34154
|
-
hint: "Check that pnpm + Node 20+ are installed; see the log at " + logPath + "."
|
|
34155
|
-
});
|
|
34156
|
-
}
|
|
34157
|
-
child.unref();
|
|
34158
|
-
fs39.writeFileSync(pidPath, String(child.pid), "utf8");
|
|
34159
|
-
const deadline = Date.now() + 45e3;
|
|
34160
|
-
const sleep5 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
34161
|
-
while (Date.now() < deadline) {
|
|
34162
|
-
await sleep5(500);
|
|
34163
|
-
if (await isLocalPortOpen(portNum)) break;
|
|
34164
|
-
}
|
|
34165
|
-
return { alreadyRunning: false, logPath };
|
|
34166
|
-
}
|
|
34167
|
-
function autoOpenOnboardingUi(url, open3 = tryOpenUrl) {
|
|
34168
|
-
try {
|
|
34169
|
-
const result2 = open3(url);
|
|
34170
|
-
if (result2 instanceof Promise) {
|
|
34171
|
-
result2.catch(() => {
|
|
34172
|
-
});
|
|
34173
|
-
}
|
|
34174
|
-
} catch {
|
|
34175
|
-
}
|
|
34176
|
-
}
|
|
34177
33720
|
function stopPidFile(pidPath) {
|
|
34178
33721
|
let pidStr;
|
|
34179
33722
|
try {
|
|
34180
|
-
pidStr =
|
|
33723
|
+
pidStr = fs40.readFileSync(pidPath, "utf8").trim();
|
|
34181
33724
|
} catch {
|
|
34182
33725
|
return false;
|
|
34183
33726
|
}
|
|
@@ -34194,551 +33737,386 @@ function stopPidFile(pidPath) {
|
|
|
34194
33737
|
}
|
|
34195
33738
|
}
|
|
34196
33739
|
try {
|
|
34197
|
-
|
|
33740
|
+
fs40.unlinkSync(pidPath);
|
|
34198
33741
|
} catch {
|
|
34199
33742
|
}
|
|
34200
33743
|
return true;
|
|
34201
33744
|
}
|
|
34202
|
-
function stopOnboardingUi(home =
|
|
33745
|
+
function stopOnboardingUi(home = os21.homedir()) {
|
|
34203
33746
|
const uiStopped = stopPidFile(onboardingUiPaths(home).pidPath);
|
|
34204
33747
|
const relayStopped = stopPidFile(onboardingRelayPaths(home).pidPath);
|
|
34205
33748
|
return uiStopped || relayStopped;
|
|
34206
33749
|
}
|
|
34207
|
-
function buildIntakeFieldOverrides(args) {
|
|
34208
|
-
return {
|
|
34209
|
-
founder_identity_note: args.founderIdentityNote,
|
|
34210
|
-
chosen_model_note: args.chosenModelNote,
|
|
34211
|
-
...args.inventoryNote ? { subscription_inventory_note: args.inventoryNote } : {}
|
|
34212
|
-
};
|
|
34213
|
-
}
|
|
34214
33750
|
|
|
34215
|
-
// src/
|
|
34216
|
-
|
|
34217
|
-
|
|
34218
|
-
|
|
34219
|
-
|
|
34220
|
-
} catch (e) {
|
|
34221
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
34222
|
-
const denied = /AuthorizationFailed|Forbidden|\b403\b|does not have authorization/i.test(msg);
|
|
34223
|
-
return { ok: false, error: denied ? "denied" : "unavailable" };
|
|
34224
|
-
}
|
|
34225
|
-
let raw;
|
|
34226
|
-
try {
|
|
34227
|
-
raw = JSON.parse(out);
|
|
34228
|
-
} catch {
|
|
34229
|
-
return { ok: false, error: "malformed" };
|
|
34230
|
-
}
|
|
34231
|
-
if (!Array.isArray(raw) || raw.some((e) => typeof e !== "object" || e === null)) {
|
|
34232
|
-
return { ok: false, error: "malformed" };
|
|
34233
|
-
}
|
|
34234
|
-
try {
|
|
34235
|
-
const rows = raw.map((e) => ({
|
|
34236
|
-
format: e.model?.format ?? "",
|
|
34237
|
-
name: e.model?.name ?? "",
|
|
34238
|
-
version: e.model?.version ?? "",
|
|
34239
|
-
skus: [...new Set((e.model?.skus ?? []).map((s) => s.name ?? "").filter(Boolean))],
|
|
34240
|
-
agentsV2: e.model?.capabilities?.agentsV2 === "true"
|
|
34241
|
-
}));
|
|
34242
|
-
return { ok: true, rows };
|
|
34243
|
-
} catch {
|
|
34244
|
-
return { ok: false, error: "malformed" };
|
|
34245
|
-
}
|
|
33751
|
+
// src/commands/bootstrap/ui.ts
|
|
33752
|
+
function renderDeprecationNotice() {
|
|
33753
|
+
return `${colors.error("\u26A0")} 'm8t bootstrap ui' is deprecated and does nothing \u2014 the local onboarding chat is gone for good.
|
|
33754
|
+
Your details + chat while you wait: ${colors.field("m8t bootstrap profile")} \xB7 Your own Ezra, once installed: ${colors.field("m8t open")}
|
|
33755
|
+
`;
|
|
34246
33756
|
}
|
|
33757
|
+
var BootstrapUiCommand = class extends M8tCommand {
|
|
33758
|
+
static paths = [["bootstrap", "ui"]];
|
|
33759
|
+
static usage = Command62.Usage({
|
|
33760
|
+
description: "DEPRECATED \u2014 does nothing. Use `m8t bootstrap profile` instead.",
|
|
33761
|
+
details: [
|
|
33762
|
+
"The local onboarding chat has been retired. Your details are collected by",
|
|
33763
|
+
"`m8t bootstrap profile`, which also opens the hosted Ezra to talk to while the",
|
|
33764
|
+
"install runs; `m8t open` reaches your own Ezra once the install finishes.",
|
|
33765
|
+
"",
|
|
33766
|
+
"This command deploys nothing, serves nothing, and changes nothing. Every flag",
|
|
33767
|
+
"below is accepted and ignored, so an older runbook or script still exits cleanly.",
|
|
33768
|
+
"`--stop` is the one exception: it still shuts down a chat server left running by",
|
|
33769
|
+
"an earlier version of this command."
|
|
33770
|
+
].join("\n"),
|
|
33771
|
+
examples: [
|
|
33772
|
+
["Collect your details and open Ezra instead", "$0 bootstrap profile"],
|
|
33773
|
+
["Stop a chat UI an older CLI left running", "$0 bootstrap ui --stop"]
|
|
33774
|
+
]
|
|
33775
|
+
});
|
|
33776
|
+
// Accepted and ignored, deliberately: removing them would turn an old script's
|
|
33777
|
+
// harmless no-op into an "unknown option" failure mid-install. CLI-rewrite: drop
|
|
33778
|
+
// the whole surface when the rewrite lands.
|
|
33779
|
+
repoRoot = Option59.String("--repo-root", { description: "Ignored (deprecated)." });
|
|
33780
|
+
port = Option59.String("--port", "3000", { description: "Ignored (deprecated)." });
|
|
33781
|
+
endpoint = Option59.String("--endpoint", { description: "Ignored (deprecated)." });
|
|
33782
|
+
prepOnly = Option59.Boolean("--prep-only", false, { description: "Ignored (deprecated)." });
|
|
33783
|
+
skipInstall = Option59.Boolean("--skip-install", false, { description: "Ignored (deprecated)." });
|
|
33784
|
+
foreground = Option59.Boolean("--foreground", false, { description: "Ignored (deprecated)." });
|
|
33785
|
+
voice = Option59.Boolean("--voice", false, { description: "Ignored (deprecated)." });
|
|
33786
|
+
stop = Option59.Boolean("--stop", false, {
|
|
33787
|
+
description: "Shut down a local chat UI left running by an earlier version of this command."
|
|
33788
|
+
});
|
|
33789
|
+
// Not `async`: there is nothing left to await. Everything this command used to
|
|
33790
|
+
// wait on — Foundry, the role grant, the agent deploy, pnpm — is gone.
|
|
33791
|
+
executeCommand() {
|
|
33792
|
+
if (this.stop === true) {
|
|
33793
|
+
const stopped = stopOnboardingUi();
|
|
33794
|
+
this.context.stdout.write(
|
|
33795
|
+
stopped ? `${colors.success("\u2713")} stopped the onboarding chat UI.
|
|
33796
|
+
` : ` ${colors.dim("nothing to stop (no onboarding-ui.pid found).")}
|
|
33797
|
+
`
|
|
33798
|
+
);
|
|
33799
|
+
return Promise.resolve(0);
|
|
33800
|
+
}
|
|
33801
|
+
this.context.stdout.write(renderDeprecationNotice());
|
|
33802
|
+
return Promise.resolve(0);
|
|
33803
|
+
}
|
|
33804
|
+
};
|
|
33805
|
+
|
|
33806
|
+
// src/commands/bootstrap/profile.ts
|
|
33807
|
+
import * as readline3 from "readline/promises";
|
|
33808
|
+
import { Command as Command63, Option as Option60 } from "clipanion";
|
|
34247
33809
|
|
|
34248
|
-
// src/lib/
|
|
34249
|
-
|
|
34250
|
-
|
|
34251
|
-
|
|
34252
|
-
const m = SCOPE_RE.exec(scope);
|
|
34253
|
-
return m ? { resourceGroup: m[1], account: m[2] } : null;
|
|
33810
|
+
// src/lib/profile-collect.ts
|
|
33811
|
+
init_errors();
|
|
33812
|
+
function looksLikeEmail(value) {
|
|
33813
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
|
|
34254
33814
|
}
|
|
34255
|
-
function
|
|
34256
|
-
|
|
34257
|
-
return `${
|
|
33815
|
+
function renderEmailQuestion(prefill) {
|
|
33816
|
+
const base = "Your email (Ezra sends your copies of its outbound mail here)";
|
|
33817
|
+
return prefill ? `${base} [${prefill}]: ` : `${base}: `;
|
|
34258
33818
|
}
|
|
34259
|
-
|
|
34260
|
-
|
|
34261
|
-
|
|
34262
|
-
"cognitiveservices",
|
|
34263
|
-
"account",
|
|
34264
|
-
"show",
|
|
34265
|
-
"--name",
|
|
34266
|
-
account,
|
|
34267
|
-
"--resource-group",
|
|
34268
|
-
resourceGroup,
|
|
34269
|
-
"--query",
|
|
34270
|
-
"location",
|
|
34271
|
-
"-o",
|
|
34272
|
-
"tsv"
|
|
34273
|
-
]);
|
|
34274
|
-
return out.trim() || fallback;
|
|
34275
|
-
} catch {
|
|
34276
|
-
return fallback;
|
|
34277
|
-
}
|
|
33819
|
+
var ADVISOR_LEAD = "Your Microsoft startup advisor's name + email (Ezra emails them\nonly when you ask it to \u2014 e.g. quota requests). It's OK if you don't\nknow it yet \u2014 skip for now.\n";
|
|
33820
|
+
function renderAdvisorQuestion(label, prior) {
|
|
33821
|
+
return prior ? ` ${label} [${prior}] (Enter to keep): ` : ` ${label} (Enter to skip): `;
|
|
34278
33822
|
}
|
|
34279
|
-
|
|
34280
|
-
|
|
34281
|
-
|
|
34282
|
-
|
|
34283
|
-
|
|
34284
|
-
|
|
34285
|
-
|
|
34286
|
-
|
|
34287
|
-
|
|
34288
|
-
|
|
34289
|
-
|
|
34290
|
-
|
|
34291
|
-
|
|
34292
|
-
|
|
34293
|
-
|
|
34294
|
-
|
|
34295
|
-
|
|
34296
|
-
|
|
34297
|
-
|
|
34298
|
-
capacity: rung.capacity,
|
|
34299
|
-
format: rung.format
|
|
34300
|
-
});
|
|
34301
|
-
};
|
|
34302
|
-
const start = Date.now();
|
|
34303
|
-
const result2 = await walkCascade(WHITELIST, plan, deploy, {
|
|
34304
|
-
now: () => Date.now(),
|
|
34305
|
-
deadlineAt: start + (args.deadlineMs ?? DEFAULT_DEADLINE_MS),
|
|
34306
|
-
onRung: (model, outcome) => args.onNarrate?.(narrate(model, outcome, region))
|
|
34307
|
-
});
|
|
33823
|
+
var ADVISOR_SKIPPED_LINE = " No advisor recorded \u2014 tell Ezra any time and it'll add them.\n";
|
|
33824
|
+
function refuseFounderEmail() {
|
|
33825
|
+
return new LocalCliError({
|
|
33826
|
+
code: "PROFILE_FOUNDER_EMAIL_REQUIRED",
|
|
33827
|
+
message: "I need the founder's email address.\n Pass --founder-email <you@example.com> \u2014 Ezra sends their copies of its\n outbound mail there. Confirm it with them; don't guess from the Azure sign-in.",
|
|
33828
|
+
hint: "Ask the founder to confirm the address, then re-run 'm8t bootstrap profile --founder-email <addr>'."
|
|
33829
|
+
});
|
|
33830
|
+
}
|
|
33831
|
+
function refuseMalformed(flag, value) {
|
|
33832
|
+
return new LocalCliError({
|
|
33833
|
+
code: "PROFILE_EMAIL_MALFORMED",
|
|
33834
|
+
message: `'${value}' doesn't look like an email address. Pass ${flag} <you@example.com>.`
|
|
33835
|
+
});
|
|
33836
|
+
}
|
|
33837
|
+
async function collectProfile(args) {
|
|
33838
|
+
const { flags, identity, isTty, ask } = args;
|
|
33839
|
+
const previous = args.previous ?? null;
|
|
33840
|
+
const hasAdvisorFlag = (flags.advisorName ?? "").trim() !== "" || (flags.advisorEmail ?? "").trim() !== "";
|
|
33841
|
+
if (flags.noAdvisor && hasAdvisorFlag) {
|
|
34308
33842
|
return {
|
|
34309
|
-
|
|
34310
|
-
|
|
34311
|
-
|
|
33843
|
+
ok: false,
|
|
33844
|
+
error: new LocalCliError({
|
|
33845
|
+
code: "PROFILE_ADVISOR_FLAGS_CONFLICT",
|
|
33846
|
+
message: "--no-advisor cannot be combined with --advisor-name / --advisor-email.",
|
|
33847
|
+
hint: "Drop --no-advisor to record the advisor, or drop the advisor flags to skip."
|
|
33848
|
+
})
|
|
34312
33849
|
};
|
|
34313
|
-
}
|
|
34314
|
-
|
|
34315
|
-
|
|
34316
|
-
|
|
34317
|
-
|
|
34318
|
-
|
|
34319
|
-
|
|
34320
|
-
|
|
34321
|
-
|
|
34322
|
-
|
|
34323
|
-
|
|
34324
|
-
|
|
34325
|
-
|
|
34326
|
-
"microsoft.compute/virtualmachinescalesets": "virtual machine scale sets",
|
|
34327
|
-
"microsoft.app/containerapps": "container apps",
|
|
34328
|
-
"microsoft.containerservice/managedclusters": "Kubernetes clusters",
|
|
34329
|
-
"microsoft.web/sites": "web apps",
|
|
34330
|
-
// data (shared noun)
|
|
34331
|
-
"microsoft.sql/servers": "databases",
|
|
34332
|
-
"microsoft.dbforpostgresql/servers": "databases",
|
|
34333
|
-
"microsoft.dbforpostgresql/flexibleservers": "databases",
|
|
34334
|
-
"microsoft.dbformysql/servers": "databases",
|
|
34335
|
-
"microsoft.dbformysql/flexibleservers": "databases",
|
|
34336
|
-
"microsoft.documentdb/databaseaccounts": "databases",
|
|
34337
|
-
"microsoft.cache/redis": "databases",
|
|
34338
|
-
// AI (shared noun)
|
|
34339
|
-
"microsoft.cognitiveservices/accounts": "AI services",
|
|
34340
|
-
"microsoft.machinelearningservices/workspaces": "AI services",
|
|
34341
|
-
// key vaults
|
|
34342
|
-
"microsoft.keyvault/vaults": "key vaults",
|
|
34343
|
-
// container registries
|
|
34344
|
-
"microsoft.containerregistry/registries": "container registries",
|
|
34345
|
-
// messaging (shared noun)
|
|
34346
|
-
"microsoft.servicebus/namespaces": "messaging namespaces",
|
|
34347
|
-
"microsoft.eventhub/namespaces": "messaging namespaces",
|
|
34348
|
-
// networking (shared noun); public DNS zones only — private zones are plumbing
|
|
34349
|
-
"microsoft.network/virtualnetworks": "networking resources",
|
|
34350
|
-
"microsoft.network/loadbalancers": "networking resources",
|
|
34351
|
-
"microsoft.network/applicationgateways": "networking resources",
|
|
34352
|
-
"microsoft.network/dnszones": "networking resources"
|
|
34353
|
-
};
|
|
34354
|
-
var MS_PER_DAY = 864e5;
|
|
34355
|
-
var REGION_MAJORITY = 0.6;
|
|
34356
|
-
var AGE_BUCKETS = [
|
|
34357
|
-
{ maxDays: 31, phrase: "all created within the last month" },
|
|
34358
|
-
{ maxDays: 93, phrase: "the oldest created a couple of months ago" },
|
|
34359
|
-
{ maxDays: 186, phrase: "the oldest created several months ago" },
|
|
34360
|
-
{ maxDays: 365, phrase: "the oldest created about a year ago" },
|
|
34361
|
-
{ maxDays: 730, phrase: "the oldest created over a year ago" }
|
|
34362
|
-
];
|
|
34363
|
-
var OLDEST_FALLBACK = "the oldest created a couple of years ago";
|
|
34364
|
-
function ageBucket(days) {
|
|
34365
|
-
for (const b of AGE_BUCKETS) if (days <= b.maxDays) return b.phrase;
|
|
34366
|
-
return OLDEST_FALLBACK;
|
|
34367
|
-
}
|
|
34368
|
-
function summarizeInventory(rows, installResourceGroup, now) {
|
|
34369
|
-
const installRg = installResourceGroup.toLowerCase();
|
|
34370
|
-
const notable = rows.filter(
|
|
34371
|
-
(r) => r.resourceGroup.toLowerCase() !== installRg && Object.hasOwn(NOTABLE_TYPES, r.type.toLowerCase())
|
|
34372
|
-
);
|
|
34373
|
-
const notableCount = notable.length;
|
|
34374
|
-
const byNoun = /* @__PURE__ */ new Map();
|
|
34375
|
-
for (const r of notable) {
|
|
34376
|
-
const noun = NOTABLE_TYPES[r.type.toLowerCase()];
|
|
34377
|
-
byNoun.set(noun, (byNoun.get(noun) ?? 0) + 1);
|
|
34378
|
-
}
|
|
34379
|
-
const categories = [...byNoun.entries()].map(([noun, count]) => ({ noun, count })).sort((a, b) => b.count - a.count || a.noun.localeCompare(b.noun));
|
|
34380
|
-
const byRegion = /* @__PURE__ */ new Map();
|
|
34381
|
-
for (const r of notable) {
|
|
34382
|
-
if (!r.location) continue;
|
|
34383
|
-
const loc = r.location.toLowerCase();
|
|
34384
|
-
byRegion.set(loc, (byRegion.get(loc) ?? 0) + 1);
|
|
34385
|
-
}
|
|
34386
|
-
const regionSpread = byRegion.size;
|
|
34387
|
-
let topRegion = null;
|
|
34388
|
-
for (const [region, count] of byRegion) {
|
|
34389
|
-
if (notableCount > 0 && count / notableCount >= REGION_MAJORITY) {
|
|
34390
|
-
topRegion = region;
|
|
34391
|
-
break;
|
|
33850
|
+
}
|
|
33851
|
+
let founderEmail = (flags.founderEmail ?? "").trim();
|
|
33852
|
+
if (!founderEmail) {
|
|
33853
|
+
const confirmed = previous?.founderEmail.trim() ?? "";
|
|
33854
|
+
const guess = identity.email.trim();
|
|
33855
|
+
if (!isTty) {
|
|
33856
|
+
if (!confirmed) return { ok: false, error: refuseFounderEmail() };
|
|
33857
|
+
founderEmail = confirmed;
|
|
33858
|
+
} else {
|
|
33859
|
+
const prefill = confirmed || guess;
|
|
33860
|
+
const answered = (await ask(renderEmailQuestion(prefill))).trim();
|
|
33861
|
+
founderEmail = answered || prefill;
|
|
33862
|
+
if (!founderEmail) return { ok: false, error: refuseFounderEmail() };
|
|
34392
33863
|
}
|
|
34393
33864
|
}
|
|
34394
|
-
|
|
34395
|
-
|
|
34396
|
-
|
|
34397
|
-
|
|
34398
|
-
|
|
34399
|
-
|
|
33865
|
+
if (!looksLikeEmail(founderEmail)) {
|
|
33866
|
+
return { ok: false, error: refuseMalformed("--founder-email", founderEmail) };
|
|
33867
|
+
}
|
|
33868
|
+
let advisor;
|
|
33869
|
+
if (flags.noAdvisor) {
|
|
33870
|
+
advisor = null;
|
|
33871
|
+
} else {
|
|
33872
|
+
const prior = previous?.advisor ?? null;
|
|
33873
|
+
let name = (flags.advisorName ?? "").trim();
|
|
33874
|
+
let email = (flags.advisorEmail ?? "").trim();
|
|
33875
|
+
if (!hasAdvisorFlag && isTty) {
|
|
33876
|
+
name = (await ask(ADVISOR_LEAD + renderAdvisorQuestion("Name ", prior?.name ?? ""))).trim();
|
|
33877
|
+
email = (await ask(renderAdvisorQuestion("Email", prior?.email ?? ""))).trim();
|
|
33878
|
+
}
|
|
33879
|
+
name = name || (prior?.name ?? "");
|
|
33880
|
+
email = email || (prior?.email ?? "");
|
|
33881
|
+
if (email && !looksLikeEmail(email)) {
|
|
33882
|
+
return { ok: false, error: refuseMalformed("--advisor-email", email) };
|
|
33883
|
+
}
|
|
33884
|
+
advisor = name || email ? { name, email } : null;
|
|
34400
33885
|
}
|
|
34401
|
-
const oldestBucket = oldestMs === null ? null : ageBucket((now - oldestMs) / MS_PER_DAY);
|
|
34402
33886
|
return {
|
|
34403
|
-
|
|
34404
|
-
|
|
34405
|
-
|
|
34406
|
-
|
|
34407
|
-
|
|
34408
|
-
|
|
33887
|
+
ok: true,
|
|
33888
|
+
profile: {
|
|
33889
|
+
schemaVersion: 1,
|
|
33890
|
+
collectedAt: args.now(),
|
|
33891
|
+
founderName: identity.name.trim(),
|
|
33892
|
+
founderEmail,
|
|
33893
|
+
advisor,
|
|
33894
|
+
...flags.noAdvisor ? { advisorCleared: true } : {}
|
|
33895
|
+
}
|
|
34409
33896
|
};
|
|
34410
33897
|
}
|
|
34411
|
-
|
|
34412
|
-
|
|
34413
|
-
var
|
|
34414
|
-
|
|
34415
|
-
|
|
34416
|
-
|
|
34417
|
-
|
|
34418
|
-
|
|
34419
|
-
|
|
34420
|
-
|
|
34421
|
-
|
|
34422
|
-
|
|
34423
|
-
|
|
34424
|
-
|
|
34425
|
-
|
|
34426
|
-
|
|
34427
|
-
|
|
34428
|
-
|
|
34429
|
-
|
|
34430
|
-
|
|
34431
|
-
}
|
|
34432
|
-
|
|
34433
|
-
|
|
34434
|
-
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
34435
|
-
}
|
|
34436
|
-
function piecesClause(s) {
|
|
34437
|
-
const shown = s.categories.slice(0, MAX_NOUNS).map((c) => `${String(c.count)} ${c.noun}`);
|
|
34438
|
-
if (s.categories.length > MAX_NOUNS) {
|
|
34439
|
-
return `The notable pieces: ${shown.join(", ")}, and a handful of others`;
|
|
34440
|
-
}
|
|
34441
|
-
return `The notable pieces: ${joinList(shown)}`;
|
|
34442
|
-
}
|
|
34443
|
-
function regionAgeClause(s) {
|
|
34444
|
-
const region = s.topRegion ? `mostly in ${regionDisplay(s.topRegion)}` : s.regionSpread > 1 ? `spread across ${String(s.regionSpread)} regions` : "";
|
|
34445
|
-
const parts = [region, s.oldestBucket ?? ""].filter((p) => p !== "");
|
|
34446
|
-
return parts.length > 0 ? ` \u2014 ${parts.join(", ")}` : "";
|
|
34447
|
-
}
|
|
34448
|
-
function renderInventoryNote(summary) {
|
|
34449
|
-
const body = summary.notableCount === 0 ? `${LEAD} There are no notable pre-existing resources.` : `${LEAD} ${piecesClause(summary)}${regionAgeClause(summary)}.`;
|
|
34450
|
-
return summary.verdict === "heavy" ? `${body} ${INVENTORY_INVITATION2}` : body;
|
|
34451
|
-
}
|
|
34452
|
-
|
|
34453
|
-
// src/lib/subscription-inventory.ts
|
|
34454
|
-
async function listSubscriptionResources(subscriptionId) {
|
|
34455
|
-
let out;
|
|
34456
|
-
try {
|
|
34457
|
-
out = await runAz(["resource", "list", "--subscription", subscriptionId, "--output", "json"]);
|
|
34458
|
-
} catch (e) {
|
|
34459
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
34460
|
-
const denied = /AuthorizationFailed|Forbidden|\b403\b|does not have authorization/i.test(msg);
|
|
34461
|
-
return { ok: false, error: denied ? "denied" : "unavailable" };
|
|
34462
|
-
}
|
|
34463
|
-
let raw;
|
|
33898
|
+
|
|
33899
|
+
// src/lib/ezra-guides.ts
|
|
33900
|
+
var EZRA_REPO = "m8t-labs/ezra";
|
|
33901
|
+
var EZRA_GUIDES_URL = `https://github.com/${EZRA_REPO}/blob/main/guides`;
|
|
33902
|
+
|
|
33903
|
+
// src/lib/chat-invite.ts
|
|
33904
|
+
var CHAT_INVITE_PATH = ".m8t/chat-invite.json";
|
|
33905
|
+
var CHAT_INVITE_TIMEOUT_MS = 3e3;
|
|
33906
|
+
var ALLOWED_INVITE_HOSTS = ["m8t.run"];
|
|
33907
|
+
function hostAllowed(hostname) {
|
|
33908
|
+
const host = hostname.toLowerCase();
|
|
33909
|
+
return ALLOWED_INVITE_HOSTS.some((allowed) => host === allowed || host.endsWith(`.${allowed}`));
|
|
33910
|
+
}
|
|
33911
|
+
var SHELL_UNSAFE = /[&|^<>"`$%\\\s]/;
|
|
33912
|
+
function chatInviteUrl() {
|
|
33913
|
+
return `https://raw.githubusercontent.com/${EZRA_REPO}/main/${CHAT_INVITE_PATH}`;
|
|
33914
|
+
}
|
|
33915
|
+
function isRecord4(value) {
|
|
33916
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33917
|
+
}
|
|
33918
|
+
async function fetchChatInvite(deps = {}) {
|
|
33919
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
33920
|
+
let parsed;
|
|
34464
33921
|
try {
|
|
34465
|
-
|
|
33922
|
+
const res = await fetchImpl(chatInviteUrl(), {
|
|
33923
|
+
headers: { accept: "application/json" },
|
|
33924
|
+
signal: AbortSignal.timeout(deps.timeoutMs ?? CHAT_INVITE_TIMEOUT_MS)
|
|
33925
|
+
});
|
|
33926
|
+
if (!res.ok) return { ok: false, reason: "http-error" };
|
|
33927
|
+
const raw2 = await res.text();
|
|
33928
|
+
try {
|
|
33929
|
+
parsed = JSON.parse(raw2);
|
|
33930
|
+
} catch {
|
|
33931
|
+
return { ok: false, reason: "malformed" };
|
|
33932
|
+
}
|
|
34466
33933
|
} catch {
|
|
34467
|
-
return { ok: false,
|
|
34468
|
-
}
|
|
34469
|
-
if (!
|
|
34470
|
-
|
|
34471
|
-
}
|
|
34472
|
-
const
|
|
34473
|
-
|
|
34474
|
-
|
|
34475
|
-
location: e.location ?? "",
|
|
34476
|
-
createdTime: e.createdTime ?? null
|
|
34477
|
-
}));
|
|
34478
|
-
return { ok: true, rows };
|
|
34479
|
-
}
|
|
34480
|
-
var DEFAULT_DEADLINE_MS2 = 25e3;
|
|
34481
|
-
async function resolveSubscriptionInventory(args) {
|
|
34482
|
-
const now = args.now ?? (() => Date.now());
|
|
34483
|
-
const deadlineMs = args.deadlineMs ?? DEFAULT_DEADLINE_MS2;
|
|
34484
|
-
const scanFn = args.scanImpl ?? listSubscriptionResources;
|
|
34485
|
-
let timer;
|
|
33934
|
+
return { ok: false, reason: "unreachable" };
|
|
33935
|
+
}
|
|
33936
|
+
if (!isRecord4(parsed)) return { ok: false, reason: "malformed" };
|
|
33937
|
+
if (parsed.schemaVersion !== 1) return { ok: false, reason: "unknown-schema" };
|
|
33938
|
+
if (parsed.enabled !== true) return { ok: false, reason: "disabled" };
|
|
33939
|
+
const raw = typeof parsed.inviteUrl === "string" ? parsed.inviteUrl.trim() : "";
|
|
33940
|
+
if (!raw) return { ok: false, reason: "no-url" };
|
|
33941
|
+
let parsedUrl;
|
|
34486
33942
|
try {
|
|
34487
|
-
|
|
34488
|
-
const scan2 = scanFn(args.subscriptionId);
|
|
34489
|
-
void scan2.catch(() => {
|
|
34490
|
-
});
|
|
34491
|
-
const timeout = new Promise((resolve6) => {
|
|
34492
|
-
timer = setTimeout(() => {
|
|
34493
|
-
resolve6("timeout");
|
|
34494
|
-
}, deadlineMs);
|
|
34495
|
-
});
|
|
34496
|
-
const raced = await Promise.race([scan2, timeout]);
|
|
34497
|
-
if (raced === "timeout" || !raced.ok) return {};
|
|
34498
|
-
const summary = summarizeInventory(raced.rows, args.installResourceGroup, now());
|
|
34499
|
-
return { note: renderInventoryNote(summary) };
|
|
33943
|
+
parsedUrl = new URL(raw);
|
|
34500
33944
|
} catch {
|
|
34501
|
-
return {};
|
|
34502
|
-
}
|
|
34503
|
-
|
|
34504
|
-
}
|
|
34505
|
-
|
|
34506
|
-
|
|
34507
|
-
|
|
34508
|
-
|
|
34509
|
-
|
|
34510
|
-
|
|
34511
|
-
|
|
34512
|
-
|
|
34513
|
-
${colors.dim("First turn may say 'warming up' for a few minutes while access propagates \u2014 that's expected.")}
|
|
33945
|
+
return { ok: false, reason: "insecure-url" };
|
|
33946
|
+
}
|
|
33947
|
+
if (parsedUrl.protocol !== "https:") return { ok: false, reason: "insecure-url" };
|
|
33948
|
+
if (!hostAllowed(parsedUrl.hostname)) return { ok: false, reason: "insecure-url" };
|
|
33949
|
+
const url = parsedUrl.href;
|
|
33950
|
+
if (SHELL_UNSAFE.test(url)) return { ok: false, reason: "insecure-url" };
|
|
33951
|
+
return { ok: true, url };
|
|
33952
|
+
}
|
|
33953
|
+
function renderChatPointer(url, opts) {
|
|
33954
|
+
const lead = "While your real Ezra installs in your cloud, talk to our Ezra to see what he can do for you.\n";
|
|
33955
|
+
return opts.print ? `${lead} Open this: ${url}
|
|
33956
|
+
` : `${lead} ${url}
|
|
34514
33957
|
`;
|
|
34515
33958
|
}
|
|
34516
|
-
|
|
34517
|
-
|
|
34518
|
-
|
|
34519
|
-
|
|
34520
|
-
|
|
33959
|
+
var CHAT_UNAVAILABLE_LINE = " Chat with Ezra isn't available right now \u2014 your install is unaffected.\n";
|
|
33960
|
+
async function openChatInvite(deps = {}) {
|
|
33961
|
+
const invite = await fetchChatInvite(deps);
|
|
33962
|
+
if (!invite.ok) return CHAT_UNAVAILABLE_LINE;
|
|
33963
|
+
const print = deps.print === true;
|
|
33964
|
+
if (!print) {
|
|
33965
|
+
const open3 = deps.open ?? tryOpenUrl;
|
|
33966
|
+
try {
|
|
33967
|
+
await open3(invite.url);
|
|
33968
|
+
} catch {
|
|
33969
|
+
}
|
|
33970
|
+
}
|
|
33971
|
+
return renderChatPointer(invite.url, { print });
|
|
34521
33972
|
}
|
|
34522
|
-
|
|
34523
|
-
|
|
34524
|
-
|
|
34525
|
-
|
|
33973
|
+
|
|
33974
|
+
// src/commands/bootstrap/profile.ts
|
|
33975
|
+
var BootstrapProfileCommand = class extends M8tCommand {
|
|
33976
|
+
static paths = [["bootstrap", "profile"]];
|
|
33977
|
+
static usage = Command63.Usage({
|
|
33978
|
+
description: "Confirm your email + your Microsoft startup advisor, and open Ezra to talk to while the install runs.",
|
|
34526
33979
|
details: [
|
|
34527
|
-
"Run after `m8t bootstrap launch`, in parallel with `status --watch`.
|
|
34528
|
-
"
|
|
34529
|
-
"
|
|
34530
|
-
"only when --voice is set \u2014 see --voice for what that does and does not affect),",
|
|
34531
|
-
"installs deps, and starts the webapp detached so a coding agent can continue without opening",
|
|
34532
|
-
"a separate terminal.",
|
|
33980
|
+
"Run after `m8t bootstrap launch`, in parallel with `status --watch`. Records the two",
|
|
33981
|
+
"facts your deployed advisors need \u2014 the address Ezra copies you on, and the Microsoft",
|
|
33982
|
+
"startup advisor it can escalate to \u2014 and points you at the hosted Ezra for the wait.",
|
|
34533
33983
|
"",
|
|
34534
|
-
"
|
|
34535
|
-
"
|
|
33984
|
+
"Non-interactive by default: pass the answers as flags and nothing is prompted. With a",
|
|
33985
|
+
"terminal, any answer you did not pass is asked for, with your Azure sign-in offered as",
|
|
33986
|
+
"the default. Idempotent \u2014 re-run it any time to correct or fill in an answer.",
|
|
33987
|
+
"",
|
|
33988
|
+
"Nothing here blocks the install, and chat never fails it."
|
|
34536
33989
|
].join("\n"),
|
|
34537
33990
|
examples: [
|
|
34538
|
-
["
|
|
34539
|
-
["
|
|
34540
|
-
["
|
|
34541
|
-
["
|
|
34542
|
-
["Experimental: start the voice relay (no effect on the text-only intake)", "$0 bootstrap ui --repo-root /path/to/m8t --voice"]
|
|
33991
|
+
["Record both answers", "$0 bootstrap profile --founder-email you@example.com --advisor-name 'Sam Lee' --advisor-email sam@example.com"],
|
|
33992
|
+
["Record the email, skip the advisor", "$0 bootstrap profile --founder-email you@example.com --no-advisor"],
|
|
33993
|
+
["Answer the questions at a terminal", "$0 bootstrap profile"],
|
|
33994
|
+
["Skip the browser hand-off", "$0 bootstrap profile --founder-email you@example.com --print"]
|
|
34543
33995
|
]
|
|
34544
33996
|
});
|
|
34545
|
-
|
|
34546
|
-
|
|
34547
|
-
|
|
34548
|
-
|
|
34549
|
-
});
|
|
34550
|
-
|
|
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, {
|
|
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."
|
|
34556
|
-
});
|
|
33997
|
+
founderEmail = Option60.String("--founder-email", { description: "The founder's contact address \u2014 Ezra copies them on its outbound mail." });
|
|
33998
|
+
advisorName = Option60.String("--advisor-name", { description: "Their Microsoft startup advisor's name." });
|
|
33999
|
+
advisorEmail = Option60.String("--advisor-email", { description: "Their Microsoft startup advisor's email." });
|
|
34000
|
+
noAdvisor = Option60.Boolean("--no-advisor", false, { description: "Record that they have no startup advisor to add (or don't know it yet)." });
|
|
34001
|
+
noChat = Option60.Boolean("--no-chat", false, { description: "Record the answers without opening the hosted Ezra." });
|
|
34002
|
+
print = Option60.Boolean("--print", false, { description: "Print the chat link instead of opening a browser (headless / SSH)." });
|
|
34557
34003
|
async executeCommand() {
|
|
34558
|
-
|
|
34559
|
-
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34563
|
-
|
|
34564
|
-
|
|
34565
|
-
|
|
34566
|
-
|
|
34567
|
-
|
|
34568
|
-
|
|
34569
|
-
|
|
34570
|
-
|
|
34571
|
-
|
|
34572
|
-
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34578
|
-
|
|
34579
|
-
|
|
34580
|
-
|
|
34581
|
-
|
|
34582
|
-
});
|
|
34583
|
-
if (this.port !== "3000") {
|
|
34584
|
-
throw new LocalCliError({
|
|
34585
|
-
code: "BOOTSTRAP_UI_PORT_MSAL_LOCKED",
|
|
34586
|
-
message: `--port ${this.port} is not supported: the app registration's redirect URI is fixed to localhost:3000, so Microsoft sign-in only works there.`,
|
|
34587
|
-
hint: "Omit --port (it defaults to 3000). If port 3000 is busy, stop the other process using it."
|
|
34004
|
+
const stdin = this.context.stdin;
|
|
34005
|
+
const stdout = this.context.stdout;
|
|
34006
|
+
const isTty = stdin.isTTY === true;
|
|
34007
|
+
const flags = {
|
|
34008
|
+
founderEmail: typeof this.founderEmail === "string" ? this.founderEmail : void 0,
|
|
34009
|
+
advisorName: typeof this.advisorName === "string" ? this.advisorName : void 0,
|
|
34010
|
+
advisorEmail: typeof this.advisorEmail === "string" ? this.advisorEmail : void 0,
|
|
34011
|
+
noAdvisor: this.noAdvisor === true
|
|
34012
|
+
};
|
|
34013
|
+
const [identity, previous] = await Promise.all([
|
|
34014
|
+
getSignedInUserIdentity(),
|
|
34015
|
+
readOnboardingProfile()
|
|
34016
|
+
]);
|
|
34017
|
+
const mayAsk = isTty && (!(flags.founderEmail ?? "").trim() || !(flags.noAdvisor || (flags.advisorName ?? "").trim() || (flags.advisorEmail ?? "").trim()));
|
|
34018
|
+
const rl = mayAsk ? readline3.createInterface({ input: stdin, output: stdout }) : null;
|
|
34019
|
+
let collected;
|
|
34020
|
+
try {
|
|
34021
|
+
collected = await collectProfile({
|
|
34022
|
+
flags,
|
|
34023
|
+
identity,
|
|
34024
|
+
previous,
|
|
34025
|
+
isTty,
|
|
34026
|
+
ask: async (question) => rl ? await rl.question(question) : "",
|
|
34027
|
+
now: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
34588
34028
|
});
|
|
34029
|
+
} finally {
|
|
34030
|
+
rl?.close();
|
|
34589
34031
|
}
|
|
34590
|
-
|
|
34591
|
-
|
|
34032
|
+
if (!collected.ok) throw collected.error;
|
|
34033
|
+
const profile = collected.profile;
|
|
34034
|
+
await writeOnboardingProfile(profile);
|
|
34035
|
+
this.context.stdout.write(`${colors.success("\u2713")} Recorded \u2014 Ezra will copy you at ${colors.field(profile.founderEmail)}.
|
|
34592
34036
|
`);
|
|
34593
|
-
|
|
34594
|
-
|
|
34595
|
-
|
|
34037
|
+
if (profile.advisor === null) this.context.stdout.write(colors.dim(ADVISOR_SKIPPED_LINE));
|
|
34038
|
+
await this.seedIfBrainsExist(profile);
|
|
34039
|
+
if (this.noChat !== true) {
|
|
34040
|
+
this.context.stdout.write(await openChatInvite({ print: this.print === true }));
|
|
34596
34041
|
}
|
|
34597
|
-
|
|
34598
|
-
|
|
34599
|
-
|
|
34600
|
-
|
|
34601
|
-
|
|
34602
|
-
|
|
34603
|
-
|
|
34604
|
-
|
|
34605
|
-
|
|
34606
|
-
|
|
34607
|
-
|
|
34608
|
-
|
|
34609
|
-
|
|
34610
|
-
|
|
34611
|
-
|
|
34612
|
-
|
|
34613
|
-
|
|
34614
|
-
|
|
34615
|
-
|
|
34616
|
-
await ensureFounderFoundryRole({ credential: credential2, subscriptionId: state.subscriptionId, principalId: oid, accountScope });
|
|
34617
|
-
const identity = await getSignedInUserIdentity();
|
|
34618
|
-
out("deploying the intake agent (ezra-intake) in the background...");
|
|
34619
|
-
const deployOutcome = (async () => {
|
|
34620
|
-
const [choice, inventory] = await Promise.all([
|
|
34621
|
-
resolveIntakeModel({
|
|
34622
|
-
accountScope,
|
|
34623
|
-
fallbackRegion: state.location,
|
|
34624
|
-
onNarrate: out
|
|
34625
|
-
}),
|
|
34626
|
-
resolveSubscriptionInventory({
|
|
34627
|
-
subscriptionId: state.subscriptionId,
|
|
34628
|
-
installResourceGroup: state.resourceGroup,
|
|
34629
|
-
onNarrate: out
|
|
34630
|
-
})
|
|
34631
|
-
]);
|
|
34632
|
-
return deployIntakeAgentWithRetry({
|
|
34633
|
-
credential: credential2,
|
|
34634
|
-
endpoint,
|
|
34635
|
-
repoRoot,
|
|
34636
|
-
model: choice.model,
|
|
34637
|
-
fieldOverrides: buildIntakeFieldOverrides({
|
|
34638
|
-
founderIdentityNote: composeFounderIdentityNote(identity),
|
|
34639
|
-
chosenModelNote: choice.note,
|
|
34640
|
-
inventoryNote: inventory.note
|
|
34641
|
-
}),
|
|
34642
|
-
onWait: out
|
|
34643
|
-
});
|
|
34644
|
-
})().then(
|
|
34645
|
-
(version) => ({ ok: true, version }),
|
|
34646
|
-
(error) => ({ ok: false, error: error instanceof Error ? error : new Error(String(error)) })
|
|
34647
|
-
);
|
|
34648
|
-
const envPath = writeWebEnvLocal({
|
|
34649
|
-
repoRoot,
|
|
34650
|
-
tenantId: account.tenantId,
|
|
34651
|
-
clientId: state.appRegClientId,
|
|
34652
|
-
foundryEndpoint: endpoint,
|
|
34653
|
-
voice: this.voice === true
|
|
34654
|
-
});
|
|
34655
|
-
if (!this.skipInstall) {
|
|
34656
|
-
out("installing apps/web dependencies (pnpm)\u2026");
|
|
34657
|
-
await installWebDeps(repoRoot);
|
|
34658
|
-
}
|
|
34659
|
-
if (this.prepOnly === true) {
|
|
34660
|
-
const outcome2 = await deployOutcome;
|
|
34661
|
-
if (!outcome2.ok) throw outcome2.error;
|
|
34662
|
-
this.context.stdout.write(renderDeploySuccess(outcome2.version, envPath));
|
|
34663
|
-
this.context.stdout.write(` ${colors.hint("serve it yourself:")} pnpm --filter web dev
|
|
34664
|
-
`);
|
|
34665
|
-
return 0;
|
|
34042
|
+
return 0;
|
|
34043
|
+
}
|
|
34044
|
+
/**
|
|
34045
|
+
* Seed now if the brains already exist, otherwise say who will.
|
|
34046
|
+
*
|
|
34047
|
+
* Best-effort on purpose. This command runs mid-install by design, when there is
|
|
34048
|
+
* usually nothing to seed yet — `bootstrap status --watch` does it at `done`. But
|
|
34049
|
+
* re-running this after the install should not leave a founder wondering whether
|
|
34050
|
+
* their correction landed, so when the brains ARE there we seed immediately.
|
|
34051
|
+
*
|
|
34052
|
+
* A failure here is reported and swallowed: the answers are already safely on disk,
|
|
34053
|
+
* and the install path will retry the seed on its own.
|
|
34054
|
+
*/
|
|
34055
|
+
async seedIfBrainsExist(profile) {
|
|
34056
|
+
let ctx = null;
|
|
34057
|
+
try {
|
|
34058
|
+
ctx = await resolveSeedContext({});
|
|
34059
|
+
} catch {
|
|
34060
|
+
ctx = null;
|
|
34666
34061
|
}
|
|
34667
|
-
if (
|
|
34668
|
-
|
|
34669
|
-
|
|
34670
|
-
|
|
34671
|
-
|
|
34672
|
-
|
|
34673
|
-
tenantId: account.tenantId,
|
|
34674
|
-
clientId: state.appRegClientId,
|
|
34675
|
-
webInternalBaseUrl: "http://localhost:3000"
|
|
34676
|
-
});
|
|
34677
|
-
out("voice relay on :8790");
|
|
34678
|
-
}
|
|
34679
|
-
const outcome2 = await deployOutcome;
|
|
34680
|
-
if (!outcome2.ok) {
|
|
34681
|
-
this.context.stderr.write(renderDeployFailure(outcome2.error));
|
|
34682
|
-
return 1;
|
|
34683
|
-
}
|
|
34684
|
-
this.context.stdout.write(renderDeploySuccess(outcome2.version, envPath));
|
|
34685
|
-
out("starting the webapp on :3000 (Ctrl-C to stop)\u2026");
|
|
34686
|
-
await runInherit("pnpm", ["--filter", "web", "dev"], repoRoot, "BOOTSTRAP_UI_DEV_FAILED");
|
|
34687
|
-
return 0;
|
|
34062
|
+
if (!ctx) {
|
|
34063
|
+
this.context.stdout.write(
|
|
34064
|
+
` ${colors.dim("Your advisors will pick this up when the install finishes.")}
|
|
34065
|
+
`
|
|
34066
|
+
);
|
|
34067
|
+
return;
|
|
34688
34068
|
}
|
|
34689
|
-
|
|
34690
|
-
|
|
34691
|
-
|
|
34692
|
-
|
|
34693
|
-
|
|
34694
|
-
|
|
34695
|
-
|
|
34696
|
-
|
|
34069
|
+
try {
|
|
34070
|
+
await applyProfileToBrains({
|
|
34071
|
+
block: toOnboardingBlock(profile),
|
|
34072
|
+
brainRepos: ctx.brainRepos,
|
|
34073
|
+
branch: "main",
|
|
34074
|
+
appCreds: ctx.appCreds,
|
|
34075
|
+
subscriptionId: ctx.subscriptionId,
|
|
34076
|
+
azIdentity: { name: profile.founderName, email: profile.founderEmail },
|
|
34077
|
+
contactsOnly: true,
|
|
34078
|
+
advisorCleared: profile.advisorCleared === true
|
|
34697
34079
|
});
|
|
34698
|
-
out("voice relay on :8790");
|
|
34699
|
-
}
|
|
34700
|
-
out("starting the webapp on :3000 in the background\u2026");
|
|
34701
|
-
const { alreadyRunning, logPath } = await serveOnboardingUiDetached({ repoRoot, port: this.port });
|
|
34702
|
-
if (alreadyRunning) {
|
|
34703
34080
|
this.context.stdout.write(
|
|
34704
|
-
`${colors.success("\u2713")}
|
|
34081
|
+
`${colors.success("\u2713")} Your advisors now know how to reach you (seeded ${ctx.brainRepos.join(", ")}).
|
|
34705
34082
|
`
|
|
34706
34083
|
);
|
|
34707
|
-
}
|
|
34708
|
-
this.context.
|
|
34709
|
-
|
|
34710
|
-
${colors.
|
|
34084
|
+
} catch (e) {
|
|
34085
|
+
this.context.stderr.write(
|
|
34086
|
+
` ${colors.dim(`saved, but the brain seed didn't land yet: ${e instanceof Error ? e.message : String(e)}`)}
|
|
34087
|
+
${colors.hint("it retries when the install finishes, or run:")} m8t bootstrap seed-profile
|
|
34711
34088
|
`
|
|
34712
34089
|
);
|
|
34713
34090
|
}
|
|
34714
|
-
autoOpenOnboardingUi(`http://localhost:${this.port}`);
|
|
34715
|
-
const outcome = await deployOutcome;
|
|
34716
|
-
if (!outcome.ok) {
|
|
34717
|
-
this.context.stderr.write(renderDeployFailure(outcome.error));
|
|
34718
|
-
return 1;
|
|
34719
|
-
}
|
|
34720
|
-
this.context.stdout.write(renderDeploySuccess(outcome.version, envPath));
|
|
34721
|
-
return 0;
|
|
34722
34091
|
}
|
|
34723
34092
|
};
|
|
34724
34093
|
|
|
34725
34094
|
// src/commands/bootstrap/seed-profile.ts
|
|
34726
|
-
import { Command as
|
|
34095
|
+
import { Command as Command64, Option as Option61 } from "clipanion";
|
|
34727
34096
|
var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
34728
34097
|
static paths = [["bootstrap", "seed-profile"]];
|
|
34729
|
-
static usage =
|
|
34730
|
-
description: "Seed your advisors' brains with
|
|
34731
|
-
details:
|
|
34098
|
+
static usage = Command64.Usage({
|
|
34099
|
+
description: "Seed your advisors' brains with how to reach you, from what you confirmed at `m8t bootstrap profile`.",
|
|
34100
|
+
details: [
|
|
34101
|
+
"Renders memory/founder.md + memory/company-profile.md (+ their MEMORY.md index lines)",
|
|
34102
|
+
"and commits them to the brains this install created, via the GitHub App. Idempotent \u2014",
|
|
34103
|
+
"an unchanged profile is a no-op.",
|
|
34104
|
+
"",
|
|
34105
|
+
"Run `m8t bootstrap profile` first; this is the manual re-run of the seed that",
|
|
34106
|
+
"`m8t bootstrap status --watch` already does when the install completes.",
|
|
34107
|
+
"",
|
|
34108
|
+
"--watch is legacy: it polls for an onboarding conversation from installs made when a",
|
|
34109
|
+
"dedicated intake agent was still deployed. It has nothing to wait for otherwise."
|
|
34110
|
+
].join("\n"),
|
|
34732
34111
|
examples: [
|
|
34733
|
-
["Seed now (idempotent)", "$0 bootstrap seed-profile"]
|
|
34734
|
-
["Wait for the founder to finish", "$0 bootstrap seed-profile --watch"]
|
|
34112
|
+
["Seed now (idempotent)", "$0 bootstrap seed-profile"]
|
|
34735
34113
|
]
|
|
34736
34114
|
});
|
|
34737
|
-
endpoint =
|
|
34738
|
-
brain =
|
|
34739
|
-
watch =
|
|
34740
|
-
timeout =
|
|
34741
|
-
githubAppCreds =
|
|
34115
|
+
endpoint = Option61.String("--endpoint", { description: "Override the Foundry endpoint (else read from the install status)." });
|
|
34116
|
+
brain = Option61.String("--brain", { description: "Seed only this one brain repo, instead of both <org>/stacey-brain and <org>/ezra-brain." });
|
|
34117
|
+
watch = Option61.Boolean("--watch", false, { description: "Poll until the intake completes (or --timeout)." });
|
|
34118
|
+
timeout = Option61.String("--timeout", { description: "Watch timeout in minutes (default 20)." });
|
|
34119
|
+
githubAppCreds = Option61.String("--github-app-creds");
|
|
34742
34120
|
async executeCommand() {
|
|
34743
34121
|
const ctx = await resolveSeedContext({
|
|
34744
34122
|
endpointOverride: typeof this.endpoint === "string" ? this.endpoint : void 0,
|
|
@@ -34750,12 +34128,30 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34750
34128
|
`);
|
|
34751
34129
|
return 0;
|
|
34752
34130
|
}
|
|
34131
|
+
const local = await readOnboardingProfile();
|
|
34132
|
+
if (local) {
|
|
34133
|
+
await applyProfileToBrains({
|
|
34134
|
+
block: toOnboardingBlock(local),
|
|
34135
|
+
brainRepos: ctx.brainRepos,
|
|
34136
|
+
branch: "main",
|
|
34137
|
+
appCreds: ctx.appCreds,
|
|
34138
|
+
subscriptionId: ctx.subscriptionId,
|
|
34139
|
+
azIdentity: { name: local.founderName, email: local.founderEmail },
|
|
34140
|
+
contactsOnly: true,
|
|
34141
|
+
advisorCleared: local.advisorCleared === true
|
|
34142
|
+
});
|
|
34143
|
+
this.context.stdout.write(
|
|
34144
|
+
`${colors.success("\u2713")} seeded your advisors' brains (${ctx.brainRepos.join(", ")}) with how to reach you.
|
|
34145
|
+
`
|
|
34146
|
+
);
|
|
34147
|
+
return 0;
|
|
34148
|
+
}
|
|
34753
34149
|
const watch = this.watch === true;
|
|
34754
34150
|
const rawTimeout = typeof this.timeout === "string" ? this.timeout.trim() : "";
|
|
34755
34151
|
const parsedTimeout = Number(rawTimeout);
|
|
34756
34152
|
const timeoutMin = rawTimeout !== "" && Number.isFinite(parsedTimeout) ? parsedTimeout : 20;
|
|
34757
34153
|
const deadline = Date.now() + timeoutMin * 6e4;
|
|
34758
|
-
const
|
|
34154
|
+
const sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
34759
34155
|
for (; ; ) {
|
|
34760
34156
|
const token = await getFoundryToken();
|
|
34761
34157
|
const { hadIntake, block, rejection } = await findOnboardingProfile({ endpoint: ctx.endpoint, token });
|
|
@@ -34782,9 +34178,16 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34782
34178
|
);
|
|
34783
34179
|
return 3;
|
|
34784
34180
|
}
|
|
34785
|
-
|
|
34181
|
+
if (!hadIntake) {
|
|
34182
|
+
this.context.stderr.write(
|
|
34183
|
+
` ${colors.dim("nothing collected yet \u2014 your advisors don't know how to reach you.")}
|
|
34184
|
+
${colors.hint("run:")} m8t bootstrap profile
|
|
34185
|
+
`
|
|
34186
|
+
);
|
|
34187
|
+
return 3;
|
|
34188
|
+
}
|
|
34786
34189
|
this.context.stderr.write(
|
|
34787
|
-
` ${colors.dim(
|
|
34190
|
+
` ${colors.dim("intake not complete yet \u2014 no m8t_onboarding block found.")}
|
|
34788
34191
|
${colors.hint("retry:")} m8t bootstrap seed-profile
|
|
34789
34192
|
`
|
|
34790
34193
|
);
|
|
@@ -34792,7 +34195,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34792
34195
|
}
|
|
34793
34196
|
this.context.stderr.write(` ${colors.dim("waiting for the intake to complete\u2026")}
|
|
34794
34197
|
`);
|
|
34795
|
-
await
|
|
34198
|
+
await sleep4(2e4);
|
|
34796
34199
|
}
|
|
34797
34200
|
}
|
|
34798
34201
|
};
|
|
@@ -34801,7 +34204,7 @@ var BootstrapSeedProfileCommand = class extends M8tCommand {
|
|
|
34801
34204
|
import * as fs41 from "fs";
|
|
34802
34205
|
import * as os22 from "os";
|
|
34803
34206
|
import * as path46 from "path";
|
|
34804
|
-
import { Command as
|
|
34207
|
+
import { Command as Command65, Option as Option62 } from "clipanion";
|
|
34805
34208
|
init_errors();
|
|
34806
34209
|
|
|
34807
34210
|
// src/lib/telemetry-enroll.ts
|
|
@@ -34820,7 +34223,7 @@ async function defaultToken() {
|
|
|
34820
34223
|
const token = out.trim();
|
|
34821
34224
|
return token.length > 0 ? token : null;
|
|
34822
34225
|
}
|
|
34823
|
-
var
|
|
34226
|
+
var sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
34824
34227
|
async function enroll(args) {
|
|
34825
34228
|
const f = args.fetchImpl ?? fetch;
|
|
34826
34229
|
const url = args.baseUrl ? `${args.baseUrl.replace(/\/+$/, "")}/api/ingest/enroll` : ingestUrl("/api/ingest/enroll");
|
|
@@ -34841,7 +34244,7 @@ async function enroll(args) {
|
|
|
34841
34244
|
throw new LocalCliError({ code: "TELEMETRY_ENROLL_UNREACHABLE", message: `Could not reach the telemetry ingest to enroll: ${e instanceof Error ? e.message : String(e)}` });
|
|
34842
34245
|
}
|
|
34843
34246
|
if (res.status !== 503 || attempt === 3) break;
|
|
34844
|
-
await
|
|
34247
|
+
await sleep3(retryDelayMs * attempt);
|
|
34845
34248
|
}
|
|
34846
34249
|
if (!res?.ok) {
|
|
34847
34250
|
const detail = res ? await res.text().catch(() => "") : "";
|
|
@@ -34883,7 +34286,7 @@ async function resolveKeyVaultName(containerAppResourceId) {
|
|
|
34883
34286
|
}
|
|
34884
34287
|
var TelemetryEnrollCommand = class extends M8tCommand {
|
|
34885
34288
|
static paths = [["telemetry", "enroll"]];
|
|
34886
|
-
static usage =
|
|
34289
|
+
static usage = Command65.Usage({
|
|
34887
34290
|
description: "Enroll this installation for operational telemetry (pre-existing installs).",
|
|
34888
34291
|
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.",
|
|
34889
34292
|
examples: [
|
|
@@ -34891,11 +34294,11 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34891
34294
|
["Enroll and share a support contact", "$0 telemetry enroll --contact-email you@example.com --company 'Acme'"]
|
|
34892
34295
|
]
|
|
34893
34296
|
});
|
|
34894
|
-
company =
|
|
34895
|
-
contactEmail =
|
|
34896
|
-
subscription =
|
|
34897
|
-
resourceGroup =
|
|
34898
|
-
force =
|
|
34297
|
+
company = Option62.String("--company", { description: "Share your company name with m8t support. Not sent unless you pass it." });
|
|
34298
|
+
contactEmail = Option62.String("--contact-email", { description: "Share a contact email with m8t support. Not sent unless you pass it." });
|
|
34299
|
+
subscription = Option62.String("--subscription", { description: "Azure subscription id used to find your deployment. Never sent to m8t." });
|
|
34300
|
+
resourceGroup = Option62.String("--resource-group", { description: "Resource group to disambiguate discovery, if you have more than one m8t deployment." });
|
|
34301
|
+
force = Option62.Boolean("--force", false, { description: "Enroll even when a key is already stored. Use only when m8t support asks you to." });
|
|
34899
34302
|
async executeCommand() {
|
|
34900
34303
|
const account = await getAzAccount();
|
|
34901
34304
|
const subscriptionId = (typeof this.subscription === "string" ? this.subscription : void 0) ?? account.subscriptionId;
|
|
@@ -34945,7 +34348,7 @@ var TelemetryEnrollCommand = class extends M8tCommand {
|
|
|
34945
34348
|
};
|
|
34946
34349
|
|
|
34947
34350
|
// src/commands/companion/bridge.ts
|
|
34948
|
-
import { Command as
|
|
34351
|
+
import { Command as Command66, Option as Option63 } from "clipanion";
|
|
34949
34352
|
|
|
34950
34353
|
// ../../packages/companion-bridge-contract/src/index.ts
|
|
34951
34354
|
var COMPANION_MESSAGE_MAX_CODE_POINTS = 32768;
|
|
@@ -34984,7 +34387,7 @@ var VERSION3 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/u;
|
|
|
34984
34387
|
function isVersionOrNull(value) {
|
|
34985
34388
|
return value === null || typeof value === "string" && VERSION3.test(value);
|
|
34986
34389
|
}
|
|
34987
|
-
function
|
|
34390
|
+
function isRecord5(value) {
|
|
34988
34391
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34989
34392
|
}
|
|
34990
34393
|
function hasExactKeys(value, keys) {
|
|
@@ -35009,11 +34412,11 @@ function isInstantOrNull(value) {
|
|
|
35009
34412
|
return value === null || typeof value === "string" && INSTANT_SHAPE.test(value) && Number.isFinite(Date.parse(value));
|
|
35010
34413
|
}
|
|
35011
34414
|
function parseDecision(value) {
|
|
35012
|
-
if (!
|
|
34415
|
+
if (!isRecord5(value) || !isBoundedPlainString(value.callId, COMPANION_DECISION_CALL_ID_MAX_LENGTH) || !isBoundedPlainString(value.title, COMPANION_DECISION_TITLE_MAX_LENGTH) || !Array.isArray(value.options) || value.options.length < COMPANION_DECISION_OPTIONS_MIN || value.options.length > COMPANION_DECISION_OPTIONS_MAX) {
|
|
35013
34416
|
return eventError();
|
|
35014
34417
|
}
|
|
35015
34418
|
const options = value.options.map((option) => {
|
|
35016
|
-
if (!
|
|
34419
|
+
if (!isRecord5(option) || !hasExactKeys(option, ["label", "detail"]) || !isBoundedPlainString(option.label, COMPANION_DECISION_LABEL_MAX_LENGTH) || !isBoundedMessageText(option.detail, COMPANION_DECISION_DETAIL_MAX_CODE_POINTS)) {
|
|
35017
34420
|
return eventError();
|
|
35018
34421
|
}
|
|
35019
34422
|
return { label: option.label, detail: option.detail };
|
|
@@ -35031,7 +34434,7 @@ function parseDecision(value) {
|
|
|
35031
34434
|
return { ...base, status: "selected", optionIndex: value.optionIndex };
|
|
35032
34435
|
}
|
|
35033
34436
|
function parseArtifact(value) {
|
|
35034
|
-
if (!
|
|
34437
|
+
if (!isRecord5(value) || !isBoundedPlainString(value.name, COMPANION_ARTIFACT_NAME_MAX_LENGTH)) {
|
|
35035
34438
|
return eventError();
|
|
35036
34439
|
}
|
|
35037
34440
|
if (value.sizeBytes === void 0) {
|
|
@@ -35050,7 +34453,7 @@ function parseTurnArtifacts(value) {
|
|
|
35050
34453
|
return value.map(parseArtifact);
|
|
35051
34454
|
}
|
|
35052
34455
|
function parseTurn(value) {
|
|
35053
|
-
if (!
|
|
34456
|
+
if (!isRecord5(value) || !isBoundedPlainString(value.id, COMPANION_TURN_ID_MAX_LENGTH) || value.role !== "user" && value.role !== "mate" || !isInstantOrNull(value.at)) {
|
|
35054
34457
|
return eventError();
|
|
35055
34458
|
}
|
|
35056
34459
|
const keys = [
|
|
@@ -35129,7 +34532,7 @@ function isValidMateRoute(value) {
|
|
|
35129
34532
|
}
|
|
35130
34533
|
}
|
|
35131
34534
|
function parseRequest(value) {
|
|
35132
|
-
if (!
|
|
34535
|
+
if (!isRecord5(value)) return requestError();
|
|
35133
34536
|
if (value.type === "roster") {
|
|
35134
34537
|
if (!hasExactKeys(value, ["type"])) return requestError();
|
|
35135
34538
|
return { type: "roster" };
|
|
@@ -35195,7 +34598,7 @@ function parseRequestLine(line2) {
|
|
|
35195
34598
|
}
|
|
35196
34599
|
}
|
|
35197
34600
|
function parseMate(value) {
|
|
35198
|
-
if (!
|
|
34601
|
+
if (!isRecord5(value) || !hasExactKeys(value, [
|
|
35199
34602
|
"personaKey",
|
|
35200
34603
|
"agentName",
|
|
35201
34604
|
"displayName",
|
|
@@ -35220,7 +34623,7 @@ function parseMate(value) {
|
|
|
35220
34623
|
};
|
|
35221
34624
|
}
|
|
35222
34625
|
function parseEvent(value) {
|
|
35223
|
-
if (!
|
|
34626
|
+
if (!isRecord5(value)) return eventError();
|
|
35224
34627
|
if (value.type === "update") {
|
|
35225
34628
|
if (!hasExactKeys(value, ["type", "installed", "available", "severity"]) || !isVersionOrNull(value.installed) || !isVersionOrNull(value.available) || !(value.severity === null || SEVERITIES2.has(value.severity))) {
|
|
35226
34629
|
return eventError();
|
|
@@ -35359,7 +34762,7 @@ var PredispatchFailure = class extends Error {
|
|
|
35359
34762
|
}
|
|
35360
34763
|
reason;
|
|
35361
34764
|
};
|
|
35362
|
-
function
|
|
34765
|
+
function isRecord6(value) {
|
|
35363
34766
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
35364
34767
|
}
|
|
35365
34768
|
function hasControlCharacter(value) {
|
|
@@ -35429,16 +34832,16 @@ async function readJsonEnvelope(response) {
|
|
|
35429
34832
|
if (error instanceof PredispatchFailure) throw error;
|
|
35430
34833
|
throw new PredispatchFailure("not-connected");
|
|
35431
34834
|
}
|
|
35432
|
-
if (!
|
|
34835
|
+
if (!isRecord6(envelope) || envelope.ok !== true || !("data" in envelope)) {
|
|
35433
34836
|
throw new PredispatchFailure("not-connected");
|
|
35434
34837
|
}
|
|
35435
34838
|
return envelope.data;
|
|
35436
34839
|
}
|
|
35437
34840
|
function decodeAgents(data) {
|
|
35438
|
-
if (!
|
|
34841
|
+
if (!isRecord6(data) || !Array.isArray(data.agents)) {
|
|
35439
34842
|
throw new PredispatchFailure("mate-unavailable");
|
|
35440
34843
|
}
|
|
35441
|
-
return data.agents.filter(
|
|
34844
|
+
return data.agents.filter(isRecord6);
|
|
35442
34845
|
}
|
|
35443
34846
|
function resolveAgentName(agents, personaKey) {
|
|
35444
34847
|
const matches = agents.filter(
|
|
@@ -35460,18 +34863,18 @@ function requireAgentName(agents, personaKey) {
|
|
|
35460
34863
|
return name;
|
|
35461
34864
|
}
|
|
35462
34865
|
function decodeConversationId(data) {
|
|
35463
|
-
if (!
|
|
34866
|
+
if (!isRecord6(data) || typeof data.id !== "string" || !OPAQUE_ID.test(data.id)) {
|
|
35464
34867
|
throw new PredispatchFailure("not-connected");
|
|
35465
34868
|
}
|
|
35466
34869
|
return data.id;
|
|
35467
34870
|
}
|
|
35468
34871
|
function decodeLastMessageId(data) {
|
|
35469
|
-
if (!
|
|
34872
|
+
if (!isRecord6(data) || !Array.isArray(data.messages)) {
|
|
35470
34873
|
throw new PredispatchFailure("not-connected");
|
|
35471
34874
|
}
|
|
35472
34875
|
const messages = data.messages;
|
|
35473
34876
|
const last = messages.at(-1);
|
|
35474
|
-
if (!
|
|
34877
|
+
if (!isRecord6(last) || typeof last.id !== "string" || !OPAQUE_ID.test(last.id)) {
|
|
35475
34878
|
return void 0;
|
|
35476
34879
|
}
|
|
35477
34880
|
return last.id;
|
|
@@ -35557,7 +34960,7 @@ function toCompanionArtifacts(data) {
|
|
|
35557
34960
|
const artifacts = [];
|
|
35558
34961
|
for (const entry of data) {
|
|
35559
34962
|
if (artifacts.length >= COMPANION_ARTIFACTS_MAX) break;
|
|
35560
|
-
if (!
|
|
34963
|
+
if (!isRecord6(entry) || typeof entry.name !== "string") continue;
|
|
35561
34964
|
const name = boundPlainLine(entry.name, COMPANION_ARTIFACT_NAME_MAX_LENGTH);
|
|
35562
34965
|
if (name.length === 0) continue;
|
|
35563
34966
|
const size = entry.size_bytes;
|
|
@@ -35571,12 +34974,12 @@ function decodeMessageInstant(value) {
|
|
|
35571
34974
|
return typeof value === "number" && Number.isFinite(value) && value > 0 && value < EPOCH_SECONDS_LIMIT ? new Date(Math.round(value) * 1e3).toISOString() : null;
|
|
35572
34975
|
}
|
|
35573
34976
|
function decodeTurns(data, limit2) {
|
|
35574
|
-
if (!
|
|
34977
|
+
if (!isRecord6(data) || !Array.isArray(data.messages)) {
|
|
35575
34978
|
throw new PredispatchFailure("not-connected");
|
|
35576
34979
|
}
|
|
35577
34980
|
const turns = [];
|
|
35578
34981
|
for (const entry of data.messages) {
|
|
35579
|
-
if (!
|
|
34982
|
+
if (!isRecord6(entry)) continue;
|
|
35580
34983
|
if (entry.role !== "user" && entry.role !== "assistant") continue;
|
|
35581
34984
|
if (typeof entry.id !== "string" || !OPAQUE_ID.test(entry.id)) continue;
|
|
35582
34985
|
if (typeof entry.content !== "string") continue;
|
|
@@ -35603,12 +35006,12 @@ function decodeDurableDecision(entry) {
|
|
|
35603
35006
|
return toCompanionDecision(frame.directive);
|
|
35604
35007
|
}
|
|
35605
35008
|
function decodeRichTurns(data, limit2) {
|
|
35606
|
-
if (!
|
|
35009
|
+
if (!isRecord6(data) || !Array.isArray(data.messages)) {
|
|
35607
35010
|
throw new PredispatchFailure("not-connected");
|
|
35608
35011
|
}
|
|
35609
35012
|
const turns = [];
|
|
35610
35013
|
for (const entry of data.messages) {
|
|
35611
|
-
if (!
|
|
35014
|
+
if (!isRecord6(entry)) continue;
|
|
35612
35015
|
if (entry.role !== "user" && entry.role !== "assistant") continue;
|
|
35613
35016
|
if (typeof entry.id !== "string" || !OPAQUE_ID.test(entry.id)) continue;
|
|
35614
35017
|
if (typeof entry.content !== "string") continue;
|
|
@@ -35744,7 +35147,7 @@ async function drainAcceptedSse(response) {
|
|
|
35744
35147
|
continue;
|
|
35745
35148
|
}
|
|
35746
35149
|
const parsed = JSON.parse(data);
|
|
35747
|
-
if (!
|
|
35150
|
+
if (!isRecord6(parsed) || typeof parsed.type !== "string" || parsed.type === "error") {
|
|
35748
35151
|
throw new Error("invalid stream event");
|
|
35749
35152
|
}
|
|
35750
35153
|
}
|
|
@@ -35792,7 +35195,7 @@ async function streamAcceptedSse(response, now, onText, onData) {
|
|
|
35792
35195
|
return;
|
|
35793
35196
|
}
|
|
35794
35197
|
const parsed = JSON.parse(data);
|
|
35795
|
-
if (!
|
|
35198
|
+
if (!isRecord6(parsed) || typeof parsed.type !== "string" || parsed.type === "error") {
|
|
35796
35199
|
throw new Error("invalid stream event");
|
|
35797
35200
|
}
|
|
35798
35201
|
if (parsed.type === "text-delta" && typeof parsed.delta === "string" && parsed.delta.length > 0) {
|
|
@@ -36016,7 +35419,7 @@ async function classifyDecideRefusal(response) {
|
|
|
36016
35419
|
let reason;
|
|
36017
35420
|
try {
|
|
36018
35421
|
const envelope = JSON.parse(await readBoundedText(response));
|
|
36019
|
-
if (
|
|
35422
|
+
if (isRecord6(envelope) && isRecord6(envelope.error) && isRecord6(envelope.error.details)) {
|
|
36020
35423
|
reason = envelope.error.details.reason;
|
|
36021
35424
|
}
|
|
36022
35425
|
} catch {
|
|
@@ -36401,14 +35804,14 @@ async function runCompanionBridge(stdin, stdout, stderr, deps = defaultDeps4) {
|
|
|
36401
35804
|
return 3;
|
|
36402
35805
|
}
|
|
36403
35806
|
}
|
|
36404
|
-
var CompanionBridgeCommand = class extends
|
|
35807
|
+
var CompanionBridgeCommand = class extends Command66 {
|
|
36405
35808
|
static paths = [["companion", "_bridge"]];
|
|
36406
35809
|
/**
|
|
36407
35810
|
* One process serving many requests instead of one per request, so the
|
|
36408
35811
|
* session keeps its authenticated context between them. A CLI predating the
|
|
36409
35812
|
* flag rejects it outright, which is how the app knows to fall back.
|
|
36410
35813
|
*/
|
|
36411
|
-
serve =
|
|
35814
|
+
serve = Option63.Boolean("--serve", false);
|
|
36412
35815
|
async execute() {
|
|
36413
35816
|
if (this.serve) {
|
|
36414
35817
|
return runCompanionBridgeServe(
|
|
@@ -36426,7 +35829,7 @@ var CompanionBridgeCommand = class extends Command65 {
|
|
|
36426
35829
|
};
|
|
36427
35830
|
|
|
36428
35831
|
// src/commands/companion/status.ts
|
|
36429
|
-
import { Command as
|
|
35832
|
+
import { Command as Command67 } from "clipanion";
|
|
36430
35833
|
async function withTimeout(work, ms) {
|
|
36431
35834
|
let timer;
|
|
36432
35835
|
try {
|
|
@@ -36498,7 +35901,7 @@ Run: m8t companion install
|
|
|
36498
35901
|
}
|
|
36499
35902
|
var CompanionStatusCommand = class extends M8tCommand {
|
|
36500
35903
|
static paths = [["companion", "status"]];
|
|
36501
|
-
static usage =
|
|
35904
|
+
static usage = Command67.Usage({
|
|
36502
35905
|
description: "Verify the installed desktop companion without launching it."
|
|
36503
35906
|
});
|
|
36504
35907
|
async executeCommand() {
|
|
@@ -36512,7 +35915,7 @@ var CompanionStatusCommand = class extends M8tCommand {
|
|
|
36512
35915
|
};
|
|
36513
35916
|
|
|
36514
35917
|
// src/commands/companion/repair.ts
|
|
36515
|
-
import { Command as
|
|
35918
|
+
import { Command as Command68, Option as Option64 } from "clipanion";
|
|
36516
35919
|
async function runCompanionRepairCommand(stdout, repair) {
|
|
36517
35920
|
const state = await repair();
|
|
36518
35921
|
if (state.state === "not-released") {
|
|
@@ -36531,10 +35934,10 @@ async function runCompanionRepairCommand(stdout, repair) {
|
|
|
36531
35934
|
}
|
|
36532
35935
|
var CompanionRepairCommand = class extends M8tCommand {
|
|
36533
35936
|
static paths = [["companion", "repair"]];
|
|
36534
|
-
static usage =
|
|
35937
|
+
static usage = Command68.Usage({
|
|
36535
35938
|
description: "Restore the desktop companions and start-at-login state."
|
|
36536
35939
|
});
|
|
36537
|
-
resourceGroup =
|
|
35940
|
+
resourceGroup = Option64.String("--resource-group", {
|
|
36538
35941
|
description: "Which deployment to bind to, when the subscription holds more than one."
|
|
36539
35942
|
});
|
|
36540
35943
|
async executeCommand() {
|
|
@@ -36548,7 +35951,7 @@ var CompanionRepairCommand = class extends M8tCommand {
|
|
|
36548
35951
|
};
|
|
36549
35952
|
|
|
36550
35953
|
// src/commands/companion/uninstall.ts
|
|
36551
|
-
import { Command as
|
|
35954
|
+
import { Command as Command69 } from "clipanion";
|
|
36552
35955
|
async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
36553
35956
|
const state = await uninstall();
|
|
36554
35957
|
if (state.state !== "not-installed") {
|
|
@@ -36560,7 +35963,7 @@ async function runCompanionUninstallCommand(stdout, uninstall) {
|
|
|
36560
35963
|
}
|
|
36561
35964
|
var CompanionUninstallCommand = class extends M8tCommand {
|
|
36562
35965
|
static paths = [["companion", "uninstall"]];
|
|
36563
|
-
static usage =
|
|
35966
|
+
static usage = Command69.Usage({
|
|
36564
35967
|
description: "Remove only this user's desktop companion installation."
|
|
36565
35968
|
});
|
|
36566
35969
|
async executeCommand() {
|
|
@@ -36639,6 +36042,7 @@ cli.register(BootstrapLaunchCommand);
|
|
|
36639
36042
|
cli.register(BootstrapStatusCommand);
|
|
36640
36043
|
cli.register(BootstrapReapCommand);
|
|
36641
36044
|
cli.register(BootstrapUiCommand);
|
|
36045
|
+
cli.register(BootstrapProfileCommand);
|
|
36642
36046
|
cli.register(BootstrapSeedProfileCommand);
|
|
36643
36047
|
cli.register(TelemetryEnrollCommand);
|
|
36644
36048
|
cli.register(CompanionBridgeCommand);
|