claude-use 7.12.1 → 7.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +486 -426
- package/dist/index.cjs +1510 -143
- package/dist/index.mjs +1507 -143
- package/dist/types/cli/pairs.d.ts +19 -0
- package/dist/types/frontdoor/realFrontDoorPort.d.ts +5 -0
- package/dist/types/frontdoor/trust.d.ts +30 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/launchWiring.d.ts +49 -0
- package/dist/types/launcher/argv.d.ts +42 -0
- package/dist/types/launcher/cliOverride.d.ts +38 -0
- package/dist/types/launcher/extraFlags.d.ts +6 -0
- package/dist/types/launcher/farm.d.ts +3 -0
- package/dist/types/launcher/spawn.d.ts +20 -0
- package/dist/types/launcher.d.ts +98 -0
- package/dist/types/realCredentialCache.d.ts +11 -0
- package/dist/types/realPorts.d.ts +8 -1
- package/dist/types/resolve/pipeline.d.ts +3 -0
- package/dist/types/resolve/walkPolicy.d.ts +15 -0
- package/package.json +1 -1
package/dist/index.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,
|
|
@@ -1386,6 +1389,37 @@ function planFarm(params) {
|
|
|
1386
1389
|
return { entries, materialised, links };
|
|
1387
1390
|
}
|
|
1388
1391
|
|
|
1392
|
+
// src/resolve/walkPolicy.ts
|
|
1393
|
+
var ENTER_EVERYTHING = () => true;
|
|
1394
|
+
function readsSubtreeAggregates(rule) {
|
|
1395
|
+
const when = rule.when;
|
|
1396
|
+
return when !== void 0 && (when.newerThan !== void 0 || when.olderThan !== void 0 || when.maxSizeBytes !== void 0);
|
|
1397
|
+
}
|
|
1398
|
+
function hasUnboundedDepth(pattern) {
|
|
1399
|
+
return pattern.includes("**") || /[{(]/.test(pattern);
|
|
1400
|
+
}
|
|
1401
|
+
function couldMatchBeneath(rule, dirRel) {
|
|
1402
|
+
if (rule.matches(dirRel)) {
|
|
1403
|
+
return rule.when !== void 0;
|
|
1404
|
+
}
|
|
1405
|
+
if (rule.isExact) {
|
|
1406
|
+
return rule.canonicalPattern.startsWith(`${dirRel}/`);
|
|
1407
|
+
}
|
|
1408
|
+
const depth = dirRel.split("/").length;
|
|
1409
|
+
if (!hasUnboundedDepth(rule.canonicalPattern) && rule.segmentCount <= depth) {
|
|
1410
|
+
return false;
|
|
1411
|
+
}
|
|
1412
|
+
const prefix = rule.literalPrefix;
|
|
1413
|
+
return prefix === "" || prefix.startsWith(`${dirRel}/`) || dirRel.startsWith(prefix);
|
|
1414
|
+
}
|
|
1415
|
+
function descendPolicy(rules) {
|
|
1416
|
+
const all = [...rules];
|
|
1417
|
+
if (all.some(readsSubtreeAggregates)) {
|
|
1418
|
+
return ENTER_EVERYTHING;
|
|
1419
|
+
}
|
|
1420
|
+
return (dirRel) => all.some((rule) => couldMatchBeneath(rule, dirRel));
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1389
1423
|
// src/resolve/walk.ts
|
|
1390
1424
|
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
1391
1425
|
|
|
@@ -1564,6 +1598,9 @@ function topLevelNames(facts) {
|
|
|
1564
1598
|
}
|
|
1565
1599
|
return [...names].sort();
|
|
1566
1600
|
}
|
|
1601
|
+
function descendPolicyFor(cascade, home) {
|
|
1602
|
+
return descendPolicy(flattenLayers(assembleCascade(cascade).layers, { home }).rules.values());
|
|
1603
|
+
}
|
|
1567
1604
|
function resolveDecisions(input) {
|
|
1568
1605
|
const assembled = assembleCascade(input.cascade);
|
|
1569
1606
|
const flattened = flattenLayers(assembled.layers, { home: input.facts.home });
|
|
@@ -1789,8 +1826,8 @@ var FarmManifestSchema = import_zod2.z.strictObject({
|
|
|
1789
1826
|
materialised: import_zod2.z.array(import_zod2.z.string()),
|
|
1790
1827
|
links: import_zod2.z.array(import_zod2.z.strictObject({ rel: import_zod2.z.string(), target: import_zod2.z.string() }))
|
|
1791
1828
|
});
|
|
1792
|
-
function readFarmManifest(
|
|
1793
|
-
const raw =
|
|
1829
|
+
function readFarmManifest(fs12, farmRoot) {
|
|
1830
|
+
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
1831
|
if (raw === void 0) {
|
|
1795
1832
|
return void 0;
|
|
1796
1833
|
}
|
|
@@ -1814,7 +1851,7 @@ function buildEntryFacts(params) {
|
|
|
1814
1851
|
const isDirectory = stat.kind === "dir";
|
|
1815
1852
|
let latestMtimeMs = stat.mtimeMs;
|
|
1816
1853
|
let totalSizeBytes = stat.sizeBytes;
|
|
1817
|
-
if (isDirectory) {
|
|
1854
|
+
if (isDirectory && params.descend(rel)) {
|
|
1818
1855
|
for (const name of [...params.fs.readdir(absolute)].sort()) {
|
|
1819
1856
|
const child = visit(`${rel}/${name}`);
|
|
1820
1857
|
if (child !== void 0) {
|
|
@@ -1848,9 +1885,9 @@ function buildEntryFacts(params) {
|
|
|
1848
1885
|
entries
|
|
1849
1886
|
};
|
|
1850
1887
|
}
|
|
1851
|
-
function listSubtree(
|
|
1888
|
+
function listSubtree(fs12, root, rel) {
|
|
1852
1889
|
const absolute = import_node_path6.default.join(root, rel);
|
|
1853
|
-
const stat =
|
|
1890
|
+
const stat = fs12.lstat(absolute);
|
|
1854
1891
|
if (stat === void 0) {
|
|
1855
1892
|
return [];
|
|
1856
1893
|
}
|
|
@@ -1858,10 +1895,10 @@ function listSubtree(fs10, root, rel) {
|
|
|
1858
1895
|
return [{ rel, kind: "symlink" }];
|
|
1859
1896
|
}
|
|
1860
1897
|
if (stat.kind === "dir") {
|
|
1861
|
-
const children = [...
|
|
1898
|
+
const children = [...fs12.readdir(absolute)].sort().flatMap((name) => listSubtree(fs12, root, `${rel}/${name}`));
|
|
1862
1899
|
return [{ rel, kind: "dir" }, ...children];
|
|
1863
1900
|
}
|
|
1864
|
-
const contentHash =
|
|
1901
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1865
1902
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1866
1903
|
}
|
|
1867
1904
|
function topLevelScopeRoots(roots) {
|
|
@@ -1875,25 +1912,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1875
1912
|
}
|
|
1876
1913
|
return kept;
|
|
1877
1914
|
}
|
|
1878
|
-
function reconciliationScope(
|
|
1915
|
+
function reconciliationScope(fs12, farmRoot, manifest) {
|
|
1879
1916
|
if (manifest !== void 0) {
|
|
1880
1917
|
return topLevelScopeRoots(manifest.materialised);
|
|
1881
1918
|
}
|
|
1882
|
-
return [...
|
|
1919
|
+
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
1920
|
}
|
|
1884
|
-
function collectFarmListing(
|
|
1885
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1921
|
+
function collectFarmListing(fs12, farmRoot, scopeRoots) {
|
|
1922
|
+
return scopeRoots.flatMap((root) => listSubtree(fs12, farmRoot, root));
|
|
1886
1923
|
}
|
|
1887
|
-
function collectCanonicalCounterparts(
|
|
1924
|
+
function collectCanonicalCounterparts(fs12, claudeHome, farmListing) {
|
|
1888
1925
|
const out = [];
|
|
1889
1926
|
for (const entry of farmListing) {
|
|
1890
1927
|
const absolute = import_node_path6.default.join(claudeHome, entry.rel);
|
|
1891
|
-
const stat =
|
|
1928
|
+
const stat = fs12.lstat(absolute);
|
|
1892
1929
|
if (stat === void 0) {
|
|
1893
1930
|
continue;
|
|
1894
1931
|
}
|
|
1895
1932
|
if (stat.kind === "file") {
|
|
1896
|
-
const contentHash =
|
|
1933
|
+
const contentHash = fs12.hashFile(absolute);
|
|
1897
1934
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1898
1935
|
continue;
|
|
1899
1936
|
}
|
|
@@ -2161,7 +2198,8 @@ function resyncFarm(params) {
|
|
|
2161
2198
|
nowMs,
|
|
2162
2199
|
env: params.env,
|
|
2163
2200
|
...params.branch === void 0 ? {} : { branch: params.branch },
|
|
2164
|
-
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached }
|
|
2201
|
+
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached },
|
|
2202
|
+
descend: descendPolicyFor(params.cascade, params.home)
|
|
2165
2203
|
});
|
|
2166
2204
|
const resolved = resolveDecisions({ facts, cascade: params.cascade, classification: params.classification });
|
|
2167
2205
|
const manifest = {
|
|
@@ -2577,8 +2615,12 @@ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
|
|
|
2577
2615
|
}
|
|
2578
2616
|
|
|
2579
2617
|
// src/frontdoor/capability.ts
|
|
2618
|
+
var CONNECT_PROXY_USER = "agent-shim";
|
|
2580
2619
|
var CONNECT_PROXY_REALM = "agent-shim front door";
|
|
2581
2620
|
var BASIC_SCHEME = "basic";
|
|
2621
|
+
function connectProxyUrl(port, token) {
|
|
2622
|
+
return `http://${encodeURIComponent(CONNECT_PROXY_USER)}:${encodeURIComponent(token)}@127.0.0.1:${String(port)}`;
|
|
2623
|
+
}
|
|
2582
2624
|
function capabilityFromProxyAuthorization(value) {
|
|
2583
2625
|
const trimmed = value.trim();
|
|
2584
2626
|
const space = trimmed.indexOf(" ");
|
|
@@ -2595,7 +2637,15 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2595
2637
|
}
|
|
2596
2638
|
|
|
2597
2639
|
// src/frontdoor/route.ts
|
|
2640
|
+
var IDENTITY_HEADER = "x-agent-shim-identity";
|
|
2641
|
+
var SESSION_HEADER = "x-agent-shim-session";
|
|
2642
|
+
var HEADROOM_FLAG_HEADER = "x-agent-shim-headroom";
|
|
2643
|
+
var PROJECT_ID_HEADER = "x-headroom-project-id";
|
|
2598
2644
|
var AUTH_HEADER = "x-agent-shim-auth";
|
|
2645
|
+
var PROVIDER_PATH_PREFIX = "/providers/";
|
|
2646
|
+
function providerBaseUrl(port, provider) {
|
|
2647
|
+
return `https://127.0.0.1:${String(port)}${PROVIDER_PATH_PREFIX}${encodeURIComponent(provider)}`;
|
|
2648
|
+
}
|
|
2599
2649
|
|
|
2600
2650
|
// src/frontdoor/state.ts
|
|
2601
2651
|
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
@@ -2632,10 +2682,10 @@ var FrontDoorStateSchema = import_zod3.z.strictObject({
|
|
|
2632
2682
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2633
2683
|
lastError: import_zod3.z.string().optional()
|
|
2634
2684
|
});
|
|
2635
|
-
function readFrontDoorState(
|
|
2685
|
+
function readFrontDoorState(fs12, stateFile) {
|
|
2636
2686
|
let raw;
|
|
2637
2687
|
try {
|
|
2638
|
-
raw =
|
|
2688
|
+
raw = fs12.readFileUtf8(stateFile);
|
|
2639
2689
|
} catch {
|
|
2640
2690
|
return void 0;
|
|
2641
2691
|
}
|
|
@@ -2649,28 +2699,28 @@ function readFrontDoorState(fs10, stateFile) {
|
|
|
2649
2699
|
return void 0;
|
|
2650
2700
|
}
|
|
2651
2701
|
}
|
|
2652
|
-
function writeFrontDoorState(
|
|
2653
|
-
|
|
2654
|
-
|
|
2702
|
+
function writeFrontDoorState(fs12, stateFile, state) {
|
|
2703
|
+
fs12.mkdirp(import_node_path7.default.dirname(stateFile));
|
|
2704
|
+
fs12.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2655
2705
|
`);
|
|
2656
2706
|
}
|
|
2657
2707
|
function frontDoorOrigin(state) {
|
|
2658
2708
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2659
2709
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2660
2710
|
}
|
|
2661
|
-
function writeFrontDoorSession(
|
|
2662
|
-
|
|
2663
|
-
|
|
2711
|
+
function writeFrontDoorSession(fs12, sessionsDir, session) {
|
|
2712
|
+
fs12.mkdirPrivate(sessionsDir);
|
|
2713
|
+
fs12.writeFilePrivate(import_node_path7.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2664
2714
|
`);
|
|
2665
2715
|
}
|
|
2666
|
-
function listFrontDoorSessions(
|
|
2716
|
+
function listFrontDoorSessions(fs12, sessionsDir) {
|
|
2667
2717
|
const sessions = [];
|
|
2668
|
-
for (const name of
|
|
2718
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
2669
2719
|
if (!name.endsWith(".json")) {
|
|
2670
2720
|
continue;
|
|
2671
2721
|
}
|
|
2672
2722
|
try {
|
|
2673
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2723
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs12.readFileUtf8(import_node_path7.default.join(sessionsDir, name)) ?? ""));
|
|
2674
2724
|
if (parsed.success) {
|
|
2675
2725
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2676
2726
|
}
|
|
@@ -2680,14 +2730,14 @@ function listFrontDoorSessions(fs10, sessionsDir) {
|
|
|
2680
2730
|
}
|
|
2681
2731
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2682
2732
|
}
|
|
2683
|
-
function removeFrontDoorSession(
|
|
2684
|
-
|
|
2733
|
+
function removeFrontDoorSession(fs12, sessionsDir, pid) {
|
|
2734
|
+
fs12.removeRecursive(import_node_path7.default.join(sessionsDir, `${String(pid)}.json`));
|
|
2685
2735
|
}
|
|
2686
|
-
function pruneDeadFrontDoorSessions(
|
|
2736
|
+
function pruneDeadFrontDoorSessions(fs12, sessionsDir, isRunning) {
|
|
2687
2737
|
const removed = [];
|
|
2688
|
-
for (const session of listFrontDoorSessions(
|
|
2738
|
+
for (const session of listFrontDoorSessions(fs12, sessionsDir)) {
|
|
2689
2739
|
if (!isRunning(session.pid)) {
|
|
2690
|
-
removeFrontDoorSession(
|
|
2740
|
+
removeFrontDoorSession(fs12, sessionsDir, session.pid);
|
|
2691
2741
|
removed.push(session.pid);
|
|
2692
2742
|
}
|
|
2693
2743
|
}
|
|
@@ -2699,8 +2749,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2699
2749
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2700
2750
|
var MS_PER_MINUTE = 6e4;
|
|
2701
2751
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2702
|
-
const { fs:
|
|
2703
|
-
const previous = readFrontDoorState(
|
|
2752
|
+
const { fs: fs12, paths } = ports;
|
|
2753
|
+
const previous = readFrontDoorState(fs12, paths.frontdoorStateFile);
|
|
2704
2754
|
let lastPort = previous?.lastPort;
|
|
2705
2755
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2706
2756
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2710,12 +2760,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2710
2760
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2711
2761
|
});
|
|
2712
2762
|
const fail = (message) => {
|
|
2713
|
-
writeFrontDoorState(
|
|
2714
|
-
|
|
2763
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2764
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2715
2765
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2716
2766
|
return 1;
|
|
2717
2767
|
};
|
|
2718
|
-
writeFrontDoorState(
|
|
2768
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2719
2769
|
const previousSticky = lastPort;
|
|
2720
2770
|
let http2;
|
|
2721
2771
|
try {
|
|
@@ -2752,8 +2802,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2752
2802
|
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
2803
|
}
|
|
2754
2804
|
lastDirectPort = direct.port;
|
|
2755
|
-
writeFrontDoorState(
|
|
2756
|
-
|
|
2805
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
2806
|
+
fs12.removeRecursive(paths.frontdoorLockFile);
|
|
2757
2807
|
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
2808
|
let idleSince;
|
|
2759
2809
|
let ticks = 0;
|
|
@@ -2762,15 +2812,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2762
2812
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2763
2813
|
}
|
|
2764
2814
|
ticks += 1;
|
|
2765
|
-
pruneDeadFrontDoorSessions(
|
|
2766
|
-
if (listFrontDoorSessions(
|
|
2815
|
+
pruneDeadFrontDoorSessions(fs12, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2816
|
+
if (listFrontDoorSessions(fs12, paths.frontdoorSessionsDir).length === 0) {
|
|
2767
2817
|
idleSince ??= ports.now();
|
|
2768
2818
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2769
2819
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2770
2820
|
await direct.close();
|
|
2771
2821
|
await connect3.close();
|
|
2772
2822
|
await http2.close();
|
|
2773
|
-
writeFrontDoorState(
|
|
2823
|
+
writeFrontDoorState(fs12, paths.frontdoorStateFile, sticky());
|
|
2774
2824
|
return 0;
|
|
2775
2825
|
}
|
|
2776
2826
|
} else {
|
|
@@ -2825,8 +2875,8 @@ function parseConnectTarget(authority) {
|
|
|
2825
2875
|
function isInterceptedHost(host, interceptHosts) {
|
|
2826
2876
|
return interceptHosts.includes(host);
|
|
2827
2877
|
}
|
|
2828
|
-
function servedByPipeline(
|
|
2829
|
-
return
|
|
2878
|
+
function servedByPipeline(path28) {
|
|
2879
|
+
return path28?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2830
2880
|
}
|
|
2831
2881
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2832
2882
|
"connection",
|
|
@@ -3360,16 +3410,16 @@ function parseJson(raw, schema) {
|
|
|
3360
3410
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
3361
3411
|
return parsed.success ? parsed.data : void 0;
|
|
3362
3412
|
}
|
|
3363
|
-
function readHeadroomState(
|
|
3413
|
+
function readHeadroomState(fs12, stateFile) {
|
|
3364
3414
|
try {
|
|
3365
|
-
return parseJson(
|
|
3415
|
+
return parseJson(fs12.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
3366
3416
|
} catch {
|
|
3367
3417
|
return void 0;
|
|
3368
3418
|
}
|
|
3369
3419
|
}
|
|
3370
|
-
function writeHeadroomState(
|
|
3371
|
-
|
|
3372
|
-
|
|
3420
|
+
function writeHeadroomState(fs12, stateFile, state) {
|
|
3421
|
+
fs12.mkdirp(import_node_path8.default.dirname(stateFile));
|
|
3422
|
+
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
3373
3423
|
`);
|
|
3374
3424
|
}
|
|
3375
3425
|
function headroomAllowlist(providers) {
|
|
@@ -3389,14 +3439,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3389
3439
|
function hashAllowlist(allowlist) {
|
|
3390
3440
|
return (0, import_node_crypto5.createHash)("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3391
3441
|
}
|
|
3392
|
-
function readAllProviders(
|
|
3442
|
+
function readAllProviders(fs12, providersDir) {
|
|
3393
3443
|
const result = [];
|
|
3394
|
-
for (const name of
|
|
3444
|
+
for (const name of fs12.readdir(providersDir)) {
|
|
3395
3445
|
if (!name.endsWith(".json")) {
|
|
3396
3446
|
continue;
|
|
3397
3447
|
}
|
|
3398
3448
|
try {
|
|
3399
|
-
const parsed = parseJson(
|
|
3449
|
+
const parsed = parseJson(fs12.readFileUtf8(import_node_path8.default.join(providersDir, name)), ProviderSchema);
|
|
3400
3450
|
if (parsed !== void 0) {
|
|
3401
3451
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
3402
3452
|
}
|
|
@@ -3406,22 +3456,22 @@ function readAllProviders(fs10, providersDir) {
|
|
|
3406
3456
|
}
|
|
3407
3457
|
return result;
|
|
3408
3458
|
}
|
|
3409
|
-
function writeSession(
|
|
3410
|
-
|
|
3411
|
-
|
|
3459
|
+
function writeSession(fs12, sessionsDir, session) {
|
|
3460
|
+
fs12.mkdirp(sessionsDir);
|
|
3461
|
+
fs12.writeFileUtf8(import_node_path8.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
3412
3462
|
`);
|
|
3413
3463
|
}
|
|
3414
|
-
function removeSession(
|
|
3415
|
-
|
|
3464
|
+
function removeSession(fs12, sessionsDir, pid) {
|
|
3465
|
+
fs12.removeRecursive(import_node_path8.default.join(sessionsDir, `${String(pid)}.json`));
|
|
3416
3466
|
}
|
|
3417
|
-
function listSessions(
|
|
3467
|
+
function listSessions(fs12, sessionsDir) {
|
|
3418
3468
|
const sessions = [];
|
|
3419
|
-
for (const name of
|
|
3469
|
+
for (const name of fs12.readdir(sessionsDir)) {
|
|
3420
3470
|
if (!name.endsWith(".json")) {
|
|
3421
3471
|
continue;
|
|
3422
3472
|
}
|
|
3423
3473
|
try {
|
|
3424
|
-
const parsed = parseJson(
|
|
3474
|
+
const parsed = parseJson(fs12.readFileUtf8(import_node_path8.default.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
3425
3475
|
if (parsed !== void 0) {
|
|
3426
3476
|
sessions.push(parsed);
|
|
3427
3477
|
}
|
|
@@ -3431,22 +3481,22 @@ function listSessions(fs10, sessionsDir) {
|
|
|
3431
3481
|
}
|
|
3432
3482
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
3433
3483
|
}
|
|
3434
|
-
function sessionsForSupervisor(
|
|
3435
|
-
return listSessions(
|
|
3484
|
+
function sessionsForSupervisor(fs12, sessionsDir, supervisorPid) {
|
|
3485
|
+
return listSessions(fs12, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3436
3486
|
}
|
|
3437
|
-
function pruneDeadSessions(
|
|
3487
|
+
function pruneDeadSessions(fs12, sessionsDir, isRunning) {
|
|
3438
3488
|
const removed = [];
|
|
3439
|
-
for (const session of listSessions(
|
|
3489
|
+
for (const session of listSessions(fs12, sessionsDir)) {
|
|
3440
3490
|
if (!isRunning(session.pid)) {
|
|
3441
|
-
removeSession(
|
|
3491
|
+
removeSession(fs12, sessionsDir, session.pid);
|
|
3442
3492
|
removed.push(session.pid);
|
|
3443
3493
|
}
|
|
3444
3494
|
}
|
|
3445
3495
|
return removed;
|
|
3446
3496
|
}
|
|
3447
|
-
function readStartLock(
|
|
3497
|
+
function readStartLock(fs12, lockFile) {
|
|
3448
3498
|
try {
|
|
3449
|
-
return parseJson(
|
|
3499
|
+
return parseJson(fs12.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
3450
3500
|
} catch {
|
|
3451
3501
|
return void 0;
|
|
3452
3502
|
}
|
|
@@ -3697,12 +3747,12 @@ function isIdentityName(name) {
|
|
|
3697
3747
|
function claudeJsonPath(identitiesDir, identity) {
|
|
3698
3748
|
return import_node_path9.default.join(identitiesDir, identity, CLAUDE_JSON);
|
|
3699
3749
|
}
|
|
3700
|
-
function readAccountMetadata(
|
|
3750
|
+
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
3701
3751
|
if (!isIdentityName(identity)) {
|
|
3702
3752
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3703
3753
|
}
|
|
3704
3754
|
const file = claudeJsonPath(identitiesDir, identity);
|
|
3705
|
-
const raw =
|
|
3755
|
+
const raw = fs12.readFileUtf8(file);
|
|
3706
3756
|
if (raw === void 0) {
|
|
3707
3757
|
return void 0;
|
|
3708
3758
|
}
|
|
@@ -3736,8 +3786,8 @@ function readAccountMetadata(fs10, identitiesDir, identity) {
|
|
|
3736
3786
|
// src/usage/read.ts
|
|
3737
3787
|
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
3738
3788
|
var DAY_MS = 864e5;
|
|
3739
|
-
function listLogSegments(
|
|
3740
|
-
return
|
|
3789
|
+
function listLogSegments(fs12, logDir) {
|
|
3790
|
+
return fs12.readdir(logDir).flatMap((name) => {
|
|
3741
3791
|
const match = SEGMENT_RE.exec(name);
|
|
3742
3792
|
const day = match?.[1];
|
|
3743
3793
|
if (day === void 0) {
|
|
@@ -3746,15 +3796,15 @@ function listLogSegments(fs10, logDir) {
|
|
|
3746
3796
|
return [{ name, day, endsAt: Date.parse(`${day}T00:00:00.000Z`) + DAY_MS }];
|
|
3747
3797
|
}).sort((left, right) => left.day.localeCompare(right.day) || left.name.localeCompare(right.name));
|
|
3748
3798
|
}
|
|
3749
|
-
function readUsageLog(
|
|
3799
|
+
function readUsageLog(fs12, logDir, options = {}) {
|
|
3750
3800
|
const { sinceMs } = options;
|
|
3751
3801
|
const records = [];
|
|
3752
3802
|
let invalidLines = 0;
|
|
3753
|
-
for (const segment of listLogSegments(
|
|
3803
|
+
for (const segment of listLogSegments(fs12, logDir)) {
|
|
3754
3804
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
3755
3805
|
continue;
|
|
3756
3806
|
}
|
|
3757
|
-
const contents =
|
|
3807
|
+
const contents = fs12.readFileUtf8(import_node_path10.default.join(logDir, segment.name));
|
|
3758
3808
|
if (contents === void 0) {
|
|
3759
3809
|
continue;
|
|
3760
3810
|
}
|
|
@@ -3798,9 +3848,9 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3798
3848
|
}
|
|
3799
3849
|
return import_node_path10.default.join(snapshotsDir, `${identity}.json`);
|
|
3800
3850
|
}
|
|
3801
|
-
function readUsageSnapshot(
|
|
3851
|
+
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
3802
3852
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3803
|
-
const raw =
|
|
3853
|
+
const raw = fs12.readFileUtf8(file);
|
|
3804
3854
|
if (raw === void 0) {
|
|
3805
3855
|
return void 0;
|
|
3806
3856
|
}
|
|
@@ -3816,8 +3866,8 @@ function readUsageSnapshot(fs10, snapshotsDir, identity) {
|
|
|
3816
3866
|
}
|
|
3817
3867
|
return snapshot.data;
|
|
3818
3868
|
}
|
|
3819
|
-
function listUsageSnapshots(
|
|
3820
|
-
return
|
|
3869
|
+
function listUsageSnapshots(fs12, snapshotsDir) {
|
|
3870
|
+
return fs12.readdir(snapshotsDir).flatMap((name) => {
|
|
3821
3871
|
if (!name.endsWith(".json")) {
|
|
3822
3872
|
return [];
|
|
3823
3873
|
}
|
|
@@ -3825,7 +3875,7 @@ function listUsageSnapshots(fs10, snapshotsDir) {
|
|
|
3825
3875
|
if (!isIdentityName(identity)) {
|
|
3826
3876
|
return [];
|
|
3827
3877
|
}
|
|
3828
|
-
const snapshot = readUsageSnapshot(
|
|
3878
|
+
const snapshot = readUsageSnapshot(fs12, snapshotsDir, identity);
|
|
3829
3879
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3830
3880
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3831
3881
|
}
|
|
@@ -4000,9 +4050,9 @@ function compareVersions(a, b) {
|
|
|
4000
4050
|
}
|
|
4001
4051
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
4002
4052
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
4003
|
-
const { fs:
|
|
4053
|
+
const { fs: fs12, paths } = ports;
|
|
4004
4054
|
let config = initialConfig;
|
|
4005
|
-
const previousState = readHeadroomState(
|
|
4055
|
+
const previousState = readHeadroomState(fs12, paths.headroomStateFile);
|
|
4006
4056
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
4007
4057
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
4008
4058
|
return 0;
|
|
@@ -4011,19 +4061,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4011
4061
|
let lastPort = previousState?.lastPort;
|
|
4012
4062
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
4013
4063
|
const ownsState = () => {
|
|
4014
|
-
const owner = readHeadroomState(
|
|
4064
|
+
const owner = readHeadroomState(fs12, paths.headroomStateFile)?.supervisorPid;
|
|
4015
4065
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
4016
4066
|
};
|
|
4017
4067
|
const writeOwnState = (state) => {
|
|
4018
4068
|
if (!ownsState()) {
|
|
4019
4069
|
return false;
|
|
4020
4070
|
}
|
|
4021
|
-
writeHeadroomState(
|
|
4071
|
+
writeHeadroomState(fs12, paths.headroomStateFile, state);
|
|
4022
4072
|
return true;
|
|
4023
4073
|
};
|
|
4024
4074
|
const fail = (message) => {
|
|
4025
4075
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
4026
|
-
|
|
4076
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
4027
4077
|
}
|
|
4028
4078
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
4029
4079
|
return 1;
|
|
@@ -4072,7 +4122,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4072
4122
|
const settingsHash = hashSettings(config.settings);
|
|
4073
4123
|
const allowlist = allowlistOf(ports);
|
|
4074
4124
|
const allowlistHash = hashAllowlist(allowlist);
|
|
4075
|
-
pruneDeadSessions(
|
|
4125
|
+
pruneDeadSessions(fs12, paths.headroomSessionsDir, ports.isRunning);
|
|
4076
4126
|
const ownsNow = ownsState();
|
|
4077
4127
|
if (!ownsNow && !superseded) {
|
|
4078
4128
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -4124,7 +4174,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4124
4174
|
...sticky()
|
|
4125
4175
|
});
|
|
4126
4176
|
if (recorded) {
|
|
4127
|
-
|
|
4177
|
+
fs12.removeRecursive(paths.headroomLockFile);
|
|
4128
4178
|
}
|
|
4129
4179
|
idleSince = void 0;
|
|
4130
4180
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -4139,7 +4189,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4139
4189
|
}
|
|
4140
4190
|
} else {
|
|
4141
4191
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
4142
|
-
if (sessionsForSupervisor(
|
|
4192
|
+
if (sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
4143
4193
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
4144
4194
|
ports.stopProcess(headroomPid);
|
|
4145
4195
|
headroomPid = void 0;
|
|
@@ -4148,7 +4198,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4148
4198
|
continue;
|
|
4149
4199
|
}
|
|
4150
4200
|
}
|
|
4151
|
-
const sessions = sessionsForSupervisor(
|
|
4201
|
+
const sessions = sessionsForSupervisor(fs12, paths.headroomSessionsDir, ports.ownPid);
|
|
4152
4202
|
if (sessions.length === 0) {
|
|
4153
4203
|
idleSince ??= ports.now();
|
|
4154
4204
|
const idleMs = ports.now() - idleSince;
|
|
@@ -4170,9 +4220,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
4170
4220
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
4171
4221
|
}
|
|
4172
4222
|
}
|
|
4173
|
-
function currentHeadroomAllowlist(
|
|
4174
|
-
const providers = readAllProviders(
|
|
4175
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
4223
|
+
function currentHeadroomAllowlist(fs12, paths) {
|
|
4224
|
+
const providers = readAllProviders(fs12, paths.providersDir).map((entry) => entry.provider);
|
|
4225
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs12, paths.frontdoorStateFile))));
|
|
4176
4226
|
}
|
|
4177
4227
|
function allowlistOf(ports) {
|
|
4178
4228
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -4349,8 +4399,8 @@ function decideConfigProfile(params) {
|
|
|
4349
4399
|
}
|
|
4350
4400
|
return { source: "none" };
|
|
4351
4401
|
}
|
|
4352
|
-
function loadIdentity(identitiesDir, name,
|
|
4353
|
-
return loadConfigFile(import_node_path12.default.join(identitiesDir, name, "identity.json"), IdentitySchema,
|
|
4402
|
+
function loadIdentity(identitiesDir, name, fs12) {
|
|
4403
|
+
return loadConfigFile(import_node_path12.default.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
4354
4404
|
}
|
|
4355
4405
|
|
|
4356
4406
|
// src/configProfilesStore.ts
|
|
@@ -4692,13 +4742,59 @@ var import_node_path15 = __toESM(require("node:path"), 1);
|
|
|
4692
4742
|
|
|
4693
4743
|
// src/credentialCache.ts
|
|
4694
4744
|
var import_zod7 = require("zod");
|
|
4745
|
+
var MS_PER_UNIT = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
|
|
4695
4746
|
var CachedCredentialSchema = import_zod7.z.strictObject({
|
|
4696
4747
|
token: import_zod7.z.string().min(1),
|
|
4697
4748
|
fetchedAt: import_zod7.z.number().int().nonnegative(),
|
|
4698
4749
|
source: CredentialSourceSchema
|
|
4699
4750
|
});
|
|
4751
|
+
function effectiveStore(cache, platform) {
|
|
4752
|
+
return cache.store ?? (platform === "darwin" ? "keychain" : "file");
|
|
4753
|
+
}
|
|
4754
|
+
function ttlMs(cache) {
|
|
4755
|
+
if (cache.ttl === void 0) {
|
|
4756
|
+
return void 0;
|
|
4757
|
+
}
|
|
4758
|
+
const multiplier = MS_PER_UNIT[cache.ttl.slice(-1)];
|
|
4759
|
+
if (multiplier === void 0) {
|
|
4760
|
+
throw new Error(`credential cache ttl "${cache.ttl}" has no recognised unit`);
|
|
4761
|
+
}
|
|
4762
|
+
return Number(cache.ttl.slice(0, -1)) * multiplier;
|
|
4763
|
+
}
|
|
4764
|
+
function isFresh(entry, cache, now) {
|
|
4765
|
+
const limit = ttlMs(cache);
|
|
4766
|
+
if (entry.fetchedAt > now) {
|
|
4767
|
+
return false;
|
|
4768
|
+
}
|
|
4769
|
+
return limit === void 0 || now - entry.fetchedAt < limit;
|
|
4770
|
+
}
|
|
4700
4771
|
|
|
4701
4772
|
// src/credential.ts
|
|
4773
|
+
var CREDENTIAL_UNAVAILABLE_EXIT = 64;
|
|
4774
|
+
var CREDENTIAL_COMMAND_TIMEOUT_MS = 1e4;
|
|
4775
|
+
var CREDENTIAL_INTERACTIVE_TIMEOUT_MS = 12e4;
|
|
4776
|
+
var GROUP_OTHER_PERMISSION_BITS = 63;
|
|
4777
|
+
var PERMISSION_BITS = 511;
|
|
4778
|
+
var OCTAL_RADIX = 8;
|
|
4779
|
+
function commandArgv(source) {
|
|
4780
|
+
if ("command" in source) {
|
|
4781
|
+
return source.command;
|
|
4782
|
+
}
|
|
4783
|
+
if ("op" in source) {
|
|
4784
|
+
return ["op", "read", source.op];
|
|
4785
|
+
}
|
|
4786
|
+
const account = source.keychain.account;
|
|
4787
|
+
return ["security", "find-generic-password", "-s", source.keychain.service, ...account === void 0 ? [] : ["-a", account], "-w"];
|
|
4788
|
+
}
|
|
4789
|
+
function isInteractiveSource(source, env) {
|
|
4790
|
+
if ("command" in source || "keychain" in source) {
|
|
4791
|
+
return source.interactive ?? false;
|
|
4792
|
+
}
|
|
4793
|
+
if ("op" in source) {
|
|
4794
|
+
return source.interactive ?? (env.OP_SERVICE_ACCOUNT_TOKEN ?? "") === "";
|
|
4795
|
+
}
|
|
4796
|
+
return false;
|
|
4797
|
+
}
|
|
4702
4798
|
function summariseSource(source) {
|
|
4703
4799
|
if ("env" in source) {
|
|
4704
4800
|
return { kind: "env", variable: source.env };
|
|
@@ -4746,6 +4842,9 @@ function formatSourceSummary(summary) {
|
|
|
4746
4842
|
return summary;
|
|
4747
4843
|
}
|
|
4748
4844
|
}
|
|
4845
|
+
function describeSource(source) {
|
|
4846
|
+
return formatSourceSummary(summariseSource(source));
|
|
4847
|
+
}
|
|
4749
4848
|
function formatCredentialSummary(summary) {
|
|
4750
4849
|
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
4850
|
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
@@ -4753,6 +4852,88 @@ function formatCredentialSummary(summary) {
|
|
|
4753
4852
|
function describeCredential(credential) {
|
|
4754
4853
|
return formatCredentialSummary(summariseCredential(credential));
|
|
4755
4854
|
}
|
|
4855
|
+
function trySource(source, params) {
|
|
4856
|
+
const none = (reason, warn = false) => ({ ok: false, reason, warn });
|
|
4857
|
+
if ("env" in source) {
|
|
4858
|
+
const value = params.env[source.env];
|
|
4859
|
+
return value === void 0 || value === "" ? none("is unset or empty") : { ok: true, token: value };
|
|
4860
|
+
}
|
|
4861
|
+
if ("literal" in source) {
|
|
4862
|
+
return { ok: true, token: source.literal };
|
|
4863
|
+
}
|
|
4864
|
+
if ("file" in source) {
|
|
4865
|
+
const read = params.port.readSecretFile(source.file);
|
|
4866
|
+
if (!read.found) {
|
|
4867
|
+
return none("does not exist");
|
|
4868
|
+
}
|
|
4869
|
+
if (read.mode !== void 0 && (read.mode & GROUP_OTHER_PERMISSION_BITS) !== 0) {
|
|
4870
|
+
return none(
|
|
4871
|
+
`is readable or writable by group or others (mode ${(read.mode & PERMISSION_BITS).toString(OCTAL_RADIX)}); run \`chmod 600 ${source.file}\``,
|
|
4872
|
+
true
|
|
4873
|
+
);
|
|
4874
|
+
}
|
|
4875
|
+
const token2 = read.content.trim();
|
|
4876
|
+
return token2 === "" ? none("is empty") : { ok: true, token: token2 };
|
|
4877
|
+
}
|
|
4878
|
+
const argv = commandArgv(source);
|
|
4879
|
+
const interactive = isInteractiveSource(source, params.env);
|
|
4880
|
+
if (interactive && !params.port.personPresent()) {
|
|
4881
|
+
return none("needs a person to approve it, but there is no terminal or desktop session");
|
|
4882
|
+
}
|
|
4883
|
+
const timeoutMs = source.timeoutMs ?? (interactive ? CREDENTIAL_INTERACTIVE_TIMEOUT_MS : CREDENTIAL_COMMAND_TIMEOUT_MS);
|
|
4884
|
+
const result = params.port.runCommand(argv, { timeoutMs, interactive });
|
|
4885
|
+
if (result.timedOut) {
|
|
4886
|
+
return none(`timed out after ${String(timeoutMs)}ms`);
|
|
4887
|
+
}
|
|
4888
|
+
if (result.status !== 0) {
|
|
4889
|
+
const stderr = result.stderr.trim();
|
|
4890
|
+
const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
|
|
4891
|
+
return none(`${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
|
|
4892
|
+
}
|
|
4893
|
+
const token = result.stdout.trim();
|
|
4894
|
+
return token === "" ? none("printed no token") : { ok: true, token };
|
|
4895
|
+
}
|
|
4896
|
+
function warmCommandFor(subject) {
|
|
4897
|
+
const [kind, name] = subject.split(" ");
|
|
4898
|
+
return kind === "provider" ? `agent-shim credential warm --provider ${name ?? ""}` : `agent-shim credential warm ${name ?? ""}`;
|
|
4899
|
+
}
|
|
4900
|
+
function resolveCredential(params) {
|
|
4901
|
+
const target = params.credential.target ?? "bearer";
|
|
4902
|
+
const cacheBlock = params.credential.cache;
|
|
4903
|
+
const cache = cacheBlock === void 0 ? void 0 : params.port.cache;
|
|
4904
|
+
const store = cacheBlock === void 0 || cache === void 0 ? void 0 : effectiveStore(cacheBlock, cache.platform);
|
|
4905
|
+
if (cacheBlock !== void 0 && cache !== void 0 && store !== void 0 && params.refresh !== true) {
|
|
4906
|
+
const entry = cache.port.read(store, params.subject);
|
|
4907
|
+
if (entry !== void 0 && isFresh(entry, cacheBlock, cache.now())) {
|
|
4908
|
+
return { ok: true, credential: { target, token: entry.token, source: entry.source, warnings: [], cachedAt: entry.fetchedAt } };
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
const attempts = [];
|
|
4912
|
+
const warnings = [];
|
|
4913
|
+
for (const source of params.credential.sources) {
|
|
4914
|
+
const outcome = trySource(source, params);
|
|
4915
|
+
if (outcome.ok) {
|
|
4916
|
+
if (cache !== void 0 && store !== void 0) {
|
|
4917
|
+
cache.port.write(store, params.subject, { token: outcome.token, fetchedAt: cache.now(), source });
|
|
4918
|
+
}
|
|
4919
|
+
return { ok: true, credential: { target, token: outcome.token, source, warnings } };
|
|
4920
|
+
}
|
|
4921
|
+
const line = `${describeSource(source)} ${outcome.reason}`;
|
|
4922
|
+
attempts.push(line);
|
|
4923
|
+
if (outcome.warn) {
|
|
4924
|
+
warnings.push(`agent-shim: ${params.subject}: skipped ${line}`);
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
const hint = cacheBlock === void 0 ? "" : `; fill the cache from a terminal with \`${warmCommandFor(params.subject)}\``;
|
|
4928
|
+
return { ok: false, message: `agent-shim: ${params.subject} has no usable credential: ${attempts.join("; ")}${hint}` };
|
|
4929
|
+
}
|
|
4930
|
+
function credentialVariables(target, token) {
|
|
4931
|
+
return {
|
|
4932
|
+
ANTHROPIC_AUTH_TOKEN: target === "bearer" ? token : void 0,
|
|
4933
|
+
ANTHROPIC_API_KEY: target === "apiKey" ? token : void 0,
|
|
4934
|
+
CLAUDE_CODE_OAUTH_TOKEN: target === "oauthToken" ? token : void 0
|
|
4935
|
+
};
|
|
4936
|
+
}
|
|
4756
4937
|
|
|
4757
4938
|
// src/providersStore.ts
|
|
4758
4939
|
var ProviderNotFoundError = class extends CliError {
|
|
@@ -4858,6 +5039,11 @@ function readProvider(paths, name) {
|
|
|
4858
5039
|
const raw = JSON.parse(import_node_fs5.default.readFileSync(filePath, "utf8"));
|
|
4859
5040
|
return parseProviderFile(filePath, raw);
|
|
4860
5041
|
}
|
|
5042
|
+
function loadProvider(providersDir, name, port) {
|
|
5043
|
+
const filePath = import_node_path15.default.join(providersDir, `${name}.json`);
|
|
5044
|
+
const raw = port.readConfigFile(filePath);
|
|
5045
|
+
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
5046
|
+
}
|
|
4861
5047
|
function listProviders(paths) {
|
|
4862
5048
|
const names = listProviderNames(
|
|
4863
5049
|
(dir) => import_node_fs5.default.readdirSync(dir),
|
|
@@ -4990,6 +5176,30 @@ function cascadeProviderName(cascade) {
|
|
|
4990
5176
|
}
|
|
4991
5177
|
return name;
|
|
4992
5178
|
}
|
|
5179
|
+
function resolveProvider(params) {
|
|
5180
|
+
const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
|
|
5181
|
+
const name = cliName ?? (params.cascade === void 0 ? void 0 : cascadeProviderName(params.cascade));
|
|
5182
|
+
if (name === void 0) {
|
|
5183
|
+
return void 0;
|
|
5184
|
+
}
|
|
5185
|
+
const definition = loadProvider(params.paths.providersDir, name, params.port);
|
|
5186
|
+
if (definition === void 0) {
|
|
5187
|
+
const known = listProviderNames(params.port.readdir, params.paths.providersDir);
|
|
5188
|
+
return {
|
|
5189
|
+
ok: false,
|
|
5190
|
+
status: 1,
|
|
5191
|
+
message: `agent-shim: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `agent-shim provider add`.")
|
|
5192
|
+
};
|
|
5193
|
+
}
|
|
5194
|
+
if (params.credentials === void 0) {
|
|
5195
|
+
return { ok: false, status: 1, message: `agent-shim: provider ${name} needs its credential resolved, but this launcher has no credential port wired` };
|
|
5196
|
+
}
|
|
5197
|
+
const resolution = resolveCredential({ credential: definition.credential, env: params.env, port: params.credentials, subject: `provider ${name}` });
|
|
5198
|
+
if (!resolution.ok) {
|
|
5199
|
+
return { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
5200
|
+
}
|
|
5201
|
+
return { ok: true, provider: { name, definition, credential: resolution.credential }, warnings: resolution.credential.warnings };
|
|
5202
|
+
}
|
|
4993
5203
|
|
|
4994
5204
|
// src/directoryRulesStore.ts
|
|
4995
5205
|
var DirectoryRuleNotFoundError = class extends CliError {
|
|
@@ -5082,7 +5292,7 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
5082
5292
|
}
|
|
5083
5293
|
|
|
5084
5294
|
// src/checkReport.ts
|
|
5085
|
-
var
|
|
5295
|
+
var import_node_os4 = __toESM(require("node:os"), 1);
|
|
5086
5296
|
|
|
5087
5297
|
// src/launcher/cascade.ts
|
|
5088
5298
|
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
@@ -5178,6 +5388,7 @@ var MS_PER_SECOND2 = 1e3;
|
|
|
5178
5388
|
var SECONDS_PER_MINUTE = 60;
|
|
5179
5389
|
var MINUTES_PER_HOUR = 60;
|
|
5180
5390
|
var HOURS_PER_DAY = 24;
|
|
5391
|
+
var WARNING_STATUSES = /* @__PURE__ */ new Set(["allowed_warning", "rejected"]);
|
|
5181
5392
|
function formatAge(ms) {
|
|
5182
5393
|
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5183
5394
|
if (minutes < MINUTES_PER_HOUR) {
|
|
@@ -5198,8 +5409,30 @@ function effectiveWindow(window, nowMs) {
|
|
|
5198
5409
|
...window.status === void 0 ? {} : { status: window.status }
|
|
5199
5410
|
};
|
|
5200
5411
|
}
|
|
5412
|
+
function quotaWarnings(snapshot, provider, nowMs) {
|
|
5413
|
+
const rateLimit = snapshot?.providers[provider]?.rateLimit;
|
|
5414
|
+
const unified = rateLimit?.unified;
|
|
5415
|
+
if (snapshot === void 0 || rateLimit === void 0 || unified === void 0) {
|
|
5416
|
+
return [];
|
|
5417
|
+
}
|
|
5418
|
+
const age = formatAge(nowMs - Date.parse(rateLimit.observedAt));
|
|
5419
|
+
const windows = [
|
|
5420
|
+
{ name: "five-hour", window: unified.fiveHour },
|
|
5421
|
+
{ name: "seven-day", window: unified.sevenDay }
|
|
5422
|
+
];
|
|
5423
|
+
return windows.flatMap(({ name, window }) => {
|
|
5424
|
+
const effective = window === void 0 ? void 0 : effectiveWindow(window, nowMs);
|
|
5425
|
+
if (window === void 0 || effective?.status === void 0 || !WARNING_STATUSES.has(effective.status)) {
|
|
5426
|
+
return [];
|
|
5427
|
+
}
|
|
5428
|
+
const reset = window.resetsAt === void 0 ? "" : `, resets ${window.resetsAt}`;
|
|
5429
|
+
const verdict = effective.status === "rejected" ? "exhausted" : "nearly used";
|
|
5430
|
+
return [`agent-shim: identity ${snapshot.identity}: the ${name} quota is ${verdict}${reset} (last seen ${age} ago)`];
|
|
5431
|
+
});
|
|
5432
|
+
}
|
|
5201
5433
|
|
|
5202
5434
|
// src/usage/poolPick.ts
|
|
5435
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
5203
5436
|
var import_zod9 = require("zod");
|
|
5204
5437
|
|
|
5205
5438
|
// src/usage/plan.ts
|
|
@@ -5452,14 +5685,14 @@ var PicksFileSchema = import_zod9.z.strictObject({
|
|
|
5452
5685
|
function errorMessage(error) {
|
|
5453
5686
|
return error instanceof Error ? error.message : String(error);
|
|
5454
5687
|
}
|
|
5455
|
-
function loadPoolMembers(
|
|
5688
|
+
function loadPoolMembers(fs12, paths, identities, nowMs) {
|
|
5456
5689
|
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5457
|
-
const log = readUsageLog(
|
|
5690
|
+
const log = readUsageLog(fs12, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5458
5691
|
return identities.map((identity) => {
|
|
5459
5692
|
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5460
5693
|
try {
|
|
5461
|
-
const snapshot = readUsageSnapshot(
|
|
5462
|
-
const account = readAccountMetadata(
|
|
5694
|
+
const snapshot = readUsageSnapshot(fs12, paths.usageSnapshotsDir, identity);
|
|
5695
|
+
const account = readAccountMetadata(fs12, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5463
5696
|
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5464
5697
|
} catch (error) {
|
|
5465
5698
|
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
@@ -5477,9 +5710,9 @@ function rankPoolFromStore(params) {
|
|
|
5477
5710
|
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5478
5711
|
});
|
|
5479
5712
|
}
|
|
5480
|
-
function readPicksFile(
|
|
5713
|
+
function readPicksFile(fs12, file) {
|
|
5481
5714
|
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5482
|
-
const raw =
|
|
5715
|
+
const raw = fs12.readFileUtf8(file);
|
|
5483
5716
|
if (raw === void 0) {
|
|
5484
5717
|
return { file: empty };
|
|
5485
5718
|
}
|
|
@@ -5492,17 +5725,72 @@ function readPicksFile(fs10, file) {
|
|
|
5492
5725
|
const result = PicksFileSchema.safeParse(parsed);
|
|
5493
5726
|
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5494
5727
|
}
|
|
5495
|
-
function readStickyPick(
|
|
5496
|
-
const read = readPicksFile(
|
|
5728
|
+
function readStickyPick(fs12, picksFile, directory) {
|
|
5729
|
+
const read = readPicksFile(fs12, picksFile);
|
|
5497
5730
|
const entry = read.file.picks[directory];
|
|
5498
5731
|
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5499
5732
|
}
|
|
5733
|
+
function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
5734
|
+
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5735
|
+
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5736
|
+
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5737
|
+
fs12.mkdirPrivate(import_node_path18.default.dirname(picksFile));
|
|
5738
|
+
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5739
|
+
`);
|
|
5740
|
+
}
|
|
5500
5741
|
|
|
5501
5742
|
// src/launcher/pool.ts
|
|
5502
|
-
|
|
5503
|
-
|
|
5743
|
+
var RESUME_FLAGS = /* @__PURE__ */ new Set(["--continue", "-c", "--resume", "-r"]);
|
|
5744
|
+
var TERMINATOR = "--";
|
|
5745
|
+
var REASONS_LOGGED = 3;
|
|
5746
|
+
function splitMembers(pool, paths, fs12) {
|
|
5747
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs12) !== void 0;
|
|
5504
5748
|
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5505
5749
|
}
|
|
5750
|
+
function isResuming(passthrough) {
|
|
5751
|
+
const end = passthrough.indexOf(TERMINATOR);
|
|
5752
|
+
return (end === -1 ? passthrough : passthrough.slice(0, end)).some((token) => RESUME_FLAGS.has(token));
|
|
5753
|
+
}
|
|
5754
|
+
function resolvePoolLaunch(params) {
|
|
5755
|
+
const { poolName, log } = params;
|
|
5756
|
+
const pool = params.pools?.[poolName];
|
|
5757
|
+
if (pool === void 0) {
|
|
5758
|
+
return { ok: false, message: `agent-shim: no pool named "${poolName}" (selected via ${params.selectedVia}). Run \`agent-shim pool add ${poolName} --identity <name>...\` first.` };
|
|
5759
|
+
}
|
|
5760
|
+
const { present: identities, missing } = splitMembers(pool, params.paths, params.fs);
|
|
5761
|
+
for (const name of missing) {
|
|
5762
|
+
log.warn(`agent-shim: pool "${poolName}" names identity "${name}", which does not exist; skipping it`);
|
|
5763
|
+
}
|
|
5764
|
+
if (identities.length === 0) {
|
|
5765
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
|
|
5766
|
+
}
|
|
5767
|
+
const resuming = isResuming(params.passthrough);
|
|
5768
|
+
const stickyRead = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd);
|
|
5769
|
+
if (stickyRead.problem !== void 0) {
|
|
5770
|
+
log.warn(`agent-shim: ${stickyRead.problem}; ignoring the last-pick record`);
|
|
5771
|
+
}
|
|
5772
|
+
for (; ; ) {
|
|
5773
|
+
const nowMs = params.now();
|
|
5774
|
+
const ranking = rankPoolFromStore({ fs: params.usageFs, paths: params.paths, identities, nowMs, resuming, ...stickyRead.sticky === void 0 ? {} : { sticky: stickyRead.sticky } });
|
|
5775
|
+
const { pick, earliestReturn } = ranking;
|
|
5776
|
+
if (pick !== void 0) {
|
|
5777
|
+
recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick.identity, nowMs);
|
|
5778
|
+
return { ok: true, identity: pick.identity, explanation: `pool ${poolName}: ${pick.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
|
|
5779
|
+
}
|
|
5780
|
+
if (earliestReturn === void 0) {
|
|
5781
|
+
return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };
|
|
5782
|
+
}
|
|
5783
|
+
const until = new Date(earliestReturn.atMs).toISOString();
|
|
5784
|
+
if (!params.wait) {
|
|
5785
|
+
return {
|
|
5786
|
+
ok: false,
|
|
5787
|
+
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.`
|
|
5788
|
+
};
|
|
5789
|
+
}
|
|
5790
|
+
log.info(`agent-shim: every member of pool "${poolName}" is refused; waiting until ${until} for ${earliestReturn.identity}`);
|
|
5791
|
+
params.sleep(Math.max(earliestReturn.atMs - nowMs, 0));
|
|
5792
|
+
}
|
|
5793
|
+
}
|
|
5506
5794
|
|
|
5507
5795
|
// src/poolPickReport.ts
|
|
5508
5796
|
function candidateView(candidate) {
|
|
@@ -5542,14 +5830,15 @@ function collectPoolPick(params) {
|
|
|
5542
5830
|
var import_node_child_process = require("node:child_process");
|
|
5543
5831
|
var import_node_crypto8 = require("node:crypto");
|
|
5544
5832
|
var import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
5545
|
-
var
|
|
5833
|
+
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
5834
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
5546
5835
|
var import_node_sea = require("node:sea");
|
|
5547
5836
|
|
|
5548
5837
|
// src/versionDiscovery.ts
|
|
5549
5838
|
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
5550
|
-
var
|
|
5839
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
5551
5840
|
function defaultVersionsDir() {
|
|
5552
|
-
return
|
|
5841
|
+
return import_node_path19.default.join(import_node_os2.default.homedir(), ".local", "share", "claude", "versions");
|
|
5553
5842
|
}
|
|
5554
5843
|
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
5555
5844
|
function isNumericDottedVersion(name) {
|
|
@@ -5591,14 +5880,14 @@ function discoverClaudeBinary(options) {
|
|
|
5591
5880
|
const highestVersion = pickHighestVersion(entries);
|
|
5592
5881
|
if (highestVersion !== void 0) {
|
|
5593
5882
|
return {
|
|
5594
|
-
path:
|
|
5883
|
+
path: import_node_path19.default.join(versionsDir, highestVersion),
|
|
5595
5884
|
source: "versions-dir",
|
|
5596
5885
|
version: highestVersion
|
|
5597
5886
|
};
|
|
5598
5887
|
}
|
|
5599
|
-
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) =>
|
|
5888
|
+
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => import_node_path19.default.resolve(dir)));
|
|
5600
5889
|
for (const dir of options.pathDirs) {
|
|
5601
|
-
if (ownInstallDirs.has(
|
|
5890
|
+
if (ownInstallDirs.has(import_node_path19.default.resolve(dir))) {
|
|
5602
5891
|
continue;
|
|
5603
5892
|
}
|
|
5604
5893
|
const found = options.findExecutableInDir(dir, "claude");
|
|
@@ -5643,7 +5932,7 @@ function isErrorWithCode(error, code) {
|
|
|
5643
5932
|
}
|
|
5644
5933
|
var OWNER_ONLY_DIR_MODE = 448;
|
|
5645
5934
|
var OWNER_ONLY_FILE_MODE = 384;
|
|
5646
|
-
var
|
|
5935
|
+
var PERMISSION_BITS2 = 511;
|
|
5647
5936
|
var realFarmFs = {
|
|
5648
5937
|
lstat(filePath) {
|
|
5649
5938
|
let stat;
|
|
@@ -5709,7 +5998,7 @@ var realFarmFs = {
|
|
|
5709
5998
|
appendFilePrivate(filePath, contents) {
|
|
5710
5999
|
const descriptor = import_node_fs6.default.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
5711
6000
|
try {
|
|
5712
|
-
if ((import_node_fs6.default.fstatSync(descriptor).mode &
|
|
6001
|
+
if ((import_node_fs6.default.fstatSync(descriptor).mode & PERMISSION_BITS2) !== OWNER_ONLY_FILE_MODE) {
|
|
5713
6002
|
import_node_fs6.default.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
5714
6003
|
}
|
|
5715
6004
|
const bytes = Buffer.from(contents, "utf8");
|
|
@@ -5743,6 +6032,10 @@ var realFarmFs = {
|
|
|
5743
6032
|
}
|
|
5744
6033
|
}
|
|
5745
6034
|
};
|
|
6035
|
+
var INT32_BYTE_LENGTH = 4;
|
|
6036
|
+
function realSleepSync(ms) {
|
|
6037
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(INT32_BYTE_LENGTH)), 0, 0, ms);
|
|
6038
|
+
}
|
|
5746
6039
|
function realIsProcessAlive(pid) {
|
|
5747
6040
|
try {
|
|
5748
6041
|
process.kill(pid, 0);
|
|
@@ -5764,6 +6057,51 @@ var realRunPort = {
|
|
|
5764
6057
|
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
5765
6058
|
}
|
|
5766
6059
|
};
|
|
6060
|
+
function expandHome2(filePath) {
|
|
6061
|
+
return filePath.startsWith("~/") ? import_node_path20.default.join(import_node_os3.default.homedir(), filePath.slice(1)) : filePath;
|
|
6062
|
+
}
|
|
6063
|
+
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
6064
|
+
function hasDesktopSession() {
|
|
6065
|
+
if (process.platform === "darwin") {
|
|
6066
|
+
const result = (0, import_node_child_process.spawnSync)("launchctl", ["managername"], { encoding: "utf8" });
|
|
6067
|
+
return result.status === 0 && result.stdout.trim() === MACOS_GUI_SESSION_MANAGER;
|
|
6068
|
+
}
|
|
6069
|
+
if (process.platform === "win32") {
|
|
6070
|
+
return false;
|
|
6071
|
+
}
|
|
6072
|
+
return (process.env.DISPLAY ?? "") !== "" || (process.env.WAYLAND_DISPLAY ?? "") !== "";
|
|
6073
|
+
}
|
|
6074
|
+
var realCredentialPort = {
|
|
6075
|
+
readSecretFile(filePath) {
|
|
6076
|
+
const resolved = expandHome2(filePath);
|
|
6077
|
+
let content;
|
|
6078
|
+
let stat;
|
|
6079
|
+
try {
|
|
6080
|
+
stat = import_node_fs6.default.statSync(resolved);
|
|
6081
|
+
content = import_node_fs6.default.readFileSync(resolved, "utf8");
|
|
6082
|
+
} catch (error) {
|
|
6083
|
+
if (isEnoent3(error)) {
|
|
6084
|
+
return { found: false };
|
|
6085
|
+
}
|
|
6086
|
+
throw error;
|
|
6087
|
+
}
|
|
6088
|
+
return { found: true, content, mode: process.platform === "win32" ? void 0 : stat.mode };
|
|
6089
|
+
},
|
|
6090
|
+
runCommand(argv, options) {
|
|
6091
|
+
const [command, ...args] = argv;
|
|
6092
|
+
const result = (0, import_node_child_process.spawnSync)(command, args, {
|
|
6093
|
+
encoding: "utf8",
|
|
6094
|
+
timeout: options.timeoutMs,
|
|
6095
|
+
stdio: options.interactive ? ["inherit", "pipe", "inherit"] : ["ignore", "pipe", "pipe"]
|
|
6096
|
+
});
|
|
6097
|
+
if (result.error !== void 0) {
|
|
6098
|
+
const timedOut = isErrorWithCode(result.error, "ETIMEDOUT");
|
|
6099
|
+
return { status: result.status, stdout: "", stderr: timedOut ? "" : result.error.message, timedOut };
|
|
6100
|
+
}
|
|
6101
|
+
return { status: result.status, stdout: result.stdout, stderr: options.interactive ? "" : result.stderr, timedOut: false };
|
|
6102
|
+
},
|
|
6103
|
+
personPresent: () => process.stdin.isTTY || hasDesktopSession()
|
|
6104
|
+
};
|
|
5767
6105
|
function resolveGitBranch(run, cwd) {
|
|
5768
6106
|
const result = run.run("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
5769
6107
|
if (result.status !== 0) {
|
|
@@ -5778,6 +6116,62 @@ function resolveGitBranch(run, cwd) {
|
|
|
5778
6116
|
}
|
|
5779
6117
|
return { branch: name, branchDetached: false };
|
|
5780
6118
|
}
|
|
6119
|
+
function resolveGitRoot(run, cwd) {
|
|
6120
|
+
const result = run.run("git", ["-C", cwd, "rev-parse", "--show-toplevel"]);
|
|
6121
|
+
if (result.status !== 0) {
|
|
6122
|
+
return void 0;
|
|
6123
|
+
}
|
|
6124
|
+
const root = result.stdout.trim();
|
|
6125
|
+
return root === "" ? void 0 : root;
|
|
6126
|
+
}
|
|
6127
|
+
function detachedDaemonSpawnOptions(logFd, env) {
|
|
6128
|
+
return { detached: true, stdio: ["ignore", logFd, logFd], env };
|
|
6129
|
+
}
|
|
6130
|
+
function spawnDaemonThrough(executable) {
|
|
6131
|
+
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
6132
|
+
}
|
|
6133
|
+
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
6134
|
+
import_node_fs6.default.mkdirSync(import_node_path20.default.dirname(logPath), { recursive: true });
|
|
6135
|
+
const logFd = import_node_fs6.default.openSync(logPath, "a");
|
|
6136
|
+
try {
|
|
6137
|
+
const child = (0, import_node_child_process.spawn)(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
6138
|
+
child.unref();
|
|
6139
|
+
if (child.pid === void 0) {
|
|
6140
|
+
throw new Error(`spawning ${subcommand} returned no pid`);
|
|
6141
|
+
}
|
|
6142
|
+
return child.pid;
|
|
6143
|
+
} finally {
|
|
6144
|
+
import_node_fs6.default.closeSync(logFd);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
function spawnHeadroomSupervisor(paths, spawnDaemon) {
|
|
6148
|
+
return spawnDaemon(paths, "__headroom-supervisor", paths.headroomLogPath);
|
|
6149
|
+
}
|
|
6150
|
+
function realHeadroomPort(paths, options) {
|
|
6151
|
+
return {
|
|
6152
|
+
ensure: () => {
|
|
6153
|
+
const up = ensureHeadroom({
|
|
6154
|
+
paths,
|
|
6155
|
+
launcherPid: process.pid,
|
|
6156
|
+
ports: {
|
|
6157
|
+
fs: realFarmFs,
|
|
6158
|
+
// 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.
|
|
6159
|
+
isRunning: realIsProcessRunning,
|
|
6160
|
+
now: () => Date.now(),
|
|
6161
|
+
sleep: realSleepSync,
|
|
6162
|
+
spawnSupervisor: (layout) => spawnHeadroomSupervisor(layout, options.spawnDaemon)
|
|
6163
|
+
}
|
|
6164
|
+
});
|
|
6165
|
+
return {
|
|
6166
|
+
port: up.port,
|
|
6167
|
+
projectId: resolveGitRoot(realRunPort, options.cwd) ?? options.cwd
|
|
6168
|
+
};
|
|
6169
|
+
},
|
|
6170
|
+
release: () => {
|
|
6171
|
+
removeSession(realFarmFs, paths.headroomSessionsDir, process.pid);
|
|
6172
|
+
}
|
|
6173
|
+
};
|
|
6174
|
+
}
|
|
5781
6175
|
var realProcPort = {
|
|
5782
6176
|
env: process.env,
|
|
5783
6177
|
argv: process.argv.slice(2),
|
|
@@ -5795,7 +6189,7 @@ function listVersionsDir(dir) {
|
|
|
5795
6189
|
throw error;
|
|
5796
6190
|
}
|
|
5797
6191
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
5798
|
-
const fullPath =
|
|
6192
|
+
const fullPath = import_node_path20.default.join(dir, entry.name);
|
|
5799
6193
|
const stat = import_node_fs6.default.statSync(fullPath);
|
|
5800
6194
|
return {
|
|
5801
6195
|
name: entry.name,
|
|
@@ -5810,7 +6204,7 @@ function windowsPathExtensions(pathext) {
|
|
|
5810
6204
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
5811
6205
|
}
|
|
5812
6206
|
function resolveExecutableCandidate(dir, name, env) {
|
|
5813
|
-
const candidate =
|
|
6207
|
+
const candidate = import_node_path20.default.join(dir, name);
|
|
5814
6208
|
if (env.platform === "win32") {
|
|
5815
6209
|
const extensions = windowsPathExtensions(env.pathext);
|
|
5816
6210
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -5859,13 +6253,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
5859
6253
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
5860
6254
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
5861
6255
|
}
|
|
5862
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
5863
|
-
const candidateDir =
|
|
5864
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
6256
|
+
const candidate = env.argv1 === void 0 ? env.execPath : import_node_path20.default.resolve(env.cwd, env.argv1);
|
|
6257
|
+
const candidateDir = import_node_path20.default.dirname(candidate);
|
|
6258
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => import_node_path20.default.resolve(dir) === candidateDir);
|
|
5865
6259
|
if (candidateDirOnPath) {
|
|
5866
6260
|
return candidate;
|
|
5867
6261
|
}
|
|
5868
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
6262
|
+
const viaPath = searchPathForExecutable(env.pathDirs, import_node_path20.default.basename(candidate), env.findExecutableInDir);
|
|
5869
6263
|
return viaPath ?? candidate;
|
|
5870
6264
|
}
|
|
5871
6265
|
function realOwnExecutablePath() {
|
|
@@ -5873,21 +6267,21 @@ function realOwnExecutablePath() {
|
|
|
5873
6267
|
argv1: process.argv[1],
|
|
5874
6268
|
execPath: process.execPath,
|
|
5875
6269
|
cwd: process.cwd(),
|
|
5876
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6270
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path20.default.delimiter).filter((dir) => dir !== ""),
|
|
5877
6271
|
findExecutableInDir
|
|
5878
6272
|
});
|
|
5879
6273
|
}
|
|
5880
6274
|
function realResolveClaudeBinary(ownInstallDirs) {
|
|
5881
6275
|
return () => discoverClaudeBinary({
|
|
5882
6276
|
listVersionsDir,
|
|
5883
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
6277
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path20.default.delimiter).filter((dir) => dir !== ""),
|
|
5884
6278
|
findExecutableInDir,
|
|
5885
6279
|
ownInstallDirs: [...ownInstallDirs]
|
|
5886
6280
|
});
|
|
5887
6281
|
}
|
|
5888
6282
|
|
|
5889
6283
|
// src/checkReport.ts
|
|
5890
|
-
var
|
|
6284
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
5891
6285
|
var import_zod10 = require("zod");
|
|
5892
6286
|
|
|
5893
6287
|
// src/cli/bool.ts
|
|
@@ -6076,7 +6470,8 @@ function runCheck(params) {
|
|
|
6076
6470
|
nowMs: params.nowMs,
|
|
6077
6471
|
env: params.env,
|
|
6078
6472
|
...params.branch === void 0 ? {} : { branch: params.branch },
|
|
6079
|
-
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached }
|
|
6473
|
+
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached },
|
|
6474
|
+
descend: descendPolicyFor(params.cascade, params.home)
|
|
6080
6475
|
});
|
|
6081
6476
|
const resolved = resolveDecisions({ facts, cascade: params.cascade, classification: params.classification });
|
|
6082
6477
|
const decisionLines = [...resolved.decisions.values()].slice().sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0).map(formatDecision);
|
|
@@ -6209,7 +6604,7 @@ function loadCheckProvider(paths, name) {
|
|
|
6209
6604
|
}
|
|
6210
6605
|
function collectCheckReport(params) {
|
|
6211
6606
|
const { paths, cwd } = params;
|
|
6212
|
-
const home =
|
|
6607
|
+
const home = import_node_os4.default.homedir();
|
|
6213
6608
|
const claudeHome = resolveClaudeHome();
|
|
6214
6609
|
const read = cosmiconfigReader();
|
|
6215
6610
|
const classification = loadClassification(paths);
|
|
@@ -6254,10 +6649,10 @@ function collectCheckReport(params) {
|
|
|
6254
6649
|
}).input;
|
|
6255
6650
|
const providerName = cascadeProviderName(cascade);
|
|
6256
6651
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
6257
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
6652
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : import_node_path21.default.join(paths.identitiesDir, identityDecision.name);
|
|
6258
6653
|
const settingsFiles = {
|
|
6259
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
6260
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
6654
|
+
"settings.json": realFsPort.readFileUtf8(import_node_path21.default.join(claudeHome, "settings.json")),
|
|
6655
|
+
"settings.local.json": realFsPort.readFileUtf8(import_node_path21.default.join(claudeHome, "settings.local.json"))
|
|
6261
6656
|
};
|
|
6262
6657
|
const report = runCheck({
|
|
6263
6658
|
cwd,
|
|
@@ -6287,7 +6682,7 @@ function collectCheckReport(params) {
|
|
|
6287
6682
|
|
|
6288
6683
|
// src/claudeShim.ts
|
|
6289
6684
|
var import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
6290
|
-
var
|
|
6685
|
+
var import_node_path22 = __toESM(require("node:path"), 1);
|
|
6291
6686
|
var import_zod11 = require("zod");
|
|
6292
6687
|
var ClaudeShimStateSchema = import_zod11.z.strictObject({
|
|
6293
6688
|
targetPath: import_zod11.z.string().min(1),
|
|
@@ -6298,29 +6693,29 @@ function commandFilename(ownExecutablePath, commandName) {
|
|
|
6298
6693
|
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
6299
6694
|
}
|
|
6300
6695
|
function resolveOwnInstallDirs(paths, ownExecutablePath) {
|
|
6301
|
-
const dirs = [
|
|
6696
|
+
const dirs = [import_node_path22.default.dirname(ownExecutablePath)];
|
|
6302
6697
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6303
6698
|
if (state !== void 0) {
|
|
6304
|
-
dirs.push(
|
|
6699
|
+
dirs.push(import_node_path22.default.dirname(state.targetPath));
|
|
6305
6700
|
}
|
|
6306
6701
|
return dirs;
|
|
6307
6702
|
}
|
|
6308
6703
|
var nodeLinkFs = { link: import_node_fs7.default.linkSync, copyFile: import_node_fs7.default.copyFileSync, chmod: import_node_fs7.default.chmodSync };
|
|
6309
6704
|
function findPathShadow(params) {
|
|
6310
|
-
const resolvedTargetDir =
|
|
6705
|
+
const resolvedTargetDir = import_node_path22.default.resolve(params.targetDir);
|
|
6311
6706
|
for (const dir of params.pathDirs) {
|
|
6312
6707
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
6313
6708
|
if (found === void 0) {
|
|
6314
6709
|
continue;
|
|
6315
6710
|
}
|
|
6316
|
-
return
|
|
6711
|
+
return import_node_path22.default.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
6317
6712
|
}
|
|
6318
|
-
return params.pathDirs.some((dir) =>
|
|
6713
|
+
return params.pathDirs.some((dir) => import_node_path22.default.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
6319
6714
|
}
|
|
6320
6715
|
|
|
6321
6716
|
// src/doctorReport.ts
|
|
6322
6717
|
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
6323
|
-
var
|
|
6718
|
+
var import_node_path23 = __toESM(require("node:path"), 1);
|
|
6324
6719
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
6325
6720
|
if (status.status !== "shadowed") {
|
|
6326
6721
|
return status;
|
|
@@ -6344,7 +6739,7 @@ function validateJson(schema, input) {
|
|
|
6344
6739
|
return { ok: true, data: result.data };
|
|
6345
6740
|
}
|
|
6346
6741
|
function pushPathResolution(push, resolution) {
|
|
6347
|
-
const ownDir =
|
|
6742
|
+
const ownDir = import_node_path23.default.dirname(resolution.ownExecutablePath);
|
|
6348
6743
|
switch (resolution.agentShim.status) {
|
|
6349
6744
|
case "ok":
|
|
6350
6745
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -6412,14 +6807,14 @@ function pushProvider(push, entry) {
|
|
|
6412
6807
|
}
|
|
6413
6808
|
function pushLegacyName(push, params) {
|
|
6414
6809
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
6415
|
-
if (legacyVariables.length === 0 &&
|
|
6810
|
+
if (legacyVariables.length === 0 && import_node_path23.default.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
6416
6811
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
6417
6812
|
return;
|
|
6418
6813
|
}
|
|
6419
6814
|
for (const name of legacyVariables) {
|
|
6420
6815
|
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
6816
|
}
|
|
6422
|
-
if (
|
|
6817
|
+
if (import_node_path23.default.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
6423
6818
|
push(
|
|
6424
6819
|
"legacy-name",
|
|
6425
6820
|
"warn",
|
|
@@ -6727,18 +7122,18 @@ function collectDoctorReport(params) {
|
|
|
6727
7122
|
const { paths } = params;
|
|
6728
7123
|
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
7124
|
const identities = identityNames.map((name) => {
|
|
6730
|
-
const farmRoot =
|
|
6731
|
-
const identityPath =
|
|
7125
|
+
const farmRoot = import_node_path23.default.join(paths.identitiesDir, name);
|
|
7126
|
+
const identityPath = import_node_path23.default.join(farmRoot, "identity.json");
|
|
6732
7127
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
6733
7128
|
});
|
|
6734
7129
|
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
7130
|
const configProfiles = profileNames.map((name) => {
|
|
6736
|
-
const profilePath =
|
|
7131
|
+
const profilePath = import_node_path23.default.join(paths.configProfilesDir, `${name}.json`);
|
|
6737
7132
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
6738
7133
|
});
|
|
6739
7134
|
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
7135
|
const providers = providerNames.map((name) => {
|
|
6741
|
-
const providerPath =
|
|
7136
|
+
const providerPath = import_node_path23.default.join(paths.providersDir, `${name}.json`);
|
|
6742
7137
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
6743
7138
|
});
|
|
6744
7139
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -6750,12 +7145,12 @@ function collectDoctorReport(params) {
|
|
|
6750
7145
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
6751
7146
|
}
|
|
6752
7147
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6753
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
7148
|
+
const pathDirs = (params.env.PATH ?? "").split(import_node_path23.default.delimiter).filter((dir) => dir !== "");
|
|
6754
7149
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
6755
7150
|
findPathShadow({
|
|
6756
7151
|
pathDirs,
|
|
6757
|
-
targetDir:
|
|
6758
|
-
targetFilename:
|
|
7152
|
+
targetDir: import_node_path23.default.dirname(shimState.targetPath),
|
|
7153
|
+
targetFilename: import_node_path23.default.basename(shimState.targetPath),
|
|
6759
7154
|
findExecutableInDir
|
|
6760
7155
|
}),
|
|
6761
7156
|
shimState.targetPath,
|
|
@@ -6778,7 +7173,7 @@ function collectDoctorReport(params) {
|
|
|
6778
7173
|
agentShim: refinePathShadow(
|
|
6779
7174
|
findPathShadow({
|
|
6780
7175
|
pathDirs,
|
|
6781
|
-
targetDir:
|
|
7176
|
+
targetDir: import_node_path23.default.dirname(ownExecutablePath),
|
|
6782
7177
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
6783
7178
|
findExecutableInDir
|
|
6784
7179
|
}),
|
|
@@ -6796,6 +7191,975 @@ function collectDoctorReport(params) {
|
|
|
6796
7191
|
});
|
|
6797
7192
|
return report;
|
|
6798
7193
|
}
|
|
7194
|
+
|
|
7195
|
+
// src/launcher.ts
|
|
7196
|
+
var import_node_crypto9 = require("node:crypto");
|
|
7197
|
+
var import_node_path25 = __toESM(require("node:path"), 1);
|
|
7198
|
+
|
|
7199
|
+
// src/launcher/argv.ts
|
|
7200
|
+
var ConflictingIdentityError = class extends UsageError {
|
|
7201
|
+
constructor(positional, flag) {
|
|
7202
|
+
super(`The launch names two identities: "@${positional}" and "--identity ${flag}". Pass only one.`);
|
|
7203
|
+
this.positional = positional;
|
|
7204
|
+
this.flag = flag;
|
|
7205
|
+
this.name = "ConflictingIdentityError";
|
|
7206
|
+
}
|
|
7207
|
+
positional;
|
|
7208
|
+
flag;
|
|
7209
|
+
};
|
|
7210
|
+
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--category", "--share", "--hide"];
|
|
7211
|
+
var BOOLEAN_FLAGS = [
|
|
7212
|
+
{ flag: "--headroom", key: "headroom" },
|
|
7213
|
+
{ flag: "--track-usage", key: "trackUsage" },
|
|
7214
|
+
{ flag: "--skip-permissions", key: "skipPermissions" },
|
|
7215
|
+
{ flag: "--remote-control", key: "remoteControl" },
|
|
7216
|
+
{ flag: "--wait", key: "wait" }
|
|
7217
|
+
];
|
|
7218
|
+
var LAUNCHER_FLAG_NAMES = [
|
|
7219
|
+
...VALUED_FLAGS,
|
|
7220
|
+
"--no-provider",
|
|
7221
|
+
...BOOLEAN_FLAGS.flatMap(({ flag }) => [flag, `--no-${flag.slice(2)}`])
|
|
7222
|
+
];
|
|
7223
|
+
var TERMINATOR2 = "--";
|
|
7224
|
+
function matchValuedFlag(token) {
|
|
7225
|
+
for (const flag of VALUED_FLAGS) {
|
|
7226
|
+
if (token === flag) {
|
|
7227
|
+
return { flag };
|
|
7228
|
+
}
|
|
7229
|
+
if (token.startsWith(`${flag}=`)) {
|
|
7230
|
+
return { flag, inlineValue: token.slice(flag.length + 1) };
|
|
7231
|
+
}
|
|
7232
|
+
}
|
|
7233
|
+
return void 0;
|
|
7234
|
+
}
|
|
7235
|
+
function matchBooleanFlag(token) {
|
|
7236
|
+
for (const { flag, key } of BOOLEAN_FLAGS) {
|
|
7237
|
+
if (token === flag) {
|
|
7238
|
+
return { key, value: true };
|
|
7239
|
+
}
|
|
7240
|
+
if (token === `--no-${flag.slice(2)}`) {
|
|
7241
|
+
return { key, value: false };
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
return void 0;
|
|
7245
|
+
}
|
|
7246
|
+
function parseLauncherArgv(argv) {
|
|
7247
|
+
const first = argv[0];
|
|
7248
|
+
const positionalIdentity = first !== void 0 && first.startsWith("@") && first.length > 1 ? first.slice(1) : void 0;
|
|
7249
|
+
const remaining = positionalIdentity === void 0 ? argv : argv.slice(1);
|
|
7250
|
+
let flagIdentity;
|
|
7251
|
+
let configProfile;
|
|
7252
|
+
let provider;
|
|
7253
|
+
const booleans = {};
|
|
7254
|
+
const categoryFlags = [];
|
|
7255
|
+
const shareFlags = [];
|
|
7256
|
+
const hideFlags = [];
|
|
7257
|
+
const rest = [];
|
|
7258
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
7259
|
+
const token = remaining[index];
|
|
7260
|
+
if (token === void 0) {
|
|
7261
|
+
continue;
|
|
7262
|
+
}
|
|
7263
|
+
if (token === TERMINATOR2) {
|
|
7264
|
+
rest.push(...remaining.slice(index));
|
|
7265
|
+
break;
|
|
7266
|
+
}
|
|
7267
|
+
if (token === "--no-provider") {
|
|
7268
|
+
provider = false;
|
|
7269
|
+
continue;
|
|
7270
|
+
}
|
|
7271
|
+
const booleanFlag = matchBooleanFlag(token);
|
|
7272
|
+
if (booleanFlag !== void 0) {
|
|
7273
|
+
booleans[booleanFlag.key] = booleanFlag.value;
|
|
7274
|
+
continue;
|
|
7275
|
+
}
|
|
7276
|
+
const matched = matchValuedFlag(token);
|
|
7277
|
+
if (matched === void 0) {
|
|
7278
|
+
rest.push(token);
|
|
7279
|
+
continue;
|
|
7280
|
+
}
|
|
7281
|
+
let value;
|
|
7282
|
+
if (matched.inlineValue !== void 0) {
|
|
7283
|
+
value = matched.inlineValue;
|
|
7284
|
+
} else {
|
|
7285
|
+
const next = remaining[index + 1];
|
|
7286
|
+
if (next === void 0 || next === TERMINATOR2) {
|
|
7287
|
+
rest.push(token);
|
|
7288
|
+
continue;
|
|
7289
|
+
}
|
|
7290
|
+
value = next;
|
|
7291
|
+
index += 1;
|
|
7292
|
+
}
|
|
7293
|
+
switch (matched.flag) {
|
|
7294
|
+
case "--identity":
|
|
7295
|
+
flagIdentity = value;
|
|
7296
|
+
break;
|
|
7297
|
+
case "--config-profile":
|
|
7298
|
+
configProfile = value;
|
|
7299
|
+
break;
|
|
7300
|
+
case "--provider":
|
|
7301
|
+
provider = value;
|
|
7302
|
+
break;
|
|
7303
|
+
case "--category":
|
|
7304
|
+
categoryFlags.push(value);
|
|
7305
|
+
break;
|
|
7306
|
+
case "--share":
|
|
7307
|
+
shareFlags.push(value);
|
|
7308
|
+
break;
|
|
7309
|
+
case "--hide":
|
|
7310
|
+
hideFlags.push(value);
|
|
7311
|
+
break;
|
|
7312
|
+
default:
|
|
7313
|
+
matched.flag;
|
|
7314
|
+
}
|
|
7315
|
+
}
|
|
7316
|
+
if (positionalIdentity !== void 0 && flagIdentity !== void 0 && positionalIdentity !== flagIdentity) {
|
|
7317
|
+
throw new ConflictingIdentityError(positionalIdentity, flagIdentity);
|
|
7318
|
+
}
|
|
7319
|
+
const identity = flagIdentity ?? positionalIdentity;
|
|
7320
|
+
return {
|
|
7321
|
+
...identity === void 0 ? {} : { identity },
|
|
7322
|
+
...configProfile === void 0 ? {} : { configProfile },
|
|
7323
|
+
...provider === void 0 ? {} : { provider },
|
|
7324
|
+
...booleans,
|
|
7325
|
+
categoryFlags,
|
|
7326
|
+
shareFlags,
|
|
7327
|
+
hideFlags,
|
|
7328
|
+
rest
|
|
7329
|
+
};
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7332
|
+
// src/cli/pairs.ts
|
|
7333
|
+
function splitCommas(input) {
|
|
7334
|
+
if (input === "") {
|
|
7335
|
+
return [];
|
|
7336
|
+
}
|
|
7337
|
+
return input.split(",");
|
|
7338
|
+
}
|
|
7339
|
+
function parsePair(input) {
|
|
7340
|
+
const eqIndex = input.indexOf("=");
|
|
7341
|
+
if (eqIndex === -1) {
|
|
7342
|
+
throw new Error(`Expected "<key>=<value>", got "${input}" (no "=" found)`);
|
|
7343
|
+
}
|
|
7344
|
+
const key = input.slice(0, eqIndex);
|
|
7345
|
+
const value = input.slice(eqIndex + 1);
|
|
7346
|
+
if (key === "") {
|
|
7347
|
+
throw new Error(`Expected a non-empty key before "=" in "${input}"`);
|
|
7348
|
+
}
|
|
7349
|
+
return { key, value };
|
|
7350
|
+
}
|
|
7351
|
+
function parseBool(input) {
|
|
7352
|
+
const parsed = parseBoolWord(input);
|
|
7353
|
+
if (parsed === void 0) {
|
|
7354
|
+
throw new Error(`Expected "true", "false", "1" or "0", got "${input}"`);
|
|
7355
|
+
}
|
|
7356
|
+
return parsed;
|
|
7357
|
+
}
|
|
7358
|
+
function parseBoolPairList(input) {
|
|
7359
|
+
const result = {};
|
|
7360
|
+
for (const piece of splitCommas(input)) {
|
|
7361
|
+
const { key, value } = parsePair(piece);
|
|
7362
|
+
result[key] = parseBool(value);
|
|
7363
|
+
}
|
|
7364
|
+
return result;
|
|
7365
|
+
}
|
|
7366
|
+
|
|
7367
|
+
// src/launcher/cliOverride.ts
|
|
7368
|
+
var InvalidCliCategoryError = class extends CliError {
|
|
7369
|
+
constructor(categoryName) {
|
|
7370
|
+
super(`"${categoryName}" is not a category this launch may toggle (runtime, history, knowledge, settings).`);
|
|
7371
|
+
this.categoryName = categoryName;
|
|
7372
|
+
this.name = "InvalidCliCategoryError";
|
|
7373
|
+
}
|
|
7374
|
+
categoryName;
|
|
7375
|
+
};
|
|
7376
|
+
var InvalidCliEntryKeyError = class extends CliError {
|
|
7377
|
+
constructor(key) {
|
|
7378
|
+
super(`"${key}" is not a valid entries key \u2014 it must start with "<category>/", e.g. "knowledge/skills/commit".`);
|
|
7379
|
+
this.key = key;
|
|
7380
|
+
this.name = "InvalidCliEntryKeyError";
|
|
7381
|
+
}
|
|
7382
|
+
key;
|
|
7383
|
+
};
|
|
7384
|
+
function toCategoryMap(pairs) {
|
|
7385
|
+
const expanded = expandAllCategoryKey(pairs);
|
|
7386
|
+
const result = {};
|
|
7387
|
+
for (const [key, value] of Object.entries(expanded)) {
|
|
7388
|
+
if (!isOverridableCategory(key)) {
|
|
7389
|
+
throw new InvalidCliCategoryError(key);
|
|
7390
|
+
}
|
|
7391
|
+
result[key] = value;
|
|
7392
|
+
}
|
|
7393
|
+
return result;
|
|
7394
|
+
}
|
|
7395
|
+
function toEntries(pairs) {
|
|
7396
|
+
for (const key of Object.keys(pairs)) {
|
|
7397
|
+
if (!ENTRY_KEY_RE.test(key)) {
|
|
7398
|
+
throw new InvalidCliEntryKeyError(key);
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
return pairs;
|
|
7402
|
+
}
|
|
7403
|
+
function parseOverride(source, raw, parse) {
|
|
7404
|
+
try {
|
|
7405
|
+
return parse(raw);
|
|
7406
|
+
} catch (error) {
|
|
7407
|
+
if (error instanceof Error && !(error instanceof CliError)) {
|
|
7408
|
+
throw new UsageError(`Invalid ${source} value "${raw}": ${error.message}.`);
|
|
7409
|
+
}
|
|
7410
|
+
throw error;
|
|
7411
|
+
}
|
|
7412
|
+
}
|
|
7413
|
+
function buildCliOverride(params) {
|
|
7414
|
+
let categoryPairs = {};
|
|
7415
|
+
if (params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_CATEGORY_OVERRIDE !== "") {
|
|
7416
|
+
categoryPairs = parseOverride("AGENT_SHIM_CATEGORY_OVERRIDE", params.env.AGENT_SHIM_CATEGORY_OVERRIDE, parseBoolPairList);
|
|
7417
|
+
}
|
|
7418
|
+
for (const flagValue of params.categoryFlags) {
|
|
7419
|
+
const pair = parseOverride("--category", flagValue, (raw) => {
|
|
7420
|
+
const { key, value } = parsePair(raw);
|
|
7421
|
+
return { key, value: parseBool(value) };
|
|
7422
|
+
});
|
|
7423
|
+
categoryPairs[pair.key] = pair.value;
|
|
7424
|
+
}
|
|
7425
|
+
let entryPairs = {};
|
|
7426
|
+
if (params.env.AGENT_SHIM_ENTRY_OVERRIDE !== void 0 && params.env.AGENT_SHIM_ENTRY_OVERRIDE !== "") {
|
|
7427
|
+
entryPairs = parseOverride("AGENT_SHIM_ENTRY_OVERRIDE", params.env.AGENT_SHIM_ENTRY_OVERRIDE, parseBoolPairList);
|
|
7428
|
+
}
|
|
7429
|
+
for (const entryPath of params.shareFlags) {
|
|
7430
|
+
entryPairs[entryPath] = true;
|
|
7431
|
+
}
|
|
7432
|
+
for (const entryPath of params.hideFlags) {
|
|
7433
|
+
entryPairs[entryPath] = false;
|
|
7434
|
+
}
|
|
7435
|
+
const hasCategories = Object.keys(categoryPairs).length > 0;
|
|
7436
|
+
const hasEntries = Object.keys(entryPairs).length > 0;
|
|
7437
|
+
const hasProvider = params.providerFlag !== void 0 && params.providerFlag !== "";
|
|
7438
|
+
if (!hasCategories && !hasEntries && !hasProvider) {
|
|
7439
|
+
return void 0;
|
|
7440
|
+
}
|
|
7441
|
+
return {
|
|
7442
|
+
...hasCategories ? { categories: toCategoryMap(categoryPairs) } : {},
|
|
7443
|
+
...hasEntries ? { entries: toEntries(entryPairs) } : {},
|
|
7444
|
+
...hasProvider ? { launch: { provider: params.providerFlag } } : {}
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
7447
|
+
|
|
7448
|
+
// src/launcher/flags.ts
|
|
7449
|
+
var import_node_path24 = __toESM(require("node:path"), 1);
|
|
7450
|
+
|
|
7451
|
+
// src/headroom/headers.ts
|
|
7452
|
+
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
7453
|
+
const merged = /* @__PURE__ */ new Map();
|
|
7454
|
+
for (const block of existing) {
|
|
7455
|
+
if (block === void 0) {
|
|
7456
|
+
continue;
|
|
7457
|
+
}
|
|
7458
|
+
for (const line of block.split("\n")) {
|
|
7459
|
+
const entry = parseCustomHeaderLine(line);
|
|
7460
|
+
if (entry === void 0) {
|
|
7461
|
+
continue;
|
|
7462
|
+
}
|
|
7463
|
+
merged.set(entry.name, entry.value);
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
for (const entry of additions) {
|
|
7467
|
+
merged.set(entry.name, entry.value);
|
|
7468
|
+
}
|
|
7469
|
+
return [...merged].map(([name, value]) => `${name}: ${value}`).join("\n");
|
|
7470
|
+
}
|
|
7471
|
+
function parseCustomHeaderLine(line) {
|
|
7472
|
+
const trimmed = line.trim();
|
|
7473
|
+
if (trimmed === "") {
|
|
7474
|
+
return void 0;
|
|
7475
|
+
}
|
|
7476
|
+
const colon = trimmed.indexOf(":");
|
|
7477
|
+
if (colon <= 0) {
|
|
7478
|
+
return void 0;
|
|
7479
|
+
}
|
|
7480
|
+
return { name: trimmed.slice(0, colon).trim(), value: trimmed.slice(colon + 1).trim() };
|
|
7481
|
+
}
|
|
7482
|
+
|
|
7483
|
+
// src/launcher/flags.ts
|
|
7484
|
+
function resolveLaunchFlags(params) {
|
|
7485
|
+
return {
|
|
7486
|
+
skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
|
|
7487
|
+
remoteControl: params.flags?.remoteControl ?? parseEnvBool("AGENT_SHIM_REMOTE_CONTROL", params.env.AGENT_SHIM_REMOTE_CONTROL) ?? params.cascade?.remoteControl ?? false,
|
|
7488
|
+
headroom: params.flags?.headroom ?? parseEnvBool("AGENT_SHIM_HEADROOM", params.env.AGENT_SHIM_HEADROOM) ?? params.cascade?.headroom ?? false,
|
|
7489
|
+
trackUsage: params.flags?.trackUsage ?? parseEnvBool("AGENT_SHIM_TRACK_USAGE", params.env.AGENT_SHIM_TRACK_USAGE) ?? params.cascade?.trackUsage ?? params.trackUsageDefault ?? false
|
|
7490
|
+
};
|
|
7491
|
+
}
|
|
7492
|
+
function buildFlagArgs(flags) {
|
|
7493
|
+
const args = [];
|
|
7494
|
+
if (flags.skipPermissions) {
|
|
7495
|
+
args.push("--dangerously-skip-permissions");
|
|
7496
|
+
}
|
|
7497
|
+
if (flags.remoteControl) {
|
|
7498
|
+
args.push("--remote-control=");
|
|
7499
|
+
}
|
|
7500
|
+
return args;
|
|
7501
|
+
}
|
|
7502
|
+
function buildArgv(params) {
|
|
7503
|
+
return [...params.toolFlags, ...params.extraFlags, ...params.passthrough];
|
|
7504
|
+
}
|
|
7505
|
+
var LOOPBACK_HOSTS = ["127.0.0.1", "localhost", "::1"];
|
|
7506
|
+
function isLoopbackUrl(baseUrl) {
|
|
7507
|
+
const host = new URL(baseUrl).hostname.replace(/^\[|\]$/g, "");
|
|
7508
|
+
return LOOPBACK_HOSTS.some((loopback) => loopback === host);
|
|
7509
|
+
}
|
|
7510
|
+
function withLoopbackHosts(existing) {
|
|
7511
|
+
const listed = (existing ?? "").split(",").map((entry) => entry.trim()).filter((entry) => entry !== "");
|
|
7512
|
+
return [...listed, ...LOOPBACK_HOSTS.filter((host) => !listed.includes(host))].join(",");
|
|
7513
|
+
}
|
|
7514
|
+
function buildEnv(params) {
|
|
7515
|
+
const env = { ...params.baseEnv };
|
|
7516
|
+
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7517
|
+
let providerCustomHeaders;
|
|
7518
|
+
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
7519
|
+
env.CLAUDE_CONFIG_DIR = import_node_path24.default.join(params.identitiesDir, params.resolvedIdentityName);
|
|
7520
|
+
}
|
|
7521
|
+
if (params.provider !== void 0) {
|
|
7522
|
+
const providerEnv = params.provider.definition.env ?? {};
|
|
7523
|
+
providerCustomHeaders = providerEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
7524
|
+
env.ANTHROPIC_BASE_URL = params.provider.baseUrl;
|
|
7525
|
+
if (isLoopbackUrl(params.provider.baseUrl)) {
|
|
7526
|
+
env.NO_PROXY = withLoopbackHosts(params.baseEnv.NO_PROXY);
|
|
7527
|
+
env.no_proxy = withLoopbackHosts(params.baseEnv.no_proxy);
|
|
7528
|
+
}
|
|
7529
|
+
env.AGENT_SHIM_PROVIDER = params.provider.definition.displayName;
|
|
7530
|
+
for (const [key, value] of Object.entries(providerEnv)) {
|
|
7531
|
+
env[key] = value;
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
const credential = params.provider?.credential ?? params.identityCredential;
|
|
7535
|
+
if (credential !== void 0) {
|
|
7536
|
+
for (const [variable, value] of Object.entries(credentialVariables(credential.target, credential.token))) {
|
|
7537
|
+
env[variable] = value;
|
|
7538
|
+
}
|
|
7539
|
+
}
|
|
7540
|
+
if (params.headroom !== void 0) {
|
|
7541
|
+
env.HEADROOM_PROXY_URL = `http://127.0.0.1:${String(params.headroom.port)}`;
|
|
7542
|
+
}
|
|
7543
|
+
if (params.frontdoor !== void 0) {
|
|
7544
|
+
env.ANTHROPIC_CUSTOM_HEADERS = mergeAnthropicCustomHeaders(
|
|
7545
|
+
[
|
|
7546
|
+
...parentCustomHeaders === void 0 ? [] : [parentCustomHeaders],
|
|
7547
|
+
...providerCustomHeaders === void 0 ? [] : [providerCustomHeaders]
|
|
7548
|
+
],
|
|
7549
|
+
[
|
|
7550
|
+
...params.resolvedIdentityName === void 0 ? [] : [{ name: IDENTITY_HEADER, value: params.resolvedIdentityName }],
|
|
7551
|
+
{ name: SESSION_HEADER, value: params.sessionId },
|
|
7552
|
+
{ name: AUTH_HEADER, value: params.frontdoor.sessionToken },
|
|
7553
|
+
...params.headroom === void 0 ? [] : [
|
|
7554
|
+
{ name: HEADROOM_FLAG_HEADER, value: "1" },
|
|
7555
|
+
{ name: PROJECT_ID_HEADER, value: params.headroom.projectId }
|
|
7556
|
+
]
|
|
7557
|
+
]
|
|
7558
|
+
);
|
|
7559
|
+
env.NODE_EXTRA_CA_CERTS = params.frontdoor.trustBundlePath;
|
|
7560
|
+
if (params.provider === void 0) {
|
|
7561
|
+
env.HTTPS_PROXY = connectProxyUrl(params.frontdoor.connectPort, params.frontdoor.sessionToken);
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
return env;
|
|
7565
|
+
}
|
|
7566
|
+
|
|
7567
|
+
// src/launcher/extraFlags.ts
|
|
7568
|
+
function splitExtraFlags(value) {
|
|
7569
|
+
if (value === void 0) {
|
|
7570
|
+
return [];
|
|
7571
|
+
}
|
|
7572
|
+
const trimmed = value.trim();
|
|
7573
|
+
if (trimmed === "") {
|
|
7574
|
+
return [];
|
|
7575
|
+
}
|
|
7576
|
+
return trimmed.split(/\s+/);
|
|
7577
|
+
}
|
|
7578
|
+
|
|
7579
|
+
// src/launcher.ts
|
|
7580
|
+
function resolveIdentityCredential(identity, credential, env, port) {
|
|
7581
|
+
if (port === void 0) {
|
|
7582
|
+
return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
|
|
7583
|
+
}
|
|
7584
|
+
const resolution = resolveCredential({ credential, env, port, subject: `identity ${identity}` });
|
|
7585
|
+
return resolution.ok ? resolution : { ok: false, status: CREDENTIAL_UNAVAILABLE_EXIT, message: resolution.message };
|
|
7586
|
+
}
|
|
7587
|
+
function pickFromPool(decided, params, parsedArgv) {
|
|
7588
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7589
|
+
if (decided.pool === void 0) {
|
|
7590
|
+
return { identityDecision: decided };
|
|
7591
|
+
}
|
|
7592
|
+
const farmRuntime = params.farm;
|
|
7593
|
+
if (farmRuntime === void 0) {
|
|
7594
|
+
log.error(`agent-shim: pool "${decided.pool}" was selected, but this launcher has no farm wired to read usage and pick a member with.`);
|
|
7595
|
+
return proc.exit(1);
|
|
7596
|
+
}
|
|
7597
|
+
const resolution = resolvePoolLaunch({
|
|
7598
|
+
poolName: decided.pool,
|
|
7599
|
+
selectedVia: decided.source,
|
|
7600
|
+
pools: params.pools,
|
|
7601
|
+
paths,
|
|
7602
|
+
fs: fs12,
|
|
7603
|
+
usageFs: farmRuntime.fs,
|
|
7604
|
+
cwd: farmRuntime.cwd,
|
|
7605
|
+
now: farmRuntime.now,
|
|
7606
|
+
sleep: farmRuntime.lock.sleep,
|
|
7607
|
+
passthrough: parsedArgv.rest,
|
|
7608
|
+
wait: parsedArgv.wait === true,
|
|
7609
|
+
log
|
|
7610
|
+
});
|
|
7611
|
+
if (!resolution.ok) {
|
|
7612
|
+
log.error(resolution.message);
|
|
7613
|
+
return proc.exit(1);
|
|
7614
|
+
}
|
|
7615
|
+
return { identityDecision: { ...decided, name: resolution.identity }, poolExplanation: resolution.explanation };
|
|
7616
|
+
}
|
|
7617
|
+
function prepareLaunch(params) {
|
|
7618
|
+
const { paths, fs: fs12, proc, log } = params;
|
|
7619
|
+
const { env, argv } = proc;
|
|
7620
|
+
const parsedArgv = parseLauncherArgv(argv);
|
|
7621
|
+
const cliOverride = buildCliOverride({
|
|
7622
|
+
env,
|
|
7623
|
+
categoryFlags: parsedArgv.categoryFlags,
|
|
7624
|
+
shareFlags: parsedArgv.shareFlags,
|
|
7625
|
+
hideFlags: parsedArgv.hideFlags
|
|
7626
|
+
});
|
|
7627
|
+
const configDirEscapeHatchApplies = env.CLAUDE_CONFIG_DIR !== void 0 && env.CLAUDE_CONFIG_DIR !== "";
|
|
7628
|
+
const decidedIdentity = decideIdentity({
|
|
7629
|
+
env,
|
|
7630
|
+
argv0Identity: parsedArgv.identity,
|
|
7631
|
+
directoryPinnedIdentity: params.directoryPinnedIdentity,
|
|
7632
|
+
readActiveIdentityFile: () => {
|
|
7633
|
+
const raw = fs12.readFileUtf8(paths.activeIdentityFile);
|
|
7634
|
+
if (raw === void 0) {
|
|
7635
|
+
return void 0;
|
|
7636
|
+
}
|
|
7637
|
+
const trimmed = raw.trim();
|
|
7638
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
7639
|
+
}
|
|
7640
|
+
});
|
|
7641
|
+
const { identityDecision, poolExplanation } = pickFromPool(decidedIdentity, params, parsedArgv);
|
|
7642
|
+
const farmIdentity = configDirEscapeHatchApplies ? void 0 : identityDecision.name;
|
|
7643
|
+
const farm = farmIdentity === void 0 ? void 0 : params.farm;
|
|
7644
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7645
|
+
let recovery;
|
|
7646
|
+
try {
|
|
7647
|
+
recovery = recoverFarm({
|
|
7648
|
+
fs: farm.fs,
|
|
7649
|
+
identitiesDir: paths.identitiesDir,
|
|
7650
|
+
identity: farmIdentity,
|
|
7651
|
+
now: farm.now,
|
|
7652
|
+
lock: farm.lock,
|
|
7653
|
+
classification: farm.classification
|
|
7654
|
+
});
|
|
7655
|
+
} catch (error) {
|
|
7656
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7657
|
+
log.error(error.message);
|
|
7658
|
+
proc.exit(1);
|
|
7659
|
+
}
|
|
7660
|
+
throw error;
|
|
7661
|
+
}
|
|
7662
|
+
for (const diagnostic of recoveryDiagnostics(recovery, farmIdentity)) {
|
|
7663
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7664
|
+
}
|
|
7665
|
+
}
|
|
7666
|
+
let loadedIdentity;
|
|
7667
|
+
if (identityDecision.name !== void 0) {
|
|
7668
|
+
loadedIdentity = loadIdentity(paths.identitiesDir, identityDecision.name, fs12);
|
|
7669
|
+
if (loadedIdentity === void 0) {
|
|
7670
|
+
log.error(
|
|
7671
|
+
`agent-shim: no identity named "${identityDecision.name}" (selected via ${identityDecision.source}). Run \`agent-shim identity add ${identityDecision.name}\` first.`
|
|
7672
|
+
);
|
|
7673
|
+
proc.exit(1);
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
const configProfileDecision = decideConfigProfile({
|
|
7677
|
+
env,
|
|
7678
|
+
cliFlagConfigProfile: params.cliFlagConfigProfile ?? parsedArgv.configProfile,
|
|
7679
|
+
directoryRuleConfigProfile: params.directoryRuleConfigProfile,
|
|
7680
|
+
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
7681
|
+
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
7682
|
+
});
|
|
7683
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(import_node_path25.default.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
7684
|
+
log.error(
|
|
7685
|
+
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
7686
|
+
);
|
|
7687
|
+
proc.exit(1);
|
|
7688
|
+
}
|
|
7689
|
+
const farmContext = farm !== void 0 && farmIdentity !== void 0 ? { farm, identity: farmIdentity, cascade: farm.loadCascade(configProfileDecision.name, cliOverride) } : void 0;
|
|
7690
|
+
const provider = parsedArgv.provider === false ? void 0 : resolveProvider({
|
|
7691
|
+
paths,
|
|
7692
|
+
port: fs12,
|
|
7693
|
+
env,
|
|
7694
|
+
...params.credentials === void 0 ? {} : { credentials: params.credentials },
|
|
7695
|
+
...parsedArgv.provider === void 0 ? {} : { cliProvider: parsedArgv.provider },
|
|
7696
|
+
...farmContext === void 0 ? {} : { cascade: farmContext.cascade }
|
|
7697
|
+
});
|
|
7698
|
+
if (provider !== void 0 && !provider.ok) {
|
|
7699
|
+
log.error(provider.message);
|
|
7700
|
+
proc.exit(provider.status);
|
|
7701
|
+
}
|
|
7702
|
+
const resolvedProvider = provider?.ok === true ? provider.provider : void 0;
|
|
7703
|
+
for (const warning of provider?.ok === true ? provider.warnings : []) {
|
|
7704
|
+
log.warn(warning);
|
|
7705
|
+
}
|
|
7706
|
+
if (farm !== void 0 && farmIdentity !== void 0) {
|
|
7707
|
+
try {
|
|
7708
|
+
const snapshot = readUsageSnapshot(farm.fs, paths.usageSnapshotsDir, farmIdentity);
|
|
7709
|
+
for (const warning of quotaWarnings(snapshot, resolvedProvider?.name ?? ANTHROPIC_PROVIDER, farm.now())) {
|
|
7710
|
+
log.warn(warning);
|
|
7711
|
+
}
|
|
7712
|
+
} catch (error) {
|
|
7713
|
+
if (!(error instanceof UsageSnapshotError)) {
|
|
7714
|
+
throw error;
|
|
7715
|
+
}
|
|
7716
|
+
log.warn(`agent-shim: ${error.message}`);
|
|
7717
|
+
}
|
|
7718
|
+
}
|
|
7719
|
+
const identityCredentialBlock = loadedIdentity?.config.credential;
|
|
7720
|
+
const identityResolution = resolvedProvider === void 0 && farmIdentity !== void 0 && identityCredentialBlock !== void 0 ? resolveIdentityCredential(farmIdentity, identityCredentialBlock, env, params.credentials) : void 0;
|
|
7721
|
+
if (identityResolution !== void 0 && !identityResolution.ok) {
|
|
7722
|
+
log.error(identityResolution.message);
|
|
7723
|
+
proc.exit(identityResolution.status);
|
|
7724
|
+
}
|
|
7725
|
+
const identityCredential = identityResolution?.ok === true ? identityResolution.credential : void 0;
|
|
7726
|
+
for (const warning of identityCredential?.warnings ?? []) {
|
|
7727
|
+
log.warn(warning);
|
|
7728
|
+
}
|
|
7729
|
+
const guardResult = evaluateAmbientCredentialGuard({
|
|
7730
|
+
env,
|
|
7731
|
+
allowAmbientCredential: loadedIdentity?.config.allowAmbientCredential ?? false,
|
|
7732
|
+
allowAmbientCredentialOverride: parseEnvBool("AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL", env.AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL) === true,
|
|
7733
|
+
identityName: identityDecision.name,
|
|
7734
|
+
providerSelected: resolvedProvider !== void 0,
|
|
7735
|
+
...identityCredential === void 0 ? {} : { injectedCredential: { variable: CREDENTIAL_TARGET_VARS[identityCredential.target], token: identityCredential.token } }
|
|
7736
|
+
});
|
|
7737
|
+
if (!guardResult.ok) {
|
|
7738
|
+
log.error(guardResult.message);
|
|
7739
|
+
proc.exit(1);
|
|
7740
|
+
}
|
|
7741
|
+
log.info(
|
|
7742
|
+
`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)}`)
|
|
7743
|
+
);
|
|
7744
|
+
let cascadeLaunch;
|
|
7745
|
+
if (farmContext !== void 0) {
|
|
7746
|
+
const { farm: resyncFarmRuntime, identity: resyncIdentity, cascade } = farmContext;
|
|
7747
|
+
let result;
|
|
7748
|
+
try {
|
|
7749
|
+
result = resyncFarm({
|
|
7750
|
+
fs: resyncFarmRuntime.fs,
|
|
7751
|
+
identitiesDir: paths.identitiesDir,
|
|
7752
|
+
identity: resyncIdentity,
|
|
7753
|
+
...configProfileDecision.name === void 0 ? {} : { configProfile: configProfileDecision.name },
|
|
7754
|
+
claudeHome: resyncFarmRuntime.claudeHome,
|
|
7755
|
+
home: resyncFarmRuntime.home,
|
|
7756
|
+
cwd: resyncFarmRuntime.cwd,
|
|
7757
|
+
env,
|
|
7758
|
+
...resyncFarmRuntime.branch === void 0 ? {} : { branch: resyncFarmRuntime.branch },
|
|
7759
|
+
...resyncFarmRuntime.branchDetached === void 0 ? {} : { branchDetached: resyncFarmRuntime.branchDetached },
|
|
7760
|
+
cascade,
|
|
7761
|
+
classification: resyncFarmRuntime.classification,
|
|
7762
|
+
now: resyncFarmRuntime.now,
|
|
7763
|
+
uniqueSuffix: resyncFarmRuntime.uniqueSuffix,
|
|
7764
|
+
lock: resyncFarmRuntime.lock
|
|
7765
|
+
});
|
|
7766
|
+
} catch (error) {
|
|
7767
|
+
if (error instanceof IdentityLockBusyError) {
|
|
7768
|
+
log.error(error.message);
|
|
7769
|
+
proc.exit(1);
|
|
7770
|
+
}
|
|
7771
|
+
throw error;
|
|
7772
|
+
}
|
|
7773
|
+
for (const diagnostic of result.diagnostics) {
|
|
7774
|
+
if (diagnostic.severity === "error") {
|
|
7775
|
+
log.error(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7776
|
+
} else if (diagnostic.severity === "warning") {
|
|
7777
|
+
log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
|
|
7778
|
+
}
|
|
7779
|
+
}
|
|
7780
|
+
log.info(
|
|
7781
|
+
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}`})`
|
|
7782
|
+
);
|
|
7783
|
+
cascadeLaunch = result.resolved.flattened.launch;
|
|
7784
|
+
} else if (params.farm !== void 0) {
|
|
7785
|
+
cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
|
|
7786
|
+
}
|
|
7787
|
+
const discovered = params.resolveClaudeBinary();
|
|
7788
|
+
const resolvedFlags = resolveLaunchFlags({
|
|
7789
|
+
env,
|
|
7790
|
+
// A pool pick reads the usage the front door records, so a pool launch records its own unless something says otherwise.
|
|
7791
|
+
...poolExplanation === void 0 ? {} : { trackUsageDefault: true },
|
|
7792
|
+
...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch },
|
|
7793
|
+
flags: {
|
|
7794
|
+
...parsedArgv.skipPermissions === void 0 ? {} : { skipPermissions: parsedArgv.skipPermissions },
|
|
7795
|
+
...parsedArgv.remoteControl === void 0 ? {} : { remoteControl: parsedArgv.remoteControl },
|
|
7796
|
+
...parsedArgv.headroom === void 0 ? {} : { headroom: parsedArgv.headroom },
|
|
7797
|
+
...parsedArgv.trackUsage === void 0 ? {} : { trackUsage: parsedArgv.trackUsage }
|
|
7798
|
+
}
|
|
7799
|
+
});
|
|
7800
|
+
const frontDoorPort = params.frontdoor;
|
|
7801
|
+
const frontDoorEngaged = resolvedProvider !== void 0 || resolvedFlags.headroom || resolvedFlags.trackUsage;
|
|
7802
|
+
if (frontDoorEngaged && frontDoorPort === void 0) {
|
|
7803
|
+
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.");
|
|
7804
|
+
proc.exit(1);
|
|
7805
|
+
}
|
|
7806
|
+
const frontDoor = frontDoorEngaged && frontDoorPort !== void 0 ? frontDoorPort.ensure(env.NODE_EXTRA_CA_CERTS) : void 0;
|
|
7807
|
+
if (frontDoor?.trustWarning !== void 0) {
|
|
7808
|
+
log.warn(frontDoor.trustWarning);
|
|
7809
|
+
}
|
|
7810
|
+
let routedProvider;
|
|
7811
|
+
if (resolvedProvider !== void 0 && frontDoor !== void 0) {
|
|
7812
|
+
routedProvider = { ...resolvedProvider, baseUrl: providerBaseUrl(frontDoor.port, resolvedProvider.name) };
|
|
7813
|
+
}
|
|
7814
|
+
let headroom;
|
|
7815
|
+
const headroomPort = params.headroom;
|
|
7816
|
+
if (resolvedFlags.headroom && headroomPort !== void 0) {
|
|
7817
|
+
headroom = headroomPort.ensure();
|
|
7818
|
+
const via = resolvedProvider === void 0 ? `OAuth via the door's CONNECT surface on 127.0.0.1:${String(frontDoor?.connectPort ?? 0)}` : `provider ${resolvedProvider.name}`;
|
|
7819
|
+
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})`);
|
|
7820
|
+
} else if (resolvedFlags.headroom) {
|
|
7821
|
+
log.error("agent-shim: headroom routing was requested but this launcher has no headroom port wired; refusing to launch without it.");
|
|
7822
|
+
proc.exit(1);
|
|
7823
|
+
} else if (frontDoor !== void 0) {
|
|
7824
|
+
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})`}`);
|
|
7825
|
+
}
|
|
7826
|
+
const frontDoorRelease = frontDoor === void 0 ? void 0 : frontDoorPort?.release;
|
|
7827
|
+
const finalArgv = buildArgv({
|
|
7828
|
+
toolFlags: buildFlagArgs(resolvedFlags),
|
|
7829
|
+
extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
|
|
7830
|
+
passthrough: parsedArgv.rest
|
|
7831
|
+
});
|
|
7832
|
+
const finalEnv = buildEnv({
|
|
7833
|
+
baseEnv: env,
|
|
7834
|
+
configDirEscapeHatch: configDirEscapeHatchApplies,
|
|
7835
|
+
resolvedIdentityName: identityDecision.name,
|
|
7836
|
+
identitiesDir: paths.identitiesDir,
|
|
7837
|
+
...routedProvider === void 0 ? {} : { provider: routedProvider },
|
|
7838
|
+
...identityCredential === void 0 ? {} : { identityCredential },
|
|
7839
|
+
...frontDoor === void 0 ? {} : { frontdoor: frontDoor },
|
|
7840
|
+
...headroom === void 0 ? {} : { headroom },
|
|
7841
|
+
sessionId: (0, import_node_crypto9.randomUUID)()
|
|
7842
|
+
});
|
|
7843
|
+
const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
|
|
7844
|
+
const release = () => {
|
|
7845
|
+
for (const registered of releases) {
|
|
7846
|
+
registered();
|
|
7847
|
+
}
|
|
7848
|
+
};
|
|
7849
|
+
return { bin: discovered.path, args: finalArgv, env: finalEnv, release };
|
|
7850
|
+
}
|
|
7851
|
+
|
|
7852
|
+
// src/launchWiring.ts
|
|
7853
|
+
var import_node_os5 = __toESM(require("node:os"), 1);
|
|
7854
|
+
var import_node_crypto11 = require("node:crypto");
|
|
7855
|
+
|
|
7856
|
+
// src/realCredentialCache.ts
|
|
7857
|
+
var import_node_child_process2 = require("node:child_process");
|
|
7858
|
+
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
7859
|
+
var import_node_path26 = __toESM(require("node:path"), 1);
|
|
7860
|
+
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7861
|
+
var CACHE_DIR_MODE = 448;
|
|
7862
|
+
var CACHE_FILE_MODE = 384;
|
|
7863
|
+
function cacheFileNameFor(owner) {
|
|
7864
|
+
return `${owner.replace(/[^A-Za-z0-9._-]/g, "_")}.json`;
|
|
7865
|
+
}
|
|
7866
|
+
function parseEntry(raw) {
|
|
7867
|
+
let json;
|
|
7868
|
+
try {
|
|
7869
|
+
json = JSON.parse(raw);
|
|
7870
|
+
} catch {
|
|
7871
|
+
return void 0;
|
|
7872
|
+
}
|
|
7873
|
+
const parsed = CachedCredentialSchema.safeParse(json);
|
|
7874
|
+
return parsed.success ? parsed.data : void 0;
|
|
7875
|
+
}
|
|
7876
|
+
function requireKeychain() {
|
|
7877
|
+
if (process.platform !== "darwin") {
|
|
7878
|
+
throw new Error("the keychain credential store needs macOS; use --credential-cache-store file");
|
|
7879
|
+
}
|
|
7880
|
+
}
|
|
7881
|
+
function security(args, stdin) {
|
|
7882
|
+
const result = (0, import_node_child_process2.spawnSync)("security", args, { encoding: "utf8", ...stdin === void 0 ? {} : { input: stdin }, stdio: ["pipe", "pipe", "ignore"] });
|
|
7883
|
+
return { status: result.status, stdout: result.stdout };
|
|
7884
|
+
}
|
|
7885
|
+
function createRealCredentialCache(root) {
|
|
7886
|
+
const dir = import_node_path26.default.join(root, "credential-cache");
|
|
7887
|
+
const filePath = (owner) => import_node_path26.default.join(dir, cacheFileNameFor(owner));
|
|
7888
|
+
return {
|
|
7889
|
+
read(store, owner) {
|
|
7890
|
+
if (store === "file") {
|
|
7891
|
+
try {
|
|
7892
|
+
return parseEntry(import_node_fs9.default.readFileSync(filePath(owner), "utf8"));
|
|
7893
|
+
} catch {
|
|
7894
|
+
return void 0;
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7897
|
+
requireKeychain();
|
|
7898
|
+
const result = security(["find-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner, "-w"]);
|
|
7899
|
+
return result.status === 0 ? parseEntry(Buffer.from(result.stdout.trim(), "base64").toString("utf8")) : void 0;
|
|
7900
|
+
},
|
|
7901
|
+
write(store, owner, entry) {
|
|
7902
|
+
const serialised = JSON.stringify(entry);
|
|
7903
|
+
if (store === "file") {
|
|
7904
|
+
import_node_fs9.default.mkdirSync(dir, { recursive: true, mode: CACHE_DIR_MODE });
|
|
7905
|
+
import_node_fs9.default.chmodSync(dir, CACHE_DIR_MODE);
|
|
7906
|
+
const target = filePath(owner);
|
|
7907
|
+
const temp = `${target}.${String(process.pid)}.tmp`;
|
|
7908
|
+
import_node_fs9.default.writeFileSync(temp, serialised, { mode: CACHE_FILE_MODE });
|
|
7909
|
+
import_node_fs9.default.renameSync(temp, target);
|
|
7910
|
+
return;
|
|
7911
|
+
}
|
|
7912
|
+
requireKeychain();
|
|
7913
|
+
const encoded = Buffer.from(serialised, "utf8").toString("base64");
|
|
7914
|
+
const result = security(["-i"], `add-generic-password -U -s ${KEYCHAIN_CACHE_SERVICE} -a ${JSON.stringify(owner)} -w ${encoded}
|
|
7915
|
+
`);
|
|
7916
|
+
if (result.status !== 0) {
|
|
7917
|
+
throw new Error(`could not write the keychain credential cache for ${owner}`);
|
|
7918
|
+
}
|
|
7919
|
+
},
|
|
7920
|
+
remove(store, owner) {
|
|
7921
|
+
if (store === "file") {
|
|
7922
|
+
import_node_fs9.default.rmSync(filePath(owner), { force: true });
|
|
7923
|
+
return;
|
|
7924
|
+
}
|
|
7925
|
+
requireKeychain();
|
|
7926
|
+
security(["delete-generic-password", "-s", KEYCHAIN_CACHE_SERVICE, "-a", owner]);
|
|
7927
|
+
}
|
|
7928
|
+
};
|
|
7929
|
+
}
|
|
7930
|
+
function realCredentialCacheEnv(paths) {
|
|
7931
|
+
return { port: createRealCredentialCache(paths.root), platform: process.platform, now: () => Date.now() };
|
|
7932
|
+
}
|
|
7933
|
+
|
|
7934
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
7935
|
+
var import_node_fs10 = __toESM(require("node:fs"), 1);
|
|
7936
|
+
|
|
7937
|
+
// src/frontdoor/probe.ts
|
|
7938
|
+
var import_node_worker_threads = require("node:worker_threads");
|
|
7939
|
+
var PROBE_TIMEOUT_MS = 2e3;
|
|
7940
|
+
var PROBE_WAIT_MARGIN_MS = 1e3;
|
|
7941
|
+
var REASON_BYTES = 1024;
|
|
7942
|
+
var HEADER_SLOTS = 2;
|
|
7943
|
+
var INT32_BYTES = 4;
|
|
7944
|
+
var VERDICT_PENDING = 0;
|
|
7945
|
+
var VERDICT_OK = 1;
|
|
7946
|
+
var VERDICT_FAILED = 2;
|
|
7947
|
+
var PROBE_SOURCE = `
|
|
7948
|
+
const { workerData } = require("node:worker_threads");
|
|
7949
|
+
const https = require("node:https");
|
|
7950
|
+
const { port, ca, timeoutMs, shared, ok, failed, headerSlots } = workerData;
|
|
7951
|
+
const header = new Int32Array(shared, 0, headerSlots);
|
|
7952
|
+
const reason = new Uint8Array(shared, headerSlots * 4);
|
|
7953
|
+
let done = false;
|
|
7954
|
+
const finish = (verdict, message) => {
|
|
7955
|
+
if (done) return;
|
|
7956
|
+
done = true;
|
|
7957
|
+
const bytes = Buffer.from(String(message), "utf8").subarray(0, reason.length);
|
|
7958
|
+
reason.set(bytes);
|
|
7959
|
+
Atomics.store(header, 1, bytes.length);
|
|
7960
|
+
Atomics.store(header, 0, verdict);
|
|
7961
|
+
Atomics.notify(header, 0);
|
|
7962
|
+
};
|
|
7963
|
+
const request = https.get({ host: "127.0.0.1", port, path: "/healthz", ca: [ca], agent: false }, (response) => {
|
|
7964
|
+
let body = "";
|
|
7965
|
+
response.setEncoding("utf8");
|
|
7966
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
7967
|
+
response.on("end", () => {
|
|
7968
|
+
if (response.statusCode === 200 && body === "ok") finish(ok, "");
|
|
7969
|
+
else finish(failed, "the listener answered its health probe with HTTP " + String(response.statusCode));
|
|
7970
|
+
});
|
|
7971
|
+
response.on("error", (error) => finish(failed, error.message));
|
|
7972
|
+
});
|
|
7973
|
+
// A hard deadline over the whole exchange, handshake included: a listener that accepts the connection and then says nothing must still fail the probe.
|
|
7974
|
+
setTimeout(() => { request.destroy(new Error("no answer within " + String(timeoutMs) + "ms")); }, timeoutMs);
|
|
7975
|
+
request.on("error", (error) => finish(failed, (error.code ? error.code + ": " : "") + error.message));
|
|
7976
|
+
`;
|
|
7977
|
+
function probeFrontDoorSync(port, caPem) {
|
|
7978
|
+
const shared = new SharedArrayBuffer(HEADER_SLOTS * INT32_BYTES + REASON_BYTES);
|
|
7979
|
+
const header = new Int32Array(shared, 0, HEADER_SLOTS);
|
|
7980
|
+
const worker = new import_node_worker_threads.Worker(PROBE_SOURCE, {
|
|
7981
|
+
eval: true,
|
|
7982
|
+
workerData: { port, ca: caPem, timeoutMs: PROBE_TIMEOUT_MS, shared, ok: VERDICT_OK, failed: VERDICT_FAILED, headerSlots: HEADER_SLOTS }
|
|
7983
|
+
});
|
|
7984
|
+
worker.unref();
|
|
7985
|
+
try {
|
|
7986
|
+
const waited = Atomics.wait(header, 0, VERDICT_PENDING, PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS);
|
|
7987
|
+
const verdict = Atomics.load(header, 0);
|
|
7988
|
+
if (waited === "timed-out" || verdict === VERDICT_PENDING) {
|
|
7989
|
+
return { ok: false, reason: `the probe did not finish within ${String(PROBE_TIMEOUT_MS + PROBE_WAIT_MARGIN_MS)}ms` };
|
|
7990
|
+
}
|
|
7991
|
+
if (verdict === VERDICT_OK) {
|
|
7992
|
+
return { ok: true };
|
|
7993
|
+
}
|
|
7994
|
+
const length = Atomics.load(header, 1);
|
|
7995
|
+
return { ok: false, reason: Buffer.from(new Uint8Array(shared, HEADER_SLOTS * INT32_BYTES, length)).toString("utf8") };
|
|
7996
|
+
} finally {
|
|
7997
|
+
void worker.terminate();
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
|
|
8001
|
+
// src/frontdoor/trust.ts
|
|
8002
|
+
var import_node_crypto10 = require("node:crypto");
|
|
8003
|
+
var import_node_path27 = __toESM(require("node:path"), 1);
|
|
8004
|
+
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
8005
|
+
function resolveTrustBundle(params) {
|
|
8006
|
+
const { caCertFile, bundlesDir, inherited, fs: fs12 } = params;
|
|
8007
|
+
if (inherited === void 0 || inherited === "" || import_node_path27.default.resolve(inherited) === import_node_path27.default.resolve(caCertFile)) {
|
|
8008
|
+
return { path: caCertFile };
|
|
8009
|
+
}
|
|
8010
|
+
const caPem = fs12.readFileUtf8(caCertFile);
|
|
8011
|
+
let inheritedPem;
|
|
8012
|
+
try {
|
|
8013
|
+
inheritedPem = fs12.readFileUtf8(inherited);
|
|
8014
|
+
} catch (error) {
|
|
8015
|
+
return {
|
|
8016
|
+
path: caCertFile,
|
|
8017
|
+
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`
|
|
8018
|
+
};
|
|
8019
|
+
}
|
|
8020
|
+
if (inheritedPem.includes(caPem.trim())) {
|
|
8021
|
+
return { path: inherited };
|
|
8022
|
+
}
|
|
8023
|
+
const combined = `${inheritedPem.trimEnd()}
|
|
8024
|
+
${caPem}`;
|
|
8025
|
+
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`);
|
|
8026
|
+
if (!fs12.exists(bundle)) {
|
|
8027
|
+
fs12.mkdirp(bundlesDir);
|
|
8028
|
+
fs12.writeFileAtomic(bundle, combined);
|
|
8029
|
+
}
|
|
8030
|
+
return { path: bundle };
|
|
8031
|
+
}
|
|
8032
|
+
|
|
8033
|
+
// src/frontdoor/realFrontDoorPort.ts
|
|
8034
|
+
var realTrustBundleFs = {
|
|
8035
|
+
readFileUtf8: (file) => import_node_fs10.default.readFileSync(file, "utf8"),
|
|
8036
|
+
exists: (file) => import_node_fs10.default.existsSync(file),
|
|
8037
|
+
mkdirp: (dir) => {
|
|
8038
|
+
import_node_fs10.default.mkdirSync(dir, { recursive: true });
|
|
8039
|
+
},
|
|
8040
|
+
writeFileAtomic: (file, contents) => {
|
|
8041
|
+
realFarmFs.writeFilePrivate(file, contents);
|
|
8042
|
+
}
|
|
8043
|
+
};
|
|
8044
|
+
function realFrontDoorPort(paths, spawnDaemon) {
|
|
8045
|
+
return {
|
|
8046
|
+
ensure: (inheritedExtraCaCerts) => {
|
|
8047
|
+
const up = ensureFrontDoor({
|
|
8048
|
+
paths,
|
|
8049
|
+
launcherPid: process.pid,
|
|
8050
|
+
ports: {
|
|
8051
|
+
fs: realFarmFs,
|
|
8052
|
+
isRunning: realIsProcessRunning,
|
|
8053
|
+
now: () => Date.now(),
|
|
8054
|
+
sleep: realSleepSync,
|
|
8055
|
+
spawnSupervisor: (layout) => spawnDaemon(layout, "__frontdoor-supervisor", layout.frontdoorLogPath),
|
|
8056
|
+
stopSupervisor: (pid) => {
|
|
8057
|
+
process.kill(pid, "SIGTERM");
|
|
8058
|
+
},
|
|
8059
|
+
// 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.
|
|
8060
|
+
verifyListener: (port) => probeFrontDoorSync(port, import_node_fs10.default.readFileSync(paths.frontdoorCaCertFile, "utf8"))
|
|
8061
|
+
}
|
|
8062
|
+
});
|
|
8063
|
+
const trust = resolveTrustBundle({ caCertFile: paths.frontdoorCaCertFile, bundlesDir: paths.frontdoorCaBundlesDir, inherited: inheritedExtraCaCerts, fs: realTrustBundleFs });
|
|
8064
|
+
return {
|
|
8065
|
+
port: up.port,
|
|
8066
|
+
connectPort: up.connectPort,
|
|
8067
|
+
trustBundlePath: trust.path,
|
|
8068
|
+
...trust.warning === void 0 ? {} : { trustWarning: trust.warning },
|
|
8069
|
+
sessionToken: up.token
|
|
8070
|
+
};
|
|
8071
|
+
},
|
|
8072
|
+
release: () => {
|
|
8073
|
+
removeFrontDoorSession(realFarmFs, paths.frontdoorSessionsDir, process.pid);
|
|
8074
|
+
}
|
|
8075
|
+
};
|
|
8076
|
+
}
|
|
8077
|
+
|
|
8078
|
+
// src/launchWiring.ts
|
|
8079
|
+
function buildFarmRuntime(paths, cwd) {
|
|
8080
|
+
const home = import_node_os5.default.homedir();
|
|
8081
|
+
const read = cosmiconfigReader();
|
|
8082
|
+
const classification = loadClassification(paths);
|
|
8083
|
+
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
8084
|
+
const selections = readDirectorySelections(loaded);
|
|
8085
|
+
const git = resolveGitBranch(realRunPort, cwd);
|
|
8086
|
+
return {
|
|
8087
|
+
runtime: {
|
|
8088
|
+
fs: realFarmFs,
|
|
8089
|
+
claudeHome: resolveClaudeHome(),
|
|
8090
|
+
home,
|
|
8091
|
+
cwd,
|
|
8092
|
+
...git.branch === void 0 ? {} : { branch: git.branch },
|
|
8093
|
+
...git.branchDetached === void 0 ? {} : { branchDetached: git.branchDetached },
|
|
8094
|
+
classification,
|
|
8095
|
+
loadCascade: (baseConfigProfile, cliOverride) => loadCascadeInput({
|
|
8096
|
+
paths,
|
|
8097
|
+
home,
|
|
8098
|
+
cwd,
|
|
8099
|
+
read,
|
|
8100
|
+
...baseConfigProfile === void 0 ? {} : { baseConfigProfile },
|
|
8101
|
+
...cliOverride === void 0 ? {} : { cliOverride }
|
|
8102
|
+
}).input,
|
|
8103
|
+
now: () => Date.now(),
|
|
8104
|
+
uniqueSuffix: `${String(process.pid)}.${(0, import_node_crypto11.randomUUID)()}`,
|
|
8105
|
+
// 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.
|
|
8106
|
+
lock: { pid: process.pid, isRunning: realIsProcessRunning, sleep: realSleepSync }
|
|
8107
|
+
},
|
|
8108
|
+
...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
|
|
8109
|
+
...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
|
|
8110
|
+
...loaded.globalConfig?.defaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: loaded.globalConfig.defaultConfigProfile },
|
|
8111
|
+
...loaded.globalConfig?.pools === void 0 ? {} : { pools: loaded.globalConfig.pools }
|
|
8112
|
+
};
|
|
8113
|
+
}
|
|
8114
|
+
function realPrepareLaunchParams(paths, options) {
|
|
8115
|
+
const { farm } = options;
|
|
8116
|
+
return {
|
|
8117
|
+
paths,
|
|
8118
|
+
fs: realFsPort,
|
|
8119
|
+
proc: options.proc,
|
|
8120
|
+
log: options.log,
|
|
8121
|
+
resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
|
|
8122
|
+
farm: farm.runtime,
|
|
8123
|
+
headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
|
|
8124
|
+
frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
|
|
8125
|
+
credentials: { ...realCredentialPort, cache: realCredentialCacheEnv(paths) },
|
|
8126
|
+
...farm.directoryIdentity === void 0 ? {} : { directoryPinnedIdentity: farm.directoryIdentity },
|
|
8127
|
+
...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
|
|
8128
|
+
...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
|
|
8129
|
+
...farm.pools === void 0 ? {} : { pools: farm.pools },
|
|
8130
|
+
...options.allowMissingConfigProfile === void 0 ? {} : { allowMissingConfigProfile: options.allowMissingConfigProfile }
|
|
8131
|
+
};
|
|
8132
|
+
}
|
|
8133
|
+
var LaunchRefusedError = class extends CliError {
|
|
8134
|
+
exitCode;
|
|
8135
|
+
constructor(message, exitCode) {
|
|
8136
|
+
super(message);
|
|
8137
|
+
this.name = "LaunchRefusedError";
|
|
8138
|
+
this.exitCode = exitCode;
|
|
8139
|
+
}
|
|
8140
|
+
};
|
|
8141
|
+
function prepareClaudeLaunch(options) {
|
|
8142
|
+
const paths = options.paths ?? resolveLayoutPaths();
|
|
8143
|
+
const refusals = [];
|
|
8144
|
+
const log = {
|
|
8145
|
+
info: () => void 0,
|
|
8146
|
+
warn: () => void 0,
|
|
8147
|
+
error: (message) => {
|
|
8148
|
+
refusals.push(message);
|
|
8149
|
+
}
|
|
8150
|
+
};
|
|
8151
|
+
const proc = {
|
|
8152
|
+
env: options.env,
|
|
8153
|
+
argv: options.argv,
|
|
8154
|
+
exit: (code) => {
|
|
8155
|
+
throw new LaunchRefusedError(refusals.join("\n") || `the launch was refused (exit ${String(code)})`, code);
|
|
8156
|
+
}
|
|
8157
|
+
};
|
|
8158
|
+
const spawnDaemon = options.agentShim === void 0 ? () => {
|
|
8159
|
+
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);
|
|
8160
|
+
} : spawnDaemonThrough(options.agentShim);
|
|
8161
|
+
return prepareLaunch(realPrepareLaunchParams(paths, { proc, log, spawnDaemon, farm: buildFarmRuntime(paths, options.cwd) }));
|
|
8162
|
+
}
|
|
6799
8163
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6800
8164
|
0 && (module.exports = {
|
|
6801
8165
|
AMBIENT_CREDENTIAL_VARS,
|
|
@@ -6828,6 +8192,7 @@ function collectDoctorReport(params) {
|
|
|
6828
8192
|
InvalidCategoryNameError,
|
|
6829
8193
|
InvalidIdentityNameError,
|
|
6830
8194
|
InvalidProviderNameError,
|
|
8195
|
+
LaunchRefusedError,
|
|
6831
8196
|
LegacyProviderFileError,
|
|
6832
8197
|
PoolNameSchema,
|
|
6833
8198
|
PoolNotFoundError,
|
|
@@ -6878,6 +8243,8 @@ function collectDoctorReport(params) {
|
|
|
6878
8243
|
listUsageSnapshots,
|
|
6879
8244
|
mintLeaf,
|
|
6880
8245
|
parseConnectTarget,
|
|
8246
|
+
prepareClaudeLaunch,
|
|
8247
|
+
prepareLaunch,
|
|
6881
8248
|
profileExists,
|
|
6882
8249
|
providerExists,
|
|
6883
8250
|
readActiveIdentity,
|