claude-use 7.12.1 → 7.13.1
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.cjs +486 -426
- package/dist/index.cjs +1510 -143
- package/dist/index.mjs +1507 -143
- package/dist/types/cli/pairs.d.ts +19 -0
- package/dist/types/frontdoor/realFrontDoorPort.d.ts +5 -0
- package/dist/types/frontdoor/trust.d.ts +30 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/launchWiring.d.ts +49 -0
- package/dist/types/launcher/argv.d.ts +42 -0
- package/dist/types/launcher/cliOverride.d.ts +38 -0
- package/dist/types/launcher/extraFlags.d.ts +6 -0
- package/dist/types/launcher/farm.d.ts +3 -0
- package/dist/types/launcher/spawn.d.ts +20 -0
- package/dist/types/launcher.d.ts +98 -0
- package/dist/types/realCredentialCache.d.ts +11 -0
- package/dist/types/realPorts.d.ts +8 -1
- package/dist/types/resolve/pipeline.d.ts +3 -0
- package/dist/types/resolve/walkPolicy.d.ts +15 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1223,6 +1223,37 @@ function planFarm(params) {
|
|
|
1223
1223
|
return { entries, materialised, links };
|
|
1224
1224
|
}
|
|
1225
1225
|
|
|
1226
|
+
// src/resolve/walkPolicy.ts
|
|
1227
|
+
var ENTER_EVERYTHING = () => true;
|
|
1228
|
+
function readsSubtreeAggregates(rule) {
|
|
1229
|
+
const when = rule.when;
|
|
1230
|
+
return when !== void 0 && (when.newerThan !== void 0 || when.olderThan !== void 0 || when.maxSizeBytes !== void 0);
|
|
1231
|
+
}
|
|
1232
|
+
function hasUnboundedDepth(pattern) {
|
|
1233
|
+
return pattern.includes("**") || /[{(]/.test(pattern);
|
|
1234
|
+
}
|
|
1235
|
+
function couldMatchBeneath(rule, dirRel) {
|
|
1236
|
+
if (rule.matches(dirRel)) {
|
|
1237
|
+
return rule.when !== void 0;
|
|
1238
|
+
}
|
|
1239
|
+
if (rule.isExact) {
|
|
1240
|
+
return rule.canonicalPattern.startsWith(`${dirRel}/`);
|
|
1241
|
+
}
|
|
1242
|
+
const depth = dirRel.split("/").length;
|
|
1243
|
+
if (!hasUnboundedDepth(rule.canonicalPattern) && rule.segmentCount <= depth) {
|
|
1244
|
+
return false;
|
|
1245
|
+
}
|
|
1246
|
+
const prefix = rule.literalPrefix;
|
|
1247
|
+
return prefix === "" || prefix.startsWith(`${dirRel}/`) || dirRel.startsWith(prefix);
|
|
1248
|
+
}
|
|
1249
|
+
function descendPolicy(rules) {
|
|
1250
|
+
const all = [...rules];
|
|
1251
|
+
if (all.some(readsSubtreeAggregates)) {
|
|
1252
|
+
return ENTER_EVERYTHING;
|
|
1253
|
+
}
|
|
1254
|
+
return (dirRel) => all.some((rule) => couldMatchBeneath(rule, dirRel));
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1226
1257
|
// src/resolve/walk.ts
|
|
1227
1258
|
import path4 from "node:path";
|
|
1228
1259
|
|
|
@@ -1401,6 +1432,9 @@ function topLevelNames(facts) {
|
|
|
1401
1432
|
}
|
|
1402
1433
|
return [...names].sort();
|
|
1403
1434
|
}
|
|
1435
|
+
function descendPolicyFor(cascade, home) {
|
|
1436
|
+
return descendPolicy(flattenLayers(assembleCascade(cascade).layers, { home }).rules.values());
|
|
1437
|
+
}
|
|
1404
1438
|
function resolveDecisions(input) {
|
|
1405
1439
|
const assembled = assembleCascade(input.cascade);
|
|
1406
1440
|
const flattened = flattenLayers(assembled.layers, { home: input.facts.home });
|
|
@@ -1626,8 +1660,8 @@ var FarmManifestSchema = z2.strictObject({
|
|
|
1626
1660
|
materialised: z2.array(z2.string()),
|
|
1627
1661
|
links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
|
|
1628
1662
|
});
|
|
1629
|
-
function readFarmManifest(
|
|
1630
|
-
const raw =
|
|
1663
|
+
function readFarmManifest(fs12, farmRoot) {
|
|
1664
|
+
const raw = fs12.readFileUtf8(path6.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs12.readFileUtf8(path6.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1631
1665
|
if (raw === void 0) {
|
|
1632
1666
|
return void 0;
|
|
1633
1667
|
}
|
|
@@ -1651,7 +1685,7 @@ function buildEntryFacts(params) {
|
|
|
1651
1685
|
const isDirectory = stat.kind === "dir";
|
|
1652
1686
|
let latestMtimeMs = stat.mtimeMs;
|
|
1653
1687
|
let totalSizeBytes = stat.sizeBytes;
|
|
1654
|
-
if (isDirectory) {
|
|
1688
|
+
if (isDirectory && params.descend(rel)) {
|
|
1655
1689
|
for (const name of [...params.fs.readdir(absolute)].sort()) {
|
|
1656
1690
|
const child = visit(`${rel}/${name}`);
|
|
1657
1691
|
if (child !== void 0) {
|
|
@@ -1685,9 +1719,9 @@ function buildEntryFacts(params) {
|
|
|
1685
1719
|
entries
|
|
1686
1720
|
};
|
|
1687
1721
|
}
|
|
1688
|
-
function listSubtree(
|
|
1722
|
+
function listSubtree(fs12, root, rel) {
|
|
1689
1723
|
const absolute = path6.join(root, rel);
|
|
1690
|
-
const stat =
|
|
1724
|
+
const stat = fs12.lstat(absolute);
|
|
1691
1725
|
if (stat === void 0) {
|
|
1692
1726
|
return [];
|
|
1693
1727
|
}
|
|
@@ -1695,10 +1729,10 @@ function listSubtree(fs10, root, rel) {
|
|
|
1695
1729
|
return [{ rel, kind: "symlink" }];
|
|
1696
1730
|
}
|
|
1697
1731
|
if (stat.kind === "dir") {
|
|
1698
|
-
const children = [...
|
|
1732
|
+
const children = [...fs12.readdir(absolute)].sort().flatMap((name) => listSubtree(fs12, root, `${rel}/${name}`));
|
|
1699
1733
|
return [{ rel, kind: "dir" }, ...children];
|
|
1700
1734
|
}
|
|
1701
|
-
const contentHash =
|
|
1735
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1702
1736
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1703
1737
|
}
|
|
1704
1738
|
function topLevelScopeRoots(roots) {
|
|
@@ -1712,25 +1746,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1712
1746
|
}
|
|
1713
1747
|
return kept;
|
|
1714
1748
|
}
|
|
1715
|
-
function reconciliationScope(
|
|
1749
|
+
function reconciliationScope(fs12, farmRoot, manifest) {
|
|
1716
1750
|
if (manifest !== void 0) {
|
|
1717
1751
|
return topLevelScopeRoots(manifest.materialised);
|
|
1718
1752
|
}
|
|
1719
|
-
return [...
|
|
1753
|
+
return [...fs12.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs12.lstat(path6.join(farmRoot, name))?.kind === "dir").sort();
|
|
1720
1754
|
}
|
|
1721
|
-
function collectFarmListing(
|
|
1722
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1755
|
+
function collectFarmListing(fs12, farmRoot, scopeRoots) {
|
|
1756
|
+
return scopeRoots.flatMap((root) => listSubtree(fs12, farmRoot, root));
|
|
1723
1757
|
}
|
|
1724
|
-
function collectCanonicalCounterparts(
|
|
1758
|
+
function collectCanonicalCounterparts(fs12, claudeHome, farmListing) {
|
|
1725
1759
|
const out = [];
|
|
1726
1760
|
for (const entry of farmListing) {
|
|
1727
1761
|
const absolute = path6.join(claudeHome, entry.rel);
|
|
1728
|
-
const stat =
|
|
1762
|
+
const stat = fs12.lstat(absolute);
|
|
1729
1763
|
if (stat === void 0) {
|
|
1730
1764
|
continue;
|
|
1731
1765
|
}
|
|
1732
1766
|
if (stat.kind === "file") {
|
|
1733
|
-
const contentHash =
|
|
1767
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1734
1768
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1735
1769
|
continue;
|
|
1736
1770
|
}
|
|
@@ -1998,7 +2032,8 @@ function resyncFarm(params) {
|
|
|
1998
2032
|
nowMs,
|
|
1999
2033
|
env: params.env,
|
|
2000
2034
|
...params.branch === void 0 ? {} : { branch: params.branch },
|
|
2001
|
-
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached }
|
|
2035
|
+
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached },
|
|
2036
|
+
descend: descendPolicyFor(params.cascade, params.home)
|
|
2002
2037
|
});
|
|
2003
2038
|
const resolved = resolveDecisions({ facts, cascade: params.cascade, classification: params.classification });
|
|
2004
2039
|
const manifest = {
|
|
@@ -2414,8 +2449,12 @@ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
|
|
|
2414
2449
|
}
|
|
2415
2450
|
|
|
2416
2451
|
// src/frontdoor/capability.ts
|
|
2452
|
+
var CONNECT_PROXY_USER = "agent-shim";
|
|
2417
2453
|
var CONNECT_PROXY_REALM = "agent-shim front door";
|
|
2418
2454
|
var BASIC_SCHEME = "basic";
|
|
2455
|
+
function connectProxyUrl(port, token) {
|
|
2456
|
+
return `http://${encodeURIComponent(CONNECT_PROXY_USER)}:${encodeURIComponent(token)}@127.0.0.1:${String(port)}`;
|
|
2457
|
+
}
|
|
2419
2458
|
function capabilityFromProxyAuthorization(value) {
|
|
2420
2459
|
const trimmed = value.trim();
|
|
2421
2460
|
const space = trimmed.indexOf(" ");
|
|
@@ -2432,7 +2471,15 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2432
2471
|
}
|
|
2433
2472
|
|
|
2434
2473
|
// src/frontdoor/route.ts
|
|
2474
|
+
var IDENTITY_HEADER = "x-agent-shim-identity";
|
|
2475
|
+
var SESSION_HEADER = "x-agent-shim-session";
|
|
2476
|
+
var HEADROOM_FLAG_HEADER = "x-agent-shim-headroom";
|
|
2477
|
+
var PROJECT_ID_HEADER = "x-headroom-project-id";
|
|
2435
2478
|
var AUTH_HEADER = "x-agent-shim-auth";
|
|
2479
|
+
var PROVIDER_PATH_PREFIX = "/providers/";
|
|
2480
|
+
function providerBaseUrl(port, provider) {
|
|
2481
|
+
return `https://127.0.0.1:${String(port)}${PROVIDER_PATH_PREFIX}${encodeURIComponent(provider)}`;
|
|
2482
|
+
}
|
|
2436
2483
|
|
|
2437
2484
|
// src/frontdoor/state.ts
|
|
2438
2485
|
import path7 from "node:path";
|
|
@@ -2469,10 +2516,10 @@ var FrontDoorStateSchema = z3.strictObject({
|
|
|
2469
2516
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2470
2517
|
lastError: z3.string().optional()
|
|
2471
2518
|
});
|
|
2472
|
-
function readFrontDoorState(
|
|
2519
|
+
function readFrontDoorState(fs12, stateFile) {
|
|
2473
2520
|
let raw;
|
|
2474
2521
|
try {
|
|
2475
|
-
raw =
|
|
2522
|
+
raw = fs12.readFileUtf8(stateFile);
|
|
2476
2523
|
} catch {
|
|
2477
2524
|
return void 0;
|
|
2478
2525
|
}
|
|
@@ -2486,28 +2533,28 @@ function readFrontDoorState(fs10, stateFile) {
|
|
|
2486
2533
|
return void 0;
|
|
2487
2534
|
}
|
|
2488
2535
|
}
|
|
2489
|
-
function writeFrontDoorState(
|
|
2490
|
-
|
|
2491
|
-
|
|
2536
|
+
function writeFrontDoorState(fs12, stateFile, state) {
|
|
2537
|
+
fs12.mkdirp(path7.dirname(stateFile));
|
|
2538
|
+
fs12.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2492
2539
|
`);
|
|
2493
2540
|
}
|
|
2494
2541
|
function frontDoorOrigin(state) {
|
|
2495
2542
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2496
2543
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2497
2544
|
}
|
|
2498
|
-
function writeFrontDoorSession(
|
|
2499
|
-
|
|
2500
|
-
|
|
2545
|
+
function writeFrontDoorSession(fs12, sessionsDir, session) {
|
|
2546
|
+
fs12.mkdirPrivate(sessionsDir);
|
|
2547
|
+
fs12.writeFilePrivate(path7.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2501
2548
|
`);
|
|
2502
2549
|
}
|
|
2503
|
-
function listFrontDoorSessions(
|
|
2550
|
+
function listFrontDoorSessions(fs12, sessionsDir) {
|
|
2504
2551
|
const sessions = [];
|
|
2505
|
-
for (const name of
|
|
2552
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
2506
2553
|
if (!name.endsWith(".json")) {
|
|
2507
2554
|
continue;
|
|
2508
2555
|
}
|
|
2509
2556
|
try {
|
|
2510
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2557
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs12.readFileUtf8(path7.join(sessionsDir, name)) ?? ""));
|
|
2511
2558
|
if (parsed.success) {
|
|
2512
2559
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2513
2560
|
}
|
|
@@ -2517,14 +2564,14 @@ function listFrontDoorSessions(fs10, sessionsDir) {
|
|
|
2517
2564
|
}
|
|
2518
2565
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2519
2566
|
}
|
|
2520
|
-
function removeFrontDoorSession(
|
|
2521
|
-
|
|
2567
|
+
function removeFrontDoorSession(fs12, sessionsDir, pid) {
|
|
2568
|
+
fs12.removeRecursive(path7.join(sessionsDir, `${String(pid)}.json`));
|
|
2522
2569
|
}
|
|
2523
|
-
function pruneDeadFrontDoorSessions(
|
|
2570
|
+
function pruneDeadFrontDoorSessions(fs12, sessionsDir, isRunning) {
|
|
2524
2571
|
const removed = [];
|
|
2525
|
-
for (const session of listFrontDoorSessions(
|
|
2572
|
+
for (const session of listFrontDoorSessions(fs12, sessionsDir)) {
|
|
2526
2573
|
if (!isRunning(session.pid)) {
|
|
2527
|
-
removeFrontDoorSession(
|
|
2574
|
+
removeFrontDoorSession(fs12, sessionsDir, session.pid);
|
|
2528
2575
|
removed.push(session.pid);
|
|
2529
2576
|
}
|
|
2530
2577
|
}
|
|
@@ -2536,8 +2583,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2536
2583
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2537
2584
|
var MS_PER_MINUTE = 6e4;
|
|
2538
2585
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2539
|
-
const { fs:
|
|
2540
|
-
const previous = readFrontDoorState(
|
|
2586
|
+
const { fs: fs12, paths } = ports;
|
|
2587
|
+
const previous = readFrontDoorState(fs12, paths.frontdoorStateFile);
|
|
2541
2588
|
let lastPort = previous?.lastPort;
|
|
2542
2589
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2543
2590
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2547,12 +2594,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2547
2594
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2548
2595
|
});
|
|
2549
2596
|
const fail = (message) => {
|
|
2550
|
-
writeFrontDoorState(
|
|
2551
|
-
|
|
2597
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2598
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2552
2599
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2553
2600
|
return 1;
|
|
2554
2601
|
};
|
|
2555
|
-
writeFrontDoorState(
|
|
2602
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2556
2603
|
const previousSticky = lastPort;
|
|
2557
2604
|
let http2;
|
|
2558
2605
|
try {
|
|
@@ -2589,8 +2636,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2589
2636
|
ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
|
|
2590
2637
|
}
|
|
2591
2638
|
lastDirectPort = direct.port;
|
|
2592
|
-
writeFrontDoorState(
|
|
2593
|
-
|
|
2639
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
2640
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2594
2641
|
ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http2.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
|
|
2595
2642
|
let idleSince;
|
|
2596
2643
|
let ticks = 0;
|
|
@@ -2599,15 +2646,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2599
2646
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2600
2647
|
}
|
|
2601
2648
|
ticks += 1;
|
|
2602
|
-
pruneDeadFrontDoorSessions(
|
|
2603
|
-
if (listFrontDoorSessions(
|
|
2649
|
+
pruneDeadFrontDoorSessions(fs12, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2650
|
+
if (listFrontDoorSessions(fs12, paths.frontdoorSessionsDir).length === 0) {
|
|
2604
2651
|
idleSince ??= ports.now();
|
|
2605
2652
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2606
2653
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2607
2654
|
await direct.close();
|
|
2608
2655
|
await connect3.close();
|
|
2609
2656
|
await http2.close();
|
|
2610
|
-
writeFrontDoorState(
|
|
2657
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, sticky());
|
|
2611
2658
|
return 0;
|
|
2612
2659
|
}
|
|
2613
2660
|
} else {
|
|
@@ -2662,8 +2709,8 @@ function parseConnectTarget(authority) {
|
|
|
2662
2709
|
function isInterceptedHost(host, interceptHosts) {
|
|
2663
2710
|
return interceptHosts.includes(host);
|
|
2664
2711
|
}
|
|
2665
|
-
function servedByPipeline(
|
|
2666
|
-
return
|
|
2712
|
+
function servedByPipeline(path28) {
|
|
2713
|
+
return path28?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2667
2714
|
}
|
|
2668
2715
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2669
2716
|
"connection",
|
|
@@ -3197,16 +3244,16 @@ function parseJson(raw, schema) {
|
|
|
3197
3244
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
3198
3245
|
return parsed.success ? parsed.data : void 0;
|
|
3199
3246
|
}
|
|
3200
|
-
function readHeadroomState(
|
|
3247
|
+
function readHeadroomState(fs12, stateFile) {
|
|
3201
3248
|
try {
|
|
3202
|
-
return parseJson(
|
|
3249
|
+
return parseJson(fs12.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
3203
3250
|
} catch {
|
|
3204
3251
|
return void 0;
|
|
3205
3252
|
}
|
|
3206
3253
|
}
|
|
3207
|
-
function writeHeadroomState(
|
|
3208
|
-
|
|
3209
|
-
|
|
3254
|
+
function writeHeadroomState(fs12, stateFile, state) {
|
|
3255
|
+
fs12.mkdirp(path8.dirname(stateFile));
|
|
3256
|
+
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
3210
3257
|
`);
|
|
3211
3258
|
}
|
|
3212
3259
|
function headroomAllowlist(providers) {
|
|
@@ -3226,14 +3273,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3226
3273
|
function hashAllowlist(allowlist) {
|
|
3227
3274
|
return createHash2("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3228
3275
|
}
|
|
3229
|
-
function readAllProviders(
|
|
3276
|
+
function readAllProviders(fs12, providersDir) {
|
|
3230
3277
|
const result = [];
|
|
3231
|
-
for (const name of
|
|
3278
|
+
for (const name of fs12.readdir(providersDir)) {
|
|
3232
3279
|
if (!name.endsWith(".json")) {
|
|
3233
3280
|
continue;
|
|
3234
3281
|
}
|
|
3235
3282
|
try {
|
|
3236
|
-
const parsed = parseJson(
|
|
3283
|
+
const parsed = parseJson(fs12.readFileUtf8(path8.join(providersDir, name)), ProviderSchema);
|
|
3237
3284
|
if (parsed !== void 0) {
|
|
3238
3285
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
3239
3286
|
}
|
|
@@ -3243,22 +3290,22 @@ function readAllProviders(fs10, providersDir) {
|
|
|
3243
3290
|
}
|
|
3244
3291
|
return result;
|
|
3245
3292
|
}
|
|
3246
|
-
function writeSession(
|
|
3247
|
-
|
|
3248
|
-
|
|
3293
|
+
function writeSession(fs12, sessionsDir, session) {
|
|
3294
|
+
fs12.mkdirp(sessionsDir);
|
|
3295
|
+
fs12.writeFileUtf8(path8.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
3249
3296
|
`);
|
|
3250
3297
|
}
|
|
3251
|
-
function removeSession(
|
|
3252
|
-
|
|
3298
|
+
function removeSession(fs12, sessionsDir, pid) {
|
|
3299
|
+
fs12.removeRecursive(path8.join(sessionsDir, `${String(pid)}.json`));
|
|
3253
3300
|
}
|
|
3254
|
-
function listSessions(
|
|
3301
|
+
function listSessions(fs12, sessionsDir) {
|
|
3255
3302
|
const sessions = [];
|
|
3256
|
-
for (const name of
|
|
3303
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
3257
3304
|
if (!name.endsWith(".json")) {
|
|
3258
3305
|
continue;
|
|
3259
3306
|
}
|
|
3260
3307
|
try {
|
|
3261
|
-
const parsed = parseJson(
|
|
3308
|
+
const parsed = parseJson(fs12.readFileUtf8(path8.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
3262
3309
|
if (parsed !== void 0) {
|
|
3263
3310
|
sessions.push(parsed);
|
|
3264
3311
|
}
|
|
@@ -3268,22 +3315,22 @@ function listSessions(fs10, sessionsDir) {
|
|
|
3268
3315
|
}
|
|
3269
3316
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
3270
3317
|
}
|
|
3271
|
-
function sessionsForSupervisor(
|
|
3272
|
-
return listSessions(
|
|
3318
|
+
function sessionsForSupervisor(fs12, sessionsDir, supervisorPid) {
|
|
3319
|
+
return listSessions(fs12, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3273
3320
|
}
|
|
3274
|
-
function pruneDeadSessions(
|
|
3321
|
+
function pruneDeadSessions(fs12, sessionsDir, isRunning) {
|
|
3275
3322
|
const removed = [];
|
|
3276
|
-
for (const session of listSessions(
|
|
3323
|
+
for (const session of listSessions(fs12, sessionsDir)) {
|
|
3277
3324
|
if (!isRunning(session.pid)) {
|
|
3278
|
-
removeSession(
|
|
3325
|
+
removeSession(fs12, sessionsDir, session.pid);
|
|
3279
3326
|
removed.push(session.pid);
|
|
3280
3327
|
}
|
|
3281
3328
|
}
|
|
3282
3329
|
return removed;
|
|
3283
3330
|
}
|
|
3284
|
-
function readStartLock(
|
|
3331
|
+
function readStartLock(fs12, lockFile) {
|
|
3285
3332
|
try {
|
|
3286
|
-
return parseJson(
|
|
3333
|
+
return parseJson(fs12.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
3287
3334
|
} catch {
|
|
3288
3335
|
return void 0;
|
|
3289
3336
|
}
|
|
@@ -3534,12 +3581,12 @@ function isIdentityName(name) {
|
|
|
3534
3581
|
function claudeJsonPath(identitiesDir, identity) {
|
|
3535
3582
|
return path9.join(identitiesDir, identity, CLAUDE_JSON);
|
|
3536
3583
|
}
|
|
3537
|
-
function readAccountMetadata(
|
|
3584
|
+
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
3538
3585
|
if (!isIdentityName(identity)) {
|
|
3539
3586
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3540
3587
|
}
|
|
3541
3588
|
const file = claudeJsonPath(identitiesDir, identity);
|
|
3542
|
-
const raw =
|
|
3589
|
+
const raw = fs12.readFileUtf8(file);
|
|
3543
3590
|
if (raw === void 0) {
|
|
3544
3591
|
return void 0;
|
|
3545
3592
|
}
|
|
@@ -3573,8 +3620,8 @@ function readAccountMetadata(fs10, identitiesDir, identity) {
|
|
|
3573
3620
|
// src/usage/read.ts
|
|
3574
3621
|
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
3575
3622
|
var DAY_MS = 864e5;
|
|
3576
|
-
function listLogSegments(
|
|
3577
|
-
return
|
|
3623
|
+
function listLogSegments(fs12, logDir) {
|
|
3624
|
+
return fs12.readdir(logDir).flatMap((name) => {
|
|
3578
3625
|
const match = SEGMENT_RE.exec(name);
|
|
3579
3626
|
const day = match?.[1];
|
|
3580
3627
|
if (day === void 0) {
|
|
@@ -3583,15 +3630,15 @@ function listLogSegments(fs10, logDir) {
|
|
|
3583
3630
|
return [{ name, day, endsAt: Date.parse(`${day}T00:00:00.000Z`) + DAY_MS }];
|
|
3584
3631
|
}).sort((left, right) => left.day.localeCompare(right.day) || left.name.localeCompare(right.name));
|
|
3585
3632
|
}
|
|
3586
|
-
function readUsageLog(
|
|
3633
|
+
function readUsageLog(fs12, logDir, options = {}) {
|
|
3587
3634
|
const { sinceMs } = options;
|
|
3588
3635
|
const records = [];
|
|
3589
3636
|
let invalidLines = 0;
|
|
3590
|
-
for (const segment of listLogSegments(
|
|
3637
|
+
for (const segment of listLogSegments(fs12, logDir)) {
|
|
3591
3638
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
3592
3639
|
continue;
|
|
3593
3640
|
}
|
|
3594
|
-
const contents =
|
|
3641
|
+
const contents = fs12.readFileUtf8(path10.join(logDir, segment.name));
|
|
3595
3642
|
if (contents === void 0) {
|
|
3596
3643
|
continue;
|
|
3597
3644
|
}
|
|
@@ -3635,9 +3682,9 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3635
3682
|
}
|
|
3636
3683
|
return path10.join(snapshotsDir, `${identity}.json`);
|
|
3637
3684
|
}
|
|
3638
|
-
function readUsageSnapshot(
|
|
3685
|
+
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
3639
3686
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3640
|
-
const raw =
|
|
3687
|
+
const raw = fs12.readFileUtf8(file);
|
|
3641
3688
|
if (raw === void 0) {
|
|
3642
3689
|
return void 0;
|
|
3643
3690
|
}
|
|
@@ -3653,8 +3700,8 @@ function readUsageSnapshot(fs10, snapshotsDir, identity) {
|
|
|
3653
3700
|
}
|
|
3654
3701
|
return snapshot.data;
|
|
3655
3702
|
}
|
|
3656
|
-
function listUsageSnapshots(
|
|
3657
|
-
return
|
|
3703
|
+
function listUsageSnapshots(fs12, snapshotsDir) {
|
|
3704
|
+
return fs12.readdir(snapshotsDir).flatMap((name) => {
|
|
3658
3705
|
if (!name.endsWith(".json")) {
|
|
3659
3706
|
return [];
|
|
3660
3707
|
}
|
|
@@ -3662,7 +3709,7 @@ function listUsageSnapshots(fs10, snapshotsDir) {
|
|
|
3662
3709
|
if (!isIdentityName(identity)) {
|
|
3663
3710
|
return [];
|
|
3664
3711
|
}
|
|
3665
|
-
const snapshot = readUsageSnapshot(
|
|
3712
|
+
const snapshot = readUsageSnapshot(fs12, snapshotsDir, identity);
|
|
3666
3713
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3667
3714
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3668
3715
|
}
|
|
@@ -3837,9 +3884,9 @@ function compareVersions(a, b) {
|
|
|
3837
3884
|
}
|
|
3838
3885
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
3839
3886
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
3840
|
-
const { fs:
|
|
3887
|
+
const { fs: fs12, paths } = ports;
|
|
3841
3888
|
let config = initialConfig;
|
|
3842
|
-
const previousState = readHeadroomState(
|
|
3889
|
+
const previousState = readHeadroomState(fs12, paths.headroomStateFile);
|
|
3843
3890
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3844
3891
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3845
3892
|
return 0;
|
|
@@ -3848,19 +3895,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3848
3895
|
let lastPort = previousState?.lastPort;
|
|
3849
3896
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3850
3897
|
const ownsState = () => {
|
|
3851
|
-
const owner = readHeadroomState(
|
|
3898
|
+
const owner = readHeadroomState(fs12, paths.headroomStateFile)?.supervisorPid;
|
|
3852
3899
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3853
3900
|
};
|
|
3854
3901
|
const writeOwnState = (state) => {
|
|
3855
3902
|
if (!ownsState()) {
|
|
3856
3903
|
return false;
|
|
3857
3904
|
}
|
|
3858
|
-
writeHeadroomState(
|
|
3905
|
+
writeHeadroomState(fs12, paths.headroomStateFile, state);
|
|
3859
3906
|
return true;
|
|
3860
3907
|
};
|
|
3861
3908
|
const fail = (message) => {
|
|
3862
3909
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3863
|
-
|
|
3910
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
3864
3911
|
}
|
|
3865
3912
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
3866
3913
|
return 1;
|
|
@@ -3909,7 +3956,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3909
3956
|
const settingsHash = hashSettings(config.settings);
|
|
3910
3957
|
const allowlist = allowlistOf(ports);
|
|
3911
3958
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3912
|
-
pruneDeadSessions(
|
|
3959
|
+
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
3913
3960
|
const ownsNow = ownsState();
|
|
3914
3961
|
if (!ownsNow && !superseded) {
|
|
3915
3962
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -3961,7 +4008,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3961
4008
|
...sticky()
|
|
3962
4009
|
});
|
|
3963
4010
|
if (recorded) {
|
|
3964
|
-
|
|
4011
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
3965
4012
|
}
|
|
3966
4013
|
idleSince = void 0;
|
|
3967
4014
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -3976,7 +4023,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3976
4023
|
}
|
|
3977
4024
|
} else {
|
|
3978
4025
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3979
|
-
if (sessionsForSupervisor(
|
|
4026
|
+
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3980
4027
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3981
4028
|
ports.stopProcess(headroomPid);
|
|
3982
4029
|
headroomPid = void 0;
|
|
@@ -3985,7 +4032,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3985
4032
|
continue;
|
|
3986
4033
|
}
|
|
3987
4034
|
}
|
|
3988
|
-
const sessions = sessionsForSupervisor(
|
|
4035
|
+
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
3989
4036
|
if (sessions.length === 0) {
|
|
3990
4037
|
idleSince ??= ports.now();
|
|
3991
4038
|
const idleMs = ports.now() - idleSince;
|
|
@@ -4007,9 +4054,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4007
4054
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
4008
4055
|
}
|
|
4009
4056
|
}
|
|
4010
|
-
function currentHeadroomAllowlist(
|
|
4011
|
-
const providers = readAllProviders(
|
|
4012
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
4057
|
+
function currentHeadroomAllowlist(fs12, paths) {
|
|
4058
|
+
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
4059
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
4013
4060
|
}
|
|
4014
4061
|
function allowlistOf(ports) {
|
|
4015
4062
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -4186,8 +4233,8 @@ function decideConfigProfile(params) {
|
|
|
4186
4233
|
}
|
|
4187
4234
|
return { source: "none" };
|
|
4188
4235
|
}
|
|
4189
|
-
function loadIdentity(identitiesDir, name,
|
|
4190
|
-
return loadConfigFile(path12.join(identitiesDir, name, "identity.json"), IdentitySchema,
|
|
4236
|
+
function loadIdentity(identitiesDir, name, fs12) {
|
|
4237
|
+
return loadConfigFile(path12.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
4191
4238
|
}
|
|
4192
4239
|
|
|
4193
4240
|
// src/configProfilesStore.ts
|
|
@@ -4529,13 +4576,59 @@ import path15 from "node:path";
|
|
|
4529
4576
|
|
|
4530
4577
|
// src/credentialCache.ts
|
|
4531
4578
|
import { z as z7 } from "zod";
|
|
4579
|
+
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
4532
4580
|
var CachedCredentialSchema = z7.strictObject({
|
|
4533
4581
|
token: z7.string().min(1),
|
|
4534
4582
|
fetchedAt: z7.number().int().nonnegative(),
|
|
4535
4583
|
source: CredentialSourceSchema
|
|
4536
4584
|
});
|
|
4585
|
+
function effectiveStore(cache, platform) {
|
|
4586
|
+
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
4587
|
+
}
|
|
4588
|
+
function ttlMs(cache) {
|
|
4589
|
+
if (cache.ttl === void 0) {
|
|
4590
|
+
return void 0;
|
|
4591
|
+
}
|
|
4592
|
+
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
4593
|
+
if (multiplier === void 0) {
|
|
4594
|
+
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
4595
|
+
}
|
|
4596
|
+
return Number(cache.ttl.slice(0, -1)) * multiplier;
|
|
4597
|
+
}
|
|
4598
|
+
function isFresh(entry, cache, now) {
|
|
4599
|
+
const limit = ttlMs(cache);
|
|
4600
|
+
if (entry.fetchedAt > now) {
|
|
4601
|
+
return false;
|
|
4602
|
+
}
|
|
4603
|
+
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
4604
|
+
}
|
|
4537
4605
|
|
|
4538
4606
|
// src/credential.ts
|
|
4607
|
+
var CREDENTIAL_UNAVAILABLE_EXIT = 64;
|
|
4608
|
+
var CREDENTIAL_COMMAND_TIMEOUT_MS = 1e4;
|
|
4609
|
+
var CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 12e4;
|
|
4610
|
+
var GROUP_OTHER_PERMISSION_BITS = 63;
|
|
4611
|
+
var PERMISSION_BITS = 511;
|
|
4612
|
+
var OCTAL_RADIX = 8;
|
|
4613
|
+
function commandArgv(source) {
|
|
4614
|
+
if ("command" in source) {
|
|
4615
|
+
return source.command;
|
|
4616
|
+
}
|
|
4617
|
+
if ("op" in source) {
|
|
4618
|
+
return ["op", "read", source.op];
|
|
4619
|
+
}
|
|
4620
|
+
const account = source.keychain.account;
|
|
4621
|
+
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
4622
|
+
}
|
|
4623
|
+
function isInteractiveSource(source, env) {
|
|
4624
|
+
if ("command" in source || "keychain" in source) {
|
|
4625
|
+
return source.interactive ?? false;
|
|
4626
|
+
}
|
|
4627
|
+
if ("op" in source) {
|
|
4628
|
+
return source.interactive ?? (env.OP_SERVICE_ACCOUNT_TOKEN ?? "") === "";
|
|
4629
|
+
}
|
|
4630
|
+
return false;
|
|
4631
|
+
}
|
|
4539
4632
|
function summariseSource(source) {
|
|
4540
4633
|
if ("env" in source) {
|
|
4541
4634
|
return { kind: "env", variable: source.env };
|
|
@@ -4583,6 +4676,9 @@ function formatSourceSummary(summary) {
|
|
|
4583
4676
|
return summary;
|
|
4584
4677
|
}
|
|
4585
4678
|
}
|
|
4679
|
+
function describeSource(source) {
|
|
4680
|
+
return formatSourceSummary(summariseSource(source));
|
|
4681
|
+
}
|
|
4586
4682
|
function formatCredentialSummary(summary) {
|
|
4587
4683
|
const cache = summary.cache === void 0 ? "" : `, cached ${summary.cache.ttl === void 0 ? "with no expiry" : `for ${summary.cache.ttl}`}${summary.cache.store === void 0 ? "" : ` in the ${summary.cache.store} store`}`;
|
|
4588
4684
|
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
@@ -4590,6 +4686,88 @@ function formatCredentialSummary(summary) {
|
|
|
4590
4686
|
function describeCredential(credential) {
|
|
4591
4687
|
return formatCredentialSummary(summariseCredential(credential));
|
|
4592
4688
|
}
|
|
4689
|
+
function trySource(source, params) {
|
|
4690
|
+
const none = (reason, warn = false) => ({ ok: false, reason, warn });
|
|
4691
|
+
if ("env" in source) {
|
|
4692
|
+
const value = params.env[source.env];
|
|
4693
|
+
return value === void 0 || value === "" ? none("is unset or empty") : { ok: true, token: value };
|
|
4694
|
+
}
|
|
4695
|
+
if ("literal" in source) {
|
|
4696
|
+
return { ok: true, token: source.literal };
|
|
4697
|
+
}
|
|
4698
|
+
if ("file" in source) {
|
|
4699
|
+
const read = params.port.readSecretFile(source.file);
|
|
4700
|
+
if (!read.found) {
|
|
4701
|
+
return none("does not exist");
|
|
4702
|
+
}
|
|
4703
|
+
if (read.mode !== void 0 && (read.mode & GROUP_OTHER_PERMISSION_BITS) !== 0) {
|
|
4704
|
+
return none(
|
|
4705
|
+
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX)}); run \`chmod 600 ${source.file}\``,
|
|
4706
|
+
true
|
|
4707
|
+
);
|
|
4708
|
+
}
|
|
4709
|
+
const token2 = read.content.trim();
|
|
4710
|
+
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
4711
|
+
}
|
|
4712
|
+
const argv = commandArgv(source);
|
|
4713
|
+
const interactive = isInteractiveSource(source, params.env);
|
|
4714
|
+
if (interactive && !params.port.personPresent()) {
|
|
4715
|
+
return none("needs a person to approve it, but there is no terminal or desktop session");
|
|
4716
|
+
}
|
|
4717
|
+
const timeoutMs = source.timeoutMs ?? (interactive ? CREDENTIAL_INTERACTIVE_TIMEOUT_MS : CREDENTIAL_COMMAND_TIMEOUT_MS);
|
|
4718
|
+
const result = params.port.runCommand(argv, { timeoutMs, interactive });
|
|
4719
|
+
if (result.timedOut) {
|
|
4720
|
+
return none(`timed out after ${String(timeoutMs)}ms`);
|
|
4721
|
+
}
|
|
4722
|
+
if (result.status !== 0) {
|
|
4723
|
+
const stderr = result.stderr.trim();
|
|
4724
|
+
const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
|
|
4725
|
+
return none(`${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
|
|
4726
|
+
}
|
|
4727
|
+
const token = result.stdout.trim();
|
|
4728
|
+
return token === "" ? none("printed no token") : { ok: true, token };
|
|
4729
|
+
}
|
|
4730
|
+
function warmCommandFor(subject) {
|
|
4731
|
+
const [kind, name] = subject.split(" ");
|
|
4732
|
+
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
4733
|
+
}
|
|
4734
|
+
function resolveCredential(params) {
|
|
4735
|
+
const target = params.credential.target ?? "bearer";
|
|
4736
|
+
const cacheBlock = params.credential.cache;
|
|
4737
|
+
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
4738
|
+
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
4739
|
+
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
4740
|
+
const entry = cache.port.read(store, params.subject);
|
|
4741
|
+
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
4742
|
+
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
4743
|
+
}
|
|
4744
|
+
}
|
|
4745
|
+
const attempts = [];
|
|
4746
|
+
const warnings = [];
|
|
4747
|
+
for (const source of params.credential.sources) {
|
|
4748
|
+
const outcome = trySource(source, params);
|
|
4749
|
+
if (outcome.ok) {
|
|
4750
|
+
if (cache !== void 0 && store !== void 0) {
|
|
4751
|
+
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
4752
|
+
}
|
|
4753
|
+
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
4754
|
+
}
|
|
4755
|
+
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
4756
|
+
attempts.push(line);
|
|
4757
|
+
if (outcome.warn) {
|
|
4758
|
+
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
4762
|
+
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
4763
|
+
}
|
|
4764
|
+
function credentialVariables(target, token) {
|
|
4765
|
+
return {
|
|
4766
|
+
ANTHROPIC_AUTH_TOKEN: target === "bearer" ? token : void 0,
|
|
4767
|
+
ANTHROPIC_API_KEY: target === "apiKey" ? token : void 0,
|
|
4768
|
+
CLAUDE_CODE_OAUTH_TOKEN: target === "oauthToken" ? token : void 0
|
|
4769
|
+
};
|
|
4770
|
+
}
|
|
4593
4771
|
|
|
4594
4772
|
// src/providersStore.ts
|
|
4595
4773
|
var ProviderNotFoundError = class extends CliError {
|
|
@@ -4695,6 +4873,11 @@ function readProvider(paths, name) {
|
|
|
4695
4873
|
const raw = JSON.parse(fs6.readFileSync(filePath, "utf8"));
|
|
4696
4874
|
return parseProviderFile(filePath, raw);
|
|
4697
4875
|
}
|
|
4876
|
+
function loadProvider(providersDir, name, port) {
|
|
4877
|
+
const filePath = path15.join(providersDir, `${name}.json`);
|
|
4878
|
+
const raw = port.readConfigFile(filePath);
|
|
4879
|
+
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
4880
|
+
}
|
|
4698
4881
|
function listProviders(paths) {
|
|
4699
4882
|
const names = listProviderNames(
|
|
4700
4883
|
(dir) => fs6.readdirSync(dir),
|
|
@@ -4827,6 +5010,30 @@ function cascadeProviderName(cascade) {
|
|
|
4827
5010
|
}
|
|
4828
5011
|
return name;
|
|
4829
5012
|
}
|
|
5013
|
+
function resolveProvider(params) {
|
|
5014
|
+
const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
|
|
5015
|
+
const name = cliName ?? (params.cascade === void 0 ? void 0 : cascadeProviderName(params.cascade));
|
|
5016
|
+
if (name === void 0) {
|
|
5017
|
+
return void 0;
|
|
5018
|
+
}
|
|
5019
|
+
const definition = loadProvider(params.paths.providersDir, name, params.port);
|
|
5020
|
+
if (definition === void 0) {
|
|
5021
|
+
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
5022
|
+
return {
|
|
5023
|
+
ok: false,
|
|
5024
|
+
status: 1,
|
|
5025
|
+
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
5026
|
+
};
|
|
5027
|
+
}
|
|
5028
|
+
if (params.credentials === void 0) {
|
|
5029
|
+
return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
|
|
5030
|
+
}
|
|
5031
|
+
const resolution = resolveCredential({ credential: definition.credential, env: params.env, port: params.credentials, subject: `provider ${name}` });
|
|
5032
|
+
if (!resolution.ok) {
|
|
5033
|
+
return { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
5034
|
+
}
|
|
5035
|
+
return { ok: true, provider: { name, definition, credential: resolution.credential }, warnings: resolution.credential.warnings };
|
|
5036
|
+
}
|
|
4830
5037
|
|
|
4831
5038
|
// src/directoryRulesStore.ts
|
|
4832
5039
|
var DirectoryRuleNotFoundError = class extends CliError {
|
|
@@ -4919,7 +5126,7 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
4919
5126
|
}
|
|
4920
5127
|
|
|
4921
5128
|
// src/checkReport.ts
|
|
4922
|
-
import
|
|
5129
|
+
import os4 from "node:os";
|
|
4923
5130
|
|
|
4924
5131
|
// src/launcher/cascade.ts
|
|
4925
5132
|
import path17 from "node:path";
|
|
@@ -5015,6 +5222,7 @@ var MS_PER_SECOND2 = 1e3;
|
|
|
5015
5222
|
var SECONDS_PER_MINUTE = 60;
|
|
5016
5223
|
var MINUTES_PER_HOUR = 60;
|
|
5017
5224
|
var HOURS_PER_DAY = 24;
|
|
5225
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
5018
5226
|
function formatAge(ms) {
|
|
5019
5227
|
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5020
5228
|
if (minutes < MINUTES_PER_HOUR) {
|
|
@@ -5035,8 +5243,30 @@ function effectiveWindow(window, nowMs) {
|
|
|
5035
5243
|
...window.status === void 0 ? {} : { status: window.status }
|
|
5036
5244
|
};
|
|
5037
5245
|
}
|
|
5246
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
5247
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
5248
|
+
const unified = rateLimit?.unified;
|
|
5249
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
5250
|
+
return [];
|
|
5251
|
+
}
|
|
5252
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
5253
|
+
const windows = [
|
|
5254
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
5255
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
5256
|
+
];
|
|
5257
|
+
return windows.flatMap(({ name, window }) => {
|
|
5258
|
+
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
5259
|
+
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
5260
|
+
return [];
|
|
5261
|
+
}
|
|
5262
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
5263
|
+
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
5264
|
+
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
5265
|
+
});
|
|
5266
|
+
}
|
|
5038
5267
|
|
|
5039
5268
|
// src/usage/poolPick.ts
|
|
5269
|
+
import path18 from "node:path";
|
|
5040
5270
|
import { z as z9 } from "zod";
|
|
5041
5271
|
|
|
5042
5272
|
// src/usage/plan.ts
|
|
@@ -5289,14 +5519,14 @@ var PicksFileSchema = z9.strictObject({
|
|
|
5289
5519
|
function errorMessage(error) {
|
|
5290
5520
|
return error instanceof Error ? error.message : String(error);
|
|
5291
5521
|
}
|
|
5292
|
-
function loadPoolMembers(
|
|
5522
|
+
function loadPoolMembers(fs12, paths, identities, nowMs) {
|
|
5293
5523
|
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5294
|
-
const log = readUsageLog(
|
|
5524
|
+
const log = readUsageLog(fs12, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5295
5525
|
return identities.map((identity) => {
|
|
5296
5526
|
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5297
5527
|
try {
|
|
5298
|
-
const snapshot = readUsageSnapshot(
|
|
5299
|
-
const account = readAccountMetadata(
|
|
5528
|
+
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
5529
|
+
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5300
5530
|
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5301
5531
|
} catch (error) {
|
|
5302
5532
|
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
@@ -5314,9 +5544,9 @@ function rankPoolFromStore(params) {
|
|
|
5314
5544
|
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5315
5545
|
});
|
|
5316
5546
|
}
|
|
5317
|
-
function readPicksFile(
|
|
5547
|
+
function readPicksFile(fs12, file) {
|
|
5318
5548
|
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5319
|
-
const raw =
|
|
5549
|
+
const raw = fs12.readFileUtf8(file);
|
|
5320
5550
|
if (raw === void 0) {
|
|
5321
5551
|
return { file: empty };
|
|
5322
5552
|
}
|
|
@@ -5329,17 +5559,72 @@ function readPicksFile(fs10, file) {
|
|
|
5329
5559
|
const result = PicksFileSchema.safeParse(parsed);
|
|
5330
5560
|
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5331
5561
|
}
|
|
5332
|
-
function readStickyPick(
|
|
5333
|
-
const read = readPicksFile(
|
|
5562
|
+
function readStickyPick(fs12, picksFile, directory) {
|
|
5563
|
+
const read = readPicksFile(fs12, picksFile);
|
|
5334
5564
|
const entry = read.file.picks[directory];
|
|
5335
5565
|
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5336
5566
|
}
|
|
5567
|
+
function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
5568
|
+
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5569
|
+
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5570
|
+
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5571
|
+
fs12.mkdirPrivate(path18.dirname(picksFile));
|
|
5572
|
+
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5573
|
+
`);
|
|
5574
|
+
}
|
|
5337
5575
|
|
|
5338
5576
|
// src/launcher/pool.ts
|
|
5339
|
-
|
|
5340
|
-
|
|
5577
|
+
var RESUME_FLAGS = /* @__PURE__ */ new Set(["--continue", "-c", "--resume", "-r"]);
|
|
5578
|
+
var TERMINATOR = "--";
|
|
5579
|
+
var REASONS_LOGGED = 3;
|
|
5580
|
+
function splitMembers(pool, paths, fs12) {
|
|
5581
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs12) !== void 0;
|
|
5341
5582
|
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5342
5583
|
}
|
|
5584
|
+
function isResuming(passthrough) {
|
|
5585
|
+
const end = passthrough.indexOf(TERMINATOR);
|
|
5586
|
+
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
5587
|
+
}
|
|
5588
|
+
function resolvePoolLaunch(params) {
|
|
5589
|
+
const { poolName, log } = params;
|
|
5590
|
+
const pool = params.pools?.[poolName];
|
|
5591
|
+
if (pool === void 0) {
|
|
5592
|
+
return { ok: false, message: `agent-shim: no pool named "${poolName}" (selected via ${params.selectedVia}). Run \`agent-shim pool add ${poolName} --identity <name>...\` first.` };
|
|
5593
|
+
}
|
|
5594
|
+
const { present: identities, missing } = splitMembers(pool, params.paths, params.fs);
|
|
5595
|
+
for (const name of missing) {
|
|
5596
|
+
log.warn(`agent-shim: pool "${poolName}" names identity "${name}", which does not exist; skipping it`);
|
|
5597
|
+
}
|
|
5598
|
+
if (identities.length === 0) {
|
|
5599
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
5600
|
+
}
|
|
5601
|
+
const resuming = isResuming(params.passthrough);
|
|
5602
|
+
const stickyRead = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd);
|
|
5603
|
+
if (stickyRead.problem !== void 0) {
|
|
5604
|
+
log.warn(`agent-shim: ${stickyRead.problem}; ignoring the last-pick record`);
|
|
5605
|
+
}
|
|
5606
|
+
for (; ; ) {
|
|
5607
|
+
const nowMs = params.now();
|
|
5608
|
+
const ranking = rankPoolFromStore({ fs: params.usageFs, paths: params.paths, identities, nowMs, resuming, ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky } });
|
|
5609
|
+
const { pick, earliestReturn } = ranking;
|
|
5610
|
+
if (pick !== void 0) {
|
|
5611
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
5612
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
5613
|
+
}
|
|
5614
|
+
if (earliestReturn === void 0) {
|
|
5615
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
|
5616
|
+
}
|
|
5617
|
+
const until = new Date(earliestReturn.atMs).toISOString();
|
|
5618
|
+
if (!params.wait) {
|
|
5619
|
+
return {
|
|
5620
|
+
ok: false,
|
|
5621
|
+
message: `agent-shim: every member of pool "${poolName}" is refused right now; the earliest, ${earliestReturn.identity}, returns at ${until} (in ${formatAge(earliestReturn.atMs - nowMs)}). Pass --wait to sleep until then.`
|
|
5622
|
+
};
|
|
5623
|
+
}
|
|
5624
|
+
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
5625
|
+
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
5626
|
+
}
|
|
5627
|
+
}
|
|
5343
5628
|
|
|
5344
5629
|
// src/poolPickReport.ts
|
|
5345
5630
|
function candidateView(candidate) {
|
|
@@ -5379,14 +5664,15 @@ function collectPoolPick(params) {
|
|
|
5379
5664
|
import { spawn, spawnSync } from "node:child_process";
|
|
5380
5665
|
import { createHash as createHash4 } from "node:crypto";
|
|
5381
5666
|
import fs7 from "node:fs";
|
|
5382
|
-
import
|
|
5667
|
+
import os3 from "node:os";
|
|
5668
|
+
import path20 from "node:path";
|
|
5383
5669
|
import { isSea } from "node:sea";
|
|
5384
5670
|
|
|
5385
5671
|
// src/versionDiscovery.ts
|
|
5386
5672
|
import os2 from "node:os";
|
|
5387
|
-
import
|
|
5673
|
+
import path19 from "node:path";
|
|
5388
5674
|
function defaultVersionsDir() {
|
|
5389
|
-
return
|
|
5675
|
+
return path19.join(os2.homedir(), ".local", "share", "claude", "versions");
|
|
5390
5676
|
}
|
|
5391
5677
|
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
5392
5678
|
function isNumericDottedVersion(name) {
|
|
@@ -5428,14 +5714,14 @@ function discoverClaudeBinary(options) {
|
|
|
5428
5714
|
const highestVersion = pickHighestVersion(entries);
|
|
5429
5715
|
if (highestVersion !== void 0) {
|
|
5430
5716
|
return {
|
|
5431
|
-
path:
|
|
5717
|
+
path: path19.join(versionsDir, highestVersion),
|
|
5432
5718
|
source: "versions-dir",
|
|
5433
5719
|
version: highestVersion
|
|
5434
5720
|
};
|
|
5435
5721
|
}
|
|
5436
|
-
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) =>
|
|
5722
|
+
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => path19.resolve(dir)));
|
|
5437
5723
|
for (const dir of options.pathDirs) {
|
|
5438
|
-
if (ownInstallDirs.has(
|
|
5724
|
+
if (ownInstallDirs.has(path19.resolve(dir))) {
|
|
5439
5725
|
continue;
|
|
5440
5726
|
}
|
|
5441
5727
|
const found = options.findExecutableInDir(dir, "claude");
|
|
@@ -5480,7 +5766,7 @@ function isErrorWithCode(error, code) {
|
|
|
5480
5766
|
}
|
|
5481
5767
|
var OWNER_ONLY_DIR_MODE = 448;
|
|
5482
5768
|
var OWNER_ONLY_FILE_MODE = 384;
|
|
5483
|
-
var
|
|
5769
|
+
var PERMISSION_BITS2 = 511;
|
|
5484
5770
|
var realFarmFs = {
|
|
5485
5771
|
lstat(filePath) {
|
|
5486
5772
|
let stat;
|
|
@@ -5546,7 +5832,7 @@ var realFarmFs = {
|
|
|
5546
5832
|
appendFilePrivate(filePath, contents) {
|
|
5547
5833
|
const descriptor = fs7.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
5548
5834
|
try {
|
|
5549
|
-
if ((fs7.fstatSync(descriptor).mode &
|
|
5835
|
+
if ((fs7.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
|
|
5550
5836
|
fs7.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
5551
5837
|
}
|
|
5552
5838
|
const bytes = Buffer.from(contents, "utf8");
|
|
@@ -5580,6 +5866,10 @@ var realFarmFs = {
|
|
|
5580
5866
|
}
|
|
5581
5867
|
}
|
|
5582
5868
|
};
|
|
5869
|
+
var INT32_BYTE_LENGTH = 4;
|
|
5870
|
+
function realSleepSync(ms) {
|
|
5871
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(INT32_BYTE_LENGTH)), 0, 0, ms);
|
|
5872
|
+
}
|
|
5583
5873
|
function realIsProcessAlive(pid) {
|
|
5584
5874
|
try {
|
|
5585
5875
|
process.kill(pid, 0);
|
|
@@ -5601,6 +5891,51 @@ var realRunPort = {
|
|
|
5601
5891
|
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
5602
5892
|
}
|
|
5603
5893
|
};
|
|
5894
|
+
function expandHome2(filePath) {
|
|
5895
|
+
return filePath.startsWith("~/") ? path20.join(os3.homedir(), filePath.slice(1)) : filePath;
|
|
5896
|
+
}
|
|
5897
|
+
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
5898
|
+
function hasDesktopSession() {
|
|
5899
|
+
if (process.platform === "darwin") {
|
|
5900
|
+
const result = spawnSync("launchctl", ["managername"], { encoding: "utf8" });
|
|
5901
|
+
return result.status === 0 && result.stdout.trim() === MACOS_GUI_SESSION_MANAGER;
|
|
5902
|
+
}
|
|
5903
|
+
if (process.platform === "win32") {
|
|
5904
|
+
return false;
|
|
5905
|
+
}
|
|
5906
|
+
return (process.env.DISPLAY ?? "") !== "" || (process.env.WAYLAND_DISPLAY ?? "") !== "";
|
|
5907
|
+
}
|
|
5908
|
+
var realCredentialPort = {
|
|
5909
|
+
readSecretFile(filePath) {
|
|
5910
|
+
const resolved = expandHome2(filePath);
|
|
5911
|
+
let content;
|
|
5912
|
+
let stat;
|
|
5913
|
+
try {
|
|
5914
|
+
stat = fs7.statSync(resolved);
|
|
5915
|
+
content = fs7.readFileSync(resolved, "utf8");
|
|
5916
|
+
} catch (error) {
|
|
5917
|
+
if (isEnoent3(error)) {
|
|
5918
|
+
return { found: false };
|
|
5919
|
+
}
|
|
5920
|
+
throw error;
|
|
5921
|
+
}
|
|
5922
|
+
return { found: true, content, mode: process.platform === "win32" ? void 0 : stat.mode };
|
|
5923
|
+
},
|
|
5924
|
+
runCommand(argv, options) {
|
|
5925
|
+
const [command, ...args] = argv;
|
|
5926
|
+
const result = spawnSync(command, args, {
|
|
5927
|
+
encoding: "utf8",
|
|
5928
|
+
timeout: options.timeoutMs,
|
|
5929
|
+
stdio: options.interactive ? ["inherit", "pipe", "inherit"] : ["ignore", "pipe", "pipe"]
|
|
5930
|
+
});
|
|
5931
|
+
if (result.error !== void 0) {
|
|
5932
|
+
const timedOut = isErrorWithCode(result.error, "ETIMEDOUT");
|
|
5933
|
+
return { status: result.status, stdout: "", stderr: timedOut ? "" : result.error.message, timedOut };
|
|
5934
|
+
}
|
|
5935
|
+
return { status: result.status, stdout: result.stdout, stderr: options.interactive ? "" : result.stderr, timedOut: false };
|
|
5936
|
+
},
|
|
5937
|
+
personPresent: () => process.stdin.isTTY || hasDesktopSession()
|
|
5938
|
+
};
|
|
5604
5939
|
function resolveGitBranch(run, cwd) {
|
|
5605
5940
|
const result = run.run("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
5606
5941
|
if (result.status !== 0) {
|
|
@@ -5615,6 +5950,62 @@ function resolveGitBranch(run, cwd) {
|
|
|
5615
5950
|
}
|
|
5616
5951
|
return { branch: name, branchDetached: false };
|
|
5617
5952
|
}
|
|
5953
|
+
function resolveGitRoot(run, cwd) {
|
|
5954
|
+
const result = run.run("git", ["-C", cwd, "rev-parse", "--show-toplevel"]);
|
|
5955
|
+
if (result.status !== 0) {
|
|
5956
|
+
return void 0;
|
|
5957
|
+
}
|
|
5958
|
+
const root = result.stdout.trim();
|
|
5959
|
+
return root === "" ? void 0 : root;
|
|
5960
|
+
}
|
|
5961
|
+
function detachedDaemonSpawnOptions(logFd, env) {
|
|
5962
|
+
return { detached: true, stdio: ["ignore", logFd, logFd], env };
|
|
5963
|
+
}
|
|
5964
|
+
function spawnDaemonThrough(executable) {
|
|
5965
|
+
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
5966
|
+
}
|
|
5967
|
+
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
5968
|
+
fs7.mkdirSync(path20.dirname(logPath), { recursive: true });
|
|
5969
|
+
const logFd = fs7.openSync(logPath, "a");
|
|
5970
|
+
try {
|
|
5971
|
+
const child = spawn(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
5972
|
+
child.unref();
|
|
5973
|
+
if (child.pid === void 0) {
|
|
5974
|
+
throw new Error(`spawning ${subcommand} returned no pid`);
|
|
5975
|
+
}
|
|
5976
|
+
return child.pid;
|
|
5977
|
+
} finally {
|
|
5978
|
+
fs7.closeSync(logFd);
|
|
5979
|
+
}
|
|
5980
|
+
}
|
|
5981
|
+
function spawnHeadroomSupervisor(paths, spawnDaemon) {
|
|
5982
|
+
return spawnDaemon(paths, "__headroom-supervisor", paths.headroomLogPath);
|
|
5983
|
+
}
|
|
5984
|
+
function realHeadroomPort(paths, options) {
|
|
5985
|
+
return {
|
|
5986
|
+
ensure: () => {
|
|
5987
|
+
const up = ensureHeadroom({
|
|
5988
|
+
paths,
|
|
5989
|
+
launcherPid: process.pid,
|
|
5990
|
+
ports: {
|
|
5991
|
+
fs: realFarmFs,
|
|
5992
|
+
// Zombie-aware on purpose: a supervisor that died while still a child of this launcher sits unreaped until the launcher itself exits, and a defunct supervisor answering signal 0 as alive would stretch every launch to the full start timeout.
|
|
5993
|
+
isRunning: realIsProcessRunning,
|
|
5994
|
+
now: () => Date.now(),
|
|
5995
|
+
sleep: realSleepSync,
|
|
5996
|
+
spawnSupervisor: (layout) => spawnHeadroomSupervisor(layout, options.spawnDaemon)
|
|
5997
|
+
}
|
|
5998
|
+
});
|
|
5999
|
+
return {
|
|
6000
|
+
port: up.port,
|
|
6001
|
+
projectId: resolveGitRoot(realRunPort, options.cwd) ?? options.cwd
|
|
6002
|
+
};
|
|
6003
|
+
},
|
|
6004
|
+
release: () => {
|
|
6005
|
+
removeSession(realFarmFs, paths.headroomSessionsDir, process.pid);
|
|
6006
|
+
}
|
|
6007
|
+
};
|
|
6008
|
+
}
|
|
5618
6009
|
var realProcPort = {
|
|
5619
6010
|
env: process.env,
|
|
5620
6011
|
argv: process.argv.slice(2),
|
|
@@ -5632,7 +6023,7 @@ function listVersionsDir(dir) {
|
|
|
5632
6023
|
throw error;
|
|
5633
6024
|
}
|
|
5634
6025
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
5635
|
-
const fullPath =
|
|
6026
|
+
const fullPath = path20.join(dir, entry.name);
|
|
5636
6027
|
const stat = fs7.statSync(fullPath);
|
|
5637
6028
|
return {
|
|
5638
6029
|
name: entry.name,
|
|
@@ -5647,7 +6038,7 @@ function windowsPathExtensions(pathext) {
|
|
|
5647
6038
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
5648
6039
|
}
|
|
5649
6040
|
function resolveExecutableCandidate(dir, name, env) {
|
|
5650
|
-
const candidate =
|
|
6041
|
+
const candidate = path20.join(dir, name);
|
|
5651
6042
|
if (env.platform === "win32") {
|
|
5652
6043
|
const extensions = windowsPathExtensions(env.pathext);
|
|
5653
6044
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -5696,13 +6087,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
5696
6087
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
5697
6088
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
5698
6089
|
}
|
|
5699
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
5700
|
-
const candidateDir =
|
|
5701
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
6090
|
+
const candidate = env.argv1 === void 0 ? env.execPath : path20.resolve(env.cwd, env.argv1);
|
|
6091
|
+
const candidateDir = path20.dirname(candidate);
|
|
6092
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => path20.resolve(dir) === candidateDir);
|
|
5702
6093
|
if (candidateDirOnPath) {
|
|
5703
6094
|
return candidate;
|
|
5704
6095
|
}
|
|
5705
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
6096
|
+
const viaPath = searchPathForExecutable(env.pathDirs, path20.basename(candidate), env.findExecutableInDir);
|
|
5706
6097
|
return viaPath ?? candidate;
|
|
5707
6098
|
}
|
|
5708
6099
|
function realOwnExecutablePath() {
|
|
@@ -5710,21 +6101,21 @@ function realOwnExecutablePath() {
|
|
|
5710
6101
|
argv1: process.argv[1],
|
|
5711
6102
|
execPath: process.execPath,
|
|
5712
6103
|
cwd: process.cwd(),
|
|
5713
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6104
|
+
pathDirs: (process.env.PATH ?? "").split(path20.delimiter).filter((dir) => dir !== ""),
|
|
5714
6105
|
findExecutableInDir
|
|
5715
6106
|
});
|
|
5716
6107
|
}
|
|
5717
6108
|
function realResolveClaudeBinary(ownInstallDirs) {
|
|
5718
6109
|
return () => discoverClaudeBinary({
|
|
5719
6110
|
listVersionsDir,
|
|
5720
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6111
|
+
pathDirs: (process.env.PATH ?? "").split(path20.delimiter).filter((dir) => dir !== ""),
|
|
5721
6112
|
findExecutableInDir,
|
|
5722
6113
|
ownInstallDirs: [...ownInstallDirs]
|
|
5723
6114
|
});
|
|
5724
6115
|
}
|
|
5725
6116
|
|
|
5726
6117
|
// src/checkReport.ts
|
|
5727
|
-
import
|
|
6118
|
+
import path21 from "node:path";
|
|
5728
6119
|
import { z as z10 } from "zod";
|
|
5729
6120
|
|
|
5730
6121
|
// src/cli/bool.ts
|
|
@@ -5913,7 +6304,8 @@ function runCheck(params) {
|
|
|
5913
6304
|
nowMs: params.nowMs,
|
|
5914
6305
|
env: params.env,
|
|
5915
6306
|
...params.branch === void 0 ? {} : { branch: params.branch },
|
|
5916
|
-
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached }
|
|
6307
|
+
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached },
|
|
6308
|
+
descend: descendPolicyFor(params.cascade, params.home)
|
|
5917
6309
|
});
|
|
5918
6310
|
const resolved = resolveDecisions({ facts, cascade: params.cascade, classification: params.classification });
|
|
5919
6311
|
const decisionLines = [...resolved.decisions.values()].slice().sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0).map(formatDecision);
|
|
@@ -6046,7 +6438,7 @@ function loadCheckProvider(paths, name) {
|
|
|
6046
6438
|
}
|
|
6047
6439
|
function collectCheckReport(params) {
|
|
6048
6440
|
const { paths, cwd } = params;
|
|
6049
|
-
const home =
|
|
6441
|
+
const home = os4.homedir();
|
|
6050
6442
|
const claudeHome = resolveClaudeHome();
|
|
6051
6443
|
const read = cosmiconfigReader();
|
|
6052
6444
|
const classification = loadClassification(paths);
|
|
@@ -6091,10 +6483,10 @@ function collectCheckReport(params) {
|
|
|
6091
6483
|
}).input;
|
|
6092
6484
|
const providerName = cascadeProviderName(cascade);
|
|
6093
6485
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
6094
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
6486
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : path21.join(paths.identitiesDir, identityDecision.name);
|
|
6095
6487
|
const settingsFiles = {
|
|
6096
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
6097
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
6488
|
+
"settings.json": realFsPort.readFileUtf8(path21.join(claudeHome, "settings.json")),
|
|
6489
|
+
"settings.local.json": realFsPort.readFileUtf8(path21.join(claudeHome, "settings.local.json"))
|
|
6098
6490
|
};
|
|
6099
6491
|
const report = runCheck({
|
|
6100
6492
|
cwd,
|
|
@@ -6124,7 +6516,7 @@ function collectCheckReport(params) {
|
|
|
6124
6516
|
|
|
6125
6517
|
// src/claudeShim.ts
|
|
6126
6518
|
import fs8 from "node:fs";
|
|
6127
|
-
import
|
|
6519
|
+
import path22 from "node:path";
|
|
6128
6520
|
import { z as z11 } from "zod";
|
|
6129
6521
|
var ClaudeShimStateSchema = z11.strictObject({
|
|
6130
6522
|
targetPath: z11.string().min(1),
|
|
@@ -6135,29 +6527,29 @@ function commandFilename(ownExecutablePath, commandName) {
|
|
|
6135
6527
|
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
6136
6528
|
}
|
|
6137
6529
|
function resolveOwnInstallDirs(paths, ownExecutablePath) {
|
|
6138
|
-
const dirs = [
|
|
6530
|
+
const dirs = [path22.dirname(ownExecutablePath)];
|
|
6139
6531
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6140
6532
|
if (state !== void 0) {
|
|
6141
|
-
dirs.push(
|
|
6533
|
+
dirs.push(path22.dirname(state.targetPath));
|
|
6142
6534
|
}
|
|
6143
6535
|
return dirs;
|
|
6144
6536
|
}
|
|
6145
6537
|
var nodeLinkFs = { link: fs8.linkSync, copyFile: fs8.copyFileSync, chmod: fs8.chmodSync };
|
|
6146
6538
|
function findPathShadow(params) {
|
|
6147
|
-
const resolvedTargetDir =
|
|
6539
|
+
const resolvedTargetDir = path22.resolve(params.targetDir);
|
|
6148
6540
|
for (const dir of params.pathDirs) {
|
|
6149
6541
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
6150
6542
|
if (found === void 0) {
|
|
6151
6543
|
continue;
|
|
6152
6544
|
}
|
|
6153
|
-
return
|
|
6545
|
+
return path22.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
6154
6546
|
}
|
|
6155
|
-
return params.pathDirs.some((dir) =>
|
|
6547
|
+
return params.pathDirs.some((dir) => path22.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
6156
6548
|
}
|
|
6157
6549
|
|
|
6158
6550
|
// src/doctorReport.ts
|
|
6159
6551
|
import fs9 from "node:fs";
|
|
6160
|
-
import
|
|
6552
|
+
import path23 from "node:path";
|
|
6161
6553
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
6162
6554
|
if (status.status !== "shadowed") {
|
|
6163
6555
|
return status;
|
|
@@ -6181,7 +6573,7 @@ function validateJson(schema, input) {
|
|
|
6181
6573
|
return { ok: true, data: result.data };
|
|
6182
6574
|
}
|
|
6183
6575
|
function pushPathResolution(push, resolution) {
|
|
6184
|
-
const ownDir =
|
|
6576
|
+
const ownDir = path23.dirname(resolution.ownExecutablePath);
|
|
6185
6577
|
switch (resolution.agentShim.status) {
|
|
6186
6578
|
case "ok":
|
|
6187
6579
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -6249,14 +6641,14 @@ function pushProvider(push, entry) {
|
|
|
6249
6641
|
}
|
|
6250
6642
|
function pushLegacyName(push, params) {
|
|
6251
6643
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
6252
|
-
if (legacyVariables.length === 0 &&
|
|
6644
|
+
if (legacyVariables.length === 0 && path23.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
6253
6645
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
6254
6646
|
return;
|
|
6255
6647
|
}
|
|
6256
6648
|
for (const name of legacyVariables) {
|
|
6257
6649
|
push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
|
|
6258
6650
|
}
|
|
6259
|
-
if (
|
|
6651
|
+
if (path23.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
6260
6652
|
push(
|
|
6261
6653
|
"legacy-name",
|
|
6262
6654
|
"warn",
|
|
@@ -6564,18 +6956,18 @@ function collectDoctorReport(params) {
|
|
|
6564
6956
|
const { paths } = params;
|
|
6565
6957
|
const identityNames = fs9.existsSync(paths.identitiesDir) ? fs9.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
6566
6958
|
const identities = identityNames.map((name) => {
|
|
6567
|
-
const farmRoot =
|
|
6568
|
-
const identityPath =
|
|
6959
|
+
const farmRoot = path23.join(paths.identitiesDir, name);
|
|
6960
|
+
const identityPath = path23.join(farmRoot, "identity.json");
|
|
6569
6961
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
6570
6962
|
});
|
|
6571
6963
|
const profileNames = fs9.existsSync(paths.configProfilesDir) ? fs9.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6572
6964
|
const configProfiles = profileNames.map((name) => {
|
|
6573
|
-
const profilePath =
|
|
6965
|
+
const profilePath = path23.join(paths.configProfilesDir, `${name}.json`);
|
|
6574
6966
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
6575
6967
|
});
|
|
6576
6968
|
const providerNames = fs9.existsSync(paths.providersDir) ? fs9.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6577
6969
|
const providers = providerNames.map((name) => {
|
|
6578
|
-
const providerPath =
|
|
6970
|
+
const providerPath = path23.join(paths.providersDir, `${name}.json`);
|
|
6579
6971
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
6580
6972
|
});
|
|
6581
6973
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -6587,12 +6979,12 @@ function collectDoctorReport(params) {
|
|
|
6587
6979
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
6588
6980
|
}
|
|
6589
6981
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6590
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
6982
|
+
const pathDirs = (params.env.PATH ?? "").split(path23.delimiter).filter((dir) => dir !== "");
|
|
6591
6983
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
6592
6984
|
findPathShadow({
|
|
6593
6985
|
pathDirs,
|
|
6594
|
-
targetDir:
|
|
6595
|
-
targetFilename:
|
|
6986
|
+
targetDir: path23.dirname(shimState.targetPath),
|
|
6987
|
+
targetFilename: path23.basename(shimState.targetPath),
|
|
6596
6988
|
findExecutableInDir
|
|
6597
6989
|
}),
|
|
6598
6990
|
shimState.targetPath,
|
|
@@ -6615,7 +7007,7 @@ function collectDoctorReport(params) {
|
|
|
6615
7007
|
agentShim: refinePathShadow(
|
|
6616
7008
|
findPathShadow({
|
|
6617
7009
|
pathDirs,
|
|
6618
|
-
targetDir:
|
|
7010
|
+
targetDir: path23.dirname(ownExecutablePath),
|
|
6619
7011
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
6620
7012
|
findExecutableInDir
|
|
6621
7013
|
}),
|
|
@@ -6633,6 +7025,975 @@ function collectDoctorReport(params) {
|
|
|
6633
7025
|
});
|
|
6634
7026
|
return report;
|
|
6635
7027
|
}
|
|
7028
|
+
|
|
7029
|
+
// src/launcher.ts
|
|
7030
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
7031
|
+
import path25 from "node:path";
|
|
7032
|
+
|
|
7033
|
+
// src/launcher/argv.ts
|
|
7034
|
+
var ConflictingIdentityError = class extends UsageError {
|
|
7035
|
+
constructor(positional, flag) {
|
|
7036
|
+
super(`The launch names two identities: "@${positional}" and "--identity ${flag}". Pass only one.`);
|
|
7037
|
+
this.positional = positional;
|
|
7038
|
+
this.flag = flag;
|
|
7039
|
+
this.name = "ConflictingIdentityError";
|
|
7040
|
+
}
|
|
7041
|
+
positional;
|
|
7042
|
+
flag;
|
|
7043
|
+
};
|
|
7044
|
+
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--category", "--share", "--hide"];
|
|
7045
|
+
var BOOLEAN_FLAGS = [
|
|
7046
|
+
{ flag: "--headroom", key: "headroom" },
|
|
7047
|
+
{ flag: "--track-usage", key: "trackUsage" },
|
|
7048
|
+
{ flag: "--skip-permissions", key: "skipPermissions" },
|
|
7049
|
+
{ flag: "--remote-control", key: "remoteControl" },
|
|
7050
|
+
{ flag: "--wait", key: "wait" }
|
|
7051
|
+
];
|
|
7052
|
+
var LAUNCHER_FLAG_NAMES = [
|
|
7053
|
+
...VALUED_FLAGS,
|
|
7054
|
+
"--no-provider",
|
|
7055
|
+
...BOOLEAN_FLAGS.flatMap(({ flag }) => [flag, `--no-${flag.slice(2)}`])
|
|
7056
|
+
];
|
|
7057
|
+
var TERMINATOR2 = "--";
|
|
7058
|
+
function matchValuedFlag(token) {
|
|
7059
|
+
for (const flag of VALUED_FLAGS) {
|
|
7060
|
+
if (token === flag) {
|
|
7061
|
+
return { flag };
|
|
7062
|
+
}
|
|
7063
|
+
if (token.startsWith(`${flag}=`)) {
|
|
7064
|
+
return { flag, inlineValue: token.slice(flag.length + 1) };
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
return void 0;
|
|
7068
|
+
}
|
|
7069
|
+
function matchBooleanFlag(token) {
|
|
7070
|
+
for (const { flag, key } of BOOLEAN_FLAGS) {
|
|
7071
|
+
if (token === flag) {
|
|
7072
|
+
return { key, value: true };
|
|
7073
|
+
}
|
|
7074
|
+
if (token === `--no-${flag.slice(2)}`) {
|
|
7075
|
+
return { key, value: false };
|
|
7076
|
+
}
|
|
7077
|
+
}
|
|
7078
|
+
return void 0;
|
|
7079
|
+
}
|
|
7080
|
+
function parseLauncherArgv(argv) {
|
|
7081
|
+
const first = argv[0];
|
|
7082
|
+
const positionalIdentity = first !== void 0 && first.startsWith("@") && first.length > 1 ? first.slice(1) : void 0;
|
|
7083
|
+
const remaining = positionalIdentity === void 0 ? argv : argv.slice(1);
|
|
7084
|
+
let flagIdentity;
|
|
7085
|
+
let configProfile;
|
|
7086
|
+
let provider;
|
|
7087
|
+
const booleans = {};
|
|
7088
|
+
const categoryFlags = [];
|
|
7089
|
+
const shareFlags = [];
|
|
7090
|
+
const hideFlags = [];
|
|
7091
|
+
const rest = [];
|
|
7092
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
7093
|
+
const token = remaining[index];
|
|
7094
|
+
if (token === void 0) {
|
|
7095
|
+
continue;
|
|
7096
|
+
}
|
|
7097
|
+
if (token === TERMINATOR2) {
|
|
7098
|
+
rest.push(...remaining.slice(index));
|
|
7099
|
+
break;
|
|
7100
|
+
}
|
|
7101
|
+
if (token === "--no-provider") {
|
|
7102
|
+
provider = false;
|
|
7103
|
+
continue;
|
|
7104
|
+
}
|
|
7105
|
+
const booleanFlag = matchBooleanFlag(token);
|
|
7106
|
+
if (booleanFlag !== void 0) {
|
|
7107
|
+
booleans[booleanFlag.key] = booleanFlag.value;
|
|
7108
|
+
continue;
|
|
7109
|
+
}
|
|
7110
|
+
const matched = matchValuedFlag(token);
|
|
7111
|
+
if (matched === void 0) {
|
|
7112
|
+
rest.push(token);
|
|
7113
|
+
continue;
|
|
7114
|
+
}
|
|
7115
|
+
let value;
|
|
7116
|
+
if (matched.inlineValue !== void 0) {
|
|
7117
|
+
value = matched.inlineValue;
|
|
7118
|
+
} else {
|
|
7119
|
+
const next = remaining[index + 1];
|
|
7120
|
+
if (next === void 0 || next === TERMINATOR2) {
|
|
7121
|
+
rest.push(token);
|
|
7122
|
+
continue;
|
|
7123
|
+
}
|
|
7124
|
+
value = next;
|
|
7125
|
+
index += 1;
|
|
7126
|
+
}
|
|
7127
|
+
switch (matched.flag) {
|
|
7128
|
+
case "--identity":
|
|
7129
|
+
flagIdentity = value;
|
|
7130
|
+
break;
|
|
7131
|
+
case "--config-profile":
|
|
7132
|
+
configProfile = value;
|
|
7133
|
+
break;
|
|
7134
|
+
case "--provider":
|
|
7135
|
+
provider = value;
|
|
7136
|
+
break;
|
|
7137
|
+
case "--category":
|
|
7138
|
+
categoryFlags.push(value);
|
|
7139
|
+
break;
|
|
7140
|
+
case "--share":
|
|
7141
|
+
shareFlags.push(value);
|
|
7142
|
+
break;
|
|
7143
|
+
case "--hide":
|
|
7144
|
+
hideFlags.push(value);
|
|
7145
|
+
break;
|
|
7146
|
+
default:
|
|
7147
|
+
matched.flag;
|
|
7148
|
+
}
|
|
7149
|
+
}
|
|
7150
|
+
if (positionalIdentity !== void 0 && flagIdentity !== void 0 && positionalIdentity !== flagIdentity) {
|
|
7151
|
+
throw new ConflictingIdentityError(positionalIdentity, flagIdentity);
|
|
7152
|
+
}
|
|
7153
|
+
const identity = flagIdentity ?? positionalIdentity;
|
|
7154
|
+
return {
|
|
7155
|
+
...identity === void 0 ? {} : { identity },
|
|
7156
|
+
...configProfile === void 0 ? {} : { configProfile },
|
|
7157
|
+
...provider === void 0 ? {} : { provider },
|
|
7158
|
+
...booleans,
|
|
7159
|
+
categoryFlags,
|
|
7160
|
+
shareFlags,
|
|
7161
|
+
hideFlags,
|
|
7162
|
+
rest
|
|
7163
|
+
};
|
|
7164
|
+
}
|
|
7165
|
+
|
|
7166
|
+
// src/cli/pairs.ts
|
|
7167
|
+
function splitCommas(input) {
|
|
7168
|
+
if (input === "") {
|
|
7169
|
+
return [];
|
|
7170
|
+
}
|
|
7171
|
+
return input.split(",");
|
|
7172
|
+
}
|
|
7173
|
+
function parsePair(input) {
|
|
7174
|
+
const eqIndex = input.indexOf("=");
|
|
7175
|
+
if (eqIndex === -1) {
|
|
7176
|
+
throw new Error(`Expected "<key>=<value>", got "${input}" (no "=" found)`);
|
|
7177
|
+
}
|
|
7178
|
+
const key = input.slice(0, eqIndex);
|
|
7179
|
+
const value = input.slice(eqIndex + 1);
|
|
7180
|
+
if (key === "") {
|
|
7181
|
+
throw new Error(`Expected a non-empty key before "=" in "${input}"`);
|
|
7182
|
+
}
|
|
7183
|
+
return { key, value };
|
|
7184
|
+
}
|
|
7185
|
+
function parseBool(input) {
|
|
7186
|
+
const parsed = parseBoolWord(input);
|
|
7187
|
+
if (parsed === void 0) {
|
|
7188
|
+
throw new Error(`Expected "true", "false", "1" or "0", got "${input}"`);
|
|
7189
|
+
}
|
|
7190
|
+
return parsed;
|
|
7191
|
+
}
|
|
7192
|
+
function parseBoolPairList(input) {
|
|
7193
|
+
const result = {};
|
|
7194
|
+
for (const piece of splitCommas(input)) {
|
|
7195
|
+
const { key, value } = parsePair(piece);
|
|
7196
|
+
result[key] = parseBool(value);
|
|
7197
|
+
}
|
|
7198
|
+
return result;
|
|
7199
|
+
}
|
|
7200
|
+
|
|
7201
|
+
// src/launcher/cliOverride.ts
|
|
7202
|
+
var InvalidCliCategoryError = class extends CliError {
|
|
7203
|
+
constructor(categoryName) {
|
|
7204
|
+
super(`"${categoryName}" is not a category this launch may toggle (runtime, history, knowledge, settings).`);
|
|
7205
|
+
this.categoryName = categoryName;
|
|
7206
|
+
this.name = "InvalidCliCategoryError";
|
|
7207
|
+
}
|
|
7208
|
+
categoryName;
|
|
7209
|
+
};
|
|
7210
|
+
var InvalidCliEntryKeyError = class extends CliError {
|
|
7211
|
+
constructor(key) {
|
|
7212
|
+
super(`"${key}" is not a valid entries key \u2014 it must start with "<category>/", e.g. "knowledge/skills/commit".`);
|
|
7213
|
+
this.key = key;
|
|
7214
|
+
this.name = "InvalidCliEntryKeyError";
|
|
7215
|
+
}
|
|
7216
|
+
key;
|
|
7217
|
+
};
|
|
7218
|
+
function toCategoryMap(pairs) {
|
|
7219
|
+
const expanded = expandAllCategoryKey(pairs);
|
|
7220
|
+
const result = {};
|
|
7221
|
+
for (const [key, value] of Object.entries(expanded)) {
|
|
7222
|
+
if (!isOverridableCategory(key)) {
|
|
7223
|
+
throw new InvalidCliCategoryError(key);
|
|
7224
|
+
}
|
|
7225
|
+
result[key] = value;
|
|
7226
|
+
}
|
|
7227
|
+
return result;
|
|
7228
|
+
}
|
|
7229
|
+
function toEntries(pairs) {
|
|
7230
|
+
for (const key of Object.keys(pairs)) {
|
|
7231
|
+
if (!ENTRY_KEY_RE.test(key)) {
|
|
7232
|
+
throw new InvalidCliEntryKeyError(key);
|
|
7233
|
+
}
|
|
7234
|
+
}
|
|
7235
|
+
return pairs;
|
|
7236
|
+
}
|
|
7237
|
+
function parseOverride(source, raw, parse) {
|
|
7238
|
+
try {
|
|
7239
|
+
return parse(raw);
|
|
7240
|
+
} catch (error) {
|
|
7241
|
+
if (error instanceof Error && !(error instanceof CliError)) {
|
|
7242
|
+
throw new UsageError(`Invalid ${source} value "${raw}": ${error.message}.`);
|
|
7243
|
+
}
|
|
7244
|
+
throw error;
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
function buildCliOverride(params) {
|
|
7248
|
+
let categoryPairs = {};
|
|
7249
|
+
if (params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== "") {
|
|
7250
|
+
categoryPairs = parseOverride("AGENT_SHIM_CATEGORY_OVERRIDE", params.env.AGENT_SHIM_CATEGORY_OVERRIDE, parseBoolPairList);
|
|
7251
|
+
}
|
|
7252
|
+
for (const flagValue of params.categoryFlags) {
|
|
7253
|
+
const pair = parseOverride("--category", flagValue, (raw) => {
|
|
7254
|
+
const { key, value } = parsePair(raw);
|
|
7255
|
+
return { key, value: parseBool(value) };
|
|
7256
|
+
});
|
|
7257
|
+
categoryPairs[pair.key] = pair.value;
|
|
7258
|
+
}
|
|
7259
|
+
let entryPairs = {};
|
|
7260
|
+
if (params.env.AGENT_SHIM_ENTRY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_ENTRY_OVERRIDE !== "") {
|
|
7261
|
+
entryPairs = parseOverride("AGENT_SHIM_ENTRY_OVERRIDE", params.env.AGENT_SHIM_ENTRY_OVERRIDE, parseBoolPairList);
|
|
7262
|
+
}
|
|
7263
|
+
for (const entryPath of params.shareFlags) {
|
|
7264
|
+
entryPairs[entryPath] = true;
|
|
7265
|
+
}
|
|
7266
|
+
for (const entryPath of params.hideFlags) {
|
|
7267
|
+
entryPairs[entryPath] = false;
|
|
7268
|
+
}
|
|
7269
|
+
const hasCategories = Object.keys(categoryPairs).length > 0;
|
|
7270
|
+
const hasEntries = Object.keys(entryPairs).length > 0;
|
|
7271
|
+
const hasProvider = params.providerFlag !== void 0 && params.providerFlag !== "";
|
|
7272
|
+
if (!hasCategories && !hasEntries && !hasProvider) {
|
|
7273
|
+
return void 0;
|
|
7274
|
+
}
|
|
7275
|
+
return {
|
|
7276
|
+
...hasCategories ? { categories: toCategoryMap(categoryPairs) } : {},
|
|
7277
|
+
...hasEntries ? { entries: toEntries(entryPairs) } : {},
|
|
7278
|
+
...hasProvider ? { launch: { provider: params.providerFlag } } : {}
|
|
7279
|
+
};
|
|
7280
|
+
}
|
|
7281
|
+
|
|
7282
|
+
// src/launcher/flags.ts
|
|
7283
|
+
import path24 from "node:path";
|
|
7284
|
+
|
|
7285
|
+
// src/headroom/headers.ts
|
|
7286
|
+
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
7287
|
+
const merged = /* @__PURE__ */ new Map();
|
|
7288
|
+
for (const block of existing) {
|
|
7289
|
+
if (block === void 0) {
|
|
7290
|
+
continue;
|
|
7291
|
+
}
|
|
7292
|
+
for (const line of block.split("\n")) {
|
|
7293
|
+
const entry = parseCustomHeaderLine(line);
|
|
7294
|
+
if (entry === void 0) {
|
|
7295
|
+
continue;
|
|
7296
|
+
}
|
|
7297
|
+
merged.set(entry.name, entry.value);
|
|
7298
|
+
}
|
|
7299
|
+
}
|
|
7300
|
+
for (const entry of additions) {
|
|
7301
|
+
merged.set(entry.name, entry.value);
|
|
7302
|
+
}
|
|
7303
|
+
return [...merged].map(([name, value]) => `${name}: ${value}`).join("\n");
|
|
7304
|
+
}
|
|
7305
|
+
function parseCustomHeaderLine(line) {
|
|
7306
|
+
const trimmed = line.trim();
|
|
7307
|
+
if (trimmed === "") {
|
|
7308
|
+
return void 0;
|
|
7309
|
+
}
|
|
7310
|
+
const colon = trimmed.indexOf(":");
|
|
7311
|
+
if (colon <= 0) {
|
|
7312
|
+
return void 0;
|
|
7313
|
+
}
|
|
7314
|
+
return { name: trimmed.slice(0, colon).trim(), value: trimmed.slice(colon + 1).trim() };
|
|
7315
|
+
}
|
|
7316
|
+
|
|
7317
|
+
// src/launcher/flags.ts
|
|
7318
|
+
function resolveLaunchFlags(params) {
|
|
7319
|
+
return {
|
|
7320
|
+
skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
|
|
7321
|
+
remoteControl: params.flags?.remoteControl ?? parseEnvBool("AGENT_SHIM_REMOTE_CONTROL", params.env.AGENT_SHIM_REMOTE_CONTROL) ?? params.cascade?.remoteControl ?? false,
|
|
7322
|
+
headroom: params.flags?.headroom ?? parseEnvBool("AGENT_SHIM_HEADROOM", params.env.AGENT_SHIM_HEADROOM) ?? params.cascade?.headroom ?? false,
|
|
7323
|
+
trackUsage: params.flags?.trackUsage ?? parseEnvBool("AGENT_SHIM_TRACK_USAGE", params.env.AGENT_SHIM_TRACK_USAGE) ?? params.cascade?.trackUsage ?? params.trackUsageDefault ?? false
|
|
7324
|
+
};
|
|
7325
|
+
}
|
|
7326
|
+
function buildFlagArgs(flags) {
|
|
7327
|
+
const args = [];
|
|
7328
|
+
if (flags.skipPermissions) {
|
|
7329
|
+
args.push("--dangerously-skip-permissions");
|
|
7330
|
+
}
|
|
7331
|
+
if (flags.remoteControl) {
|
|
7332
|
+
args.push("--remote-control=");
|
|
7333
|
+
}
|
|
7334
|
+
return args;
|
|
7335
|
+
}
|
|
7336
|
+
function buildArgv(params) {
|
|
7337
|
+
return [...params.toolFlags, ...params.extraFlags, ...params.passthrough];
|
|
7338
|
+
}
|
|
7339
|
+
var LOOPBACK_HOSTS = ["127.0.0.1", "localhost", "::1"];
|
|
7340
|
+
function isLoopbackUrl(baseUrl) {
|
|
7341
|
+
const host = new URL(baseUrl).hostname.replace(/^\[|\]$/g, "");
|
|
7342
|
+
return LOOPBACK_HOSTS.some((loopback) => loopback === host);
|
|
7343
|
+
}
|
|
7344
|
+
function withLoopbackHosts(existing) {
|
|
7345
|
+
const listed = (existing ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry !== "");
|
|
7346
|
+
return [...listed, ...LOOPBACK_HOSTS.filter((host) => !listed.includes(host))].join(",");
|
|
7347
|
+
}
|
|
7348
|
+
function buildEnv(params) {
|
|
7349
|
+
const env = { ...params.baseEnv };
|
|
7350
|
+
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7351
|
+
let providerCustomHeaders;
|
|
7352
|
+
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
7353
|
+
env.CLAUDE_CONFIG_DIR = path24.join(params.identitiesDir, params.resolvedIdentityName);
|
|
7354
|
+
}
|
|
7355
|
+
if (params.provider !== void 0) {
|
|
7356
|
+
const providerEnv = params.provider.definition.env ?? {};
|
|
7357
|
+
providerCustomHeaders = providerEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7358
|
+
env.ANTHROPIC_BASE_URL = params.provider.baseUrl;
|
|
7359
|
+
if (isLoopbackUrl(params.provider.baseUrl)) {
|
|
7360
|
+
env.NO_PROXY = withLoopbackHosts(params.baseEnv.NO_PROXY);
|
|
7361
|
+
env.no_proxy = withLoopbackHosts(params.baseEnv.no_proxy);
|
|
7362
|
+
}
|
|
7363
|
+
env.AGENT_SHIM_PROVIDER = params.provider.definition.displayName;
|
|
7364
|
+
for (const [key, value] of Object.entries(providerEnv)) {
|
|
7365
|
+
env[key] = value;
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7368
|
+
const credential = params.provider?.credential ?? params.identityCredential;
|
|
7369
|
+
if (credential !== void 0) {
|
|
7370
|
+
for (const [variable, value] of Object.entries(credentialVariables(credential.target, credential.token))) {
|
|
7371
|
+
env[variable] = value;
|
|
7372
|
+
}
|
|
7373
|
+
}
|
|
7374
|
+
if (params.headroom !== void 0) {
|
|
7375
|
+
env.HEADROOM_PROXY_URL = `http://127.0.0.1:${String(params.headroom.port)}`;
|
|
7376
|
+
}
|
|
7377
|
+
if (params.frontdoor !== void 0) {
|
|
7378
|
+
env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeaders(
|
|
7379
|
+
[
|
|
7380
|
+
...parentCustomHeaders === void 0 ? [] : [parentCustomHeaders],
|
|
7381
|
+
...providerCustomHeaders === void 0 ? [] : [providerCustomHeaders]
|
|
7382
|
+
],
|
|
7383
|
+
[
|
|
7384
|
+
...params.resolvedIdentityName === void 0 ? [] : [{ name: IDENTITY_HEADER, value: params.resolvedIdentityName }],
|
|
7385
|
+
{ name: SESSION_HEADER, value: params.sessionId },
|
|
7386
|
+
{ name: AUTH_HEADER, value: params.frontdoor.sessionToken },
|
|
7387
|
+
...params.headroom === void 0 ? [] : [
|
|
7388
|
+
{ name: HEADROOM_FLAG_HEADER, value: "1" },
|
|
7389
|
+
{ name: PROJECT_ID_HEADER, value: params.headroom.projectId }
|
|
7390
|
+
]
|
|
7391
|
+
]
|
|
7392
|
+
);
|
|
7393
|
+
env.NODE_EXTRA_CA_CERTS = params.frontdoor.trustBundlePath;
|
|
7394
|
+
if (params.provider === void 0) {
|
|
7395
|
+
env.HTTPS_PROXY = connectProxyUrl(params.frontdoor.connectPort, params.frontdoor.sessionToken);
|
|
7396
|
+
}
|
|
7397
|
+
}
|
|
7398
|
+
return env;
|
|
7399
|
+
}
|
|
7400
|
+
|
|
7401
|
+
// src/launcher/extraFlags.ts
|
|
7402
|
+
function splitExtraFlags(value) {
|
|
7403
|
+
if (value === void 0) {
|
|
7404
|
+
return [];
|
|
7405
|
+
}
|
|
7406
|
+
const trimmed = value.trim();
|
|
7407
|
+
if (trimmed === "") {
|
|
7408
|
+
return [];
|
|
7409
|
+
}
|
|
7410
|
+
return trimmed.split(/\s+/);
|
|
7411
|
+
}
|
|
7412
|
+
|
|
7413
|
+
// src/launcher.ts
|
|
7414
|
+
function resolveIdentityCredential(identity, credential, env, port) {
|
|
7415
|
+
if (port === void 0) {
|
|
7416
|
+
return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
|
|
7417
|
+
}
|
|
7418
|
+
const resolution = resolveCredential({ credential, env, port, subject: `identity ${identity}` });
|
|
7419
|
+
return resolution.ok ? resolution : { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
7420
|
+
}
|
|
7421
|
+
function pickFromPool(decided, params, parsedArgv) {
|
|
7422
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7423
|
+
if (decided.pool === void 0) {
|
|
7424
|
+
return { identityDecision: decided };
|
|
7425
|
+
}
|
|
7426
|
+
const farmRuntime = params.farm;
|
|
7427
|
+
if (farmRuntime === void 0) {
|
|
7428
|
+
log.error(`agent-shim: pool "${decided.pool}" was selected, but this launcher has no farm wired to read usage and pick a member with.`);
|
|
7429
|
+
return proc.exit(1);
|
|
7430
|
+
}
|
|
7431
|
+
const resolution = resolvePoolLaunch({
|
|
7432
|
+
poolName: decided.pool,
|
|
7433
|
+
selectedVia: decided.source,
|
|
7434
|
+
pools: params.pools,
|
|
7435
|
+
paths,
|
|
7436
|
+
fs: fs12,
|
|
7437
|
+
usageFs: farmRuntime.fs,
|
|
7438
|
+
cwd: farmRuntime.cwd,
|
|
7439
|
+
now: farmRuntime.now,
|
|
7440
|
+
sleep: farmRuntime.lock.sleep,
|
|
7441
|
+
passthrough: parsedArgv.rest,
|
|
7442
|
+
wait: parsedArgv.wait === true,
|
|
7443
|
+
log
|
|
7444
|
+
});
|
|
7445
|
+
if (!resolution.ok) {
|
|
7446
|
+
log.error(resolution.message);
|
|
7447
|
+
return proc.exit(1);
|
|
7448
|
+
}
|
|
7449
|
+
return { identityDecision: { ...decided, name: resolution.identity }, poolExplanation: resolution.explanation };
|
|
7450
|
+
}
|
|
7451
|
+
function prepareLaunch(params) {
|
|
7452
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7453
|
+
const { env, argv } = proc;
|
|
7454
|
+
const parsedArgv = parseLauncherArgv(argv);
|
|
7455
|
+
const cliOverride = buildCliOverride({
|
|
7456
|
+
env,
|
|
7457
|
+
categoryFlags: parsedArgv.categoryFlags,
|
|
7458
|
+
shareFlags: parsedArgv.shareFlags,
|
|
7459
|
+
hideFlags: parsedArgv.hideFlags
|
|
7460
|
+
});
|
|
7461
|
+
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
7462
|
+
const decidedIdentity = decideIdentity({
|
|
7463
|
+
env,
|
|
7464
|
+
argv0Identity: parsedArgv.identity,
|
|
7465
|
+
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
7466
|
+
readActiveIdentityFile: () => {
|
|
7467
|
+
const raw = fs12.readFileUtf8(paths.activeIdentityFile);
|
|
7468
|
+
if (raw === void 0) {
|
|
7469
|
+
return void 0;
|
|
7470
|
+
}
|
|
7471
|
+
const trimmed = raw.trim();
|
|
7472
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
7473
|
+
}
|
|
7474
|
+
});
|
|
7475
|
+
const { identityDecision, poolExplanation } = pickFromPool(decidedIdentity, params, parsedArgv);
|
|
7476
|
+
const farmIdentity = configDirEscapeHatchApplies ? void 0 : identityDecision.name;
|
|
7477
|
+
const farm = farmIdentity === void 0 ? void 0 : params.farm;
|
|
7478
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7479
|
+
let recovery;
|
|
7480
|
+
try {
|
|
7481
|
+
recovery = recoverFarm({
|
|
7482
|
+
fs: farm.fs,
|
|
7483
|
+
identitiesDir: paths.identitiesDir,
|
|
7484
|
+
identity: farmIdentity,
|
|
7485
|
+
now: farm.now,
|
|
7486
|
+
lock: farm.lock,
|
|
7487
|
+
classification: farm.classification
|
|
7488
|
+
});
|
|
7489
|
+
} catch (error) {
|
|
7490
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7491
|
+
log.error(error.message);
|
|
7492
|
+
proc.exit(1);
|
|
7493
|
+
}
|
|
7494
|
+
throw error;
|
|
7495
|
+
}
|
|
7496
|
+
for (const diagnostic of recoveryDiagnostics(recovery, farmIdentity)) {
|
|
7497
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
let loadedIdentity;
|
|
7501
|
+
if (identityDecision.name !== void 0) {
|
|
7502
|
+
loadedIdentity = loadIdentity(paths.identitiesDir, identityDecision.name, fs12);
|
|
7503
|
+
if (loadedIdentity === void 0) {
|
|
7504
|
+
log.error(
|
|
7505
|
+
`agent-shim: no identity named "${identityDecision.name}" (selected via ${identityDecision.source}). Run \`agent-shim identity add ${identityDecision.name}\` first.`
|
|
7506
|
+
);
|
|
7507
|
+
proc.exit(1);
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
const configProfileDecision = decideConfigProfile({
|
|
7511
|
+
env,
|
|
7512
|
+
cliFlagConfigProfile: params.cliFlagConfigProfile ?? parsedArgv.configProfile,
|
|
7513
|
+
directoryRuleConfigProfile: params.directoryRuleConfigProfile,
|
|
7514
|
+
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
7515
|
+
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
7516
|
+
});
|
|
7517
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path25.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
7518
|
+
log.error(
|
|
7519
|
+
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
7520
|
+
);
|
|
7521
|
+
proc.exit(1);
|
|
7522
|
+
}
|
|
7523
|
+
const farmContext = farm !== void 0 && farmIdentity !== void 0 ? { farm, identity: farmIdentity, cascade: farm.loadCascade(configProfileDecision.name, cliOverride) } : void 0;
|
|
7524
|
+
const provider = parsedArgv.provider === false ? void 0 : resolveProvider({
|
|
7525
|
+
paths,
|
|
7526
|
+
port: fs12,
|
|
7527
|
+
env,
|
|
7528
|
+
...params.credentials === void 0 ? {} : { credentials: params.credentials },
|
|
7529
|
+
...parsedArgv.provider === void 0 ? {} : { cliProvider: parsedArgv.provider },
|
|
7530
|
+
...farmContext === void 0 ? {} : { cascade: farmContext.cascade }
|
|
7531
|
+
});
|
|
7532
|
+
if (provider !== void 0 && !provider.ok) {
|
|
7533
|
+
log.error(provider.message);
|
|
7534
|
+
proc.exit(provider.status);
|
|
7535
|
+
}
|
|
7536
|
+
const resolvedProvider = provider?.ok === true ? provider.provider : void 0;
|
|
7537
|
+
for (const warning of provider?.ok === true ? provider.warnings : []) {
|
|
7538
|
+
log.warn(warning);
|
|
7539
|
+
}
|
|
7540
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7541
|
+
try {
|
|
7542
|
+
const snapshot = readUsageSnapshot(farm.fs, paths.usageSnapshotsDir, farmIdentity);
|
|
7543
|
+
for (const warning of quotaWarnings(snapshot, resolvedProvider?.name ?? ANTHROPIC_PROVIDER, farm.now())) {
|
|
7544
|
+
log.warn(warning);
|
|
7545
|
+
}
|
|
7546
|
+
} catch (error) {
|
|
7547
|
+
if (!(error instanceof UsageSnapshotError)) {
|
|
7548
|
+
throw error;
|
|
7549
|
+
}
|
|
7550
|
+
log.warn(`agent-shim: ${error.message}`);
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
const identityCredentialBlock = loadedIdentity?.config.credential;
|
|
7554
|
+
const identityResolution = resolvedProvider === void 0 && farmIdentity !== void 0 && identityCredentialBlock !== void 0 ? resolveIdentityCredential(farmIdentity, identityCredentialBlock, env, params.credentials) : void 0;
|
|
7555
|
+
if (identityResolution !== void 0 && !identityResolution.ok) {
|
|
7556
|
+
log.error(identityResolution.message);
|
|
7557
|
+
proc.exit(identityResolution.status);
|
|
7558
|
+
}
|
|
7559
|
+
const identityCredential = identityResolution?.ok === true ? identityResolution.credential : void 0;
|
|
7560
|
+
for (const warning of identityCredential?.warnings ?? []) {
|
|
7561
|
+
log.warn(warning);
|
|
7562
|
+
}
|
|
7563
|
+
const guardResult = evaluateAmbientCredentialGuard({
|
|
7564
|
+
env,
|
|
7565
|
+
allowAmbientCredential: loadedIdentity?.config.allowAmbientCredential ?? false,
|
|
7566
|
+
allowAmbientCredentialOverride: parseEnvBool("AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL", env.AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL) === true,
|
|
7567
|
+
identityName: identityDecision.name,
|
|
7568
|
+
providerSelected: resolvedProvider !== void 0,
|
|
7569
|
+
...identityCredential === void 0 ? {} : { injectedCredential: { variable: CREDENTIAL_TARGET_VARS[identityCredential.target], token: identityCredential.token } }
|
|
7570
|
+
});
|
|
7571
|
+
if (!guardResult.ok) {
|
|
7572
|
+
log.error(guardResult.message);
|
|
7573
|
+
proc.exit(1);
|
|
7574
|
+
}
|
|
7575
|
+
log.info(
|
|
7576
|
+
`agent-shim: identity ${identityDecision.name ?? "(none)"} (${identityDecision.source}${poolExplanation === void 0 ? "" : `, ${poolExplanation}`}), config profile ${configProfileDecision.name ?? "(none)"} (${configProfileDecision.source})` + (resolvedProvider === void 0 ? "" : `, provider ${resolvedProvider.name} (credential ${resolvedProvider.credential.target} from ${describeSource(resolvedProvider.credential.source)})`) + (identityCredential === void 0 ? "" : `, identity credential ${identityCredential.target} from ${describeSource(identityCredential.source)}`)
|
|
7577
|
+
);
|
|
7578
|
+
let cascadeLaunch;
|
|
7579
|
+
if (farmContext !== void 0) {
|
|
7580
|
+
const { farm: resyncFarmRuntime, identity: resyncIdentity, cascade } = farmContext;
|
|
7581
|
+
let result;
|
|
7582
|
+
try {
|
|
7583
|
+
result = resyncFarm({
|
|
7584
|
+
fs: resyncFarmRuntime.fs,
|
|
7585
|
+
identitiesDir: paths.identitiesDir,
|
|
7586
|
+
identity: resyncIdentity,
|
|
7587
|
+
...configProfileDecision.name === void 0 ? {} : { configProfile: configProfileDecision.name },
|
|
7588
|
+
claudeHome: resyncFarmRuntime.claudeHome,
|
|
7589
|
+
home: resyncFarmRuntime.home,
|
|
7590
|
+
cwd: resyncFarmRuntime.cwd,
|
|
7591
|
+
env,
|
|
7592
|
+
...resyncFarmRuntime.branch === void 0 ? {} : { branch: resyncFarmRuntime.branch },
|
|
7593
|
+
...resyncFarmRuntime.branchDetached === void 0 ? {} : { branchDetached: resyncFarmRuntime.branchDetached },
|
|
7594
|
+
cascade,
|
|
7595
|
+
classification: resyncFarmRuntime.classification,
|
|
7596
|
+
now: resyncFarmRuntime.now,
|
|
7597
|
+
uniqueSuffix: resyncFarmRuntime.uniqueSuffix,
|
|
7598
|
+
lock: resyncFarmRuntime.lock
|
|
7599
|
+
});
|
|
7600
|
+
} catch (error) {
|
|
7601
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7602
|
+
log.error(error.message);
|
|
7603
|
+
proc.exit(1);
|
|
7604
|
+
}
|
|
7605
|
+
throw error;
|
|
7606
|
+
}
|
|
7607
|
+
for (const diagnostic of result.diagnostics) {
|
|
7608
|
+
if (diagnostic.severity === "error") {
|
|
7609
|
+
log.error(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7610
|
+
} else if (diagnostic.severity === "warning") {
|
|
7611
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7612
|
+
}
|
|
7613
|
+
}
|
|
7614
|
+
log.info(
|
|
7615
|
+
result.noOp ? `agent-shim: farm at ${result.farmRoot} already matches the resolved cascade` : `agent-shim: farm at ${result.farmRoot} resynced (${String(result.manifest.links.length)} link(s), ${String(result.manifest.materialised.length)} built director(ies)${result.adopted.length === 0 ? "" : `, ${String(result.adopted.length)} adopted into ${resyncFarmRuntime.claudeHome}`})`
|
|
7616
|
+
);
|
|
7617
|
+
cascadeLaunch = result.resolved.flattened.launch;
|
|
7618
|
+
} else if (params.farm !== void 0) {
|
|
7619
|
+
cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
|
|
7620
|
+
}
|
|
7621
|
+
const discovered = params.resolveClaudeBinary();
|
|
7622
|
+
const resolvedFlags = resolveLaunchFlags({
|
|
7623
|
+
env,
|
|
7624
|
+
// A pool pick reads the usage the front door records, so a pool launch records its own unless something says otherwise.
|
|
7625
|
+
...poolExplanation === void 0 ? {} : { trackUsageDefault: true },
|
|
7626
|
+
...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch },
|
|
7627
|
+
flags: {
|
|
7628
|
+
...parsedArgv.skipPermissions === void 0 ? {} : { skipPermissions: parsedArgv.skipPermissions },
|
|
7629
|
+
...parsedArgv.remoteControl === void 0 ? {} : { remoteControl: parsedArgv.remoteControl },
|
|
7630
|
+
...parsedArgv.headroom === void 0 ? {} : { headroom: parsedArgv.headroom },
|
|
7631
|
+
...parsedArgv.trackUsage === void 0 ? {} : { trackUsage: parsedArgv.trackUsage }
|
|
7632
|
+
}
|
|
7633
|
+
});
|
|
7634
|
+
const frontDoorPort = params.frontdoor;
|
|
7635
|
+
const frontDoorEngaged = resolvedProvider !== void 0 || resolvedFlags.headroom || resolvedFlags.trackUsage;
|
|
7636
|
+
if (frontDoorEngaged && frontDoorPort === void 0) {
|
|
7637
|
+
log.error("agent-shim: this launch routes through the front-door daemon, but this launcher has no front-door port wired; refusing to launch without it.");
|
|
7638
|
+
proc.exit(1);
|
|
7639
|
+
}
|
|
7640
|
+
const frontDoor = frontDoorEngaged && frontDoorPort !== void 0 ? frontDoorPort.ensure(env.NODE_EXTRA_CA_CERTS) : void 0;
|
|
7641
|
+
if (frontDoor?.trustWarning !== void 0) {
|
|
7642
|
+
log.warn(frontDoor.trustWarning);
|
|
7643
|
+
}
|
|
7644
|
+
let routedProvider;
|
|
7645
|
+
if (resolvedProvider !== void 0 && frontDoor !== void 0) {
|
|
7646
|
+
routedProvider = { ...resolvedProvider, baseUrl: providerBaseUrl(frontDoor.port, resolvedProvider.name) };
|
|
7647
|
+
}
|
|
7648
|
+
let headroom;
|
|
7649
|
+
const headroomPort = params.headroom;
|
|
7650
|
+
if (resolvedFlags.headroom && headroomPort !== void 0) {
|
|
7651
|
+
headroom = headroomPort.ensure();
|
|
7652
|
+
const via = resolvedProvider === void 0 ? `OAuth via the door's CONNECT surface on 127.0.0.1:${String(frontDoor?.connectPort ?? 0)}` : `provider ${resolvedProvider.name}`;
|
|
7653
|
+
log.info(`agent-shim: routing through the front door on 127.0.0.1:${String(frontDoor?.port ?? 0)} with headroom on 127.0.0.1:${String(headroom.port)} (${via}, project ${headroom.projectId})`);
|
|
7654
|
+
} else if (resolvedFlags.headroom) {
|
|
7655
|
+
log.error("agent-shim: headroom routing was requested but this launcher has no headroom port wired; refusing to launch without it.");
|
|
7656
|
+
proc.exit(1);
|
|
7657
|
+
} else if (frontDoor !== void 0) {
|
|
7658
|
+
log.info(`agent-shim: routing through the front door on 127.0.0.1:${String(frontDoor.port)}${resolvedProvider === void 0 ? ` (OAuth via the door's CONNECT surface on 127.0.0.1:${String(frontDoor.connectPort)}, usage recorded)` : ` (provider ${resolvedProvider.name})`}`);
|
|
7659
|
+
}
|
|
7660
|
+
const frontDoorRelease = frontDoor === void 0 ? void 0 : frontDoorPort?.release;
|
|
7661
|
+
const finalArgv = buildArgv({
|
|
7662
|
+
toolFlags: buildFlagArgs(resolvedFlags),
|
|
7663
|
+
extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
|
|
7664
|
+
passthrough: parsedArgv.rest
|
|
7665
|
+
});
|
|
7666
|
+
const finalEnv = buildEnv({
|
|
7667
|
+
baseEnv: env,
|
|
7668
|
+
configDirEscapeHatch: configDirEscapeHatchApplies,
|
|
7669
|
+
resolvedIdentityName: identityDecision.name,
|
|
7670
|
+
identitiesDir: paths.identitiesDir,
|
|
7671
|
+
...routedProvider === void 0 ? {} : { provider: routedProvider },
|
|
7672
|
+
...identityCredential === void 0 ? {} : { identityCredential },
|
|
7673
|
+
...frontDoor === void 0 ? {} : { frontdoor: frontDoor },
|
|
7674
|
+
...headroom === void 0 ? {} : { headroom },
|
|
7675
|
+
sessionId: randomUUID4()
|
|
7676
|
+
});
|
|
7677
|
+
const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
|
|
7678
|
+
const release = () => {
|
|
7679
|
+
for (const registered of releases) {
|
|
7680
|
+
registered();
|
|
7681
|
+
}
|
|
7682
|
+
};
|
|
7683
|
+
return { bin: discovered.path, args: finalArgv, env: finalEnv, release };
|
|
7684
|
+
}
|
|
7685
|
+
|
|
7686
|
+
// src/launchWiring.ts
|
|
7687
|
+
import os5 from "node:os";
|
|
7688
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
7689
|
+
|
|
7690
|
+
// src/realCredentialCache.ts
|
|
7691
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
7692
|
+
import fs10 from "node:fs";
|
|
7693
|
+
import path26 from "node:path";
|
|
7694
|
+
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7695
|
+
var CACHE_DIR_MODE = 448;
|
|
7696
|
+
var CACHE_FILE_MODE = 384;
|
|
7697
|
+
function cacheFileNameFor(owner) {
|
|
7698
|
+
return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
|
|
7699
|
+
}
|
|
7700
|
+
function parseEntry(raw) {
|
|
7701
|
+
let json;
|
|
7702
|
+
try {
|
|
7703
|
+
json = JSON.parse(raw);
|
|
7704
|
+
} catch {
|
|
7705
|
+
return void 0;
|
|
7706
|
+
}
|
|
7707
|
+
const parsed = CachedCredentialSchema.safeParse(json);
|
|
7708
|
+
return parsed.success ? parsed.data : void 0;
|
|
7709
|
+
}
|
|
7710
|
+
function requireKeychain() {
|
|
7711
|
+
if (process.platform !== "darwin") {
|
|
7712
|
+
throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
|
|
7713
|
+
}
|
|
7714
|
+
}
|
|
7715
|
+
function security(args, stdin) {
|
|
7716
|
+
const result = spawnSync2("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
|
|
7717
|
+
return { status: result.status, stdout: result.stdout };
|
|
7718
|
+
}
|
|
7719
|
+
function createRealCredentialCache(root) {
|
|
7720
|
+
const dir = path26.join(root, "credential-cache");
|
|
7721
|
+
const filePath = (owner) => path26.join(dir, cacheFileNameFor(owner));
|
|
7722
|
+
return {
|
|
7723
|
+
read(store, owner) {
|
|
7724
|
+
if (store === "file") {
|
|
7725
|
+
try {
|
|
7726
|
+
return parseEntry(fs10.readFileSync(filePath(owner), "utf8"));
|
|
7727
|
+
} catch {
|
|
7728
|
+
return void 0;
|
|
7729
|
+
}
|
|
7730
|
+
}
|
|
7731
|
+
requireKeychain();
|
|
7732
|
+
const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
|
|
7733
|
+
return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
|
|
7734
|
+
},
|
|
7735
|
+
write(store, owner, entry) {
|
|
7736
|
+
const serialised = JSON.stringify(entry);
|
|
7737
|
+
if (store === "file") {
|
|
7738
|
+
fs10.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
|
|
7739
|
+
fs10.chmodSync(dir, CACHE_DIR_MODE);
|
|
7740
|
+
const target = filePath(owner);
|
|
7741
|
+
const temp = `${target}.${String(process.pid)}.tmp`;
|
|
7742
|
+
fs10.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
|
|
7743
|
+
fs10.renameSync(temp, target);
|
|
7744
|
+
return;
|
|
7745
|
+
}
|
|
7746
|
+
requireKeychain();
|
|
7747
|
+
const encoded = Buffer.from(serialised, "utf8").toString("base64");
|
|
7748
|
+
const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
|
|
7749
|
+
`);
|
|
7750
|
+
if (result.status !== 0) {
|
|
7751
|
+
throw new Error(`could not write the keychain credential cache for ${owner}`);
|
|
7752
|
+
}
|
|
7753
|
+
},
|
|
7754
|
+
remove(store, owner) {
|
|
7755
|
+
if (store === "file") {
|
|
7756
|
+
fs10.rmSync(filePath(owner), { force: true });
|
|
7757
|
+
return;
|
|
7758
|
+
}
|
|
7759
|
+
requireKeychain();
|
|
7760
|
+
security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
|
|
7761
|
+
}
|
|
7762
|
+
};
|
|
7763
|
+
}
|
|
7764
|
+
function realCredentialCacheEnv(paths) {
|
|
7765
|
+
return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
|
|
7766
|
+
}
|
|
7767
|
+
|
|
7768
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7769
|
+
import fs11 from "node:fs";
|
|
7770
|
+
|
|
7771
|
+
// src/frontdoor/probe.ts
|
|
7772
|
+
import { Worker } from "node:worker_threads";
|
|
7773
|
+
var PROBE_TIMEOUT_MS = 2e3;
|
|
7774
|
+
var PROBE_WAIT_MARGIN_MS = 1e3;
|
|
7775
|
+
var REASON_BYTES = 1024;
|
|
7776
|
+
var HEADER_SLOTS = 2;
|
|
7777
|
+
var INT32_BYTES = 4;
|
|
7778
|
+
var VERDICT_PENDING = 0;
|
|
7779
|
+
var VERDICT_OK = 1;
|
|
7780
|
+
var VERDICT_FAILED = 2;
|
|
7781
|
+
var PROBE_SOURCE = `
|
|
7782
|
+
const { workerData } = require("node:worker_threads");
|
|
7783
|
+
const https = require("node:https");
|
|
7784
|
+
const { port, ca, timeoutMs, shared, ok, failed, headerSlots } = workerData;
|
|
7785
|
+
const header = new Int32Array(shared, 0, headerSlots);
|
|
7786
|
+
const reason = new Uint8Array(shared, headerSlots * 4);
|
|
7787
|
+
let done = false;
|
|
7788
|
+
const finish = (verdict, message) => {
|
|
7789
|
+
if (done) return;
|
|
7790
|
+
done = true;
|
|
7791
|
+
const bytes = Buffer.from(String(message), "utf8").subarray(0, reason.length);
|
|
7792
|
+
reason.set(bytes);
|
|
7793
|
+
Atomics.store(header, 1, bytes.length);
|
|
7794
|
+
Atomics.store(header, 0, verdict);
|
|
7795
|
+
Atomics.notify(header, 0);
|
|
7796
|
+
};
|
|
7797
|
+
const request = https.get({ host: "127.0.0.1", port, path: "/healthz", ca: [ca], agent: false }, (response) => {
|
|
7798
|
+
let body = "";
|
|
7799
|
+
response.setEncoding("utf8");
|
|
7800
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
7801
|
+
response.on("end", () => {
|
|
7802
|
+
if (response.statusCode === 200 && body === "ok") finish(ok, "");
|
|
7803
|
+
else finish(failed, "the listener answered its health probe with HTTP " + String(response.statusCode));
|
|
7804
|
+
});
|
|
7805
|
+
response.on("error", (error) => finish(failed, error.message));
|
|
7806
|
+
});
|
|
7807
|
+
// A hard deadline over the whole exchange, handshake included: a listener that accepts the connection and then says nothing must still fail the probe.
|
|
7808
|
+
setTimeout(() => { request.destroy(new Error("no answer within " + String(timeoutMs) + "ms")); }, timeoutMs);
|
|
7809
|
+
request.on("error", (error) => finish(failed, (error.code ? error.code + ": " : "") + error.message));
|
|
7810
|
+
`;
|
|
7811
|
+
function probeFrontDoorSync(port, caPem) {
|
|
7812
|
+
const shared = new SharedArrayBuffer(HEADER_SLOTS * INT32_BYTES + REASON_BYTES);
|
|
7813
|
+
const header = new Int32Array(shared, 0, HEADER_SLOTS);
|
|
7814
|
+
const worker = new Worker(PROBE_SOURCE, {
|
|
7815
|
+
eval: true,
|
|
7816
|
+
workerData: { port, ca: caPem, timeoutMs: PROBE_TIMEOUT_MS, shared, ok: VERDICT_OK, failed: VERDICT_FAILED, headerSlots: HEADER_SLOTS }
|
|
7817
|
+
});
|
|
7818
|
+
worker.unref();
|
|
7819
|
+
try {
|
|
7820
|
+
const waited = Atomics.wait(header, 0, VERDICT_PENDING, PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS);
|
|
7821
|
+
const verdict = Atomics.load(header, 0);
|
|
7822
|
+
if (waited === "timed-out" || verdict === VERDICT_PENDING) {
|
|
7823
|
+
return { ok: false, reason: `the probe did not finish within ${String(PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS)}ms` };
|
|
7824
|
+
}
|
|
7825
|
+
if (verdict === VERDICT_OK) {
|
|
7826
|
+
return { ok: true };
|
|
7827
|
+
}
|
|
7828
|
+
const length = Atomics.load(header, 1);
|
|
7829
|
+
return { ok: false, reason: Buffer.from(new Uint8Array(shared, HEADER_SLOTS * INT32_BYTES, length)).toString("utf8") };
|
|
7830
|
+
} finally {
|
|
7831
|
+
void worker.terminate();
|
|
7832
|
+
}
|
|
7833
|
+
}
|
|
7834
|
+
|
|
7835
|
+
// src/frontdoor/trust.ts
|
|
7836
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
7837
|
+
import path27 from "node:path";
|
|
7838
|
+
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
7839
|
+
function resolveTrustBundle(params) {
|
|
7840
|
+
const { caCertFile, bundlesDir, inherited, fs: fs12 } = params;
|
|
7841
|
+
if (inherited === void 0 || inherited === "" || path27.resolve(inherited) === path27.resolve(caCertFile)) {
|
|
7842
|
+
return { path: caCertFile };
|
|
7843
|
+
}
|
|
7844
|
+
const caPem = fs12.readFileUtf8(caCertFile);
|
|
7845
|
+
let inheritedPem;
|
|
7846
|
+
try {
|
|
7847
|
+
inheritedPem = fs12.readFileUtf8(inherited);
|
|
7848
|
+
} catch (error) {
|
|
7849
|
+
return {
|
|
7850
|
+
path: caCertFile,
|
|
7851
|
+
warning: `agent-shim: NODE_EXTRA_CA_CERTS names ${inherited}, which could not be read (${error instanceof Error ? error.message : String(error)}); the child trusts agent-shim's front-door CA on top of the system store, and nothing from that file`
|
|
7852
|
+
};
|
|
7853
|
+
}
|
|
7854
|
+
if (inheritedPem.includes(caPem.trim())) {
|
|
7855
|
+
return { path: inherited };
|
|
7856
|
+
}
|
|
7857
|
+
const combined = `${inheritedPem.trimEnd()}
|
|
7858
|
+
${caPem}`;
|
|
7859
|
+
const bundle = path27.join(bundlesDir, `${createHash5("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
|
|
7860
|
+
if (!fs12.exists(bundle)) {
|
|
7861
|
+
fs12.mkdirp(bundlesDir);
|
|
7862
|
+
fs12.writeFileAtomic(bundle, combined);
|
|
7863
|
+
}
|
|
7864
|
+
return { path: bundle };
|
|
7865
|
+
}
|
|
7866
|
+
|
|
7867
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7868
|
+
var realTrustBundleFs = {
|
|
7869
|
+
readFileUtf8: (file) => fs11.readFileSync(file, "utf8"),
|
|
7870
|
+
exists: (file) => fs11.existsSync(file),
|
|
7871
|
+
mkdirp: (dir) => {
|
|
7872
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
7873
|
+
},
|
|
7874
|
+
writeFileAtomic: (file, contents) => {
|
|
7875
|
+
realFarmFs.writeFilePrivate(file, contents);
|
|
7876
|
+
}
|
|
7877
|
+
};
|
|
7878
|
+
function realFrontDoorPort(paths, spawnDaemon) {
|
|
7879
|
+
return {
|
|
7880
|
+
ensure: (inheritedExtraCaCerts) => {
|
|
7881
|
+
const up = ensureFrontDoor({
|
|
7882
|
+
paths,
|
|
7883
|
+
launcherPid: process.pid,
|
|
7884
|
+
ports: {
|
|
7885
|
+
fs: realFarmFs,
|
|
7886
|
+
isRunning: realIsProcessRunning,
|
|
7887
|
+
now: () => Date.now(),
|
|
7888
|
+
sleep: realSleepSync,
|
|
7889
|
+
spawnSupervisor: (layout) => spawnDaemon(layout, "__frontdoor-supervisor", layout.frontdoorLogPath),
|
|
7890
|
+
stopSupervisor: (pid) => {
|
|
7891
|
+
process.kill(pid, "SIGTERM");
|
|
7892
|
+
},
|
|
7893
|
+
// Read fresh for each probe: a replacement supervisor may have regenerated an unparseable CA, and the probe must trust exactly what the serving door's leaf chains to.
|
|
7894
|
+
verifyListener: (port) => probeFrontDoorSync(port, fs11.readFileSync(paths.frontdoorCaCertFile, "utf8"))
|
|
7895
|
+
}
|
|
7896
|
+
});
|
|
7897
|
+
const trust = resolveTrustBundle({ caCertFile: paths.frontdoorCaCertFile, bundlesDir: paths.frontdoorCaBundlesDir, inherited: inheritedExtraCaCerts, fs: realTrustBundleFs });
|
|
7898
|
+
return {
|
|
7899
|
+
port: up.port,
|
|
7900
|
+
connectPort: up.connectPort,
|
|
7901
|
+
trustBundlePath: trust.path,
|
|
7902
|
+
...trust.warning === void 0 ? {} : { trustWarning: trust.warning },
|
|
7903
|
+
sessionToken: up.token
|
|
7904
|
+
};
|
|
7905
|
+
},
|
|
7906
|
+
release: () => {
|
|
7907
|
+
removeFrontDoorSession(realFarmFs, paths.frontdoorSessionsDir, process.pid);
|
|
7908
|
+
}
|
|
7909
|
+
};
|
|
7910
|
+
}
|
|
7911
|
+
|
|
7912
|
+
// src/launchWiring.ts
|
|
7913
|
+
function buildFarmRuntime(paths, cwd) {
|
|
7914
|
+
const home = os5.homedir();
|
|
7915
|
+
const read = cosmiconfigReader();
|
|
7916
|
+
const classification = loadClassification(paths);
|
|
7917
|
+
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
7918
|
+
const selections = readDirectorySelections(loaded);
|
|
7919
|
+
const git = resolveGitBranch(realRunPort, cwd);
|
|
7920
|
+
return {
|
|
7921
|
+
runtime: {
|
|
7922
|
+
fs: realFarmFs,
|
|
7923
|
+
claudeHome: resolveClaudeHome(),
|
|
7924
|
+
home,
|
|
7925
|
+
cwd,
|
|
7926
|
+
...git.branch === void 0 ? {} : { branch: git.branch },
|
|
7927
|
+
...git.branchDetached === void 0 ? {} : { branchDetached: git.branchDetached },
|
|
7928
|
+
classification,
|
|
7929
|
+
loadCascade: (baseConfigProfile, cliOverride) => loadCascadeInput({
|
|
7930
|
+
paths,
|
|
7931
|
+
home,
|
|
7932
|
+
cwd,
|
|
7933
|
+
read,
|
|
7934
|
+
...baseConfigProfile === void 0 ? {} : { baseConfigProfile },
|
|
7935
|
+
...cliOverride === void 0 ? {} : { cliOverride }
|
|
7936
|
+
}).input,
|
|
7937
|
+
now: () => Date.now(),
|
|
7938
|
+
uniqueSuffix: `${String(process.pid)}.${randomUUID5()}`,
|
|
7939
|
+
// Zombie-aware on purpose: a previous launcher that crashed out of a resync without releasing the lock may sit unreaped, still answering signal 0 as alive, and must read as a dead holder so this launch takes the lock over instead of timing out.
|
|
7940
|
+
lock: { pid: process.pid, isRunning: realIsProcessRunning, sleep: realSleepSync }
|
|
7941
|
+
},
|
|
7942
|
+
...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
|
|
7943
|
+
...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
|
|
7944
|
+
...loaded.globalConfig?.defaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: loaded.globalConfig.defaultConfigProfile },
|
|
7945
|
+
...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
|
|
7946
|
+
};
|
|
7947
|
+
}
|
|
7948
|
+
function realPrepareLaunchParams(paths, options) {
|
|
7949
|
+
const { farm } = options;
|
|
7950
|
+
return {
|
|
7951
|
+
paths,
|
|
7952
|
+
fs: realFsPort,
|
|
7953
|
+
proc: options.proc,
|
|
7954
|
+
log: options.log,
|
|
7955
|
+
resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
|
|
7956
|
+
farm: farm.runtime,
|
|
7957
|
+
headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
|
|
7958
|
+
frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
|
|
7959
|
+
credentials: { ...realCredentialPort, cache: realCredentialCacheEnv(paths) },
|
|
7960
|
+
...farm.directoryIdentity === void 0 ? {} : { directoryPinnedIdentity: farm.directoryIdentity },
|
|
7961
|
+
...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
|
|
7962
|
+
...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
|
|
7963
|
+
...farm.pools === void 0 ? {} : { pools: farm.pools },
|
|
7964
|
+
...options.allowMissingConfigProfile === void 0 ? {} : { allowMissingConfigProfile: options.allowMissingConfigProfile }
|
|
7965
|
+
};
|
|
7966
|
+
}
|
|
7967
|
+
var LaunchRefusedError = class extends CliError {
|
|
7968
|
+
exitCode;
|
|
7969
|
+
constructor(message, exitCode) {
|
|
7970
|
+
super(message);
|
|
7971
|
+
this.name = "LaunchRefusedError";
|
|
7972
|
+
this.exitCode = exitCode;
|
|
7973
|
+
}
|
|
7974
|
+
};
|
|
7975
|
+
function prepareClaudeLaunch(options) {
|
|
7976
|
+
const paths = options.paths ?? resolveLayoutPaths();
|
|
7977
|
+
const refusals = [];
|
|
7978
|
+
const log = {
|
|
7979
|
+
info: () => void 0,
|
|
7980
|
+
warn: () => void 0,
|
|
7981
|
+
error: (message) => {
|
|
7982
|
+
refusals.push(message);
|
|
7983
|
+
}
|
|
7984
|
+
};
|
|
7985
|
+
const proc = {
|
|
7986
|
+
env: options.env,
|
|
7987
|
+
argv: options.argv,
|
|
7988
|
+
exit: (code) => {
|
|
7989
|
+
throw new LaunchRefusedError(refusals.join("\n") || `the launch was refused (exit ${String(code)})`, code);
|
|
7990
|
+
}
|
|
7991
|
+
};
|
|
7992
|
+
const spawnDaemon = options.agentShim === void 0 ? () => {
|
|
7993
|
+
throw new LaunchRefusedError("this launch routes through the front door or headroom, which need `agentShim`, the path of the agent-shim executable, to start their daemons", 1);
|
|
7994
|
+
} : spawnDaemonThrough(options.agentShim);
|
|
7995
|
+
return prepareLaunch(realPrepareLaunchParams(paths, { proc, log, spawnDaemon, farm: buildFarmRuntime(paths, options.cwd) }));
|
|
7996
|
+
}
|
|
6636
7997
|
export {
|
|
6637
7998
|
AMBIENT_CREDENTIAL_VARS,
|
|
6638
7999
|
CONNECT_INTERCEPT_HOST,
|
|
@@ -6664,6 +8025,7 @@ export {
|
|
|
6664
8025
|
InvalidCategoryNameError,
|
|
6665
8026
|
InvalidIdentityNameError,
|
|
6666
8027
|
InvalidProviderNameError,
|
|
8028
|
+
LaunchRefusedError,
|
|
6667
8029
|
LegacyProviderFileError,
|
|
6668
8030
|
PoolNameSchema,
|
|
6669
8031
|
PoolNotFoundError,
|
|
@@ -6714,6 +8076,8 @@ export {
|
|
|
6714
8076
|
listUsageSnapshots,
|
|
6715
8077
|
mintLeaf,
|
|
6716
8078
|
parseConnectTarget,
|
|
8079
|
+
prepareClaudeLaunch,
|
|
8080
|
+
prepareLaunch,
|
|
6717
8081
|
profileExists,
|
|
6718
8082
|
providerExists,
|
|
6719
8083
|
readActiveIdentity,
|