agent-shim 7.3.0 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.cjs +29 -23
- package/dist/index.cjs +229 -100
- package/dist/index.mjs +203 -100
- package/dist/types/config/schema.d.ts +1 -1
- package/dist/types/frontdoor/connect.d.ts +17 -9
- package/dist/types/index.d.ts +6 -2
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -248,10 +248,18 @@ import { cosmiconfigSync } from "cosmiconfig";
|
|
|
248
248
|
|
|
249
249
|
// src/cliError.ts
|
|
250
250
|
var EXIT_FAILURE = 1;
|
|
251
|
+
var EXIT_USAGE = 2;
|
|
251
252
|
var CliError = class extends Error {
|
|
252
253
|
/** The process exit status this failure maps to. `EXIT_FAILURE` unless a subclass says otherwise. */
|
|
253
254
|
exitCode = EXIT_FAILURE;
|
|
254
255
|
};
|
|
256
|
+
var UsageError = class extends CliError {
|
|
257
|
+
exitCode = EXIT_USAGE;
|
|
258
|
+
constructor(message) {
|
|
259
|
+
super(message);
|
|
260
|
+
this.name = "UsageError";
|
|
261
|
+
}
|
|
262
|
+
};
|
|
255
263
|
|
|
256
264
|
// src/config/classify.ts
|
|
257
265
|
var GLOB_METACHARACTERS = /[*?[\]{}!()]/;
|
|
@@ -1212,6 +1220,8 @@ function planReconciliation(params) {
|
|
|
1212
1220
|
}
|
|
1213
1221
|
|
|
1214
1222
|
// src/legacy.ts
|
|
1223
|
+
var HOME_DIRNAME = ".agent-shim";
|
|
1224
|
+
var LEGACY_HOME_DIRNAME = ".claude-use";
|
|
1215
1225
|
var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
|
|
1216
1226
|
|
|
1217
1227
|
// src/launcher/lock.ts
|
|
@@ -1313,8 +1323,8 @@ var FarmManifestSchema = z2.strictObject({
|
|
|
1313
1323
|
materialised: z2.array(z2.string()),
|
|
1314
1324
|
links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
|
|
1315
1325
|
});
|
|
1316
|
-
function readFarmManifest(
|
|
1317
|
-
const raw =
|
|
1326
|
+
function readFarmManifest(fs3, farmRoot) {
|
|
1327
|
+
const raw = fs3.readFileUtf8(path4.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs3.readFileUtf8(path4.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1318
1328
|
if (raw === void 0) {
|
|
1319
1329
|
return void 0;
|
|
1320
1330
|
}
|
|
@@ -1372,9 +1382,9 @@ function buildEntryFacts(params) {
|
|
|
1372
1382
|
entries
|
|
1373
1383
|
};
|
|
1374
1384
|
}
|
|
1375
|
-
function listSubtree(
|
|
1385
|
+
function listSubtree(fs3, root, rel) {
|
|
1376
1386
|
const absolute = path4.join(root, rel);
|
|
1377
|
-
const stat =
|
|
1387
|
+
const stat = fs3.lstat(absolute);
|
|
1378
1388
|
if (stat === void 0) {
|
|
1379
1389
|
return [];
|
|
1380
1390
|
}
|
|
@@ -1382,10 +1392,10 @@ function listSubtree(fs2, root, rel) {
|
|
|
1382
1392
|
return [{ rel, kind: "symlink" }];
|
|
1383
1393
|
}
|
|
1384
1394
|
if (stat.kind === "dir") {
|
|
1385
|
-
const children = [...
|
|
1395
|
+
const children = [...fs3.readdir(absolute)].sort().flatMap((name) => listSubtree(fs3, root, `${rel}/${name}`));
|
|
1386
1396
|
return [{ rel, kind: "dir" }, ...children];
|
|
1387
1397
|
}
|
|
1388
|
-
const contentHash =
|
|
1398
|
+
const contentHash = fs3.hashFile(absolute);
|
|
1389
1399
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1390
1400
|
}
|
|
1391
1401
|
function topLevelScopeRoots(roots) {
|
|
@@ -1399,25 +1409,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1399
1409
|
}
|
|
1400
1410
|
return kept;
|
|
1401
1411
|
}
|
|
1402
|
-
function reconciliationScope(
|
|
1412
|
+
function reconciliationScope(fs3, farmRoot, manifest) {
|
|
1403
1413
|
if (manifest !== void 0) {
|
|
1404
1414
|
return topLevelScopeRoots(manifest.materialised);
|
|
1405
1415
|
}
|
|
1406
|
-
return [...
|
|
1416
|
+
return [...fs3.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs3.lstat(path4.join(farmRoot, name))?.kind === "dir").sort();
|
|
1407
1417
|
}
|
|
1408
|
-
function collectFarmListing(
|
|
1409
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1418
|
+
function collectFarmListing(fs3, farmRoot, scopeRoots) {
|
|
1419
|
+
return scopeRoots.flatMap((root) => listSubtree(fs3, farmRoot, root));
|
|
1410
1420
|
}
|
|
1411
|
-
function collectCanonicalCounterparts(
|
|
1421
|
+
function collectCanonicalCounterparts(fs3, claudeHome, farmListing) {
|
|
1412
1422
|
const out = [];
|
|
1413
1423
|
for (const entry of farmListing) {
|
|
1414
1424
|
const absolute = path4.join(claudeHome, entry.rel);
|
|
1415
|
-
const stat =
|
|
1425
|
+
const stat = fs3.lstat(absolute);
|
|
1416
1426
|
if (stat === void 0) {
|
|
1417
1427
|
continue;
|
|
1418
1428
|
}
|
|
1419
1429
|
if (stat.kind === "file") {
|
|
1420
|
-
const contentHash =
|
|
1430
|
+
const contentHash = fs3.hashFile(absolute);
|
|
1421
1431
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1422
1432
|
continue;
|
|
1423
1433
|
}
|
|
@@ -2101,10 +2111,10 @@ var FrontDoorStateSchema = z3.strictObject({
|
|
|
2101
2111
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2102
2112
|
lastError: z3.string().optional()
|
|
2103
2113
|
});
|
|
2104
|
-
function readFrontDoorState(
|
|
2114
|
+
function readFrontDoorState(fs3, stateFile) {
|
|
2105
2115
|
let raw;
|
|
2106
2116
|
try {
|
|
2107
|
-
raw =
|
|
2117
|
+
raw = fs3.readFileUtf8(stateFile);
|
|
2108
2118
|
} catch {
|
|
2109
2119
|
return void 0;
|
|
2110
2120
|
}
|
|
@@ -2118,28 +2128,28 @@ function readFrontDoorState(fs2, stateFile) {
|
|
|
2118
2128
|
return void 0;
|
|
2119
2129
|
}
|
|
2120
2130
|
}
|
|
2121
|
-
function writeFrontDoorState(
|
|
2122
|
-
|
|
2123
|
-
|
|
2131
|
+
function writeFrontDoorState(fs3, stateFile, state) {
|
|
2132
|
+
fs3.mkdirp(path5.dirname(stateFile));
|
|
2133
|
+
fs3.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2124
2134
|
`);
|
|
2125
2135
|
}
|
|
2126
2136
|
function frontDoorOrigin(state) {
|
|
2127
2137
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2128
2138
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2129
2139
|
}
|
|
2130
|
-
function writeFrontDoorSession(
|
|
2131
|
-
|
|
2132
|
-
|
|
2140
|
+
function writeFrontDoorSession(fs3, sessionsDir, session) {
|
|
2141
|
+
fs3.mkdirPrivate(sessionsDir);
|
|
2142
|
+
fs3.writeFilePrivate(path5.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2133
2143
|
`);
|
|
2134
2144
|
}
|
|
2135
|
-
function listFrontDoorSessions(
|
|
2145
|
+
function listFrontDoorSessions(fs3, sessionsDir) {
|
|
2136
2146
|
const sessions = [];
|
|
2137
|
-
for (const name of
|
|
2147
|
+
for (const name of fs3.readdir(sessionsDir)) {
|
|
2138
2148
|
if (!name.endsWith(".json")) {
|
|
2139
2149
|
continue;
|
|
2140
2150
|
}
|
|
2141
2151
|
try {
|
|
2142
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2152
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs3.readFileUtf8(path5.join(sessionsDir, name)) ?? ""));
|
|
2143
2153
|
if (parsed.success) {
|
|
2144
2154
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2145
2155
|
}
|
|
@@ -2149,14 +2159,14 @@ function listFrontDoorSessions(fs2, sessionsDir) {
|
|
|
2149
2159
|
}
|
|
2150
2160
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2151
2161
|
}
|
|
2152
|
-
function removeFrontDoorSession(
|
|
2153
|
-
|
|
2162
|
+
function removeFrontDoorSession(fs3, sessionsDir, pid) {
|
|
2163
|
+
fs3.removeRecursive(path5.join(sessionsDir, `${String(pid)}.json`));
|
|
2154
2164
|
}
|
|
2155
|
-
function pruneDeadFrontDoorSessions(
|
|
2165
|
+
function pruneDeadFrontDoorSessions(fs3, sessionsDir, isRunning) {
|
|
2156
2166
|
const removed = [];
|
|
2157
|
-
for (const session of listFrontDoorSessions(
|
|
2167
|
+
for (const session of listFrontDoorSessions(fs3, sessionsDir)) {
|
|
2158
2168
|
if (!isRunning(session.pid)) {
|
|
2159
|
-
removeFrontDoorSession(
|
|
2169
|
+
removeFrontDoorSession(fs3, sessionsDir, session.pid);
|
|
2160
2170
|
removed.push(session.pid);
|
|
2161
2171
|
}
|
|
2162
2172
|
}
|
|
@@ -2168,8 +2178,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2168
2178
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2169
2179
|
var MS_PER_MINUTE = 6e4;
|
|
2170
2180
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2171
|
-
const { fs:
|
|
2172
|
-
const previous = readFrontDoorState(
|
|
2181
|
+
const { fs: fs3, paths } = ports;
|
|
2182
|
+
const previous = readFrontDoorState(fs3, paths.frontdoorStateFile);
|
|
2173
2183
|
let lastPort = previous?.lastPort;
|
|
2174
2184
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2175
2185
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2179,12 +2189,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2179
2189
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2180
2190
|
});
|
|
2181
2191
|
const fail = (message) => {
|
|
2182
|
-
writeFrontDoorState(
|
|
2183
|
-
|
|
2192
|
+
writeFrontDoorState(fs3, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2193
|
+
fs3.removeRecursive(paths.frontdoorLockFile);
|
|
2184
2194
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2185
2195
|
return 1;
|
|
2186
2196
|
};
|
|
2187
|
-
writeFrontDoorState(
|
|
2197
|
+
writeFrontDoorState(fs3, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2188
2198
|
const previousSticky = lastPort;
|
|
2189
2199
|
let http2;
|
|
2190
2200
|
try {
|
|
@@ -2221,8 +2231,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2221
2231
|
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)`);
|
|
2222
2232
|
}
|
|
2223
2233
|
lastDirectPort = direct.port;
|
|
2224
|
-
writeFrontDoorState(
|
|
2225
|
-
|
|
2234
|
+
writeFrontDoorState(fs3, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, port: http2.port, connectPort: connect2.port, directPort: direct.port, ...sticky() });
|
|
2235
|
+
fs3.removeRecursive(paths.frontdoorLockFile);
|
|
2226
2236
|
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(connect2.port)}`);
|
|
2227
2237
|
let idleSince;
|
|
2228
2238
|
let ticks = 0;
|
|
@@ -2231,15 +2241,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2231
2241
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2232
2242
|
}
|
|
2233
2243
|
ticks += 1;
|
|
2234
|
-
pruneDeadFrontDoorSessions(
|
|
2235
|
-
if (listFrontDoorSessions(
|
|
2244
|
+
pruneDeadFrontDoorSessions(fs3, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2245
|
+
if (listFrontDoorSessions(fs3, paths.frontdoorSessionsDir).length === 0) {
|
|
2236
2246
|
idleSince ??= ports.now();
|
|
2237
2247
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2238
2248
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2239
2249
|
await direct.close();
|
|
2240
2250
|
await connect2.close();
|
|
2241
2251
|
await http2.close();
|
|
2242
|
-
writeFrontDoorState(
|
|
2252
|
+
writeFrontDoorState(fs3, paths.frontdoorStateFile, sticky());
|
|
2243
2253
|
return 0;
|
|
2244
2254
|
}
|
|
2245
2255
|
} else {
|
|
@@ -2251,6 +2261,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2251
2261
|
|
|
2252
2262
|
// src/frontdoor/connect.ts
|
|
2253
2263
|
var CONNECT_INTERCEPT_HOST = "api.anthropic.com";
|
|
2264
|
+
var CONNECT_INTERCEPT_HOSTS = [CONNECT_INTERCEPT_HOST, "platform.claude.com"];
|
|
2254
2265
|
var ROUTED_PATH_PREFIX = "/v1/";
|
|
2255
2266
|
var HTTPS_PORT = 443;
|
|
2256
2267
|
var MAX_PORT = 65535;
|
|
@@ -2290,11 +2301,11 @@ function parseConnectTarget(authority) {
|
|
|
2290
2301
|
}
|
|
2291
2302
|
return { host, port };
|
|
2292
2303
|
}
|
|
2293
|
-
function isInterceptedHost(host,
|
|
2294
|
-
return host
|
|
2304
|
+
function isInterceptedHost(host, interceptHosts) {
|
|
2305
|
+
return interceptHosts.includes(host);
|
|
2295
2306
|
}
|
|
2296
|
-
function servedByPipeline(
|
|
2297
|
-
return
|
|
2307
|
+
function servedByPipeline(path9) {
|
|
2308
|
+
return path9?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2298
2309
|
}
|
|
2299
2310
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2300
2311
|
"connection",
|
|
@@ -2361,28 +2372,34 @@ function caParses(ca) {
|
|
|
2361
2372
|
}
|
|
2362
2373
|
}
|
|
2363
2374
|
async function startConnectServer(config, effects, preferredPort) {
|
|
2364
|
-
const
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
httpSession.
|
|
2375
|
+
const onUpgrade = config.capture === void 0 ? void 0 : (request3) => config.capture?.upgrade(request3);
|
|
2376
|
+
const sessions = config.interceptHosts.map((host) => {
|
|
2377
|
+
const handler = (request3, response) => {
|
|
2378
|
+
if (host === config.routedHost && servedByPipeline(request3.url)) {
|
|
2379
|
+
config.serveRouted(request3, response);
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
effects.forwardHttp(config.upstreamFor(host), request3, response, config.capture?.observePassthrough(request3));
|
|
2383
|
+
};
|
|
2384
|
+
const httpSession = effects.createHttpSession(handler, onUpgrade);
|
|
2385
|
+
const tlsAcceptor = effects.createTlsAcceptor(config.leafFor(host), (secure) => {
|
|
2386
|
+
httpSession.serve(secure);
|
|
2387
|
+
});
|
|
2388
|
+
return { host, httpSession, tlsAcceptor };
|
|
2374
2389
|
});
|
|
2375
2390
|
const ledger = createConnectionLedger(config.limits, config.isLiveCapability);
|
|
2376
2391
|
const listener = await effects.listenLoopback(preferredPort, (socket) => {
|
|
2377
|
-
handleConnect(socket, { config, effects,
|
|
2392
|
+
handleConnect(socket, { config, effects, sessions, ledger });
|
|
2378
2393
|
});
|
|
2379
2394
|
return {
|
|
2380
2395
|
port: listener.port,
|
|
2381
2396
|
close: async () => {
|
|
2382
2397
|
ledger.close();
|
|
2383
2398
|
await listener.close();
|
|
2384
|
-
tlsAcceptor
|
|
2385
|
-
|
|
2399
|
+
for (const { httpSession, tlsAcceptor } of sessions) {
|
|
2400
|
+
tlsAcceptor.close();
|
|
2401
|
+
httpSession.close();
|
|
2402
|
+
}
|
|
2386
2403
|
}
|
|
2387
2404
|
};
|
|
2388
2405
|
}
|
|
@@ -2502,7 +2519,7 @@ function proxyAuthorizationOf(head) {
|
|
|
2502
2519
|
return values.length === 1 ? value : void 0;
|
|
2503
2520
|
}
|
|
2504
2521
|
function routeConnect(socket, head, context) {
|
|
2505
|
-
const { config, effects,
|
|
2522
|
+
const { config, effects, sessions, ledger } = context;
|
|
2506
2523
|
const credential = proxyAuthorizationOf(head);
|
|
2507
2524
|
const capability = credential === void 0 ? void 0 : capabilityFromProxyAuthorization(credential);
|
|
2508
2525
|
if (capability === void 0 || !config.isLiveCapability(capability)) {
|
|
@@ -2521,10 +2538,11 @@ function routeConnect(socket, head, context) {
|
|
|
2521
2538
|
refuse(socket, REFUSAL.serviceUnavailable);
|
|
2522
2539
|
return;
|
|
2523
2540
|
}
|
|
2524
|
-
|
|
2541
|
+
const session = sessions.find((entry) => entry.host === target.host);
|
|
2542
|
+
config.capture?.connect(target, session !== void 0);
|
|
2525
2543
|
socket.write(CONNECT_ESTABLISHED);
|
|
2526
|
-
if (
|
|
2527
|
-
tlsAcceptor.accept(socket);
|
|
2544
|
+
if (session !== void 0) {
|
|
2545
|
+
session.tlsAcceptor.accept(socket);
|
|
2528
2546
|
return;
|
|
2529
2547
|
}
|
|
2530
2548
|
void blindTunnel(socket, target, effects);
|
|
@@ -2770,16 +2788,16 @@ function parseJson(raw, schema) {
|
|
|
2770
2788
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
2771
2789
|
return parsed.success ? parsed.data : void 0;
|
|
2772
2790
|
}
|
|
2773
|
-
function readHeadroomState(
|
|
2791
|
+
function readHeadroomState(fs3, stateFile) {
|
|
2774
2792
|
try {
|
|
2775
|
-
return parseJson(
|
|
2793
|
+
return parseJson(fs3.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
2776
2794
|
} catch {
|
|
2777
2795
|
return void 0;
|
|
2778
2796
|
}
|
|
2779
2797
|
}
|
|
2780
|
-
function writeHeadroomState(
|
|
2781
|
-
|
|
2782
|
-
|
|
2798
|
+
function writeHeadroomState(fs3, stateFile, state) {
|
|
2799
|
+
fs3.mkdirp(path6.dirname(stateFile));
|
|
2800
|
+
fs3.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2783
2801
|
`);
|
|
2784
2802
|
}
|
|
2785
2803
|
function headroomAllowlist(providers) {
|
|
@@ -2799,14 +2817,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
2799
2817
|
function hashAllowlist(allowlist) {
|
|
2800
2818
|
return createHash2("sha256").update(allowlist.join("\n")).digest("hex");
|
|
2801
2819
|
}
|
|
2802
|
-
function readAllProviders(
|
|
2820
|
+
function readAllProviders(fs3, providersDir) {
|
|
2803
2821
|
const result = [];
|
|
2804
|
-
for (const name of
|
|
2822
|
+
for (const name of fs3.readdir(providersDir)) {
|
|
2805
2823
|
if (!name.endsWith(".json")) {
|
|
2806
2824
|
continue;
|
|
2807
2825
|
}
|
|
2808
2826
|
try {
|
|
2809
|
-
const parsed = parseJson(
|
|
2827
|
+
const parsed = parseJson(fs3.readFileUtf8(path6.join(providersDir, name)), ProviderSchema);
|
|
2810
2828
|
if (parsed !== void 0) {
|
|
2811
2829
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
2812
2830
|
}
|
|
@@ -2816,22 +2834,22 @@ function readAllProviders(fs2, providersDir) {
|
|
|
2816
2834
|
}
|
|
2817
2835
|
return result;
|
|
2818
2836
|
}
|
|
2819
|
-
function writeSession(
|
|
2820
|
-
|
|
2821
|
-
|
|
2837
|
+
function writeSession(fs3, sessionsDir, session) {
|
|
2838
|
+
fs3.mkdirp(sessionsDir);
|
|
2839
|
+
fs3.writeFileUtf8(path6.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2822
2840
|
`);
|
|
2823
2841
|
}
|
|
2824
|
-
function removeSession(
|
|
2825
|
-
|
|
2842
|
+
function removeSession(fs3, sessionsDir, pid) {
|
|
2843
|
+
fs3.removeRecursive(path6.join(sessionsDir, `${String(pid)}.json`));
|
|
2826
2844
|
}
|
|
2827
|
-
function listSessions(
|
|
2845
|
+
function listSessions(fs3, sessionsDir) {
|
|
2828
2846
|
const sessions = [];
|
|
2829
|
-
for (const name of
|
|
2847
|
+
for (const name of fs3.readdir(sessionsDir)) {
|
|
2830
2848
|
if (!name.endsWith(".json")) {
|
|
2831
2849
|
continue;
|
|
2832
2850
|
}
|
|
2833
2851
|
try {
|
|
2834
|
-
const parsed = parseJson(
|
|
2852
|
+
const parsed = parseJson(fs3.readFileUtf8(path6.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
2835
2853
|
if (parsed !== void 0) {
|
|
2836
2854
|
sessions.push(parsed);
|
|
2837
2855
|
}
|
|
@@ -2841,22 +2859,22 @@ function listSessions(fs2, sessionsDir) {
|
|
|
2841
2859
|
}
|
|
2842
2860
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2843
2861
|
}
|
|
2844
|
-
function sessionsForSupervisor(
|
|
2845
|
-
return listSessions(
|
|
2862
|
+
function sessionsForSupervisor(fs3, sessionsDir, supervisorPid) {
|
|
2863
|
+
return listSessions(fs3, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
2846
2864
|
}
|
|
2847
|
-
function pruneDeadSessions(
|
|
2865
|
+
function pruneDeadSessions(fs3, sessionsDir, isRunning) {
|
|
2848
2866
|
const removed = [];
|
|
2849
|
-
for (const session of listSessions(
|
|
2867
|
+
for (const session of listSessions(fs3, sessionsDir)) {
|
|
2850
2868
|
if (!isRunning(session.pid)) {
|
|
2851
|
-
removeSession(
|
|
2869
|
+
removeSession(fs3, sessionsDir, session.pid);
|
|
2852
2870
|
removed.push(session.pid);
|
|
2853
2871
|
}
|
|
2854
2872
|
}
|
|
2855
2873
|
return removed;
|
|
2856
2874
|
}
|
|
2857
|
-
function readStartLock(
|
|
2875
|
+
function readStartLock(fs3, lockFile) {
|
|
2858
2876
|
try {
|
|
2859
|
-
return parseJson(
|
|
2877
|
+
return parseJson(fs3.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
2860
2878
|
} catch {
|
|
2861
2879
|
return void 0;
|
|
2862
2880
|
}
|
|
@@ -3093,9 +3111,9 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3093
3111
|
}
|
|
3094
3112
|
return path7.join(snapshotsDir, `${identity}.json`);
|
|
3095
3113
|
}
|
|
3096
|
-
function readUsageSnapshot(
|
|
3114
|
+
function readUsageSnapshot(fs3, snapshotsDir, identity) {
|
|
3097
3115
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3098
|
-
const raw =
|
|
3116
|
+
const raw = fs3.readFileUtf8(file);
|
|
3099
3117
|
if (raw === void 0) {
|
|
3100
3118
|
return void 0;
|
|
3101
3119
|
}
|
|
@@ -3111,8 +3129,8 @@ function readUsageSnapshot(fs2, snapshotsDir, identity) {
|
|
|
3111
3129
|
}
|
|
3112
3130
|
return snapshot.data;
|
|
3113
3131
|
}
|
|
3114
|
-
function listUsageSnapshots(
|
|
3115
|
-
return
|
|
3132
|
+
function listUsageSnapshots(fs3, snapshotsDir) {
|
|
3133
|
+
return fs3.readdir(snapshotsDir).flatMap((name) => {
|
|
3116
3134
|
if (!name.endsWith(".json")) {
|
|
3117
3135
|
return [];
|
|
3118
3136
|
}
|
|
@@ -3120,7 +3138,7 @@ function listUsageSnapshots(fs2, snapshotsDir) {
|
|
|
3120
3138
|
if (!isIdentityName(identity)) {
|
|
3121
3139
|
return [];
|
|
3122
3140
|
}
|
|
3123
|
-
const snapshot = readUsageSnapshot(
|
|
3141
|
+
const snapshot = readUsageSnapshot(fs3, snapshotsDir, identity);
|
|
3124
3142
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3125
3143
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3126
3144
|
}
|
|
@@ -3287,9 +3305,9 @@ function compareVersions(a, b) {
|
|
|
3287
3305
|
}
|
|
3288
3306
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
3289
3307
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
3290
|
-
const { fs:
|
|
3308
|
+
const { fs: fs3, paths } = ports;
|
|
3291
3309
|
let config = initialConfig;
|
|
3292
|
-
const previousState = readHeadroomState(
|
|
3310
|
+
const previousState = readHeadroomState(fs3, paths.headroomStateFile);
|
|
3293
3311
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3294
3312
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3295
3313
|
return 0;
|
|
@@ -3298,19 +3316,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3298
3316
|
let lastPort = previousState?.lastPort;
|
|
3299
3317
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3300
3318
|
const ownsState = () => {
|
|
3301
|
-
const owner = readHeadroomState(
|
|
3319
|
+
const owner = readHeadroomState(fs3, paths.headroomStateFile)?.supervisorPid;
|
|
3302
3320
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3303
3321
|
};
|
|
3304
3322
|
const writeOwnState = (state) => {
|
|
3305
3323
|
if (!ownsState()) {
|
|
3306
3324
|
return false;
|
|
3307
3325
|
}
|
|
3308
|
-
writeHeadroomState(
|
|
3326
|
+
writeHeadroomState(fs3, paths.headroomStateFile, state);
|
|
3309
3327
|
return true;
|
|
3310
3328
|
};
|
|
3311
3329
|
const fail = (message) => {
|
|
3312
3330
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3313
|
-
|
|
3331
|
+
fs3.removeRecursive(paths.headroomLockFile);
|
|
3314
3332
|
}
|
|
3315
3333
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
3316
3334
|
return 1;
|
|
@@ -3359,7 +3377,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3359
3377
|
const settingsHash = hashSettings(config.settings);
|
|
3360
3378
|
const allowlist = allowlistOf(ports);
|
|
3361
3379
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3362
|
-
pruneDeadSessions(
|
|
3380
|
+
pruneDeadSessions(fs3, paths.headroomSessionsDir, ports.isRunning);
|
|
3363
3381
|
const ownsNow = ownsState();
|
|
3364
3382
|
if (!ownsNow && !superseded) {
|
|
3365
3383
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -3411,7 +3429,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3411
3429
|
...sticky()
|
|
3412
3430
|
});
|
|
3413
3431
|
if (recorded) {
|
|
3414
|
-
|
|
3432
|
+
fs3.removeRecursive(paths.headroomLockFile);
|
|
3415
3433
|
}
|
|
3416
3434
|
idleSince = void 0;
|
|
3417
3435
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -3426,7 +3444,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3426
3444
|
}
|
|
3427
3445
|
} else {
|
|
3428
3446
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3429
|
-
if (sessionsForSupervisor(
|
|
3447
|
+
if (sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3430
3448
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3431
3449
|
ports.stopProcess(headroomPid);
|
|
3432
3450
|
headroomPid = void 0;
|
|
@@ -3435,7 +3453,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3435
3453
|
continue;
|
|
3436
3454
|
}
|
|
3437
3455
|
}
|
|
3438
|
-
const sessions = sessionsForSupervisor(
|
|
3456
|
+
const sessions = sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid);
|
|
3439
3457
|
if (sessions.length === 0) {
|
|
3440
3458
|
idleSince ??= ports.now();
|
|
3441
3459
|
const idleMs = ports.now() - idleSince;
|
|
@@ -3457,9 +3475,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3457
3475
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
3458
3476
|
}
|
|
3459
3477
|
}
|
|
3460
|
-
function currentHeadroomAllowlist(
|
|
3461
|
-
const providers = readAllProviders(
|
|
3462
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
3478
|
+
function currentHeadroomAllowlist(fs3, paths) {
|
|
3479
|
+
const providers = readAllProviders(fs3, paths.providersDir).map((entry) => entry.provider);
|
|
3480
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs3, paths.frontdoorStateFile))));
|
|
3463
3481
|
}
|
|
3464
3482
|
function allowlistOf(ports) {
|
|
3465
3483
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -3523,17 +3541,99 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
3523
3541
|
message: formatAmbientCredentialGuardMessage(detected.variable, params.identityName)
|
|
3524
3542
|
};
|
|
3525
3543
|
}
|
|
3544
|
+
|
|
3545
|
+
// src/paths.ts
|
|
3546
|
+
import fs2 from "node:fs";
|
|
3547
|
+
import os from "node:os";
|
|
3548
|
+
import path8 from "node:path";
|
|
3549
|
+
function resolveAgentShimHome(env = process.env, home = os.homedir(), isDirectory = isExistingDirectory) {
|
|
3550
|
+
const fromEnv = env.AGENT_SHIM_HOME;
|
|
3551
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
3552
|
+
return fromEnv;
|
|
3553
|
+
}
|
|
3554
|
+
const current = path8.join(home, HOME_DIRNAME);
|
|
3555
|
+
const legacy = path8.join(home, LEGACY_HOME_DIRNAME);
|
|
3556
|
+
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
3557
|
+
}
|
|
3558
|
+
function isExistingDirectory(candidate) {
|
|
3559
|
+
return fs2.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
3560
|
+
}
|
|
3561
|
+
function resolveClaudeHome() {
|
|
3562
|
+
const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
|
|
3563
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
3564
|
+
return fromEnv;
|
|
3565
|
+
}
|
|
3566
|
+
return path8.join(os.homedir(), ".claude");
|
|
3567
|
+
}
|
|
3568
|
+
function buildLayoutPaths(root) {
|
|
3569
|
+
return {
|
|
3570
|
+
root,
|
|
3571
|
+
identitiesDir: path8.join(root, "identities"),
|
|
3572
|
+
configProfilesDir: path8.join(root, "config-profiles"),
|
|
3573
|
+
providersDir: path8.join(root, "providers"),
|
|
3574
|
+
directoryRulesFile: path8.join(root, "directory-rules.json"),
|
|
3575
|
+
activeIdentityFile: path8.join(root, "active-identity"),
|
|
3576
|
+
globalConfigFile: path8.join(root, "config.json"),
|
|
3577
|
+
categoriesLocalFile: path8.join(root, "categories.local.json"),
|
|
3578
|
+
claudeShimFile: path8.join(root, "claude-shim.json"),
|
|
3579
|
+
headroomDir: path8.join(root, "headroom"),
|
|
3580
|
+
headroomStateFile: path8.join(root, "headroom", "state.json"),
|
|
3581
|
+
headroomLockFile: path8.join(root, "headroom", "start.lock"),
|
|
3582
|
+
headroomSessionsDir: path8.join(root, "headroom", "sessions"),
|
|
3583
|
+
logsDir: path8.join(root, "logs"),
|
|
3584
|
+
headroomLogPath: path8.join(root, "logs", "headroom.log"),
|
|
3585
|
+
frontdoorDir: path8.join(root, "frontdoor"),
|
|
3586
|
+
frontdoorCaDir: path8.join(root, "frontdoor", "ca"),
|
|
3587
|
+
frontdoorCaCertFile: path8.join(root, "frontdoor", "ca", "ca.pem"),
|
|
3588
|
+
frontdoorCaKeyFile: path8.join(root, "frontdoor", "ca", "ca.key"),
|
|
3589
|
+
frontdoorCaBundlesDir: path8.join(root, "frontdoor", "ca", "bundles"),
|
|
3590
|
+
frontdoorStateFile: path8.join(root, "frontdoor", "state.json"),
|
|
3591
|
+
frontdoorLockFile: path8.join(root, "frontdoor", "start.lock"),
|
|
3592
|
+
frontdoorSessionsDir: path8.join(root, "frontdoor", "sessions"),
|
|
3593
|
+
frontdoorLogPath: path8.join(root, "logs", "frontdoor.log"),
|
|
3594
|
+
usageDir: path8.join(root, "usage"),
|
|
3595
|
+
usageLogDir: path8.join(root, "usage", "log"),
|
|
3596
|
+
usageSnapshotsDir: path8.join(root, "usage", "snapshots"),
|
|
3597
|
+
usagePicksFile: path8.join(root, "usage", "picks.json")
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
3600
|
+
function resolveLayoutPaths() {
|
|
3601
|
+
return buildLayoutPaths(resolveAgentShimHome());
|
|
3602
|
+
}
|
|
3526
3603
|
export {
|
|
3527
3604
|
AMBIENT_CREDENTIAL_VARS,
|
|
3528
3605
|
CONNECT_INTERCEPT_HOST,
|
|
3606
|
+
CONNECT_INTERCEPT_HOSTS,
|
|
3529
3607
|
CONNECT_LIMITS,
|
|
3608
|
+
CategoryClassificationOverlaySchema,
|
|
3609
|
+
CategoryClassificationSchema,
|
|
3610
|
+
CategoryMapSchema,
|
|
3611
|
+
CliError,
|
|
3612
|
+
ConfigProfileSchema,
|
|
3613
|
+
CredentialCacheSchema,
|
|
3614
|
+
CredentialSchema,
|
|
3615
|
+
CredentialSourceSchema,
|
|
3616
|
+
DirectoryRuleSchema,
|
|
3617
|
+
DirectoryRulesSchema,
|
|
3618
|
+
EXIT_FAILURE,
|
|
3619
|
+
EXIT_USAGE,
|
|
3620
|
+
EntryValueSchema,
|
|
3530
3621
|
FARM_MANIFEST_FILENAME,
|
|
3531
3622
|
FrontDoorStartError,
|
|
3623
|
+
GlobalConfigSchema,
|
|
3532
3624
|
HeadroomStartError,
|
|
3625
|
+
IdentitySchema,
|
|
3626
|
+
PoolNameSchema,
|
|
3627
|
+
PoolSchema,
|
|
3628
|
+
PortableConfigSchema,
|
|
3629
|
+
ProviderSchema,
|
|
3533
3630
|
ROUTED_PATH_PREFIX,
|
|
3631
|
+
UsageError,
|
|
3534
3632
|
UsageSnapshotError,
|
|
3535
3633
|
UsageSnapshotSchema,
|
|
3634
|
+
WhenSchema,
|
|
3536
3635
|
buildEntryFacts,
|
|
3636
|
+
buildLayoutPaths,
|
|
3537
3637
|
carryOver,
|
|
3538
3638
|
createLeafCache,
|
|
3539
3639
|
detectAmbientCredential,
|
|
@@ -3554,7 +3654,10 @@ export {
|
|
|
3554
3654
|
realConnectEffects,
|
|
3555
3655
|
recoverFarm,
|
|
3556
3656
|
recoveryDiagnostics,
|
|
3657
|
+
resolveAgentShimHome,
|
|
3658
|
+
resolveClaudeHome,
|
|
3557
3659
|
resolveDecisions,
|
|
3660
|
+
resolveLayoutPaths,
|
|
3558
3661
|
resolveSupervisorConfig,
|
|
3559
3662
|
resyncFarm,
|
|
3560
3663
|
runFrontDoorSupervisor,
|
|
@@ -427,7 +427,7 @@ export declare const POOL_SELECTOR_PREFIX = "pool:";
|
|
|
427
427
|
/** A pool's own name: the identity alphabet minus `@`, since a pool is never an email address. */
|
|
428
428
|
export declare const PoolNameSchema: z.ZodString;
|
|
429
429
|
/** A named, explicit set of identities to choose among at launch. Never implicit: a pool lists its members, so an identity that bills a client is only ever picked when someone put it there. */
|
|
430
|
-
declare const PoolSchema: z.ZodObject<{
|
|
430
|
+
export declare const PoolSchema: z.ZodObject<{
|
|
431
431
|
identities: z.ZodArray<z.ZodString>;
|
|
432
432
|
}, z.core.$strict>;
|
|
433
433
|
export type Pool = z.infer<typeof PoolSchema>;
|