agent-shim 8.5.0 → 8.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -55107,18 +55107,14 @@ function discoverClaudeBinary(options) {
55107
55107
  version: highestVersion
55108
55108
  };
55109
55109
  }
55110
- const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => import_node_path3.default.resolve(dir)));
55111
55110
  for (const dir of options.pathDirs) {
55112
- if (ownInstallDirs.has(import_node_path3.default.resolve(dir))) {
55113
- continue;
55114
- }
55115
55111
  const found = options.findExecutableInDir(dir, "claude");
55116
- if (found !== void 0) {
55112
+ if (found !== void 0 && !options.isOwnBinary(found)) {
55117
55113
  return { path: found, source: "path-fallback" };
55118
55114
  }
55119
55115
  }
55120
55116
  throw new Error(
55121
- `Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (excluding this tool's own install directory). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
55117
+ `Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (never this tool's own binary). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
55122
55118
  );
55123
55119
  }
55124
55120
 
@@ -55970,12 +55966,12 @@ function resolveContentSourcePath(env) {
55970
55966
  function realContentSourcePath() {
55971
55967
  return resolveContentSourcePath({ isSea: (0, import_node_sea.isSea)(), execPath: process.execPath, argv1: process.argv[1] });
55972
55968
  }
55973
- function realResolveClaudeBinary(ownInstallDirs) {
55969
+ function realResolveClaudeBinary(isOwnBinary) {
55974
55970
  return () => discoverClaudeBinary({
55975
55971
  listVersionsDir,
55976
55972
  pathDirs: (process.env.PATH ?? "").split(import_node_path6.default.delimiter).filter((dir) => dir !== ""),
55977
55973
  findExecutableInDir,
55978
- ownInstallDirs: [...ownInstallDirs]
55974
+ isOwnBinary
55979
55975
  });
55980
55976
  }
55981
55977
 
@@ -56017,13 +56013,19 @@ function commandFilename(ownExecutablePath, commandName) {
56017
56013
  function isInvokedAsClaude(invokedName) {
56018
56014
  return invokedName === "claude" || invokedName.toLowerCase() === "claude.exe";
56019
56015
  }
56020
- function resolveOwnInstallDirs(paths, ownExecutablePath) {
56021
- const dirs = [import_node_path7.default.dirname(ownExecutablePath)];
56016
+ function resolveOwnBinaryCheck(paths, contentSourcePath) {
56022
56017
  const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
56023
- if (state !== void 0) {
56024
- dirs.push(import_node_path7.default.dirname(state.targetPath));
56025
- }
56026
- return dirs;
56018
+ const recordedTarget = state === void 0 ? void 0 : import_node_path7.default.resolve(state.targetPath);
56019
+ let ownStat;
56020
+ return (candidate) => {
56021
+ if (recordedTarget !== void 0 && import_node_path7.default.resolve(candidate) === recordedTarget) {
56022
+ return true;
56023
+ }
56024
+ ownStat ??= statOrUndefined(contentSourcePath);
56025
+ const candidateStat = statOrUndefined(candidate);
56026
+ return ownStat !== void 0 && candidateStat !== void 0 && // A platform reporting no inode number for either file must not read every candidate as this tool.
56027
+ ownStat.ino !== 0 && candidateStat.ino !== 0 && candidateStat.dev === ownStat.dev && candidateStat.ino === ownStat.ino;
56028
+ };
56027
56029
  }
56028
56030
  function resolveClaudeTargetPath(ownExecutablePath, dir) {
56029
56031
  return import_node_path7.default.join(dir ?? import_node_path7.default.dirname(ownExecutablePath), claudeTargetFilename(ownExecutablePath));
@@ -56044,6 +56046,16 @@ function lstatOrUndefined(target) {
56044
56046
  throw error62;
56045
56047
  }
56046
56048
  }
56049
+ function statOrUndefined(target) {
56050
+ try {
56051
+ return import_node_fs4.default.statSync(target);
56052
+ } catch (error62) {
56053
+ if (isEnoent3(error62)) {
56054
+ return void 0;
56055
+ }
56056
+ throw error62;
56057
+ }
56058
+ }
56047
56059
  var nodeLinkFs = { link: import_node_fs4.default.linkSync, copyFile: import_node_fs4.default.copyFileSync, chmod: import_node_fs4.default.chmodSync };
56048
56060
  function enableClaudeShim(params, linkFs = nodeLinkFs) {
56049
56061
  const realContentPath = import_node_fs4.default.realpathSync(params.contentSourcePath);
@@ -61040,7 +61052,7 @@ function registerIdentityCommand(program2, deps) {
61040
61052
  // package.json
61041
61053
  var package_default = {
61042
61054
  name: "agent-shim",
61043
- version: "8.5.0",
61055
+ version: "8.5.2",
61044
61056
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
61045
61057
  license: "MIT",
61046
61058
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -64924,13 +64936,14 @@ function realConnectCertStore(paths) {
64924
64936
  }
64925
64937
 
64926
64938
  // src/frontdoor/connectEffects.ts
64927
- var dns = __toESM(require("node:dns"), 1);
64939
+ var import_node_dns = __toESM(require("node:dns"), 1);
64928
64940
  var http = __toESM(require("node:http"), 1);
64929
64941
  var https = __toESM(require("node:https"), 1);
64930
64942
  var net2 = __toESM(require("node:net"), 1);
64931
64943
  var tls = __toESM(require("node:tls"), 1);
64932
64944
  var UPSTREAM_LOCAL_PORT_START = 47900;
64933
64945
  var UPSTREAM_LOCAL_PORT_END = 47919;
64946
+ var UPSTREAM_LOCAL_PORT_COUNT = UPSTREAM_LOCAL_PORT_END - UPSTREAM_LOCAL_PORT_START + 1;
64934
64947
  var nextUpstreamLocalPort = UPSTREAM_LOCAL_PORT_START;
64935
64948
  function upstreamLocalPort() {
64936
64949
  const port = nextUpstreamLocalPort;
@@ -64942,42 +64955,93 @@ var realAddressLookup = (host, _options, callback) => {
64942
64955
  callback(null, [{ address: host, family: net2.isIP(host) }]);
64943
64956
  return;
64944
64957
  }
64945
- dns.resolve4(host, (error62, addresses) => {
64958
+ import_node_dns.default.resolve4(host, (error62, addresses) => {
64959
+ if (error62 !== null) {
64960
+ callback(error62, [{ address: "", family: 4 }]);
64961
+ return;
64962
+ }
64946
64963
  const first = addresses[0];
64947
- if (error62 !== null || first === void 0) {
64948
- callback(error62 ?? new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
64964
+ if (first === void 0) {
64965
+ callback(new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
64949
64966
  return;
64950
64967
  }
64951
64968
  callback(null, [{ address: first, family: 4 }]);
64952
64969
  });
64953
64970
  };
64954
64971
  var HTTP_PORT = 80;
64955
- function exemptDial(options, defaultPort) {
64956
- const host = typeof options.host === "string" ? options.host : "";
64957
- const port = typeof options.port === "number" ? options.port : defaultPort;
64958
- return net2.connect(net2.isIP(host) === 0 ? { host, port, localPort: upstreamLocalPort(), lookup: realAddressLookup } : { host, port });
64972
+ var HELD_PORT_ERRNOS = /* @__PURE__ */ new Set(["EADDRINUSE", "EADDRNOTAVAIL"]);
64973
+ async function dialOnce(connectOptions) {
64974
+ return await new Promise((resolve) => {
64975
+ const socket = net2.connect(connectOptions);
64976
+ socket.once("connect", () => {
64977
+ resolve({ error: null, socket });
64978
+ });
64979
+ socket.once("error", (error62) => {
64980
+ resolve({ error: error62, socket });
64981
+ });
64982
+ });
64983
+ }
64984
+ function dialTarget(options, defaultPort) {
64985
+ return { host: typeof options.host === "string" ? options.host : "", port: typeof options.port === "number" ? options.port : defaultPort };
64986
+ }
64987
+ function literalDial(target) {
64988
+ return net2.connect({ host: target.host, port: target.port });
64989
+ }
64990
+ async function reservedDial(target) {
64991
+ for (let attempt = 0; ; attempt += 1) {
64992
+ const outcome = await dialOnce({ host: target.host, port: target.port, localPort: upstreamLocalPort(), lookup: realAddressLookup });
64993
+ if (outcome.error === null) {
64994
+ return outcome;
64995
+ }
64996
+ const held = outcome.error.code !== void 0 && HELD_PORT_ERRNOS.has(outcome.error.code);
64997
+ if (!held || attempt === UPSTREAM_LOCAL_PORT_COUNT - 1) {
64998
+ return outcome;
64999
+ }
65000
+ }
64959
65001
  }
64960
65002
  function requestServername(options, fallback) {
64961
65003
  return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
64962
65004
  }
64963
65005
  var ExemptHttpAgent = class extends http.Agent {
64964
- createConnection(options) {
64965
- return exemptDial(options, HTTP_PORT);
65006
+ // A reserved-port dial that has to retry cannot produce its socket synchronously, so it settles first and arrives through the callback (node's documented asynchronous createConnection form); a literal dial has nothing to retry and keeps node's own synchronous shape.
65007
+ createConnection(options, oncreate) {
65008
+ const target = dialTarget(options, HTTP_PORT);
65009
+ if (net2.isIP(target.host) !== 0) {
65010
+ return literalDial(target);
65011
+ }
65012
+ void reservedDial(target).then((outcome) => {
65013
+ oncreate?.(outcome.error, outcome.socket);
65014
+ });
65015
+ return void 0;
64966
65016
  }
64967
65017
  };
64968
65018
  var ExemptTlsAgent = class extends https.Agent {
64969
- createConnection(options) {
64970
- const host = typeof options.host === "string" ? options.host : "";
64971
- const tlsOptions = { socket: exemptDial(options, HTTPS_PORT), servername: requestServername(options, host) };
64972
- if ("ca" in options && Array.isArray(options.ca)) {
64973
- tlsOptions.ca = options.ca;
64974
- }
64975
- if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
64976
- tlsOptions.rejectUnauthorized = options.rejectUnauthorized;
64977
- }
64978
- return tls.connect(tlsOptions);
65019
+ // The same split as the plain sibling: a literal dial wraps TLS around the socket node's own agent would have returned, a reserved dial settles (retries included) first and arrives through the callback, which is exactly where node's own agent wraps its own dials.
65020
+ createConnection(options, oncreate) {
65021
+ const target = dialTarget(options, HTTPS_PORT);
65022
+ if (net2.isIP(target.host) !== 0) {
65023
+ return tls.connect({ socket: literalDial(target), servername: requestServername(options, target.host), ...tlsFacts(options) });
65024
+ }
65025
+ void reservedDial(target).then((outcome) => {
65026
+ if (outcome.error !== null) {
65027
+ oncreate?.(outcome.error, outcome.socket);
65028
+ return;
65029
+ }
65030
+ oncreate?.(null, tls.connect({ socket: outcome.socket, servername: requestServername(options, target.host), ...tlsFacts(options) }));
65031
+ });
65032
+ return void 0;
64979
65033
  }
64980
65034
  };
65035
+ function tlsFacts(options) {
65036
+ const facts = {};
65037
+ if ("ca" in options && Array.isArray(options.ca)) {
65038
+ facts.ca = options.ca;
65039
+ }
65040
+ if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
65041
+ facts.rejectUnauthorized = options.rejectUnauthorized;
65042
+ }
65043
+ return facts;
65044
+ }
64981
65045
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
64982
65046
  const agent = new ExemptTlsAgent();
64983
65047
  return {
@@ -68982,7 +69046,7 @@ function collectDoctorReport(params) {
68982
69046
  const ownExecutablePath = realOwnExecutablePath();
68983
69047
  let binaryDiscovery;
68984
69048
  try {
68985
- const binary = realResolveClaudeBinary(resolveOwnInstallDirs(paths, ownExecutablePath))();
69049
+ const binary = realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath()))();
68986
69050
  binaryDiscovery = { ok: true, binary };
68987
69051
  } catch (error62) {
68988
69052
  binaryDiscovery = { ok: false, message: error62 instanceof Error ? error62.message : String(error62) };
@@ -71062,7 +71126,7 @@ function realPrepareLaunchParams(paths, options) {
71062
71126
  fs: realFsPort,
71063
71127
  proc: options.proc,
71064
71128
  log: options.log,
71065
- resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
71129
+ resolveClaudeBinary: realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath())),
71066
71130
  farm: farm.runtime,
71067
71131
  headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
71068
71132
  frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
package/dist/index.cjs CHANGED
@@ -3248,13 +3248,14 @@ function realConnectCertStore(paths) {
3248
3248
  }
3249
3249
 
3250
3250
  // src/frontdoor/connectEffects.ts
3251
- var dns = __toESM(require("node:dns"), 1);
3251
+ var import_node_dns = __toESM(require("node:dns"), 1);
3252
3252
  var http = __toESM(require("node:http"), 1);
3253
3253
  var https = __toESM(require("node:https"), 1);
3254
3254
  var net2 = __toESM(require("node:net"), 1);
3255
3255
  var tls = __toESM(require("node:tls"), 1);
3256
3256
  var UPSTREAM_LOCAL_PORT_START = 47900;
3257
3257
  var UPSTREAM_LOCAL_PORT_END = 47919;
3258
+ var UPSTREAM_LOCAL_PORT_COUNT = UPSTREAM_LOCAL_PORT_END - UPSTREAM_LOCAL_PORT_START + 1;
3258
3259
  var nextUpstreamLocalPort = UPSTREAM_LOCAL_PORT_START;
3259
3260
  function upstreamLocalPort() {
3260
3261
  const port = nextUpstreamLocalPort;
@@ -3266,42 +3267,93 @@ var realAddressLookup = (host, _options, callback) => {
3266
3267
  callback(null, [{ address: host, family: net2.isIP(host) }]);
3267
3268
  return;
3268
3269
  }
3269
- dns.resolve4(host, (error, addresses) => {
3270
+ import_node_dns.default.resolve4(host, (error, addresses) => {
3271
+ if (error !== null) {
3272
+ callback(error, [{ address: "", family: 4 }]);
3273
+ return;
3274
+ }
3270
3275
  const first = addresses[0];
3271
- if (error !== null || first === void 0) {
3272
- callback(error ?? new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
3276
+ if (first === void 0) {
3277
+ callback(new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
3273
3278
  return;
3274
3279
  }
3275
3280
  callback(null, [{ address: first, family: 4 }]);
3276
3281
  });
3277
3282
  };
3278
3283
  var HTTP_PORT = 80;
3279
- function exemptDial(options, defaultPort) {
3280
- const host = typeof options.host === "string" ? options.host : "";
3281
- const port = typeof options.port === "number" ? options.port : defaultPort;
3282
- return net2.connect(net2.isIP(host) === 0 ? { host, port, localPort: upstreamLocalPort(), lookup: realAddressLookup } : { host, port });
3284
+ var HELD_PORT_ERRNOS = /* @__PURE__ */ new Set(["EADDRINUSE", "EADDRNOTAVAIL"]);
3285
+ async function dialOnce(connectOptions) {
3286
+ return await new Promise((resolve) => {
3287
+ const socket = net2.connect(connectOptions);
3288
+ socket.once("connect", () => {
3289
+ resolve({ error: null, socket });
3290
+ });
3291
+ socket.once("error", (error) => {
3292
+ resolve({ error, socket });
3293
+ });
3294
+ });
3295
+ }
3296
+ function dialTarget(options, defaultPort) {
3297
+ return { host: typeof options.host === "string" ? options.host : "", port: typeof options.port === "number" ? options.port : defaultPort };
3298
+ }
3299
+ function literalDial(target) {
3300
+ return net2.connect({ host: target.host, port: target.port });
3301
+ }
3302
+ async function reservedDial(target) {
3303
+ for (let attempt = 0; ; attempt += 1) {
3304
+ const outcome = await dialOnce({ host: target.host, port: target.port, localPort: upstreamLocalPort(), lookup: realAddressLookup });
3305
+ if (outcome.error === null) {
3306
+ return outcome;
3307
+ }
3308
+ const held = outcome.error.code !== void 0 && HELD_PORT_ERRNOS.has(outcome.error.code);
3309
+ if (!held || attempt === UPSTREAM_LOCAL_PORT_COUNT - 1) {
3310
+ return outcome;
3311
+ }
3312
+ }
3283
3313
  }
3284
3314
  function requestServername(options, fallback) {
3285
3315
  return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
3286
3316
  }
3287
3317
  var ExemptHttpAgent = class extends http.Agent {
3288
- createConnection(options) {
3289
- return exemptDial(options, HTTP_PORT);
3318
+ // A reserved-port dial that has to retry cannot produce its socket synchronously, so it settles first and arrives through the callback (node's documented asynchronous createConnection form); a literal dial has nothing to retry and keeps node's own synchronous shape.
3319
+ createConnection(options, oncreate) {
3320
+ const target = dialTarget(options, HTTP_PORT);
3321
+ if (net2.isIP(target.host) !== 0) {
3322
+ return literalDial(target);
3323
+ }
3324
+ void reservedDial(target).then((outcome) => {
3325
+ oncreate?.(outcome.error, outcome.socket);
3326
+ });
3327
+ return void 0;
3290
3328
  }
3291
3329
  };
3292
3330
  var ExemptTlsAgent = class extends https.Agent {
3293
- createConnection(options) {
3294
- const host = typeof options.host === "string" ? options.host : "";
3295
- const tlsOptions = { socket: exemptDial(options, HTTPS_PORT), servername: requestServername(options, host) };
3296
- if ("ca" in options && Array.isArray(options.ca)) {
3297
- tlsOptions.ca = options.ca;
3298
- }
3299
- if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
3300
- tlsOptions.rejectUnauthorized = options.rejectUnauthorized;
3301
- }
3302
- return tls.connect(tlsOptions);
3331
+ // The same split as the plain sibling: a literal dial wraps TLS around the socket node's own agent would have returned, a reserved dial settles (retries included) first and arrives through the callback, which is exactly where node's own agent wraps its own dials.
3332
+ createConnection(options, oncreate) {
3333
+ const target = dialTarget(options, HTTPS_PORT);
3334
+ if (net2.isIP(target.host) !== 0) {
3335
+ return tls.connect({ socket: literalDial(target), servername: requestServername(options, target.host), ...tlsFacts(options) });
3336
+ }
3337
+ void reservedDial(target).then((outcome) => {
3338
+ if (outcome.error !== null) {
3339
+ oncreate?.(outcome.error, outcome.socket);
3340
+ return;
3341
+ }
3342
+ oncreate?.(null, tls.connect({ socket: outcome.socket, servername: requestServername(options, target.host), ...tlsFacts(options) }));
3343
+ });
3344
+ return void 0;
3303
3345
  }
3304
3346
  };
3347
+ function tlsFacts(options) {
3348
+ const facts = {};
3349
+ if ("ca" in options && Array.isArray(options.ca)) {
3350
+ facts.ca = options.ca;
3351
+ }
3352
+ if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
3353
+ facts.rejectUnauthorized = options.rejectUnauthorized;
3354
+ }
3355
+ return facts;
3356
+ }
3305
3357
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
3306
3358
  const agent = new ExemptTlsAgent();
3307
3359
  return {
@@ -6935,18 +6987,14 @@ function discoverClaudeBinary(options) {
6935
6987
  version: highestVersion
6936
6988
  };
6937
6989
  }
6938
- const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => import_node_path20.default.resolve(dir)));
6939
6990
  for (const dir of options.pathDirs) {
6940
- if (ownInstallDirs.has(import_node_path20.default.resolve(dir))) {
6941
- continue;
6942
- }
6943
6991
  const found = options.findExecutableInDir(dir, "claude");
6944
- if (found !== void 0) {
6992
+ if (found !== void 0 && !options.isOwnBinary(found)) {
6945
6993
  return { path: found, source: "path-fallback" };
6946
6994
  }
6947
6995
  }
6948
6996
  throw new Error(
6949
- `Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (excluding this tool's own install directory). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
6997
+ `Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (never this tool's own binary). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
6950
6998
  );
6951
6999
  }
6952
7000
 
@@ -7344,12 +7392,18 @@ function realOwnExecutablePath() {
7344
7392
  findExecutableInDir
7345
7393
  });
7346
7394
  }
7347
- function realResolveClaudeBinary(ownInstallDirs) {
7395
+ function resolveContentSourcePath(env) {
7396
+ return env.isSea ? env.execPath : env.argv1 ?? env.execPath;
7397
+ }
7398
+ function realContentSourcePath() {
7399
+ return resolveContentSourcePath({ isSea: (0, import_node_sea.isSea)(), execPath: process.execPath, argv1: process.argv[1] });
7400
+ }
7401
+ function realResolveClaudeBinary(isOwnBinary) {
7348
7402
  return () => discoverClaudeBinary({
7349
7403
  listVersionsDir,
7350
7404
  pathDirs: (process.env.PATH ?? "").split(import_node_path21.default.delimiter).filter((dir) => dir !== ""),
7351
7405
  findExecutableInDir,
7352
- ownInstallDirs: [...ownInstallDirs]
7406
+ isOwnBinary
7353
7407
  });
7354
7408
  }
7355
7409
 
@@ -7765,13 +7819,32 @@ var ClaudeShimStateSchema = import_zod11.z.strictObject({
7765
7819
  function commandFilename(ownExecutablePath, commandName) {
7766
7820
  return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
7767
7821
  }
7768
- function resolveOwnInstallDirs(paths, ownExecutablePath) {
7769
- const dirs = [import_node_path23.default.dirname(ownExecutablePath)];
7822
+ function resolveOwnBinaryCheck(paths, contentSourcePath) {
7770
7823
  const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
7771
- if (state !== void 0) {
7772
- dirs.push(import_node_path23.default.dirname(state.targetPath));
7824
+ const recordedTarget = state === void 0 ? void 0 : import_node_path23.default.resolve(state.targetPath);
7825
+ let ownStat;
7826
+ return (candidate) => {
7827
+ if (recordedTarget !== void 0 && import_node_path23.default.resolve(candidate) === recordedTarget) {
7828
+ return true;
7829
+ }
7830
+ ownStat ??= statOrUndefined(contentSourcePath);
7831
+ const candidateStat = statOrUndefined(candidate);
7832
+ return ownStat !== void 0 && candidateStat !== void 0 && // A platform reporting no inode number for either file must not read every candidate as this tool.
7833
+ ownStat.ino !== 0 && candidateStat.ino !== 0 && candidateStat.dev === ownStat.dev && candidateStat.ino === ownStat.ino;
7834
+ };
7835
+ }
7836
+ function isEnoent4(error) {
7837
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
7838
+ }
7839
+ function statOrUndefined(target) {
7840
+ try {
7841
+ return import_node_fs7.default.statSync(target);
7842
+ } catch (error) {
7843
+ if (isEnoent4(error)) {
7844
+ return void 0;
7845
+ }
7846
+ throw error;
7773
7847
  }
7774
- return dirs;
7775
7848
  }
7776
7849
  var nodeLinkFs = { link: import_node_fs7.default.linkSync, copyFile: import_node_fs7.default.copyFileSync, chmod: import_node_fs7.default.chmodSync };
7777
7850
  function findPathShadow(params) {
@@ -8212,7 +8285,7 @@ function collectDoctorReport(params) {
8212
8285
  const ownExecutablePath = realOwnExecutablePath();
8213
8286
  let binaryDiscovery;
8214
8287
  try {
8215
- const binary = realResolveClaudeBinary(resolveOwnInstallDirs(paths, ownExecutablePath))();
8288
+ const binary = realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath()))();
8216
8289
  binaryDiscovery = { ok: true, binary };
8217
8290
  } catch (error) {
8218
8291
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
@@ -9188,7 +9261,7 @@ function realPrepareLaunchParams(paths, options) {
9188
9261
  fs: realFsPort,
9189
9262
  proc: options.proc,
9190
9263
  log: options.log,
9191
- resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
9264
+ resolveClaudeBinary: realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath())),
9192
9265
  farm: farm.runtime,
9193
9266
  headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
9194
9267
  frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
package/dist/index.mjs CHANGED
@@ -3064,13 +3064,14 @@ function realConnectCertStore(paths) {
3064
3064
  }
3065
3065
 
3066
3066
  // src/frontdoor/connectEffects.ts
3067
- import * as dns from "node:dns";
3067
+ import dns from "node:dns";
3068
3068
  import * as http from "node:http";
3069
3069
  import * as https from "node:https";
3070
3070
  import * as net2 from "node:net";
3071
3071
  import * as tls from "node:tls";
3072
3072
  var UPSTREAM_LOCAL_PORT_START = 47900;
3073
3073
  var UPSTREAM_LOCAL_PORT_END = 47919;
3074
+ var UPSTREAM_LOCAL_PORT_COUNT = UPSTREAM_LOCAL_PORT_END - UPSTREAM_LOCAL_PORT_START + 1;
3074
3075
  var nextUpstreamLocalPort = UPSTREAM_LOCAL_PORT_START;
3075
3076
  function upstreamLocalPort() {
3076
3077
  const port = nextUpstreamLocalPort;
@@ -3083,41 +3084,92 @@ var realAddressLookup = (host, _options, callback) => {
3083
3084
  return;
3084
3085
  }
3085
3086
  dns.resolve4(host, (error, addresses) => {
3087
+ if (error !== null) {
3088
+ callback(error, [{ address: "", family: 4 }]);
3089
+ return;
3090
+ }
3086
3091
  const first = addresses[0];
3087
- if (error !== null || first === void 0) {
3088
- callback(error ?? new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
3092
+ if (first === void 0) {
3093
+ callback(new Error(`no A record for ${host}`), [{ address: "", family: 4 }]);
3089
3094
  return;
3090
3095
  }
3091
3096
  callback(null, [{ address: first, family: 4 }]);
3092
3097
  });
3093
3098
  };
3094
3099
  var HTTP_PORT = 80;
3095
- function exemptDial(options, defaultPort) {
3096
- const host = typeof options.host === "string" ? options.host : "";
3097
- const port = typeof options.port === "number" ? options.port : defaultPort;
3098
- return net2.connect(net2.isIP(host) === 0 ? { host, port, localPort: upstreamLocalPort(), lookup: realAddressLookup } : { host, port });
3100
+ var HELD_PORT_ERRNOS = /* @__PURE__ */ new Set(["EADDRINUSE", "EADDRNOTAVAIL"]);
3101
+ async function dialOnce(connectOptions) {
3102
+ return await new Promise((resolve) => {
3103
+ const socket = net2.connect(connectOptions);
3104
+ socket.once("connect", () => {
3105
+ resolve({ error: null, socket });
3106
+ });
3107
+ socket.once("error", (error) => {
3108
+ resolve({ error, socket });
3109
+ });
3110
+ });
3111
+ }
3112
+ function dialTarget(options, defaultPort) {
3113
+ return { host: typeof options.host === "string" ? options.host : "", port: typeof options.port === "number" ? options.port : defaultPort };
3114
+ }
3115
+ function literalDial(target) {
3116
+ return net2.connect({ host: target.host, port: target.port });
3117
+ }
3118
+ async function reservedDial(target) {
3119
+ for (let attempt = 0; ; attempt += 1) {
3120
+ const outcome = await dialOnce({ host: target.host, port: target.port, localPort: upstreamLocalPort(), lookup: realAddressLookup });
3121
+ if (outcome.error === null) {
3122
+ return outcome;
3123
+ }
3124
+ const held = outcome.error.code !== void 0 && HELD_PORT_ERRNOS.has(outcome.error.code);
3125
+ if (!held || attempt === UPSTREAM_LOCAL_PORT_COUNT - 1) {
3126
+ return outcome;
3127
+ }
3128
+ }
3099
3129
  }
3100
3130
  function requestServername(options, fallback) {
3101
3131
  return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
3102
3132
  }
3103
3133
  var ExemptHttpAgent = class extends http.Agent {
3104
- createConnection(options) {
3105
- return exemptDial(options, HTTP_PORT);
3134
+ // A reserved-port dial that has to retry cannot produce its socket synchronously, so it settles first and arrives through the callback (node's documented asynchronous createConnection form); a literal dial has nothing to retry and keeps node's own synchronous shape.
3135
+ createConnection(options, oncreate) {
3136
+ const target = dialTarget(options, HTTP_PORT);
3137
+ if (net2.isIP(target.host) !== 0) {
3138
+ return literalDial(target);
3139
+ }
3140
+ void reservedDial(target).then((outcome) => {
3141
+ oncreate?.(outcome.error, outcome.socket);
3142
+ });
3143
+ return void 0;
3106
3144
  }
3107
3145
  };
3108
3146
  var ExemptTlsAgent = class extends https.Agent {
3109
- createConnection(options) {
3110
- const host = typeof options.host === "string" ? options.host : "";
3111
- const tlsOptions = { socket: exemptDial(options, HTTPS_PORT), servername: requestServername(options, host) };
3112
- if ("ca" in options && Array.isArray(options.ca)) {
3113
- tlsOptions.ca = options.ca;
3114
- }
3115
- if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
3116
- tlsOptions.rejectUnauthorized = options.rejectUnauthorized;
3117
- }
3118
- return tls.connect(tlsOptions);
3147
+ // The same split as the plain sibling: a literal dial wraps TLS around the socket node's own agent would have returned, a reserved dial settles (retries included) first and arrives through the callback, which is exactly where node's own agent wraps its own dials.
3148
+ createConnection(options, oncreate) {
3149
+ const target = dialTarget(options, HTTPS_PORT);
3150
+ if (net2.isIP(target.host) !== 0) {
3151
+ return tls.connect({ socket: literalDial(target), servername: requestServername(options, target.host), ...tlsFacts(options) });
3152
+ }
3153
+ void reservedDial(target).then((outcome) => {
3154
+ if (outcome.error !== null) {
3155
+ oncreate?.(outcome.error, outcome.socket);
3156
+ return;
3157
+ }
3158
+ oncreate?.(null, tls.connect({ socket: outcome.socket, servername: requestServername(options, target.host), ...tlsFacts(options) }));
3159
+ });
3160
+ return void 0;
3119
3161
  }
3120
3162
  };
3163
+ function tlsFacts(options) {
3164
+ const facts = {};
3165
+ if ("ca" in options && Array.isArray(options.ca)) {
3166
+ facts.ca = options.ca;
3167
+ }
3168
+ if ("rejectUnauthorized" in options && typeof options.rejectUnauthorized === "boolean") {
3169
+ facts.rejectUnauthorized = options.rejectUnauthorized;
3170
+ }
3171
+ return facts;
3172
+ }
3121
3173
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
3122
3174
  const agent = new ExemptTlsAgent();
3123
3175
  return {
@@ -6751,18 +6803,14 @@ function discoverClaudeBinary(options) {
6751
6803
  version: highestVersion
6752
6804
  };
6753
6805
  }
6754
- const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => path20.resolve(dir)));
6755
6806
  for (const dir of options.pathDirs) {
6756
- if (ownInstallDirs.has(path20.resolve(dir))) {
6757
- continue;
6758
- }
6759
6807
  const found = options.findExecutableInDir(dir, "claude");
6760
- if (found !== void 0) {
6808
+ if (found !== void 0 && !options.isOwnBinary(found)) {
6761
6809
  return { path: found, source: "path-fallback" };
6762
6810
  }
6763
6811
  }
6764
6812
  throw new Error(
6765
- `Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (excluding this tool's own install directory). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
6813
+ `Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (never this tool's own binary). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
6766
6814
  );
6767
6815
  }
6768
6816
 
@@ -7160,12 +7208,18 @@ function realOwnExecutablePath() {
7160
7208
  findExecutableInDir
7161
7209
  });
7162
7210
  }
7163
- function realResolveClaudeBinary(ownInstallDirs) {
7211
+ function resolveContentSourcePath(env) {
7212
+ return env.isSea ? env.execPath : env.argv1 ?? env.execPath;
7213
+ }
7214
+ function realContentSourcePath() {
7215
+ return resolveContentSourcePath({ isSea: isSea(), execPath: process.execPath, argv1: process.argv[1] });
7216
+ }
7217
+ function realResolveClaudeBinary(isOwnBinary) {
7164
7218
  return () => discoverClaudeBinary({
7165
7219
  listVersionsDir,
7166
7220
  pathDirs: (process.env.PATH ?? "").split(path21.delimiter).filter((dir) => dir !== ""),
7167
7221
  findExecutableInDir,
7168
- ownInstallDirs: [...ownInstallDirs]
7222
+ isOwnBinary
7169
7223
  });
7170
7224
  }
7171
7225
 
@@ -7581,13 +7635,32 @@ var ClaudeShimStateSchema = z11.strictObject({
7581
7635
  function commandFilename(ownExecutablePath, commandName) {
7582
7636
  return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
7583
7637
  }
7584
- function resolveOwnInstallDirs(paths, ownExecutablePath) {
7585
- const dirs = [path23.dirname(ownExecutablePath)];
7638
+ function resolveOwnBinaryCheck(paths, contentSourcePath) {
7586
7639
  const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
7587
- if (state !== void 0) {
7588
- dirs.push(path23.dirname(state.targetPath));
7640
+ const recordedTarget = state === void 0 ? void 0 : path23.resolve(state.targetPath);
7641
+ let ownStat;
7642
+ return (candidate) => {
7643
+ if (recordedTarget !== void 0 && path23.resolve(candidate) === recordedTarget) {
7644
+ return true;
7645
+ }
7646
+ ownStat ??= statOrUndefined(contentSourcePath);
7647
+ const candidateStat = statOrUndefined(candidate);
7648
+ return ownStat !== void 0 && candidateStat !== void 0 && // A platform reporting no inode number for either file must not read every candidate as this tool.
7649
+ ownStat.ino !== 0 && candidateStat.ino !== 0 && candidateStat.dev === ownStat.dev && candidateStat.ino === ownStat.ino;
7650
+ };
7651
+ }
7652
+ function isEnoent4(error) {
7653
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
7654
+ }
7655
+ function statOrUndefined(target) {
7656
+ try {
7657
+ return fs8.statSync(target);
7658
+ } catch (error) {
7659
+ if (isEnoent4(error)) {
7660
+ return void 0;
7661
+ }
7662
+ throw error;
7589
7663
  }
7590
- return dirs;
7591
7664
  }
7592
7665
  var nodeLinkFs = { link: fs8.linkSync, copyFile: fs8.copyFileSync, chmod: fs8.chmodSync };
7593
7666
  function findPathShadow(params) {
@@ -8028,7 +8101,7 @@ function collectDoctorReport(params) {
8028
8101
  const ownExecutablePath = realOwnExecutablePath();
8029
8102
  let binaryDiscovery;
8030
8103
  try {
8031
- const binary = realResolveClaudeBinary(resolveOwnInstallDirs(paths, ownExecutablePath))();
8104
+ const binary = realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath()))();
8032
8105
  binaryDiscovery = { ok: true, binary };
8033
8106
  } catch (error) {
8034
8107
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
@@ -9004,7 +9077,7 @@ function realPrepareLaunchParams(paths, options) {
9004
9077
  fs: realFsPort,
9005
9078
  proc: options.proc,
9006
9079
  log: options.log,
9007
- resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
9080
+ resolveClaudeBinary: realResolveClaudeBinary(resolveOwnBinaryCheck(paths, realContentSourcePath())),
9008
9081
  farm: farm.runtime,
9009
9082
  headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
9010
9083
  frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-shim",
3
- "version": "8.5.0",
3
+ "version": "8.5.2",
4
4
  "description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5
5
  "license": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",