claude-use 6.2.3 → 7.1.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/LICENSE +21 -201
- package/README.md +91 -80
- package/dist/cli.cjs +535 -474
- package/dist/index.cjs +42 -39
- package/dist/index.mjs +42 -39
- package/dist/types/cliError.d.ts +1 -1
- package/dist/types/config/classify.d.ts +3 -3
- package/dist/types/config/schema.d.ts +13 -13
- package/dist/types/frontdoor/capability.d.ts +3 -3
- package/dist/types/frontdoor/connect.d.ts +2 -2
- package/dist/types/frontdoor/ensure.d.ts +2 -2
- package/dist/types/frontdoor/probe.d.ts +1 -1
- package/dist/types/frontdoor/supervisor.d.ts +1 -1
- package/dist/types/frontdoor/x509.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/launcher/farm.d.ts +5 -5
- package/dist/types/launcher/guard.d.ts +3 -3
- package/dist/types/launcher/lock.d.ts +1 -1
- package/dist/types/launcher/ports.d.ts +3 -3
- package/dist/types/legacy.d.ts +24 -0
- package/dist/types/paths.d.ts +12 -10
- package/dist/types/resolve/conditions.d.ts +1 -1
- package/dist/types/resolve/match.d.ts +1 -1
- package/dist/types/resolve/reconcile.d.ts +1 -1
- package/dist/types/resolve/types.d.ts +3 -3
- package/dist/types/resolve/walk.d.ts +6 -6
- package/dist/types/usage/read.d.ts +1 -1
- package/dist/types/usage/schema.d.ts +2 -2
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -17,7 +17,7 @@ function expandAllCategoryKey(pairs) {
|
|
|
17
17
|
if (all === void 0) {
|
|
18
18
|
return { ...rest };
|
|
19
19
|
}
|
|
20
|
-
const expanded = Object.fromEntries(OVERRIDABLE_CATEGORIES.map((category) => [category, all]));
|
|
20
|
+
const expanded = Object.fromEntries(OVERRIDABLE_CATEGORIES.filter((category) => !all || category !== "runtime").map((category) => [category, all]));
|
|
21
21
|
return { ...expanded, ...rest };
|
|
22
22
|
}
|
|
23
23
|
var CategoryMapSchema = z.strictObject({
|
|
@@ -91,7 +91,7 @@ var CredentialSchema = z.strictObject({
|
|
|
91
91
|
var ProviderCredentialSchema = CredentialSchema.extend({ target: z.enum(PROVIDER_CREDENTIAL_TARGETS).optional() });
|
|
92
92
|
var RESERVED_PROVIDER_ENV_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
93
93
|
var ProviderEnvSchema = z.record(z.string().min(1), z.string()).refine((env) => !Object.keys(env).some((key) => RESERVED_PROVIDER_ENV_KEYS.includes(key)), {
|
|
94
|
-
message: `env may not set ${RESERVED_PROVIDER_ENV_KEYS.join(", ")}: the credential block's target sets one and
|
|
94
|
+
message: `env may not set ${RESERVED_PROVIDER_ENV_KEYS.join(", ")}: the credential block's target sets one and agent-shim removes the others`
|
|
95
95
|
});
|
|
96
96
|
var CODEX_EFFORTS = ["none", "low", "medium", "high"];
|
|
97
97
|
var CODEX_DEFAULT_TIER_MODELS = Object.freeze({
|
|
@@ -734,7 +734,7 @@ function resolveEntry(relPath, params) {
|
|
|
734
734
|
diagnostics.push({
|
|
735
735
|
code: "UNCLASSIFIED_ENTRY",
|
|
736
736
|
severity: "warning",
|
|
737
|
-
message: `"${head}" is not in the category map, so it is excluded. Run \`
|
|
737
|
+
message: `"${head}" is not in the category map, so it is excluded. Run \`agent-shim configure\` to classify it, which records the answer in the local overlay without touching the shipped default map.`,
|
|
738
738
|
subject: head
|
|
739
739
|
});
|
|
740
740
|
return { decision: { relPath, shared: false, via: "unclassified", category }, diagnostics };
|
|
@@ -1211,6 +1211,9 @@ function planReconciliation(params) {
|
|
|
1211
1211
|
return { actions, diagnostics, conservative };
|
|
1212
1212
|
}
|
|
1213
1213
|
|
|
1214
|
+
// src/legacy.ts
|
|
1215
|
+
var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
|
|
1216
|
+
|
|
1214
1217
|
// src/launcher/lock.ts
|
|
1215
1218
|
import { randomUUID } from "node:crypto";
|
|
1216
1219
|
import path3 from "node:path";
|
|
@@ -1220,7 +1223,7 @@ var DEFAULT_MAX_ATTEMPTS = 200;
|
|
|
1220
1223
|
var IdentityLockBusyError = class extends CliError {
|
|
1221
1224
|
constructor(identity, lockPath, holderPid) {
|
|
1222
1225
|
super(
|
|
1223
|
-
`Another
|
|
1226
|
+
`Another agent-shim resync is already running for identity "${identity}"` + (holderPid === void 0 ? "" : ` (pid ${String(holderPid)})`) + `. Its lock at ${lockPath} was still held after the full retry budget; nothing was changed.`
|
|
1224
1227
|
);
|
|
1225
1228
|
this.identity = identity;
|
|
1226
1229
|
this.lockPath = lockPath;
|
|
@@ -1299,7 +1302,7 @@ function acquireIdentityLock(params) {
|
|
|
1299
1302
|
}
|
|
1300
1303
|
|
|
1301
1304
|
// src/launcher/farm.ts
|
|
1302
|
-
var FARM_MANIFEST_FILENAME = ".
|
|
1305
|
+
var FARM_MANIFEST_FILENAME = ".agent-shim-farm.json";
|
|
1303
1306
|
var FarmManifestSchema = z2.strictObject({
|
|
1304
1307
|
version: z2.literal(1),
|
|
1305
1308
|
builtAtMs: z2.number(),
|
|
@@ -1311,7 +1314,7 @@ var FarmManifestSchema = z2.strictObject({
|
|
|
1311
1314
|
links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
|
|
1312
1315
|
});
|
|
1313
1316
|
function readFarmManifest(fs2, farmRoot) {
|
|
1314
|
-
const raw = fs2.readFileUtf8(path4.join(farmRoot, FARM_MANIFEST_FILENAME));
|
|
1317
|
+
const raw = fs2.readFileUtf8(path4.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs2.readFileUtf8(path4.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1315
1318
|
if (raw === void 0) {
|
|
1316
1319
|
return void 0;
|
|
1317
1320
|
}
|
|
@@ -1400,7 +1403,7 @@ function reconciliationScope(fs2, farmRoot, manifest) {
|
|
|
1400
1403
|
if (manifest !== void 0) {
|
|
1401
1404
|
return topLevelScopeRoots(manifest.materialised);
|
|
1402
1405
|
}
|
|
1403
|
-
return [...fs2.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && fs2.lstat(path4.join(farmRoot, name))?.kind === "dir").sort();
|
|
1406
|
+
return [...fs2.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs2.lstat(path4.join(farmRoot, name))?.kind === "dir").sort();
|
|
1404
1407
|
}
|
|
1405
1408
|
function collectFarmListing(fs2, farmRoot, scopeRoots) {
|
|
1406
1409
|
return scopeRoots.flatMap((root) => listSubtree(fs2, farmRoot, root));
|
|
@@ -1424,7 +1427,7 @@ function collectCanonicalCounterparts(fs2, claudeHome, farmListing) {
|
|
|
1424
1427
|
}
|
|
1425
1428
|
function carryOver(params) {
|
|
1426
1429
|
const manifest = readFarmManifest(params.fs, params.previousRoot);
|
|
1427
|
-
const accounted = /* @__PURE__ */ new Set([FARM_MANIFEST_FILENAME]);
|
|
1430
|
+
const accounted = /* @__PURE__ */ new Set([FARM_MANIFEST_FILENAME, LEGACY_FARM_MANIFEST_FILENAME]);
|
|
1428
1431
|
for (const rel of manifest?.materialised ?? []) {
|
|
1429
1432
|
const head = rel.split("/")[0];
|
|
1430
1433
|
if (head !== void 0 && head !== "") {
|
|
@@ -1722,7 +1725,7 @@ function resyncFarm(params) {
|
|
|
1722
1725
|
diagnostics.push({
|
|
1723
1726
|
code: "FARM_PREVIOUS_RETAINED",
|
|
1724
1727
|
severity: "warning",
|
|
1725
|
-
message: `The superseded farm was left at ${swap.retainedPrevious} because it still holds ${swap.collided.join(", ")}, which the new farm has its own entry for. Nothing was overwritten in either direction; run \`
|
|
1728
|
+
message: `The superseded farm was left at ${swap.retainedPrevious} because it still holds ${swap.collided.join(", ")}, which the new farm has its own entry for. Nothing was overwritten in either direction; run \`agent-shim identity resolve-conflicts ${params.identity}\` to resolve it interactively.`,
|
|
1726
1729
|
subject: swap.retainedPrevious
|
|
1727
1730
|
});
|
|
1728
1731
|
}
|
|
@@ -1759,7 +1762,7 @@ function recoveryDiagnostics(recovery, identity) {
|
|
|
1759
1762
|
}
|
|
1760
1763
|
if (recovery.retained.length > 0) {
|
|
1761
1764
|
parts.push(
|
|
1762
|
-
`kept ${recovery.retained.join(", ")}, which still holds data the current farm also has an entry for \u2014 run \`
|
|
1765
|
+
`kept ${recovery.retained.join(", ")}, which still holds data the current farm also has an entry for \u2014 run \`agent-shim identity resolve-conflicts ${identity}\` to resolve it interactively`
|
|
1763
1766
|
);
|
|
1764
1767
|
}
|
|
1765
1768
|
return [
|
|
@@ -2051,7 +2054,7 @@ function issueLeafCertificate(ca, names, notBefore, notAfter) {
|
|
|
2051
2054
|
}
|
|
2052
2055
|
|
|
2053
2056
|
// src/frontdoor/capability.ts
|
|
2054
|
-
var CONNECT_PROXY_REALM = "
|
|
2057
|
+
var CONNECT_PROXY_REALM = "agent-shim front door";
|
|
2055
2058
|
var BASIC_SCHEME = "basic";
|
|
2056
2059
|
function capabilityFromProxyAuthorization(value) {
|
|
2057
2060
|
const trimmed = value.trim();
|
|
@@ -2178,7 +2181,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2178
2181
|
const fail = (message) => {
|
|
2179
2182
|
writeFrontDoorState(fs2, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2180
2183
|
fs2.removeRecursive(paths.frontdoorLockFile);
|
|
2181
|
-
ports.log(`
|
|
2184
|
+
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2182
2185
|
return 1;
|
|
2183
2186
|
};
|
|
2184
2187
|
writeFrontDoorState(fs2, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
@@ -2190,7 +2193,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2190
2193
|
return fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
|
|
2191
2194
|
}
|
|
2192
2195
|
if (previousSticky !== void 0 && http2.port !== previousSticky) {
|
|
2193
|
-
ports.log(`
|
|
2196
|
+
ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http2.port)} (sessions launched against the old port are stale until they relaunch)`);
|
|
2194
2197
|
}
|
|
2195
2198
|
lastPort = http2.port;
|
|
2196
2199
|
const previousStickyConnect = lastConnectPort;
|
|
@@ -2202,7 +2205,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2202
2205
|
return fail(`could not start the CONNECT surface: ${error instanceof Error ? error.message : String(error)}`);
|
|
2203
2206
|
}
|
|
2204
2207
|
if (previousStickyConnect !== void 0 && connect2.port !== previousStickyConnect) {
|
|
2205
|
-
ports.log(`
|
|
2208
|
+
ports.log(`agent-shim frontdoor supervisor: sticky CONNECT port ${String(previousStickyConnect)} was occupied; moving to ${String(connect2.port)} (OAuth sessions launched against the old port are stale until they relaunch)`);
|
|
2206
2209
|
}
|
|
2207
2210
|
lastConnectPort = connect2.port;
|
|
2208
2211
|
const previousStickyDirect = lastDirectPort;
|
|
@@ -2215,12 +2218,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2215
2218
|
return fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
|
|
2216
2219
|
}
|
|
2217
2220
|
if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
|
|
2218
|
-
ports.log(`
|
|
2221
|
+
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)`);
|
|
2219
2222
|
}
|
|
2220
2223
|
lastDirectPort = direct.port;
|
|
2221
2224
|
writeFrontDoorState(fs2, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, port: http2.port, connectPort: connect2.port, directPort: direct.port, ...sticky() });
|
|
2222
2225
|
fs2.removeRecursive(paths.frontdoorLockFile);
|
|
2223
|
-
ports.log(`
|
|
2226
|
+
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)}`);
|
|
2224
2227
|
let idleSince;
|
|
2225
2228
|
let ticks = 0;
|
|
2226
2229
|
for (; ; ) {
|
|
@@ -2232,7 +2235,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2232
2235
|
if (listFrontDoorSessions(fs2, paths.frontdoorSessionsDir).length === 0) {
|
|
2233
2236
|
idleSince ??= ports.now();
|
|
2234
2237
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2235
|
-
ports.log(`
|
|
2238
|
+
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2236
2239
|
await direct.close();
|
|
2237
2240
|
await connect2.close();
|
|
2238
2241
|
await http2.close();
|
|
@@ -2272,7 +2275,7 @@ var CONNECT_LIMITS = {
|
|
|
2272
2275
|
};
|
|
2273
2276
|
var CA_VALIDITY_DAYS = 3650;
|
|
2274
2277
|
var LEAF_VALIDITY_DAYS = 397;
|
|
2275
|
-
var CA_SUBJECT_COMMON_NAME = "
|
|
2278
|
+
var CA_SUBJECT_COMMON_NAME = "agent-shim front door CA";
|
|
2276
2279
|
var MS_PER_DAY = 864e5;
|
|
2277
2280
|
function parseConnectTarget(authority) {
|
|
2278
2281
|
const colon = authority.lastIndexOf(":");
|
|
@@ -2666,7 +2669,7 @@ function realConnectEffects() {
|
|
|
2666
2669
|
response.destroy(error);
|
|
2667
2670
|
return;
|
|
2668
2671
|
}
|
|
2669
|
-
const body = `
|
|
2672
|
+
const body = `agent-shim headroom: upstream unreachable (${error.message})`;
|
|
2670
2673
|
response.writeHead(HTTP_BAD_GATEWAY, { "content-type": "text/plain", "content-length": String(body.length) });
|
|
2671
2674
|
response.end(body);
|
|
2672
2675
|
});
|
|
@@ -2866,13 +2869,13 @@ function ensureFrontDoor(params) {
|
|
|
2866
2869
|
}
|
|
2867
2870
|
lastRejection = `the listener on 127.0.0.1:${String(state.port)} (recorded for pid ${String(supervisorPid)}) failed authentication: ${verdict.reason}`;
|
|
2868
2871
|
if (spawned) {
|
|
2869
|
-
throw new FrontDoorStartError(`
|
|
2872
|
+
throw new FrontDoorStartError(`agent-shim: ${lastRejection}; refusing to send this session's credentials to it. Daemon log: ${paths.frontdoorLogPath}`);
|
|
2870
2873
|
}
|
|
2871
2874
|
distrusted.add(supervisorPid);
|
|
2872
2875
|
continue;
|
|
2873
2876
|
}
|
|
2874
2877
|
if (state?.lastError !== void 0) {
|
|
2875
|
-
throw new FrontDoorStartError(`
|
|
2878
|
+
throw new FrontDoorStartError(`agent-shim: the front door reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.frontdoorLogPath})`);
|
|
2876
2879
|
}
|
|
2877
2880
|
} else if (!spawned) {
|
|
2878
2881
|
ports.fs.mkdirp(paths.frontdoorDir);
|
|
@@ -2892,7 +2895,7 @@ function ensureFrontDoor(params) {
|
|
|
2892
2895
|
if (ports.now() >= deadline) {
|
|
2893
2896
|
const lastError = state?.lastError;
|
|
2894
2897
|
throw new FrontDoorStartError(
|
|
2895
|
-
`
|
|
2898
|
+
`agent-shim: the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + `. Daemon log: ${paths.frontdoorLogPath}`
|
|
2896
2899
|
);
|
|
2897
2900
|
}
|
|
2898
2901
|
ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
|
|
@@ -3057,7 +3060,7 @@ function isIdentityName(name) {
|
|
|
3057
3060
|
// src/usage/read.ts
|
|
3058
3061
|
var UsageSnapshotError = class extends Error {
|
|
3059
3062
|
constructor(file, detail) {
|
|
3060
|
-
super(`${file} is not a usage snapshot this
|
|
3063
|
+
super(`${file} is not a usage snapshot this agent-shim can read (schema version ${String(USAGE_SCHEMA_VERSION)}): ${detail}`);
|
|
3061
3064
|
this.file = file;
|
|
3062
3065
|
this.name = "UsageSnapshotError";
|
|
3063
3066
|
}
|
|
@@ -3124,7 +3127,7 @@ function ensureHeadroom(params) {
|
|
|
3124
3127
|
}
|
|
3125
3128
|
if (state.lastError !== void 0) {
|
|
3126
3129
|
throw new HeadroomStartError(
|
|
3127
|
-
`
|
|
3130
|
+
`agent-shim: the headroom daemon reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.headroomLogPath})`
|
|
3128
3131
|
);
|
|
3129
3132
|
}
|
|
3130
3133
|
} else if (!spawnedSupervisor) {
|
|
@@ -3148,7 +3151,7 @@ function ensureHeadroom(params) {
|
|
|
3148
3151
|
if (ports.now() >= deadline) {
|
|
3149
3152
|
const lastError = state?.lastError;
|
|
3150
3153
|
throw new HeadroomStartError(
|
|
3151
|
-
`
|
|
3154
|
+
`agent-shim: the headroom daemon did not become ready within ${String(HEADROOM_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + `. Daemon log: ${paths.headroomLogPath}`
|
|
3152
3155
|
);
|
|
3153
3156
|
}
|
|
3154
3157
|
ports.sleep(HEADROOM_LAUNCHER_POLL_MS);
|
|
@@ -3267,7 +3270,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3267
3270
|
let config = initialConfig;
|
|
3268
3271
|
const previousState = readHeadroomState(fs2, paths.headroomStateFile);
|
|
3269
3272
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3270
|
-
ports.log(`
|
|
3273
|
+
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3271
3274
|
return 0;
|
|
3272
3275
|
}
|
|
3273
3276
|
let installedSource = previousState?.installedSource;
|
|
@@ -3288,7 +3291,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3288
3291
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3289
3292
|
fs2.removeRecursive(paths.headroomLockFile);
|
|
3290
3293
|
}
|
|
3291
|
-
ports.log(`
|
|
3294
|
+
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
3292
3295
|
return 1;
|
|
3293
3296
|
};
|
|
3294
3297
|
const ensureInstalled = () => {
|
|
@@ -3300,7 +3303,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3300
3303
|
installedSource = config.source;
|
|
3301
3304
|
return true;
|
|
3302
3305
|
}
|
|
3303
|
-
ports.log(`
|
|
3306
|
+
ports.log(`agent-shim headroom supervisor: installing headroom from ${config.source}`);
|
|
3304
3307
|
const result = ports.install(config.source);
|
|
3305
3308
|
if (!result.ok) {
|
|
3306
3309
|
return false;
|
|
@@ -3313,10 +3316,10 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3313
3316
|
}
|
|
3314
3317
|
const version = ports.headroomVersion() ?? "unknown";
|
|
3315
3318
|
writeOwnState({ supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
|
|
3316
|
-
ports.log(`
|
|
3319
|
+
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
|
|
3317
3320
|
const orphanPid = previousState?.headroomPid;
|
|
3318
3321
|
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
3319
|
-
ports.log(`
|
|
3322
|
+
ports.log(`agent-shim headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
|
|
3320
3323
|
ports.stopProcess(orphanPid);
|
|
3321
3324
|
}
|
|
3322
3325
|
let headroomPid;
|
|
@@ -3338,13 +3341,13 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3338
3341
|
pruneDeadSessions(fs2, paths.headroomSessionsDir, ports.isRunning);
|
|
3339
3342
|
const ownsNow = ownsState();
|
|
3340
3343
|
if (!ownsNow && !superseded) {
|
|
3341
|
-
ports.log("
|
|
3344
|
+
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
3342
3345
|
}
|
|
3343
3346
|
superseded = !ownsNow;
|
|
3344
3347
|
const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
|
|
3345
3348
|
if (headroomPid === void 0 || crashed) {
|
|
3346
3349
|
if (crashed) {
|
|
3347
|
-
ports.log(`
|
|
3350
|
+
ports.log(`agent-shim headroom supervisor: headroom pid ${String(headroomPid)} died`);
|
|
3348
3351
|
headroomPid = void 0;
|
|
3349
3352
|
runningHash = void 0;
|
|
3350
3353
|
runningSettingsHash = void 0;
|
|
@@ -3373,7 +3376,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3373
3376
|
lastPort = port;
|
|
3374
3377
|
if (previousSticky !== void 0 && port !== previousSticky) {
|
|
3375
3378
|
ports.log(
|
|
3376
|
-
`
|
|
3379
|
+
`agent-shim headroom supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(port)} (sessions launched against the old port are stale until they relaunch)`
|
|
3377
3380
|
);
|
|
3378
3381
|
}
|
|
3379
3382
|
const recorded = writeOwnState({
|
|
@@ -3390,12 +3393,12 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3390
3393
|
fs2.removeRecursive(paths.headroomLockFile);
|
|
3391
3394
|
}
|
|
3392
3395
|
idleSince = void 0;
|
|
3393
|
-
ports.log(`
|
|
3396
|
+
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
3394
3397
|
} else {
|
|
3395
3398
|
ports.stopProcess(pid);
|
|
3396
3399
|
consecutiveFailures += 1;
|
|
3397
3400
|
ports.log(
|
|
3398
|
-
`
|
|
3401
|
+
`agent-shim headroom supervisor: headroom did not become ready on port ${String(port)} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
|
|
3399
3402
|
);
|
|
3400
3403
|
await ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
3401
3404
|
continue;
|
|
@@ -3403,7 +3406,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3403
3406
|
} else {
|
|
3404
3407
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3405
3408
|
if (sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3406
|
-
ports.log("
|
|
3409
|
+
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3407
3410
|
ports.stopProcess(headroomPid);
|
|
3408
3411
|
headroomPid = void 0;
|
|
3409
3412
|
runningHash = void 0;
|
|
@@ -3417,7 +3420,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3417
3420
|
const idleMs = ports.now() - idleSince;
|
|
3418
3421
|
if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE2) {
|
|
3419
3422
|
ports.log(
|
|
3420
|
-
`
|
|
3423
|
+
`agent-shim headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
3421
3424
|
);
|
|
3422
3425
|
ports.stopProcess(headroomPid);
|
|
3423
3426
|
writeOwnState({
|
|
@@ -3476,12 +3479,12 @@ function detectAmbientCredential(env, injected) {
|
|
|
3476
3479
|
return void 0;
|
|
3477
3480
|
}
|
|
3478
3481
|
function formatAmbientCredentialGuardMessage(variable, identityName) {
|
|
3479
|
-
const identitySetCommand = identityName === void 0 ? "
|
|
3482
|
+
const identitySetCommand = identityName === void 0 ? "agent-shim identity set <name> --allow-ambient-credential" : `agent-shim identity set ${identityName} --allow-ambient-credential`;
|
|
3480
3483
|
return [
|
|
3481
3484
|
`error: ${variable} is set in the environment. This identity's isolated`,
|
|
3482
3485
|
"credential would be bypassed \u2014 every identity authenticates as this same key",
|
|
3483
3486
|
"while it's set. Unset it, or if this is deliberate, opt in per-launch with",
|
|
3484
|
-
"
|
|
3487
|
+
"AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL=1, or persistently for this identity with",
|
|
3485
3488
|
`\`${identitySetCommand}\`.`
|
|
3486
3489
|
].join("\n");
|
|
3487
3490
|
}
|
package/dist/types/cliError.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare const EXIT_FAILURE = 1;
|
|
|
3
3
|
/** Exit status for a usage error: an unknown command or option, a malformed flag value, or required input missing with no terminal to prompt on. */
|
|
4
4
|
export declare const EXIT_USAGE = 2;
|
|
5
5
|
/**
|
|
6
|
-
* Base class for every error this CLI throws to represent an expected, user-facing failure: bad input, a missing identity/profile/rule, a malformed config file. `reportFatalError` prints this class as `
|
|
6
|
+
* Base class for every error this CLI throws to represent an expected, user-facing failure: bad input, a missing identity/profile/rule, a malformed config file. `reportFatalError` prints this class as `agent-shim: <message>` with no stack trace and exits with `exitCode`; anything that does not extend it is an unexpected bug, reported by message alone unless `AGENT_SHIM_DEBUG` asks for the stack.
|
|
7
7
|
*/
|
|
8
8
|
export declare abstract class CliError extends Error {
|
|
9
9
|
/** The process exit status this failure maps to. `EXIT_FAILURE` unless a subclass says otherwise. */
|
|
@@ -17,9 +17,9 @@ export interface ClassificationPattern {
|
|
|
17
17
|
export interface ClassifyResult {
|
|
18
18
|
/** Every input name mapped to its category, or to null when nothing in either map recognises it. */
|
|
19
19
|
readonly classification: ReadonlyMap<string, CategoryName | null>;
|
|
20
|
-
/** The input names that nothing recognised, in input order. These are the names `
|
|
20
|
+
/** The input names that nothing recognised, in input order. These are the names `agent-shim configure` prompts about. */
|
|
21
21
|
readonly unclassified: readonly string[];
|
|
22
|
-
/** For each classified name, the pattern that decided it — used by `
|
|
22
|
+
/** For each classified name, the pattern that decided it — used by `agent-shim check` to explain a classification. */
|
|
23
23
|
readonly decidedBy: ReadonlyMap<string, ClassificationPattern>;
|
|
24
24
|
}
|
|
25
25
|
/** True when `pattern` contains no glob metacharacter, so it matches exactly one literal name. */
|
|
@@ -40,7 +40,7 @@ export declare function loadClassification(paths: LayoutPaths): LoadedClassifica
|
|
|
40
40
|
/**
|
|
41
41
|
* Classifies a list of real top-level `~/.claude` entry names against the shipped category map plus an optional local overlay.
|
|
42
42
|
*
|
|
43
|
-
* An unrecognised name maps to `null` rather than being silently assumed safe or silently dropped: the resolver treats it as not-shared and reports it, and `
|
|
43
|
+
* An unrecognised name maps to `null` rather than being silently assumed safe or silently dropped: the resolver treats it as not-shared and reports it, and `agent-shim configure` prompts for an answer that is then written to the local overlay.
|
|
44
44
|
*/
|
|
45
45
|
export declare function classifyEntries(names: readonly string[], options: {
|
|
46
46
|
defaults: CategoryClassification;
|
|
@@ -16,13 +16,13 @@ export declare function isCategoryName(name: string): name is CategoryName;
|
|
|
16
16
|
*/
|
|
17
17
|
export type CategoryMap = Partial<Record<OverridableCategory, boolean>>;
|
|
18
18
|
/**
|
|
19
|
-
* Expands the `all` pseudo-category key
|
|
19
|
+
* Expands the `all` pseudo-category key, dropping `all` itself from the result. `all: true` opens every category meant to be shared across identities, which is every overridable category except `runtime`: `runtime` holds live per-process and per-machine state (daemon locks and auth status, PIDs, IDE and shell snapshots) that must never be pooled across running identities, so it opens only when named explicitly. `all: false` closes every overridable category, `runtime` included, so it also closes a `runtime` an earlier layer opened. An explicit named category always wins over the `all` expansion regardless of where it appears relative to `all` in the input: `{ all: true, runtime: true }` opens `runtime` too, and `{ all: true, history: false }` means "share everything except history", not "history is false, then immediately overwritten back to true".
|
|
20
20
|
*
|
|
21
|
-
* This is the one shared implementation `CategoryMapSchema`'s own transform, `launcher/cliOverride.ts`'s `--category`/`
|
|
21
|
+
* This is the one shared implementation `CategoryMapSchema`'s own transform, `launcher/cliOverride.ts`'s `--category`/`AGENT_SHIM_CATEGORY_OVERRIDE` handling, and `configProfiles.ts`'s `agent-shim profile set --category` all call, so `all` means the same thing regardless of which of those three input paths it arrived through.
|
|
22
22
|
*/
|
|
23
23
|
export declare function expandAllCategoryKey(pairs: Readonly<Record<string, boolean>>): Record<string, boolean>;
|
|
24
24
|
/**
|
|
25
|
-
* The category toggle map as written by hand: the four overridable categories, plus `all` as shorthand for "every
|
|
25
|
+
* The category toggle map as written by hand: the four overridable categories, plus `all` as shorthand for "every shareable category at once" (expanded by `expandAllCategoryKey` above). `secret` is omitted from the shape entirely, so `{ "categories": { "secret": true } }` is rejected at parse time rather than relying solely on the resolver's runtime floor check. The closed shape is also what lets the published JSON Schema offer real key-name autocomplete, which an open record type cannot.
|
|
26
26
|
*/
|
|
27
27
|
export declare const CategoryMapSchema: z.ZodPipe<z.ZodObject<{
|
|
28
28
|
all: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -40,7 +40,7 @@ export declare const CategoryMapSchema: z.ZodPipe<z.ZodObject<{
|
|
|
40
40
|
/** A duration literal: a positive integer count followed by a unit. Used by `newerThan`/`olderThan`. */
|
|
41
41
|
export declare const DURATION_RE: RegExp;
|
|
42
42
|
/**
|
|
43
|
-
* A conditional guard on an entries value or a whole directory rule. Every field present within one `when` object must hold (AND logic). An empty object is vacuously true — `
|
|
43
|
+
* A conditional guard on an entries value or a whole directory rule. Every field present within one `when` object must hold (AND logic). An empty object is vacuously true — `agent-shim check` warns about it, it is never an error.
|
|
44
44
|
*/
|
|
45
45
|
export declare const WhenSchema: z.ZodObject<{
|
|
46
46
|
newerThan: z.ZodOptional<z.ZodString>;
|
|
@@ -93,7 +93,7 @@ export declare const CREDENTIAL_TARGETS: readonly ["bearer", "apiKey", "oauthTok
|
|
|
93
93
|
export type CredentialTarget = (typeof CREDENTIAL_TARGETS)[number];
|
|
94
94
|
/** The targets a provider's credential may use: every target except `oauthToken`, which only an identity can carry. */
|
|
95
95
|
export declare const PROVIDER_CREDENTIAL_TARGETS: readonly ["bearer", "apiKey"];
|
|
96
|
-
/** The environment variable each credential target exports the token as. These are also the variables no provider `env` block may set, since
|
|
96
|
+
/** The environment variable each credential target exports the token as. These are also the variables no provider `env` block may set, since agent-shim sets and clears them itself. */
|
|
97
97
|
export declare const CREDENTIAL_TARGET_VARS: {
|
|
98
98
|
readonly bearer: "ANTHROPIC_AUTH_TOKEN";
|
|
99
99
|
readonly apiKey: "ANTHROPIC_API_KEY";
|
|
@@ -133,7 +133,7 @@ export declare const CredentialSourceSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
133
133
|
literal: z.ZodString;
|
|
134
134
|
}, z.core.$strict>]>;
|
|
135
135
|
export type CredentialSource = z.infer<typeof CredentialSourceSchema>;
|
|
136
|
-
/** Where a cached credential is kept: the macOS login Keychain, or a mode-0600 file under the
|
|
136
|
+
/** Where a cached credential is kept: the macOS login Keychain, or a mode-0600 file under the agent-shim home. */
|
|
137
137
|
export declare const CREDENTIAL_CACHE_STORES: readonly ["keychain", "file"];
|
|
138
138
|
export type CredentialCacheStore = (typeof CREDENTIAL_CACHE_STORES)[number];
|
|
139
139
|
/**
|
|
@@ -187,7 +187,7 @@ export declare const CredentialSchema: z.ZodObject<{
|
|
|
187
187
|
}, z.core.$strict>>;
|
|
188
188
|
}, z.core.$strict>;
|
|
189
189
|
export type Credential = z.infer<typeof CredentialSchema>;
|
|
190
|
-
/** The kinds of provider: `http` is an Anthropic Messages endpoint at a fixed `baseUrl` (the default when `kind` is absent), and `codex` is ChatGPT's Codex backend reached through
|
|
190
|
+
/** The kinds of provider: `http` is an Anthropic Messages endpoint at a fixed `baseUrl` (the default when `kind` is absent), and `codex` is ChatGPT's Codex backend reached through agent-shim's own supervised translation daemon. */
|
|
191
191
|
export declare const PROVIDER_KINDS: readonly ["http", "codex"];
|
|
192
192
|
/**
|
|
193
193
|
* The Claude model tiers the codex translator maps, in the order they are matched: the first tier whose name is a substring of the requested model (case-insensitive) decides the codex model. `fable` comes before `opus` because a fable model name is the most specific.
|
|
@@ -382,7 +382,7 @@ export type Provider = z.infer<typeof ProviderSchema>;
|
|
|
382
382
|
/** True when `provider` is a codex provider. `kind` is the tag, since a codex provider's own distinguishing block is optional. */
|
|
383
383
|
export declare function isCodexProvider(provider: Provider): provider is CodexProvider;
|
|
384
384
|
/**
|
|
385
|
-
* A named, reusable configuration profile at `~/.
|
|
385
|
+
* A named, reusable configuration profile at `~/.agent-shim/config-profiles/<name>.json`.
|
|
386
386
|
*
|
|
387
387
|
* `extends` is a flat array of other profiles' *names*, deliberately not a self-referential `z.lazy()` schema: nothing in this shape points back at a profile object, so each file validates in isolation and the extends graph is walked at resolve time. That also means Zod cannot detect a circular `extends` definition — the walker in `src/resolve/extends.ts` carries its own cycle guard.
|
|
388
388
|
*/
|
|
@@ -431,7 +431,7 @@ 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>;
|
|
434
|
-
/** One directory rule in `~/.
|
|
434
|
+
/** One directory rule in `~/.agent-shim/directory-rules.json`, scoped to an explicit absolute (or `~`-rooted) path. */
|
|
435
435
|
export declare const DirectoryRuleSchema: z.ZodObject<{
|
|
436
436
|
entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
437
437
|
value: z.ZodBoolean;
|
|
@@ -477,7 +477,7 @@ export declare const DirectoryRuleSchema: z.ZodObject<{
|
|
|
477
477
|
}, z.core.$strict>>;
|
|
478
478
|
}, z.core.$strict>;
|
|
479
479
|
export type DirectoryRule = z.infer<typeof DirectoryRuleSchema>;
|
|
480
|
-
/** The `~/.
|
|
480
|
+
/** The `~/.agent-shim/directory-rules.json` file. */
|
|
481
481
|
export declare const DirectoryRulesSchema: z.ZodObject<{
|
|
482
482
|
$schema: z.ZodOptional<z.ZodString>;
|
|
483
483
|
rules: z.ZodArray<z.ZodObject<{
|
|
@@ -527,7 +527,7 @@ export declare const DirectoryRulesSchema: z.ZodObject<{
|
|
|
527
527
|
}, z.core.$strict>;
|
|
528
528
|
export type DirectoryRules = z.infer<typeof DirectoryRulesSchema>;
|
|
529
529
|
/**
|
|
530
|
-
* A committed `.
|
|
530
|
+
* A committed `.agent-shim.json` (or its gitignored `.agent-shim.local.json` sibling). Structurally a directory rule without the `path` field: its scope is implicit — wherever the file lives, and everything below it — which is exactly what makes it portable across clone locations.
|
|
531
531
|
*/
|
|
532
532
|
export declare const PortableConfigSchema: z.ZodObject<{
|
|
533
533
|
entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
@@ -607,7 +607,7 @@ export declare const HEADROOM_DEFAULT_IDLE_SHUTDOWN_MINUTES = 15;
|
|
|
607
607
|
* The default `frontdoor.idleShutdownMinutes`, the same trade-off as headroom's: warm through a break, gone the same working day once abandoned.
|
|
608
608
|
*/
|
|
609
609
|
export declare const FRONTDOOR_DEFAULT_IDLE_SHUTDOWN_MINUTES = 15;
|
|
610
|
-
/** The user-global `~/.
|
|
610
|
+
/** The user-global `~/.agent-shim/config.json`. */
|
|
611
611
|
export declare const GlobalConfigSchema: z.ZodObject<{
|
|
612
612
|
$schema: z.ZodOptional<z.ZodString>;
|
|
613
613
|
defaultConfigProfile: z.ZodOptional<z.ZodString>;
|
|
@@ -729,7 +729,7 @@ export declare const CategoryClassificationSchema: z.ZodObject<{
|
|
|
729
729
|
settings: z.ZodArray<z.ZodString>;
|
|
730
730
|
}, z.core.$strict>;
|
|
731
731
|
export type CategoryClassification = z.infer<typeof CategoryClassificationSchema>;
|
|
732
|
-
/** The gitignored `~/.
|
|
732
|
+
/** The gitignored `~/.agent-shim/categories.local.json` overlay: any subset of the classification lists, answering "what category is this new entry?" without editing the shipped default map. */
|
|
733
733
|
export declare const CategoryClassificationOverlaySchema: z.ZodObject<{
|
|
734
734
|
$schema: z.ZodOptional<z.ZodString>;
|
|
735
735
|
secret: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* How a launch's capability token is presented and checked. The token reaches the child twice: as the `x-
|
|
2
|
+
* How a launch's capability token is presented and checked. The token reaches the child twice: as the `x-agent-shim-auth` header on every request it sends (see `AUTH_HEADER`), and as the password in the `HTTPS_PROXY` URL an OAuth launch gets, which every proxy-aware client turns into `Proxy-Authorization: Basic ...` on its CONNECT request. Both are checked against the same live registry with the same constant-time comparison.
|
|
3
3
|
*/
|
|
4
4
|
/** The user name in the proxy URL. A label only: the surface checks the password, which is the capability, and ignores the user name, so nothing depends on how a client spells it. */
|
|
5
|
-
export declare const CONNECT_PROXY_USER = "
|
|
5
|
+
export declare const CONNECT_PROXY_USER = "agent-shim";
|
|
6
6
|
/** The realm the CONNECT surface's 407 challenge names, so a client that waits for a challenge before sending credentials knows which ones to send. */
|
|
7
|
-
export declare const CONNECT_PROXY_REALM = "
|
|
7
|
+
export declare const CONNECT_PROXY_REALM = "agent-shim front door";
|
|
8
8
|
/**
|
|
9
9
|
* The `HTTPS_PROXY` value for a launch: the CONNECT surface's loopback address with the capability as the URL's password. Both userinfo halves are percent-encoded, because a client percent-decodes userinfo before it builds the `Proxy-Authorization` value, so the surface receives exactly the token whatever characters it holds.
|
|
10
10
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import * as net from "node:net";
|
|
3
3
|
/**
|
|
4
|
-
* The front door's CONNECT surface, so an OAuth session can have
|
|
4
|
+
* The front door's CONNECT surface, so an OAuth session can have agent-shim's routing AND Claude Code's Remote Control at once: Remote Control refuses any `ANTHROPIC_BASE_URL` other than the real API, but happily honours `HTTPS_PROXY`, so OAuth launches route at the proxy layer instead.
|
|
5
5
|
*
|
|
6
6
|
* Nothing is tunnelled or intercepted for a client that has not authenticated: every CONNECT request must present a live launch's capability as its proxy credential (`Proxy-Authorization: Basic`, which clients derive from the credential in the `HTTPS_PROXY` URL the launcher sets), or it is answered 407 before any target is dialled, so the port is no open proxy for other local processes. Pending and authenticated connections are bounded by `ConnectLimits`.
|
|
7
7
|
*
|
|
@@ -69,7 +69,7 @@ export interface LeafCert {
|
|
|
69
69
|
readonly certPem: string;
|
|
70
70
|
readonly keyPem: string;
|
|
71
71
|
}
|
|
72
|
-
/** Generates the local CA: one RSA keypair, self-signed, CA-only by basicConstraints and keyUsage, with a random serial. Built on `node:crypto` alone (see `x509.ts`), no openssl, so the same code works on every platform
|
|
72
|
+
/** Generates the local CA: one RSA keypair, self-signed, CA-only by basicConstraints and keyUsage, with a random serial. Built on `node:crypto` alone (see `x509.ts`), no openssl, so the same code works on every platform agent-shim ships to. */
|
|
73
73
|
export declare function generateCa(now: Readonly<Date>): CaMaterial;
|
|
74
74
|
/** The names the front door's provider listener answers to: the loopback address every provider session's base URL names, plus the loopback hostname for a client that spells it that way. */
|
|
75
75
|
export declare const LOOPBACK_LEAF_NAMES: readonly string[];
|
|
@@ -15,13 +15,13 @@ export interface EnsureFrontDoorPorts {
|
|
|
15
15
|
readonly sleep: (ms: number) => void;
|
|
16
16
|
/** Spawns the detached front-door supervisor, returning its pid. */
|
|
17
17
|
readonly spawnSupervisor: (paths: LayoutPaths) => number;
|
|
18
|
-
/** Authenticates the provider listener on `port`: a TLS handshake chaining to
|
|
18
|
+
/** Authenticates the provider listener on `port`: a TLS handshake chaining to agent-shim's CA and a healthy answer, with no capability sent (see `probeFrontDoorSync`). */
|
|
19
19
|
readonly verifyListener: (port: number) => ListenerVerdict;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Brings the front door up for this launch, or finds it serving, and registers this launcher pid in its session registry: the fact that keeps the door from idling out while this session lives, and the record that holds this launch's capability token. The same lock-and-poll coordination as headroom's and the old codex daemon's ensure: the exclusive-create start lock decides which of several concurrent launches spawns the one supervisor, everyone waits on state.json, and a lock whose holder died is removed and retried. Ready means state names a live supervisor and both listeners' ports, which the supervisor writes only after both have bound (and the provider listener has answered its own health probe).
|
|
23
23
|
*
|
|
24
|
-
* A live pid in state proves nothing about who holds the port it names: the door may have crashed and left the port to anyone, or the pid may have been reused by an unrelated process. So before anything is registered or returned, the provider listener is authenticated over TLS against
|
|
24
|
+
* A live pid in state proves nothing about who holds the port it names: the door may have crashed and left the port to anyone, or the pid may have been reused by an unrelated process. So before anything is registered or returned, the provider listener is authenticated over TLS against agent-shim's CA. A listener that fails (wrong certificate, nothing answering, a bad answer) is treated as a stale or hostile owner: its pid is distrusted for the rest of this call and a replacement supervisor is spawned, which binds a free port when the sticky one is held and is then verified in turn. If a supervisor this call spawned also fails verification, the launch is refused naming the reason and the daemon log; no capability is written and no address is handed to the child.
|
|
25
25
|
*/
|
|
26
26
|
export declare function ensureFrontDoor(params: {
|
|
27
27
|
readonly paths: LayoutPaths;
|
|
@@ -6,7 +6,7 @@ export type ListenerVerdict = {
|
|
|
6
6
|
readonly reason: string;
|
|
7
7
|
};
|
|
8
8
|
/**
|
|
9
|
-
* Authenticates the front door's provider listener before a launch trusts it: a TLS handshake that must chain to
|
|
9
|
+
* Authenticates the front door's provider listener before a launch trusts it: a TLS handshake that must chain to agent-shim's CA (whose key only this user can read) for 127.0.0.1, then a healthy answer. A process that merely holds the port (a stale state file's port re-bound by someone else, or a reused pid's listener) cannot complete that handshake, so it fails here and the launch never hands its child that address.
|
|
10
10
|
*
|
|
11
11
|
* Synchronous because the launcher is synchronous right through to `spawnSync`: the HTTPS request runs on a worker thread and this thread blocks on a shared buffer until the worker posts its verdict.
|
|
12
12
|
*/
|
|
@@ -17,7 +17,7 @@ export interface FrontDoorSupervisorPorts {
|
|
|
17
17
|
/** Zombie-aware liveness, the notion every coordination decision in this project uses. */
|
|
18
18
|
readonly isRunning: (pid: number) => boolean;
|
|
19
19
|
/**
|
|
20
|
-
* Starts the provider listener in this process on `preferredPort` (falling back to any free port when the sticky one is taken, decided at bind time rather than by a racy check-then-bind probe) and resolves once it has bound and answered its own health probe over TLS. This is the HTTPS listener every provider session's base URL points at, serving a loopback leaf signed by
|
|
20
|
+
* Starts the provider listener in this process on `preferredPort` (falling back to any free port when the sticky one is taken, decided at bind time rather than by a racy check-then-bind probe) and resolves once it has bound and answered its own health probe over TLS. This is the HTTPS listener every provider session's base URL points at, serving a loopback leaf signed by agent-shim's CA.
|
|
21
21
|
*/
|
|
22
22
|
readonly startProviderListener: (preferredPort: number | undefined) => Promise<FrontDoorListenerHandle>;
|
|
23
23
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** An IP address as the 4 or 16 octets an iPAddress GeneralName carries. */
|
|
2
2
|
export declare function ipBytes(address: string): Buffer;
|
|
3
|
-
/** A key pair in the PEM forms
|
|
3
|
+
/** A key pair in the PEM forms agent-shim persists. */
|
|
4
4
|
export interface PemKeyPair {
|
|
5
5
|
readonly certPem: string;
|
|
6
6
|
readonly keyPem: string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The library surface of
|
|
2
|
+
* The library surface of agent-shim: the pure, port-injected parts other tools can call in-process instead of shelling out to the CLI. Four groups, none of which imports interactive prompting or argument parsing:
|
|
3
3
|
*
|
|
4
4
|
* - identity and configuration-profile resolution and farm sync (`resolveDecisions`, `resyncFarm`, `buildEntryFacts`, ...), pure over facts and ports passed in;
|
|
5
5
|
* - routing: the front door's Remote-Control-preserving, capability-authenticated CONNECT surface and its certificate authority, the front-door supervisor and ensure lifecycle, and the headroom supervisor and ensure lifecycle, each pure over its injected ports;
|
|
@@ -4,8 +4,8 @@ import { type ResolvedState } from "../resolve/pipeline";
|
|
|
4
4
|
import type { CascadeInput } from "../resolve/walk";
|
|
5
5
|
import type { Diagnostic, EntryFacts } from "../resolve/types";
|
|
6
6
|
import type { FarmFs } from "./ports";
|
|
7
|
-
/** The manifest file each farm carries at its own root, recording what that resync built. Named distinctly from anything Claude Code itself writes into a config directory. */
|
|
8
|
-
export declare const FARM_MANIFEST_FILENAME = ".
|
|
7
|
+
/** The manifest file each farm carries at its own root, recording what that resync built. Named distinctly from anything Claude Code itself writes into a config directory. A farm built under the former name carries `LEGACY_FARM_MANIFEST_FILENAME` instead, which is read as a fallback and never carried into the next farm, whose resync writes this name. */
|
|
8
|
+
export declare const FARM_MANIFEST_FILENAME = ".agent-shim-farm.json";
|
|
9
9
|
/** Reads and validates the manifest at a farm root, or undefined when it is missing, unparseable, or does not validate. */
|
|
10
10
|
export declare function readFarmManifest(fs: FarmFs, farmRoot: string): FarmManifest | undefined;
|
|
11
11
|
/** Everything `buildEntryFacts` needs. Everything except the tree itself is injected, so the fact manifest is reproducible from a fake filesystem, a fake clock, and a fake branch. */
|
|
@@ -60,7 +60,7 @@ export interface CarryOverResult {
|
|
|
60
60
|
*
|
|
61
61
|
* One category is not ambiguous, though: `runtime`'s own definition (see `config/categories.default.json`'s category table in the README) is specifically "live per-process or per-machine artifacts" — daemon locks, an MCP auth-needed cache, an update-check result — that make no sense being preserved across a swap at all, let alone fought over. When `classification` is given, a colliding name whose category resolves to `runtime` is discarded from the superseded copy and left exactly as the new farm already has it, with no data ever moved: `keep-new` is not a judgement call for this category, it is what the category already means. This needs only the name's *static* classification, never the resolved shared/not-shared decision for the current directory — a `runtime` entry is disposable whether or not this identity currently chooses to share it, so no cascade resolution is needed to make the call.
|
|
62
62
|
*
|
|
63
|
-
* Exported so `launcher/farmResolve.ts` can reuse this exact collision detection (and the same `runtime` auto-resolution) for `
|
|
63
|
+
* Exported so `launcher/farmResolve.ts` can reuse this exact collision detection (and the same `runtime` auto-resolution) for `agent-shim identity resolve-conflicts`'s interactive pass, rather than a second implementation that could drift from this one.
|
|
64
64
|
*/
|
|
65
65
|
export declare function carryOver(params: CarryOverParams): CarryOverResult;
|
|
66
66
|
/** What recovery found and did. */
|
|
@@ -101,10 +101,10 @@ export declare function recoverFarm(params: RecoverFarmParams): RecoveryResult;
|
|
|
101
101
|
/** Inputs to `resyncFarm`. */
|
|
102
102
|
export interface ResyncFarmParams {
|
|
103
103
|
readonly fs: FarmFs;
|
|
104
|
-
/** `~/.
|
|
104
|
+
/** `~/.agent-shim/identities` — the farm root's parent, and where the lock, scratch trees, and superseded farms all live. */
|
|
105
105
|
readonly identitiesDir: string;
|
|
106
106
|
readonly identity: string;
|
|
107
|
-
/** The configuration profile this launch resolved to, recorded in the manifest for `
|
|
107
|
+
/** The configuration profile this launch resolved to, recorded in the manifest for `agent-shim check`. */
|
|
108
108
|
readonly configProfile?: string;
|
|
109
109
|
/** The canonical `~/.claude` every farm symlink points back into. */
|
|
110
110
|
readonly claudeHome: string;
|