claude-use 11.62.3 → 11.64.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.mjs CHANGED
@@ -306,7 +306,7 @@ var init_statusline = __esm({
306
306
  init_graphemes();
307
307
  init_languages();
308
308
  STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "dailyCost", "sessionName", "duration", "speed"];
309
- STATUSLINE_ROWS = ["context", "quota", "addedDirs", "gitFiles", "memory", "environment", "sessionTime", "promptCache", "compactions", "tokens"];
309
+ STATUSLINE_ROWS = ["context", "quota", "addedDirs", "gitFiles", "memory", "environment", "sessionTime", "promptCache", "compactions", "tokens", "tools", "skills", "mcp", "agents", "todos"];
310
310
  STATUSLINE_SESSION_TIME_PARTS = ["started", "lastReply"];
311
311
  PROMPT_CACHE_TTL_SECONDS = { fiveMinutes: 300, oneHour: 3600 };
312
312
  STATUSLINE_COLOUR_NAMES = [
@@ -337,13 +337,13 @@ var init_statusline = __esm({
337
337
  PRESET_BUNDLES = {
338
338
  standard: { layout: { header: DEFAULT_HEADER_SEGMENTS, rows: DEFAULT_ROWS }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: false }, context: { breakdown: "never" } },
339
339
  full: {
340
- layout: { header: ["model", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "sessionName", "duration"], rows: ["context", "quota", "addedDirs", "memory", "environment", "promptCache", "compactions", "tokens"] },
340
+ layout: { header: ["model", "project", "vcs", "effort", "advisor", "version", "outputStyle", "cost", "sessionName", "duration"], rows: ["context", "quota", "addedDirs", "memory", "environment", "tools", "skills", "mcp", "agents", "todos", "promptCache", "compactions", "tokens"] },
341
341
  git: GIT_WITH_DIRTY_MARKER,
342
342
  jj: { enabled: true, showDirty: true, showConflicts: true },
343
343
  usage: { resetLabel: true },
344
344
  context: { breakdown: "critical" }
345
345
  },
346
- essential: { layout: { header: ["model", "project", "vcs", "duration"], rows: ["context", "addedDirs"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } },
346
+ essential: { layout: { header: ["model", "project", "vcs", "duration"], rows: ["context", "addedDirs", "tools", "agents", "todos"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } },
347
347
  minimal: { layout: { header: ["model", "project", "vcs"], rows: ["context", "addedDirs"] }, git: GIT_WITH_DIRTY_MARKER, jj: JJ_OFF, usage: { resetLabel: true }, context: { breakdown: "never" } }
348
348
  };
349
349
  STATUSLINE_PATH_LEVELS = ["1", "2", "3", "full"];
@@ -459,6 +459,10 @@ var init_statusline = __esm({
459
459
  promptCache: {
460
460
  ttlSeconds: z2.number().int().min(1).describe("How long the prompt cache lives, in seconds, when the transcript has not yet shown which tier the session writes (five minutes or one hour); the tier the transcript shows takes over once it does.")
461
461
  },
462
+ tools: {
463
+ nameMaxLength: z2.number().int().min(0).describe("The most terminal columns of a tool's name the tools row draws, cut with an ellipsis. With a limit set, a tool of an MCP server is shown by its own name, without the server's, whether or not it needs cutting. 0 draws every name whole, an MCP tool's with its server."),
464
+ maxVisible: z2.number().int().min(0).describe("The most kinds of finished tool call the tools row names, the most-used first, with the rest counted as +N more. 0 names every kind.")
465
+ },
462
466
  colours: {
463
467
  context: colourValue.describe("The colour of the context bar and its figures below the warning threshold."),
464
468
  usage: colourValue.describe("The colour of the quota bars and their figures below the warning threshold."),
@@ -470,7 +474,9 @@ var init_statusline = __esm({
470
474
  empty: colourValue.describe("The colour of the unused part of a bar."),
471
475
  project: colourValue.describe("The colour of the project path segment."),
472
476
  git: colourValue.describe("The colour of the version control segment's wrapper text (`git:(` and `)`)."),
473
- gitBranch: colourValue.describe("The colour of the branch or bookmark and its ahead, behind and dirty markers.")
477
+ gitBranch: colourValue.describe("The colour of the branch or bookmark and its ahead, behind and dirty markers."),
478
+ activity: colourValue.describe("The colour of the names of the tools, skills and MCP servers the tools, skills and mcp rows draw. Their running markers take colours.warning and their finished markers colours.context."),
479
+ agent: colourValue.describe("The colour of an agent's type in the agents row.")
474
480
  },
475
481
  project: {
476
482
  pathLevels: z2.enum(STATUSLINE_PATH_LEVELS).describe("How many trailing directories of the working directory the project segment shows: 1, 2 or 3, or full for the whole absolute path."),
@@ -497,7 +503,7 @@ var init_statusline = __esm({
497
503
  LANGUAGE_ALIASES = { zh: "zh-Hans", "zh-TW": "zh-Hant" };
498
504
  TOP_LEVEL = {
499
505
  language: z2.enum(STATUSLINE_LANGUAGES).describe(LANGUAGE_DESCRIPTION),
500
- preset: z2.enum(STATUSLINE_PRESETS).describe("A named bundle of settings (layout.header, layout.rows, git.enabled, git.showDirty, git.showAheadBehind, git.showLineDiff, jj.enabled, jj.showDirty, jj.showConflicts, usage.resetLabel and context.breakdown) that this layer sets beneath the keys it names itself and above everything the shallower layers set: standard is the statusline's own; full, essential and minimal are claude-hud's presets. Each draws the model, project and version control segments and the context row; full adds the effort, advisor, version, output style, cost, session name and duration segments and the quota, added directories, memory, environment, prompt cache, compactions and session tokens rows, with the token breakdown at high use, essential adds the duration and the added directories row, and minimal adds the added directories row alone.")
506
+ preset: z2.enum(STATUSLINE_PRESETS).describe("A named bundle of settings (layout.header, layout.rows, git.enabled, git.showDirty, git.showAheadBehind, git.showLineDiff, jj.enabled, jj.showDirty, jj.showConflicts, usage.resetLabel and context.breakdown) that this layer sets beneath the keys it names itself and above everything the shallower layers set: standard is the statusline's own; full, essential and minimal are claude-hud's presets. Each draws the model, project and version control segments and the context row; full adds the effort, advisor, version, output style, cost, session name and duration segments and the quota, added directories, memory, environment, tools, skills, mcp, agents, todos, prompt cache, compactions and session tokens rows, with the token breakdown at high use, essential adds the duration, the added directories row and the tools, agents and todos rows, and minimal adds the added directories row alone.")
501
507
  };
502
508
  StatuslineSchema = z2.strictObject({
503
509
  language: z2.enum(LANGUAGE_TAGS).transform(canonicalLanguage).describe(LANGUAGE_DESCRIPTION).optional(),
@@ -518,6 +524,7 @@ var init_statusline = __esm({
518
524
  environment: z2.strictObject(SHAPES.environment).partial().optional(),
519
525
  sessionTime: z2.strictObject(SHAPES.sessionTime).partial().optional(),
520
526
  promptCache: z2.strictObject(SHAPES.promptCache).partial().optional(),
527
+ tools: z2.strictObject(SHAPES.tools).partial().optional(),
521
528
  colours: z2.strictObject(SHAPES.colours).partial().optional(),
522
529
  project: z2.strictObject(SHAPES.project).partial().optional(),
523
530
  git: z2.strictObject(SHAPES.git).partial().optional(),
@@ -542,6 +549,7 @@ var init_statusline = __esm({
542
549
  environment: z2.strictObject(SHAPES.environment),
543
550
  sessionTime: z2.strictObject(SHAPES.sessionTime),
544
551
  promptCache: z2.strictObject(SHAPES.promptCache),
552
+ tools: z2.strictObject(SHAPES.tools),
545
553
  colours: z2.strictObject(SHAPES.colours),
546
554
  project: z2.strictObject(SHAPES.project),
547
555
  git: z2.strictObject(SHAPES.git),
@@ -566,6 +574,7 @@ var init_statusline = __esm({
566
574
  environment: { threshold: 0 },
567
575
  sessionTime: { parts: [...STATUSLINE_SESSION_TIME_PARTS] },
568
576
  promptCache: { ttlSeconds: PROMPT_CACHE_TTL_SECONDS.fiveMinutes },
577
+ tools: { nameMaxLength: 0, maxVisible: 4 },
569
578
  colours: {
570
579
  context: "green",
571
580
  usage: "green",
@@ -577,7 +586,9 @@ var init_statusline = __esm({
577
586
  empty: "brightBlack",
578
587
  project: "yellow",
579
588
  git: "magenta",
580
- gitBranch: "cyan"
589
+ gitBranch: "cyan",
590
+ activity: "cyan",
591
+ agent: "magenta"
581
592
  },
582
593
  project: { pathLevels: "1", addedDirsLayout: "inline" },
583
594
  git: { enabled: true, showDirty: true, showAheadBehind: false, showLineDiff: false, branchOverflow: "truncate", pushWarnAt: 0, pushCriticalAt: 0 },
@@ -3901,12 +3912,13 @@ function pruneDeadFrontDoorSessions(fs14, sessionsDir, listed, isRunning) {
3901
3912
  }
3902
3913
  return removed;
3903
3914
  }
3904
- var FRONT_DOOR_PROTOCOL, FrontDoorSessionSchema, FrontDoorStateSchema, MAX_TCP_PORT, FrontDoorDirectRecordSchema, RECORD_TEMPORARY_FILE;
3915
+ var FRONT_DOOR_PROTOCOL, FRONT_DOOR_HANDOVER_PROTOCOL, FrontDoorSessionSchema, FrontDoorStateSchema, MAX_TCP_PORT, FrontDoorDirectRecordSchema, RECORD_TEMPORARY_FILE;
3905
3916
  var init_state = __esm({
3906
3917
  "src/frontdoor/state.ts"() {
3907
3918
  "use strict";
3908
3919
  init_schema();
3909
- FRONT_DOOR_PROTOCOL = 5;
3920
+ FRONT_DOOR_PROTOCOL = 6;
3921
+ FRONT_DOOR_HANDOVER_PROTOCOL = 6;
3910
3922
  FrontDoorSessionSchema = z5.strictObject({ pid: z5.number().int().positive(), startedAt: z5.number(), token: z5.string().min(1), supervisorPid: z5.number().int().positive().optional(), sessionId: z5.string().min(1).optional(), models: ModelsConfigSchema.optional() });
3911
3923
  FrontDoorStateSchema = z5.strictObject({
3912
3924
  /**
@@ -4003,18 +4015,24 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4003
4015
  ...lastConnectPort === void 0 ? {} : { lastConnectPort },
4004
4016
  ...lastDirectPort === void 0 ? {} : { lastDirectPort }
4005
4017
  });
4018
+ const { predecessor } = ports;
4019
+ const sourceOf = (listener, preferredPort) => predecessor === void 0 ? { preferredPort } : { inherit: async () => await predecessor.take(listener) };
4006
4020
  const fail = (message) => {
4007
4021
  ports.closeRcStreamHub();
4008
- writeFrontDoorState(fs14, paths.frontdoorStateFile, { lastError: message, ...sticky() });
4009
- fs14.removeRecursive(paths.frontdoorLockFile);
4022
+ if (predecessor === void 0) {
4023
+ writeFrontDoorState(fs14, paths.frontdoorStateFile, { lastError: message, ...sticky() });
4024
+ fs14.removeRecursive(paths.frontdoorLockFile);
4025
+ }
4010
4026
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
4011
4027
  return 1;
4012
4028
  };
4013
- writeFrontDoorState(fs14, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
4029
+ if (predecessor === void 0) {
4030
+ writeFrontDoorState(fs14, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
4031
+ }
4014
4032
  const previousSticky = lastPort;
4015
4033
  let http3;
4016
4034
  try {
4017
- http3 = await ports.startProviderListener(lastPort);
4035
+ http3 = await ports.startProviderListener(sourceOf("provider", lastPort));
4018
4036
  } catch (error) {
4019
4037
  ports.doorHealth.listenerFailed("provider", error instanceof Error ? error.message : String(error));
4020
4038
  return fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
@@ -4026,7 +4044,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4026
4044
  const previousStickyConnect = lastConnectPort;
4027
4045
  let connect3;
4028
4046
  try {
4029
- connect3 = await ports.startConnectListener(lastConnectPort);
4047
+ connect3 = await ports.startConnectListener(sourceOf("connect", lastConnectPort));
4030
4048
  } catch (error) {
4031
4049
  await http3.close();
4032
4050
  ports.doorHealth.listenerFailed("connect", error instanceof Error ? error.message : String(error));
@@ -4039,7 +4057,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4039
4057
  const previousStickyDirect = lastDirectPort;
4040
4058
  let direct;
4041
4059
  try {
4042
- direct = await ports.startDirectListener(lastDirectPort);
4060
+ direct = await ports.startDirectListener(sourceOf("direct", lastDirectPort));
4043
4061
  } catch (error) {
4044
4062
  await connect3.close();
4045
4063
  await http3.close();
@@ -4053,8 +4071,12 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4053
4071
  writeFrontDoorDirectRecord(fs14, paths.frontdoorDirectRecordsDir, { pid: ports.ownPid, directPort: direct.port });
4054
4072
  writeFrontDoorState(fs14, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http3.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
4055
4073
  ports.doorHealth.generation({ providerPort: http3.port, connectPort: connect3.port, directPort: direct.port });
4056
- fs14.removeRecursive(paths.frontdoorLockFile);
4057
- ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http3.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
4074
+ if (predecessor === void 0) {
4075
+ fs14.removeRecursive(paths.frontdoorLockFile);
4076
+ } else {
4077
+ predecessor.serving();
4078
+ }
4079
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http3.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}${predecessor === void 0 ? "" : ", taken over from the door it replaced"}`);
4058
4080
  const watchdog = ports.startWatchdog?.();
4059
4081
  let idleSince;
4060
4082
  let ticks = 0;
@@ -4074,14 +4096,16 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4074
4096
  resolve();
4075
4097
  };
4076
4098
  ports.retire?.addEventListener("abort", wake, { once: true });
4099
+ ports.handover?.requested.addEventListener("abort", wake, { once: true });
4077
4100
  void ports.sleep(FRONTDOOR_POLL_MS).then(() => {
4078
4101
  ports.retire?.removeEventListener("abort", wake);
4102
+ ports.handover?.requested.removeEventListener("abort", wake);
4079
4103
  resolve();
4080
4104
  });
4081
4105
  });
4082
4106
  };
4083
4107
  const retireByDrain = async () => {
4084
- ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: asked to exit; releasing the front door's addresses and finishing the connections it carries`);
4108
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: releasing the front door's addresses and finishing the connections it carries`);
4085
4109
  watchdog?.stop();
4086
4110
  ports.stopBackgroundWork?.();
4087
4111
  const drained = [direct.drain(), connect3.drain(), http3.drain()];
@@ -4096,6 +4120,17 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4096
4120
  ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: the connections this door carried have ended; exiting`);
4097
4121
  return 0;
4098
4122
  };
4123
+ const handOverThenDrain = async (run) => {
4124
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: asked to hand the front door's listeners to a successor; accepting on them until it serves`);
4125
+ const outcome = await run({ provider: http3.listening, connect: connect3.listening, direct: direct.listening });
4126
+ if (outcome.ok) {
4127
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: successor ${String(outcome.successorPid)} serves the front door's listeners`);
4128
+ ports.doorHealth.superseded(outcome.successorPid);
4129
+ } else {
4130
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: the handover failed (${outcome.reason}); retiring by drain so a successor is started on the released addresses`);
4131
+ }
4132
+ return await retireByDrain();
4133
+ };
4099
4134
  const closeServing = async () => {
4100
4135
  watchdog?.stop();
4101
4136
  await direct.close();
@@ -4113,8 +4148,12 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4113
4148
  };
4114
4149
  for (; ; ) {
4115
4150
  if (ports.retire?.aborted === true) {
4151
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: asked to exit`);
4116
4152
  return await retireByDrain();
4117
4153
  }
4154
+ if (ports.handover?.requested.aborted === true && supersededBy === void 0) {
4155
+ return await handOverThenDrain(ports.handover.run);
4156
+ }
4118
4157
  if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
4119
4158
  watchdog?.stop();
4120
4159
  return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
@@ -4252,8 +4291,10 @@ function caParses(ca) {
4252
4291
  return false;
4253
4292
  }
4254
4293
  }
4255
- async function startConnectServer(config, effects, preferredPort) {
4294
+ async function startConnectServer(config, effects, source) {
4256
4295
  const tunnelTokens = /* @__PURE__ */ new WeakMap();
4296
+ const ledger = createConnectionLedger(config.limits, config.isLiveCapability);
4297
+ const lateSessionDeadlines = /* @__PURE__ */ new WeakMap();
4257
4298
  const onUpgrade = (host) => (request4, socket, head) => {
4258
4299
  config.capture?.upgrade(request4);
4259
4300
  if (head.length > 0) {
@@ -4275,6 +4316,12 @@ ${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
4275
4316
  return { host, serveSecure: serveSecure2, tlsAcceptor: tlsAcceptor2 };
4276
4317
  }
4277
4318
  const handleHostRequest = async (request4, response) => {
4319
+ if (ledger.retiring()) {
4320
+ clearTimeout(lateSessionDeadlines.get(request4.socket));
4321
+ response.once("close", () => {
4322
+ request4.socket.end();
4323
+ });
4324
+ }
4278
4325
  if (config.localSurface !== void 0 && await config.localSurface.serve(host, request4, response)) {
4279
4326
  return;
4280
4327
  }
@@ -4294,13 +4341,19 @@ ${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
4294
4341
  const httpSession = effects.createHttpSession(handler, onUpgrade(host));
4295
4342
  const serveSecure = (secure, capability) => {
4296
4343
  tunnelTokens.set(secure, capability);
4344
+ if (ledger.retiring()) {
4345
+ const deadline = setTimeout(() => {
4346
+ secure.destroy();
4347
+ }, config.limits.headDeadlineMs);
4348
+ deadline.unref();
4349
+ lateSessionDeadlines.set(secure, deadline);
4350
+ }
4297
4351
  httpSession.serve(secure);
4298
4352
  };
4299
4353
  const tlsAcceptor = effects.createTlsAcceptor(config.leafFor(host), serveSecure);
4300
4354
  return { host, serveSecure, httpSession, tlsAcceptor };
4301
4355
  });
4302
- const ledger = createConnectionLedger(config.limits, config.isLiveCapability);
4303
- const listener = await effects.listenLoopback(preferredPort, (socket) => {
4356
+ const listener = await effects.listenLoopback(source, (socket) => {
4304
4357
  handleConnect(socket, { config, effects, sessions, ledger });
4305
4358
  });
4306
4359
  const apiSession = sessions.find((session) => session.host === config.routedHost);
@@ -4313,7 +4366,7 @@ ${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
4313
4366
  }
4314
4367
  session.serveSecure(secure, config.transparentCapability ?? "");
4315
4368
  });
4316
- const transparent = config.transparentPort === void 0 || apiSession === void 0 ? void 0 : await effects.listenLoopback(config.transparentPort, (socket) => {
4369
+ const transparent = config.transparentPort === void 0 || apiSession === void 0 ? void 0 : await effects.listenLoopback({ preferredPort: config.transparentPort }, (socket) => {
4317
4370
  sniAcceptor.accept(socket);
4318
4371
  });
4319
4372
  const closeSurface = async () => {
@@ -4328,6 +4381,7 @@ ${request4.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `$
4328
4381
  };
4329
4382
  return {
4330
4383
  port: listener.port,
4384
+ listening: listener.listening,
4331
4385
  ...transparent === void 0 ? {} : { transparentPort: transparent.port },
4332
4386
  openTunnels: ledger.openTunnels,
4333
4387
  drain: async () => {
@@ -4344,7 +4398,9 @@ function createConnectionLedger(limits, isLiveCapability2) {
4344
4398
  const authenticated = /* @__PURE__ */ new Map();
4345
4399
  let tunnels = 0;
4346
4400
  const blind = /* @__PURE__ */ new Set();
4347
- let onNoBlindTunnels;
4401
+ const live = /* @__PURE__ */ new Set();
4402
+ let retiring = false;
4403
+ let onNoConnections;
4348
4404
  const leavePending = (socket) => {
4349
4405
  const deadline = pending.get(socket);
4350
4406
  if (deadline !== void 0) {
@@ -4376,20 +4432,19 @@ function createConnectionLedger(limits, isLiveCapability2) {
4376
4432
  refuse(socket, REFUSAL.requestTimeout);
4377
4433
  }, limits.headDeadlineMs)
4378
4434
  );
4435
+ live.add(socket);
4379
4436
  socket.once("close", () => {
4380
4437
  leavePending(socket);
4438
+ live.delete(socket);
4439
+ if (live.size === 0) {
4440
+ onNoConnections?.();
4441
+ }
4381
4442
  });
4382
4443
  },
4383
4444
  headReceived: leavePending,
4384
4445
  openTunnels: () => tunnels,
4385
4446
  retire: async () => {
4386
- for (const socket of pending.keys()) {
4387
- socket.destroy();
4388
- }
4389
- for (const deadline of pending.values()) {
4390
- clearTimeout(deadline);
4391
- }
4392
- pending.clear();
4447
+ retiring = true;
4393
4448
  for (const sockets of authenticated.values()) {
4394
4449
  for (const socket of sockets) {
4395
4450
  if (!blind.has(socket)) {
@@ -4397,12 +4452,13 @@ function createConnectionLedger(limits, isLiveCapability2) {
4397
4452
  }
4398
4453
  }
4399
4454
  }
4400
- if (blind.size > 0) {
4455
+ if (live.size > 0) {
4401
4456
  await new Promise((resolve) => {
4402
- onNoBlindTunnels = resolve;
4457
+ onNoConnections = resolve;
4403
4458
  });
4404
4459
  }
4405
4460
  },
4461
+ retiring: () => retiring,
4406
4462
  establish: (socket, capability, kind) => {
4407
4463
  if (tunnels >= limits.maxTunnels) {
4408
4464
  return false;
@@ -4417,9 +4473,6 @@ function createConnectionLedger(limits, isLiveCapability2) {
4417
4473
  socket.once("close", () => {
4418
4474
  tunnels -= 1;
4419
4475
  blind.delete(socket);
4420
- if (blind.size === 0) {
4421
- onNoBlindTunnels?.();
4422
- }
4423
4476
  sockets.delete(socket);
4424
4477
  if (sockets.size === 0 && authenticated.get(capability) === sockets) {
4425
4478
  authenticated.delete(capability);
@@ -4627,7 +4680,7 @@ var init_package = __esm({
4627
4680
  "package.json"() {
4628
4681
  package_default = {
4629
4682
  name: "claude-use",
4630
- version: "11.62.3",
4683
+ version: "11.64.0",
4631
4684
  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.",
4632
4685
  license: "MIT",
4633
4686
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -10154,7 +10207,8 @@ var init_replacement = __esm({
10154
10207
  import { randomUUID as randomUUID3 } from "node:crypto";
10155
10208
  function ensureFrontDoor(params) {
10156
10209
  const { paths, ports } = params;
10157
- const deadline = ports.now() + FRONTDOOR_START_TIMEOUT_MS;
10210
+ const startedAt = ports.now();
10211
+ let deadline = startedAt + FRONTDOOR_START_TIMEOUT_MS;
10158
10212
  let spawned = false;
10159
10213
  let replaced = false;
10160
10214
  const distrusted = /* @__PURE__ */ new Set();
@@ -10194,6 +10248,19 @@ function ensureFrontDoor(params) {
10194
10248
  ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
10195
10249
  }
10196
10250
  };
10251
+ const handOverAndAwait = (supervisorPid, doorProtocol, reason, request4) => {
10252
+ recordReplacement(supervisorPid, doorProtocol, reason, "SIGUSR2");
10253
+ request4(supervisorPid);
10254
+ const waitEnds = ports.now() + FRONTDOOR_START_TIMEOUT_MS + FRONTDOOR_EXIT_GRACE_MS;
10255
+ deadline += FRONTDOOR_START_TIMEOUT_MS + FRONTDOOR_EXIT_GRACE_MS;
10256
+ while (ports.isRunning(supervisorPid) && readFrontDoorState(ports.fs, paths.frontdoorStateFile)?.supervisorPid === supervisorPid) {
10257
+ if (ports.now() >= waitEnds) {
10258
+ stopAndAwaitExit(supervisorPid, doorProtocol, reason, `the front door (pid ${String(supervisorPid)}) did not act on a request to hand its listeners over, and did not exit when asked to`);
10259
+ return;
10260
+ }
10261
+ ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
10262
+ }
10263
+ };
10197
10264
  for (; ; ) {
10198
10265
  const state = readFrontDoorState(ports.fs, paths.frontdoorStateFile);
10199
10266
  const supervisorPid = state?.supervisorPid;
@@ -10207,12 +10274,16 @@ function ensureFrontDoor(params) {
10207
10274
  const verdict = verifyWithRetry(state.port);
10208
10275
  const doorProtocol = state.protocol ?? 0;
10209
10276
  if (verdict.ok && !replaced && !spawned && (params.replace !== void 0 || doorProtocol < FRONT_DOOR_PROTOCOL)) {
10210
- stopAndAwaitExit(
10211
- supervisorPid,
10212
- doorProtocol,
10213
- params.replace ?? "protocol-older",
10214
- `the front door (pid ${String(supervisorPid)}, protocol ${String(doorProtocol)}; this launcher speaks protocol ${String(FRONT_DOOR_PROTOCOL)}) was asked to be replaced and did not exit`
10215
- );
10277
+ if (params.replace !== void 0 && doorProtocol >= FRONT_DOOR_HANDOVER_PROTOCOL && ports.requestHandover !== void 0 && ports.isFrontDoorSupervisor(supervisorPid)) {
10278
+ handOverAndAwait(supervisorPid, doorProtocol, params.replace, ports.requestHandover);
10279
+ } else {
10280
+ stopAndAwaitExit(
10281
+ supervisorPid,
10282
+ doorProtocol,
10283
+ params.replace ?? "protocol-older",
10284
+ `the front door (pid ${String(supervisorPid)}, protocol ${String(doorProtocol)}; this launcher speaks protocol ${String(FRONT_DOOR_PROTOCOL)}) was asked to be replaced and did not exit`
10285
+ );
10286
+ }
10216
10287
  replaced = true;
10217
10288
  continue;
10218
10289
  }
@@ -10264,7 +10335,7 @@ function ensureFrontDoor(params) {
10264
10335
  if (ports.now() >= deadline) {
10265
10336
  const lastError = state?.lastError;
10266
10337
  throw new FrontDoorStartError(
10267
- `the front door did not become ready within ${String(FRONTDOOR_START_TIMEOUT_MS)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + (busyDoor === void 0 ? "" : `. It was not replaced because ${String(busyDoor.sessions)} live session(s) are using it and would lose their connections; run \`agent-shim frontdoor restart\` to replace it deliberately`) + `. Daemon log: ${paths.frontdoorLogPath}`
10338
+ `the front door did not become ready within ${String(deadline - startedAt)}ms` + (lastError === void 0 ? "" : ` (last error: ${lastError})`) + (lastRejection === void 0 ? "" : ` (${lastRejection})`) + (busyDoor === void 0 ? "" : `. It was not replaced because ${String(busyDoor.sessions)} live session(s) are using it and would lose their connections; run \`agent-shim frontdoor restart\` to replace it deliberately`) + `. Daemon log: ${paths.frontdoorLogPath}`
10268
10339
  );
10269
10340
  }
10270
10341
  ports.sleep(FRONTDOOR_LAUNCHER_POLL_MS);
@@ -11907,6 +11978,12 @@ function realEnsurePorts(paths, spawnDaemon) {
11907
11978
  killSupervisor: (pid) => {
11908
11979
  process.kill(pid, "SIGKILL");
11909
11980
  },
11981
+ // Windows has no user signals (Node refuses to send one), so a replacement there keeps the stop-and-start path.
11982
+ ...process.platform === "win32" ? {} : {
11983
+ requestHandover: (pid) => {
11984
+ process.kill(pid, "SIGUSR2");
11985
+ }
11986
+ },
11910
11987
  // Read fresh for each probe: a replacement supervisor may have regenerated an unparseable CA, and the probe must trust exactly what the serving door's leaf chains to.
11911
11988
  verifyListener: (port) => probeFrontDoorSync(port, fs13.readFileSync(paths.frontdoorCaCertFile, "utf8"))
11912
11989
  };
@@ -14336,7 +14413,7 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
14336
14413
  function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
14337
14414
  const plainAgent = new ExemptHttpAgent(ports);
14338
14415
  const tlsAgent = new ExemptTlsAgent(ports);
14339
- const listenOnce = async (port, onSocket) => await new Promise((resolve, reject) => {
14416
+ const listenOnce = async (target, onSocket) => await new Promise((resolve, reject) => {
14340
14417
  const accepted = /* @__PURE__ */ new Set();
14341
14418
  const server = net2.createServer((socket) => {
14342
14419
  accepted.add(socket);
@@ -14346,7 +14423,7 @@ function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
14346
14423
  onSocket(socket);
14347
14424
  });
14348
14425
  server.once("error", reject);
14349
- server.listen(port, "127.0.0.1", () => {
14426
+ const onListening = () => {
14350
14427
  const address = server.address();
14351
14428
  const bound = typeof address === "object" && address !== null ? address.port : 0;
14352
14429
  if (bound === 0) {
@@ -14355,6 +14432,7 @@ function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
14355
14432
  }
14356
14433
  resolve({
14357
14434
  port: bound,
14435
+ listening: server,
14358
14436
  // The listening socket is released by `close` at once; the connections already accepted are untouched and keep the server's `close` callback pending until they end.
14359
14437
  stopAccepting: () => {
14360
14438
  server.close();
@@ -14370,10 +14448,19 @@ function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
14370
14448
  });
14371
14449
  }
14372
14450
  });
14373
- });
14451
+ };
14452
+ if (typeof target === "number") {
14453
+ server.listen(target, "127.0.0.1", onListening);
14454
+ } else {
14455
+ server.listen(target, onListening);
14456
+ }
14374
14457
  });
14375
14458
  return {
14376
- listenLoopback: async (preferredPort, onSocket) => {
14459
+ listenLoopback: async (source, onSocket) => {
14460
+ if ("inherit" in source) {
14461
+ return await listenOnce(await source.inherit(), onSocket);
14462
+ }
14463
+ const { preferredPort } = source;
14377
14464
  if (preferredPort !== void 0) {
14378
14465
  try {
14379
14466
  return await listenOnce(preferredPort, onSocket);