@tablation/crew 0.31.0 → 0.33.0
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 +171 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { resolve as resolve4, dirname as dirname12, join as join21, basename as basename9 } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { hostname as hostname2, homedir as homedir6 } from "os";
|
|
7
|
-
import { spawnSync as
|
|
7
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
10
10
|
import { readFileSync as readFileSync5, existsSync as existsSync4 } from "fs";
|
|
@@ -159,6 +159,7 @@ var eventFileFor = (stateDir) => join(stateDir, "events.jsonl");
|
|
|
159
159
|
|
|
160
160
|
// src/pair-install.ts
|
|
161
161
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync } from "fs";
|
|
162
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
162
163
|
import { homedir as homedir2 } from "os";
|
|
163
164
|
import { dirname as dirname2, join as join3 } from "path";
|
|
164
165
|
|
|
@@ -385,7 +386,7 @@ var claudeAdapter = {
|
|
|
385
386
|
})] : [],
|
|
386
387
|
...spec.args
|
|
387
388
|
];
|
|
388
|
-
return spec.launcher ? { bin: spec.launcher, args: [spec.bin, ...args] } : { bin: spec.bin, args };
|
|
389
|
+
return spec.launcher ? { bin: spec.launcherBin ?? spec.launcher, args: [spec.bin, ...args] } : { bin: spec.bin, args };
|
|
389
390
|
},
|
|
390
391
|
runEnv(spec) {
|
|
391
392
|
return {
|
|
@@ -668,10 +669,144 @@ var geminiPair = {
|
|
|
668
669
|
return hadBlock || hadMcp ? `pair(gemini): ${o.dryRun ? "(dry run) would remove" : "removed"} ${what}${kept}` : `pair(gemini): nothing to remove${kept}`;
|
|
669
670
|
}
|
|
670
671
|
};
|
|
672
|
+
var VSCODE_FLAVOURS = ["Code", "Code - Insiders"];
|
|
673
|
+
function vscodeUserDirs(o) {
|
|
674
|
+
const home = o.home ?? homedir2();
|
|
675
|
+
const platform2 = o.platform ?? process.platform;
|
|
676
|
+
const root = platform2 === "darwin" ? join3(home, "Library", "Application Support") : platform2 === "win32" ? process.env.APPDATA ?? join3(home, "AppData", "Roaming") : join3(home, ".config");
|
|
677
|
+
const all = VSCODE_FLAVOURS.map((f) => join3(root, f, "User"));
|
|
678
|
+
const present = all.filter((d) => existsSync2(d));
|
|
679
|
+
return present.length > 0 ? present : [all[0]];
|
|
680
|
+
}
|
|
681
|
+
var COPILOT_FRONTMATTER = '---\napplyTo: "**"\n---\n\n';
|
|
682
|
+
function readVscodeMcp(path) {
|
|
683
|
+
const text = readIfExists(path);
|
|
684
|
+
if (text === void 0 || text.trim() === "") return {};
|
|
685
|
+
try {
|
|
686
|
+
const j = JSON.parse(text);
|
|
687
|
+
return j && typeof j === "object" && !Array.isArray(j) ? j : void 0;
|
|
688
|
+
} catch {
|
|
689
|
+
return void 0;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
var COPILOT_AUTH = `Bearer \${env:${PAIR_KEY_VAR}}`;
|
|
693
|
+
var copilotEntry = (url) => ({ type: "http", url, headers: { Authorization: COPILOT_AUTH } });
|
|
694
|
+
function copilotMcpState(s, url) {
|
|
695
|
+
const e = s.servers?.[SERVER];
|
|
696
|
+
if (!e) return { state: "missing" };
|
|
697
|
+
if (e.url !== url) return { state: "different", detail: `it points at ${e.url ?? "(no url)"}, not ${url}` };
|
|
698
|
+
if (e.headers?.Authorization !== COPILOT_AUTH) return { state: "different", detail: `it does not read its key from \${env:${PAIR_KEY_VAR}}` };
|
|
699
|
+
return { state: "registered" };
|
|
700
|
+
}
|
|
701
|
+
function installCopilotInstructions(path, dryRun) {
|
|
702
|
+
const before = readIfExists(path);
|
|
703
|
+
const base = before ?? COPILOT_FRONTMATTER;
|
|
704
|
+
if (pairBlockIsBroken(base)) return { changed: false, error: `${path} has an unmatched ${PAIR_BLOCK_START}/${PAIR_BLOCK_END} marker \u2014 fix it by hand` };
|
|
705
|
+
const after = upsertPairBlock(base, pairBlock("copilot"));
|
|
706
|
+
if (after === before) return { changed: false };
|
|
707
|
+
if (!dryRun) writeFileMkdir(path, after);
|
|
708
|
+
return { changed: true };
|
|
709
|
+
}
|
|
710
|
+
function removeCopilotInstructions(path, dryRun) {
|
|
711
|
+
const before = readIfExists(path);
|
|
712
|
+
if (before === void 0 || !hasPairBlock(before)) return false;
|
|
713
|
+
if (dryRun) return true;
|
|
714
|
+
const after = removePairBlock(before);
|
|
715
|
+
if (after.trim() === "" || after.trim() === COPILOT_FRONTMATTER.trim()) rmSync2(path);
|
|
716
|
+
else writeFileSync(path, after);
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
var copilotPair = {
|
|
720
|
+
harness: "copilot",
|
|
721
|
+
displayName: "GitHub Copilot (VS Code)",
|
|
722
|
+
installPair(o) {
|
|
723
|
+
const lines = [];
|
|
724
|
+
for (const dir of vscodeUserDirs(o)) {
|
|
725
|
+
const mcpPath = join3(dir, "mcp.json");
|
|
726
|
+
const instrPath = join3(dir, "prompts", "crew-pair.instructions.md");
|
|
727
|
+
const settings = readVscodeMcp(mcpPath);
|
|
728
|
+
if (!settings) {
|
|
729
|
+
lines.push(`pair(copilot): ${mcpPath} is not a plain JSON object (comments are not supported) \u2014 fix it by hand; nothing was written there`);
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const st = copilotMcpState(settings, o.url);
|
|
733
|
+
if (st.state === "different" && !o.replace) {
|
|
734
|
+
lines.push(`pair(copilot): left ${mcpPath} alone \u2014 a "${SERVER}" server exists and ${st.detail}. Re-run with --mcp replace to overwrite it.`);
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
const instr = installCopilotInstructions(instrPath, o.dryRun);
|
|
738
|
+
if (instr.error) {
|
|
739
|
+
lines.push(`pair(copilot): ${instr.error}`);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
const mcpChanged = st.state !== "registered";
|
|
743
|
+
if (mcpChanged && !o.dryRun) {
|
|
744
|
+
const next = { ...settings, servers: { ...settings.servers, [SERVER]: copilotEntry(o.url) } };
|
|
745
|
+
writeFileMkdir(mcpPath, `${JSON.stringify(next, null, 2)}
|
|
746
|
+
`);
|
|
747
|
+
}
|
|
748
|
+
lines.push(!instr.changed && !mcpChanged ? `pair(copilot): already installed (${instrPath}, ${mcpPath})` : `pair(copilot): ${o.dryRun ? "(dry run) would install" : "installed"} the Pair instructions in ${instrPath} and ${SERVER} in ${mcpPath} (key read from \${env:${PAIR_KEY_VAR}})`);
|
|
749
|
+
}
|
|
750
|
+
return lines.join("\n");
|
|
751
|
+
},
|
|
752
|
+
verifyPair(o) {
|
|
753
|
+
const verdicts = vscodeUserDirs(o).map((dir) => {
|
|
754
|
+
const instrPath = join3(dir, "prompts", "crew-pair.instructions.md");
|
|
755
|
+
const mcpPath = join3(dir, "mcp.json");
|
|
756
|
+
const b = blockVerdict(instrPath, "copilot");
|
|
757
|
+
if (!b.ok) return b;
|
|
758
|
+
const settings = readVscodeMcp(mcpPath);
|
|
759
|
+
if (!settings) return { ok: false, detail: `${mcpPath} is not a plain JSON object` };
|
|
760
|
+
const st = copilotMcpState(settings, o.url);
|
|
761
|
+
if (st.state === "missing") return { ok: false, detail: `no ${SERVER} server in ${mcpPath}` };
|
|
762
|
+
if (st.state === "different") return { ok: false, detail: `${SERVER} in ${mcpPath} ${st.detail}` };
|
|
763
|
+
return { ok: true, detail: `${instrPath}, ${mcpPath}` };
|
|
764
|
+
});
|
|
765
|
+
return verdicts.find((v) => !v.ok) ?? { ok: true, detail: verdicts.map((v) => v.detail).join("; ") };
|
|
766
|
+
},
|
|
767
|
+
removePair(o) {
|
|
768
|
+
const lines = [];
|
|
769
|
+
for (const dir of vscodeUserDirs(o)) {
|
|
770
|
+
const mcpPath = join3(dir, "mcp.json");
|
|
771
|
+
const hadBlock = removeCopilotInstructions(join3(dir, "prompts", "crew-pair.instructions.md"), o.dryRun);
|
|
772
|
+
const settings = readVscodeMcp(mcpPath);
|
|
773
|
+
const st = settings ? copilotMcpState(settings, o.url) : { state: "missing" };
|
|
774
|
+
const hadMcp = st.state === "registered";
|
|
775
|
+
const kept = st.state === "different" ? `; left the existing ${SERVER} server alone \u2014 ${st.detail}` : "";
|
|
776
|
+
if (hadMcp && settings && !o.dryRun) {
|
|
777
|
+
const { [SERVER]: _gone, ...rest } = settings.servers ?? {};
|
|
778
|
+
const next = { ...settings };
|
|
779
|
+
if (Object.keys(rest).length > 0) next.servers = rest;
|
|
780
|
+
else delete next.servers;
|
|
781
|
+
if (Object.keys(next).length === 0) rmSync2(mcpPath);
|
|
782
|
+
else writeFileSync(mcpPath, `${JSON.stringify(next, null, 2)}
|
|
783
|
+
`);
|
|
784
|
+
}
|
|
785
|
+
const what = hadBlock && hadMcp ? `the Pair instructions and ${SERVER} server` : hadBlock ? "the Pair instructions" : `the ${SERVER} server`;
|
|
786
|
+
lines.push(hadBlock || hadMcp ? `pair(copilot): ${o.dryRun ? "(dry run) would remove" : "removed"} ${what} (${dir})${kept}` : `pair(copilot): nothing to remove in ${dir}${kept}`);
|
|
787
|
+
}
|
|
788
|
+
return lines.join("\n");
|
|
789
|
+
},
|
|
790
|
+
/**
|
|
791
|
+
* VS Code launched from the Dock or Spotlight does not inherit a shell's
|
|
792
|
+
* exports, so `${env:CREW_API_KEY}` expands to nothing there. macOS only: the
|
|
793
|
+
* login environment is `launchctl`'s, and it is the one place we can ask.
|
|
794
|
+
*/
|
|
795
|
+
envWarning(o) {
|
|
796
|
+
if ((o.platform ?? process.platform) !== "darwin") return void 0;
|
|
797
|
+
const get2 = o.loginEnv ?? ((name) => {
|
|
798
|
+
const r = spawnSync2("launchctl", ["getenv", name], { encoding: "utf8" });
|
|
799
|
+
return r.status === 0 ? r.stdout.trim() : void 0;
|
|
800
|
+
});
|
|
801
|
+
if (get2(PAIR_KEY_VAR)) return void 0;
|
|
802
|
+
return `$${PAIR_KEY_VAR} is not in the login environment, so VS Code started from the Dock will not send the tracker key. Launch VS Code from a terminal (\`code .\`), or run \`launchctl setenv ${PAIR_KEY_VAR} <key>\` once per login.`;
|
|
803
|
+
}
|
|
804
|
+
};
|
|
671
805
|
var PAIR_INSTALLERS = {
|
|
672
806
|
[claudePair.harness]: claudePair,
|
|
673
807
|
[codexPair.harness]: codexPair,
|
|
674
|
-
[geminiPair.harness]: geminiPair
|
|
808
|
+
[geminiPair.harness]: geminiPair,
|
|
809
|
+
[copilotPair.harness]: copilotPair
|
|
675
810
|
};
|
|
676
811
|
var PAIR_HARNESS_NAMES = Object.keys(PAIR_INSTALLERS);
|
|
677
812
|
var DEFAULT_PAIR = ["claude"];
|
|
@@ -730,9 +865,15 @@ function resolveToolchain(ship, route) {
|
|
|
730
865
|
return found;
|
|
731
866
|
}
|
|
732
867
|
function toolchainVendor(spec) {
|
|
733
|
-
if (
|
|
868
|
+
if (isOriLauncher(spec)) return "openrouter";
|
|
734
869
|
return adapterFor(spec.harness).vendor;
|
|
735
870
|
}
|
|
871
|
+
function isOriLauncher(spec) {
|
|
872
|
+
return !!spec.launcher && basename(spec.launcher) === "ori";
|
|
873
|
+
}
|
|
874
|
+
function toolchainProvider(spec) {
|
|
875
|
+
return isOriLauncher(spec) ? "openrouter" : adapterFor(spec.harness).provider(spec);
|
|
876
|
+
}
|
|
736
877
|
function resolveSeat(ship, route, role, persona = {}) {
|
|
737
878
|
const warnings = [];
|
|
738
879
|
const base = resolveToolchain(ship, route);
|
|
@@ -1774,7 +1915,7 @@ Create the first of those:
|
|
|
1774
1915
|
function expandCommand(cmd, base) {
|
|
1775
1916
|
return cmd.includes("/") || cmd.startsWith("~") ? expand(cmd, base) : cmd;
|
|
1776
1917
|
}
|
|
1777
|
-
var TOOLCHAIN_KEYS = /* @__PURE__ */ new Set(["harness", "bin", "launcher", "model", "tiers", "args", "env", "maxThinkingTokens"]);
|
|
1918
|
+
var TOOLCHAIN_KEYS = /* @__PURE__ */ new Set(["harness", "bin", "launcher", "launcherBin", "model", "tiers", "args", "env", "maxThinkingTokens"]);
|
|
1778
1919
|
function parsePair(raw, missing) {
|
|
1779
1920
|
if (raw === void 0) return void 0;
|
|
1780
1921
|
if (!Array.isArray(raw)) {
|
|
@@ -1836,6 +1977,7 @@ function parseToolchains(raw, base, missing, file) {
|
|
|
1836
1977
|
harness,
|
|
1837
1978
|
bin: expandCommand(String(entryRaw.bin ?? adapter.defaultBin), base),
|
|
1838
1979
|
...entryRaw.launcher ? { launcher: expandCommand(String(entryRaw.launcher), base) } : {},
|
|
1980
|
+
...entryRaw.launcher && entryRaw.launcherBin ? { launcherBin: expandCommand(String(entryRaw.launcherBin), base) } : {},
|
|
1839
1981
|
model: String(entryRaw.model ?? tiers.standard?.model ?? adapter.defaultModel),
|
|
1840
1982
|
tiers,
|
|
1841
1983
|
args: Array.isArray(args) ? args.map(String) : [],
|
|
@@ -2901,7 +3043,7 @@ function describeRelease(o, route) {
|
|
|
2901
3043
|
import { dirname as dirname4, join as join7, extname } from "path";
|
|
2902
3044
|
import { homedir as homedir4 } from "os";
|
|
2903
3045
|
import { accessSync, constants, existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync3, unlinkSync, realpathSync } from "fs";
|
|
2904
|
-
import { spawnSync as
|
|
3046
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
2905
3047
|
import { createHash } from "crypto";
|
|
2906
3048
|
|
|
2907
3049
|
// src/runtime-info.ts
|
|
@@ -3192,7 +3334,7 @@ function planDaemonControl(crewHome, hostShip, hasSystemd) {
|
|
|
3192
3334
|
}
|
|
3193
3335
|
function runAll(commands, cwd, log) {
|
|
3194
3336
|
for (const [bin, ...args] of commands) {
|
|
3195
|
-
const res =
|
|
3337
|
+
const res = spawnSync3(bin, args, { cwd, encoding: "utf8" });
|
|
3196
3338
|
if (res.error || res.status !== 0) {
|
|
3197
3339
|
log.warn(`${bin} ${args.join(" ")} exited ${res.status ?? "error"}`, {
|
|
3198
3340
|
output: `${res.stdout ?? ""}${res.stderr ?? ""}${res.error ? String(res.error) : ""}`
|
|
@@ -3257,7 +3399,7 @@ async function applyUninstall(plan, crewHome, dryRun, log) {
|
|
|
3257
3399
|
if (dryRun) {
|
|
3258
3400
|
log.emit(`would run: launchctl remove ${plan.removeLabel}`);
|
|
3259
3401
|
} else {
|
|
3260
|
-
if (
|
|
3402
|
+
if (spawnSync3("launchctl", ["remove", plan.removeLabel], { encoding: "utf8" }).status === 0) {
|
|
3261
3403
|
log.emit(`removed loaded job ${plan.removeLabel}`);
|
|
3262
3404
|
}
|
|
3263
3405
|
}
|
|
@@ -3294,7 +3436,7 @@ function programFromPlist(text) {
|
|
|
3294
3436
|
return args.find((a) => a.endsWith("/bin/crew")) ?? args[0] ?? null;
|
|
3295
3437
|
}
|
|
3296
3438
|
function loadedCrewLabels() {
|
|
3297
|
-
const list =
|
|
3439
|
+
const list = spawnSync3("launchctl", ["list"], { encoding: "utf8" });
|
|
3298
3440
|
if (list.status !== 0) return [];
|
|
3299
3441
|
return [...parseLaunchctlList(list.stdout ?? "").keys()].filter((l) => l.startsWith(CREW_LAUNCHD_PREFIX));
|
|
3300
3442
|
}
|
|
@@ -3330,8 +3472,8 @@ function removeLaunchdUnit(unit, dryRun, log) {
|
|
|
3330
3472
|
log.emit(`would unload and remove ${unit.label}`);
|
|
3331
3473
|
return;
|
|
3332
3474
|
}
|
|
3333
|
-
if (existsSync6(unit.plistPath))
|
|
3334
|
-
|
|
3475
|
+
if (existsSync6(unit.plistPath)) spawnSync3("launchctl", ["unload", unit.plistPath], { encoding: "utf8" });
|
|
3476
|
+
spawnSync3("launchctl", ["remove", unit.label], { encoding: "utf8" });
|
|
3335
3477
|
if (existsSync6(unit.plistPath)) unlinkSync(unit.plistPath);
|
|
3336
3478
|
log.emit(`removed ${unit.label} (${unit.plistPath})`);
|
|
3337
3479
|
}
|
|
@@ -3364,7 +3506,7 @@ function parseLaunchctlList(text) {
|
|
|
3364
3506
|
return jobs;
|
|
3365
3507
|
}
|
|
3366
3508
|
function describeForeignLaunchdUnits(crewHome, dir) {
|
|
3367
|
-
const list =
|
|
3509
|
+
const list = spawnSync3("launchctl", ["list"], { encoding: "utf8" });
|
|
3368
3510
|
const jobs = parseLaunchctlList(list.status === 0 ? list.stdout ?? "" : "");
|
|
3369
3511
|
const foreign = foreignLaunchdUnits(crewHome, dir, jobs.keys());
|
|
3370
3512
|
return foreign.map((u) => {
|
|
@@ -3377,11 +3519,11 @@ function describeForeignLaunchdUnits(crewHome, dir) {
|
|
|
3377
3519
|
});
|
|
3378
3520
|
}
|
|
3379
3521
|
function readCrontab() {
|
|
3380
|
-
const res =
|
|
3522
|
+
const res = spawnSync3("crontab", ["-l"], { encoding: "utf8" });
|
|
3381
3523
|
return res.status === 0 ? res.stdout ?? "" : "";
|
|
3382
3524
|
}
|
|
3383
3525
|
function writeCrontab(text) {
|
|
3384
|
-
|
|
3526
|
+
spawnSync3("crontab", ["-"], { input: text, encoding: "utf8" });
|
|
3385
3527
|
}
|
|
3386
3528
|
function addCrontabLine(marker, line) {
|
|
3387
3529
|
const existing = readCrontab();
|
|
@@ -3405,7 +3547,7 @@ function removeCrontabLine(marker) {
|
|
|
3405
3547
|
}
|
|
3406
3548
|
function detectSystemd() {
|
|
3407
3549
|
if (process.platform !== "linux") return false;
|
|
3408
|
-
const res =
|
|
3550
|
+
const res = spawnSync3("systemctl", ["--user", "status"], { encoding: "utf8" });
|
|
3409
3551
|
return !res.error;
|
|
3410
3552
|
}
|
|
3411
3553
|
|
|
@@ -6627,7 +6769,7 @@ function planAgentRun(o) {
|
|
|
6627
6769
|
args: invocation.args,
|
|
6628
6770
|
toolchain: toolchain.name,
|
|
6629
6771
|
harness: toolchain.harness,
|
|
6630
|
-
provider:
|
|
6772
|
+
provider: toolchainProvider(toolchain),
|
|
6631
6773
|
resolution: { toolchain: seat.toolchainWhy, model: seat.modelWhy },
|
|
6632
6774
|
warnings: seat.warnings,
|
|
6633
6775
|
prompt,
|
|
@@ -11727,7 +11869,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
11727
11869
|
`
|
|
11728
11870
|
);
|
|
11729
11871
|
if (!dryRun) {
|
|
11730
|
-
|
|
11872
|
+
spawnSync4("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
|
|
11731
11873
|
try {
|
|
11732
11874
|
unlinkSync5(staleTimer);
|
|
11733
11875
|
} catch {
|
|
@@ -11766,7 +11908,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
11766
11908
|
async function doctorFix(o) {
|
|
11767
11909
|
const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
|
|
11768
11910
|
const unitExists = (p) => existsSync21(p);
|
|
11769
|
-
const crontab = o.host === "linux" ?
|
|
11911
|
+
const crontab = o.host === "linux" ? spawnSync4("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
|
|
11770
11912
|
const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run"), unitExists, crontab) : true;
|
|
11771
11913
|
const plan = planFix({ routes, schedulerAbsent, host: o.host });
|
|
11772
11914
|
for (const n of plan.notes) process.stdout.write(`fix: ${n}
|
|
@@ -12638,7 +12780,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
12638
12780
|
}
|
|
12639
12781
|
let failed = false;
|
|
12640
12782
|
for (const cmd2 of commands) {
|
|
12641
|
-
const res2 =
|
|
12783
|
+
const res2 = spawnSync4(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
12642
12784
|
if (res2.stdout) process.stdout.write(res2.stdout);
|
|
12643
12785
|
if (res2.stderr) process.stderr.write(res2.stderr);
|
|
12644
12786
|
if (cmd2 === control.startCommand && (res2.error || res2.status !== 0)) {
|
|
@@ -12656,7 +12798,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
12656
12798
|
`);
|
|
12657
12799
|
break;
|
|
12658
12800
|
}
|
|
12659
|
-
const res =
|
|
12801
|
+
const res = spawnSync4(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
12660
12802
|
if (res.stdout) process.stdout.write(res.stdout);
|
|
12661
12803
|
if (res.stderr) process.stderr.write(res.stderr);
|
|
12662
12804
|
if (daemonSub !== "status" && (res.error || res.status !== 0)) {
|
|
@@ -14189,15 +14331,19 @@ crew manifest: ${seats2.length ? seats2.map((c) => c.name).join(", ") : "non
|
|
|
14189
14331
|
let allBinsFound = true;
|
|
14190
14332
|
for (const tc of shipToolchains(cfg.ship)) {
|
|
14191
14333
|
const adapter = adapterFor(tc.harness);
|
|
14192
|
-
const binPath = findOnPath(tc.launcher ?? tc.bin, schedulerPath);
|
|
14334
|
+
const binPath = findOnPath(tc.launcherBin ?? tc.launcher ?? tc.bin, schedulerPath);
|
|
14193
14335
|
if (!binPath) allBinsFound = false;
|
|
14194
14336
|
const label = tc.name === defaultToolchain ? "agent binary: " : `toolchain ${tc.name}:`.padEnd(18);
|
|
14195
14337
|
process.stdout.write(
|
|
14196
|
-
`${label} ${tc.launcher ?? tc.bin} \u2014 ${binPath ? `OK (${binPath})` : "NOT FOUND on the scheduler PATH"}${tc.name === defaultToolchain ? "" : ` [${adapter.displayName}]`}
|
|
14338
|
+
`${label} ${tc.launcherBin ?? tc.launcher ?? tc.bin} \u2014 ${binPath ? `OK (${binPath})` : "NOT FOUND on the scheduler PATH"}${tc.name === defaultToolchain ? "" : ` [${adapter.displayName}]`}
|
|
14197
14339
|
`
|
|
14198
14340
|
);
|
|
14199
14341
|
for (const limit of adapter.limitations) process.stdout.write(` cannot enforce: ${limit}
|
|
14200
14342
|
`);
|
|
14343
|
+
if (isOriLauncher(tc)) {
|
|
14344
|
+
process.stdout.write(` ori: headless auth under the scheduler is unverified \u2014 run \`ori login\` as the scheduler user, then confirm a run records provider openrouter (see docs/toolchains.md)
|
|
14345
|
+
`);
|
|
14346
|
+
}
|
|
14201
14347
|
}
|
|
14202
14348
|
process.stdout.write(`git: ${gitPath ? `OK (${gitPath})` : "NOT FOUND on the scheduler PATH"}
|
|
14203
14349
|
`);
|
|
@@ -14231,6 +14377,9 @@ crew manifest: ${seats2.length ? seats2.map((c) => c.name).join(", ") : "non
|
|
|
14231
14377
|
process.stdout.write(`pair ${h}:`.padEnd(19) + (v.ok ? ` installed (${v.detail})
|
|
14232
14378
|
` : ` NOT installed \u2014 ${v.detail}; run crew connect
|
|
14233
14379
|
`));
|
|
14380
|
+
const envWarn = inst.envWarning?.({ url: mcpUrlFor(route.baseUrl) });
|
|
14381
|
+
if (envWarn) process.stdout.write(`pair ${h} env:`.padEnd(19) + ` WARN \u2014 ${envWarn}
|
|
14382
|
+
`);
|
|
14234
14383
|
}
|
|
14235
14384
|
const shipVendors = new Set(shipToolchains(cfg.ship).map(toolchainVendor));
|
|
14236
14385
|
if (route.apiKey && route.resolved) {
|
package/package.json
CHANGED