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.cjs
CHANGED
|
@@ -60,6 +60,7 @@ __export(index_exports, {
|
|
|
60
60
|
InvalidCategoryNameError: () => InvalidCategoryNameError,
|
|
61
61
|
InvalidIdentityNameError: () => InvalidIdentityNameError,
|
|
62
62
|
InvalidProviderNameError: () => InvalidProviderNameError,
|
|
63
|
+
LaunchRefusedError: () => LaunchRefusedError,
|
|
63
64
|
LegacyProviderFileError: () => LegacyProviderFileError,
|
|
64
65
|
PoolNameSchema: () => PoolNameSchema,
|
|
65
66
|
PoolNotFoundError: () => PoolNotFoundError,
|
|
@@ -110,6 +111,8 @@ __export(index_exports, {
|
|
|
110
111
|
listUsageSnapshots: () => listUsageSnapshots,
|
|
111
112
|
mintLeaf: () => mintLeaf,
|
|
112
113
|
parseConnectTarget: () => parseConnectTarget,
|
|
114
|
+
prepareClaudeLaunch: () => prepareClaudeLaunch,
|
|
115
|
+
prepareLaunch: () => prepareLaunch,
|
|
113
116
|
profileExists: () => profileExists,
|
|
114
117
|
providerExists: () => providerExists,
|
|
115
118
|
readActiveIdentity: () => readActiveIdentity,
|
|
@@ -1789,8 +1792,8 @@ var FarmManifestSchema = import_zod2.z.strictObject({
|
|
|
1789
1792
|
materialised: import_zod2.z.array(import_zod2.z.string()),
|
|
1790
1793
|
links: import_zod2.z.array(import_zod2.z.strictObject({ rel: import_zod2.z.string(), target: import_zod2.z.string() }))
|
|
1791
1794
|
});
|
|
1792
|
-
function readFarmManifest(
|
|
1793
|
-
const raw =
|
|
1795
|
+
function readFarmManifest(fs12, farmRoot) {
|
|
1796
|
+
const raw = fs12.readFileUtf8(import_node_path6.default.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs12.readFileUtf8(import_node_path6.default.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1794
1797
|
if (raw === void 0) {
|
|
1795
1798
|
return void 0;
|
|
1796
1799
|
}
|
|
@@ -1848,9 +1851,9 @@ function buildEntryFacts(params) {
|
|
|
1848
1851
|
entries
|
|
1849
1852
|
};
|
|
1850
1853
|
}
|
|
1851
|
-
function listSubtree(
|
|
1854
|
+
function listSubtree(fs12, root, rel) {
|
|
1852
1855
|
const absolute = import_node_path6.default.join(root, rel);
|
|
1853
|
-
const stat =
|
|
1856
|
+
const stat = fs12.lstat(absolute);
|
|
1854
1857
|
if (stat === void 0) {
|
|
1855
1858
|
return [];
|
|
1856
1859
|
}
|
|
@@ -1858,10 +1861,10 @@ function listSubtree(fs10, root, rel) {
|
|
|
1858
1861
|
return [{ rel, kind: "symlink" }];
|
|
1859
1862
|
}
|
|
1860
1863
|
if (stat.kind === "dir") {
|
|
1861
|
-
const children = [...
|
|
1864
|
+
const children = [...fs12.readdir(absolute)].sort().flatMap((name) => listSubtree(fs12, root, `${rel}/${name}`));
|
|
1862
1865
|
return [{ rel, kind: "dir" }, ...children];
|
|
1863
1866
|
}
|
|
1864
|
-
const contentHash =
|
|
1867
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1865
1868
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1866
1869
|
}
|
|
1867
1870
|
function topLevelScopeRoots(roots) {
|
|
@@ -1875,25 +1878,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1875
1878
|
}
|
|
1876
1879
|
return kept;
|
|
1877
1880
|
}
|
|
1878
|
-
function reconciliationScope(
|
|
1881
|
+
function reconciliationScope(fs12, farmRoot, manifest) {
|
|
1879
1882
|
if (manifest !== void 0) {
|
|
1880
1883
|
return topLevelScopeRoots(manifest.materialised);
|
|
1881
1884
|
}
|
|
1882
|
-
return [...
|
|
1885
|
+
return [...fs12.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs12.lstat(import_node_path6.default.join(farmRoot, name))?.kind === "dir").sort();
|
|
1883
1886
|
}
|
|
1884
|
-
function collectFarmListing(
|
|
1885
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1887
|
+
function collectFarmListing(fs12, farmRoot, scopeRoots) {
|
|
1888
|
+
return scopeRoots.flatMap((root) => listSubtree(fs12, farmRoot, root));
|
|
1886
1889
|
}
|
|
1887
|
-
function collectCanonicalCounterparts(
|
|
1890
|
+
function collectCanonicalCounterparts(fs12, claudeHome, farmListing) {
|
|
1888
1891
|
const out = [];
|
|
1889
1892
|
for (const entry of farmListing) {
|
|
1890
1893
|
const absolute = import_node_path6.default.join(claudeHome, entry.rel);
|
|
1891
|
-
const stat =
|
|
1894
|
+
const stat = fs12.lstat(absolute);
|
|
1892
1895
|
if (stat === void 0) {
|
|
1893
1896
|
continue;
|
|
1894
1897
|
}
|
|
1895
1898
|
if (stat.kind === "file") {
|
|
1896
|
-
const contentHash =
|
|
1899
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1897
1900
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1898
1901
|
continue;
|
|
1899
1902
|
}
|
|
@@ -2577,8 +2580,12 @@ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
|
|
|
2577
2580
|
}
|
|
2578
2581
|
|
|
2579
2582
|
// src/frontdoor/capability.ts
|
|
2583
|
+
var CONNECT_PROXY_USER = "agent-shim";
|
|
2580
2584
|
var CONNECT_PROXY_REALM = "agent-shim front door";
|
|
2581
2585
|
var BASIC_SCHEME = "basic";
|
|
2586
|
+
function connectProxyUrl(port, token) {
|
|
2587
|
+
return `http://${encodeURIComponent(CONNECT_PROXY_USER)}:${encodeURIComponent(token)}@127.0.0.1:${String(port)}`;
|
|
2588
|
+
}
|
|
2582
2589
|
function capabilityFromProxyAuthorization(value) {
|
|
2583
2590
|
const trimmed = value.trim();
|
|
2584
2591
|
const space = trimmed.indexOf(" ");
|
|
@@ -2595,7 +2602,15 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2595
2602
|
}
|
|
2596
2603
|
|
|
2597
2604
|
// src/frontdoor/route.ts
|
|
2605
|
+
var IDENTITY_HEADER = "x-agent-shim-identity";
|
|
2606
|
+
var SESSION_HEADER = "x-agent-shim-session";
|
|
2607
|
+
var HEADROOM_FLAG_HEADER = "x-agent-shim-headroom";
|
|
2608
|
+
var PROJECT_ID_HEADER = "x-headroom-project-id";
|
|
2598
2609
|
var AUTH_HEADER = "x-agent-shim-auth";
|
|
2610
|
+
var PROVIDER_PATH_PREFIX = "/providers/";
|
|
2611
|
+
function providerBaseUrl(port, provider) {
|
|
2612
|
+
return `https://127.0.0.1:${String(port)}${PROVIDER_PATH_PREFIX}${encodeURIComponent(provider)}`;
|
|
2613
|
+
}
|
|
2599
2614
|
|
|
2600
2615
|
// src/frontdoor/state.ts
|
|
2601
2616
|
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
@@ -2632,10 +2647,10 @@ var FrontDoorStateSchema = import_zod3.z.strictObject({
|
|
|
2632
2647
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2633
2648
|
lastError: import_zod3.z.string().optional()
|
|
2634
2649
|
});
|
|
2635
|
-
function readFrontDoorState(
|
|
2650
|
+
function readFrontDoorState(fs12, stateFile) {
|
|
2636
2651
|
let raw;
|
|
2637
2652
|
try {
|
|
2638
|
-
raw =
|
|
2653
|
+
raw = fs12.readFileUtf8(stateFile);
|
|
2639
2654
|
} catch {
|
|
2640
2655
|
return void 0;
|
|
2641
2656
|
}
|
|
@@ -2649,28 +2664,28 @@ function readFrontDoorState(fs10, stateFile) {
|
|
|
2649
2664
|
return void 0;
|
|
2650
2665
|
}
|
|
2651
2666
|
}
|
|
2652
|
-
function writeFrontDoorState(
|
|
2653
|
-
|
|
2654
|
-
|
|
2667
|
+
function writeFrontDoorState(fs12, stateFile, state) {
|
|
2668
|
+
fs12.mkdirp(import_node_path7.default.dirname(stateFile));
|
|
2669
|
+
fs12.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2655
2670
|
`);
|
|
2656
2671
|
}
|
|
2657
2672
|
function frontDoorOrigin(state) {
|
|
2658
2673
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2659
2674
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2660
2675
|
}
|
|
2661
|
-
function writeFrontDoorSession(
|
|
2662
|
-
|
|
2663
|
-
|
|
2676
|
+
function writeFrontDoorSession(fs12, sessionsDir, session) {
|
|
2677
|
+
fs12.mkdirPrivate(sessionsDir);
|
|
2678
|
+
fs12.writeFilePrivate(import_node_path7.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2664
2679
|
`);
|
|
2665
2680
|
}
|
|
2666
|
-
function listFrontDoorSessions(
|
|
2681
|
+
function listFrontDoorSessions(fs12, sessionsDir) {
|
|
2667
2682
|
const sessions = [];
|
|
2668
|
-
for (const name of
|
|
2683
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
2669
2684
|
if (!name.endsWith(".json")) {
|
|
2670
2685
|
continue;
|
|
2671
2686
|
}
|
|
2672
2687
|
try {
|
|
2673
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2688
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs12.readFileUtf8(import_node_path7.default.join(sessionsDir, name)) ?? ""));
|
|
2674
2689
|
if (parsed.success) {
|
|
2675
2690
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2676
2691
|
}
|
|
@@ -2680,14 +2695,14 @@ function listFrontDoorSessions(fs10, sessionsDir) {
|
|
|
2680
2695
|
}
|
|
2681
2696
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2682
2697
|
}
|
|
2683
|
-
function removeFrontDoorSession(
|
|
2684
|
-
|
|
2698
|
+
function removeFrontDoorSession(fs12, sessionsDir, pid) {
|
|
2699
|
+
fs12.removeRecursive(import_node_path7.default.join(sessionsDir, `${String(pid)}.json`));
|
|
2685
2700
|
}
|
|
2686
|
-
function pruneDeadFrontDoorSessions(
|
|
2701
|
+
function pruneDeadFrontDoorSessions(fs12, sessionsDir, isRunning) {
|
|
2687
2702
|
const removed = [];
|
|
2688
|
-
for (const session of listFrontDoorSessions(
|
|
2703
|
+
for (const session of listFrontDoorSessions(fs12, sessionsDir)) {
|
|
2689
2704
|
if (!isRunning(session.pid)) {
|
|
2690
|
-
removeFrontDoorSession(
|
|
2705
|
+
removeFrontDoorSession(fs12, sessionsDir, session.pid);
|
|
2691
2706
|
removed.push(session.pid);
|
|
2692
2707
|
}
|
|
2693
2708
|
}
|
|
@@ -2699,8 +2714,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2699
2714
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2700
2715
|
var MS_PER_MINUTE = 6e4;
|
|
2701
2716
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2702
|
-
const { fs:
|
|
2703
|
-
const previous = readFrontDoorState(
|
|
2717
|
+
const { fs: fs12, paths } = ports;
|
|
2718
|
+
const previous = readFrontDoorState(fs12, paths.frontdoorStateFile);
|
|
2704
2719
|
let lastPort = previous?.lastPort;
|
|
2705
2720
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2706
2721
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2710,12 +2725,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2710
2725
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2711
2726
|
});
|
|
2712
2727
|
const fail = (message) => {
|
|
2713
|
-
writeFrontDoorState(
|
|
2714
|
-
|
|
2728
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2729
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2715
2730
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2716
2731
|
return 1;
|
|
2717
2732
|
};
|
|
2718
|
-
writeFrontDoorState(
|
|
2733
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2719
2734
|
const previousSticky = lastPort;
|
|
2720
2735
|
let http2;
|
|
2721
2736
|
try {
|
|
@@ -2752,8 +2767,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2752
2767
|
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)`);
|
|
2753
2768
|
}
|
|
2754
2769
|
lastDirectPort = direct.port;
|
|
2755
|
-
writeFrontDoorState(
|
|
2756
|
-
|
|
2770
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
2771
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2757
2772
|
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)}`);
|
|
2758
2773
|
let idleSince;
|
|
2759
2774
|
let ticks = 0;
|
|
@@ -2762,15 +2777,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2762
2777
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2763
2778
|
}
|
|
2764
2779
|
ticks += 1;
|
|
2765
|
-
pruneDeadFrontDoorSessions(
|
|
2766
|
-
if (listFrontDoorSessions(
|
|
2780
|
+
pruneDeadFrontDoorSessions(fs12, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2781
|
+
if (listFrontDoorSessions(fs12, paths.frontdoorSessionsDir).length === 0) {
|
|
2767
2782
|
idleSince ??= ports.now();
|
|
2768
2783
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2769
2784
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2770
2785
|
await direct.close();
|
|
2771
2786
|
await connect3.close();
|
|
2772
2787
|
await http2.close();
|
|
2773
|
-
writeFrontDoorState(
|
|
2788
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, sticky());
|
|
2774
2789
|
return 0;
|
|
2775
2790
|
}
|
|
2776
2791
|
} else {
|
|
@@ -2825,8 +2840,8 @@ function parseConnectTarget(authority) {
|
|
|
2825
2840
|
function isInterceptedHost(host, interceptHosts) {
|
|
2826
2841
|
return interceptHosts.includes(host);
|
|
2827
2842
|
}
|
|
2828
|
-
function servedByPipeline(
|
|
2829
|
-
return
|
|
2843
|
+
function servedByPipeline(path28) {
|
|
2844
|
+
return path28?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2830
2845
|
}
|
|
2831
2846
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2832
2847
|
"connection",
|
|
@@ -3360,16 +3375,16 @@ function parseJson(raw, schema) {
|
|
|
3360
3375
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
3361
3376
|
return parsed.success ? parsed.data : void 0;
|
|
3362
3377
|
}
|
|
3363
|
-
function readHeadroomState(
|
|
3378
|
+
function readHeadroomState(fs12, stateFile) {
|
|
3364
3379
|
try {
|
|
3365
|
-
return parseJson(
|
|
3380
|
+
return parseJson(fs12.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
3366
3381
|
} catch {
|
|
3367
3382
|
return void 0;
|
|
3368
3383
|
}
|
|
3369
3384
|
}
|
|
3370
|
-
function writeHeadroomState(
|
|
3371
|
-
|
|
3372
|
-
|
|
3385
|
+
function writeHeadroomState(fs12, stateFile, state) {
|
|
3386
|
+
fs12.mkdirp(import_node_path8.default.dirname(stateFile));
|
|
3387
|
+
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
3373
3388
|
`);
|
|
3374
3389
|
}
|
|
3375
3390
|
function headroomAllowlist(providers) {
|
|
@@ -3389,14 +3404,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3389
3404
|
function hashAllowlist(allowlist) {
|
|
3390
3405
|
return (0, import_node_crypto5.createHash)("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3391
3406
|
}
|
|
3392
|
-
function readAllProviders(
|
|
3407
|
+
function readAllProviders(fs12, providersDir) {
|
|
3393
3408
|
const result = [];
|
|
3394
|
-
for (const name of
|
|
3409
|
+
for (const name of fs12.readdir(providersDir)) {
|
|
3395
3410
|
if (!name.endsWith(".json")) {
|
|
3396
3411
|
continue;
|
|
3397
3412
|
}
|
|
3398
3413
|
try {
|
|
3399
|
-
const parsed = parseJson(
|
|
3414
|
+
const parsed = parseJson(fs12.readFileUtf8(import_node_path8.default.join(providersDir, name)), ProviderSchema);
|
|
3400
3415
|
if (parsed !== void 0) {
|
|
3401
3416
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
3402
3417
|
}
|
|
@@ -3406,22 +3421,22 @@ function readAllProviders(fs10, providersDir) {
|
|
|
3406
3421
|
}
|
|
3407
3422
|
return result;
|
|
3408
3423
|
}
|
|
3409
|
-
function writeSession(
|
|
3410
|
-
|
|
3411
|
-
|
|
3424
|
+
function writeSession(fs12, sessionsDir, session) {
|
|
3425
|
+
fs12.mkdirp(sessionsDir);
|
|
3426
|
+
fs12.writeFileUtf8(import_node_path8.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
3412
3427
|
`);
|
|
3413
3428
|
}
|
|
3414
|
-
function removeSession(
|
|
3415
|
-
|
|
3429
|
+
function removeSession(fs12, sessionsDir, pid) {
|
|
3430
|
+
fs12.removeRecursive(import_node_path8.default.join(sessionsDir, `${String(pid)}.json`));
|
|
3416
3431
|
}
|
|
3417
|
-
function listSessions(
|
|
3432
|
+
function listSessions(fs12, sessionsDir) {
|
|
3418
3433
|
const sessions = [];
|
|
3419
|
-
for (const name of
|
|
3434
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
3420
3435
|
if (!name.endsWith(".json")) {
|
|
3421
3436
|
continue;
|
|
3422
3437
|
}
|
|
3423
3438
|
try {
|
|
3424
|
-
const parsed = parseJson(
|
|
3439
|
+
const parsed = parseJson(fs12.readFileUtf8(import_node_path8.default.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
3425
3440
|
if (parsed !== void 0) {
|
|
3426
3441
|
sessions.push(parsed);
|
|
3427
3442
|
}
|
|
@@ -3431,22 +3446,22 @@ function listSessions(fs10, sessionsDir) {
|
|
|
3431
3446
|
}
|
|
3432
3447
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
3433
3448
|
}
|
|
3434
|
-
function sessionsForSupervisor(
|
|
3435
|
-
return listSessions(
|
|
3449
|
+
function sessionsForSupervisor(fs12, sessionsDir, supervisorPid) {
|
|
3450
|
+
return listSessions(fs12, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3436
3451
|
}
|
|
3437
|
-
function pruneDeadSessions(
|
|
3452
|
+
function pruneDeadSessions(fs12, sessionsDir, isRunning) {
|
|
3438
3453
|
const removed = [];
|
|
3439
|
-
for (const session of listSessions(
|
|
3454
|
+
for (const session of listSessions(fs12, sessionsDir)) {
|
|
3440
3455
|
if (!isRunning(session.pid)) {
|
|
3441
|
-
removeSession(
|
|
3456
|
+
removeSession(fs12, sessionsDir, session.pid);
|
|
3442
3457
|
removed.push(session.pid);
|
|
3443
3458
|
}
|
|
3444
3459
|
}
|
|
3445
3460
|
return removed;
|
|
3446
3461
|
}
|
|
3447
|
-
function readStartLock(
|
|
3462
|
+
function readStartLock(fs12, lockFile) {
|
|
3448
3463
|
try {
|
|
3449
|
-
return parseJson(
|
|
3464
|
+
return parseJson(fs12.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
3450
3465
|
} catch {
|
|
3451
3466
|
return void 0;
|
|
3452
3467
|
}
|
|
@@ -3697,12 +3712,12 @@ function isIdentityName(name) {
|
|
|
3697
3712
|
function claudeJsonPath(identitiesDir, identity) {
|
|
3698
3713
|
return import_node_path9.default.join(identitiesDir, identity, CLAUDE_JSON);
|
|
3699
3714
|
}
|
|
3700
|
-
function readAccountMetadata(
|
|
3715
|
+
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
3701
3716
|
if (!isIdentityName(identity)) {
|
|
3702
3717
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3703
3718
|
}
|
|
3704
3719
|
const file = claudeJsonPath(identitiesDir, identity);
|
|
3705
|
-
const raw =
|
|
3720
|
+
const raw = fs12.readFileUtf8(file);
|
|
3706
3721
|
if (raw === void 0) {
|
|
3707
3722
|
return void 0;
|
|
3708
3723
|
}
|
|
@@ -3736,8 +3751,8 @@ function readAccountMetadata(fs10, identitiesDir, identity) {
|
|
|
3736
3751
|
// src/usage/read.ts
|
|
3737
3752
|
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
3738
3753
|
var DAY_MS = 864e5;
|
|
3739
|
-
function listLogSegments(
|
|
3740
|
-
return
|
|
3754
|
+
function listLogSegments(fs12, logDir) {
|
|
3755
|
+
return fs12.readdir(logDir).flatMap((name) => {
|
|
3741
3756
|
const match = SEGMENT_RE.exec(name);
|
|
3742
3757
|
const day = match?.[1];
|
|
3743
3758
|
if (day === void 0) {
|
|
@@ -3746,15 +3761,15 @@ function listLogSegments(fs10, logDir) {
|
|
|
3746
3761
|
return [{ name, day, endsAt: Date.parse(`${day}T00:00:00.000Z`) + DAY_MS }];
|
|
3747
3762
|
}).sort((left, right) => left.day.localeCompare(right.day) || left.name.localeCompare(right.name));
|
|
3748
3763
|
}
|
|
3749
|
-
function readUsageLog(
|
|
3764
|
+
function readUsageLog(fs12, logDir, options = {}) {
|
|
3750
3765
|
const { sinceMs } = options;
|
|
3751
3766
|
const records = [];
|
|
3752
3767
|
let invalidLines = 0;
|
|
3753
|
-
for (const segment of listLogSegments(
|
|
3768
|
+
for (const segment of listLogSegments(fs12, logDir)) {
|
|
3754
3769
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
3755
3770
|
continue;
|
|
3756
3771
|
}
|
|
3757
|
-
const contents =
|
|
3772
|
+
const contents = fs12.readFileUtf8(import_node_path10.default.join(logDir, segment.name));
|
|
3758
3773
|
if (contents === void 0) {
|
|
3759
3774
|
continue;
|
|
3760
3775
|
}
|
|
@@ -3798,9 +3813,9 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3798
3813
|
}
|
|
3799
3814
|
return import_node_path10.default.join(snapshotsDir, `${identity}.json`);
|
|
3800
3815
|
}
|
|
3801
|
-
function readUsageSnapshot(
|
|
3816
|
+
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
3802
3817
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3803
|
-
const raw =
|
|
3818
|
+
const raw = fs12.readFileUtf8(file);
|
|
3804
3819
|
if (raw === void 0) {
|
|
3805
3820
|
return void 0;
|
|
3806
3821
|
}
|
|
@@ -3816,8 +3831,8 @@ function readUsageSnapshot(fs10, snapshotsDir, identity) {
|
|
|
3816
3831
|
}
|
|
3817
3832
|
return snapshot.data;
|
|
3818
3833
|
}
|
|
3819
|
-
function listUsageSnapshots(
|
|
3820
|
-
return
|
|
3834
|
+
function listUsageSnapshots(fs12, snapshotsDir) {
|
|
3835
|
+
return fs12.readdir(snapshotsDir).flatMap((name) => {
|
|
3821
3836
|
if (!name.endsWith(".json")) {
|
|
3822
3837
|
return [];
|
|
3823
3838
|
}
|
|
@@ -3825,7 +3840,7 @@ function listUsageSnapshots(fs10, snapshotsDir) {
|
|
|
3825
3840
|
if (!isIdentityName(identity)) {
|
|
3826
3841
|
return [];
|
|
3827
3842
|
}
|
|
3828
|
-
const snapshot = readUsageSnapshot(
|
|
3843
|
+
const snapshot = readUsageSnapshot(fs12, snapshotsDir, identity);
|
|
3829
3844
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3830
3845
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3831
3846
|
}
|
|
@@ -4000,9 +4015,9 @@ function compareVersions(a, b) {
|
|
|
4000
4015
|
}
|
|
4001
4016
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
4002
4017
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
4003
|
-
const { fs:
|
|
4018
|
+
const { fs: fs12, paths } = ports;
|
|
4004
4019
|
let config = initialConfig;
|
|
4005
|
-
const previousState = readHeadroomState(
|
|
4020
|
+
const previousState = readHeadroomState(fs12, paths.headroomStateFile);
|
|
4006
4021
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
4007
4022
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
4008
4023
|
return 0;
|
|
@@ -4011,19 +4026,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4011
4026
|
let lastPort = previousState?.lastPort;
|
|
4012
4027
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
4013
4028
|
const ownsState = () => {
|
|
4014
|
-
const owner = readHeadroomState(
|
|
4029
|
+
const owner = readHeadroomState(fs12, paths.headroomStateFile)?.supervisorPid;
|
|
4015
4030
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
4016
4031
|
};
|
|
4017
4032
|
const writeOwnState = (state) => {
|
|
4018
4033
|
if (!ownsState()) {
|
|
4019
4034
|
return false;
|
|
4020
4035
|
}
|
|
4021
|
-
writeHeadroomState(
|
|
4036
|
+
writeHeadroomState(fs12, paths.headroomStateFile, state);
|
|
4022
4037
|
return true;
|
|
4023
4038
|
};
|
|
4024
4039
|
const fail = (message) => {
|
|
4025
4040
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
4026
|
-
|
|
4041
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
4027
4042
|
}
|
|
4028
4043
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
4029
4044
|
return 1;
|
|
@@ -4072,7 +4087,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4072
4087
|
const settingsHash = hashSettings(config.settings);
|
|
4073
4088
|
const allowlist = allowlistOf(ports);
|
|
4074
4089
|
const allowlistHash = hashAllowlist(allowlist);
|
|
4075
|
-
pruneDeadSessions(
|
|
4090
|
+
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
4076
4091
|
const ownsNow = ownsState();
|
|
4077
4092
|
if (!ownsNow && !superseded) {
|
|
4078
4093
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -4124,7 +4139,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4124
4139
|
...sticky()
|
|
4125
4140
|
});
|
|
4126
4141
|
if (recorded) {
|
|
4127
|
-
|
|
4142
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
4128
4143
|
}
|
|
4129
4144
|
idleSince = void 0;
|
|
4130
4145
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -4139,7 +4154,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4139
4154
|
}
|
|
4140
4155
|
} else {
|
|
4141
4156
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
4142
|
-
if (sessionsForSupervisor(
|
|
4157
|
+
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
4143
4158
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
4144
4159
|
ports.stopProcess(headroomPid);
|
|
4145
4160
|
headroomPid = void 0;
|
|
@@ -4148,7 +4163,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4148
4163
|
continue;
|
|
4149
4164
|
}
|
|
4150
4165
|
}
|
|
4151
|
-
const sessions = sessionsForSupervisor(
|
|
4166
|
+
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
4152
4167
|
if (sessions.length === 0) {
|
|
4153
4168
|
idleSince ??= ports.now();
|
|
4154
4169
|
const idleMs = ports.now() - idleSince;
|
|
@@ -4170,9 +4185,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4170
4185
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
4171
4186
|
}
|
|
4172
4187
|
}
|
|
4173
|
-
function currentHeadroomAllowlist(
|
|
4174
|
-
const providers = readAllProviders(
|
|
4175
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
4188
|
+
function currentHeadroomAllowlist(fs12, paths) {
|
|
4189
|
+
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
4190
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
4176
4191
|
}
|
|
4177
4192
|
function allowlistOf(ports) {
|
|
4178
4193
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -4349,8 +4364,8 @@ function decideConfigProfile(params) {
|
|
|
4349
4364
|
}
|
|
4350
4365
|
return { source: "none" };
|
|
4351
4366
|
}
|
|
4352
|
-
function loadIdentity(identitiesDir, name,
|
|
4353
|
-
return loadConfigFile(import_node_path12.default.join(identitiesDir, name, "identity.json"), IdentitySchema,
|
|
4367
|
+
function loadIdentity(identitiesDir, name, fs12) {
|
|
4368
|
+
return loadConfigFile(import_node_path12.default.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
4354
4369
|
}
|
|
4355
4370
|
|
|
4356
4371
|
// src/configProfilesStore.ts
|
|
@@ -4692,13 +4707,59 @@ var import_node_path15 = __toESM(require("node:path"), 1);
|
|
|
4692
4707
|
|
|
4693
4708
|
// src/credentialCache.ts
|
|
4694
4709
|
var import_zod7 = require("zod");
|
|
4710
|
+
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
4695
4711
|
var CachedCredentialSchema = import_zod7.z.strictObject({
|
|
4696
4712
|
token: import_zod7.z.string().min(1),
|
|
4697
4713
|
fetchedAt: import_zod7.z.number().int().nonnegative(),
|
|
4698
4714
|
source: CredentialSourceSchema
|
|
4699
4715
|
});
|
|
4716
|
+
function effectiveStore(cache, platform) {
|
|
4717
|
+
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
4718
|
+
}
|
|
4719
|
+
function ttlMs(cache) {
|
|
4720
|
+
if (cache.ttl === void 0) {
|
|
4721
|
+
return void 0;
|
|
4722
|
+
}
|
|
4723
|
+
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
4724
|
+
if (multiplier === void 0) {
|
|
4725
|
+
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
4726
|
+
}
|
|
4727
|
+
return Number(cache.ttl.slice(0, -1)) * multiplier;
|
|
4728
|
+
}
|
|
4729
|
+
function isFresh(entry, cache, now) {
|
|
4730
|
+
const limit = ttlMs(cache);
|
|
4731
|
+
if (entry.fetchedAt > now) {
|
|
4732
|
+
return false;
|
|
4733
|
+
}
|
|
4734
|
+
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
4735
|
+
}
|
|
4700
4736
|
|
|
4701
4737
|
// src/credential.ts
|
|
4738
|
+
var CREDENTIAL_UNAVAILABLE_EXIT = 64;
|
|
4739
|
+
var CREDENTIAL_COMMAND_TIMEOUT_MS = 1e4;
|
|
4740
|
+
var CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 12e4;
|
|
4741
|
+
var GROUP_OTHER_PERMISSION_BITS = 63;
|
|
4742
|
+
var PERMISSION_BITS = 511;
|
|
4743
|
+
var OCTAL_RADIX = 8;
|
|
4744
|
+
function commandArgv(source) {
|
|
4745
|
+
if ("command" in source) {
|
|
4746
|
+
return source.command;
|
|
4747
|
+
}
|
|
4748
|
+
if ("op" in source) {
|
|
4749
|
+
return ["op", "read", source.op];
|
|
4750
|
+
}
|
|
4751
|
+
const account = source.keychain.account;
|
|
4752
|
+
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
4753
|
+
}
|
|
4754
|
+
function isInteractiveSource(source, env) {
|
|
4755
|
+
if ("command" in source || "keychain" in source) {
|
|
4756
|
+
return source.interactive ?? false;
|
|
4757
|
+
}
|
|
4758
|
+
if ("op" in source) {
|
|
4759
|
+
return source.interactive ?? (env.OP_SERVICE_ACCOUNT_TOKEN ?? "") === "";
|
|
4760
|
+
}
|
|
4761
|
+
return false;
|
|
4762
|
+
}
|
|
4702
4763
|
function summariseSource(source) {
|
|
4703
4764
|
if ("env" in source) {
|
|
4704
4765
|
return { kind: "env", variable: source.env };
|
|
@@ -4746,6 +4807,9 @@ function formatSourceSummary(summary) {
|
|
|
4746
4807
|
return summary;
|
|
4747
4808
|
}
|
|
4748
4809
|
}
|
|
4810
|
+
function describeSource(source) {
|
|
4811
|
+
return formatSourceSummary(summariseSource(source));
|
|
4812
|
+
}
|
|
4749
4813
|
function formatCredentialSummary(summary) {
|
|
4750
4814
|
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`}`;
|
|
4751
4815
|
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
@@ -4753,6 +4817,88 @@ function formatCredentialSummary(summary) {
|
|
|
4753
4817
|
function describeCredential(credential) {
|
|
4754
4818
|
return formatCredentialSummary(summariseCredential(credential));
|
|
4755
4819
|
}
|
|
4820
|
+
function trySource(source, params) {
|
|
4821
|
+
const none = (reason, warn = false) => ({ ok: false, reason, warn });
|
|
4822
|
+
if ("env" in source) {
|
|
4823
|
+
const value = params.env[source.env];
|
|
4824
|
+
return value === void 0 || value === "" ? none("is unset or empty") : { ok: true, token: value };
|
|
4825
|
+
}
|
|
4826
|
+
if ("literal" in source) {
|
|
4827
|
+
return { ok: true, token: source.literal };
|
|
4828
|
+
}
|
|
4829
|
+
if ("file" in source) {
|
|
4830
|
+
const read = params.port.readSecretFile(source.file);
|
|
4831
|
+
if (!read.found) {
|
|
4832
|
+
return none("does not exist");
|
|
4833
|
+
}
|
|
4834
|
+
if (read.mode !== void 0 && (read.mode & GROUP_OTHER_PERMISSION_BITS) !== 0) {
|
|
4835
|
+
return none(
|
|
4836
|
+
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX)}); run \`chmod 600 ${source.file}\``,
|
|
4837
|
+
true
|
|
4838
|
+
);
|
|
4839
|
+
}
|
|
4840
|
+
const token2 = read.content.trim();
|
|
4841
|
+
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
4842
|
+
}
|
|
4843
|
+
const argv = commandArgv(source);
|
|
4844
|
+
const interactive = isInteractiveSource(source, params.env);
|
|
4845
|
+
if (interactive && !params.port.personPresent()) {
|
|
4846
|
+
return none("needs a person to approve it, but there is no terminal or desktop session");
|
|
4847
|
+
}
|
|
4848
|
+
const timeoutMs = source.timeoutMs ?? (interactive ? CREDENTIAL_INTERACTIVE_TIMEOUT_MS : CREDENTIAL_COMMAND_TIMEOUT_MS);
|
|
4849
|
+
const result = params.port.runCommand(argv, { timeoutMs, interactive });
|
|
4850
|
+
if (result.timedOut) {
|
|
4851
|
+
return none(`timed out after ${String(timeoutMs)}ms`);
|
|
4852
|
+
}
|
|
4853
|
+
if (result.status !== 0) {
|
|
4854
|
+
const stderr = result.stderr.trim();
|
|
4855
|
+
const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
|
|
4856
|
+
return none(`${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
|
|
4857
|
+
}
|
|
4858
|
+
const token = result.stdout.trim();
|
|
4859
|
+
return token === "" ? none("printed no token") : { ok: true, token };
|
|
4860
|
+
}
|
|
4861
|
+
function warmCommandFor(subject) {
|
|
4862
|
+
const [kind, name] = subject.split(" ");
|
|
4863
|
+
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
4864
|
+
}
|
|
4865
|
+
function resolveCredential(params) {
|
|
4866
|
+
const target = params.credential.target ?? "bearer";
|
|
4867
|
+
const cacheBlock = params.credential.cache;
|
|
4868
|
+
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
4869
|
+
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
4870
|
+
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
4871
|
+
const entry = cache.port.read(store, params.subject);
|
|
4872
|
+
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
4873
|
+
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
const attempts = [];
|
|
4877
|
+
const warnings = [];
|
|
4878
|
+
for (const source of params.credential.sources) {
|
|
4879
|
+
const outcome = trySource(source, params);
|
|
4880
|
+
if (outcome.ok) {
|
|
4881
|
+
if (cache !== void 0 && store !== void 0) {
|
|
4882
|
+
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
4883
|
+
}
|
|
4884
|
+
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
4885
|
+
}
|
|
4886
|
+
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
4887
|
+
attempts.push(line);
|
|
4888
|
+
if (outcome.warn) {
|
|
4889
|
+
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
4893
|
+
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
4894
|
+
}
|
|
4895
|
+
function credentialVariables(target, token) {
|
|
4896
|
+
return {
|
|
4897
|
+
ANTHROPIC_AUTH_TOKEN: target === "bearer" ? token : void 0,
|
|
4898
|
+
ANTHROPIC_API_KEY: target === "apiKey" ? token : void 0,
|
|
4899
|
+
CLAUDE_CODE_OAUTH_TOKEN: target === "oauthToken" ? token : void 0
|
|
4900
|
+
};
|
|
4901
|
+
}
|
|
4756
4902
|
|
|
4757
4903
|
// src/providersStore.ts
|
|
4758
4904
|
var ProviderNotFoundError = class extends CliError {
|
|
@@ -4858,6 +5004,11 @@ function readProvider(paths, name) {
|
|
|
4858
5004
|
const raw = JSON.parse(import_node_fs5.default.readFileSync(filePath, "utf8"));
|
|
4859
5005
|
return parseProviderFile(filePath, raw);
|
|
4860
5006
|
}
|
|
5007
|
+
function loadProvider(providersDir, name, port) {
|
|
5008
|
+
const filePath = import_node_path15.default.join(providersDir, `${name}.json`);
|
|
5009
|
+
const raw = port.readConfigFile(filePath);
|
|
5010
|
+
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
5011
|
+
}
|
|
4861
5012
|
function listProviders(paths) {
|
|
4862
5013
|
const names = listProviderNames(
|
|
4863
5014
|
(dir) => import_node_fs5.default.readdirSync(dir),
|
|
@@ -4990,6 +5141,30 @@ function cascadeProviderName(cascade) {
|
|
|
4990
5141
|
}
|
|
4991
5142
|
return name;
|
|
4992
5143
|
}
|
|
5144
|
+
function resolveProvider(params) {
|
|
5145
|
+
const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
|
|
5146
|
+
const name = cliName ?? (params.cascade === void 0 ? void 0 : cascadeProviderName(params.cascade));
|
|
5147
|
+
if (name === void 0) {
|
|
5148
|
+
return void 0;
|
|
5149
|
+
}
|
|
5150
|
+
const definition = loadProvider(params.paths.providersDir, name, params.port);
|
|
5151
|
+
if (definition === void 0) {
|
|
5152
|
+
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
5153
|
+
return {
|
|
5154
|
+
ok: false,
|
|
5155
|
+
status: 1,
|
|
5156
|
+
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
5157
|
+
};
|
|
5158
|
+
}
|
|
5159
|
+
if (params.credentials === void 0) {
|
|
5160
|
+
return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
|
|
5161
|
+
}
|
|
5162
|
+
const resolution = resolveCredential({ credential: definition.credential, env: params.env, port: params.credentials, subject: `provider ${name}` });
|
|
5163
|
+
if (!resolution.ok) {
|
|
5164
|
+
return { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
5165
|
+
}
|
|
5166
|
+
return { ok: true, provider: { name, definition, credential: resolution.credential }, warnings: resolution.credential.warnings };
|
|
5167
|
+
}
|
|
4993
5168
|
|
|
4994
5169
|
// src/directoryRulesStore.ts
|
|
4995
5170
|
var DirectoryRuleNotFoundError = class extends CliError {
|
|
@@ -5082,7 +5257,7 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
5082
5257
|
}
|
|
5083
5258
|
|
|
5084
5259
|
// src/checkReport.ts
|
|
5085
|
-
var
|
|
5260
|
+
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
5086
5261
|
|
|
5087
5262
|
// src/launcher/cascade.ts
|
|
5088
5263
|
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
@@ -5178,6 +5353,7 @@ var MS_PER_SECOND2 = 1e3;
|
|
|
5178
5353
|
var SECONDS_PER_MINUTE = 60;
|
|
5179
5354
|
var MINUTES_PER_HOUR = 60;
|
|
5180
5355
|
var HOURS_PER_DAY = 24;
|
|
5356
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
5181
5357
|
function formatAge(ms) {
|
|
5182
5358
|
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5183
5359
|
if (minutes < MINUTES_PER_HOUR) {
|
|
@@ -5198,8 +5374,30 @@ function effectiveWindow(window, nowMs) {
|
|
|
5198
5374
|
...window.status === void 0 ? {} : { status: window.status }
|
|
5199
5375
|
};
|
|
5200
5376
|
}
|
|
5377
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
5378
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
5379
|
+
const unified = rateLimit?.unified;
|
|
5380
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
5381
|
+
return [];
|
|
5382
|
+
}
|
|
5383
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
5384
|
+
const windows = [
|
|
5385
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
5386
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
5387
|
+
];
|
|
5388
|
+
return windows.flatMap(({ name, window }) => {
|
|
5389
|
+
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
5390
|
+
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
5391
|
+
return [];
|
|
5392
|
+
}
|
|
5393
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
5394
|
+
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
5395
|
+
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
5396
|
+
});
|
|
5397
|
+
}
|
|
5201
5398
|
|
|
5202
5399
|
// src/usage/poolPick.ts
|
|
5400
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
5203
5401
|
var import_zod9 = require("zod");
|
|
5204
5402
|
|
|
5205
5403
|
// src/usage/plan.ts
|
|
@@ -5452,14 +5650,14 @@ var PicksFileSchema = import_zod9.z.strictObject({
|
|
|
5452
5650
|
function errorMessage(error) {
|
|
5453
5651
|
return error instanceof Error ? error.message : String(error);
|
|
5454
5652
|
}
|
|
5455
|
-
function loadPoolMembers(
|
|
5653
|
+
function loadPoolMembers(fs12, paths, identities, nowMs) {
|
|
5456
5654
|
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5457
|
-
const log = readUsageLog(
|
|
5655
|
+
const log = readUsageLog(fs12, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5458
5656
|
return identities.map((identity) => {
|
|
5459
5657
|
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5460
5658
|
try {
|
|
5461
|
-
const snapshot = readUsageSnapshot(
|
|
5462
|
-
const account = readAccountMetadata(
|
|
5659
|
+
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
5660
|
+
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5463
5661
|
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5464
5662
|
} catch (error) {
|
|
5465
5663
|
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
@@ -5477,9 +5675,9 @@ function rankPoolFromStore(params) {
|
|
|
5477
5675
|
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5478
5676
|
});
|
|
5479
5677
|
}
|
|
5480
|
-
function readPicksFile(
|
|
5678
|
+
function readPicksFile(fs12, file) {
|
|
5481
5679
|
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5482
|
-
const raw =
|
|
5680
|
+
const raw = fs12.readFileUtf8(file);
|
|
5483
5681
|
if (raw === void 0) {
|
|
5484
5682
|
return { file: empty };
|
|
5485
5683
|
}
|
|
@@ -5492,17 +5690,72 @@ function readPicksFile(fs10, file) {
|
|
|
5492
5690
|
const result = PicksFileSchema.safeParse(parsed);
|
|
5493
5691
|
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5494
5692
|
}
|
|
5495
|
-
function readStickyPick(
|
|
5496
|
-
const read = readPicksFile(
|
|
5693
|
+
function readStickyPick(fs12, picksFile, directory) {
|
|
5694
|
+
const read = readPicksFile(fs12, picksFile);
|
|
5497
5695
|
const entry = read.file.picks[directory];
|
|
5498
5696
|
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5499
5697
|
}
|
|
5698
|
+
function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
5699
|
+
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5700
|
+
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5701
|
+
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5702
|
+
fs12.mkdirPrivate(import_node_path18.default.dirname(picksFile));
|
|
5703
|
+
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5704
|
+
`);
|
|
5705
|
+
}
|
|
5500
5706
|
|
|
5501
5707
|
// src/launcher/pool.ts
|
|
5502
|
-
|
|
5503
|
-
|
|
5708
|
+
var RESUME_FLAGS = /* @__PURE__ */ new Set(["--continue", "-c", "--resume", "-r"]);
|
|
5709
|
+
var TERMINATOR = "--";
|
|
5710
|
+
var REASONS_LOGGED = 3;
|
|
5711
|
+
function splitMembers(pool, paths, fs12) {
|
|
5712
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs12) !== void 0;
|
|
5504
5713
|
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5505
5714
|
}
|
|
5715
|
+
function isResuming(passthrough) {
|
|
5716
|
+
const end = passthrough.indexOf(TERMINATOR);
|
|
5717
|
+
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
5718
|
+
}
|
|
5719
|
+
function resolvePoolLaunch(params) {
|
|
5720
|
+
const { poolName, log } = params;
|
|
5721
|
+
const pool = params.pools?.[poolName];
|
|
5722
|
+
if (pool === void 0) {
|
|
5723
|
+
return { ok: false, message: `agent-shim: no pool named "${poolName}" (selected via ${params.selectedVia}). Run \`agent-shim pool add ${poolName} --identity <name>...\` first.` };
|
|
5724
|
+
}
|
|
5725
|
+
const { present: identities, missing } = splitMembers(pool, params.paths, params.fs);
|
|
5726
|
+
for (const name of missing) {
|
|
5727
|
+
log.warn(`agent-shim: pool "${poolName}" names identity "${name}", which does not exist; skipping it`);
|
|
5728
|
+
}
|
|
5729
|
+
if (identities.length === 0) {
|
|
5730
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
5731
|
+
}
|
|
5732
|
+
const resuming = isResuming(params.passthrough);
|
|
5733
|
+
const stickyRead = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd);
|
|
5734
|
+
if (stickyRead.problem !== void 0) {
|
|
5735
|
+
log.warn(`agent-shim: ${stickyRead.problem}; ignoring the last-pick record`);
|
|
5736
|
+
}
|
|
5737
|
+
for (; ; ) {
|
|
5738
|
+
const nowMs = params.now();
|
|
5739
|
+
const ranking = rankPoolFromStore({ fs: params.usageFs, paths: params.paths, identities, nowMs, resuming, ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky } });
|
|
5740
|
+
const { pick, earliestReturn } = ranking;
|
|
5741
|
+
if (pick !== void 0) {
|
|
5742
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
5743
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
5744
|
+
}
|
|
5745
|
+
if (earliestReturn === void 0) {
|
|
5746
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
|
5747
|
+
}
|
|
5748
|
+
const until = new Date(earliestReturn.atMs).toISOString();
|
|
5749
|
+
if (!params.wait) {
|
|
5750
|
+
return {
|
|
5751
|
+
ok: false,
|
|
5752
|
+
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.`
|
|
5753
|
+
};
|
|
5754
|
+
}
|
|
5755
|
+
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
5756
|
+
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5506
5759
|
|
|
5507
5760
|
// src/poolPickReport.ts
|
|
5508
5761
|
function candidateView(candidate) {
|
|
@@ -5542,14 +5795,15 @@ function collectPoolPick(params) {
|
|
|
5542
5795
|
var import_node_child_process = require("node:child_process");
|
|
5543
5796
|
var import_node_crypto8 = require("node:crypto");
|
|
5544
5797
|
var import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
5545
|
-
var
|
|
5798
|
+
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
5799
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
5546
5800
|
var import_node_sea = require("node:sea");
|
|
5547
5801
|
|
|
5548
5802
|
// src/versionDiscovery.ts
|
|
5549
5803
|
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
5550
|
-
var
|
|
5804
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
5551
5805
|
function defaultVersionsDir() {
|
|
5552
|
-
return
|
|
5806
|
+
return import_node_path19.default.join(import_node_os2.default.homedir(), ".local", "share", "claude", "versions");
|
|
5553
5807
|
}
|
|
5554
5808
|
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
5555
5809
|
function isNumericDottedVersion(name) {
|
|
@@ -5591,14 +5845,14 @@ function discoverClaudeBinary(options) {
|
|
|
5591
5845
|
const highestVersion = pickHighestVersion(entries);
|
|
5592
5846
|
if (highestVersion !== void 0) {
|
|
5593
5847
|
return {
|
|
5594
|
-
path:
|
|
5848
|
+
path: import_node_path19.default.join(versionsDir, highestVersion),
|
|
5595
5849
|
source: "versions-dir",
|
|
5596
5850
|
version: highestVersion
|
|
5597
5851
|
};
|
|
5598
5852
|
}
|
|
5599
|
-
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) =>
|
|
5853
|
+
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => import_node_path19.default.resolve(dir)));
|
|
5600
5854
|
for (const dir of options.pathDirs) {
|
|
5601
|
-
if (ownInstallDirs.has(
|
|
5855
|
+
if (ownInstallDirs.has(import_node_path19.default.resolve(dir))) {
|
|
5602
5856
|
continue;
|
|
5603
5857
|
}
|
|
5604
5858
|
const found = options.findExecutableInDir(dir, "claude");
|
|
@@ -5643,7 +5897,7 @@ function isErrorWithCode(error, code) {
|
|
|
5643
5897
|
}
|
|
5644
5898
|
var OWNER_ONLY_DIR_MODE = 448;
|
|
5645
5899
|
var OWNER_ONLY_FILE_MODE = 384;
|
|
5646
|
-
var
|
|
5900
|
+
var PERMISSION_BITS2 = 511;
|
|
5647
5901
|
var realFarmFs = {
|
|
5648
5902
|
lstat(filePath) {
|
|
5649
5903
|
let stat;
|
|
@@ -5709,7 +5963,7 @@ var realFarmFs = {
|
|
|
5709
5963
|
appendFilePrivate(filePath, contents) {
|
|
5710
5964
|
const descriptor = import_node_fs6.default.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
5711
5965
|
try {
|
|
5712
|
-
if ((import_node_fs6.default.fstatSync(descriptor).mode &
|
|
5966
|
+
if ((import_node_fs6.default.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
|
|
5713
5967
|
import_node_fs6.default.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
5714
5968
|
}
|
|
5715
5969
|
const bytes = Buffer.from(contents, "utf8");
|
|
@@ -5743,6 +5997,10 @@ var realFarmFs = {
|
|
|
5743
5997
|
}
|
|
5744
5998
|
}
|
|
5745
5999
|
};
|
|
6000
|
+
var INT32_BYTE_LENGTH = 4;
|
|
6001
|
+
function realSleepSync(ms) {
|
|
6002
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(INT32_BYTE_LENGTH)), 0, 0, ms);
|
|
6003
|
+
}
|
|
5746
6004
|
function realIsProcessAlive(pid) {
|
|
5747
6005
|
try {
|
|
5748
6006
|
process.kill(pid, 0);
|
|
@@ -5764,6 +6022,51 @@ var realRunPort = {
|
|
|
5764
6022
|
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
5765
6023
|
}
|
|
5766
6024
|
};
|
|
6025
|
+
function expandHome2(filePath) {
|
|
6026
|
+
return filePath.startsWith("~/") ? import_node_path20.default.join(import_node_os3.default.homedir(), filePath.slice(1)) : filePath;
|
|
6027
|
+
}
|
|
6028
|
+
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
6029
|
+
function hasDesktopSession() {
|
|
6030
|
+
if (process.platform === "darwin") {
|
|
6031
|
+
const result = (0, import_node_child_process.spawnSync)("launchctl", ["managername"], { encoding: "utf8" });
|
|
6032
|
+
return result.status === 0 && result.stdout.trim() === MACOS_GUI_SESSION_MANAGER;
|
|
6033
|
+
}
|
|
6034
|
+
if (process.platform === "win32") {
|
|
6035
|
+
return false;
|
|
6036
|
+
}
|
|
6037
|
+
return (process.env.DISPLAY ?? "") !== "" || (process.env.WAYLAND_DISPLAY ?? "") !== "";
|
|
6038
|
+
}
|
|
6039
|
+
var realCredentialPort = {
|
|
6040
|
+
readSecretFile(filePath) {
|
|
6041
|
+
const resolved = expandHome2(filePath);
|
|
6042
|
+
let content;
|
|
6043
|
+
let stat;
|
|
6044
|
+
try {
|
|
6045
|
+
stat = import_node_fs6.default.statSync(resolved);
|
|
6046
|
+
content = import_node_fs6.default.readFileSync(resolved, "utf8");
|
|
6047
|
+
} catch (error) {
|
|
6048
|
+
if (isEnoent3(error)) {
|
|
6049
|
+
return { found: false };
|
|
6050
|
+
}
|
|
6051
|
+
throw error;
|
|
6052
|
+
}
|
|
6053
|
+
return { found: true, content, mode: process.platform === "win32" ? void 0 : stat.mode };
|
|
6054
|
+
},
|
|
6055
|
+
runCommand(argv, options) {
|
|
6056
|
+
const [command, ...args] = argv;
|
|
6057
|
+
const result = (0, import_node_child_process.spawnSync)(command, args, {
|
|
6058
|
+
encoding: "utf8",
|
|
6059
|
+
timeout: options.timeoutMs,
|
|
6060
|
+
stdio: options.interactive ? ["inherit", "pipe", "inherit"] : ["ignore", "pipe", "pipe"]
|
|
6061
|
+
});
|
|
6062
|
+
if (result.error !== void 0) {
|
|
6063
|
+
const timedOut = isErrorWithCode(result.error, "ETIMEDOUT");
|
|
6064
|
+
return { status: result.status, stdout: "", stderr: timedOut ? "" : result.error.message, timedOut };
|
|
6065
|
+
}
|
|
6066
|
+
return { status: result.status, stdout: result.stdout, stderr: options.interactive ? "" : result.stderr, timedOut: false };
|
|
6067
|
+
},
|
|
6068
|
+
personPresent: () => process.stdin.isTTY || hasDesktopSession()
|
|
6069
|
+
};
|
|
5767
6070
|
function resolveGitBranch(run, cwd) {
|
|
5768
6071
|
const result = run.run("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
5769
6072
|
if (result.status !== 0) {
|
|
@@ -5778,6 +6081,62 @@ function resolveGitBranch(run, cwd) {
|
|
|
5778
6081
|
}
|
|
5779
6082
|
return { branch: name, branchDetached: false };
|
|
5780
6083
|
}
|
|
6084
|
+
function resolveGitRoot(run, cwd) {
|
|
6085
|
+
const result = run.run("git", ["-C", cwd, "rev-parse", "--show-toplevel"]);
|
|
6086
|
+
if (result.status !== 0) {
|
|
6087
|
+
return void 0;
|
|
6088
|
+
}
|
|
6089
|
+
const root = result.stdout.trim();
|
|
6090
|
+
return root === "" ? void 0 : root;
|
|
6091
|
+
}
|
|
6092
|
+
function detachedDaemonSpawnOptions(logFd, env) {
|
|
6093
|
+
return { detached: true, stdio: ["ignore", logFd, logFd], env };
|
|
6094
|
+
}
|
|
6095
|
+
function spawnDaemonThrough(executable) {
|
|
6096
|
+
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
6097
|
+
}
|
|
6098
|
+
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
6099
|
+
import_node_fs6.default.mkdirSync(import_node_path20.default.dirname(logPath), { recursive: true });
|
|
6100
|
+
const logFd = import_node_fs6.default.openSync(logPath, "a");
|
|
6101
|
+
try {
|
|
6102
|
+
const child = (0, import_node_child_process.spawn)(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
6103
|
+
child.unref();
|
|
6104
|
+
if (child.pid === void 0) {
|
|
6105
|
+
throw new Error(`spawning ${subcommand} returned no pid`);
|
|
6106
|
+
}
|
|
6107
|
+
return child.pid;
|
|
6108
|
+
} finally {
|
|
6109
|
+
import_node_fs6.default.closeSync(logFd);
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
function spawnHeadroomSupervisor(paths, spawnDaemon) {
|
|
6113
|
+
return spawnDaemon(paths, "__headroom-supervisor", paths.headroomLogPath);
|
|
6114
|
+
}
|
|
6115
|
+
function realHeadroomPort(paths, options) {
|
|
6116
|
+
return {
|
|
6117
|
+
ensure: () => {
|
|
6118
|
+
const up = ensureHeadroom({
|
|
6119
|
+
paths,
|
|
6120
|
+
launcherPid: process.pid,
|
|
6121
|
+
ports: {
|
|
6122
|
+
fs: realFarmFs,
|
|
6123
|
+
// 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.
|
|
6124
|
+
isRunning: realIsProcessRunning,
|
|
6125
|
+
now: () => Date.now(),
|
|
6126
|
+
sleep: realSleepSync,
|
|
6127
|
+
spawnSupervisor: (layout) => spawnHeadroomSupervisor(layout, options.spawnDaemon)
|
|
6128
|
+
}
|
|
6129
|
+
});
|
|
6130
|
+
return {
|
|
6131
|
+
port: up.port,
|
|
6132
|
+
projectId: resolveGitRoot(realRunPort, options.cwd) ?? options.cwd
|
|
6133
|
+
};
|
|
6134
|
+
},
|
|
6135
|
+
release: () => {
|
|
6136
|
+
removeSession(realFarmFs, paths.headroomSessionsDir, process.pid);
|
|
6137
|
+
}
|
|
6138
|
+
};
|
|
6139
|
+
}
|
|
5781
6140
|
var realProcPort = {
|
|
5782
6141
|
env: process.env,
|
|
5783
6142
|
argv: process.argv.slice(2),
|
|
@@ -5795,7 +6154,7 @@ function listVersionsDir(dir) {
|
|
|
5795
6154
|
throw error;
|
|
5796
6155
|
}
|
|
5797
6156
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
5798
|
-
const fullPath =
|
|
6157
|
+
const fullPath = import_node_path20.default.join(dir, entry.name);
|
|
5799
6158
|
const stat = import_node_fs6.default.statSync(fullPath);
|
|
5800
6159
|
return {
|
|
5801
6160
|
name: entry.name,
|
|
@@ -5810,7 +6169,7 @@ function windowsPathExtensions(pathext) {
|
|
|
5810
6169
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
5811
6170
|
}
|
|
5812
6171
|
function resolveExecutableCandidate(dir, name, env) {
|
|
5813
|
-
const candidate =
|
|
6172
|
+
const candidate = import_node_path20.default.join(dir, name);
|
|
5814
6173
|
if (env.platform === "win32") {
|
|
5815
6174
|
const extensions = windowsPathExtensions(env.pathext);
|
|
5816
6175
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -5859,13 +6218,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
5859
6218
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
5860
6219
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
5861
6220
|
}
|
|
5862
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
5863
|
-
const candidateDir =
|
|
5864
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
6221
|
+
const candidate = env.argv1 === void 0 ? env.execPath : import_node_path20.default.resolve(env.cwd, env.argv1);
|
|
6222
|
+
const candidateDir = import_node_path20.default.dirname(candidate);
|
|
6223
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => import_node_path20.default.resolve(dir) === candidateDir);
|
|
5865
6224
|
if (candidateDirOnPath) {
|
|
5866
6225
|
return candidate;
|
|
5867
6226
|
}
|
|
5868
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
6227
|
+
const viaPath = searchPathForExecutable(env.pathDirs, import_node_path20.default.basename(candidate), env.findExecutableInDir);
|
|
5869
6228
|
return viaPath ?? candidate;
|
|
5870
6229
|
}
|
|
5871
6230
|
function realOwnExecutablePath() {
|
|
@@ -5873,21 +6232,21 @@ function realOwnExecutablePath() {
|
|
|
5873
6232
|
argv1: process.argv[1],
|
|
5874
6233
|
execPath: process.execPath,
|
|
5875
6234
|
cwd: process.cwd(),
|
|
5876
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6235
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path20.default.delimiter).filter((dir) => dir !== ""),
|
|
5877
6236
|
findExecutableInDir
|
|
5878
6237
|
});
|
|
5879
6238
|
}
|
|
5880
6239
|
function realResolveClaudeBinary(ownInstallDirs) {
|
|
5881
6240
|
return () => discoverClaudeBinary({
|
|
5882
6241
|
listVersionsDir,
|
|
5883
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6242
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path20.default.delimiter).filter((dir) => dir !== ""),
|
|
5884
6243
|
findExecutableInDir,
|
|
5885
6244
|
ownInstallDirs: [...ownInstallDirs]
|
|
5886
6245
|
});
|
|
5887
6246
|
}
|
|
5888
6247
|
|
|
5889
6248
|
// src/checkReport.ts
|
|
5890
|
-
var
|
|
6249
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
5891
6250
|
var import_zod10 = require("zod");
|
|
5892
6251
|
|
|
5893
6252
|
// src/cli/bool.ts
|
|
@@ -6209,7 +6568,7 @@ function loadCheckProvider(paths, name) {
|
|
|
6209
6568
|
}
|
|
6210
6569
|
function collectCheckReport(params) {
|
|
6211
6570
|
const { paths, cwd } = params;
|
|
6212
|
-
const home =
|
|
6571
|
+
const home = import_node_os4.default.homedir();
|
|
6213
6572
|
const claudeHome = resolveClaudeHome();
|
|
6214
6573
|
const read = cosmiconfigReader();
|
|
6215
6574
|
const classification = loadClassification(paths);
|
|
@@ -6254,10 +6613,10 @@ function collectCheckReport(params) {
|
|
|
6254
6613
|
}).input;
|
|
6255
6614
|
const providerName = cascadeProviderName(cascade);
|
|
6256
6615
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
6257
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
6616
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : import_node_path21.default.join(paths.identitiesDir, identityDecision.name);
|
|
6258
6617
|
const settingsFiles = {
|
|
6259
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
6260
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
6618
|
+
"settings.json": realFsPort.readFileUtf8(import_node_path21.default.join(claudeHome, "settings.json")),
|
|
6619
|
+
"settings.local.json": realFsPort.readFileUtf8(import_node_path21.default.join(claudeHome, "settings.local.json"))
|
|
6261
6620
|
};
|
|
6262
6621
|
const report = runCheck({
|
|
6263
6622
|
cwd,
|
|
@@ -6287,7 +6646,7 @@ function collectCheckReport(params) {
|
|
|
6287
6646
|
|
|
6288
6647
|
// src/claudeShim.ts
|
|
6289
6648
|
var import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
6290
|
-
var
|
|
6649
|
+
var import_node_path22 = __toESM(require("node:path"), 1);
|
|
6291
6650
|
var import_zod11 = require("zod");
|
|
6292
6651
|
var ClaudeShimStateSchema = import_zod11.z.strictObject({
|
|
6293
6652
|
targetPath: import_zod11.z.string().min(1),
|
|
@@ -6298,29 +6657,29 @@ function commandFilename(ownExecutablePath, commandName) {
|
|
|
6298
6657
|
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
6299
6658
|
}
|
|
6300
6659
|
function resolveOwnInstallDirs(paths, ownExecutablePath) {
|
|
6301
|
-
const dirs = [
|
|
6660
|
+
const dirs = [import_node_path22.default.dirname(ownExecutablePath)];
|
|
6302
6661
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6303
6662
|
if (state !== void 0) {
|
|
6304
|
-
dirs.push(
|
|
6663
|
+
dirs.push(import_node_path22.default.dirname(state.targetPath));
|
|
6305
6664
|
}
|
|
6306
6665
|
return dirs;
|
|
6307
6666
|
}
|
|
6308
6667
|
var nodeLinkFs = { link: import_node_fs7.default.linkSync, copyFile: import_node_fs7.default.copyFileSync, chmod: import_node_fs7.default.chmodSync };
|
|
6309
6668
|
function findPathShadow(params) {
|
|
6310
|
-
const resolvedTargetDir =
|
|
6669
|
+
const resolvedTargetDir = import_node_path22.default.resolve(params.targetDir);
|
|
6311
6670
|
for (const dir of params.pathDirs) {
|
|
6312
6671
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
6313
6672
|
if (found === void 0) {
|
|
6314
6673
|
continue;
|
|
6315
6674
|
}
|
|
6316
|
-
return
|
|
6675
|
+
return import_node_path22.default.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
6317
6676
|
}
|
|
6318
|
-
return params.pathDirs.some((dir) =>
|
|
6677
|
+
return params.pathDirs.some((dir) => import_node_path22.default.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
6319
6678
|
}
|
|
6320
6679
|
|
|
6321
6680
|
// src/doctorReport.ts
|
|
6322
6681
|
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
6323
|
-
var
|
|
6682
|
+
var import_node_path23 = __toESM(require("node:path"), 1);
|
|
6324
6683
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
6325
6684
|
if (status.status !== "shadowed") {
|
|
6326
6685
|
return status;
|
|
@@ -6344,7 +6703,7 @@ function validateJson(schema, input) {
|
|
|
6344
6703
|
return { ok: true, data: result.data };
|
|
6345
6704
|
}
|
|
6346
6705
|
function pushPathResolution(push, resolution) {
|
|
6347
|
-
const ownDir =
|
|
6706
|
+
const ownDir = import_node_path23.default.dirname(resolution.ownExecutablePath);
|
|
6348
6707
|
switch (resolution.agentShim.status) {
|
|
6349
6708
|
case "ok":
|
|
6350
6709
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -6412,14 +6771,14 @@ function pushProvider(push, entry) {
|
|
|
6412
6771
|
}
|
|
6413
6772
|
function pushLegacyName(push, params) {
|
|
6414
6773
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
6415
|
-
if (legacyVariables.length === 0 &&
|
|
6774
|
+
if (legacyVariables.length === 0 && import_node_path23.default.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
6416
6775
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
6417
6776
|
return;
|
|
6418
6777
|
}
|
|
6419
6778
|
for (const name of legacyVariables) {
|
|
6420
6779
|
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);
|
|
6421
6780
|
}
|
|
6422
|
-
if (
|
|
6781
|
+
if (import_node_path23.default.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
6423
6782
|
push(
|
|
6424
6783
|
"legacy-name",
|
|
6425
6784
|
"warn",
|
|
@@ -6727,18 +7086,18 @@ function collectDoctorReport(params) {
|
|
|
6727
7086
|
const { paths } = params;
|
|
6728
7087
|
const identityNames = import_node_fs8.default.existsSync(paths.identitiesDir) ? import_node_fs8.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
6729
7088
|
const identities = identityNames.map((name) => {
|
|
6730
|
-
const farmRoot =
|
|
6731
|
-
const identityPath =
|
|
7089
|
+
const farmRoot = import_node_path23.default.join(paths.identitiesDir, name);
|
|
7090
|
+
const identityPath = import_node_path23.default.join(farmRoot, "identity.json");
|
|
6732
7091
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
6733
7092
|
});
|
|
6734
7093
|
const profileNames = import_node_fs8.default.existsSync(paths.configProfilesDir) ? import_node_fs8.default.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6735
7094
|
const configProfiles = profileNames.map((name) => {
|
|
6736
|
-
const profilePath =
|
|
7095
|
+
const profilePath = import_node_path23.default.join(paths.configProfilesDir, `${name}.json`);
|
|
6737
7096
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
6738
7097
|
});
|
|
6739
7098
|
const providerNames = import_node_fs8.default.existsSync(paths.providersDir) ? import_node_fs8.default.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6740
7099
|
const providers = providerNames.map((name) => {
|
|
6741
|
-
const providerPath =
|
|
7100
|
+
const providerPath = import_node_path23.default.join(paths.providersDir, `${name}.json`);
|
|
6742
7101
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
6743
7102
|
});
|
|
6744
7103
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -6750,12 +7109,12 @@ function collectDoctorReport(params) {
|
|
|
6750
7109
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
6751
7110
|
}
|
|
6752
7111
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6753
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
7112
|
+
const pathDirs = (params.env.PATH ?? "").split(import_node_path23.default.delimiter).filter((dir) => dir !== "");
|
|
6754
7113
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
6755
7114
|
findPathShadow({
|
|
6756
7115
|
pathDirs,
|
|
6757
|
-
targetDir:
|
|
6758
|
-
targetFilename:
|
|
7116
|
+
targetDir: import_node_path23.default.dirname(shimState.targetPath),
|
|
7117
|
+
targetFilename: import_node_path23.default.basename(shimState.targetPath),
|
|
6759
7118
|
findExecutableInDir
|
|
6760
7119
|
}),
|
|
6761
7120
|
shimState.targetPath,
|
|
@@ -6778,7 +7137,7 @@ function collectDoctorReport(params) {
|
|
|
6778
7137
|
agentShim: refinePathShadow(
|
|
6779
7138
|
findPathShadow({
|
|
6780
7139
|
pathDirs,
|
|
6781
|
-
targetDir:
|
|
7140
|
+
targetDir: import_node_path23.default.dirname(ownExecutablePath),
|
|
6782
7141
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
6783
7142
|
findExecutableInDir
|
|
6784
7143
|
}),
|
|
@@ -6796,6 +7155,975 @@ function collectDoctorReport(params) {
|
|
|
6796
7155
|
});
|
|
6797
7156
|
return report;
|
|
6798
7157
|
}
|
|
7158
|
+
|
|
7159
|
+
// src/launcher.ts
|
|
7160
|
+
var import_node_crypto9 = require("node:crypto");
|
|
7161
|
+
var import_node_path25 = __toESM(require("node:path"), 1);
|
|
7162
|
+
|
|
7163
|
+
// src/launcher/argv.ts
|
|
7164
|
+
var ConflictingIdentityError = class extends UsageError {
|
|
7165
|
+
constructor(positional, flag) {
|
|
7166
|
+
super(`The launch names two identities: "@${positional}" and "--identity ${flag}". Pass only one.`);
|
|
7167
|
+
this.positional = positional;
|
|
7168
|
+
this.flag = flag;
|
|
7169
|
+
this.name = "ConflictingIdentityError";
|
|
7170
|
+
}
|
|
7171
|
+
positional;
|
|
7172
|
+
flag;
|
|
7173
|
+
};
|
|
7174
|
+
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--category", "--share", "--hide"];
|
|
7175
|
+
var BOOLEAN_FLAGS = [
|
|
7176
|
+
{ flag: "--headroom", key: "headroom" },
|
|
7177
|
+
{ flag: "--track-usage", key: "trackUsage" },
|
|
7178
|
+
{ flag: "--skip-permissions", key: "skipPermissions" },
|
|
7179
|
+
{ flag: "--remote-control", key: "remoteControl" },
|
|
7180
|
+
{ flag: "--wait", key: "wait" }
|
|
7181
|
+
];
|
|
7182
|
+
var LAUNCHER_FLAG_NAMES = [
|
|
7183
|
+
...VALUED_FLAGS,
|
|
7184
|
+
"--no-provider",
|
|
7185
|
+
...BOOLEAN_FLAGS.flatMap(({ flag }) => [flag, `--no-${flag.slice(2)}`])
|
|
7186
|
+
];
|
|
7187
|
+
var TERMINATOR2 = "--";
|
|
7188
|
+
function matchValuedFlag(token) {
|
|
7189
|
+
for (const flag of VALUED_FLAGS) {
|
|
7190
|
+
if (token === flag) {
|
|
7191
|
+
return { flag };
|
|
7192
|
+
}
|
|
7193
|
+
if (token.startsWith(`${flag}=`)) {
|
|
7194
|
+
return { flag, inlineValue: token.slice(flag.length + 1) };
|
|
7195
|
+
}
|
|
7196
|
+
}
|
|
7197
|
+
return void 0;
|
|
7198
|
+
}
|
|
7199
|
+
function matchBooleanFlag(token) {
|
|
7200
|
+
for (const { flag, key } of BOOLEAN_FLAGS) {
|
|
7201
|
+
if (token === flag) {
|
|
7202
|
+
return { key, value: true };
|
|
7203
|
+
}
|
|
7204
|
+
if (token === `--no-${flag.slice(2)}`) {
|
|
7205
|
+
return { key, value: false };
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7208
|
+
return void 0;
|
|
7209
|
+
}
|
|
7210
|
+
function parseLauncherArgv(argv) {
|
|
7211
|
+
const first = argv[0];
|
|
7212
|
+
const positionalIdentity = first !== void 0 && first.startsWith("@") && first.length > 1 ? first.slice(1) : void 0;
|
|
7213
|
+
const remaining = positionalIdentity === void 0 ? argv : argv.slice(1);
|
|
7214
|
+
let flagIdentity;
|
|
7215
|
+
let configProfile;
|
|
7216
|
+
let provider;
|
|
7217
|
+
const booleans = {};
|
|
7218
|
+
const categoryFlags = [];
|
|
7219
|
+
const shareFlags = [];
|
|
7220
|
+
const hideFlags = [];
|
|
7221
|
+
const rest = [];
|
|
7222
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
7223
|
+
const token = remaining[index];
|
|
7224
|
+
if (token === void 0) {
|
|
7225
|
+
continue;
|
|
7226
|
+
}
|
|
7227
|
+
if (token === TERMINATOR2) {
|
|
7228
|
+
rest.push(...remaining.slice(index));
|
|
7229
|
+
break;
|
|
7230
|
+
}
|
|
7231
|
+
if (token === "--no-provider") {
|
|
7232
|
+
provider = false;
|
|
7233
|
+
continue;
|
|
7234
|
+
}
|
|
7235
|
+
const booleanFlag = matchBooleanFlag(token);
|
|
7236
|
+
if (booleanFlag !== void 0) {
|
|
7237
|
+
booleans[booleanFlag.key] = booleanFlag.value;
|
|
7238
|
+
continue;
|
|
7239
|
+
}
|
|
7240
|
+
const matched = matchValuedFlag(token);
|
|
7241
|
+
if (matched === void 0) {
|
|
7242
|
+
rest.push(token);
|
|
7243
|
+
continue;
|
|
7244
|
+
}
|
|
7245
|
+
let value;
|
|
7246
|
+
if (matched.inlineValue !== void 0) {
|
|
7247
|
+
value = matched.inlineValue;
|
|
7248
|
+
} else {
|
|
7249
|
+
const next = remaining[index + 1];
|
|
7250
|
+
if (next === void 0 || next === TERMINATOR2) {
|
|
7251
|
+
rest.push(token);
|
|
7252
|
+
continue;
|
|
7253
|
+
}
|
|
7254
|
+
value = next;
|
|
7255
|
+
index += 1;
|
|
7256
|
+
}
|
|
7257
|
+
switch (matched.flag) {
|
|
7258
|
+
case "--identity":
|
|
7259
|
+
flagIdentity = value;
|
|
7260
|
+
break;
|
|
7261
|
+
case "--config-profile":
|
|
7262
|
+
configProfile = value;
|
|
7263
|
+
break;
|
|
7264
|
+
case "--provider":
|
|
7265
|
+
provider = value;
|
|
7266
|
+
break;
|
|
7267
|
+
case "--category":
|
|
7268
|
+
categoryFlags.push(value);
|
|
7269
|
+
break;
|
|
7270
|
+
case "--share":
|
|
7271
|
+
shareFlags.push(value);
|
|
7272
|
+
break;
|
|
7273
|
+
case "--hide":
|
|
7274
|
+
hideFlags.push(value);
|
|
7275
|
+
break;
|
|
7276
|
+
default:
|
|
7277
|
+
matched.flag;
|
|
7278
|
+
}
|
|
7279
|
+
}
|
|
7280
|
+
if (positionalIdentity !== void 0 && flagIdentity !== void 0 && positionalIdentity !== flagIdentity) {
|
|
7281
|
+
throw new ConflictingIdentityError(positionalIdentity, flagIdentity);
|
|
7282
|
+
}
|
|
7283
|
+
const identity = flagIdentity ?? positionalIdentity;
|
|
7284
|
+
return {
|
|
7285
|
+
...identity === void 0 ? {} : { identity },
|
|
7286
|
+
...configProfile === void 0 ? {} : { configProfile },
|
|
7287
|
+
...provider === void 0 ? {} : { provider },
|
|
7288
|
+
...booleans,
|
|
7289
|
+
categoryFlags,
|
|
7290
|
+
shareFlags,
|
|
7291
|
+
hideFlags,
|
|
7292
|
+
rest
|
|
7293
|
+
};
|
|
7294
|
+
}
|
|
7295
|
+
|
|
7296
|
+
// src/cli/pairs.ts
|
|
7297
|
+
function splitCommas(input) {
|
|
7298
|
+
if (input === "") {
|
|
7299
|
+
return [];
|
|
7300
|
+
}
|
|
7301
|
+
return input.split(",");
|
|
7302
|
+
}
|
|
7303
|
+
function parsePair(input) {
|
|
7304
|
+
const eqIndex = input.indexOf("=");
|
|
7305
|
+
if (eqIndex === -1) {
|
|
7306
|
+
throw new Error(`Expected "<key>=<value>", got "${input}" (no "=" found)`);
|
|
7307
|
+
}
|
|
7308
|
+
const key = input.slice(0, eqIndex);
|
|
7309
|
+
const value = input.slice(eqIndex + 1);
|
|
7310
|
+
if (key === "") {
|
|
7311
|
+
throw new Error(`Expected a non-empty key before "=" in "${input}"`);
|
|
7312
|
+
}
|
|
7313
|
+
return { key, value };
|
|
7314
|
+
}
|
|
7315
|
+
function parseBool(input) {
|
|
7316
|
+
const parsed = parseBoolWord(input);
|
|
7317
|
+
if (parsed === void 0) {
|
|
7318
|
+
throw new Error(`Expected "true", "false", "1" or "0", got "${input}"`);
|
|
7319
|
+
}
|
|
7320
|
+
return parsed;
|
|
7321
|
+
}
|
|
7322
|
+
function parseBoolPairList(input) {
|
|
7323
|
+
const result = {};
|
|
7324
|
+
for (const piece of splitCommas(input)) {
|
|
7325
|
+
const { key, value } = parsePair(piece);
|
|
7326
|
+
result[key] = parseBool(value);
|
|
7327
|
+
}
|
|
7328
|
+
return result;
|
|
7329
|
+
}
|
|
7330
|
+
|
|
7331
|
+
// src/launcher/cliOverride.ts
|
|
7332
|
+
var InvalidCliCategoryError = class extends CliError {
|
|
7333
|
+
constructor(categoryName) {
|
|
7334
|
+
super(`"${categoryName}" is not a category this launch may toggle (runtime, history, knowledge, settings).`);
|
|
7335
|
+
this.categoryName = categoryName;
|
|
7336
|
+
this.name = "InvalidCliCategoryError";
|
|
7337
|
+
}
|
|
7338
|
+
categoryName;
|
|
7339
|
+
};
|
|
7340
|
+
var InvalidCliEntryKeyError = class extends CliError {
|
|
7341
|
+
constructor(key) {
|
|
7342
|
+
super(`"${key}" is not a valid entries key \u2014 it must start with "<category>/", e.g. "knowledge/skills/commit".`);
|
|
7343
|
+
this.key = key;
|
|
7344
|
+
this.name = "InvalidCliEntryKeyError";
|
|
7345
|
+
}
|
|
7346
|
+
key;
|
|
7347
|
+
};
|
|
7348
|
+
function toCategoryMap(pairs) {
|
|
7349
|
+
const expanded = expandAllCategoryKey(pairs);
|
|
7350
|
+
const result = {};
|
|
7351
|
+
for (const [key, value] of Object.entries(expanded)) {
|
|
7352
|
+
if (!isOverridableCategory(key)) {
|
|
7353
|
+
throw new InvalidCliCategoryError(key);
|
|
7354
|
+
}
|
|
7355
|
+
result[key] = value;
|
|
7356
|
+
}
|
|
7357
|
+
return result;
|
|
7358
|
+
}
|
|
7359
|
+
function toEntries(pairs) {
|
|
7360
|
+
for (const key of Object.keys(pairs)) {
|
|
7361
|
+
if (!ENTRY_KEY_RE.test(key)) {
|
|
7362
|
+
throw new InvalidCliEntryKeyError(key);
|
|
7363
|
+
}
|
|
7364
|
+
}
|
|
7365
|
+
return pairs;
|
|
7366
|
+
}
|
|
7367
|
+
function parseOverride(source, raw, parse) {
|
|
7368
|
+
try {
|
|
7369
|
+
return parse(raw);
|
|
7370
|
+
} catch (error) {
|
|
7371
|
+
if (error instanceof Error && !(error instanceof CliError)) {
|
|
7372
|
+
throw new UsageError(`Invalid ${source} value "${raw}": ${error.message}.`);
|
|
7373
|
+
}
|
|
7374
|
+
throw error;
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
function buildCliOverride(params) {
|
|
7378
|
+
let categoryPairs = {};
|
|
7379
|
+
if (params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== "") {
|
|
7380
|
+
categoryPairs = parseOverride("AGENT_SHIM_CATEGORY_OVERRIDE", params.env.AGENT_SHIM_CATEGORY_OVERRIDE, parseBoolPairList);
|
|
7381
|
+
}
|
|
7382
|
+
for (const flagValue of params.categoryFlags) {
|
|
7383
|
+
const pair = parseOverride("--category", flagValue, (raw) => {
|
|
7384
|
+
const { key, value } = parsePair(raw);
|
|
7385
|
+
return { key, value: parseBool(value) };
|
|
7386
|
+
});
|
|
7387
|
+
categoryPairs[pair.key] = pair.value;
|
|
7388
|
+
}
|
|
7389
|
+
let entryPairs = {};
|
|
7390
|
+
if (params.env.AGENT_SHIM_ENTRY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_ENTRY_OVERRIDE !== "") {
|
|
7391
|
+
entryPairs = parseOverride("AGENT_SHIM_ENTRY_OVERRIDE", params.env.AGENT_SHIM_ENTRY_OVERRIDE, parseBoolPairList);
|
|
7392
|
+
}
|
|
7393
|
+
for (const entryPath of params.shareFlags) {
|
|
7394
|
+
entryPairs[entryPath] = true;
|
|
7395
|
+
}
|
|
7396
|
+
for (const entryPath of params.hideFlags) {
|
|
7397
|
+
entryPairs[entryPath] = false;
|
|
7398
|
+
}
|
|
7399
|
+
const hasCategories = Object.keys(categoryPairs).length > 0;
|
|
7400
|
+
const hasEntries = Object.keys(entryPairs).length > 0;
|
|
7401
|
+
const hasProvider = params.providerFlag !== void 0 && params.providerFlag !== "";
|
|
7402
|
+
if (!hasCategories && !hasEntries && !hasProvider) {
|
|
7403
|
+
return void 0;
|
|
7404
|
+
}
|
|
7405
|
+
return {
|
|
7406
|
+
...hasCategories ? { categories: toCategoryMap(categoryPairs) } : {},
|
|
7407
|
+
...hasEntries ? { entries: toEntries(entryPairs) } : {},
|
|
7408
|
+
...hasProvider ? { launch: { provider: params.providerFlag } } : {}
|
|
7409
|
+
};
|
|
7410
|
+
}
|
|
7411
|
+
|
|
7412
|
+
// src/launcher/flags.ts
|
|
7413
|
+
var import_node_path24 = __toESM(require("node:path"), 1);
|
|
7414
|
+
|
|
7415
|
+
// src/headroom/headers.ts
|
|
7416
|
+
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
7417
|
+
const merged = /* @__PURE__ */ new Map();
|
|
7418
|
+
for (const block of existing) {
|
|
7419
|
+
if (block === void 0) {
|
|
7420
|
+
continue;
|
|
7421
|
+
}
|
|
7422
|
+
for (const line of block.split("\n")) {
|
|
7423
|
+
const entry = parseCustomHeaderLine(line);
|
|
7424
|
+
if (entry === void 0) {
|
|
7425
|
+
continue;
|
|
7426
|
+
}
|
|
7427
|
+
merged.set(entry.name, entry.value);
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7430
|
+
for (const entry of additions) {
|
|
7431
|
+
merged.set(entry.name, entry.value);
|
|
7432
|
+
}
|
|
7433
|
+
return [...merged].map(([name, value]) => `${name}: ${value}`).join("\n");
|
|
7434
|
+
}
|
|
7435
|
+
function parseCustomHeaderLine(line) {
|
|
7436
|
+
const trimmed = line.trim();
|
|
7437
|
+
if (trimmed === "") {
|
|
7438
|
+
return void 0;
|
|
7439
|
+
}
|
|
7440
|
+
const colon = trimmed.indexOf(":");
|
|
7441
|
+
if (colon <= 0) {
|
|
7442
|
+
return void 0;
|
|
7443
|
+
}
|
|
7444
|
+
return { name: trimmed.slice(0, colon).trim(), value: trimmed.slice(colon + 1).trim() };
|
|
7445
|
+
}
|
|
7446
|
+
|
|
7447
|
+
// src/launcher/flags.ts
|
|
7448
|
+
function resolveLaunchFlags(params) {
|
|
7449
|
+
return {
|
|
7450
|
+
skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
|
|
7451
|
+
remoteControl: params.flags?.remoteControl ?? parseEnvBool("AGENT_SHIM_REMOTE_CONTROL", params.env.AGENT_SHIM_REMOTE_CONTROL) ?? params.cascade?.remoteControl ?? false,
|
|
7452
|
+
headroom: params.flags?.headroom ?? parseEnvBool("AGENT_SHIM_HEADROOM", params.env.AGENT_SHIM_HEADROOM) ?? params.cascade?.headroom ?? false,
|
|
7453
|
+
trackUsage: params.flags?.trackUsage ?? parseEnvBool("AGENT_SHIM_TRACK_USAGE", params.env.AGENT_SHIM_TRACK_USAGE) ?? params.cascade?.trackUsage ?? params.trackUsageDefault ?? false
|
|
7454
|
+
};
|
|
7455
|
+
}
|
|
7456
|
+
function buildFlagArgs(flags) {
|
|
7457
|
+
const args = [];
|
|
7458
|
+
if (flags.skipPermissions) {
|
|
7459
|
+
args.push("--dangerously-skip-permissions");
|
|
7460
|
+
}
|
|
7461
|
+
if (flags.remoteControl) {
|
|
7462
|
+
args.push("--remote-control=");
|
|
7463
|
+
}
|
|
7464
|
+
return args;
|
|
7465
|
+
}
|
|
7466
|
+
function buildArgv(params) {
|
|
7467
|
+
return [...params.toolFlags, ...params.extraFlags, ...params.passthrough];
|
|
7468
|
+
}
|
|
7469
|
+
var LOOPBACK_HOSTS = ["127.0.0.1", "localhost", "::1"];
|
|
7470
|
+
function isLoopbackUrl(baseUrl) {
|
|
7471
|
+
const host = new URL(baseUrl).hostname.replace(/^\[|\]$/g, "");
|
|
7472
|
+
return LOOPBACK_HOSTS.some((loopback) => loopback === host);
|
|
7473
|
+
}
|
|
7474
|
+
function withLoopbackHosts(existing) {
|
|
7475
|
+
const listed = (existing ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry !== "");
|
|
7476
|
+
return [...listed, ...LOOPBACK_HOSTS.filter((host) => !listed.includes(host))].join(",");
|
|
7477
|
+
}
|
|
7478
|
+
function buildEnv(params) {
|
|
7479
|
+
const env = { ...params.baseEnv };
|
|
7480
|
+
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7481
|
+
let providerCustomHeaders;
|
|
7482
|
+
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
7483
|
+
env.CLAUDE_CONFIG_DIR = import_node_path24.default.join(params.identitiesDir, params.resolvedIdentityName);
|
|
7484
|
+
}
|
|
7485
|
+
if (params.provider !== void 0) {
|
|
7486
|
+
const providerEnv = params.provider.definition.env ?? {};
|
|
7487
|
+
providerCustomHeaders = providerEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7488
|
+
env.ANTHROPIC_BASE_URL = params.provider.baseUrl;
|
|
7489
|
+
if (isLoopbackUrl(params.provider.baseUrl)) {
|
|
7490
|
+
env.NO_PROXY = withLoopbackHosts(params.baseEnv.NO_PROXY);
|
|
7491
|
+
env.no_proxy = withLoopbackHosts(params.baseEnv.no_proxy);
|
|
7492
|
+
}
|
|
7493
|
+
env.AGENT_SHIM_PROVIDER = params.provider.definition.displayName;
|
|
7494
|
+
for (const [key, value] of Object.entries(providerEnv)) {
|
|
7495
|
+
env[key] = value;
|
|
7496
|
+
}
|
|
7497
|
+
}
|
|
7498
|
+
const credential = params.provider?.credential ?? params.identityCredential;
|
|
7499
|
+
if (credential !== void 0) {
|
|
7500
|
+
for (const [variable, value] of Object.entries(credentialVariables(credential.target, credential.token))) {
|
|
7501
|
+
env[variable] = value;
|
|
7502
|
+
}
|
|
7503
|
+
}
|
|
7504
|
+
if (params.headroom !== void 0) {
|
|
7505
|
+
env.HEADROOM_PROXY_URL = `http://127.0.0.1:${String(params.headroom.port)}`;
|
|
7506
|
+
}
|
|
7507
|
+
if (params.frontdoor !== void 0) {
|
|
7508
|
+
env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeaders(
|
|
7509
|
+
[
|
|
7510
|
+
...parentCustomHeaders === void 0 ? [] : [parentCustomHeaders],
|
|
7511
|
+
...providerCustomHeaders === void 0 ? [] : [providerCustomHeaders]
|
|
7512
|
+
],
|
|
7513
|
+
[
|
|
7514
|
+
...params.resolvedIdentityName === void 0 ? [] : [{ name: IDENTITY_HEADER, value: params.resolvedIdentityName }],
|
|
7515
|
+
{ name: SESSION_HEADER, value: params.sessionId },
|
|
7516
|
+
{ name: AUTH_HEADER, value: params.frontdoor.sessionToken },
|
|
7517
|
+
...params.headroom === void 0 ? [] : [
|
|
7518
|
+
{ name: HEADROOM_FLAG_HEADER, value: "1" },
|
|
7519
|
+
{ name: PROJECT_ID_HEADER, value: params.headroom.projectId }
|
|
7520
|
+
]
|
|
7521
|
+
]
|
|
7522
|
+
);
|
|
7523
|
+
env.NODE_EXTRA_CA_CERTS = params.frontdoor.trustBundlePath;
|
|
7524
|
+
if (params.provider === void 0) {
|
|
7525
|
+
env.HTTPS_PROXY = connectProxyUrl(params.frontdoor.connectPort, params.frontdoor.sessionToken);
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
return env;
|
|
7529
|
+
}
|
|
7530
|
+
|
|
7531
|
+
// src/launcher/extraFlags.ts
|
|
7532
|
+
function splitExtraFlags(value) {
|
|
7533
|
+
if (value === void 0) {
|
|
7534
|
+
return [];
|
|
7535
|
+
}
|
|
7536
|
+
const trimmed = value.trim();
|
|
7537
|
+
if (trimmed === "") {
|
|
7538
|
+
return [];
|
|
7539
|
+
}
|
|
7540
|
+
return trimmed.split(/\s+/);
|
|
7541
|
+
}
|
|
7542
|
+
|
|
7543
|
+
// src/launcher.ts
|
|
7544
|
+
function resolveIdentityCredential(identity, credential, env, port) {
|
|
7545
|
+
if (port === void 0) {
|
|
7546
|
+
return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
|
|
7547
|
+
}
|
|
7548
|
+
const resolution = resolveCredential({ credential, env, port, subject: `identity ${identity}` });
|
|
7549
|
+
return resolution.ok ? resolution : { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
7550
|
+
}
|
|
7551
|
+
function pickFromPool(decided, params, parsedArgv) {
|
|
7552
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7553
|
+
if (decided.pool === void 0) {
|
|
7554
|
+
return { identityDecision: decided };
|
|
7555
|
+
}
|
|
7556
|
+
const farmRuntime = params.farm;
|
|
7557
|
+
if (farmRuntime === void 0) {
|
|
7558
|
+
log.error(`agent-shim: pool "${decided.pool}" was selected, but this launcher has no farm wired to read usage and pick a member with.`);
|
|
7559
|
+
return proc.exit(1);
|
|
7560
|
+
}
|
|
7561
|
+
const resolution = resolvePoolLaunch({
|
|
7562
|
+
poolName: decided.pool,
|
|
7563
|
+
selectedVia: decided.source,
|
|
7564
|
+
pools: params.pools,
|
|
7565
|
+
paths,
|
|
7566
|
+
fs: fs12,
|
|
7567
|
+
usageFs: farmRuntime.fs,
|
|
7568
|
+
cwd: farmRuntime.cwd,
|
|
7569
|
+
now: farmRuntime.now,
|
|
7570
|
+
sleep: farmRuntime.lock.sleep,
|
|
7571
|
+
passthrough: parsedArgv.rest,
|
|
7572
|
+
wait: parsedArgv.wait === true,
|
|
7573
|
+
log
|
|
7574
|
+
});
|
|
7575
|
+
if (!resolution.ok) {
|
|
7576
|
+
log.error(resolution.message);
|
|
7577
|
+
return proc.exit(1);
|
|
7578
|
+
}
|
|
7579
|
+
return { identityDecision: { ...decided, name: resolution.identity }, poolExplanation: resolution.explanation };
|
|
7580
|
+
}
|
|
7581
|
+
function prepareLaunch(params) {
|
|
7582
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7583
|
+
const { env, argv } = proc;
|
|
7584
|
+
const parsedArgv = parseLauncherArgv(argv);
|
|
7585
|
+
const cliOverride = buildCliOverride({
|
|
7586
|
+
env,
|
|
7587
|
+
categoryFlags: parsedArgv.categoryFlags,
|
|
7588
|
+
shareFlags: parsedArgv.shareFlags,
|
|
7589
|
+
hideFlags: parsedArgv.hideFlags
|
|
7590
|
+
});
|
|
7591
|
+
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
7592
|
+
const decidedIdentity = decideIdentity({
|
|
7593
|
+
env,
|
|
7594
|
+
argv0Identity: parsedArgv.identity,
|
|
7595
|
+
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
7596
|
+
readActiveIdentityFile: () => {
|
|
7597
|
+
const raw = fs12.readFileUtf8(paths.activeIdentityFile);
|
|
7598
|
+
if (raw === void 0) {
|
|
7599
|
+
return void 0;
|
|
7600
|
+
}
|
|
7601
|
+
const trimmed = raw.trim();
|
|
7602
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
7603
|
+
}
|
|
7604
|
+
});
|
|
7605
|
+
const { identityDecision, poolExplanation } = pickFromPool(decidedIdentity, params, parsedArgv);
|
|
7606
|
+
const farmIdentity = configDirEscapeHatchApplies ? void 0 : identityDecision.name;
|
|
7607
|
+
const farm = farmIdentity === void 0 ? void 0 : params.farm;
|
|
7608
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7609
|
+
let recovery;
|
|
7610
|
+
try {
|
|
7611
|
+
recovery = recoverFarm({
|
|
7612
|
+
fs: farm.fs,
|
|
7613
|
+
identitiesDir: paths.identitiesDir,
|
|
7614
|
+
identity: farmIdentity,
|
|
7615
|
+
now: farm.now,
|
|
7616
|
+
lock: farm.lock,
|
|
7617
|
+
classification: farm.classification
|
|
7618
|
+
});
|
|
7619
|
+
} catch (error) {
|
|
7620
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7621
|
+
log.error(error.message);
|
|
7622
|
+
proc.exit(1);
|
|
7623
|
+
}
|
|
7624
|
+
throw error;
|
|
7625
|
+
}
|
|
7626
|
+
for (const diagnostic of recoveryDiagnostics(recovery, farmIdentity)) {
|
|
7627
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
let loadedIdentity;
|
|
7631
|
+
if (identityDecision.name !== void 0) {
|
|
7632
|
+
loadedIdentity = loadIdentity(paths.identitiesDir, identityDecision.name, fs12);
|
|
7633
|
+
if (loadedIdentity === void 0) {
|
|
7634
|
+
log.error(
|
|
7635
|
+
`agent-shim: no identity named "${identityDecision.name}" (selected via ${identityDecision.source}). Run \`agent-shim identity add ${identityDecision.name}\` first.`
|
|
7636
|
+
);
|
|
7637
|
+
proc.exit(1);
|
|
7638
|
+
}
|
|
7639
|
+
}
|
|
7640
|
+
const configProfileDecision = decideConfigProfile({
|
|
7641
|
+
env,
|
|
7642
|
+
cliFlagConfigProfile: params.cliFlagConfigProfile ?? parsedArgv.configProfile,
|
|
7643
|
+
directoryRuleConfigProfile: params.directoryRuleConfigProfile,
|
|
7644
|
+
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
7645
|
+
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
7646
|
+
});
|
|
7647
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(import_node_path25.default.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
7648
|
+
log.error(
|
|
7649
|
+
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
7650
|
+
);
|
|
7651
|
+
proc.exit(1);
|
|
7652
|
+
}
|
|
7653
|
+
const farmContext = farm !== void 0 && farmIdentity !== void 0 ? { farm, identity: farmIdentity, cascade: farm.loadCascade(configProfileDecision.name, cliOverride) } : void 0;
|
|
7654
|
+
const provider = parsedArgv.provider === false ? void 0 : resolveProvider({
|
|
7655
|
+
paths,
|
|
7656
|
+
port: fs12,
|
|
7657
|
+
env,
|
|
7658
|
+
...params.credentials === void 0 ? {} : { credentials: params.credentials },
|
|
7659
|
+
...parsedArgv.provider === void 0 ? {} : { cliProvider: parsedArgv.provider },
|
|
7660
|
+
...farmContext === void 0 ? {} : { cascade: farmContext.cascade }
|
|
7661
|
+
});
|
|
7662
|
+
if (provider !== void 0 && !provider.ok) {
|
|
7663
|
+
log.error(provider.message);
|
|
7664
|
+
proc.exit(provider.status);
|
|
7665
|
+
}
|
|
7666
|
+
const resolvedProvider = provider?.ok === true ? provider.provider : void 0;
|
|
7667
|
+
for (const warning of provider?.ok === true ? provider.warnings : []) {
|
|
7668
|
+
log.warn(warning);
|
|
7669
|
+
}
|
|
7670
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7671
|
+
try {
|
|
7672
|
+
const snapshot = readUsageSnapshot(farm.fs, paths.usageSnapshotsDir, farmIdentity);
|
|
7673
|
+
for (const warning of quotaWarnings(snapshot, resolvedProvider?.name ?? ANTHROPIC_PROVIDER, farm.now())) {
|
|
7674
|
+
log.warn(warning);
|
|
7675
|
+
}
|
|
7676
|
+
} catch (error) {
|
|
7677
|
+
if (!(error instanceof UsageSnapshotError)) {
|
|
7678
|
+
throw error;
|
|
7679
|
+
}
|
|
7680
|
+
log.warn(`agent-shim: ${error.message}`);
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
const identityCredentialBlock = loadedIdentity?.config.credential;
|
|
7684
|
+
const identityResolution = resolvedProvider === void 0 && farmIdentity !== void 0 && identityCredentialBlock !== void 0 ? resolveIdentityCredential(farmIdentity, identityCredentialBlock, env, params.credentials) : void 0;
|
|
7685
|
+
if (identityResolution !== void 0 && !identityResolution.ok) {
|
|
7686
|
+
log.error(identityResolution.message);
|
|
7687
|
+
proc.exit(identityResolution.status);
|
|
7688
|
+
}
|
|
7689
|
+
const identityCredential = identityResolution?.ok === true ? identityResolution.credential : void 0;
|
|
7690
|
+
for (const warning of identityCredential?.warnings ?? []) {
|
|
7691
|
+
log.warn(warning);
|
|
7692
|
+
}
|
|
7693
|
+
const guardResult = evaluateAmbientCredentialGuard({
|
|
7694
|
+
env,
|
|
7695
|
+
allowAmbientCredential: loadedIdentity?.config.allowAmbientCredential ?? false,
|
|
7696
|
+
allowAmbientCredentialOverride: parseEnvBool("AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL", env.AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL) === true,
|
|
7697
|
+
identityName: identityDecision.name,
|
|
7698
|
+
providerSelected: resolvedProvider !== void 0,
|
|
7699
|
+
...identityCredential === void 0 ? {} : { injectedCredential: { variable: CREDENTIAL_TARGET_VARS[identityCredential.target], token: identityCredential.token } }
|
|
7700
|
+
});
|
|
7701
|
+
if (!guardResult.ok) {
|
|
7702
|
+
log.error(guardResult.message);
|
|
7703
|
+
proc.exit(1);
|
|
7704
|
+
}
|
|
7705
|
+
log.info(
|
|
7706
|
+
`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)}`)
|
|
7707
|
+
);
|
|
7708
|
+
let cascadeLaunch;
|
|
7709
|
+
if (farmContext !== void 0) {
|
|
7710
|
+
const { farm: resyncFarmRuntime, identity: resyncIdentity, cascade } = farmContext;
|
|
7711
|
+
let result;
|
|
7712
|
+
try {
|
|
7713
|
+
result = resyncFarm({
|
|
7714
|
+
fs: resyncFarmRuntime.fs,
|
|
7715
|
+
identitiesDir: paths.identitiesDir,
|
|
7716
|
+
identity: resyncIdentity,
|
|
7717
|
+
...configProfileDecision.name === void 0 ? {} : { configProfile: configProfileDecision.name },
|
|
7718
|
+
claudeHome: resyncFarmRuntime.claudeHome,
|
|
7719
|
+
home: resyncFarmRuntime.home,
|
|
7720
|
+
cwd: resyncFarmRuntime.cwd,
|
|
7721
|
+
env,
|
|
7722
|
+
...resyncFarmRuntime.branch === void 0 ? {} : { branch: resyncFarmRuntime.branch },
|
|
7723
|
+
...resyncFarmRuntime.branchDetached === void 0 ? {} : { branchDetached: resyncFarmRuntime.branchDetached },
|
|
7724
|
+
cascade,
|
|
7725
|
+
classification: resyncFarmRuntime.classification,
|
|
7726
|
+
now: resyncFarmRuntime.now,
|
|
7727
|
+
uniqueSuffix: resyncFarmRuntime.uniqueSuffix,
|
|
7728
|
+
lock: resyncFarmRuntime.lock
|
|
7729
|
+
});
|
|
7730
|
+
} catch (error) {
|
|
7731
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7732
|
+
log.error(error.message);
|
|
7733
|
+
proc.exit(1);
|
|
7734
|
+
}
|
|
7735
|
+
throw error;
|
|
7736
|
+
}
|
|
7737
|
+
for (const diagnostic of result.diagnostics) {
|
|
7738
|
+
if (diagnostic.severity === "error") {
|
|
7739
|
+
log.error(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7740
|
+
} else if (diagnostic.severity === "warning") {
|
|
7741
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7742
|
+
}
|
|
7743
|
+
}
|
|
7744
|
+
log.info(
|
|
7745
|
+
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}`})`
|
|
7746
|
+
);
|
|
7747
|
+
cascadeLaunch = result.resolved.flattened.launch;
|
|
7748
|
+
} else if (params.farm !== void 0) {
|
|
7749
|
+
cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
|
|
7750
|
+
}
|
|
7751
|
+
const discovered = params.resolveClaudeBinary();
|
|
7752
|
+
const resolvedFlags = resolveLaunchFlags({
|
|
7753
|
+
env,
|
|
7754
|
+
// A pool pick reads the usage the front door records, so a pool launch records its own unless something says otherwise.
|
|
7755
|
+
...poolExplanation === void 0 ? {} : { trackUsageDefault: true },
|
|
7756
|
+
...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch },
|
|
7757
|
+
flags: {
|
|
7758
|
+
...parsedArgv.skipPermissions === void 0 ? {} : { skipPermissions: parsedArgv.skipPermissions },
|
|
7759
|
+
...parsedArgv.remoteControl === void 0 ? {} : { remoteControl: parsedArgv.remoteControl },
|
|
7760
|
+
...parsedArgv.headroom === void 0 ? {} : { headroom: parsedArgv.headroom },
|
|
7761
|
+
...parsedArgv.trackUsage === void 0 ? {} : { trackUsage: parsedArgv.trackUsage }
|
|
7762
|
+
}
|
|
7763
|
+
});
|
|
7764
|
+
const frontDoorPort = params.frontdoor;
|
|
7765
|
+
const frontDoorEngaged = resolvedProvider !== void 0 || resolvedFlags.headroom || resolvedFlags.trackUsage;
|
|
7766
|
+
if (frontDoorEngaged && frontDoorPort === void 0) {
|
|
7767
|
+
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.");
|
|
7768
|
+
proc.exit(1);
|
|
7769
|
+
}
|
|
7770
|
+
const frontDoor = frontDoorEngaged && frontDoorPort !== void 0 ? frontDoorPort.ensure(env.NODE_EXTRA_CA_CERTS) : void 0;
|
|
7771
|
+
if (frontDoor?.trustWarning !== void 0) {
|
|
7772
|
+
log.warn(frontDoor.trustWarning);
|
|
7773
|
+
}
|
|
7774
|
+
let routedProvider;
|
|
7775
|
+
if (resolvedProvider !== void 0 && frontDoor !== void 0) {
|
|
7776
|
+
routedProvider = { ...resolvedProvider, baseUrl: providerBaseUrl(frontDoor.port, resolvedProvider.name) };
|
|
7777
|
+
}
|
|
7778
|
+
let headroom;
|
|
7779
|
+
const headroomPort = params.headroom;
|
|
7780
|
+
if (resolvedFlags.headroom && headroomPort !== void 0) {
|
|
7781
|
+
headroom = headroomPort.ensure();
|
|
7782
|
+
const via = resolvedProvider === void 0 ? `OAuth via the door's CONNECT surface on 127.0.0.1:${String(frontDoor?.connectPort ?? 0)}` : `provider ${resolvedProvider.name}`;
|
|
7783
|
+
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})`);
|
|
7784
|
+
} else if (resolvedFlags.headroom) {
|
|
7785
|
+
log.error("agent-shim: headroom routing was requested but this launcher has no headroom port wired; refusing to launch without it.");
|
|
7786
|
+
proc.exit(1);
|
|
7787
|
+
} else if (frontDoor !== void 0) {
|
|
7788
|
+
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})`}`);
|
|
7789
|
+
}
|
|
7790
|
+
const frontDoorRelease = frontDoor === void 0 ? void 0 : frontDoorPort?.release;
|
|
7791
|
+
const finalArgv = buildArgv({
|
|
7792
|
+
toolFlags: buildFlagArgs(resolvedFlags),
|
|
7793
|
+
extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
|
|
7794
|
+
passthrough: parsedArgv.rest
|
|
7795
|
+
});
|
|
7796
|
+
const finalEnv = buildEnv({
|
|
7797
|
+
baseEnv: env,
|
|
7798
|
+
configDirEscapeHatch: configDirEscapeHatchApplies,
|
|
7799
|
+
resolvedIdentityName: identityDecision.name,
|
|
7800
|
+
identitiesDir: paths.identitiesDir,
|
|
7801
|
+
...routedProvider === void 0 ? {} : { provider: routedProvider },
|
|
7802
|
+
...identityCredential === void 0 ? {} : { identityCredential },
|
|
7803
|
+
...frontDoor === void 0 ? {} : { frontdoor: frontDoor },
|
|
7804
|
+
...headroom === void 0 ? {} : { headroom },
|
|
7805
|
+
sessionId: (0, import_node_crypto9.randomUUID)()
|
|
7806
|
+
});
|
|
7807
|
+
const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
|
|
7808
|
+
const release = () => {
|
|
7809
|
+
for (const registered of releases) {
|
|
7810
|
+
registered();
|
|
7811
|
+
}
|
|
7812
|
+
};
|
|
7813
|
+
return { bin: discovered.path, args: finalArgv, env: finalEnv, release };
|
|
7814
|
+
}
|
|
7815
|
+
|
|
7816
|
+
// src/launchWiring.ts
|
|
7817
|
+
var import_node_os5 = __toESM(require("node:os"), 1);
|
|
7818
|
+
var import_node_crypto11 = require("node:crypto");
|
|
7819
|
+
|
|
7820
|
+
// src/realCredentialCache.ts
|
|
7821
|
+
var import_node_child_process2 = require("node:child_process");
|
|
7822
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
7823
|
+
var import_node_path26 = __toESM(require("node:path"), 1);
|
|
7824
|
+
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7825
|
+
var CACHE_DIR_MODE = 448;
|
|
7826
|
+
var CACHE_FILE_MODE = 384;
|
|
7827
|
+
function cacheFileNameFor(owner) {
|
|
7828
|
+
return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
|
|
7829
|
+
}
|
|
7830
|
+
function parseEntry(raw) {
|
|
7831
|
+
let json;
|
|
7832
|
+
try {
|
|
7833
|
+
json = JSON.parse(raw);
|
|
7834
|
+
} catch {
|
|
7835
|
+
return void 0;
|
|
7836
|
+
}
|
|
7837
|
+
const parsed = CachedCredentialSchema.safeParse(json);
|
|
7838
|
+
return parsed.success ? parsed.data : void 0;
|
|
7839
|
+
}
|
|
7840
|
+
function requireKeychain() {
|
|
7841
|
+
if (process.platform !== "darwin") {
|
|
7842
|
+
throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
|
|
7843
|
+
}
|
|
7844
|
+
}
|
|
7845
|
+
function security(args, stdin) {
|
|
7846
|
+
const result = (0, import_node_child_process2.spawnSync)("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
|
|
7847
|
+
return { status: result.status, stdout: result.stdout };
|
|
7848
|
+
}
|
|
7849
|
+
function createRealCredentialCache(root) {
|
|
7850
|
+
const dir = import_node_path26.default.join(root, "credential-cache");
|
|
7851
|
+
const filePath = (owner) => import_node_path26.default.join(dir, cacheFileNameFor(owner));
|
|
7852
|
+
return {
|
|
7853
|
+
read(store, owner) {
|
|
7854
|
+
if (store === "file") {
|
|
7855
|
+
try {
|
|
7856
|
+
return parseEntry(import_node_fs9.default.readFileSync(filePath(owner), "utf8"));
|
|
7857
|
+
} catch {
|
|
7858
|
+
return void 0;
|
|
7859
|
+
}
|
|
7860
|
+
}
|
|
7861
|
+
requireKeychain();
|
|
7862
|
+
const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
|
|
7863
|
+
return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
|
|
7864
|
+
},
|
|
7865
|
+
write(store, owner, entry) {
|
|
7866
|
+
const serialised = JSON.stringify(entry);
|
|
7867
|
+
if (store === "file") {
|
|
7868
|
+
import_node_fs9.default.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
|
|
7869
|
+
import_node_fs9.default.chmodSync(dir, CACHE_DIR_MODE);
|
|
7870
|
+
const target = filePath(owner);
|
|
7871
|
+
const temp = `${target}.${String(process.pid)}.tmp`;
|
|
7872
|
+
import_node_fs9.default.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
|
|
7873
|
+
import_node_fs9.default.renameSync(temp, target);
|
|
7874
|
+
return;
|
|
7875
|
+
}
|
|
7876
|
+
requireKeychain();
|
|
7877
|
+
const encoded = Buffer.from(serialised, "utf8").toString("base64");
|
|
7878
|
+
const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
|
|
7879
|
+
`);
|
|
7880
|
+
if (result.status !== 0) {
|
|
7881
|
+
throw new Error(`could not write the keychain credential cache for ${owner}`);
|
|
7882
|
+
}
|
|
7883
|
+
},
|
|
7884
|
+
remove(store, owner) {
|
|
7885
|
+
if (store === "file") {
|
|
7886
|
+
import_node_fs9.default.rmSync(filePath(owner), { force: true });
|
|
7887
|
+
return;
|
|
7888
|
+
}
|
|
7889
|
+
requireKeychain();
|
|
7890
|
+
security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
|
|
7891
|
+
}
|
|
7892
|
+
};
|
|
7893
|
+
}
|
|
7894
|
+
function realCredentialCacheEnv(paths) {
|
|
7895
|
+
return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
|
|
7896
|
+
}
|
|
7897
|
+
|
|
7898
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7899
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
7900
|
+
|
|
7901
|
+
// src/frontdoor/probe.ts
|
|
7902
|
+
var import_node_worker_threads = require("node:worker_threads");
|
|
7903
|
+
var PROBE_TIMEOUT_MS = 2e3;
|
|
7904
|
+
var PROBE_WAIT_MARGIN_MS = 1e3;
|
|
7905
|
+
var REASON_BYTES = 1024;
|
|
7906
|
+
var HEADER_SLOTS = 2;
|
|
7907
|
+
var INT32_BYTES = 4;
|
|
7908
|
+
var VERDICT_PENDING = 0;
|
|
7909
|
+
var VERDICT_OK = 1;
|
|
7910
|
+
var VERDICT_FAILED = 2;
|
|
7911
|
+
var PROBE_SOURCE = `
|
|
7912
|
+
const { workerData } = require("node:worker_threads");
|
|
7913
|
+
const https = require("node:https");
|
|
7914
|
+
const { port, ca, timeoutMs, shared, ok, failed, headerSlots } = workerData;
|
|
7915
|
+
const header = new Int32Array(shared, 0, headerSlots);
|
|
7916
|
+
const reason = new Uint8Array(shared, headerSlots * 4);
|
|
7917
|
+
let done = false;
|
|
7918
|
+
const finish = (verdict, message) => {
|
|
7919
|
+
if (done) return;
|
|
7920
|
+
done = true;
|
|
7921
|
+
const bytes = Buffer.from(String(message), "utf8").subarray(0, reason.length);
|
|
7922
|
+
reason.set(bytes);
|
|
7923
|
+
Atomics.store(header, 1, bytes.length);
|
|
7924
|
+
Atomics.store(header, 0, verdict);
|
|
7925
|
+
Atomics.notify(header, 0);
|
|
7926
|
+
};
|
|
7927
|
+
const request = https.get({ host: "127.0.0.1", port, path: "/healthz", ca: [ca], agent: false }, (response) => {
|
|
7928
|
+
let body = "";
|
|
7929
|
+
response.setEncoding("utf8");
|
|
7930
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
7931
|
+
response.on("end", () => {
|
|
7932
|
+
if (response.statusCode === 200 && body === "ok") finish(ok, "");
|
|
7933
|
+
else finish(failed, "the listener answered its health probe with HTTP " + String(response.statusCode));
|
|
7934
|
+
});
|
|
7935
|
+
response.on("error", (error) => finish(failed, error.message));
|
|
7936
|
+
});
|
|
7937
|
+
// A hard deadline over the whole exchange, handshake included: a listener that accepts the connection and then says nothing must still fail the probe.
|
|
7938
|
+
setTimeout(() => { request.destroy(new Error("no answer within " + String(timeoutMs) + "ms")); }, timeoutMs);
|
|
7939
|
+
request.on("error", (error) => finish(failed, (error.code ? error.code + ": " : "") + error.message));
|
|
7940
|
+
`;
|
|
7941
|
+
function probeFrontDoorSync(port, caPem) {
|
|
7942
|
+
const shared = new SharedArrayBuffer(HEADER_SLOTS * INT32_BYTES + REASON_BYTES);
|
|
7943
|
+
const header = new Int32Array(shared, 0, HEADER_SLOTS);
|
|
7944
|
+
const worker = new import_node_worker_threads.Worker(PROBE_SOURCE, {
|
|
7945
|
+
eval: true,
|
|
7946
|
+
workerData: { port, ca: caPem, timeoutMs: PROBE_TIMEOUT_MS, shared, ok: VERDICT_OK, failed: VERDICT_FAILED, headerSlots: HEADER_SLOTS }
|
|
7947
|
+
});
|
|
7948
|
+
worker.unref();
|
|
7949
|
+
try {
|
|
7950
|
+
const waited = Atomics.wait(header, 0, VERDICT_PENDING, PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS);
|
|
7951
|
+
const verdict = Atomics.load(header, 0);
|
|
7952
|
+
if (waited === "timed-out" || verdict === VERDICT_PENDING) {
|
|
7953
|
+
return { ok: false, reason: `the probe did not finish within ${String(PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS)}ms` };
|
|
7954
|
+
}
|
|
7955
|
+
if (verdict === VERDICT_OK) {
|
|
7956
|
+
return { ok: true };
|
|
7957
|
+
}
|
|
7958
|
+
const length = Atomics.load(header, 1);
|
|
7959
|
+
return { ok: false, reason: Buffer.from(new Uint8Array(shared, HEADER_SLOTS * INT32_BYTES, length)).toString("utf8") };
|
|
7960
|
+
} finally {
|
|
7961
|
+
void worker.terminate();
|
|
7962
|
+
}
|
|
7963
|
+
}
|
|
7964
|
+
|
|
7965
|
+
// src/frontdoor/trust.ts
|
|
7966
|
+
var import_node_crypto10 = require("node:crypto");
|
|
7967
|
+
var import_node_path27 = __toESM(require("node:path"), 1);
|
|
7968
|
+
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
7969
|
+
function resolveTrustBundle(params) {
|
|
7970
|
+
const { caCertFile, bundlesDir, inherited, fs: fs12 } = params;
|
|
7971
|
+
if (inherited === void 0 || inherited === "" || import_node_path27.default.resolve(inherited) === import_node_path27.default.resolve(caCertFile)) {
|
|
7972
|
+
return { path: caCertFile };
|
|
7973
|
+
}
|
|
7974
|
+
const caPem = fs12.readFileUtf8(caCertFile);
|
|
7975
|
+
let inheritedPem;
|
|
7976
|
+
try {
|
|
7977
|
+
inheritedPem = fs12.readFileUtf8(inherited);
|
|
7978
|
+
} catch (error) {
|
|
7979
|
+
return {
|
|
7980
|
+
path: caCertFile,
|
|
7981
|
+
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`
|
|
7982
|
+
};
|
|
7983
|
+
}
|
|
7984
|
+
if (inheritedPem.includes(caPem.trim())) {
|
|
7985
|
+
return { path: inherited };
|
|
7986
|
+
}
|
|
7987
|
+
const combined = `${inheritedPem.trimEnd()}
|
|
7988
|
+
${caPem}`;
|
|
7989
|
+
const bundle = import_node_path27.default.join(bundlesDir, `${(0, import_node_crypto10.createHash)("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
|
|
7990
|
+
if (!fs12.exists(bundle)) {
|
|
7991
|
+
fs12.mkdirp(bundlesDir);
|
|
7992
|
+
fs12.writeFileAtomic(bundle, combined);
|
|
7993
|
+
}
|
|
7994
|
+
return { path: bundle };
|
|
7995
|
+
}
|
|
7996
|
+
|
|
7997
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7998
|
+
var realTrustBundleFs = {
|
|
7999
|
+
readFileUtf8: (file) => import_node_fs10.default.readFileSync(file, "utf8"),
|
|
8000
|
+
exists: (file) => import_node_fs10.default.existsSync(file),
|
|
8001
|
+
mkdirp: (dir) => {
|
|
8002
|
+
import_node_fs10.default.mkdirSync(dir, { recursive: true });
|
|
8003
|
+
},
|
|
8004
|
+
writeFileAtomic: (file, contents) => {
|
|
8005
|
+
realFarmFs.writeFilePrivate(file, contents);
|
|
8006
|
+
}
|
|
8007
|
+
};
|
|
8008
|
+
function realFrontDoorPort(paths, spawnDaemon) {
|
|
8009
|
+
return {
|
|
8010
|
+
ensure: (inheritedExtraCaCerts) => {
|
|
8011
|
+
const up = ensureFrontDoor({
|
|
8012
|
+
paths,
|
|
8013
|
+
launcherPid: process.pid,
|
|
8014
|
+
ports: {
|
|
8015
|
+
fs: realFarmFs,
|
|
8016
|
+
isRunning: realIsProcessRunning,
|
|
8017
|
+
now: () => Date.now(),
|
|
8018
|
+
sleep: realSleepSync,
|
|
8019
|
+
spawnSupervisor: (layout) => spawnDaemon(layout, "__frontdoor-supervisor", layout.frontdoorLogPath),
|
|
8020
|
+
stopSupervisor: (pid) => {
|
|
8021
|
+
process.kill(pid, "SIGTERM");
|
|
8022
|
+
},
|
|
8023
|
+
// 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.
|
|
8024
|
+
verifyListener: (port) => probeFrontDoorSync(port, import_node_fs10.default.readFileSync(paths.frontdoorCaCertFile, "utf8"))
|
|
8025
|
+
}
|
|
8026
|
+
});
|
|
8027
|
+
const trust = resolveTrustBundle({ caCertFile: paths.frontdoorCaCertFile, bundlesDir: paths.frontdoorCaBundlesDir, inherited: inheritedExtraCaCerts, fs: realTrustBundleFs });
|
|
8028
|
+
return {
|
|
8029
|
+
port: up.port,
|
|
8030
|
+
connectPort: up.connectPort,
|
|
8031
|
+
trustBundlePath: trust.path,
|
|
8032
|
+
...trust.warning === void 0 ? {} : { trustWarning: trust.warning },
|
|
8033
|
+
sessionToken: up.token
|
|
8034
|
+
};
|
|
8035
|
+
},
|
|
8036
|
+
release: () => {
|
|
8037
|
+
removeFrontDoorSession(realFarmFs, paths.frontdoorSessionsDir, process.pid);
|
|
8038
|
+
}
|
|
8039
|
+
};
|
|
8040
|
+
}
|
|
8041
|
+
|
|
8042
|
+
// src/launchWiring.ts
|
|
8043
|
+
function buildFarmRuntime(paths, cwd) {
|
|
8044
|
+
const home = import_node_os5.default.homedir();
|
|
8045
|
+
const read = cosmiconfigReader();
|
|
8046
|
+
const classification = loadClassification(paths);
|
|
8047
|
+
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
8048
|
+
const selections = readDirectorySelections(loaded);
|
|
8049
|
+
const git = resolveGitBranch(realRunPort, cwd);
|
|
8050
|
+
return {
|
|
8051
|
+
runtime: {
|
|
8052
|
+
fs: realFarmFs,
|
|
8053
|
+
claudeHome: resolveClaudeHome(),
|
|
8054
|
+
home,
|
|
8055
|
+
cwd,
|
|
8056
|
+
...git.branch === void 0 ? {} : { branch: git.branch },
|
|
8057
|
+
...git.branchDetached === void 0 ? {} : { branchDetached: git.branchDetached },
|
|
8058
|
+
classification,
|
|
8059
|
+
loadCascade: (baseConfigProfile, cliOverride) => loadCascadeInput({
|
|
8060
|
+
paths,
|
|
8061
|
+
home,
|
|
8062
|
+
cwd,
|
|
8063
|
+
read,
|
|
8064
|
+
...baseConfigProfile === void 0 ? {} : { baseConfigProfile },
|
|
8065
|
+
...cliOverride === void 0 ? {} : { cliOverride }
|
|
8066
|
+
}).input,
|
|
8067
|
+
now: () => Date.now(),
|
|
8068
|
+
uniqueSuffix: `${String(process.pid)}.${(0, import_node_crypto11.randomUUID)()}`,
|
|
8069
|
+
// 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.
|
|
8070
|
+
lock: { pid: process.pid, isRunning: realIsProcessRunning, sleep: realSleepSync }
|
|
8071
|
+
},
|
|
8072
|
+
...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
|
|
8073
|
+
...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
|
|
8074
|
+
...loaded.globalConfig?.defaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: loaded.globalConfig.defaultConfigProfile },
|
|
8075
|
+
...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
|
|
8076
|
+
};
|
|
8077
|
+
}
|
|
8078
|
+
function realPrepareLaunchParams(paths, options) {
|
|
8079
|
+
const { farm } = options;
|
|
8080
|
+
return {
|
|
8081
|
+
paths,
|
|
8082
|
+
fs: realFsPort,
|
|
8083
|
+
proc: options.proc,
|
|
8084
|
+
log: options.log,
|
|
8085
|
+
resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
|
|
8086
|
+
farm: farm.runtime,
|
|
8087
|
+
headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
|
|
8088
|
+
frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
|
|
8089
|
+
credentials: { ...realCredentialPort, cache: realCredentialCacheEnv(paths) },
|
|
8090
|
+
...farm.directoryIdentity === void 0 ? {} : { directoryPinnedIdentity: farm.directoryIdentity },
|
|
8091
|
+
...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
|
|
8092
|
+
...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
|
|
8093
|
+
...farm.pools === void 0 ? {} : { pools: farm.pools },
|
|
8094
|
+
...options.allowMissingConfigProfile === void 0 ? {} : { allowMissingConfigProfile: options.allowMissingConfigProfile }
|
|
8095
|
+
};
|
|
8096
|
+
}
|
|
8097
|
+
var LaunchRefusedError = class extends CliError {
|
|
8098
|
+
exitCode;
|
|
8099
|
+
constructor(message, exitCode) {
|
|
8100
|
+
super(message);
|
|
8101
|
+
this.name = "LaunchRefusedError";
|
|
8102
|
+
this.exitCode = exitCode;
|
|
8103
|
+
}
|
|
8104
|
+
};
|
|
8105
|
+
function prepareClaudeLaunch(options) {
|
|
8106
|
+
const paths = options.paths ?? resolveLayoutPaths();
|
|
8107
|
+
const refusals = [];
|
|
8108
|
+
const log = {
|
|
8109
|
+
info: () => void 0,
|
|
8110
|
+
warn: () => void 0,
|
|
8111
|
+
error: (message) => {
|
|
8112
|
+
refusals.push(message);
|
|
8113
|
+
}
|
|
8114
|
+
};
|
|
8115
|
+
const proc = {
|
|
8116
|
+
env: options.env,
|
|
8117
|
+
argv: options.argv,
|
|
8118
|
+
exit: (code) => {
|
|
8119
|
+
throw new LaunchRefusedError(refusals.join("\n") || `the launch was refused (exit ${String(code)})`, code);
|
|
8120
|
+
}
|
|
8121
|
+
};
|
|
8122
|
+
const spawnDaemon = options.agentShim === void 0 ? () => {
|
|
8123
|
+
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);
|
|
8124
|
+
} : spawnDaemonThrough(options.agentShim);
|
|
8125
|
+
return prepareLaunch(realPrepareLaunchParams(paths, { proc, log, spawnDaemon, farm: buildFarmRuntime(paths, options.cwd) }));
|
|
8126
|
+
}
|
|
6799
8127
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6800
8128
|
0 && (module.exports = {
|
|
6801
8129
|
AMBIENT_CREDENTIAL_VARS,
|
|
@@ -6828,6 +8156,7 @@ function collectDoctorReport(params) {
|
|
|
6828
8156
|
InvalidCategoryNameError,
|
|
6829
8157
|
InvalidIdentityNameError,
|
|
6830
8158
|
InvalidProviderNameError,
|
|
8159
|
+
LaunchRefusedError,
|
|
6831
8160
|
LegacyProviderFileError,
|
|
6832
8161
|
PoolNameSchema,
|
|
6833
8162
|
PoolNotFoundError,
|
|
@@ -6878,6 +8207,8 @@ function collectDoctorReport(params) {
|
|
|
6878
8207
|
listUsageSnapshots,
|
|
6879
8208
|
mintLeaf,
|
|
6880
8209
|
parseConnectTarget,
|
|
8210
|
+
prepareClaudeLaunch,
|
|
8211
|
+
prepareLaunch,
|
|
6881
8212
|
profileExists,
|
|
6882
8213
|
providerExists,
|
|
6883
8214
|
readActiveIdentity,
|