claude-use 4.5.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,15 +31,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AMBIENT_CREDENTIAL_VARS: () => AMBIENT_CREDENTIAL_VARS,
34
+ CONNECT_INTERCEPT_HOST: () => CONNECT_INTERCEPT_HOST,
35
+ CONNECT_LIMITS: () => CONNECT_LIMITS,
34
36
  FARM_MANIFEST_FILENAME: () => FARM_MANIFEST_FILENAME,
35
- HEADROOM_SERVED_PATH_PREFIX: () => HEADROOM_SERVED_PATH_PREFIX,
37
+ FrontDoorStartError: () => FrontDoorStartError,
36
38
  HeadroomStartError: () => HeadroomStartError,
37
- MITM_INTERCEPT_HOST: () => MITM_INTERCEPT_HOST,
39
+ ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
38
40
  buildEntryFacts: () => buildEntryFacts,
39
41
  carryOver: () => carryOver,
40
42
  createLeafCache: () => createLeafCache,
41
43
  detectAmbientCredential: () => detectAmbientCredential,
42
44
  ensureCa: () => ensureCa,
45
+ ensureFrontDoor: () => ensureFrontDoor,
43
46
  ensureHeadroom: () => ensureHeadroom,
44
47
  evaluateAmbientCredentialGuard: () => evaluateAmbientCredentialGuard,
45
48
  formatAmbientCredentialGuardMessage: () => formatAmbientCredentialGuardMessage,
@@ -49,16 +52,17 @@ __export(index_exports, {
49
52
  mintLeaf: () => mintLeaf,
50
53
  parseConnectTarget: () => parseConnectTarget,
51
54
  readFarmManifest: () => readFarmManifest,
52
- realMitmCertStore: () => realMitmCertStore,
53
- realMitmEffects: () => realMitmEffects,
55
+ realConnectCertStore: () => realConnectCertStore,
56
+ realConnectEffects: () => realConnectEffects,
54
57
  recoverFarm: () => recoverFarm,
55
58
  recoveryDiagnostics: () => recoveryDiagnostics,
56
59
  resolveDecisions: () => resolveDecisions,
57
60
  resolveSupervisorConfig: () => resolveSupervisorConfig,
58
61
  resyncFarm: () => resyncFarm,
62
+ runFrontDoorSupervisor: () => runFrontDoorSupervisor,
59
63
  runSupervisor: () => runSupervisor,
60
- servedByHeadroom: () => servedByHeadroom,
61
- startMitmServer: () => startMitmServer,
64
+ servedByPipeline: () => servedByPipeline,
65
+ startConnectServer: () => startConnectServer,
62
66
  topLevelNames: () => topLevelNames
63
67
  });
64
68
  module.exports = __toCommonJS(index_exports);
@@ -1798,15 +1802,213 @@ function recoveryDiagnostics(recovery, identity) {
1798
1802
  ];
1799
1803
  }
1800
1804
 
1801
- // src/headroom/mitm.ts
1805
+ // src/frontdoor/connect.ts
1802
1806
  var fs = __toESM(require("node:fs"), 1);
1803
1807
  var http = __toESM(require("node:http"), 1);
1804
1808
  var https = __toESM(require("node:https"), 1);
1805
1809
  var net = __toESM(require("node:net"), 1);
1806
1810
  var tls = __toESM(require("node:tls"), 1);
1807
1811
  var import_node_forge = __toESM(require("node-forge"), 1);
1808
- var MITM_INTERCEPT_HOST = "api.anthropic.com";
1809
- var HEADROOM_SERVED_PATH_PREFIX = "/v1/";
1812
+
1813
+ // src/frontdoor/capability.ts
1814
+ var CONNECT_PROXY_REALM = "claude-use front door";
1815
+ var BASIC_SCHEME = "basic";
1816
+ function capabilityFromProxyAuthorization(value) {
1817
+ const trimmed = value.trim();
1818
+ const space = trimmed.indexOf(" ");
1819
+ if (space === -1 || trimmed.slice(0, space).toLowerCase() !== BASIC_SCHEME) {
1820
+ return void 0;
1821
+ }
1822
+ const userPass = Buffer.from(trimmed.slice(space + 1).trim(), "base64").toString("utf8");
1823
+ const colon = userPass.indexOf(":");
1824
+ if (colon === -1) {
1825
+ return void 0;
1826
+ }
1827
+ const password = userPass.slice(colon + 1);
1828
+ return password === "" ? void 0 : password;
1829
+ }
1830
+
1831
+ // src/frontdoor/state.ts
1832
+ var import_node_path5 = __toESM(require("node:path"), 1);
1833
+ var import_zod3 = require("zod");
1834
+ var FrontDoorSessionSchema = import_zod3.z.strictObject({ pid: import_zod3.z.number().int().positive(), startedAt: import_zod3.z.number(), token: import_zod3.z.string().min(1) });
1835
+ var FrontDoorStateSchema = import_zod3.z.strictObject({
1836
+ /** The supervisor process serving the front door. Alive means both listeners are up in that process. */
1837
+ supervisorPid: import_zod3.z.number().int().positive().optional(),
1838
+ /** The loopback port the HTTPS provider listener serves on. Absent until the listener has bound and answered its health probe, so "port is set" is itself the ready signal a launcher polls for. */
1839
+ port: import_zod3.z.number().int().positive().optional(),
1840
+ /**
1841
+ * The sticky port preference for the provider listener: kept across crashes, restarts and idle shutdowns so every provider session, whose base URL was frozen at launch, keeps finding the front door at the same address.
1842
+ */
1843
+ lastPort: import_zod3.z.number().int().positive().optional(),
1844
+ /**
1845
+ * The loopback port the CONNECT surface listens on. Absent whenever no supervisor is serving it, so "connectPort is set" is the ready signal an OAuth launch polls for alongside `port`.
1846
+ */
1847
+ connectPort: import_zod3.z.number().int().positive().optional(),
1848
+ /**
1849
+ * The sticky port preference for the CONNECT surface, the exact analogue of `lastPort` for the second listener: it survives every shutdown, because every OAuth session's environment was frozen at launch with HTTPS_PROXY pointing at this address and a restart that moves strands them.
1850
+ */
1851
+ lastConnectPort: import_zod3.z.number().int().positive().optional(),
1852
+ /**
1853
+ * The loopback port the direct listener serves on: the listener a headroom hop tells the daemon to forward routed traffic back to. Sticky like the others, because headroom's allowlist admits its exact origin and an ephemeral port would invalidate the allowlist on every restart.
1854
+ */
1855
+ directPort: import_zod3.z.number().int().positive().optional(),
1856
+ /** The sticky port preference for the direct listener, for the reason `directPort` gives. */
1857
+ lastDirectPort: import_zod3.z.number().int().positive().optional(),
1858
+ /** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
1859
+ lastError: import_zod3.z.string().optional()
1860
+ });
1861
+ function readFrontDoorState(fs2, stateFile) {
1862
+ let raw;
1863
+ try {
1864
+ raw = fs2.readFileUtf8(stateFile);
1865
+ } catch {
1866
+ return void 0;
1867
+ }
1868
+ if (raw === void 0 || raw.trim() === "") {
1869
+ return void 0;
1870
+ }
1871
+ try {
1872
+ const parsed = FrontDoorStateSchema.safeParse(JSON.parse(raw));
1873
+ return parsed.success ? parsed.data : void 0;
1874
+ } catch {
1875
+ return void 0;
1876
+ }
1877
+ }
1878
+ function writeFrontDoorState(fs2, stateFile, state) {
1879
+ fs2.mkdirp(import_node_path5.default.dirname(stateFile));
1880
+ fs2.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
1881
+ `);
1882
+ }
1883
+ function frontDoorOrigin(state) {
1884
+ const port = state?.directPort ?? state?.lastDirectPort;
1885
+ return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
1886
+ }
1887
+ function writeFrontDoorSession(fs2, sessionsDir, session) {
1888
+ fs2.mkdirPrivate(sessionsDir);
1889
+ fs2.writeFilePrivate(import_node_path5.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
1890
+ `);
1891
+ }
1892
+ function listFrontDoorSessions(fs2, sessionsDir) {
1893
+ const sessions = [];
1894
+ for (const name of fs2.readdir(sessionsDir)) {
1895
+ if (!name.endsWith(".json")) {
1896
+ continue;
1897
+ }
1898
+ try {
1899
+ const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs2.readFileUtf8(import_node_path5.default.join(sessionsDir, name)) ?? ""));
1900
+ if (parsed.success) {
1901
+ sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
1902
+ }
1903
+ } catch {
1904
+ continue;
1905
+ }
1906
+ }
1907
+ return sessions.sort((a, b) => a.pid - b.pid);
1908
+ }
1909
+ function removeFrontDoorSession(fs2, sessionsDir, pid) {
1910
+ fs2.removeRecursive(import_node_path5.default.join(sessionsDir, `${String(pid)}.json`));
1911
+ }
1912
+ function pruneDeadFrontDoorSessions(fs2, sessionsDir, isRunning) {
1913
+ const removed = [];
1914
+ for (const session of listFrontDoorSessions(fs2, sessionsDir)) {
1915
+ if (!isRunning(session.pid)) {
1916
+ removeFrontDoorSession(fs2, sessionsDir, session.pid);
1917
+ removed.push(session.pid);
1918
+ }
1919
+ }
1920
+ return removed;
1921
+ }
1922
+
1923
+ // src/frontdoor/supervisor.ts
1924
+ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
1925
+ var FRONTDOOR_POLL_MS = 1e3;
1926
+ var MS_PER_MINUTE = 6e4;
1927
+ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
1928
+ const { fs: fs2, paths } = ports;
1929
+ const previous = readFrontDoorState(fs2, paths.frontdoorStateFile);
1930
+ let lastPort = previous?.lastPort;
1931
+ let lastConnectPort = previous?.lastConnectPort;
1932
+ let lastDirectPort = previous?.lastDirectPort;
1933
+ const sticky = () => ({
1934
+ ...lastPort === void 0 ? {} : { lastPort },
1935
+ ...lastConnectPort === void 0 ? {} : { lastConnectPort },
1936
+ ...lastDirectPort === void 0 ? {} : { lastDirectPort }
1937
+ });
1938
+ const fail = (message) => {
1939
+ writeFrontDoorState(fs2, paths.frontdoorStateFile, { lastError: message, ...sticky() });
1940
+ fs2.removeRecursive(paths.frontdoorLockFile);
1941
+ ports.log(`claude-use frontdoor supervisor: ${message}`);
1942
+ return 1;
1943
+ };
1944
+ writeFrontDoorState(fs2, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
1945
+ const previousSticky = lastPort;
1946
+ let http2;
1947
+ try {
1948
+ http2 = await ports.startProviderListener(lastPort);
1949
+ } catch (error) {
1950
+ return fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
1951
+ }
1952
+ if (previousSticky !== void 0 && http2.port !== previousSticky) {
1953
+ ports.log(`claude-use frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http2.port)} (sessions launched against the old port are stale until they relaunch)`);
1954
+ }
1955
+ lastPort = http2.port;
1956
+ const previousStickyConnect = lastConnectPort;
1957
+ let connect2;
1958
+ try {
1959
+ connect2 = await ports.startConnectListener(lastConnectPort);
1960
+ } catch (error) {
1961
+ await http2.close();
1962
+ return fail(`could not start the CONNECT surface: ${error instanceof Error ? error.message : String(error)}`);
1963
+ }
1964
+ if (previousStickyConnect !== void 0 && connect2.port !== previousStickyConnect) {
1965
+ ports.log(`claude-use 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)`);
1966
+ }
1967
+ lastConnectPort = connect2.port;
1968
+ const previousStickyDirect = lastDirectPort;
1969
+ let direct;
1970
+ try {
1971
+ direct = await ports.startDirectListener(lastDirectPort);
1972
+ } catch (error) {
1973
+ await connect2.close();
1974
+ await http2.close();
1975
+ return fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
1976
+ }
1977
+ if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
1978
+ ports.log(`claude-use frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
1979
+ }
1980
+ lastDirectPort = direct.port;
1981
+ writeFrontDoorState(fs2, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, port: http2.port, connectPort: connect2.port, directPort: direct.port, ...sticky() });
1982
+ fs2.removeRecursive(paths.frontdoorLockFile);
1983
+ ports.log(`claude-use 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)}`);
1984
+ let idleSince;
1985
+ let ticks = 0;
1986
+ for (; ; ) {
1987
+ if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
1988
+ return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
1989
+ }
1990
+ ticks += 1;
1991
+ pruneDeadFrontDoorSessions(fs2, paths.frontdoorSessionsDir, ports.isRunning);
1992
+ if (listFrontDoorSessions(fs2, paths.frontdoorSessionsDir).length === 0) {
1993
+ idleSince ??= ports.now();
1994
+ if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
1995
+ ports.log(`claude-use frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
1996
+ await direct.close();
1997
+ await connect2.close();
1998
+ await http2.close();
1999
+ writeFrontDoorState(fs2, paths.frontdoorStateFile, sticky());
2000
+ return 0;
2001
+ }
2002
+ } else {
2003
+ idleSince = void 0;
2004
+ }
2005
+ await ports.sleep(FRONTDOOR_POLL_MS);
2006
+ }
2007
+ }
2008
+
2009
+ // src/frontdoor/connect.ts
2010
+ var CONNECT_INTERCEPT_HOST = "api.anthropic.com";
2011
+ var ROUTED_PATH_PREFIX = "/v1/";
1810
2012
  var HTTPS_PORT = 443;
1811
2013
  var MAX_PORT = 65535;
1812
2014
  var SERIAL_NUMBER_BYTES = 16;
@@ -1816,9 +2018,22 @@ var CONNECT_LINE_TOKENS = 3;
1816
2018
  var HEAD_TERMINATOR = "\r\n\r\n";
1817
2019
  var MAX_CONNECT_HEAD_BYTES = 8192;
1818
2020
  var CONNECT_ESTABLISHED = "HTTP/1.1 200 Connection Established\r\n\r\n";
2021
+ var REFUSAL = {
2022
+ badRequest: "400 Bad Request",
2023
+ proxyAuthenticationRequired: "407 Proxy Authentication Required",
2024
+ requestTimeout: "408 Request Timeout",
2025
+ headTooLarge: "431 Request Header Fields Too Large",
2026
+ serviceUnavailable: "503 Service Unavailable"
2027
+ };
2028
+ var CONNECT_LIMITS = {
2029
+ headDeadlineMs: 1e4,
2030
+ maxPendingHeads: 64,
2031
+ maxTunnels: 1024,
2032
+ revalidateMs: FRONTDOOR_POLL_MS
2033
+ };
1819
2034
  var CA_VALIDITY_DAYS = 3650;
1820
2035
  var LEAF_VALIDITY_DAYS = 397;
1821
- var CA_SUBJECT_COMMON_NAME = "claude-use headroom CA";
2036
+ var CA_SUBJECT_COMMON_NAME = "claude-use front door CA";
1822
2037
  var MS_PER_DAY = 864e5;
1823
2038
  function parseConnectTarget(authority) {
1824
2039
  const colon = authority.lastIndexOf(":");
@@ -1836,8 +2051,8 @@ function parseConnectTarget(authority) {
1836
2051
  function isInterceptedHost(host, interceptHost) {
1837
2052
  return host === interceptHost;
1838
2053
  }
1839
- function servedByHeadroom(path6) {
1840
- return path6?.startsWith(HEADROOM_SERVED_PATH_PREFIX) === true;
2054
+ function servedByPipeline(path7) {
2055
+ return path7?.startsWith(ROUTED_PATH_PREFIX) === true;
1841
2056
  }
1842
2057
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
1843
2058
  "connection",
@@ -1885,7 +2100,13 @@ function generateCa(now) {
1885
2100
  cert.sign(keys.privateKey, import_node_forge.default.md.sha256.create());
1886
2101
  return { certPem: import_node_forge.default.pki.certificateToPem(cert), keyPem: import_node_forge.default.pki.privateKeyToPem(keys.privateKey) };
1887
2102
  }
1888
- function mintLeaf(ca, host, now) {
2103
+ var SAN_TYPE_DNS = 2;
2104
+ var SAN_TYPE_IP = 7;
2105
+ function mintLeaf(ca, names, now) {
2106
+ const [commonName] = names;
2107
+ if (commonName === void 0) {
2108
+ throw new Error("a leaf certificate needs at least one name");
2109
+ }
1889
2110
  const caCert = import_node_forge.default.pki.certificateFromPem(ca.certPem);
1890
2111
  const caKey = import_node_forge.default.pki.privateKeyFromPem(ca.keyPem);
1891
2112
  const keys = import_node_forge.default.pki.rsa.generateKeyPair({ bits: 2048 });
@@ -1894,13 +2115,13 @@ function mintLeaf(ca, host, now) {
1894
2115
  cert.serialNumber = import_node_forge.default.util.bytesToHex(import_node_forge.default.random.getBytesSync(SERIAL_NUMBER_BYTES));
1895
2116
  cert.validity.notBefore = new Date(now.getTime() - MS_PER_DAY);
1896
2117
  cert.validity.notAfter = new Date(now.getTime() + LEAF_VALIDITY_DAYS * MS_PER_DAY);
1897
- cert.setSubject([{ name: "commonName", value: host }]);
2118
+ cert.setSubject([{ name: "commonName", value: commonName }]);
1898
2119
  cert.setIssuer(caCert.subject.attributes);
1899
2120
  cert.setExtensions([
1900
2121
  { name: "basicConstraints", cA: false, critical: true },
1901
2122
  { name: "keyUsage", digitalSignature: true, keyEncipherment: true, critical: true },
1902
2123
  { name: "extKeyUsage", serverAuth: true },
1903
- { name: "subjectAltName", altNames: [{ type: 2, value: host }] },
2124
+ { name: "subjectAltName", altNames: names.map((name) => net.isIP(name) === 0 ? { type: SAN_TYPE_DNS, value: name } : { type: SAN_TYPE_IP, ip: name }) },
1904
2125
  { name: "subjectKeyIdentifier" },
1905
2126
  { name: "authorityKeyIdentifier", keyLocatorFields: [] }
1906
2127
  ]);
@@ -1914,7 +2135,7 @@ function createLeafCache(ca, now) {
1914
2135
  if (existing !== void 0) {
1915
2136
  return existing;
1916
2137
  }
1917
- const minted = mintLeaf(ca, host, now());
2138
+ const minted = mintLeaf(ca, [host], now());
1918
2139
  cache.set(host, minted);
1919
2140
  return minted;
1920
2141
  };
@@ -1937,17 +2158,10 @@ function caParses(ca) {
1937
2158
  return false;
1938
2159
  }
1939
2160
  }
1940
- async function startMitmServer(config, effects, preferredPort) {
2161
+ async function startConnectServer(config, effects, preferredPort) {
1941
2162
  const handler = (request3, response) => {
1942
- if (servedByHeadroom(request3.url)) {
1943
- const daemonPort = config.headroomPort();
1944
- if (daemonPort === void 0) {
1945
- const body = "claude-use headroom: the daemon is restarting";
1946
- response.writeHead(HTTP_BAD_GATEWAY, { "content-type": "text/plain", "content-length": String(body.length) });
1947
- response.end(body);
1948
- return;
1949
- }
1950
- effects.forwardHttp({ host: "127.0.0.1", port: daemonPort, tls: false }, request3, response);
2163
+ if (servedByPipeline(request3.url)) {
2164
+ config.serveRouted(request3, response);
1951
2165
  return;
1952
2166
  }
1953
2167
  effects.forwardHttp(config.upstream, request3, response);
@@ -1956,50 +2170,153 @@ async function startMitmServer(config, effects, preferredPort) {
1956
2170
  const tlsAcceptor = effects.createTlsAcceptor(config.leafFor(config.interceptHost), (secure) => {
1957
2171
  httpSession.serve(secure);
1958
2172
  });
2173
+ const ledger = createConnectionLedger(config.limits, config.isLiveCapability);
1959
2174
  const listener = await effects.listenLoopback(preferredPort, (socket) => {
1960
- handleConnect(socket, config, effects, tlsAcceptor);
2175
+ handleConnect(socket, { config, effects, tlsAcceptor, ledger });
1961
2176
  });
1962
2177
  return {
1963
2178
  port: listener.port,
1964
2179
  close: async () => {
2180
+ ledger.close();
1965
2181
  await listener.close();
1966
2182
  tlsAcceptor.close();
1967
2183
  httpSession.close();
1968
2184
  }
1969
2185
  };
1970
2186
  }
1971
- function handleConnect(socket, config, effects, tlsAcceptor) {
2187
+ function createConnectionLedger(limits, isLiveCapability) {
2188
+ const pending = /* @__PURE__ */ new Map();
2189
+ const authenticated = /* @__PURE__ */ new Map();
2190
+ let tunnels = 0;
2191
+ const leavePending = (socket) => {
2192
+ const deadline = pending.get(socket);
2193
+ if (deadline !== void 0) {
2194
+ clearTimeout(deadline);
2195
+ pending.delete(socket);
2196
+ }
2197
+ };
2198
+ const revalidation = setInterval(() => {
2199
+ for (const [capability, sockets] of authenticated) {
2200
+ if (!isLiveCapability(capability)) {
2201
+ for (const socket of sockets) {
2202
+ socket.destroy();
2203
+ }
2204
+ }
2205
+ }
2206
+ }, limits.revalidateMs);
2207
+ revalidation.unref();
2208
+ return {
2209
+ accept: (socket) => {
2210
+ const [oldest] = pending.keys();
2211
+ if (pending.size >= limits.maxPendingHeads && oldest !== void 0) {
2212
+ leavePending(oldest);
2213
+ refuse(oldest, REFUSAL.requestTimeout);
2214
+ }
2215
+ pending.set(
2216
+ socket,
2217
+ setTimeout(() => {
2218
+ leavePending(socket);
2219
+ refuse(socket, REFUSAL.requestTimeout);
2220
+ }, limits.headDeadlineMs)
2221
+ );
2222
+ socket.once("close", () => {
2223
+ leavePending(socket);
2224
+ });
2225
+ },
2226
+ headReceived: leavePending,
2227
+ establish: (socket, capability) => {
2228
+ if (tunnels >= limits.maxTunnels) {
2229
+ return false;
2230
+ }
2231
+ tunnels += 1;
2232
+ const sockets = authenticated.get(capability) ?? /* @__PURE__ */ new Set();
2233
+ sockets.add(socket);
2234
+ authenticated.set(capability, sockets);
2235
+ socket.once("close", () => {
2236
+ tunnels -= 1;
2237
+ sockets.delete(socket);
2238
+ if (sockets.size === 0 && authenticated.get(capability) === sockets) {
2239
+ authenticated.delete(capability);
2240
+ }
2241
+ });
2242
+ return true;
2243
+ },
2244
+ close: () => {
2245
+ clearInterval(revalidation);
2246
+ for (const deadline of pending.values()) {
2247
+ clearTimeout(deadline);
2248
+ }
2249
+ pending.clear();
2250
+ }
2251
+ };
2252
+ }
2253
+ function refuse(socket, status, headers = []) {
2254
+ socket.removeAllListeners("data");
2255
+ socket.pause();
2256
+ if (socket.destroyed || socket.writableEnded) {
2257
+ return;
2258
+ }
2259
+ socket.end(`HTTP/1.1 ${status}\r
2260
+ ${headers.map((header) => `${header}\r
2261
+ `).join("")}content-length: 0\r
2262
+ connection: close\r
2263
+ \r
2264
+ `, () => {
2265
+ socket.destroy();
2266
+ });
2267
+ }
2268
+ function handleConnect(socket, context) {
2269
+ socket.on("error", () => {
2270
+ socket.destroy();
2271
+ });
2272
+ context.ledger.accept(socket);
1972
2273
  let buffer = Buffer.alloc(0);
1973
2274
  const onData = (chunk) => {
1974
2275
  buffer = Buffer.concat([buffer, chunk]);
1975
2276
  const headEnd = buffer.indexOf(HEAD_TERMINATOR);
2277
+ if (headEnd === -1 ? buffer.length > MAX_CONNECT_HEAD_BYTES : headEnd > MAX_CONNECT_HEAD_BYTES) {
2278
+ context.ledger.headReceived(socket);
2279
+ refuse(socket, REFUSAL.headTooLarge);
2280
+ return;
2281
+ }
1976
2282
  if (headEnd === -1) {
1977
- if (buffer.length > MAX_CONNECT_HEAD_BYTES) {
1978
- socket.removeListener("data", onData);
1979
- socket.pause();
1980
- socket.destroy();
1981
- }
1982
2283
  return;
1983
2284
  }
1984
2285
  socket.removeListener("data", onData);
1985
2286
  socket.pause();
2287
+ context.ledger.headReceived(socket);
1986
2288
  const head = buffer.subarray(0, headEnd).toString("utf8");
1987
2289
  socket.unshift(buffer.subarray(headEnd + HEAD_TERMINATOR.length));
1988
- routeConnect(socket, head, config, effects, tlsAcceptor);
2290
+ routeConnect(socket, head, context);
1989
2291
  };
1990
2292
  socket.on("data", onData);
1991
- socket.on("error", () => {
1992
- socket.destroy();
1993
- });
1994
2293
  }
1995
- function routeConnect(socket, head, config, effects, tlsAcceptor) {
2294
+ function proxyAuthorizationOf(head) {
2295
+ const values = head.split("\r\n").slice(1).flatMap((line) => {
2296
+ const colon = line.indexOf(":");
2297
+ return colon > 0 && line.slice(0, colon).trim().toLowerCase() === "proxy-authorization" ? [line.slice(colon + 1)] : [];
2298
+ });
2299
+ const [value] = values;
2300
+ return values.length === 1 ? value : void 0;
2301
+ }
2302
+ function routeConnect(socket, head, context) {
2303
+ const { config, effects, tlsAcceptor, ledger } = context;
2304
+ const credential = proxyAuthorizationOf(head);
2305
+ const capability = credential === void 0 ? void 0 : capabilityFromProxyAuthorization(credential);
2306
+ if (capability === void 0 || !config.isLiveCapability(capability)) {
2307
+ refuse(socket, REFUSAL.proxyAuthenticationRequired, [`Proxy-Authenticate: Basic realm="${CONNECT_PROXY_REALM}"`]);
2308
+ return;
2309
+ }
1996
2310
  const requestLine = head.split("\r\n", 1)[0] ?? "";
1997
2311
  const parts = requestLine.split(" ");
1998
2312
  const authority = parts.length === CONNECT_LINE_TOKENS && parts[0]?.toUpperCase() === "CONNECT" ? parts[1] ?? "" : void 0;
1999
2313
  const target = authority === void 0 ? void 0 : parseConnectTarget(authority);
2000
2314
  if (target === void 0) {
2001
- socket.write("HTTP/1.1 400 Bad Request\r\ncontent-length: 0\r\n\r\n");
2002
- socket.destroy();
2315
+ refuse(socket, REFUSAL.badRequest);
2316
+ return;
2317
+ }
2318
+ if (!ledger.establish(socket, capability)) {
2319
+ refuse(socket, REFUSAL.serviceUnavailable);
2003
2320
  return;
2004
2321
  }
2005
2322
  socket.write(CONNECT_ESTABLISHED);
@@ -2017,6 +2334,10 @@ async function blindTunnel(socket, target, effects) {
2017
2334
  socket.destroy();
2018
2335
  return;
2019
2336
  }
2337
+ if (socket.destroyed) {
2338
+ upstream.destroy();
2339
+ return;
2340
+ }
2020
2341
  socket.pipe(upstream);
2021
2342
  upstream.pipe(socket);
2022
2343
  const drop = () => {
@@ -2032,7 +2353,7 @@ async function blindTunnel(socket, target, effects) {
2032
2353
  socket.destroy();
2033
2354
  });
2034
2355
  }
2035
- function realMitmEffects() {
2356
+ function realConnectEffects() {
2036
2357
  const plainAgent = new http.Agent({ keepAlive: true });
2037
2358
  const tlsAgent = new https.Agent({ keepAlive: true });
2038
2359
  const listenOnce = async (port, onSocket) => await new Promise((resolve, reject) => {
@@ -2161,7 +2482,7 @@ function realMitmEffects() {
2161
2482
  function isEnoent(error) {
2162
2483
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2163
2484
  }
2164
- function realMitmCertStore(paths) {
2485
+ function realConnectCertStore(paths) {
2165
2486
  const read = (file) => {
2166
2487
  try {
2167
2488
  return fs.readFileSync(file, "utf8");
@@ -2174,54 +2495,49 @@ function realMitmCertStore(paths) {
2174
2495
  };
2175
2496
  return {
2176
2497
  loadCa: () => {
2177
- const certPem = read(paths.headroomCaCertFile);
2178
- const keyPem = read(paths.headroomCaKeyFile);
2498
+ const certPem = read(paths.frontdoorCaCertFile);
2499
+ const keyPem = read(paths.frontdoorCaKeyFile);
2179
2500
  return certPem === void 0 || keyPem === void 0 ? void 0 : { certPem, keyPem };
2180
2501
  },
2181
2502
  writeCa: (ca) => {
2182
- fs.mkdirSync(paths.headroomCaDir, { recursive: true });
2183
- fs.writeFileSync(paths.headroomCaCertFile, ca.certPem, "utf8");
2184
- fs.writeFileSync(paths.headroomCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2185
- fs.chmodSync(paths.headroomCaKeyFile, CA_KEY_FILE_MODE);
2503
+ fs.mkdirSync(paths.frontdoorCaDir, { recursive: true });
2504
+ fs.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
2505
+ fs.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2506
+ fs.chmodSync(paths.frontdoorCaKeyFile, CA_KEY_FILE_MODE);
2186
2507
  }
2187
2508
  };
2188
2509
  }
2189
2510
 
2511
+ // src/frontdoor/ensure.ts
2512
+ var import_node_crypto3 = require("node:crypto");
2513
+
2190
2514
  // src/headroom/state.ts
2191
2515
  var import_node_crypto2 = require("node:crypto");
2192
- var import_node_path5 = __toESM(require("node:path"), 1);
2193
- var import_zod3 = require("zod");
2516
+ var import_node_path6 = __toESM(require("node:path"), 1);
2517
+ var import_zod4 = require("zod");
2194
2518
  var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
2195
- var HeadroomStateSchema = import_zod3.z.strictObject({
2519
+ var HeadroomStateSchema = import_zod4.z.strictObject({
2196
2520
  /** The supervisor process owning the daemon. Alive means someone is keeping headroom running. */
2197
- supervisorPid: import_zod3.z.number().int().positive().optional(),
2521
+ supervisorPid: import_zod4.z.number().int().positive().optional(),
2198
2522
  /** The headroom proxy process itself. Absent while the supervisor is between restarts. */
2199
- headroomPid: import_zod3.z.number().int().positive().optional(),
2523
+ headroomPid: import_zod4.z.number().int().positive().optional(),
2200
2524
  /** The loopback port the proxy listens on. Absent until the proxy has passed its readiness check, so "port is set" is itself the ready signal a launcher polls for. */
2201
- port: import_zod3.z.number().int().positive().optional(),
2202
- /**
2203
- * The loopback port the supervisor's in-process MITM CONNECT proxy listens on. Absent whenever no supervisor is serving it, so "mitmPort is set" is the ready signal an OAuth launch polls for alongside `port`.
2204
- */
2205
- mitmPort: import_zod3.z.number().int().positive().optional(),
2206
- /**
2207
- * The sticky MITM port preference, the exact analogue of `lastPort` for the second server: it survives every shutdown, because every OAuth session's environment was frozen at launch with HTTPS_PROXY pointing at this address and a restart that moves strands them.
2208
- */
2209
- lastMitmPort: import_zod3.z.number().int().positive().optional(),
2525
+ port: import_zod4.z.number().int().positive().optional(),
2210
2526
  /**
2211
- * The sticky port preference: the address the daemon last served on, kept across crashes, restarts, and idle shutdowns so the next start reuses it. Distinct from `port` on purpose: `port` is the ready signal (absent whenever nothing is serving), while `lastPort` survives every shutdown, because every live session's environment was frozen at launch pointing at this address and a restart that moves strands them.
2527
+ * The sticky port preference: the address the daemon last served on, kept across crashes, restarts, and idle shutdowns so the next start reuses it. Distinct from `port` on purpose: `port` is the ready signal (absent whenever nothing is serving), while `lastPort` survives every shutdown, because the front door (which routes sessions through this daemon) reads it live and follows a restart to whichever address it lands on.
2212
2528
  */
2213
- lastPort: import_zod3.z.number().int().positive().optional(),
2529
+ lastPort: import_zod4.z.number().int().positive().optional(),
2214
2530
  /** The `headroom --version` output of the running install. */
2215
- version: import_zod3.z.string().optional(),
2531
+ version: import_zod4.z.string().optional(),
2216
2532
  /** Hash of the allowlist the running proxy was started with, so a provider-file change is detected as drift. */
2217
- allowlistHash: import_zod3.z.string().optional(),
2533
+ allowlistHash: import_zod4.z.string().optional(),
2218
2534
  /** The install spec the running install was last installed from, so a `source` config change is detected as drift. */
2219
- installedSource: import_zod3.z.string().optional(),
2535
+ installedSource: import_zod4.z.string().optional(),
2220
2536
  /** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
2221
- lastError: import_zod3.z.string().optional()
2537
+ lastError: import_zod4.z.string().optional()
2222
2538
  });
2223
- var HeadroomSessionSchema = import_zod3.z.strictObject({ pid: import_zod3.z.number().int().positive(), startedAt: import_zod3.z.number() });
2224
- var HeadroomLockSchema = import_zod3.z.strictObject({ pid: import_zod3.z.number().int().positive(), at: import_zod3.z.number() });
2539
+ var HeadroomSessionSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), startedAt: import_zod4.z.number() });
2540
+ var HeadroomLockSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), at: import_zod4.z.number() });
2225
2541
  function parseJson(raw, schema) {
2226
2542
  if (raw === void 0 || raw.trim() === "") {
2227
2543
  return void 0;
@@ -2237,7 +2553,7 @@ function readHeadroomState(fs2, stateFile) {
2237
2553
  }
2238
2554
  }
2239
2555
  function writeHeadroomState(fs2, stateFile, state) {
2240
- fs2.mkdirp(import_node_path5.default.dirname(stateFile));
2556
+ fs2.mkdirp(import_node_path6.default.dirname(stateFile));
2241
2557
  fs2.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
2242
2558
  `);
2243
2559
  }
@@ -2249,16 +2565,8 @@ function headroomAllowlist(providers) {
2249
2565
  return [...urls].sort();
2250
2566
  }
2251
2567
  function headroomUpstreams(providers, frontDoorOrigin2) {
2252
- const upstreams = [];
2253
- let hasCodex = false;
2254
- for (const provider of providers) {
2255
- if (isCodexProvider(provider)) {
2256
- hasCodex = true;
2257
- } else {
2258
- upstreams.push({ baseUrl: provider.baseUrl });
2259
- }
2260
- }
2261
- if (hasCodex && frontDoorOrigin2 !== void 0) {
2568
+ const upstreams = providers.filter((provider) => !isCodexProvider(provider)).map((provider) => ({ baseUrl: provider.baseUrl }));
2569
+ if (providers.length > 0 && frontDoorOrigin2 !== void 0) {
2262
2570
  upstreams.push({ baseUrl: frontDoorOrigin2 });
2263
2571
  }
2264
2572
  return upstreams;
@@ -2273,7 +2581,7 @@ function readAllProviders(fs2, providersDir) {
2273
2581
  continue;
2274
2582
  }
2275
2583
  try {
2276
- const parsed = parseJson(fs2.readFileUtf8(import_node_path5.default.join(providersDir, name)), ProviderSchema);
2584
+ const parsed = parseJson(fs2.readFileUtf8(import_node_path6.default.join(providersDir, name)), ProviderSchema);
2277
2585
  if (parsed !== void 0) {
2278
2586
  result.push({ name: name.slice(0, -".json".length), provider: parsed });
2279
2587
  }
@@ -2285,11 +2593,11 @@ function readAllProviders(fs2, providersDir) {
2285
2593
  }
2286
2594
  function writeSession(fs2, sessionsDir, session) {
2287
2595
  fs2.mkdirp(sessionsDir);
2288
- fs2.writeFileUtf8(import_node_path5.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2596
+ fs2.writeFileUtf8(import_node_path6.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2289
2597
  `);
2290
2598
  }
2291
2599
  function removeSession(fs2, sessionsDir, pid) {
2292
- fs2.removeRecursive(import_node_path5.default.join(sessionsDir, `${String(pid)}.json`));
2600
+ fs2.removeRecursive(import_node_path6.default.join(sessionsDir, `${String(pid)}.json`));
2293
2601
  }
2294
2602
  function listSessions(fs2, sessionsDir) {
2295
2603
  const sessions = [];
@@ -2298,7 +2606,7 @@ function listSessions(fs2, sessionsDir) {
2298
2606
  continue;
2299
2607
  }
2300
2608
  try {
2301
- const parsed = parseJson(fs2.readFileUtf8(import_node_path5.default.join(sessionsDir, name)), HeadroomSessionSchema);
2609
+ const parsed = parseJson(fs2.readFileUtf8(import_node_path6.default.join(sessionsDir, name)), HeadroomSessionSchema);
2302
2610
  if (parsed !== void 0) {
2303
2611
  sessions.push(parsed);
2304
2612
  }
@@ -2326,6 +2634,67 @@ function readStartLock(fs2, lockFile) {
2326
2634
  }
2327
2635
  }
2328
2636
 
2637
+ // src/frontdoor/ensure.ts
2638
+ var FRONTDOOR_START_TIMEOUT_MS = 3e4;
2639
+ var FRONTDOOR_LAUNCHER_POLL_MS = 100;
2640
+ var FrontDoorStartError = class extends CliError {
2641
+ constructor(message) {
2642
+ super(message);
2643
+ this.name = "FrontDoorStartError";
2644
+ }
2645
+ };
2646
+ function ensureFrontDoor(params) {
2647
+ const { paths, ports } = params;
2648
+ const deadline = ports.now() + FRONTDOOR_START_TIMEOUT_MS;
2649
+ let spawned = false;
2650
+ const distrusted = /* @__PURE__ */ new Set();
2651
+ let lastRejection;
2652
+ for (; ; ) {
2653
+ const state = readFrontDoorState(ports.fs, paths.frontdoorStateFile);
2654
+ const supervisorPid = state?.supervisorPid;
2655
+ if (supervisorPid !== void 0 && !distrusted.has(supervisorPid) && ports.isRunning(supervisorPid)) {
2656
+ if (state?.port !== void 0 && state.connectPort !== void 0) {
2657
+ const verdict = ports.verifyListener(state.port);
2658
+ if (verdict.ok) {
2659
+ const token = (0, import_node_crypto3.randomUUID)();
2660
+ writeFrontDoorSession(ports.fs, paths.frontdoorSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), token });
2661
+ return { port: state.port, connectPort: state.connectPort, token };
2662
+ }
2663
+ lastRejection = `the listener on 127.0.0.1:${String(state.port)} (recorded for pid ${String(supervisorPid)}) failed authentication: ${verdict.reason}`;
2664
+ if (spawned) {
2665
+ throw new FrontDoorStartError(`claude-use: ${lastRejection}; refusing to send this session's credentials to it. Daemon log: ${paths.frontdoorLogPath}`);
2666
+ }
2667
+ distrusted.add(supervisorPid);
2668
+ continue;
2669
+ }
2670
+ if (state?.lastError !== void 0) {
2671
+ throw new FrontDoorStartError(`claude-use: the front door reported a fatal error and is not serving: ${state.lastError} (daemon log: ${paths.frontdoorLogPath})`);
2672
+ }
2673
+ } else if (!spawned) {
2674
+ ports.fs.mkdirp(paths.frontdoorDir);
2675
+ const created = ports.fs.writeFileExclusive(paths.frontdoorLockFile, `${JSON.stringify({ pid: params.launcherPid, at: ports.now() })}
2676
+ `);
2677
+ if (created) {
2678
+ ports.spawnSupervisor(paths);
2679
+ spawned = true;
2680
+ } else {
2681
+ const lock = readStartLock(ports.fs, paths.frontdoorLockFile);
2682
+ if (lock === void 0 || !ports.isRunning(lock.pid)) {
2683
+ ports.fs.removeRecursive(paths.frontdoorLockFile);
2684
+ continue;
2685
+ }
2686
+ }
2687
+ }
2688
+ if (ports.now() >= deadline) {
2689
+ const lastError = state?.lastError;
2690
+ throw new FrontDoorStartError(
2691
+ `claude-use: 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}`
2692
+ );
2693
+ }
2694
+ ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
2695
+ }
2696
+ }
2697
+
2329
2698
  // src/headroom/ensure.ts
2330
2699
  var HEADROOM_START_TIMEOUT_MS = 12e4;
2331
2700
  var HEADROOM_LAUNCHER_POLL_MS = 250;
@@ -2342,10 +2711,10 @@ function ensureHeadroom(params) {
2342
2711
  for (; ; ) {
2343
2712
  const state = readHeadroomState(ports.fs, paths.headroomStateFile);
2344
2713
  if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
2345
- const daemonUp = state.port !== void 0 && state.mitmPort !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
2346
- if (daemonUp && state.port !== void 0 && state.mitmPort !== void 0) {
2714
+ const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
2715
+ if (daemonUp && state.port !== void 0) {
2347
2716
  writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now() });
2348
- return { port: state.port, mitmPort: state.mitmPort };
2717
+ return { port: state.port };
2349
2718
  }
2350
2719
  if (state.lastError !== void 0) {
2351
2720
  throw new HeadroomStartError(
@@ -2380,42 +2749,6 @@ function ensureHeadroom(params) {
2380
2749
  }
2381
2750
  }
2382
2751
 
2383
- // src/frontdoor/state.ts
2384
- var import_zod4 = require("zod");
2385
- var FrontDoorStateSchema = import_zod4.z.strictObject({
2386
- /** The supervisor process serving the front door. Alive means the listener is up in that process. */
2387
- supervisorPid: import_zod4.z.number().int().positive().optional(),
2388
- /** The loopback port the front-door listener serves on. Absent until the listener has bound and answered its health probe, so "port is set" is itself the ready signal a launcher polls for. */
2389
- port: import_zod4.z.number().int().positive().optional(),
2390
- /**
2391
- * The sticky port preference: kept across crashes, restarts and idle shutdowns so every session, whose base URL was frozen at launch, keeps finding the front door at the same address.
2392
- */
2393
- lastPort: import_zod4.z.number().int().positive().optional(),
2394
- /** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
2395
- lastError: import_zod4.z.string().optional()
2396
- });
2397
- function readFrontDoorState(fs2, stateFile) {
2398
- let raw;
2399
- try {
2400
- raw = fs2.readFileUtf8(stateFile);
2401
- } catch {
2402
- return void 0;
2403
- }
2404
- if (raw === void 0 || raw.trim() === "") {
2405
- return void 0;
2406
- }
2407
- try {
2408
- const parsed = FrontDoorStateSchema.safeParse(JSON.parse(raw));
2409
- return parsed.success ? parsed.data : void 0;
2410
- } catch {
2411
- return void 0;
2412
- }
2413
- }
2414
- function frontDoorOrigin(state) {
2415
- const port = state?.port ?? state?.lastPort;
2416
- return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
2417
- }
2418
-
2419
2752
  // src/headroom/supervisor.ts
2420
2753
  function resolveSupervisorConfig(configured) {
2421
2754
  return {
@@ -2428,7 +2761,7 @@ var HEADROOM_BACKOFF_CAP_MS = 3e4;
2428
2761
  var HEADROOM_START_RETRY_BUDGET = 5;
2429
2762
  var HEADROOM_READY_TIMEOUT_MS = 15e3;
2430
2763
  var HEADROOM_POLL_MS = 1e3;
2431
- var MS_PER_MINUTE = 6e4;
2764
+ var MS_PER_MINUTE2 = 6e4;
2432
2765
  function backoffForAttempt(attempt) {
2433
2766
  return Math.min(HEADROOM_BACKOFF_CAP_MS, HEADROOM_BACKOFF_BASE_MS * 2 ** (attempt - 1));
2434
2767
  }
@@ -2488,13 +2821,7 @@ async function runSupervisor(config, ports, options = {}) {
2488
2821
  const previousState = readHeadroomState(fs2, paths.headroomStateFile);
2489
2822
  let installedSource = previousState?.installedSource;
2490
2823
  let lastPort = previousState?.lastPort;
2491
- let lastMitmPort = previousState?.lastMitmPort;
2492
- let daemonPort;
2493
- const sticky = () => ({
2494
- ...lastPort === void 0 ? {} : { lastPort },
2495
- ...lastMitmPort === void 0 ? {} : { lastMitmPort }
2496
- });
2497
- let mitm;
2824
+ const sticky = () => lastPort === void 0 ? {} : { lastPort };
2498
2825
  const fail = (message) => {
2499
2826
  writeHeadroomState(fs2, paths.headroomStateFile, {
2500
2827
  lastError: message,
@@ -2523,18 +2850,7 @@ async function runSupervisor(config, ports, options = {}) {
2523
2850
  return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
2524
2851
  }
2525
2852
  const version = ports.headroomVersion() ?? "unknown";
2526
- try {
2527
- mitm = await ports.startMitm(lastMitmPort, () => daemonPort);
2528
- } catch (error) {
2529
- return fail(`could not start the MITM proxy: ${error instanceof Error ? error.message : String(error)}`);
2530
- }
2531
- lastMitmPort = mitm.port;
2532
- if (previousState?.lastMitmPort !== void 0 && mitm.port !== previousState.lastMitmPort) {
2533
- ports.log(
2534
- `claude-use headroom supervisor: sticky MITM port ${String(previousState.lastMitmPort)} was occupied; moving to ${String(mitm.port)} (OAuth sessions launched against the old port are stale until they relaunch)`
2535
- );
2536
- }
2537
- writeHeadroomState(fs2, paths.headroomStateFile, { supervisorPid: ports.ownPid, version, installedSource, mitmPort: mitm.port, ...sticky() });
2853
+ writeHeadroomState(fs2, paths.headroomStateFile, { supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
2538
2854
  ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
2539
2855
  const orphanPid = previousState?.headroomPid;
2540
2856
  if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
@@ -2559,13 +2875,11 @@ async function runSupervisor(config, ports, options = {}) {
2559
2875
  if (crashed) {
2560
2876
  ports.log(`claude-use headroom supervisor: headroom pid ${String(headroomPid)} died`);
2561
2877
  headroomPid = void 0;
2562
- daemonPort = void 0;
2563
2878
  runningHash = void 0;
2564
2879
  writeHeadroomState(fs2, paths.headroomStateFile, {
2565
2880
  supervisorPid: ports.ownPid,
2566
2881
  version,
2567
2882
  installedSource,
2568
- mitmPort: mitm.port,
2569
2883
  ...sticky()
2570
2884
  });
2571
2885
  }
@@ -2582,7 +2896,6 @@ async function runSupervisor(config, ports, options = {}) {
2582
2896
  if (ready) {
2583
2897
  consecutiveFailures = 0;
2584
2898
  headroomPid = pid;
2585
- daemonPort = port;
2586
2899
  runningHash = allowlistHash;
2587
2900
  lastPort = port;
2588
2901
  if (previousSticky !== void 0 && port !== previousSticky) {
@@ -2594,7 +2907,6 @@ async function runSupervisor(config, ports, options = {}) {
2594
2907
  supervisorPid: ports.ownPid,
2595
2908
  headroomPid: pid,
2596
2909
  port,
2597
- mitmPort: mitm.port,
2598
2910
  version,
2599
2911
  allowlistHash,
2600
2912
  installedSource: config.source,
@@ -2618,7 +2930,6 @@ async function runSupervisor(config, ports, options = {}) {
2618
2930
  ports.log("claude-use headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
2619
2931
  ports.stopProcess(headroomPid);
2620
2932
  headroomPid = void 0;
2621
- daemonPort = void 0;
2622
2933
  runningHash = void 0;
2623
2934
  continue;
2624
2935
  }
@@ -2627,13 +2938,11 @@ async function runSupervisor(config, ports, options = {}) {
2627
2938
  if (sessions.length === 0) {
2628
2939
  idleSince ??= ports.now();
2629
2940
  const idleMs = ports.now() - idleSince;
2630
- if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE) {
2941
+ if (idleMs >= config.idleShutdownMinutes * MS_PER_MINUTE2) {
2631
2942
  ports.log(
2632
- `claude-use headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and the MITM proxy, and exiting`
2943
+ `claude-use headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
2633
2944
  );
2634
2945
  ports.stopProcess(headroomPid);
2635
- daemonPort = void 0;
2636
- await mitm.close();
2637
2946
  writeHeadroomState(fs2, paths.headroomStateFile, {
2638
2947
  version,
2639
2948
  ...sticky()
@@ -2716,15 +3025,18 @@ function evaluateAmbientCredentialGuard(params) {
2716
3025
  // Annotate the CommonJS export names for ESM import in node:
2717
3026
  0 && (module.exports = {
2718
3027
  AMBIENT_CREDENTIAL_VARS,
3028
+ CONNECT_INTERCEPT_HOST,
3029
+ CONNECT_LIMITS,
2719
3030
  FARM_MANIFEST_FILENAME,
2720
- HEADROOM_SERVED_PATH_PREFIX,
3031
+ FrontDoorStartError,
2721
3032
  HeadroomStartError,
2722
- MITM_INTERCEPT_HOST,
3033
+ ROUTED_PATH_PREFIX,
2723
3034
  buildEntryFacts,
2724
3035
  carryOver,
2725
3036
  createLeafCache,
2726
3037
  detectAmbientCredential,
2727
3038
  ensureCa,
3039
+ ensureFrontDoor,
2728
3040
  ensureHeadroom,
2729
3041
  evaluateAmbientCredentialGuard,
2730
3042
  formatAmbientCredentialGuardMessage,
@@ -2734,15 +3046,16 @@ function evaluateAmbientCredentialGuard(params) {
2734
3046
  mintLeaf,
2735
3047
  parseConnectTarget,
2736
3048
  readFarmManifest,
2737
- realMitmCertStore,
2738
- realMitmEffects,
3049
+ realConnectCertStore,
3050
+ realConnectEffects,
2739
3051
  recoverFarm,
2740
3052
  recoveryDiagnostics,
2741
3053
  resolveDecisions,
2742
3054
  resolveSupervisorConfig,
2743
3055
  resyncFarm,
3056
+ runFrontDoorSupervisor,
2744
3057
  runSupervisor,
2745
- servedByHeadroom,
2746
- startMitmServer,
3058
+ servedByPipeline,
3059
+ startConnectServer,
2747
3060
  topLevelNames
2748
3061
  });