agent-shim 7.12.0 → 7.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +445 -424
- package/dist/index.cjs +1471 -140
- package/dist/index.mjs +1468 -140
- 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/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/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1626,8 +1626,8 @@ var FarmManifestSchema = z2.strictObject({
|
|
|
1626
1626
|
materialised: z2.array(z2.string()),
|
|
1627
1627
|
links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
|
|
1628
1628
|
});
|
|
1629
|
-
function readFarmManifest(
|
|
1630
|
-
const raw =
|
|
1629
|
+
function readFarmManifest(fs12, farmRoot) {
|
|
1630
|
+
const raw = fs12.readFileUtf8(path6.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs12.readFileUtf8(path6.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1631
1631
|
if (raw === void 0) {
|
|
1632
1632
|
return void 0;
|
|
1633
1633
|
}
|
|
@@ -1685,9 +1685,9 @@ function buildEntryFacts(params) {
|
|
|
1685
1685
|
entries
|
|
1686
1686
|
};
|
|
1687
1687
|
}
|
|
1688
|
-
function listSubtree(
|
|
1688
|
+
function listSubtree(fs12, root, rel) {
|
|
1689
1689
|
const absolute = path6.join(root, rel);
|
|
1690
|
-
const stat =
|
|
1690
|
+
const stat = fs12.lstat(absolute);
|
|
1691
1691
|
if (stat === void 0) {
|
|
1692
1692
|
return [];
|
|
1693
1693
|
}
|
|
@@ -1695,10 +1695,10 @@ function listSubtree(fs10, root, rel) {
|
|
|
1695
1695
|
return [{ rel, kind: "symlink" }];
|
|
1696
1696
|
}
|
|
1697
1697
|
if (stat.kind === "dir") {
|
|
1698
|
-
const children = [...
|
|
1698
|
+
const children = [...fs12.readdir(absolute)].sort().flatMap((name) => listSubtree(fs12, root, `${rel}/${name}`));
|
|
1699
1699
|
return [{ rel, kind: "dir" }, ...children];
|
|
1700
1700
|
}
|
|
1701
|
-
const contentHash =
|
|
1701
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1702
1702
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1703
1703
|
}
|
|
1704
1704
|
function topLevelScopeRoots(roots) {
|
|
@@ -1712,25 +1712,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1712
1712
|
}
|
|
1713
1713
|
return kept;
|
|
1714
1714
|
}
|
|
1715
|
-
function reconciliationScope(
|
|
1715
|
+
function reconciliationScope(fs12, farmRoot, manifest) {
|
|
1716
1716
|
if (manifest !== void 0) {
|
|
1717
1717
|
return topLevelScopeRoots(manifest.materialised);
|
|
1718
1718
|
}
|
|
1719
|
-
return [...
|
|
1719
|
+
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
1720
|
}
|
|
1721
|
-
function collectFarmListing(
|
|
1722
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1721
|
+
function collectFarmListing(fs12, farmRoot, scopeRoots) {
|
|
1722
|
+
return scopeRoots.flatMap((root) => listSubtree(fs12, farmRoot, root));
|
|
1723
1723
|
}
|
|
1724
|
-
function collectCanonicalCounterparts(
|
|
1724
|
+
function collectCanonicalCounterparts(fs12, claudeHome, farmListing) {
|
|
1725
1725
|
const out = [];
|
|
1726
1726
|
for (const entry of farmListing) {
|
|
1727
1727
|
const absolute = path6.join(claudeHome, entry.rel);
|
|
1728
|
-
const stat =
|
|
1728
|
+
const stat = fs12.lstat(absolute);
|
|
1729
1729
|
if (stat === void 0) {
|
|
1730
1730
|
continue;
|
|
1731
1731
|
}
|
|
1732
1732
|
if (stat.kind === "file") {
|
|
1733
|
-
const contentHash =
|
|
1733
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1734
1734
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1735
1735
|
continue;
|
|
1736
1736
|
}
|
|
@@ -2414,8 +2414,12 @@ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
|
|
|
2414
2414
|
}
|
|
2415
2415
|
|
|
2416
2416
|
// src/frontdoor/capability.ts
|
|
2417
|
+
var CONNECT_PROXY_USER = "agent-shim";
|
|
2417
2418
|
var CONNECT_PROXY_REALM = "agent-shim front door";
|
|
2418
2419
|
var BASIC_SCHEME = "basic";
|
|
2420
|
+
function connectProxyUrl(port, token) {
|
|
2421
|
+
return `http://${encodeURIComponent(CONNECT_PROXY_USER)}:${encodeURIComponent(token)}@127.0.0.1:${String(port)}`;
|
|
2422
|
+
}
|
|
2419
2423
|
function capabilityFromProxyAuthorization(value) {
|
|
2420
2424
|
const trimmed = value.trim();
|
|
2421
2425
|
const space = trimmed.indexOf(" ");
|
|
@@ -2432,7 +2436,15 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2432
2436
|
}
|
|
2433
2437
|
|
|
2434
2438
|
// src/frontdoor/route.ts
|
|
2439
|
+
var IDENTITY_HEADER = "x-agent-shim-identity";
|
|
2440
|
+
var SESSION_HEADER = "x-agent-shim-session";
|
|
2441
|
+
var HEADROOM_FLAG_HEADER = "x-agent-shim-headroom";
|
|
2442
|
+
var PROJECT_ID_HEADER = "x-headroom-project-id";
|
|
2435
2443
|
var AUTH_HEADER = "x-agent-shim-auth";
|
|
2444
|
+
var PROVIDER_PATH_PREFIX = "/providers/";
|
|
2445
|
+
function providerBaseUrl(port, provider) {
|
|
2446
|
+
return `https://127.0.0.1:${String(port)}${PROVIDER_PATH_PREFIX}${encodeURIComponent(provider)}`;
|
|
2447
|
+
}
|
|
2436
2448
|
|
|
2437
2449
|
// src/frontdoor/state.ts
|
|
2438
2450
|
import path7 from "node:path";
|
|
@@ -2469,10 +2481,10 @@ var FrontDoorStateSchema = z3.strictObject({
|
|
|
2469
2481
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2470
2482
|
lastError: z3.string().optional()
|
|
2471
2483
|
});
|
|
2472
|
-
function readFrontDoorState(
|
|
2484
|
+
function readFrontDoorState(fs12, stateFile) {
|
|
2473
2485
|
let raw;
|
|
2474
2486
|
try {
|
|
2475
|
-
raw =
|
|
2487
|
+
raw = fs12.readFileUtf8(stateFile);
|
|
2476
2488
|
} catch {
|
|
2477
2489
|
return void 0;
|
|
2478
2490
|
}
|
|
@@ -2486,28 +2498,28 @@ function readFrontDoorState(fs10, stateFile) {
|
|
|
2486
2498
|
return void 0;
|
|
2487
2499
|
}
|
|
2488
2500
|
}
|
|
2489
|
-
function writeFrontDoorState(
|
|
2490
|
-
|
|
2491
|
-
|
|
2501
|
+
function writeFrontDoorState(fs12, stateFile, state) {
|
|
2502
|
+
fs12.mkdirp(path7.dirname(stateFile));
|
|
2503
|
+
fs12.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2492
2504
|
`);
|
|
2493
2505
|
}
|
|
2494
2506
|
function frontDoorOrigin(state) {
|
|
2495
2507
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2496
2508
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2497
2509
|
}
|
|
2498
|
-
function writeFrontDoorSession(
|
|
2499
|
-
|
|
2500
|
-
|
|
2510
|
+
function writeFrontDoorSession(fs12, sessionsDir, session) {
|
|
2511
|
+
fs12.mkdirPrivate(sessionsDir);
|
|
2512
|
+
fs12.writeFilePrivate(path7.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2501
2513
|
`);
|
|
2502
2514
|
}
|
|
2503
|
-
function listFrontDoorSessions(
|
|
2515
|
+
function listFrontDoorSessions(fs12, sessionsDir) {
|
|
2504
2516
|
const sessions = [];
|
|
2505
|
-
for (const name of
|
|
2517
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
2506
2518
|
if (!name.endsWith(".json")) {
|
|
2507
2519
|
continue;
|
|
2508
2520
|
}
|
|
2509
2521
|
try {
|
|
2510
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2522
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs12.readFileUtf8(path7.join(sessionsDir, name)) ?? ""));
|
|
2511
2523
|
if (parsed.success) {
|
|
2512
2524
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2513
2525
|
}
|
|
@@ -2517,14 +2529,14 @@ function listFrontDoorSessions(fs10, sessionsDir) {
|
|
|
2517
2529
|
}
|
|
2518
2530
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2519
2531
|
}
|
|
2520
|
-
function removeFrontDoorSession(
|
|
2521
|
-
|
|
2532
|
+
function removeFrontDoorSession(fs12, sessionsDir, pid) {
|
|
2533
|
+
fs12.removeRecursive(path7.join(sessionsDir, `${String(pid)}.json`));
|
|
2522
2534
|
}
|
|
2523
|
-
function pruneDeadFrontDoorSessions(
|
|
2535
|
+
function pruneDeadFrontDoorSessions(fs12, sessionsDir, isRunning) {
|
|
2524
2536
|
const removed = [];
|
|
2525
|
-
for (const session of listFrontDoorSessions(
|
|
2537
|
+
for (const session of listFrontDoorSessions(fs12, sessionsDir)) {
|
|
2526
2538
|
if (!isRunning(session.pid)) {
|
|
2527
|
-
removeFrontDoorSession(
|
|
2539
|
+
removeFrontDoorSession(fs12, sessionsDir, session.pid);
|
|
2528
2540
|
removed.push(session.pid);
|
|
2529
2541
|
}
|
|
2530
2542
|
}
|
|
@@ -2536,8 +2548,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2536
2548
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2537
2549
|
var MS_PER_MINUTE = 6e4;
|
|
2538
2550
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2539
|
-
const { fs:
|
|
2540
|
-
const previous = readFrontDoorState(
|
|
2551
|
+
const { fs: fs12, paths } = ports;
|
|
2552
|
+
const previous = readFrontDoorState(fs12, paths.frontdoorStateFile);
|
|
2541
2553
|
let lastPort = previous?.lastPort;
|
|
2542
2554
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2543
2555
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2547,12 +2559,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2547
2559
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2548
2560
|
});
|
|
2549
2561
|
const fail = (message) => {
|
|
2550
|
-
writeFrontDoorState(
|
|
2551
|
-
|
|
2562
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2563
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2552
2564
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2553
2565
|
return 1;
|
|
2554
2566
|
};
|
|
2555
|
-
writeFrontDoorState(
|
|
2567
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2556
2568
|
const previousSticky = lastPort;
|
|
2557
2569
|
let http2;
|
|
2558
2570
|
try {
|
|
@@ -2589,8 +2601,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2589
2601
|
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
2602
|
}
|
|
2591
2603
|
lastDirectPort = direct.port;
|
|
2592
|
-
writeFrontDoorState(
|
|
2593
|
-
|
|
2604
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
2605
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2594
2606
|
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
2607
|
let idleSince;
|
|
2596
2608
|
let ticks = 0;
|
|
@@ -2599,15 +2611,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2599
2611
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2600
2612
|
}
|
|
2601
2613
|
ticks += 1;
|
|
2602
|
-
pruneDeadFrontDoorSessions(
|
|
2603
|
-
if (listFrontDoorSessions(
|
|
2614
|
+
pruneDeadFrontDoorSessions(fs12, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2615
|
+
if (listFrontDoorSessions(fs12, paths.frontdoorSessionsDir).length === 0) {
|
|
2604
2616
|
idleSince ??= ports.now();
|
|
2605
2617
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2606
2618
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2607
2619
|
await direct.close();
|
|
2608
2620
|
await connect3.close();
|
|
2609
2621
|
await http2.close();
|
|
2610
|
-
writeFrontDoorState(
|
|
2622
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, sticky());
|
|
2611
2623
|
return 0;
|
|
2612
2624
|
}
|
|
2613
2625
|
} else {
|
|
@@ -2662,8 +2674,8 @@ function parseConnectTarget(authority) {
|
|
|
2662
2674
|
function isInterceptedHost(host, interceptHosts) {
|
|
2663
2675
|
return interceptHosts.includes(host);
|
|
2664
2676
|
}
|
|
2665
|
-
function servedByPipeline(
|
|
2666
|
-
return
|
|
2677
|
+
function servedByPipeline(path28) {
|
|
2678
|
+
return path28?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2667
2679
|
}
|
|
2668
2680
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2669
2681
|
"connection",
|
|
@@ -3197,16 +3209,16 @@ function parseJson(raw, schema) {
|
|
|
3197
3209
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
3198
3210
|
return parsed.success ? parsed.data : void 0;
|
|
3199
3211
|
}
|
|
3200
|
-
function readHeadroomState(
|
|
3212
|
+
function readHeadroomState(fs12, stateFile) {
|
|
3201
3213
|
try {
|
|
3202
|
-
return parseJson(
|
|
3214
|
+
return parseJson(fs12.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
3203
3215
|
} catch {
|
|
3204
3216
|
return void 0;
|
|
3205
3217
|
}
|
|
3206
3218
|
}
|
|
3207
|
-
function writeHeadroomState(
|
|
3208
|
-
|
|
3209
|
-
|
|
3219
|
+
function writeHeadroomState(fs12, stateFile, state) {
|
|
3220
|
+
fs12.mkdirp(path8.dirname(stateFile));
|
|
3221
|
+
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
3210
3222
|
`);
|
|
3211
3223
|
}
|
|
3212
3224
|
function headroomAllowlist(providers) {
|
|
@@ -3226,14 +3238,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3226
3238
|
function hashAllowlist(allowlist) {
|
|
3227
3239
|
return createHash2("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3228
3240
|
}
|
|
3229
|
-
function readAllProviders(
|
|
3241
|
+
function readAllProviders(fs12, providersDir) {
|
|
3230
3242
|
const result = [];
|
|
3231
|
-
for (const name of
|
|
3243
|
+
for (const name of fs12.readdir(providersDir)) {
|
|
3232
3244
|
if (!name.endsWith(".json")) {
|
|
3233
3245
|
continue;
|
|
3234
3246
|
}
|
|
3235
3247
|
try {
|
|
3236
|
-
const parsed = parseJson(
|
|
3248
|
+
const parsed = parseJson(fs12.readFileUtf8(path8.join(providersDir, name)), ProviderSchema);
|
|
3237
3249
|
if (parsed !== void 0) {
|
|
3238
3250
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
3239
3251
|
}
|
|
@@ -3243,22 +3255,22 @@ function readAllProviders(fs10, providersDir) {
|
|
|
3243
3255
|
}
|
|
3244
3256
|
return result;
|
|
3245
3257
|
}
|
|
3246
|
-
function writeSession(
|
|
3247
|
-
|
|
3248
|
-
|
|
3258
|
+
function writeSession(fs12, sessionsDir, session) {
|
|
3259
|
+
fs12.mkdirp(sessionsDir);
|
|
3260
|
+
fs12.writeFileUtf8(path8.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
3249
3261
|
`);
|
|
3250
3262
|
}
|
|
3251
|
-
function removeSession(
|
|
3252
|
-
|
|
3263
|
+
function removeSession(fs12, sessionsDir, pid) {
|
|
3264
|
+
fs12.removeRecursive(path8.join(sessionsDir, `${String(pid)}.json`));
|
|
3253
3265
|
}
|
|
3254
|
-
function listSessions(
|
|
3266
|
+
function listSessions(fs12, sessionsDir) {
|
|
3255
3267
|
const sessions = [];
|
|
3256
|
-
for (const name of
|
|
3268
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
3257
3269
|
if (!name.endsWith(".json")) {
|
|
3258
3270
|
continue;
|
|
3259
3271
|
}
|
|
3260
3272
|
try {
|
|
3261
|
-
const parsed = parseJson(
|
|
3273
|
+
const parsed = parseJson(fs12.readFileUtf8(path8.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
3262
3274
|
if (parsed !== void 0) {
|
|
3263
3275
|
sessions.push(parsed);
|
|
3264
3276
|
}
|
|
@@ -3268,22 +3280,22 @@ function listSessions(fs10, sessionsDir) {
|
|
|
3268
3280
|
}
|
|
3269
3281
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
3270
3282
|
}
|
|
3271
|
-
function sessionsForSupervisor(
|
|
3272
|
-
return listSessions(
|
|
3283
|
+
function sessionsForSupervisor(fs12, sessionsDir, supervisorPid) {
|
|
3284
|
+
return listSessions(fs12, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3273
3285
|
}
|
|
3274
|
-
function pruneDeadSessions(
|
|
3286
|
+
function pruneDeadSessions(fs12, sessionsDir, isRunning) {
|
|
3275
3287
|
const removed = [];
|
|
3276
|
-
for (const session of listSessions(
|
|
3288
|
+
for (const session of listSessions(fs12, sessionsDir)) {
|
|
3277
3289
|
if (!isRunning(session.pid)) {
|
|
3278
|
-
removeSession(
|
|
3290
|
+
removeSession(fs12, sessionsDir, session.pid);
|
|
3279
3291
|
removed.push(session.pid);
|
|
3280
3292
|
}
|
|
3281
3293
|
}
|
|
3282
3294
|
return removed;
|
|
3283
3295
|
}
|
|
3284
|
-
function readStartLock(
|
|
3296
|
+
function readStartLock(fs12, lockFile) {
|
|
3285
3297
|
try {
|
|
3286
|
-
return parseJson(
|
|
3298
|
+
return parseJson(fs12.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
3287
3299
|
} catch {
|
|
3288
3300
|
return void 0;
|
|
3289
3301
|
}
|
|
@@ -3534,12 +3546,12 @@ function isIdentityName(name) {
|
|
|
3534
3546
|
function claudeJsonPath(identitiesDir, identity) {
|
|
3535
3547
|
return path9.join(identitiesDir, identity, CLAUDE_JSON);
|
|
3536
3548
|
}
|
|
3537
|
-
function readAccountMetadata(
|
|
3549
|
+
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
3538
3550
|
if (!isIdentityName(identity)) {
|
|
3539
3551
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3540
3552
|
}
|
|
3541
3553
|
const file = claudeJsonPath(identitiesDir, identity);
|
|
3542
|
-
const raw =
|
|
3554
|
+
const raw = fs12.readFileUtf8(file);
|
|
3543
3555
|
if (raw === void 0) {
|
|
3544
3556
|
return void 0;
|
|
3545
3557
|
}
|
|
@@ -3573,8 +3585,8 @@ function readAccountMetadata(fs10, identitiesDir, identity) {
|
|
|
3573
3585
|
// src/usage/read.ts
|
|
3574
3586
|
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
3575
3587
|
var DAY_MS = 864e5;
|
|
3576
|
-
function listLogSegments(
|
|
3577
|
-
return
|
|
3588
|
+
function listLogSegments(fs12, logDir) {
|
|
3589
|
+
return fs12.readdir(logDir).flatMap((name) => {
|
|
3578
3590
|
const match = SEGMENT_RE.exec(name);
|
|
3579
3591
|
const day = match?.[1];
|
|
3580
3592
|
if (day === void 0) {
|
|
@@ -3583,15 +3595,15 @@ function listLogSegments(fs10, logDir) {
|
|
|
3583
3595
|
return [{ name, day, endsAt: Date.parse(`${day}T00:00:00.000Z`) + DAY_MS }];
|
|
3584
3596
|
}).sort((left, right) => left.day.localeCompare(right.day) || left.name.localeCompare(right.name));
|
|
3585
3597
|
}
|
|
3586
|
-
function readUsageLog(
|
|
3598
|
+
function readUsageLog(fs12, logDir, options = {}) {
|
|
3587
3599
|
const { sinceMs } = options;
|
|
3588
3600
|
const records = [];
|
|
3589
3601
|
let invalidLines = 0;
|
|
3590
|
-
for (const segment of listLogSegments(
|
|
3602
|
+
for (const segment of listLogSegments(fs12, logDir)) {
|
|
3591
3603
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
3592
3604
|
continue;
|
|
3593
3605
|
}
|
|
3594
|
-
const contents =
|
|
3606
|
+
const contents = fs12.readFileUtf8(path10.join(logDir, segment.name));
|
|
3595
3607
|
if (contents === void 0) {
|
|
3596
3608
|
continue;
|
|
3597
3609
|
}
|
|
@@ -3635,9 +3647,9 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3635
3647
|
}
|
|
3636
3648
|
return path10.join(snapshotsDir, `${identity}.json`);
|
|
3637
3649
|
}
|
|
3638
|
-
function readUsageSnapshot(
|
|
3650
|
+
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
3639
3651
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3640
|
-
const raw =
|
|
3652
|
+
const raw = fs12.readFileUtf8(file);
|
|
3641
3653
|
if (raw === void 0) {
|
|
3642
3654
|
return void 0;
|
|
3643
3655
|
}
|
|
@@ -3653,8 +3665,8 @@ function readUsageSnapshot(fs10, snapshotsDir, identity) {
|
|
|
3653
3665
|
}
|
|
3654
3666
|
return snapshot.data;
|
|
3655
3667
|
}
|
|
3656
|
-
function listUsageSnapshots(
|
|
3657
|
-
return
|
|
3668
|
+
function listUsageSnapshots(fs12, snapshotsDir) {
|
|
3669
|
+
return fs12.readdir(snapshotsDir).flatMap((name) => {
|
|
3658
3670
|
if (!name.endsWith(".json")) {
|
|
3659
3671
|
return [];
|
|
3660
3672
|
}
|
|
@@ -3662,7 +3674,7 @@ function listUsageSnapshots(fs10, snapshotsDir) {
|
|
|
3662
3674
|
if (!isIdentityName(identity)) {
|
|
3663
3675
|
return [];
|
|
3664
3676
|
}
|
|
3665
|
-
const snapshot = readUsageSnapshot(
|
|
3677
|
+
const snapshot = readUsageSnapshot(fs12, snapshotsDir, identity);
|
|
3666
3678
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3667
3679
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3668
3680
|
}
|
|
@@ -3837,9 +3849,9 @@ function compareVersions(a, b) {
|
|
|
3837
3849
|
}
|
|
3838
3850
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
3839
3851
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
3840
|
-
const { fs:
|
|
3852
|
+
const { fs: fs12, paths } = ports;
|
|
3841
3853
|
let config = initialConfig;
|
|
3842
|
-
const previousState = readHeadroomState(
|
|
3854
|
+
const previousState = readHeadroomState(fs12, paths.headroomStateFile);
|
|
3843
3855
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3844
3856
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3845
3857
|
return 0;
|
|
@@ -3848,19 +3860,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3848
3860
|
let lastPort = previousState?.lastPort;
|
|
3849
3861
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3850
3862
|
const ownsState = () => {
|
|
3851
|
-
const owner = readHeadroomState(
|
|
3863
|
+
const owner = readHeadroomState(fs12, paths.headroomStateFile)?.supervisorPid;
|
|
3852
3864
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3853
3865
|
};
|
|
3854
3866
|
const writeOwnState = (state) => {
|
|
3855
3867
|
if (!ownsState()) {
|
|
3856
3868
|
return false;
|
|
3857
3869
|
}
|
|
3858
|
-
writeHeadroomState(
|
|
3870
|
+
writeHeadroomState(fs12, paths.headroomStateFile, state);
|
|
3859
3871
|
return true;
|
|
3860
3872
|
};
|
|
3861
3873
|
const fail = (message) => {
|
|
3862
3874
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3863
|
-
|
|
3875
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
3864
3876
|
}
|
|
3865
3877
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
3866
3878
|
return 1;
|
|
@@ -3909,7 +3921,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3909
3921
|
const settingsHash = hashSettings(config.settings);
|
|
3910
3922
|
const allowlist = allowlistOf(ports);
|
|
3911
3923
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3912
|
-
pruneDeadSessions(
|
|
3924
|
+
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
3913
3925
|
const ownsNow = ownsState();
|
|
3914
3926
|
if (!ownsNow && !superseded) {
|
|
3915
3927
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -3961,7 +3973,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3961
3973
|
...sticky()
|
|
3962
3974
|
});
|
|
3963
3975
|
if (recorded) {
|
|
3964
|
-
|
|
3976
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
3965
3977
|
}
|
|
3966
3978
|
idleSince = void 0;
|
|
3967
3979
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -3976,7 +3988,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3976
3988
|
}
|
|
3977
3989
|
} else {
|
|
3978
3990
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3979
|
-
if (sessionsForSupervisor(
|
|
3991
|
+
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3980
3992
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3981
3993
|
ports.stopProcess(headroomPid);
|
|
3982
3994
|
headroomPid = void 0;
|
|
@@ -3985,7 +3997,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3985
3997
|
continue;
|
|
3986
3998
|
}
|
|
3987
3999
|
}
|
|
3988
|
-
const sessions = sessionsForSupervisor(
|
|
4000
|
+
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
3989
4001
|
if (sessions.length === 0) {
|
|
3990
4002
|
idleSince ??= ports.now();
|
|
3991
4003
|
const idleMs = ports.now() - idleSince;
|
|
@@ -4007,9 +4019,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4007
4019
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
4008
4020
|
}
|
|
4009
4021
|
}
|
|
4010
|
-
function currentHeadroomAllowlist(
|
|
4011
|
-
const providers = readAllProviders(
|
|
4012
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
4022
|
+
function currentHeadroomAllowlist(fs12, paths) {
|
|
4023
|
+
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
4024
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
4013
4025
|
}
|
|
4014
4026
|
function allowlistOf(ports) {
|
|
4015
4027
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -4186,8 +4198,8 @@ function decideConfigProfile(params) {
|
|
|
4186
4198
|
}
|
|
4187
4199
|
return { source: "none" };
|
|
4188
4200
|
}
|
|
4189
|
-
function loadIdentity(identitiesDir, name,
|
|
4190
|
-
return loadConfigFile(path12.join(identitiesDir, name, "identity.json"), IdentitySchema,
|
|
4201
|
+
function loadIdentity(identitiesDir, name, fs12) {
|
|
4202
|
+
return loadConfigFile(path12.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
4191
4203
|
}
|
|
4192
4204
|
|
|
4193
4205
|
// src/configProfilesStore.ts
|
|
@@ -4529,13 +4541,59 @@ import path15 from "node:path";
|
|
|
4529
4541
|
|
|
4530
4542
|
// src/credentialCache.ts
|
|
4531
4543
|
import { z as z7 } from "zod";
|
|
4544
|
+
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
4532
4545
|
var CachedCredentialSchema = z7.strictObject({
|
|
4533
4546
|
token: z7.string().min(1),
|
|
4534
4547
|
fetchedAt: z7.number().int().nonnegative(),
|
|
4535
4548
|
source: CredentialSourceSchema
|
|
4536
4549
|
});
|
|
4550
|
+
function effectiveStore(cache, platform) {
|
|
4551
|
+
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
4552
|
+
}
|
|
4553
|
+
function ttlMs(cache) {
|
|
4554
|
+
if (cache.ttl === void 0) {
|
|
4555
|
+
return void 0;
|
|
4556
|
+
}
|
|
4557
|
+
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
4558
|
+
if (multiplier === void 0) {
|
|
4559
|
+
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
4560
|
+
}
|
|
4561
|
+
return Number(cache.ttl.slice(0, -1)) * multiplier;
|
|
4562
|
+
}
|
|
4563
|
+
function isFresh(entry, cache, now) {
|
|
4564
|
+
const limit = ttlMs(cache);
|
|
4565
|
+
if (entry.fetchedAt > now) {
|
|
4566
|
+
return false;
|
|
4567
|
+
}
|
|
4568
|
+
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
4569
|
+
}
|
|
4537
4570
|
|
|
4538
4571
|
// src/credential.ts
|
|
4572
|
+
var CREDENTIAL_UNAVAILABLE_EXIT = 64;
|
|
4573
|
+
var CREDENTIAL_COMMAND_TIMEOUT_MS = 1e4;
|
|
4574
|
+
var CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 12e4;
|
|
4575
|
+
var GROUP_OTHER_PERMISSION_BITS = 63;
|
|
4576
|
+
var PERMISSION_BITS = 511;
|
|
4577
|
+
var OCTAL_RADIX = 8;
|
|
4578
|
+
function commandArgv(source) {
|
|
4579
|
+
if ("command" in source) {
|
|
4580
|
+
return source.command;
|
|
4581
|
+
}
|
|
4582
|
+
if ("op" in source) {
|
|
4583
|
+
return ["op", "read", source.op];
|
|
4584
|
+
}
|
|
4585
|
+
const account = source.keychain.account;
|
|
4586
|
+
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
4587
|
+
}
|
|
4588
|
+
function isInteractiveSource(source, env) {
|
|
4589
|
+
if ("command" in source || "keychain" in source) {
|
|
4590
|
+
return source.interactive ?? false;
|
|
4591
|
+
}
|
|
4592
|
+
if ("op" in source) {
|
|
4593
|
+
return source.interactive ?? (env.OP_SERVICE_ACCOUNT_TOKEN ?? "") === "";
|
|
4594
|
+
}
|
|
4595
|
+
return false;
|
|
4596
|
+
}
|
|
4539
4597
|
function summariseSource(source) {
|
|
4540
4598
|
if ("env" in source) {
|
|
4541
4599
|
return { kind: "env", variable: source.env };
|
|
@@ -4583,6 +4641,9 @@ function formatSourceSummary(summary) {
|
|
|
4583
4641
|
return summary;
|
|
4584
4642
|
}
|
|
4585
4643
|
}
|
|
4644
|
+
function describeSource(source) {
|
|
4645
|
+
return formatSourceSummary(summariseSource(source));
|
|
4646
|
+
}
|
|
4586
4647
|
function formatCredentialSummary(summary) {
|
|
4587
4648
|
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
4649
|
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
@@ -4590,6 +4651,88 @@ function formatCredentialSummary(summary) {
|
|
|
4590
4651
|
function describeCredential(credential) {
|
|
4591
4652
|
return formatCredentialSummary(summariseCredential(credential));
|
|
4592
4653
|
}
|
|
4654
|
+
function trySource(source, params) {
|
|
4655
|
+
const none = (reason, warn = false) => ({ ok: false, reason, warn });
|
|
4656
|
+
if ("env" in source) {
|
|
4657
|
+
const value = params.env[source.env];
|
|
4658
|
+
return value === void 0 || value === "" ? none("is unset or empty") : { ok: true, token: value };
|
|
4659
|
+
}
|
|
4660
|
+
if ("literal" in source) {
|
|
4661
|
+
return { ok: true, token: source.literal };
|
|
4662
|
+
}
|
|
4663
|
+
if ("file" in source) {
|
|
4664
|
+
const read = params.port.readSecretFile(source.file);
|
|
4665
|
+
if (!read.found) {
|
|
4666
|
+
return none("does not exist");
|
|
4667
|
+
}
|
|
4668
|
+
if (read.mode !== void 0 && (read.mode & GROUP_OTHER_PERMISSION_BITS) !== 0) {
|
|
4669
|
+
return none(
|
|
4670
|
+
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX)}); run \`chmod 600 ${source.file}\``,
|
|
4671
|
+
true
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
const token2 = read.content.trim();
|
|
4675
|
+
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
4676
|
+
}
|
|
4677
|
+
const argv = commandArgv(source);
|
|
4678
|
+
const interactive = isInteractiveSource(source, params.env);
|
|
4679
|
+
if (interactive && !params.port.personPresent()) {
|
|
4680
|
+
return none("needs a person to approve it, but there is no terminal or desktop session");
|
|
4681
|
+
}
|
|
4682
|
+
const timeoutMs = source.timeoutMs ?? (interactive ? CREDENTIAL_INTERACTIVE_TIMEOUT_MS : CREDENTIAL_COMMAND_TIMEOUT_MS);
|
|
4683
|
+
const result = params.port.runCommand(argv, { timeoutMs, interactive });
|
|
4684
|
+
if (result.timedOut) {
|
|
4685
|
+
return none(`timed out after ${String(timeoutMs)}ms`);
|
|
4686
|
+
}
|
|
4687
|
+
if (result.status !== 0) {
|
|
4688
|
+
const stderr = result.stderr.trim();
|
|
4689
|
+
const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
|
|
4690
|
+
return none(`${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
|
|
4691
|
+
}
|
|
4692
|
+
const token = result.stdout.trim();
|
|
4693
|
+
return token === "" ? none("printed no token") : { ok: true, token };
|
|
4694
|
+
}
|
|
4695
|
+
function warmCommandFor(subject) {
|
|
4696
|
+
const [kind, name] = subject.split(" ");
|
|
4697
|
+
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
4698
|
+
}
|
|
4699
|
+
function resolveCredential(params) {
|
|
4700
|
+
const target = params.credential.target ?? "bearer";
|
|
4701
|
+
const cacheBlock = params.credential.cache;
|
|
4702
|
+
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
4703
|
+
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
4704
|
+
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
4705
|
+
const entry = cache.port.read(store, params.subject);
|
|
4706
|
+
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
4707
|
+
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
const attempts = [];
|
|
4711
|
+
const warnings = [];
|
|
4712
|
+
for (const source of params.credential.sources) {
|
|
4713
|
+
const outcome = trySource(source, params);
|
|
4714
|
+
if (outcome.ok) {
|
|
4715
|
+
if (cache !== void 0 && store !== void 0) {
|
|
4716
|
+
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
4717
|
+
}
|
|
4718
|
+
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
4719
|
+
}
|
|
4720
|
+
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
4721
|
+
attempts.push(line);
|
|
4722
|
+
if (outcome.warn) {
|
|
4723
|
+
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
4727
|
+
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
4728
|
+
}
|
|
4729
|
+
function credentialVariables(target, token) {
|
|
4730
|
+
return {
|
|
4731
|
+
ANTHROPIC_AUTH_TOKEN: target === "bearer" ? token : void 0,
|
|
4732
|
+
ANTHROPIC_API_KEY: target === "apiKey" ? token : void 0,
|
|
4733
|
+
CLAUDE_CODE_OAUTH_TOKEN: target === "oauthToken" ? token : void 0
|
|
4734
|
+
};
|
|
4735
|
+
}
|
|
4593
4736
|
|
|
4594
4737
|
// src/providersStore.ts
|
|
4595
4738
|
var ProviderNotFoundError = class extends CliError {
|
|
@@ -4695,6 +4838,11 @@ function readProvider(paths, name) {
|
|
|
4695
4838
|
const raw = JSON.parse(fs6.readFileSync(filePath, "utf8"));
|
|
4696
4839
|
return parseProviderFile(filePath, raw);
|
|
4697
4840
|
}
|
|
4841
|
+
function loadProvider(providersDir, name, port) {
|
|
4842
|
+
const filePath = path15.join(providersDir, `${name}.json`);
|
|
4843
|
+
const raw = port.readConfigFile(filePath);
|
|
4844
|
+
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
4845
|
+
}
|
|
4698
4846
|
function listProviders(paths) {
|
|
4699
4847
|
const names = listProviderNames(
|
|
4700
4848
|
(dir) => fs6.readdirSync(dir),
|
|
@@ -4827,6 +4975,30 @@ function cascadeProviderName(cascade) {
|
|
|
4827
4975
|
}
|
|
4828
4976
|
return name;
|
|
4829
4977
|
}
|
|
4978
|
+
function resolveProvider(params) {
|
|
4979
|
+
const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
|
|
4980
|
+
const name = cliName ?? (params.cascade === void 0 ? void 0 : cascadeProviderName(params.cascade));
|
|
4981
|
+
if (name === void 0) {
|
|
4982
|
+
return void 0;
|
|
4983
|
+
}
|
|
4984
|
+
const definition = loadProvider(params.paths.providersDir, name, params.port);
|
|
4985
|
+
if (definition === void 0) {
|
|
4986
|
+
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
4987
|
+
return {
|
|
4988
|
+
ok: false,
|
|
4989
|
+
status: 1,
|
|
4990
|
+
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
4991
|
+
};
|
|
4992
|
+
}
|
|
4993
|
+
if (params.credentials === void 0) {
|
|
4994
|
+
return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
|
|
4995
|
+
}
|
|
4996
|
+
const resolution = resolveCredential({ credential: definition.credential, env: params.env, port: params.credentials, subject: `provider ${name}` });
|
|
4997
|
+
if (!resolution.ok) {
|
|
4998
|
+
return { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
4999
|
+
}
|
|
5000
|
+
return { ok: true, provider: { name, definition, credential: resolution.credential }, warnings: resolution.credential.warnings };
|
|
5001
|
+
}
|
|
4830
5002
|
|
|
4831
5003
|
// src/directoryRulesStore.ts
|
|
4832
5004
|
var DirectoryRuleNotFoundError = class extends CliError {
|
|
@@ -4919,7 +5091,7 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
4919
5091
|
}
|
|
4920
5092
|
|
|
4921
5093
|
// src/checkReport.ts
|
|
4922
|
-
import
|
|
5094
|
+
import os4 from "node:os";
|
|
4923
5095
|
|
|
4924
5096
|
// src/launcher/cascade.ts
|
|
4925
5097
|
import path17 from "node:path";
|
|
@@ -5015,6 +5187,7 @@ var MS_PER_SECOND2 = 1e3;
|
|
|
5015
5187
|
var SECONDS_PER_MINUTE = 60;
|
|
5016
5188
|
var MINUTES_PER_HOUR = 60;
|
|
5017
5189
|
var HOURS_PER_DAY = 24;
|
|
5190
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
5018
5191
|
function formatAge(ms) {
|
|
5019
5192
|
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5020
5193
|
if (minutes < MINUTES_PER_HOUR) {
|
|
@@ -5035,8 +5208,30 @@ function effectiveWindow(window, nowMs) {
|
|
|
5035
5208
|
...window.status === void 0 ? {} : { status: window.status }
|
|
5036
5209
|
};
|
|
5037
5210
|
}
|
|
5211
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
5212
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
5213
|
+
const unified = rateLimit?.unified;
|
|
5214
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
5215
|
+
return [];
|
|
5216
|
+
}
|
|
5217
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
5218
|
+
const windows = [
|
|
5219
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
5220
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
5221
|
+
];
|
|
5222
|
+
return windows.flatMap(({ name, window }) => {
|
|
5223
|
+
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
5224
|
+
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
5225
|
+
return [];
|
|
5226
|
+
}
|
|
5227
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
5228
|
+
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
5229
|
+
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
5230
|
+
});
|
|
5231
|
+
}
|
|
5038
5232
|
|
|
5039
5233
|
// src/usage/poolPick.ts
|
|
5234
|
+
import path18 from "node:path";
|
|
5040
5235
|
import { z as z9 } from "zod";
|
|
5041
5236
|
|
|
5042
5237
|
// src/usage/plan.ts
|
|
@@ -5289,14 +5484,14 @@ var PicksFileSchema = z9.strictObject({
|
|
|
5289
5484
|
function errorMessage(error) {
|
|
5290
5485
|
return error instanceof Error ? error.message : String(error);
|
|
5291
5486
|
}
|
|
5292
|
-
function loadPoolMembers(
|
|
5487
|
+
function loadPoolMembers(fs12, paths, identities, nowMs) {
|
|
5293
5488
|
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5294
|
-
const log = readUsageLog(
|
|
5489
|
+
const log = readUsageLog(fs12, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5295
5490
|
return identities.map((identity) => {
|
|
5296
5491
|
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5297
5492
|
try {
|
|
5298
|
-
const snapshot = readUsageSnapshot(
|
|
5299
|
-
const account = readAccountMetadata(
|
|
5493
|
+
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
5494
|
+
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5300
5495
|
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5301
5496
|
} catch (error) {
|
|
5302
5497
|
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
@@ -5314,9 +5509,9 @@ function rankPoolFromStore(params) {
|
|
|
5314
5509
|
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5315
5510
|
});
|
|
5316
5511
|
}
|
|
5317
|
-
function readPicksFile(
|
|
5512
|
+
function readPicksFile(fs12, file) {
|
|
5318
5513
|
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5319
|
-
const raw =
|
|
5514
|
+
const raw = fs12.readFileUtf8(file);
|
|
5320
5515
|
if (raw === void 0) {
|
|
5321
5516
|
return { file: empty };
|
|
5322
5517
|
}
|
|
@@ -5329,17 +5524,72 @@ function readPicksFile(fs10, file) {
|
|
|
5329
5524
|
const result = PicksFileSchema.safeParse(parsed);
|
|
5330
5525
|
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5331
5526
|
}
|
|
5332
|
-
function readStickyPick(
|
|
5333
|
-
const read = readPicksFile(
|
|
5527
|
+
function readStickyPick(fs12, picksFile, directory) {
|
|
5528
|
+
const read = readPicksFile(fs12, picksFile);
|
|
5334
5529
|
const entry = read.file.picks[directory];
|
|
5335
5530
|
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5336
5531
|
}
|
|
5532
|
+
function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
5533
|
+
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5534
|
+
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5535
|
+
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5536
|
+
fs12.mkdirPrivate(path18.dirname(picksFile));
|
|
5537
|
+
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5538
|
+
`);
|
|
5539
|
+
}
|
|
5337
5540
|
|
|
5338
5541
|
// src/launcher/pool.ts
|
|
5339
|
-
|
|
5340
|
-
|
|
5542
|
+
var RESUME_FLAGS = /* @__PURE__ */ new Set(["--continue", "-c", "--resume", "-r"]);
|
|
5543
|
+
var TERMINATOR = "--";
|
|
5544
|
+
var REASONS_LOGGED = 3;
|
|
5545
|
+
function splitMembers(pool, paths, fs12) {
|
|
5546
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs12) !== void 0;
|
|
5341
5547
|
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5342
5548
|
}
|
|
5549
|
+
function isResuming(passthrough) {
|
|
5550
|
+
const end = passthrough.indexOf(TERMINATOR);
|
|
5551
|
+
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
5552
|
+
}
|
|
5553
|
+
function resolvePoolLaunch(params) {
|
|
5554
|
+
const { poolName, log } = params;
|
|
5555
|
+
const pool = params.pools?.[poolName];
|
|
5556
|
+
if (pool === void 0) {
|
|
5557
|
+
return { ok: false, message: `agent-shim: no pool named "${poolName}" (selected via ${params.selectedVia}). Run \`agent-shim pool add ${poolName} --identity <name>...\` first.` };
|
|
5558
|
+
}
|
|
5559
|
+
const { present: identities, missing } = splitMembers(pool, params.paths, params.fs);
|
|
5560
|
+
for (const name of missing) {
|
|
5561
|
+
log.warn(`agent-shim: pool "${poolName}" names identity "${name}", which does not exist; skipping it`);
|
|
5562
|
+
}
|
|
5563
|
+
if (identities.length === 0) {
|
|
5564
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
5565
|
+
}
|
|
5566
|
+
const resuming = isResuming(params.passthrough);
|
|
5567
|
+
const stickyRead = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd);
|
|
5568
|
+
if (stickyRead.problem !== void 0) {
|
|
5569
|
+
log.warn(`agent-shim: ${stickyRead.problem}; ignoring the last-pick record`);
|
|
5570
|
+
}
|
|
5571
|
+
for (; ; ) {
|
|
5572
|
+
const nowMs = params.now();
|
|
5573
|
+
const ranking = rankPoolFromStore({ fs: params.usageFs, paths: params.paths, identities, nowMs, resuming, ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky } });
|
|
5574
|
+
const { pick, earliestReturn } = ranking;
|
|
5575
|
+
if (pick !== void 0) {
|
|
5576
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
5577
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
5578
|
+
}
|
|
5579
|
+
if (earliestReturn === void 0) {
|
|
5580
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
|
5581
|
+
}
|
|
5582
|
+
const until = new Date(earliestReturn.atMs).toISOString();
|
|
5583
|
+
if (!params.wait) {
|
|
5584
|
+
return {
|
|
5585
|
+
ok: false,
|
|
5586
|
+
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.`
|
|
5587
|
+
};
|
|
5588
|
+
}
|
|
5589
|
+
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
5590
|
+
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5343
5593
|
|
|
5344
5594
|
// src/poolPickReport.ts
|
|
5345
5595
|
function candidateView(candidate) {
|
|
@@ -5379,14 +5629,15 @@ function collectPoolPick(params) {
|
|
|
5379
5629
|
import { spawn, spawnSync } from "node:child_process";
|
|
5380
5630
|
import { createHash as createHash4 } from "node:crypto";
|
|
5381
5631
|
import fs7 from "node:fs";
|
|
5382
|
-
import
|
|
5632
|
+
import os3 from "node:os";
|
|
5633
|
+
import path20 from "node:path";
|
|
5383
5634
|
import { isSea } from "node:sea";
|
|
5384
5635
|
|
|
5385
5636
|
// src/versionDiscovery.ts
|
|
5386
5637
|
import os2 from "node:os";
|
|
5387
|
-
import
|
|
5638
|
+
import path19 from "node:path";
|
|
5388
5639
|
function defaultVersionsDir() {
|
|
5389
|
-
return
|
|
5640
|
+
return path19.join(os2.homedir(), ".local", "share", "claude", "versions");
|
|
5390
5641
|
}
|
|
5391
5642
|
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
5392
5643
|
function isNumericDottedVersion(name) {
|
|
@@ -5428,14 +5679,14 @@ function discoverClaudeBinary(options) {
|
|
|
5428
5679
|
const highestVersion = pickHighestVersion(entries);
|
|
5429
5680
|
if (highestVersion !== void 0) {
|
|
5430
5681
|
return {
|
|
5431
|
-
path:
|
|
5682
|
+
path: path19.join(versionsDir, highestVersion),
|
|
5432
5683
|
source: "versions-dir",
|
|
5433
5684
|
version: highestVersion
|
|
5434
5685
|
};
|
|
5435
5686
|
}
|
|
5436
|
-
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) =>
|
|
5687
|
+
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => path19.resolve(dir)));
|
|
5437
5688
|
for (const dir of options.pathDirs) {
|
|
5438
|
-
if (ownInstallDirs.has(
|
|
5689
|
+
if (ownInstallDirs.has(path19.resolve(dir))) {
|
|
5439
5690
|
continue;
|
|
5440
5691
|
}
|
|
5441
5692
|
const found = options.findExecutableInDir(dir, "claude");
|
|
@@ -5480,7 +5731,7 @@ function isErrorWithCode(error, code) {
|
|
|
5480
5731
|
}
|
|
5481
5732
|
var OWNER_ONLY_DIR_MODE = 448;
|
|
5482
5733
|
var OWNER_ONLY_FILE_MODE = 384;
|
|
5483
|
-
var
|
|
5734
|
+
var PERMISSION_BITS2 = 511;
|
|
5484
5735
|
var realFarmFs = {
|
|
5485
5736
|
lstat(filePath) {
|
|
5486
5737
|
let stat;
|
|
@@ -5546,7 +5797,7 @@ var realFarmFs = {
|
|
|
5546
5797
|
appendFilePrivate(filePath, contents) {
|
|
5547
5798
|
const descriptor = fs7.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
5548
5799
|
try {
|
|
5549
|
-
if ((fs7.fstatSync(descriptor).mode &
|
|
5800
|
+
if ((fs7.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
|
|
5550
5801
|
fs7.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
5551
5802
|
}
|
|
5552
5803
|
const bytes = Buffer.from(contents, "utf8");
|
|
@@ -5580,6 +5831,10 @@ var realFarmFs = {
|
|
|
5580
5831
|
}
|
|
5581
5832
|
}
|
|
5582
5833
|
};
|
|
5834
|
+
var INT32_BYTE_LENGTH = 4;
|
|
5835
|
+
function realSleepSync(ms) {
|
|
5836
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(INT32_BYTE_LENGTH)), 0, 0, ms);
|
|
5837
|
+
}
|
|
5583
5838
|
function realIsProcessAlive(pid) {
|
|
5584
5839
|
try {
|
|
5585
5840
|
process.kill(pid, 0);
|
|
@@ -5601,6 +5856,51 @@ var realRunPort = {
|
|
|
5601
5856
|
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
5602
5857
|
}
|
|
5603
5858
|
};
|
|
5859
|
+
function expandHome2(filePath) {
|
|
5860
|
+
return filePath.startsWith("~/") ? path20.join(os3.homedir(), filePath.slice(1)) : filePath;
|
|
5861
|
+
}
|
|
5862
|
+
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
5863
|
+
function hasDesktopSession() {
|
|
5864
|
+
if (process.platform === "darwin") {
|
|
5865
|
+
const result = spawnSync("launchctl", ["managername"], { encoding: "utf8" });
|
|
5866
|
+
return result.status === 0 && result.stdout.trim() === MACOS_GUI_SESSION_MANAGER;
|
|
5867
|
+
}
|
|
5868
|
+
if (process.platform === "win32") {
|
|
5869
|
+
return false;
|
|
5870
|
+
}
|
|
5871
|
+
return (process.env.DISPLAY ?? "") !== "" || (process.env.WAYLAND_DISPLAY ?? "") !== "";
|
|
5872
|
+
}
|
|
5873
|
+
var realCredentialPort = {
|
|
5874
|
+
readSecretFile(filePath) {
|
|
5875
|
+
const resolved = expandHome2(filePath);
|
|
5876
|
+
let content;
|
|
5877
|
+
let stat;
|
|
5878
|
+
try {
|
|
5879
|
+
stat = fs7.statSync(resolved);
|
|
5880
|
+
content = fs7.readFileSync(resolved, "utf8");
|
|
5881
|
+
} catch (error) {
|
|
5882
|
+
if (isEnoent3(error)) {
|
|
5883
|
+
return { found: false };
|
|
5884
|
+
}
|
|
5885
|
+
throw error;
|
|
5886
|
+
}
|
|
5887
|
+
return { found: true, content, mode: process.platform === "win32" ? void 0 : stat.mode };
|
|
5888
|
+
},
|
|
5889
|
+
runCommand(argv, options) {
|
|
5890
|
+
const [command, ...args] = argv;
|
|
5891
|
+
const result = spawnSync(command, args, {
|
|
5892
|
+
encoding: "utf8",
|
|
5893
|
+
timeout: options.timeoutMs,
|
|
5894
|
+
stdio: options.interactive ? ["inherit", "pipe", "inherit"] : ["ignore", "pipe", "pipe"]
|
|
5895
|
+
});
|
|
5896
|
+
if (result.error !== void 0) {
|
|
5897
|
+
const timedOut = isErrorWithCode(result.error, "ETIMEDOUT");
|
|
5898
|
+
return { status: result.status, stdout: "", stderr: timedOut ? "" : result.error.message, timedOut };
|
|
5899
|
+
}
|
|
5900
|
+
return { status: result.status, stdout: result.stdout, stderr: options.interactive ? "" : result.stderr, timedOut: false };
|
|
5901
|
+
},
|
|
5902
|
+
personPresent: () => process.stdin.isTTY || hasDesktopSession()
|
|
5903
|
+
};
|
|
5604
5904
|
function resolveGitBranch(run, cwd) {
|
|
5605
5905
|
const result = run.run("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
5606
5906
|
if (result.status !== 0) {
|
|
@@ -5615,6 +5915,62 @@ function resolveGitBranch(run, cwd) {
|
|
|
5615
5915
|
}
|
|
5616
5916
|
return { branch: name, branchDetached: false };
|
|
5617
5917
|
}
|
|
5918
|
+
function resolveGitRoot(run, cwd) {
|
|
5919
|
+
const result = run.run("git", ["-C", cwd, "rev-parse", "--show-toplevel"]);
|
|
5920
|
+
if (result.status !== 0) {
|
|
5921
|
+
return void 0;
|
|
5922
|
+
}
|
|
5923
|
+
const root = result.stdout.trim();
|
|
5924
|
+
return root === "" ? void 0 : root;
|
|
5925
|
+
}
|
|
5926
|
+
function detachedDaemonSpawnOptions(logFd, env) {
|
|
5927
|
+
return { detached: true, stdio: ["ignore", logFd, logFd], env };
|
|
5928
|
+
}
|
|
5929
|
+
function spawnDaemonThrough(executable) {
|
|
5930
|
+
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
5931
|
+
}
|
|
5932
|
+
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
5933
|
+
fs7.mkdirSync(path20.dirname(logPath), { recursive: true });
|
|
5934
|
+
const logFd = fs7.openSync(logPath, "a");
|
|
5935
|
+
try {
|
|
5936
|
+
const child = spawn(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
5937
|
+
child.unref();
|
|
5938
|
+
if (child.pid === void 0) {
|
|
5939
|
+
throw new Error(`spawning ${subcommand} returned no pid`);
|
|
5940
|
+
}
|
|
5941
|
+
return child.pid;
|
|
5942
|
+
} finally {
|
|
5943
|
+
fs7.closeSync(logFd);
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
function spawnHeadroomSupervisor(paths, spawnDaemon) {
|
|
5947
|
+
return spawnDaemon(paths, "__headroom-supervisor", paths.headroomLogPath);
|
|
5948
|
+
}
|
|
5949
|
+
function realHeadroomPort(paths, options) {
|
|
5950
|
+
return {
|
|
5951
|
+
ensure: () => {
|
|
5952
|
+
const up = ensureHeadroom({
|
|
5953
|
+
paths,
|
|
5954
|
+
launcherPid: process.pid,
|
|
5955
|
+
ports: {
|
|
5956
|
+
fs: realFarmFs,
|
|
5957
|
+
// 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.
|
|
5958
|
+
isRunning: realIsProcessRunning,
|
|
5959
|
+
now: () => Date.now(),
|
|
5960
|
+
sleep: realSleepSync,
|
|
5961
|
+
spawnSupervisor: (layout) => spawnHeadroomSupervisor(layout, options.spawnDaemon)
|
|
5962
|
+
}
|
|
5963
|
+
});
|
|
5964
|
+
return {
|
|
5965
|
+
port: up.port,
|
|
5966
|
+
projectId: resolveGitRoot(realRunPort, options.cwd) ?? options.cwd
|
|
5967
|
+
};
|
|
5968
|
+
},
|
|
5969
|
+
release: () => {
|
|
5970
|
+
removeSession(realFarmFs, paths.headroomSessionsDir, process.pid);
|
|
5971
|
+
}
|
|
5972
|
+
};
|
|
5973
|
+
}
|
|
5618
5974
|
var realProcPort = {
|
|
5619
5975
|
env: process.env,
|
|
5620
5976
|
argv: process.argv.slice(2),
|
|
@@ -5632,7 +5988,7 @@ function listVersionsDir(dir) {
|
|
|
5632
5988
|
throw error;
|
|
5633
5989
|
}
|
|
5634
5990
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
5635
|
-
const fullPath =
|
|
5991
|
+
const fullPath = path20.join(dir, entry.name);
|
|
5636
5992
|
const stat = fs7.statSync(fullPath);
|
|
5637
5993
|
return {
|
|
5638
5994
|
name: entry.name,
|
|
@@ -5647,7 +6003,7 @@ function windowsPathExtensions(pathext) {
|
|
|
5647
6003
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
5648
6004
|
}
|
|
5649
6005
|
function resolveExecutableCandidate(dir, name, env) {
|
|
5650
|
-
const candidate =
|
|
6006
|
+
const candidate = path20.join(dir, name);
|
|
5651
6007
|
if (env.platform === "win32") {
|
|
5652
6008
|
const extensions = windowsPathExtensions(env.pathext);
|
|
5653
6009
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -5696,13 +6052,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
5696
6052
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
5697
6053
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
5698
6054
|
}
|
|
5699
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
5700
|
-
const candidateDir =
|
|
5701
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
6055
|
+
const candidate = env.argv1 === void 0 ? env.execPath : path20.resolve(env.cwd, env.argv1);
|
|
6056
|
+
const candidateDir = path20.dirname(candidate);
|
|
6057
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => path20.resolve(dir) === candidateDir);
|
|
5702
6058
|
if (candidateDirOnPath) {
|
|
5703
6059
|
return candidate;
|
|
5704
6060
|
}
|
|
5705
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
6061
|
+
const viaPath = searchPathForExecutable(env.pathDirs, path20.basename(candidate), env.findExecutableInDir);
|
|
5706
6062
|
return viaPath ?? candidate;
|
|
5707
6063
|
}
|
|
5708
6064
|
function realOwnExecutablePath() {
|
|
@@ -5710,21 +6066,21 @@ function realOwnExecutablePath() {
|
|
|
5710
6066
|
argv1: process.argv[1],
|
|
5711
6067
|
execPath: process.execPath,
|
|
5712
6068
|
cwd: process.cwd(),
|
|
5713
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6069
|
+
pathDirs: (process.env.PATH ?? "").split(path20.delimiter).filter((dir) => dir !== ""),
|
|
5714
6070
|
findExecutableInDir
|
|
5715
6071
|
});
|
|
5716
6072
|
}
|
|
5717
6073
|
function realResolveClaudeBinary(ownInstallDirs) {
|
|
5718
6074
|
return () => discoverClaudeBinary({
|
|
5719
6075
|
listVersionsDir,
|
|
5720
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6076
|
+
pathDirs: (process.env.PATH ?? "").split(path20.delimiter).filter((dir) => dir !== ""),
|
|
5721
6077
|
findExecutableInDir,
|
|
5722
6078
|
ownInstallDirs: [...ownInstallDirs]
|
|
5723
6079
|
});
|
|
5724
6080
|
}
|
|
5725
6081
|
|
|
5726
6082
|
// src/checkReport.ts
|
|
5727
|
-
import
|
|
6083
|
+
import path21 from "node:path";
|
|
5728
6084
|
import { z as z10 } from "zod";
|
|
5729
6085
|
|
|
5730
6086
|
// src/cli/bool.ts
|
|
@@ -6046,7 +6402,7 @@ function loadCheckProvider(paths, name) {
|
|
|
6046
6402
|
}
|
|
6047
6403
|
function collectCheckReport(params) {
|
|
6048
6404
|
const { paths, cwd } = params;
|
|
6049
|
-
const home =
|
|
6405
|
+
const home = os4.homedir();
|
|
6050
6406
|
const claudeHome = resolveClaudeHome();
|
|
6051
6407
|
const read = cosmiconfigReader();
|
|
6052
6408
|
const classification = loadClassification(paths);
|
|
@@ -6091,10 +6447,10 @@ function collectCheckReport(params) {
|
|
|
6091
6447
|
}).input;
|
|
6092
6448
|
const providerName = cascadeProviderName(cascade);
|
|
6093
6449
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
6094
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
6450
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : path21.join(paths.identitiesDir, identityDecision.name);
|
|
6095
6451
|
const settingsFiles = {
|
|
6096
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
6097
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
6452
|
+
"settings.json": realFsPort.readFileUtf8(path21.join(claudeHome, "settings.json")),
|
|
6453
|
+
"settings.local.json": realFsPort.readFileUtf8(path21.join(claudeHome, "settings.local.json"))
|
|
6098
6454
|
};
|
|
6099
6455
|
const report = runCheck({
|
|
6100
6456
|
cwd,
|
|
@@ -6124,7 +6480,7 @@ function collectCheckReport(params) {
|
|
|
6124
6480
|
|
|
6125
6481
|
// src/claudeShim.ts
|
|
6126
6482
|
import fs8 from "node:fs";
|
|
6127
|
-
import
|
|
6483
|
+
import path22 from "node:path";
|
|
6128
6484
|
import { z as z11 } from "zod";
|
|
6129
6485
|
var ClaudeShimStateSchema = z11.strictObject({
|
|
6130
6486
|
targetPath: z11.string().min(1),
|
|
@@ -6135,29 +6491,29 @@ function commandFilename(ownExecutablePath, commandName) {
|
|
|
6135
6491
|
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
6136
6492
|
}
|
|
6137
6493
|
function resolveOwnInstallDirs(paths, ownExecutablePath) {
|
|
6138
|
-
const dirs = [
|
|
6494
|
+
const dirs = [path22.dirname(ownExecutablePath)];
|
|
6139
6495
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6140
6496
|
if (state !== void 0) {
|
|
6141
|
-
dirs.push(
|
|
6497
|
+
dirs.push(path22.dirname(state.targetPath));
|
|
6142
6498
|
}
|
|
6143
6499
|
return dirs;
|
|
6144
6500
|
}
|
|
6145
6501
|
var nodeLinkFs = { link: fs8.linkSync, copyFile: fs8.copyFileSync, chmod: fs8.chmodSync };
|
|
6146
6502
|
function findPathShadow(params) {
|
|
6147
|
-
const resolvedTargetDir =
|
|
6503
|
+
const resolvedTargetDir = path22.resolve(params.targetDir);
|
|
6148
6504
|
for (const dir of params.pathDirs) {
|
|
6149
6505
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
6150
6506
|
if (found === void 0) {
|
|
6151
6507
|
continue;
|
|
6152
6508
|
}
|
|
6153
|
-
return
|
|
6509
|
+
return path22.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
6154
6510
|
}
|
|
6155
|
-
return params.pathDirs.some((dir) =>
|
|
6511
|
+
return params.pathDirs.some((dir) => path22.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
6156
6512
|
}
|
|
6157
6513
|
|
|
6158
6514
|
// src/doctorReport.ts
|
|
6159
6515
|
import fs9 from "node:fs";
|
|
6160
|
-
import
|
|
6516
|
+
import path23 from "node:path";
|
|
6161
6517
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
6162
6518
|
if (status.status !== "shadowed") {
|
|
6163
6519
|
return status;
|
|
@@ -6181,7 +6537,7 @@ function validateJson(schema, input) {
|
|
|
6181
6537
|
return { ok: true, data: result.data };
|
|
6182
6538
|
}
|
|
6183
6539
|
function pushPathResolution(push, resolution) {
|
|
6184
|
-
const ownDir =
|
|
6540
|
+
const ownDir = path23.dirname(resolution.ownExecutablePath);
|
|
6185
6541
|
switch (resolution.agentShim.status) {
|
|
6186
6542
|
case "ok":
|
|
6187
6543
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -6249,14 +6605,14 @@ function pushProvider(push, entry) {
|
|
|
6249
6605
|
}
|
|
6250
6606
|
function pushLegacyName(push, params) {
|
|
6251
6607
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
6252
|
-
if (legacyVariables.length === 0 &&
|
|
6608
|
+
if (legacyVariables.length === 0 && path23.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
6253
6609
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
6254
6610
|
return;
|
|
6255
6611
|
}
|
|
6256
6612
|
for (const name of legacyVariables) {
|
|
6257
6613
|
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
6614
|
}
|
|
6259
|
-
if (
|
|
6615
|
+
if (path23.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
6260
6616
|
push(
|
|
6261
6617
|
"legacy-name",
|
|
6262
6618
|
"warn",
|
|
@@ -6564,18 +6920,18 @@ function collectDoctorReport(params) {
|
|
|
6564
6920
|
const { paths } = params;
|
|
6565
6921
|
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
6922
|
const identities = identityNames.map((name) => {
|
|
6567
|
-
const farmRoot =
|
|
6568
|
-
const identityPath =
|
|
6923
|
+
const farmRoot = path23.join(paths.identitiesDir, name);
|
|
6924
|
+
const identityPath = path23.join(farmRoot, "identity.json");
|
|
6569
6925
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
6570
6926
|
});
|
|
6571
6927
|
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
6928
|
const configProfiles = profileNames.map((name) => {
|
|
6573
|
-
const profilePath =
|
|
6929
|
+
const profilePath = path23.join(paths.configProfilesDir, `${name}.json`);
|
|
6574
6930
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
6575
6931
|
});
|
|
6576
6932
|
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
6933
|
const providers = providerNames.map((name) => {
|
|
6578
|
-
const providerPath =
|
|
6934
|
+
const providerPath = path23.join(paths.providersDir, `${name}.json`);
|
|
6579
6935
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
6580
6936
|
});
|
|
6581
6937
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -6587,12 +6943,12 @@ function collectDoctorReport(params) {
|
|
|
6587
6943
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
6588
6944
|
}
|
|
6589
6945
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6590
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
6946
|
+
const pathDirs = (params.env.PATH ?? "").split(path23.delimiter).filter((dir) => dir !== "");
|
|
6591
6947
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
6592
6948
|
findPathShadow({
|
|
6593
6949
|
pathDirs,
|
|
6594
|
-
targetDir:
|
|
6595
|
-
targetFilename:
|
|
6950
|
+
targetDir: path23.dirname(shimState.targetPath),
|
|
6951
|
+
targetFilename: path23.basename(shimState.targetPath),
|
|
6596
6952
|
findExecutableInDir
|
|
6597
6953
|
}),
|
|
6598
6954
|
shimState.targetPath,
|
|
@@ -6615,7 +6971,7 @@ function collectDoctorReport(params) {
|
|
|
6615
6971
|
agentShim: refinePathShadow(
|
|
6616
6972
|
findPathShadow({
|
|
6617
6973
|
pathDirs,
|
|
6618
|
-
targetDir:
|
|
6974
|
+
targetDir: path23.dirname(ownExecutablePath),
|
|
6619
6975
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
6620
6976
|
findExecutableInDir
|
|
6621
6977
|
}),
|
|
@@ -6633,6 +6989,975 @@ function collectDoctorReport(params) {
|
|
|
6633
6989
|
});
|
|
6634
6990
|
return report;
|
|
6635
6991
|
}
|
|
6992
|
+
|
|
6993
|
+
// src/launcher.ts
|
|
6994
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
6995
|
+
import path25 from "node:path";
|
|
6996
|
+
|
|
6997
|
+
// src/launcher/argv.ts
|
|
6998
|
+
var ConflictingIdentityError = class extends UsageError {
|
|
6999
|
+
constructor(positional, flag) {
|
|
7000
|
+
super(`The launch names two identities: "@${positional}" and "--identity ${flag}". Pass only one.`);
|
|
7001
|
+
this.positional = positional;
|
|
7002
|
+
this.flag = flag;
|
|
7003
|
+
this.name = "ConflictingIdentityError";
|
|
7004
|
+
}
|
|
7005
|
+
positional;
|
|
7006
|
+
flag;
|
|
7007
|
+
};
|
|
7008
|
+
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--category", "--share", "--hide"];
|
|
7009
|
+
var BOOLEAN_FLAGS = [
|
|
7010
|
+
{ flag: "--headroom", key: "headroom" },
|
|
7011
|
+
{ flag: "--track-usage", key: "trackUsage" },
|
|
7012
|
+
{ flag: "--skip-permissions", key: "skipPermissions" },
|
|
7013
|
+
{ flag: "--remote-control", key: "remoteControl" },
|
|
7014
|
+
{ flag: "--wait", key: "wait" }
|
|
7015
|
+
];
|
|
7016
|
+
var LAUNCHER_FLAG_NAMES = [
|
|
7017
|
+
...VALUED_FLAGS,
|
|
7018
|
+
"--no-provider",
|
|
7019
|
+
...BOOLEAN_FLAGS.flatMap(({ flag }) => [flag, `--no-${flag.slice(2)}`])
|
|
7020
|
+
];
|
|
7021
|
+
var TERMINATOR2 = "--";
|
|
7022
|
+
function matchValuedFlag(token) {
|
|
7023
|
+
for (const flag of VALUED_FLAGS) {
|
|
7024
|
+
if (token === flag) {
|
|
7025
|
+
return { flag };
|
|
7026
|
+
}
|
|
7027
|
+
if (token.startsWith(`${flag}=`)) {
|
|
7028
|
+
return { flag, inlineValue: token.slice(flag.length + 1) };
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
return void 0;
|
|
7032
|
+
}
|
|
7033
|
+
function matchBooleanFlag(token) {
|
|
7034
|
+
for (const { flag, key } of BOOLEAN_FLAGS) {
|
|
7035
|
+
if (token === flag) {
|
|
7036
|
+
return { key, value: true };
|
|
7037
|
+
}
|
|
7038
|
+
if (token === `--no-${flag.slice(2)}`) {
|
|
7039
|
+
return { key, value: false };
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
7042
|
+
return void 0;
|
|
7043
|
+
}
|
|
7044
|
+
function parseLauncherArgv(argv) {
|
|
7045
|
+
const first = argv[0];
|
|
7046
|
+
const positionalIdentity = first !== void 0 && first.startsWith("@") && first.length > 1 ? first.slice(1) : void 0;
|
|
7047
|
+
const remaining = positionalIdentity === void 0 ? argv : argv.slice(1);
|
|
7048
|
+
let flagIdentity;
|
|
7049
|
+
let configProfile;
|
|
7050
|
+
let provider;
|
|
7051
|
+
const booleans = {};
|
|
7052
|
+
const categoryFlags = [];
|
|
7053
|
+
const shareFlags = [];
|
|
7054
|
+
const hideFlags = [];
|
|
7055
|
+
const rest = [];
|
|
7056
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
7057
|
+
const token = remaining[index];
|
|
7058
|
+
if (token === void 0) {
|
|
7059
|
+
continue;
|
|
7060
|
+
}
|
|
7061
|
+
if (token === TERMINATOR2) {
|
|
7062
|
+
rest.push(...remaining.slice(index));
|
|
7063
|
+
break;
|
|
7064
|
+
}
|
|
7065
|
+
if (token === "--no-provider") {
|
|
7066
|
+
provider = false;
|
|
7067
|
+
continue;
|
|
7068
|
+
}
|
|
7069
|
+
const booleanFlag = matchBooleanFlag(token);
|
|
7070
|
+
if (booleanFlag !== void 0) {
|
|
7071
|
+
booleans[booleanFlag.key] = booleanFlag.value;
|
|
7072
|
+
continue;
|
|
7073
|
+
}
|
|
7074
|
+
const matched = matchValuedFlag(token);
|
|
7075
|
+
if (matched === void 0) {
|
|
7076
|
+
rest.push(token);
|
|
7077
|
+
continue;
|
|
7078
|
+
}
|
|
7079
|
+
let value;
|
|
7080
|
+
if (matched.inlineValue !== void 0) {
|
|
7081
|
+
value = matched.inlineValue;
|
|
7082
|
+
} else {
|
|
7083
|
+
const next = remaining[index + 1];
|
|
7084
|
+
if (next === void 0 || next === TERMINATOR2) {
|
|
7085
|
+
rest.push(token);
|
|
7086
|
+
continue;
|
|
7087
|
+
}
|
|
7088
|
+
value = next;
|
|
7089
|
+
index += 1;
|
|
7090
|
+
}
|
|
7091
|
+
switch (matched.flag) {
|
|
7092
|
+
case "--identity":
|
|
7093
|
+
flagIdentity = value;
|
|
7094
|
+
break;
|
|
7095
|
+
case "--config-profile":
|
|
7096
|
+
configProfile = value;
|
|
7097
|
+
break;
|
|
7098
|
+
case "--provider":
|
|
7099
|
+
provider = value;
|
|
7100
|
+
break;
|
|
7101
|
+
case "--category":
|
|
7102
|
+
categoryFlags.push(value);
|
|
7103
|
+
break;
|
|
7104
|
+
case "--share":
|
|
7105
|
+
shareFlags.push(value);
|
|
7106
|
+
break;
|
|
7107
|
+
case "--hide":
|
|
7108
|
+
hideFlags.push(value);
|
|
7109
|
+
break;
|
|
7110
|
+
default:
|
|
7111
|
+
matched.flag;
|
|
7112
|
+
}
|
|
7113
|
+
}
|
|
7114
|
+
if (positionalIdentity !== void 0 && flagIdentity !== void 0 && positionalIdentity !== flagIdentity) {
|
|
7115
|
+
throw new ConflictingIdentityError(positionalIdentity, flagIdentity);
|
|
7116
|
+
}
|
|
7117
|
+
const identity = flagIdentity ?? positionalIdentity;
|
|
7118
|
+
return {
|
|
7119
|
+
...identity === void 0 ? {} : { identity },
|
|
7120
|
+
...configProfile === void 0 ? {} : { configProfile },
|
|
7121
|
+
...provider === void 0 ? {} : { provider },
|
|
7122
|
+
...booleans,
|
|
7123
|
+
categoryFlags,
|
|
7124
|
+
shareFlags,
|
|
7125
|
+
hideFlags,
|
|
7126
|
+
rest
|
|
7127
|
+
};
|
|
7128
|
+
}
|
|
7129
|
+
|
|
7130
|
+
// src/cli/pairs.ts
|
|
7131
|
+
function splitCommas(input) {
|
|
7132
|
+
if (input === "") {
|
|
7133
|
+
return [];
|
|
7134
|
+
}
|
|
7135
|
+
return input.split(",");
|
|
7136
|
+
}
|
|
7137
|
+
function parsePair(input) {
|
|
7138
|
+
const eqIndex = input.indexOf("=");
|
|
7139
|
+
if (eqIndex === -1) {
|
|
7140
|
+
throw new Error(`Expected "<key>=<value>", got "${input}" (no "=" found)`);
|
|
7141
|
+
}
|
|
7142
|
+
const key = input.slice(0, eqIndex);
|
|
7143
|
+
const value = input.slice(eqIndex + 1);
|
|
7144
|
+
if (key === "") {
|
|
7145
|
+
throw new Error(`Expected a non-empty key before "=" in "${input}"`);
|
|
7146
|
+
}
|
|
7147
|
+
return { key, value };
|
|
7148
|
+
}
|
|
7149
|
+
function parseBool(input) {
|
|
7150
|
+
const parsed = parseBoolWord(input);
|
|
7151
|
+
if (parsed === void 0) {
|
|
7152
|
+
throw new Error(`Expected "true", "false", "1" or "0", got "${input}"`);
|
|
7153
|
+
}
|
|
7154
|
+
return parsed;
|
|
7155
|
+
}
|
|
7156
|
+
function parseBoolPairList(input) {
|
|
7157
|
+
const result = {};
|
|
7158
|
+
for (const piece of splitCommas(input)) {
|
|
7159
|
+
const { key, value } = parsePair(piece);
|
|
7160
|
+
result[key] = parseBool(value);
|
|
7161
|
+
}
|
|
7162
|
+
return result;
|
|
7163
|
+
}
|
|
7164
|
+
|
|
7165
|
+
// src/launcher/cliOverride.ts
|
|
7166
|
+
var InvalidCliCategoryError = class extends CliError {
|
|
7167
|
+
constructor(categoryName) {
|
|
7168
|
+
super(`"${categoryName}" is not a category this launch may toggle (runtime, history, knowledge, settings).`);
|
|
7169
|
+
this.categoryName = categoryName;
|
|
7170
|
+
this.name = "InvalidCliCategoryError";
|
|
7171
|
+
}
|
|
7172
|
+
categoryName;
|
|
7173
|
+
};
|
|
7174
|
+
var InvalidCliEntryKeyError = class extends CliError {
|
|
7175
|
+
constructor(key) {
|
|
7176
|
+
super(`"${key}" is not a valid entries key \u2014 it must start with "<category>/", e.g. "knowledge/skills/commit".`);
|
|
7177
|
+
this.key = key;
|
|
7178
|
+
this.name = "InvalidCliEntryKeyError";
|
|
7179
|
+
}
|
|
7180
|
+
key;
|
|
7181
|
+
};
|
|
7182
|
+
function toCategoryMap(pairs) {
|
|
7183
|
+
const expanded = expandAllCategoryKey(pairs);
|
|
7184
|
+
const result = {};
|
|
7185
|
+
for (const [key, value] of Object.entries(expanded)) {
|
|
7186
|
+
if (!isOverridableCategory(key)) {
|
|
7187
|
+
throw new InvalidCliCategoryError(key);
|
|
7188
|
+
}
|
|
7189
|
+
result[key] = value;
|
|
7190
|
+
}
|
|
7191
|
+
return result;
|
|
7192
|
+
}
|
|
7193
|
+
function toEntries(pairs) {
|
|
7194
|
+
for (const key of Object.keys(pairs)) {
|
|
7195
|
+
if (!ENTRY_KEY_RE.test(key)) {
|
|
7196
|
+
throw new InvalidCliEntryKeyError(key);
|
|
7197
|
+
}
|
|
7198
|
+
}
|
|
7199
|
+
return pairs;
|
|
7200
|
+
}
|
|
7201
|
+
function parseOverride(source, raw, parse) {
|
|
7202
|
+
try {
|
|
7203
|
+
return parse(raw);
|
|
7204
|
+
} catch (error) {
|
|
7205
|
+
if (error instanceof Error && !(error instanceof CliError)) {
|
|
7206
|
+
throw new UsageError(`Invalid ${source} value "${raw}": ${error.message}.`);
|
|
7207
|
+
}
|
|
7208
|
+
throw error;
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
function buildCliOverride(params) {
|
|
7212
|
+
let categoryPairs = {};
|
|
7213
|
+
if (params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== "") {
|
|
7214
|
+
categoryPairs = parseOverride("AGENT_SHIM_CATEGORY_OVERRIDE", params.env.AGENT_SHIM_CATEGORY_OVERRIDE, parseBoolPairList);
|
|
7215
|
+
}
|
|
7216
|
+
for (const flagValue of params.categoryFlags) {
|
|
7217
|
+
const pair = parseOverride("--category", flagValue, (raw) => {
|
|
7218
|
+
const { key, value } = parsePair(raw);
|
|
7219
|
+
return { key, value: parseBool(value) };
|
|
7220
|
+
});
|
|
7221
|
+
categoryPairs[pair.key] = pair.value;
|
|
7222
|
+
}
|
|
7223
|
+
let entryPairs = {};
|
|
7224
|
+
if (params.env.AGENT_SHIM_ENTRY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_ENTRY_OVERRIDE !== "") {
|
|
7225
|
+
entryPairs = parseOverride("AGENT_SHIM_ENTRY_OVERRIDE", params.env.AGENT_SHIM_ENTRY_OVERRIDE, parseBoolPairList);
|
|
7226
|
+
}
|
|
7227
|
+
for (const entryPath of params.shareFlags) {
|
|
7228
|
+
entryPairs[entryPath] = true;
|
|
7229
|
+
}
|
|
7230
|
+
for (const entryPath of params.hideFlags) {
|
|
7231
|
+
entryPairs[entryPath] = false;
|
|
7232
|
+
}
|
|
7233
|
+
const hasCategories = Object.keys(categoryPairs).length > 0;
|
|
7234
|
+
const hasEntries = Object.keys(entryPairs).length > 0;
|
|
7235
|
+
const hasProvider = params.providerFlag !== void 0 && params.providerFlag !== "";
|
|
7236
|
+
if (!hasCategories && !hasEntries && !hasProvider) {
|
|
7237
|
+
return void 0;
|
|
7238
|
+
}
|
|
7239
|
+
return {
|
|
7240
|
+
...hasCategories ? { categories: toCategoryMap(categoryPairs) } : {},
|
|
7241
|
+
...hasEntries ? { entries: toEntries(entryPairs) } : {},
|
|
7242
|
+
...hasProvider ? { launch: { provider: params.providerFlag } } : {}
|
|
7243
|
+
};
|
|
7244
|
+
}
|
|
7245
|
+
|
|
7246
|
+
// src/launcher/flags.ts
|
|
7247
|
+
import path24 from "node:path";
|
|
7248
|
+
|
|
7249
|
+
// src/headroom/headers.ts
|
|
7250
|
+
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
7251
|
+
const merged = /* @__PURE__ */ new Map();
|
|
7252
|
+
for (const block of existing) {
|
|
7253
|
+
if (block === void 0) {
|
|
7254
|
+
continue;
|
|
7255
|
+
}
|
|
7256
|
+
for (const line of block.split("\n")) {
|
|
7257
|
+
const entry = parseCustomHeaderLine(line);
|
|
7258
|
+
if (entry === void 0) {
|
|
7259
|
+
continue;
|
|
7260
|
+
}
|
|
7261
|
+
merged.set(entry.name, entry.value);
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
for (const entry of additions) {
|
|
7265
|
+
merged.set(entry.name, entry.value);
|
|
7266
|
+
}
|
|
7267
|
+
return [...merged].map(([name, value]) => `${name}: ${value}`).join("\n");
|
|
7268
|
+
}
|
|
7269
|
+
function parseCustomHeaderLine(line) {
|
|
7270
|
+
const trimmed = line.trim();
|
|
7271
|
+
if (trimmed === "") {
|
|
7272
|
+
return void 0;
|
|
7273
|
+
}
|
|
7274
|
+
const colon = trimmed.indexOf(":");
|
|
7275
|
+
if (colon <= 0) {
|
|
7276
|
+
return void 0;
|
|
7277
|
+
}
|
|
7278
|
+
return { name: trimmed.slice(0, colon).trim(), value: trimmed.slice(colon + 1).trim() };
|
|
7279
|
+
}
|
|
7280
|
+
|
|
7281
|
+
// src/launcher/flags.ts
|
|
7282
|
+
function resolveLaunchFlags(params) {
|
|
7283
|
+
return {
|
|
7284
|
+
skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
|
|
7285
|
+
remoteControl: params.flags?.remoteControl ?? parseEnvBool("AGENT_SHIM_REMOTE_CONTROL", params.env.AGENT_SHIM_REMOTE_CONTROL) ?? params.cascade?.remoteControl ?? false,
|
|
7286
|
+
headroom: params.flags?.headroom ?? parseEnvBool("AGENT_SHIM_HEADROOM", params.env.AGENT_SHIM_HEADROOM) ?? params.cascade?.headroom ?? false,
|
|
7287
|
+
trackUsage: params.flags?.trackUsage ?? parseEnvBool("AGENT_SHIM_TRACK_USAGE", params.env.AGENT_SHIM_TRACK_USAGE) ?? params.cascade?.trackUsage ?? params.trackUsageDefault ?? false
|
|
7288
|
+
};
|
|
7289
|
+
}
|
|
7290
|
+
function buildFlagArgs(flags) {
|
|
7291
|
+
const args = [];
|
|
7292
|
+
if (flags.skipPermissions) {
|
|
7293
|
+
args.push("--dangerously-skip-permissions");
|
|
7294
|
+
}
|
|
7295
|
+
if (flags.remoteControl) {
|
|
7296
|
+
args.push("--remote-control=");
|
|
7297
|
+
}
|
|
7298
|
+
return args;
|
|
7299
|
+
}
|
|
7300
|
+
function buildArgv(params) {
|
|
7301
|
+
return [...params.toolFlags, ...params.extraFlags, ...params.passthrough];
|
|
7302
|
+
}
|
|
7303
|
+
var LOOPBACK_HOSTS = ["127.0.0.1", "localhost", "::1"];
|
|
7304
|
+
function isLoopbackUrl(baseUrl) {
|
|
7305
|
+
const host = new URL(baseUrl).hostname.replace(/^\[|\]$/g, "");
|
|
7306
|
+
return LOOPBACK_HOSTS.some((loopback) => loopback === host);
|
|
7307
|
+
}
|
|
7308
|
+
function withLoopbackHosts(existing) {
|
|
7309
|
+
const listed = (existing ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry !== "");
|
|
7310
|
+
return [...listed, ...LOOPBACK_HOSTS.filter((host) => !listed.includes(host))].join(",");
|
|
7311
|
+
}
|
|
7312
|
+
function buildEnv(params) {
|
|
7313
|
+
const env = { ...params.baseEnv };
|
|
7314
|
+
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7315
|
+
let providerCustomHeaders;
|
|
7316
|
+
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
7317
|
+
env.CLAUDE_CONFIG_DIR = path24.join(params.identitiesDir, params.resolvedIdentityName);
|
|
7318
|
+
}
|
|
7319
|
+
if (params.provider !== void 0) {
|
|
7320
|
+
const providerEnv = params.provider.definition.env ?? {};
|
|
7321
|
+
providerCustomHeaders = providerEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7322
|
+
env.ANTHROPIC_BASE_URL = params.provider.baseUrl;
|
|
7323
|
+
if (isLoopbackUrl(params.provider.baseUrl)) {
|
|
7324
|
+
env.NO_PROXY = withLoopbackHosts(params.baseEnv.NO_PROXY);
|
|
7325
|
+
env.no_proxy = withLoopbackHosts(params.baseEnv.no_proxy);
|
|
7326
|
+
}
|
|
7327
|
+
env.AGENT_SHIM_PROVIDER = params.provider.definition.displayName;
|
|
7328
|
+
for (const [key, value] of Object.entries(providerEnv)) {
|
|
7329
|
+
env[key] = value;
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
const credential = params.provider?.credential ?? params.identityCredential;
|
|
7333
|
+
if (credential !== void 0) {
|
|
7334
|
+
for (const [variable, value] of Object.entries(credentialVariables(credential.target, credential.token))) {
|
|
7335
|
+
env[variable] = value;
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
if (params.headroom !== void 0) {
|
|
7339
|
+
env.HEADROOM_PROXY_URL = `http://127.0.0.1:${String(params.headroom.port)}`;
|
|
7340
|
+
}
|
|
7341
|
+
if (params.frontdoor !== void 0) {
|
|
7342
|
+
env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeaders(
|
|
7343
|
+
[
|
|
7344
|
+
...parentCustomHeaders === void 0 ? [] : [parentCustomHeaders],
|
|
7345
|
+
...providerCustomHeaders === void 0 ? [] : [providerCustomHeaders]
|
|
7346
|
+
],
|
|
7347
|
+
[
|
|
7348
|
+
...params.resolvedIdentityName === void 0 ? [] : [{ name: IDENTITY_HEADER, value: params.resolvedIdentityName }],
|
|
7349
|
+
{ name: SESSION_HEADER, value: params.sessionId },
|
|
7350
|
+
{ name: AUTH_HEADER, value: params.frontdoor.sessionToken },
|
|
7351
|
+
...params.headroom === void 0 ? [] : [
|
|
7352
|
+
{ name: HEADROOM_FLAG_HEADER, value: "1" },
|
|
7353
|
+
{ name: PROJECT_ID_HEADER, value: params.headroom.projectId }
|
|
7354
|
+
]
|
|
7355
|
+
]
|
|
7356
|
+
);
|
|
7357
|
+
env.NODE_EXTRA_CA_CERTS = params.frontdoor.trustBundlePath;
|
|
7358
|
+
if (params.provider === void 0) {
|
|
7359
|
+
env.HTTPS_PROXY = connectProxyUrl(params.frontdoor.connectPort, params.frontdoor.sessionToken);
|
|
7360
|
+
}
|
|
7361
|
+
}
|
|
7362
|
+
return env;
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7365
|
+
// src/launcher/extraFlags.ts
|
|
7366
|
+
function splitExtraFlags(value) {
|
|
7367
|
+
if (value === void 0) {
|
|
7368
|
+
return [];
|
|
7369
|
+
}
|
|
7370
|
+
const trimmed = value.trim();
|
|
7371
|
+
if (trimmed === "") {
|
|
7372
|
+
return [];
|
|
7373
|
+
}
|
|
7374
|
+
return trimmed.split(/\s+/);
|
|
7375
|
+
}
|
|
7376
|
+
|
|
7377
|
+
// src/launcher.ts
|
|
7378
|
+
function resolveIdentityCredential(identity, credential, env, port) {
|
|
7379
|
+
if (port === void 0) {
|
|
7380
|
+
return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
|
|
7381
|
+
}
|
|
7382
|
+
const resolution = resolveCredential({ credential, env, port, subject: `identity ${identity}` });
|
|
7383
|
+
return resolution.ok ? resolution : { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
7384
|
+
}
|
|
7385
|
+
function pickFromPool(decided, params, parsedArgv) {
|
|
7386
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7387
|
+
if (decided.pool === void 0) {
|
|
7388
|
+
return { identityDecision: decided };
|
|
7389
|
+
}
|
|
7390
|
+
const farmRuntime = params.farm;
|
|
7391
|
+
if (farmRuntime === void 0) {
|
|
7392
|
+
log.error(`agent-shim: pool "${decided.pool}" was selected, but this launcher has no farm wired to read usage and pick a member with.`);
|
|
7393
|
+
return proc.exit(1);
|
|
7394
|
+
}
|
|
7395
|
+
const resolution = resolvePoolLaunch({
|
|
7396
|
+
poolName: decided.pool,
|
|
7397
|
+
selectedVia: decided.source,
|
|
7398
|
+
pools: params.pools,
|
|
7399
|
+
paths,
|
|
7400
|
+
fs: fs12,
|
|
7401
|
+
usageFs: farmRuntime.fs,
|
|
7402
|
+
cwd: farmRuntime.cwd,
|
|
7403
|
+
now: farmRuntime.now,
|
|
7404
|
+
sleep: farmRuntime.lock.sleep,
|
|
7405
|
+
passthrough: parsedArgv.rest,
|
|
7406
|
+
wait: parsedArgv.wait === true,
|
|
7407
|
+
log
|
|
7408
|
+
});
|
|
7409
|
+
if (!resolution.ok) {
|
|
7410
|
+
log.error(resolution.message);
|
|
7411
|
+
return proc.exit(1);
|
|
7412
|
+
}
|
|
7413
|
+
return { identityDecision: { ...decided, name: resolution.identity }, poolExplanation: resolution.explanation };
|
|
7414
|
+
}
|
|
7415
|
+
function prepareLaunch(params) {
|
|
7416
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7417
|
+
const { env, argv } = proc;
|
|
7418
|
+
const parsedArgv = parseLauncherArgv(argv);
|
|
7419
|
+
const cliOverride = buildCliOverride({
|
|
7420
|
+
env,
|
|
7421
|
+
categoryFlags: parsedArgv.categoryFlags,
|
|
7422
|
+
shareFlags: parsedArgv.shareFlags,
|
|
7423
|
+
hideFlags: parsedArgv.hideFlags
|
|
7424
|
+
});
|
|
7425
|
+
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
7426
|
+
const decidedIdentity = decideIdentity({
|
|
7427
|
+
env,
|
|
7428
|
+
argv0Identity: parsedArgv.identity,
|
|
7429
|
+
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
7430
|
+
readActiveIdentityFile: () => {
|
|
7431
|
+
const raw = fs12.readFileUtf8(paths.activeIdentityFile);
|
|
7432
|
+
if (raw === void 0) {
|
|
7433
|
+
return void 0;
|
|
7434
|
+
}
|
|
7435
|
+
const trimmed = raw.trim();
|
|
7436
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
7437
|
+
}
|
|
7438
|
+
});
|
|
7439
|
+
const { identityDecision, poolExplanation } = pickFromPool(decidedIdentity, params, parsedArgv);
|
|
7440
|
+
const farmIdentity = configDirEscapeHatchApplies ? void 0 : identityDecision.name;
|
|
7441
|
+
const farm = farmIdentity === void 0 ? void 0 : params.farm;
|
|
7442
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7443
|
+
let recovery;
|
|
7444
|
+
try {
|
|
7445
|
+
recovery = recoverFarm({
|
|
7446
|
+
fs: farm.fs,
|
|
7447
|
+
identitiesDir: paths.identitiesDir,
|
|
7448
|
+
identity: farmIdentity,
|
|
7449
|
+
now: farm.now,
|
|
7450
|
+
lock: farm.lock,
|
|
7451
|
+
classification: farm.classification
|
|
7452
|
+
});
|
|
7453
|
+
} catch (error) {
|
|
7454
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7455
|
+
log.error(error.message);
|
|
7456
|
+
proc.exit(1);
|
|
7457
|
+
}
|
|
7458
|
+
throw error;
|
|
7459
|
+
}
|
|
7460
|
+
for (const diagnostic of recoveryDiagnostics(recovery, farmIdentity)) {
|
|
7461
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
let loadedIdentity;
|
|
7465
|
+
if (identityDecision.name !== void 0) {
|
|
7466
|
+
loadedIdentity = loadIdentity(paths.identitiesDir, identityDecision.name, fs12);
|
|
7467
|
+
if (loadedIdentity === void 0) {
|
|
7468
|
+
log.error(
|
|
7469
|
+
`agent-shim: no identity named "${identityDecision.name}" (selected via ${identityDecision.source}). Run \`agent-shim identity add ${identityDecision.name}\` first.`
|
|
7470
|
+
);
|
|
7471
|
+
proc.exit(1);
|
|
7472
|
+
}
|
|
7473
|
+
}
|
|
7474
|
+
const configProfileDecision = decideConfigProfile({
|
|
7475
|
+
env,
|
|
7476
|
+
cliFlagConfigProfile: params.cliFlagConfigProfile ?? parsedArgv.configProfile,
|
|
7477
|
+
directoryRuleConfigProfile: params.directoryRuleConfigProfile,
|
|
7478
|
+
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
7479
|
+
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
7480
|
+
});
|
|
7481
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path25.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
7482
|
+
log.error(
|
|
7483
|
+
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
7484
|
+
);
|
|
7485
|
+
proc.exit(1);
|
|
7486
|
+
}
|
|
7487
|
+
const farmContext = farm !== void 0 && farmIdentity !== void 0 ? { farm, identity: farmIdentity, cascade: farm.loadCascade(configProfileDecision.name, cliOverride) } : void 0;
|
|
7488
|
+
const provider = parsedArgv.provider === false ? void 0 : resolveProvider({
|
|
7489
|
+
paths,
|
|
7490
|
+
port: fs12,
|
|
7491
|
+
env,
|
|
7492
|
+
...params.credentials === void 0 ? {} : { credentials: params.credentials },
|
|
7493
|
+
...parsedArgv.provider === void 0 ? {} : { cliProvider: parsedArgv.provider },
|
|
7494
|
+
...farmContext === void 0 ? {} : { cascade: farmContext.cascade }
|
|
7495
|
+
});
|
|
7496
|
+
if (provider !== void 0 && !provider.ok) {
|
|
7497
|
+
log.error(provider.message);
|
|
7498
|
+
proc.exit(provider.status);
|
|
7499
|
+
}
|
|
7500
|
+
const resolvedProvider = provider?.ok === true ? provider.provider : void 0;
|
|
7501
|
+
for (const warning of provider?.ok === true ? provider.warnings : []) {
|
|
7502
|
+
log.warn(warning);
|
|
7503
|
+
}
|
|
7504
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7505
|
+
try {
|
|
7506
|
+
const snapshot = readUsageSnapshot(farm.fs, paths.usageSnapshotsDir, farmIdentity);
|
|
7507
|
+
for (const warning of quotaWarnings(snapshot, resolvedProvider?.name ?? ANTHROPIC_PROVIDER, farm.now())) {
|
|
7508
|
+
log.warn(warning);
|
|
7509
|
+
}
|
|
7510
|
+
} catch (error) {
|
|
7511
|
+
if (!(error instanceof UsageSnapshotError)) {
|
|
7512
|
+
throw error;
|
|
7513
|
+
}
|
|
7514
|
+
log.warn(`agent-shim: ${error.message}`);
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7517
|
+
const identityCredentialBlock = loadedIdentity?.config.credential;
|
|
7518
|
+
const identityResolution = resolvedProvider === void 0 && farmIdentity !== void 0 && identityCredentialBlock !== void 0 ? resolveIdentityCredential(farmIdentity, identityCredentialBlock, env, params.credentials) : void 0;
|
|
7519
|
+
if (identityResolution !== void 0 && !identityResolution.ok) {
|
|
7520
|
+
log.error(identityResolution.message);
|
|
7521
|
+
proc.exit(identityResolution.status);
|
|
7522
|
+
}
|
|
7523
|
+
const identityCredential = identityResolution?.ok === true ? identityResolution.credential : void 0;
|
|
7524
|
+
for (const warning of identityCredential?.warnings ?? []) {
|
|
7525
|
+
log.warn(warning);
|
|
7526
|
+
}
|
|
7527
|
+
const guardResult = evaluateAmbientCredentialGuard({
|
|
7528
|
+
env,
|
|
7529
|
+
allowAmbientCredential: loadedIdentity?.config.allowAmbientCredential ?? false,
|
|
7530
|
+
allowAmbientCredentialOverride: parseEnvBool("AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL", env.AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL) === true,
|
|
7531
|
+
identityName: identityDecision.name,
|
|
7532
|
+
providerSelected: resolvedProvider !== void 0,
|
|
7533
|
+
...identityCredential === void 0 ? {} : { injectedCredential: { variable: CREDENTIAL_TARGET_VARS[identityCredential.target], token: identityCredential.token } }
|
|
7534
|
+
});
|
|
7535
|
+
if (!guardResult.ok) {
|
|
7536
|
+
log.error(guardResult.message);
|
|
7537
|
+
proc.exit(1);
|
|
7538
|
+
}
|
|
7539
|
+
log.info(
|
|
7540
|
+
`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)}`)
|
|
7541
|
+
);
|
|
7542
|
+
let cascadeLaunch;
|
|
7543
|
+
if (farmContext !== void 0) {
|
|
7544
|
+
const { farm: resyncFarmRuntime, identity: resyncIdentity, cascade } = farmContext;
|
|
7545
|
+
let result;
|
|
7546
|
+
try {
|
|
7547
|
+
result = resyncFarm({
|
|
7548
|
+
fs: resyncFarmRuntime.fs,
|
|
7549
|
+
identitiesDir: paths.identitiesDir,
|
|
7550
|
+
identity: resyncIdentity,
|
|
7551
|
+
...configProfileDecision.name === void 0 ? {} : { configProfile: configProfileDecision.name },
|
|
7552
|
+
claudeHome: resyncFarmRuntime.claudeHome,
|
|
7553
|
+
home: resyncFarmRuntime.home,
|
|
7554
|
+
cwd: resyncFarmRuntime.cwd,
|
|
7555
|
+
env,
|
|
7556
|
+
...resyncFarmRuntime.branch === void 0 ? {} : { branch: resyncFarmRuntime.branch },
|
|
7557
|
+
...resyncFarmRuntime.branchDetached === void 0 ? {} : { branchDetached: resyncFarmRuntime.branchDetached },
|
|
7558
|
+
cascade,
|
|
7559
|
+
classification: resyncFarmRuntime.classification,
|
|
7560
|
+
now: resyncFarmRuntime.now,
|
|
7561
|
+
uniqueSuffix: resyncFarmRuntime.uniqueSuffix,
|
|
7562
|
+
lock: resyncFarmRuntime.lock
|
|
7563
|
+
});
|
|
7564
|
+
} catch (error) {
|
|
7565
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7566
|
+
log.error(error.message);
|
|
7567
|
+
proc.exit(1);
|
|
7568
|
+
}
|
|
7569
|
+
throw error;
|
|
7570
|
+
}
|
|
7571
|
+
for (const diagnostic of result.diagnostics) {
|
|
7572
|
+
if (diagnostic.severity === "error") {
|
|
7573
|
+
log.error(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7574
|
+
} else if (diagnostic.severity === "warning") {
|
|
7575
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
log.info(
|
|
7579
|
+
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}`})`
|
|
7580
|
+
);
|
|
7581
|
+
cascadeLaunch = result.resolved.flattened.launch;
|
|
7582
|
+
} else if (params.farm !== void 0) {
|
|
7583
|
+
cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
|
|
7584
|
+
}
|
|
7585
|
+
const discovered = params.resolveClaudeBinary();
|
|
7586
|
+
const resolvedFlags = resolveLaunchFlags({
|
|
7587
|
+
env,
|
|
7588
|
+
// A pool pick reads the usage the front door records, so a pool launch records its own unless something says otherwise.
|
|
7589
|
+
...poolExplanation === void 0 ? {} : { trackUsageDefault: true },
|
|
7590
|
+
...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch },
|
|
7591
|
+
flags: {
|
|
7592
|
+
...parsedArgv.skipPermissions === void 0 ? {} : { skipPermissions: parsedArgv.skipPermissions },
|
|
7593
|
+
...parsedArgv.remoteControl === void 0 ? {} : { remoteControl: parsedArgv.remoteControl },
|
|
7594
|
+
...parsedArgv.headroom === void 0 ? {} : { headroom: parsedArgv.headroom },
|
|
7595
|
+
...parsedArgv.trackUsage === void 0 ? {} : { trackUsage: parsedArgv.trackUsage }
|
|
7596
|
+
}
|
|
7597
|
+
});
|
|
7598
|
+
const frontDoorPort = params.frontdoor;
|
|
7599
|
+
const frontDoorEngaged = resolvedProvider !== void 0 || resolvedFlags.headroom || resolvedFlags.trackUsage;
|
|
7600
|
+
if (frontDoorEngaged && frontDoorPort === void 0) {
|
|
7601
|
+
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.");
|
|
7602
|
+
proc.exit(1);
|
|
7603
|
+
}
|
|
7604
|
+
const frontDoor = frontDoorEngaged && frontDoorPort !== void 0 ? frontDoorPort.ensure(env.NODE_EXTRA_CA_CERTS) : void 0;
|
|
7605
|
+
if (frontDoor?.trustWarning !== void 0) {
|
|
7606
|
+
log.warn(frontDoor.trustWarning);
|
|
7607
|
+
}
|
|
7608
|
+
let routedProvider;
|
|
7609
|
+
if (resolvedProvider !== void 0 && frontDoor !== void 0) {
|
|
7610
|
+
routedProvider = { ...resolvedProvider, baseUrl: providerBaseUrl(frontDoor.port, resolvedProvider.name) };
|
|
7611
|
+
}
|
|
7612
|
+
let headroom;
|
|
7613
|
+
const headroomPort = params.headroom;
|
|
7614
|
+
if (resolvedFlags.headroom && headroomPort !== void 0) {
|
|
7615
|
+
headroom = headroomPort.ensure();
|
|
7616
|
+
const via = resolvedProvider === void 0 ? `OAuth via the door's CONNECT surface on 127.0.0.1:${String(frontDoor?.connectPort ?? 0)}` : `provider ${resolvedProvider.name}`;
|
|
7617
|
+
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})`);
|
|
7618
|
+
} else if (resolvedFlags.headroom) {
|
|
7619
|
+
log.error("agent-shim: headroom routing was requested but this launcher has no headroom port wired; refusing to launch without it.");
|
|
7620
|
+
proc.exit(1);
|
|
7621
|
+
} else if (frontDoor !== void 0) {
|
|
7622
|
+
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})`}`);
|
|
7623
|
+
}
|
|
7624
|
+
const frontDoorRelease = frontDoor === void 0 ? void 0 : frontDoorPort?.release;
|
|
7625
|
+
const finalArgv = buildArgv({
|
|
7626
|
+
toolFlags: buildFlagArgs(resolvedFlags),
|
|
7627
|
+
extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
|
|
7628
|
+
passthrough: parsedArgv.rest
|
|
7629
|
+
});
|
|
7630
|
+
const finalEnv = buildEnv({
|
|
7631
|
+
baseEnv: env,
|
|
7632
|
+
configDirEscapeHatch: configDirEscapeHatchApplies,
|
|
7633
|
+
resolvedIdentityName: identityDecision.name,
|
|
7634
|
+
identitiesDir: paths.identitiesDir,
|
|
7635
|
+
...routedProvider === void 0 ? {} : { provider: routedProvider },
|
|
7636
|
+
...identityCredential === void 0 ? {} : { identityCredential },
|
|
7637
|
+
...frontDoor === void 0 ? {} : { frontdoor: frontDoor },
|
|
7638
|
+
...headroom === void 0 ? {} : { headroom },
|
|
7639
|
+
sessionId: randomUUID4()
|
|
7640
|
+
});
|
|
7641
|
+
const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
|
|
7642
|
+
const release = () => {
|
|
7643
|
+
for (const registered of releases) {
|
|
7644
|
+
registered();
|
|
7645
|
+
}
|
|
7646
|
+
};
|
|
7647
|
+
return { bin: discovered.path, args: finalArgv, env: finalEnv, release };
|
|
7648
|
+
}
|
|
7649
|
+
|
|
7650
|
+
// src/launchWiring.ts
|
|
7651
|
+
import os5 from "node:os";
|
|
7652
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
7653
|
+
|
|
7654
|
+
// src/realCredentialCache.ts
|
|
7655
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
7656
|
+
import fs10 from "node:fs";
|
|
7657
|
+
import path26 from "node:path";
|
|
7658
|
+
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7659
|
+
var CACHE_DIR_MODE = 448;
|
|
7660
|
+
var CACHE_FILE_MODE = 384;
|
|
7661
|
+
function cacheFileNameFor(owner) {
|
|
7662
|
+
return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
|
|
7663
|
+
}
|
|
7664
|
+
function parseEntry(raw) {
|
|
7665
|
+
let json;
|
|
7666
|
+
try {
|
|
7667
|
+
json = JSON.parse(raw);
|
|
7668
|
+
} catch {
|
|
7669
|
+
return void 0;
|
|
7670
|
+
}
|
|
7671
|
+
const parsed = CachedCredentialSchema.safeParse(json);
|
|
7672
|
+
return parsed.success ? parsed.data : void 0;
|
|
7673
|
+
}
|
|
7674
|
+
function requireKeychain() {
|
|
7675
|
+
if (process.platform !== "darwin") {
|
|
7676
|
+
throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
function security(args, stdin) {
|
|
7680
|
+
const result = spawnSync2("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
|
|
7681
|
+
return { status: result.status, stdout: result.stdout };
|
|
7682
|
+
}
|
|
7683
|
+
function createRealCredentialCache(root) {
|
|
7684
|
+
const dir = path26.join(root, "credential-cache");
|
|
7685
|
+
const filePath = (owner) => path26.join(dir, cacheFileNameFor(owner));
|
|
7686
|
+
return {
|
|
7687
|
+
read(store, owner) {
|
|
7688
|
+
if (store === "file") {
|
|
7689
|
+
try {
|
|
7690
|
+
return parseEntry(fs10.readFileSync(filePath(owner), "utf8"));
|
|
7691
|
+
} catch {
|
|
7692
|
+
return void 0;
|
|
7693
|
+
}
|
|
7694
|
+
}
|
|
7695
|
+
requireKeychain();
|
|
7696
|
+
const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
|
|
7697
|
+
return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
|
|
7698
|
+
},
|
|
7699
|
+
write(store, owner, entry) {
|
|
7700
|
+
const serialised = JSON.stringify(entry);
|
|
7701
|
+
if (store === "file") {
|
|
7702
|
+
fs10.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
|
|
7703
|
+
fs10.chmodSync(dir, CACHE_DIR_MODE);
|
|
7704
|
+
const target = filePath(owner);
|
|
7705
|
+
const temp = `${target}.${String(process.pid)}.tmp`;
|
|
7706
|
+
fs10.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
|
|
7707
|
+
fs10.renameSync(temp, target);
|
|
7708
|
+
return;
|
|
7709
|
+
}
|
|
7710
|
+
requireKeychain();
|
|
7711
|
+
const encoded = Buffer.from(serialised, "utf8").toString("base64");
|
|
7712
|
+
const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
|
|
7713
|
+
`);
|
|
7714
|
+
if (result.status !== 0) {
|
|
7715
|
+
throw new Error(`could not write the keychain credential cache for ${owner}`);
|
|
7716
|
+
}
|
|
7717
|
+
},
|
|
7718
|
+
remove(store, owner) {
|
|
7719
|
+
if (store === "file") {
|
|
7720
|
+
fs10.rmSync(filePath(owner), { force: true });
|
|
7721
|
+
return;
|
|
7722
|
+
}
|
|
7723
|
+
requireKeychain();
|
|
7724
|
+
security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
|
|
7725
|
+
}
|
|
7726
|
+
};
|
|
7727
|
+
}
|
|
7728
|
+
function realCredentialCacheEnv(paths) {
|
|
7729
|
+
return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
|
|
7730
|
+
}
|
|
7731
|
+
|
|
7732
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7733
|
+
import fs11 from "node:fs";
|
|
7734
|
+
|
|
7735
|
+
// src/frontdoor/probe.ts
|
|
7736
|
+
import { Worker } from "node:worker_threads";
|
|
7737
|
+
var PROBE_TIMEOUT_MS = 2e3;
|
|
7738
|
+
var PROBE_WAIT_MARGIN_MS = 1e3;
|
|
7739
|
+
var REASON_BYTES = 1024;
|
|
7740
|
+
var HEADER_SLOTS = 2;
|
|
7741
|
+
var INT32_BYTES = 4;
|
|
7742
|
+
var VERDICT_PENDING = 0;
|
|
7743
|
+
var VERDICT_OK = 1;
|
|
7744
|
+
var VERDICT_FAILED = 2;
|
|
7745
|
+
var PROBE_SOURCE = `
|
|
7746
|
+
const { workerData } = require("node:worker_threads");
|
|
7747
|
+
const https = require("node:https");
|
|
7748
|
+
const { port, ca, timeoutMs, shared, ok, failed, headerSlots } = workerData;
|
|
7749
|
+
const header = new Int32Array(shared, 0, headerSlots);
|
|
7750
|
+
const reason = new Uint8Array(shared, headerSlots * 4);
|
|
7751
|
+
let done = false;
|
|
7752
|
+
const finish = (verdict, message) => {
|
|
7753
|
+
if (done) return;
|
|
7754
|
+
done = true;
|
|
7755
|
+
const bytes = Buffer.from(String(message), "utf8").subarray(0, reason.length);
|
|
7756
|
+
reason.set(bytes);
|
|
7757
|
+
Atomics.store(header, 1, bytes.length);
|
|
7758
|
+
Atomics.store(header, 0, verdict);
|
|
7759
|
+
Atomics.notify(header, 0);
|
|
7760
|
+
};
|
|
7761
|
+
const request = https.get({ host: "127.0.0.1", port, path: "/healthz", ca: [ca], agent: false }, (response) => {
|
|
7762
|
+
let body = "";
|
|
7763
|
+
response.setEncoding("utf8");
|
|
7764
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
7765
|
+
response.on("end", () => {
|
|
7766
|
+
if (response.statusCode === 200 && body === "ok") finish(ok, "");
|
|
7767
|
+
else finish(failed, "the listener answered its health probe with HTTP " + String(response.statusCode));
|
|
7768
|
+
});
|
|
7769
|
+
response.on("error", (error) => finish(failed, error.message));
|
|
7770
|
+
});
|
|
7771
|
+
// A hard deadline over the whole exchange, handshake included: a listener that accepts the connection and then says nothing must still fail the probe.
|
|
7772
|
+
setTimeout(() => { request.destroy(new Error("no answer within " + String(timeoutMs) + "ms")); }, timeoutMs);
|
|
7773
|
+
request.on("error", (error) => finish(failed, (error.code ? error.code + ": " : "") + error.message));
|
|
7774
|
+
`;
|
|
7775
|
+
function probeFrontDoorSync(port, caPem) {
|
|
7776
|
+
const shared = new SharedArrayBuffer(HEADER_SLOTS * INT32_BYTES + REASON_BYTES);
|
|
7777
|
+
const header = new Int32Array(shared, 0, HEADER_SLOTS);
|
|
7778
|
+
const worker = new Worker(PROBE_SOURCE, {
|
|
7779
|
+
eval: true,
|
|
7780
|
+
workerData: { port, ca: caPem, timeoutMs: PROBE_TIMEOUT_MS, shared, ok: VERDICT_OK, failed: VERDICT_FAILED, headerSlots: HEADER_SLOTS }
|
|
7781
|
+
});
|
|
7782
|
+
worker.unref();
|
|
7783
|
+
try {
|
|
7784
|
+
const waited = Atomics.wait(header, 0, VERDICT_PENDING, PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS);
|
|
7785
|
+
const verdict = Atomics.load(header, 0);
|
|
7786
|
+
if (waited === "timed-out" || verdict === VERDICT_PENDING) {
|
|
7787
|
+
return { ok: false, reason: `the probe did not finish within ${String(PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS)}ms` };
|
|
7788
|
+
}
|
|
7789
|
+
if (verdict === VERDICT_OK) {
|
|
7790
|
+
return { ok: true };
|
|
7791
|
+
}
|
|
7792
|
+
const length = Atomics.load(header, 1);
|
|
7793
|
+
return { ok: false, reason: Buffer.from(new Uint8Array(shared, HEADER_SLOTS * INT32_BYTES, length)).toString("utf8") };
|
|
7794
|
+
} finally {
|
|
7795
|
+
void worker.terminate();
|
|
7796
|
+
}
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7799
|
+
// src/frontdoor/trust.ts
|
|
7800
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
7801
|
+
import path27 from "node:path";
|
|
7802
|
+
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
7803
|
+
function resolveTrustBundle(params) {
|
|
7804
|
+
const { caCertFile, bundlesDir, inherited, fs: fs12 } = params;
|
|
7805
|
+
if (inherited === void 0 || inherited === "" || path27.resolve(inherited) === path27.resolve(caCertFile)) {
|
|
7806
|
+
return { path: caCertFile };
|
|
7807
|
+
}
|
|
7808
|
+
const caPem = fs12.readFileUtf8(caCertFile);
|
|
7809
|
+
let inheritedPem;
|
|
7810
|
+
try {
|
|
7811
|
+
inheritedPem = fs12.readFileUtf8(inherited);
|
|
7812
|
+
} catch (error) {
|
|
7813
|
+
return {
|
|
7814
|
+
path: caCertFile,
|
|
7815
|
+
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`
|
|
7816
|
+
};
|
|
7817
|
+
}
|
|
7818
|
+
if (inheritedPem.includes(caPem.trim())) {
|
|
7819
|
+
return { path: inherited };
|
|
7820
|
+
}
|
|
7821
|
+
const combined = `${inheritedPem.trimEnd()}
|
|
7822
|
+
${caPem}`;
|
|
7823
|
+
const bundle = path27.join(bundlesDir, `${createHash5("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
|
|
7824
|
+
if (!fs12.exists(bundle)) {
|
|
7825
|
+
fs12.mkdirp(bundlesDir);
|
|
7826
|
+
fs12.writeFileAtomic(bundle, combined);
|
|
7827
|
+
}
|
|
7828
|
+
return { path: bundle };
|
|
7829
|
+
}
|
|
7830
|
+
|
|
7831
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7832
|
+
var realTrustBundleFs = {
|
|
7833
|
+
readFileUtf8: (file) => fs11.readFileSync(file, "utf8"),
|
|
7834
|
+
exists: (file) => fs11.existsSync(file),
|
|
7835
|
+
mkdirp: (dir) => {
|
|
7836
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
7837
|
+
},
|
|
7838
|
+
writeFileAtomic: (file, contents) => {
|
|
7839
|
+
realFarmFs.writeFilePrivate(file, contents);
|
|
7840
|
+
}
|
|
7841
|
+
};
|
|
7842
|
+
function realFrontDoorPort(paths, spawnDaemon) {
|
|
7843
|
+
return {
|
|
7844
|
+
ensure: (inheritedExtraCaCerts) => {
|
|
7845
|
+
const up = ensureFrontDoor({
|
|
7846
|
+
paths,
|
|
7847
|
+
launcherPid: process.pid,
|
|
7848
|
+
ports: {
|
|
7849
|
+
fs: realFarmFs,
|
|
7850
|
+
isRunning: realIsProcessRunning,
|
|
7851
|
+
now: () => Date.now(),
|
|
7852
|
+
sleep: realSleepSync,
|
|
7853
|
+
spawnSupervisor: (layout) => spawnDaemon(layout, "__frontdoor-supervisor", layout.frontdoorLogPath),
|
|
7854
|
+
stopSupervisor: (pid) => {
|
|
7855
|
+
process.kill(pid, "SIGTERM");
|
|
7856
|
+
},
|
|
7857
|
+
// 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.
|
|
7858
|
+
verifyListener: (port) => probeFrontDoorSync(port, fs11.readFileSync(paths.frontdoorCaCertFile, "utf8"))
|
|
7859
|
+
}
|
|
7860
|
+
});
|
|
7861
|
+
const trust = resolveTrustBundle({ caCertFile: paths.frontdoorCaCertFile, bundlesDir: paths.frontdoorCaBundlesDir, inherited: inheritedExtraCaCerts, fs: realTrustBundleFs });
|
|
7862
|
+
return {
|
|
7863
|
+
port: up.port,
|
|
7864
|
+
connectPort: up.connectPort,
|
|
7865
|
+
trustBundlePath: trust.path,
|
|
7866
|
+
...trust.warning === void 0 ? {} : { trustWarning: trust.warning },
|
|
7867
|
+
sessionToken: up.token
|
|
7868
|
+
};
|
|
7869
|
+
},
|
|
7870
|
+
release: () => {
|
|
7871
|
+
removeFrontDoorSession(realFarmFs, paths.frontdoorSessionsDir, process.pid);
|
|
7872
|
+
}
|
|
7873
|
+
};
|
|
7874
|
+
}
|
|
7875
|
+
|
|
7876
|
+
// src/launchWiring.ts
|
|
7877
|
+
function buildFarmRuntime(paths, cwd) {
|
|
7878
|
+
const home = os5.homedir();
|
|
7879
|
+
const read = cosmiconfigReader();
|
|
7880
|
+
const classification = loadClassification(paths);
|
|
7881
|
+
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
7882
|
+
const selections = readDirectorySelections(loaded);
|
|
7883
|
+
const git = resolveGitBranch(realRunPort, cwd);
|
|
7884
|
+
return {
|
|
7885
|
+
runtime: {
|
|
7886
|
+
fs: realFarmFs,
|
|
7887
|
+
claudeHome: resolveClaudeHome(),
|
|
7888
|
+
home,
|
|
7889
|
+
cwd,
|
|
7890
|
+
...git.branch === void 0 ? {} : { branch: git.branch },
|
|
7891
|
+
...git.branchDetached === void 0 ? {} : { branchDetached: git.branchDetached },
|
|
7892
|
+
classification,
|
|
7893
|
+
loadCascade: (baseConfigProfile, cliOverride) => loadCascadeInput({
|
|
7894
|
+
paths,
|
|
7895
|
+
home,
|
|
7896
|
+
cwd,
|
|
7897
|
+
read,
|
|
7898
|
+
...baseConfigProfile === void 0 ? {} : { baseConfigProfile },
|
|
7899
|
+
...cliOverride === void 0 ? {} : { cliOverride }
|
|
7900
|
+
}).input,
|
|
7901
|
+
now: () => Date.now(),
|
|
7902
|
+
uniqueSuffix: `${String(process.pid)}.${randomUUID5()}`,
|
|
7903
|
+
// 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.
|
|
7904
|
+
lock: { pid: process.pid, isRunning: realIsProcessRunning, sleep: realSleepSync }
|
|
7905
|
+
},
|
|
7906
|
+
...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
|
|
7907
|
+
...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
|
|
7908
|
+
...loaded.globalConfig?.defaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: loaded.globalConfig.defaultConfigProfile },
|
|
7909
|
+
...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
|
|
7910
|
+
};
|
|
7911
|
+
}
|
|
7912
|
+
function realPrepareLaunchParams(paths, options) {
|
|
7913
|
+
const { farm } = options;
|
|
7914
|
+
return {
|
|
7915
|
+
paths,
|
|
7916
|
+
fs: realFsPort,
|
|
7917
|
+
proc: options.proc,
|
|
7918
|
+
log: options.log,
|
|
7919
|
+
resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
|
|
7920
|
+
farm: farm.runtime,
|
|
7921
|
+
headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
|
|
7922
|
+
frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
|
|
7923
|
+
credentials: { ...realCredentialPort, cache: realCredentialCacheEnv(paths) },
|
|
7924
|
+
...farm.directoryIdentity === void 0 ? {} : { directoryPinnedIdentity: farm.directoryIdentity },
|
|
7925
|
+
...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
|
|
7926
|
+
...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
|
|
7927
|
+
...farm.pools === void 0 ? {} : { pools: farm.pools },
|
|
7928
|
+
...options.allowMissingConfigProfile === void 0 ? {} : { allowMissingConfigProfile: options.allowMissingConfigProfile }
|
|
7929
|
+
};
|
|
7930
|
+
}
|
|
7931
|
+
var LaunchRefusedError = class extends CliError {
|
|
7932
|
+
exitCode;
|
|
7933
|
+
constructor(message, exitCode) {
|
|
7934
|
+
super(message);
|
|
7935
|
+
this.name = "LaunchRefusedError";
|
|
7936
|
+
this.exitCode = exitCode;
|
|
7937
|
+
}
|
|
7938
|
+
};
|
|
7939
|
+
function prepareClaudeLaunch(options) {
|
|
7940
|
+
const paths = options.paths ?? resolveLayoutPaths();
|
|
7941
|
+
const refusals = [];
|
|
7942
|
+
const log = {
|
|
7943
|
+
info: () => void 0,
|
|
7944
|
+
warn: () => void 0,
|
|
7945
|
+
error: (message) => {
|
|
7946
|
+
refusals.push(message);
|
|
7947
|
+
}
|
|
7948
|
+
};
|
|
7949
|
+
const proc = {
|
|
7950
|
+
env: options.env,
|
|
7951
|
+
argv: options.argv,
|
|
7952
|
+
exit: (code) => {
|
|
7953
|
+
throw new LaunchRefusedError(refusals.join("\n") || `the launch was refused (exit ${String(code)})`, code);
|
|
7954
|
+
}
|
|
7955
|
+
};
|
|
7956
|
+
const spawnDaemon = options.agentShim === void 0 ? () => {
|
|
7957
|
+
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);
|
|
7958
|
+
} : spawnDaemonThrough(options.agentShim);
|
|
7959
|
+
return prepareLaunch(realPrepareLaunchParams(paths, { proc, log, spawnDaemon, farm: buildFarmRuntime(paths, options.cwd) }));
|
|
7960
|
+
}
|
|
6636
7961
|
export {
|
|
6637
7962
|
AMBIENT_CREDENTIAL_VARS,
|
|
6638
7963
|
CONNECT_INTERCEPT_HOST,
|
|
@@ -6664,6 +7989,7 @@ export {
|
|
|
6664
7989
|
InvalidCategoryNameError,
|
|
6665
7990
|
InvalidIdentityNameError,
|
|
6666
7991
|
InvalidProviderNameError,
|
|
7992
|
+
LaunchRefusedError,
|
|
6667
7993
|
LegacyProviderFileError,
|
|
6668
7994
|
PoolNameSchema,
|
|
6669
7995
|
PoolNotFoundError,
|
|
@@ -6714,6 +8040,8 @@ export {
|
|
|
6714
8040
|
listUsageSnapshots,
|
|
6715
8041
|
mintLeaf,
|
|
6716
8042
|
parseConnectTarget,
|
|
8043
|
+
prepareClaudeLaunch,
|
|
8044
|
+
prepareLaunch,
|
|
6717
8045
|
profileExists,
|
|
6718
8046
|
providerExists,
|
|
6719
8047
|
readActiveIdentity,
|