claude-use 2.5.2 → 2.5.4
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 +161 -51
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -221824,7 +221824,7 @@ var program = new Command();
|
|
|
221824
221824
|
// package.json
|
|
221825
221825
|
var package_default = {
|
|
221826
221826
|
name: "claude-use",
|
|
221827
|
-
version: "2.5.
|
|
221827
|
+
version: "2.5.4",
|
|
221828
221828
|
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.",
|
|
221829
221829
|
license: "Apache-2.0",
|
|
221830
221830
|
author: "Joseph Mearman <joseph@mearman.co.uk>",
|
|
@@ -243134,7 +243134,7 @@ function acquireIdentityLock(params) {
|
|
|
243134
243134
|
}
|
|
243135
243135
|
lastHolderPid = existing.pid;
|
|
243136
243136
|
const expired = params.nowMs() - existing.acquiredAtMs > staleAfterMs;
|
|
243137
|
-
if (expired || !params.
|
|
243137
|
+
if (expired || !params.isRunning(existing.pid)) {
|
|
243138
243138
|
params.fs.removeRecursive(lockPath);
|
|
243139
243139
|
continue;
|
|
243140
243140
|
}
|
|
@@ -243394,7 +243394,7 @@ function recoverFarm(params) {
|
|
|
243394
243394
|
fs: params.fs,
|
|
243395
243395
|
nowMs: params.now,
|
|
243396
243396
|
pid: params.lock.pid,
|
|
243397
|
-
|
|
243397
|
+
isRunning: params.lock.isRunning,
|
|
243398
243398
|
sleep: params.lock.sleep,
|
|
243399
243399
|
...params.lock.staleAfterMs === void 0 ? {} : { staleAfterMs: params.lock.staleAfterMs },
|
|
243400
243400
|
...params.lock.retryDelayMs === void 0 ? {} : { retryDelayMs: params.lock.retryDelayMs },
|
|
@@ -243487,7 +243487,7 @@ function resyncFarm(params) {
|
|
|
243487
243487
|
fs: params.fs,
|
|
243488
243488
|
nowMs: params.now,
|
|
243489
243489
|
pid: params.lock.pid,
|
|
243490
|
-
|
|
243490
|
+
isRunning: params.lock.isRunning,
|
|
243491
243491
|
sleep: params.lock.sleep,
|
|
243492
243492
|
...params.lock.staleAfterMs === void 0 ? {} : { staleAfterMs: params.lock.staleAfterMs },
|
|
243493
243493
|
...params.lock.retryDelayMs === void 0 ? {} : { retryDelayMs: params.lock.retryDelayMs },
|
|
@@ -243749,6 +243749,7 @@ function resolveLayoutPaths() {
|
|
|
243749
243749
|
var import_node_child_process2 = require("node:child_process");
|
|
243750
243750
|
var import_node_crypto4 = require("node:crypto");
|
|
243751
243751
|
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
243752
|
+
var import_node_net = __toESM(require("node:net"), 1);
|
|
243752
243753
|
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
243753
243754
|
var import_node_sea = require("node:sea");
|
|
243754
243755
|
|
|
@@ -243829,6 +243830,10 @@ var HeadroomStateSchema = external_exports.strictObject({
|
|
|
243829
243830
|
headroomPid: external_exports.number().int().positive().optional(),
|
|
243830
243831
|
/** 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. */
|
|
243831
243832
|
port: external_exports.number().int().positive().optional(),
|
|
243833
|
+
/**
|
|
243834
|
+
* 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.
|
|
243835
|
+
*/
|
|
243836
|
+
lastPort: external_exports.number().int().positive().optional(),
|
|
243832
243837
|
/** The `headroom --version` output of the running install. */
|
|
243833
243838
|
version: external_exports.string().optional(),
|
|
243834
243839
|
/** Hash of the allowlist the running proxy was started with, so a provider-file change is detected as drift. */
|
|
@@ -243911,10 +243916,10 @@ function listSessions(fs11, sessionsDir) {
|
|
|
243911
243916
|
}
|
|
243912
243917
|
return sessions.sort((a2, b) => a2.pid - b.pid);
|
|
243913
243918
|
}
|
|
243914
|
-
function pruneDeadSessions(fs11, sessionsDir,
|
|
243919
|
+
function pruneDeadSessions(fs11, sessionsDir, isRunning) {
|
|
243915
243920
|
const removed = [];
|
|
243916
243921
|
for (const session of listSessions(fs11, sessionsDir)) {
|
|
243917
|
-
if (!
|
|
243922
|
+
if (!isRunning(session.pid)) {
|
|
243918
243923
|
removeSession(fs11, sessionsDir, session.pid);
|
|
243919
243924
|
removed.push(session.pid);
|
|
243920
243925
|
}
|
|
@@ -243944,8 +243949,8 @@ function ensureHeadroom(params) {
|
|
|
243944
243949
|
let spawnedSupervisor = false;
|
|
243945
243950
|
for (; ; ) {
|
|
243946
243951
|
const state = readHeadroomState(ports.fs, paths.headroomStateFile);
|
|
243947
|
-
if (state?.supervisorPid !== void 0 && ports.
|
|
243948
|
-
const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.
|
|
243952
|
+
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
243953
|
+
const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
243949
243954
|
if (daemonUp && state.port !== void 0) {
|
|
243950
243955
|
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now() });
|
|
243951
243956
|
return { port: state.port };
|
|
@@ -243967,7 +243972,7 @@ function ensureHeadroom(params) {
|
|
|
243967
243972
|
spawnedSupervisor = true;
|
|
243968
243973
|
} else {
|
|
243969
243974
|
const lock = readStartLock(ports.fs, paths.headroomLockFile);
|
|
243970
|
-
if (lock === void 0 || !ports.
|
|
243975
|
+
if (lock === void 0 || !ports.isRunning(lock.pid)) {
|
|
243971
243976
|
ports.fs.removeRecursive(paths.headroomLockFile);
|
|
243972
243977
|
continue;
|
|
243973
243978
|
}
|
|
@@ -244099,6 +244104,13 @@ function realIsProcessAlive(pid) {
|
|
|
244099
244104
|
return isErrorWithCode(error62, "EPERM");
|
|
244100
244105
|
}
|
|
244101
244106
|
}
|
|
244107
|
+
function realIsProcessZombie(pid) {
|
|
244108
|
+
const result = (0, import_node_child_process2.spawnSync)("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
|
|
244109
|
+
return result.status === 0 && result.stdout.trim().startsWith("Z");
|
|
244110
|
+
}
|
|
244111
|
+
function realIsProcessRunning(pid) {
|
|
244112
|
+
return realIsProcessAlive(pid) && !realIsProcessZombie(pid);
|
|
244113
|
+
}
|
|
244102
244114
|
var realRunPort = {
|
|
244103
244115
|
run(command, args) {
|
|
244104
244116
|
const result = (0, import_node_child_process2.spawnSync)(command, [...args], { encoding: "utf8" });
|
|
@@ -244146,6 +244158,21 @@ function spawnHeadroomSupervisor(paths) {
|
|
|
244146
244158
|
import_node_fs3.default.closeSync(logFd);
|
|
244147
244159
|
}
|
|
244148
244160
|
}
|
|
244161
|
+
async function realIsPortFree(port) {
|
|
244162
|
+
return await new Promise((resolve) => {
|
|
244163
|
+
const server2 = import_node_net.default.createServer();
|
|
244164
|
+
server2.unref();
|
|
244165
|
+
server2.once("error", () => {
|
|
244166
|
+
resolve(false);
|
|
244167
|
+
});
|
|
244168
|
+
server2.once("close", () => {
|
|
244169
|
+
resolve(true);
|
|
244170
|
+
});
|
|
244171
|
+
server2.listen(port, "127.0.0.1", () => {
|
|
244172
|
+
server2.close();
|
|
244173
|
+
});
|
|
244174
|
+
});
|
|
244175
|
+
}
|
|
244149
244176
|
function realHeadroomPort(paths) {
|
|
244150
244177
|
return {
|
|
244151
244178
|
ensure: () => {
|
|
@@ -244154,7 +244181,8 @@ function realHeadroomPort(paths) {
|
|
|
244154
244181
|
launcherPid: process.pid,
|
|
244155
244182
|
ports: {
|
|
244156
244183
|
fs: realFarmFs,
|
|
244157
|
-
|
|
244184
|
+
// Zombie-aware on purpose: a supervisor that died while still a child of this launcher sits unreaped until the launcher itself exits, and a defunct supervisor answering signal 0 as alive would stretch every launch to the full start timeout.
|
|
244185
|
+
isRunning: realIsProcessRunning,
|
|
244158
244186
|
now: () => Date.now(),
|
|
244159
244187
|
sleep: realSleepSync,
|
|
244160
244188
|
spawnSupervisor: spawnHeadroomSupervisor
|
|
@@ -247223,8 +247251,8 @@ function runDoctor(params) {
|
|
|
247223
247251
|
push("headroom", "fail", validated.message);
|
|
247224
247252
|
} else {
|
|
247225
247253
|
const state = validated.data;
|
|
247226
|
-
const supervisorAlive = state.supervisorPid !== void 0 && params.headroom.
|
|
247227
|
-
const headroomAlive = state.headroomPid !== void 0 && params.headroom.
|
|
247254
|
+
const supervisorAlive = state.supervisorPid !== void 0 && params.headroom.isRunning(state.supervisorPid);
|
|
247255
|
+
const headroomAlive = state.headroomPid !== void 0 && params.headroom.isRunning(state.headroomPid);
|
|
247228
247256
|
if (state.supervisorPid === void 0) {
|
|
247229
247257
|
if (state.lastError === void 0) {
|
|
247230
247258
|
push("headroom", "pass", "Headroom daemon is stopped (idle shutdown) with no recorded error.");
|
|
@@ -247400,7 +247428,7 @@ function registerDoctorCommand(program2, paths) {
|
|
|
247400
247428
|
},
|
|
247401
247429
|
run: realRunPort,
|
|
247402
247430
|
platform: process.platform,
|
|
247403
|
-
headroom: { state: { path: paths.headroomStateFile, raw: realFsPort.readFileUtf8(paths.headroomStateFile) },
|
|
247431
|
+
headroom: { state: { path: paths.headroomStateFile, raw: realFsPort.readFileUtf8(paths.headroomStateFile) }, isRunning: realIsProcessRunning }
|
|
247404
247432
|
});
|
|
247405
247433
|
for (const line of formatDoctorReport(report)) {
|
|
247406
247434
|
console.log(line);
|
|
@@ -247413,7 +247441,7 @@ function registerDoctorCommand(program2, paths) {
|
|
|
247413
247441
|
|
|
247414
247442
|
// src/headroom/commands.ts
|
|
247415
247443
|
var import_node_fs9 = __toESM(require("node:fs"), 1);
|
|
247416
|
-
var
|
|
247444
|
+
var import_node_net2 = __toESM(require("node:net"), 1);
|
|
247417
247445
|
var import_node_child_process3 = require("node:child_process");
|
|
247418
247446
|
|
|
247419
247447
|
// src/headroom/supervisor.ts
|
|
@@ -247429,6 +247457,36 @@ var HEADROOM_START_RETRY_BUDGET = 5;
|
|
|
247429
247457
|
var HEADROOM_READY_TIMEOUT_MS = 15e3;
|
|
247430
247458
|
var HEADROOM_POLL_MS = 1e3;
|
|
247431
247459
|
var MS_PER_MINUTE = 6e4;
|
|
247460
|
+
var STOP_POLL_MS = 100;
|
|
247461
|
+
var TERM_GRACE_MS = 1500;
|
|
247462
|
+
var KILL_GRACE_MS = 1500;
|
|
247463
|
+
function stopSupervisedProcess(pid, primitives) {
|
|
247464
|
+
const send = (signal) => {
|
|
247465
|
+
try {
|
|
247466
|
+
primitives.signal(pid, signal);
|
|
247467
|
+
} catch {
|
|
247468
|
+
}
|
|
247469
|
+
};
|
|
247470
|
+
send("SIGTERM");
|
|
247471
|
+
if (awaitExit(pid, primitives, TERM_GRACE_MS)) {
|
|
247472
|
+
return "exited-on-term";
|
|
247473
|
+
}
|
|
247474
|
+
send("SIGKILL");
|
|
247475
|
+
if (awaitExit(pid, primitives, KILL_GRACE_MS)) {
|
|
247476
|
+
return "killed";
|
|
247477
|
+
}
|
|
247478
|
+
return "still-running";
|
|
247479
|
+
}
|
|
247480
|
+
function awaitExit(pid, primitives, budgetMs) {
|
|
247481
|
+
const deadline = primitives.now() + budgetMs;
|
|
247482
|
+
while (primitives.isRunning(pid)) {
|
|
247483
|
+
if (primitives.now() >= deadline) {
|
|
247484
|
+
return false;
|
|
247485
|
+
}
|
|
247486
|
+
primitives.waitMs(STOP_POLL_MS);
|
|
247487
|
+
}
|
|
247488
|
+
return true;
|
|
247489
|
+
}
|
|
247432
247490
|
function backoffForAttempt(attempt) {
|
|
247433
247491
|
return Math.min(HEADROOM_BACKOFF_CAP_MS, HEADROOM_BACKOFF_BASE_MS * 2 ** (attempt - 1));
|
|
247434
247492
|
}
|
|
@@ -247485,13 +247543,18 @@ function compareVersions2(a2, b) {
|
|
|
247485
247543
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
247486
247544
|
async function runSupervisor(config2, ports, options = {}) {
|
|
247487
247545
|
const { fs: fs11, paths } = ports;
|
|
247546
|
+
const previousState = readHeadroomState(fs11, paths.headroomStateFile);
|
|
247547
|
+
let installedSource = previousState?.installedSource;
|
|
247548
|
+
let lastPort = previousState?.lastPort;
|
|
247488
247549
|
const fail = (message) => {
|
|
247489
|
-
writeHeadroomState(fs11, paths.headroomStateFile, {
|
|
247550
|
+
writeHeadroomState(fs11, paths.headroomStateFile, {
|
|
247551
|
+
lastError: message,
|
|
247552
|
+
...lastPort === void 0 ? {} : { lastPort }
|
|
247553
|
+
});
|
|
247490
247554
|
fs11.removeRecursive(paths.headroomLockFile);
|
|
247491
247555
|
ports.log(`claude-use headroom supervisor: ${message}`);
|
|
247492
247556
|
return 1;
|
|
247493
247557
|
};
|
|
247494
|
-
let installedSource = readHeadroomState(fs11, paths.headroomStateFile)?.installedSource;
|
|
247495
247558
|
const ensureInstalled = () => {
|
|
247496
247559
|
const version3 = ports.headroomVersion();
|
|
247497
247560
|
const versionOk = version3 !== void 0 && versionSatisfies(version3, config2.source);
|
|
@@ -247511,8 +247574,13 @@ async function runSupervisor(config2, ports, options = {}) {
|
|
|
247511
247574
|
return fail(`could not install headroom from ${config2.source}: install failed (is uv installed and on PATH?)`);
|
|
247512
247575
|
}
|
|
247513
247576
|
const version2 = ports.headroomVersion() ?? "unknown";
|
|
247514
|
-
writeHeadroomState(fs11, paths.headroomStateFile, { supervisorPid: ports.ownPid, version: version2, installedSource });
|
|
247577
|
+
writeHeadroomState(fs11, paths.headroomStateFile, { supervisorPid: ports.ownPid, version: version2, installedSource, ...lastPort === void 0 ? {} : { lastPort } });
|
|
247515
247578
|
ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
|
|
247579
|
+
const orphanPid = previousState?.headroomPid;
|
|
247580
|
+
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
247581
|
+
ports.log(`claude-use headroom supervisor: stopping daemon pid ${String(orphanPid)} left behind by the previous supervisor`);
|
|
247582
|
+
ports.stopProcess(orphanPid);
|
|
247583
|
+
}
|
|
247516
247584
|
let headroomPid;
|
|
247517
247585
|
let runningHash;
|
|
247518
247586
|
let consecutiveFailures = 0;
|
|
@@ -247525,13 +247593,19 @@ async function runSupervisor(config2, ports, options = {}) {
|
|
|
247525
247593
|
ticks += 1;
|
|
247526
247594
|
const allowlist = allowlistOf(ports);
|
|
247527
247595
|
const allowlistHash = hashAllowlist(allowlist);
|
|
247528
|
-
pruneDeadSessions(fs11, paths.headroomSessionsDir, ports.
|
|
247529
|
-
const crashed = headroomPid !== void 0 && !ports.
|
|
247596
|
+
pruneDeadSessions(fs11, paths.headroomSessionsDir, ports.isRunning);
|
|
247597
|
+
const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
|
|
247530
247598
|
if (headroomPid === void 0 || crashed) {
|
|
247531
247599
|
if (crashed) {
|
|
247532
247600
|
ports.log(`claude-use headroom supervisor: headroom pid ${String(headroomPid)} died`);
|
|
247533
247601
|
headroomPid = void 0;
|
|
247534
247602
|
runningHash = void 0;
|
|
247603
|
+
writeHeadroomState(fs11, paths.headroomStateFile, {
|
|
247604
|
+
supervisorPid: ports.ownPid,
|
|
247605
|
+
version: version2,
|
|
247606
|
+
installedSource,
|
|
247607
|
+
...lastPort === void 0 ? {} : { lastPort }
|
|
247608
|
+
});
|
|
247535
247609
|
}
|
|
247536
247610
|
if (consecutiveFailures >= HEADROOM_START_RETRY_BUDGET) {
|
|
247537
247611
|
return fail(`headroom failed to become ready ${String(consecutiveFailures)} times in a row; giving up`);
|
|
@@ -247539,17 +247613,25 @@ async function runSupervisor(config2, ports, options = {}) {
|
|
|
247539
247613
|
if (installedSource !== config2.source && !ensureInstalled()) {
|
|
247540
247614
|
return fail(`could not install headroom from ${config2.source}: install failed (is uv installed and on PATH?)`);
|
|
247541
247615
|
}
|
|
247542
|
-
const
|
|
247616
|
+
const previousSticky = lastPort;
|
|
247617
|
+
const port = lastPort !== void 0 && await ports.isPortFree(lastPort) ? lastPort : await ports.freePort();
|
|
247543
247618
|
const pid = ports.spawnHeadroom(port, allowlist);
|
|
247544
247619
|
const ready = await waitUntilReady(ports, port);
|
|
247545
247620
|
if (ready) {
|
|
247546
247621
|
consecutiveFailures = 0;
|
|
247547
247622
|
headroomPid = pid;
|
|
247548
247623
|
runningHash = allowlistHash;
|
|
247624
|
+
lastPort = port;
|
|
247625
|
+
if (previousSticky !== void 0 && port !== previousSticky) {
|
|
247626
|
+
ports.log(
|
|
247627
|
+
`claude-use headroom supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(port)} (sessions launched against the old port are stale until they relaunch)`
|
|
247628
|
+
);
|
|
247629
|
+
}
|
|
247549
247630
|
writeHeadroomState(fs11, paths.headroomStateFile, {
|
|
247550
247631
|
supervisorPid: ports.ownPid,
|
|
247551
247632
|
headroomPid: pid,
|
|
247552
247633
|
port,
|
|
247634
|
+
lastPort: port,
|
|
247553
247635
|
version: version2,
|
|
247554
247636
|
allowlistHash,
|
|
247555
247637
|
installedSource: config2.source
|
|
@@ -247563,7 +247645,7 @@ async function runSupervisor(config2, ports, options = {}) {
|
|
|
247563
247645
|
ports.log(
|
|
247564
247646
|
`claude-use headroom supervisor: headroom did not become ready on port ${String(port)} (attempt ${String(consecutiveFailures)} of ${String(HEADROOM_START_RETRY_BUDGET)})`
|
|
247565
247647
|
);
|
|
247566
|
-
ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
247648
|
+
await ports.sleep(backoffForAttempt(consecutiveFailures));
|
|
247567
247649
|
continue;
|
|
247568
247650
|
}
|
|
247569
247651
|
} else {
|
|
@@ -247585,14 +247667,17 @@ async function runSupervisor(config2, ports, options = {}) {
|
|
|
247585
247667
|
`claude-use headroom supervisor: no sessions for ${String(config2.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
247586
247668
|
);
|
|
247587
247669
|
ports.stopProcess(headroomPid);
|
|
247588
|
-
writeHeadroomState(fs11, paths.headroomStateFile, {
|
|
247670
|
+
writeHeadroomState(fs11, paths.headroomStateFile, {
|
|
247671
|
+
version: version2,
|
|
247672
|
+
...lastPort === void 0 ? {} : { lastPort }
|
|
247673
|
+
});
|
|
247589
247674
|
return 0;
|
|
247590
247675
|
}
|
|
247591
247676
|
} else {
|
|
247592
247677
|
idleSince = void 0;
|
|
247593
247678
|
}
|
|
247594
247679
|
}
|
|
247595
|
-
ports.sleep(HEADROOM_POLL_MS);
|
|
247680
|
+
await ports.sleep(HEADROOM_POLL_MS);
|
|
247596
247681
|
}
|
|
247597
247682
|
}
|
|
247598
247683
|
function allowlistOf(ports) {
|
|
@@ -247607,21 +247692,21 @@ async function waitUntilReady(ports, port) {
|
|
|
247607
247692
|
if (ports.now() >= deadline) {
|
|
247608
247693
|
return false;
|
|
247609
247694
|
}
|
|
247610
|
-
ports.sleep(HEADROOM_POLL_MS);
|
|
247695
|
+
await ports.sleep(HEADROOM_POLL_MS);
|
|
247611
247696
|
}
|
|
247612
247697
|
}
|
|
247613
247698
|
|
|
247614
247699
|
// src/headroom/commands.ts
|
|
247615
|
-
function collectHeadroomStatus(fsPort, paths,
|
|
247700
|
+
function collectHeadroomStatus(fsPort, paths, isRunning) {
|
|
247616
247701
|
const state = readHeadroomState(fsPort, paths.headroomStateFile) ?? {};
|
|
247617
247702
|
const allowlist = headroomAllowlist(readAllProviders(fsPort, paths.providersDir).map((entry) => entry.provider));
|
|
247618
247703
|
return {
|
|
247619
247704
|
state,
|
|
247620
|
-
supervisorAlive: state.supervisorPid !== void 0 &&
|
|
247621
|
-
headroomAlive: state.headroomPid !== void 0 &&
|
|
247705
|
+
supervisorAlive: state.supervisorPid !== void 0 && isRunning(state.supervisorPid),
|
|
247706
|
+
headroomAlive: state.headroomPid !== void 0 && isRunning(state.headroomPid),
|
|
247622
247707
|
sessions: listSessions(fsPort, paths.headroomSessionsDir).map((session) => ({
|
|
247623
247708
|
...session,
|
|
247624
|
-
alive:
|
|
247709
|
+
alive: isRunning(session.pid)
|
|
247625
247710
|
})),
|
|
247626
247711
|
allowlist,
|
|
247627
247712
|
allowlistDrifted: state.allowlistHash !== void 0 && state.allowlistHash !== hashAllowlist(allowlist),
|
|
@@ -247661,7 +247746,7 @@ function formatHeadroomStatus(status) {
|
|
|
247661
247746
|
}
|
|
247662
247747
|
async function realFreePort() {
|
|
247663
247748
|
return await new Promise((resolve, reject) => {
|
|
247664
|
-
const server2 =
|
|
247749
|
+
const server2 = import_node_net2.default.createServer();
|
|
247665
247750
|
server2.unref();
|
|
247666
247751
|
server2.on("error", reject);
|
|
247667
247752
|
server2.listen(0, "127.0.0.1", () => {
|
|
@@ -247677,18 +247762,27 @@ async function realFreePort() {
|
|
|
247677
247762
|
});
|
|
247678
247763
|
});
|
|
247679
247764
|
}
|
|
247680
|
-
var
|
|
247681
|
-
var
|
|
247765
|
+
var ownedHeadroom = /* @__PURE__ */ new Map();
|
|
247766
|
+
var exitedHeadroom = /* @__PURE__ */ new Set();
|
|
247682
247767
|
var READY_FETCH_TIMEOUT_MS = 2e3;
|
|
247768
|
+
function headroomPidRunning(pid) {
|
|
247769
|
+
return !exitedHeadroom.has(pid) && realIsProcessRunning(pid);
|
|
247770
|
+
}
|
|
247683
247771
|
function realSupervisorPorts(paths) {
|
|
247684
247772
|
return {
|
|
247685
247773
|
fs: realFarmFs,
|
|
247686
247774
|
paths,
|
|
247687
247775
|
ownPid: process.pid,
|
|
247688
247776
|
now: () => Date.now(),
|
|
247689
|
-
|
|
247690
|
-
|
|
247777
|
+
// A real timer, not the launcher's blocking Atomics.wait: the supervisor lives on its event loop, and the child's exit event can only be delivered while the loop turns.
|
|
247778
|
+
sleep: async (ms) => {
|
|
247779
|
+
await new Promise((resolve) => {
|
|
247780
|
+
setTimeout(resolve, ms);
|
|
247781
|
+
});
|
|
247782
|
+
},
|
|
247783
|
+
isRunning: headroomPidRunning,
|
|
247691
247784
|
freePort: realFreePort,
|
|
247785
|
+
isPortFree: realIsPortFree,
|
|
247692
247786
|
spawnHeadroom: (port, allowlist) => {
|
|
247693
247787
|
import_node_fs9.default.mkdirSync(paths.logsDir, { recursive: true });
|
|
247694
247788
|
const logFd = import_node_fs9.default.openSync(paths.headroomLogPath, "a");
|
|
@@ -247698,28 +247792,37 @@ function realSupervisorPorts(paths) {
|
|
|
247698
247792
|
stdio: ["ignore", logFd, logFd],
|
|
247699
247793
|
env: { ...process.env, HEADROOM_ALLOWED_BASE_URLS: allowlist.join(",") }
|
|
247700
247794
|
});
|
|
247701
|
-
child.unref();
|
|
247702
247795
|
if (child.pid === void 0) {
|
|
247703
247796
|
throw new Error("spawning headroom returned no pid");
|
|
247704
247797
|
}
|
|
247705
|
-
|
|
247706
|
-
|
|
247798
|
+
const pid = child.pid;
|
|
247799
|
+
ownedHeadroom.set(pid, child);
|
|
247800
|
+
child.once("exit", (code, signal) => {
|
|
247801
|
+
ownedHeadroom.delete(pid);
|
|
247802
|
+
exitedHeadroom.add(pid);
|
|
247803
|
+
import_node_fs9.default.appendFileSync(paths.headroomLogPath, `${(/* @__PURE__ */ new Date()).toISOString()} claude-use headroom supervisor: headroom exited (${signal ?? `code ${String(code)}`})
|
|
247804
|
+
`);
|
|
247805
|
+
});
|
|
247806
|
+
return pid;
|
|
247707
247807
|
} finally {
|
|
247708
247808
|
import_node_fs9.default.closeSync(logFd);
|
|
247709
247809
|
}
|
|
247710
247810
|
},
|
|
247711
247811
|
stopProcess: (pid) => {
|
|
247712
|
-
|
|
247713
|
-
|
|
247714
|
-
|
|
247715
|
-
|
|
247716
|
-
|
|
247717
|
-
|
|
247718
|
-
|
|
247719
|
-
}
|
|
247720
|
-
|
|
247721
|
-
|
|
247722
|
-
|
|
247812
|
+
const outcome = stopSupervisedProcess(pid, {
|
|
247813
|
+
signal: (target, signal) => {
|
|
247814
|
+
process.kill(target, signal);
|
|
247815
|
+
},
|
|
247816
|
+
isRunning: headroomPidRunning,
|
|
247817
|
+
waitMs: realSleepSync,
|
|
247818
|
+
now: () => Date.now()
|
|
247819
|
+
});
|
|
247820
|
+
if (outcome === "still-running") {
|
|
247821
|
+
import_node_fs9.default.appendFileSync(
|
|
247822
|
+
paths.headroomLogPath,
|
|
247823
|
+
`${(/* @__PURE__ */ new Date()).toISOString()} claude-use headroom supervisor: pid ${String(pid)} survived SIGKILL within its grace; it is stuck uninterruptibly
|
|
247824
|
+
`
|
|
247825
|
+
);
|
|
247723
247826
|
}
|
|
247724
247827
|
},
|
|
247725
247828
|
ready: async (port) => {
|
|
@@ -247757,7 +247860,7 @@ function realSupervisorPorts(paths) {
|
|
|
247757
247860
|
function registerHeadroomCommand(program2, paths) {
|
|
247758
247861
|
const headroom = program2.command("headroom").description("Inspect the headroom routing daemon.");
|
|
247759
247862
|
headroom.command("status").description("Report the headroom daemon's supervisor, process, port, sessions, and last error. Read-only.").action(() => {
|
|
247760
|
-
for (const line of formatHeadroomStatus(collectHeadroomStatus(realFarmFs, paths,
|
|
247863
|
+
for (const line of formatHeadroomStatus(collectHeadroomStatus(realFarmFs, paths, realIsProcessRunning))) {
|
|
247761
247864
|
console.log(line);
|
|
247762
247865
|
}
|
|
247763
247866
|
});
|
|
@@ -247765,13 +247868,19 @@ function registerHeadroomCommand(program2, paths) {
|
|
|
247765
247868
|
const globalConfig2 = readGlobalConfig(paths);
|
|
247766
247869
|
const config2 = resolveSupervisorConfig(globalConfig2?.headroom ?? {});
|
|
247767
247870
|
process.on("exit", () => {
|
|
247768
|
-
|
|
247871
|
+
for (const pid of ownedHeadroom.keys()) {
|
|
247769
247872
|
try {
|
|
247770
|
-
process.kill(
|
|
247873
|
+
process.kill(pid, "SIGKILL");
|
|
247771
247874
|
} catch {
|
|
247772
247875
|
}
|
|
247773
247876
|
}
|
|
247774
247877
|
});
|
|
247878
|
+
process.on("SIGTERM", () => {
|
|
247879
|
+
process.exit(0);
|
|
247880
|
+
});
|
|
247881
|
+
process.on("SIGINT", () => {
|
|
247882
|
+
process.exit(0);
|
|
247883
|
+
});
|
|
247775
247884
|
const code = await runSupervisor(config2, realSupervisorPorts(paths));
|
|
247776
247885
|
process.exit(code);
|
|
247777
247886
|
});
|
|
@@ -248462,7 +248571,8 @@ function buildFarmRuntime(paths) {
|
|
|
248462
248571
|
}).input,
|
|
248463
248572
|
now: () => Date.now(),
|
|
248464
248573
|
uniqueSuffix: `${String(process.pid)}.${(0, import_node_crypto5.randomUUID)()}`,
|
|
248465
|
-
|
|
248574
|
+
// Zombie-aware on purpose: a previous launcher that crashed out of a resync without releasing the lock may sit unreaped, still answering signal 0 as alive, and must read as a dead holder so this launch takes the lock over instead of timing out.
|
|
248575
|
+
lock: { pid: process.pid, isRunning: realIsProcessRunning, sleep: realSleepSync }
|
|
248466
248576
|
},
|
|
248467
248577
|
...selections.identity === void 0 ? {} : { directoryIdentity: selections.identity },
|
|
248468
248578
|
...selections.configProfile === void 0 ? {} : { directoryConfigProfile: selections.configProfile },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-use",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.4",
|
|
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": "Apache-2.0",
|
|
6
6
|
"author": "Joseph Mearman <joseph@mearman.co.uk>",
|