claude-use 6.1.0 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/cli.cjs +5202 -4574
- package/dist/index.cjs +50 -17
- package/dist/index.mjs +50 -17
- package/dist/types/config/schema.d.ts +12 -0
- package/dist/types/headroom/state.d.ts +6 -1
- package/dist/types/paths.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -215,10 +215,18 @@ var ConfigProfileSchema = import_zod.z.strictObject({
|
|
|
215
215
|
entries: EntriesSchema.optional(),
|
|
216
216
|
launch: LaunchSchema.optional()
|
|
217
217
|
});
|
|
218
|
+
var POOL_SELECTOR_PREFIX = "pool:";
|
|
219
|
+
var POOL_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._-]*";
|
|
220
|
+
var IDENTITY_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._@-]*";
|
|
221
|
+
var PoolNameSchema = import_zod.z.string().min(1).regex(new RegExp(`^${POOL_NAME_PATTERN}$`));
|
|
222
|
+
var IdentitySelectorSchema = import_zod.z.string().min(1).regex(new RegExp(`^(${POOL_SELECTOR_PREFIX}${POOL_NAME_PATTERN}|${IDENTITY_NAME_PATTERN})$`));
|
|
223
|
+
var PoolSchema = import_zod.z.strictObject({
|
|
224
|
+
identities: import_zod.z.array(import_zod.z.string().min(1).regex(new RegExp(`^${IDENTITY_NAME_PATTERN}$`))).min(1).refine((names) => new Set(names).size === names.length, { message: "pool members must be unique" })
|
|
225
|
+
});
|
|
218
226
|
var DirectoryRuleSchema = ConfigProfileSchema.omit({ description: true }).extend({
|
|
219
227
|
path: import_zod.z.string().min(1),
|
|
220
228
|
configProfile: import_zod.z.string().min(1).optional(),
|
|
221
|
-
identity:
|
|
229
|
+
identity: IdentitySelectorSchema.optional(),
|
|
222
230
|
when: WhenSchema.optional()
|
|
223
231
|
});
|
|
224
232
|
var DirectoryRulesSchema = import_zod.z.strictObject({
|
|
@@ -227,7 +235,7 @@ var DirectoryRulesSchema = import_zod.z.strictObject({
|
|
|
227
235
|
});
|
|
228
236
|
var PortableConfigSchema = ConfigProfileSchema.omit({ description: true }).extend({
|
|
229
237
|
configProfile: import_zod.z.string().min(1).optional(),
|
|
230
|
-
identity:
|
|
238
|
+
identity: IdentitySelectorSchema.optional(),
|
|
231
239
|
when: WhenSchema.optional()
|
|
232
240
|
});
|
|
233
241
|
var HEADROOM_MODES = ["cache", "token"];
|
|
@@ -275,7 +283,8 @@ var GlobalConfigSchema = import_zod.z.strictObject({
|
|
|
275
283
|
entries: EntriesSchema.optional(),
|
|
276
284
|
launch: LaunchSchema.optional(),
|
|
277
285
|
headroom: HeadroomGlobalConfigSchema.optional(),
|
|
278
|
-
frontdoor: FrontDoorGlobalConfigSchema.optional()
|
|
286
|
+
frontdoor: FrontDoorGlobalConfigSchema.optional(),
|
|
287
|
+
pools: import_zod.z.record(PoolNameSchema, PoolSchema).optional()
|
|
279
288
|
});
|
|
280
289
|
var IdentitySchema = import_zod.z.strictObject({
|
|
281
290
|
$schema: import_zod.z.string().optional(),
|
|
@@ -2802,7 +2811,7 @@ var HeadroomStateSchema = import_zod4.z.strictObject({
|
|
|
2802
2811
|
/** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
|
|
2803
2812
|
lastError: import_zod4.z.string().optional()
|
|
2804
2813
|
});
|
|
2805
|
-
var HeadroomSessionSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), startedAt: import_zod4.z.number() });
|
|
2814
|
+
var HeadroomSessionSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), startedAt: import_zod4.z.number(), supervisorPid: import_zod4.z.number().int().positive() });
|
|
2806
2815
|
var HeadroomLockSchema = import_zod4.z.strictObject({ pid: import_zod4.z.number().int().positive(), at: import_zod4.z.number() });
|
|
2807
2816
|
function parseJson(raw, schema) {
|
|
2808
2817
|
if (raw === void 0 || raw.trim() === "") {
|
|
@@ -2882,6 +2891,9 @@ function listSessions(fs2, sessionsDir) {
|
|
|
2882
2891
|
}
|
|
2883
2892
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2884
2893
|
}
|
|
2894
|
+
function sessionsForSupervisor(fs2, sessionsDir, supervisorPid) {
|
|
2895
|
+
return listSessions(fs2, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
2896
|
+
}
|
|
2885
2897
|
function pruneDeadSessions(fs2, sessionsDir, isRunning) {
|
|
2886
2898
|
const removed = [];
|
|
2887
2899
|
for (const session of listSessions(fs2, sessionsDir)) {
|
|
@@ -3181,7 +3193,7 @@ function ensureHeadroom(params) {
|
|
|
3181
3193
|
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
3182
3194
|
const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
3183
3195
|
if (daemonUp && state.port !== void 0) {
|
|
3184
|
-
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now() });
|
|
3196
|
+
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
|
|
3185
3197
|
return { port: state.port };
|
|
3186
3198
|
}
|
|
3187
3199
|
if (state.lastError !== void 0) {
|
|
@@ -3318,15 +3330,28 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3318
3330
|
const { fs: fs2, paths } = ports;
|
|
3319
3331
|
let config = initialConfig;
|
|
3320
3332
|
const previousState = readHeadroomState(fs2, paths.headroomStateFile);
|
|
3333
|
+
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3334
|
+
ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3335
|
+
return 0;
|
|
3336
|
+
}
|
|
3321
3337
|
let installedSource = previousState?.installedSource;
|
|
3322
3338
|
let lastPort = previousState?.lastPort;
|
|
3323
3339
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3340
|
+
const ownsState = () => {
|
|
3341
|
+
const owner = readHeadroomState(fs2, paths.headroomStateFile)?.supervisorPid;
|
|
3342
|
+
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3343
|
+
};
|
|
3344
|
+
const writeOwnState = (state) => {
|
|
3345
|
+
if (!ownsState()) {
|
|
3346
|
+
return false;
|
|
3347
|
+
}
|
|
3348
|
+
writeHeadroomState(fs2, paths.headroomStateFile, state);
|
|
3349
|
+
return true;
|
|
3350
|
+
};
|
|
3324
3351
|
const fail = (message) => {
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
});
|
|
3329
|
-
fs2.removeRecursive(paths.headroomLockFile);
|
|
3352
|
+
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3353
|
+
fs2.removeRecursive(paths.headroomLockFile);
|
|
3354
|
+
}
|
|
3330
3355
|
ports.log(`claude-use headroom supervisor: ${message}`);
|
|
3331
3356
|
return 1;
|
|
3332
3357
|
};
|
|
@@ -3349,7 +3374,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3349
3374
|
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
3350
3375
|
}
|
|
3351
3376
|
const version = ports.headroomVersion() ?? "unknown";
|
|
3352
|
-
|
|
3377
|
+
writeOwnState({ supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
|
|
3353
3378
|
ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
|
|
3354
3379
|
const orphanPid = previousState?.headroomPid;
|
|
3355
3380
|
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
@@ -3361,6 +3386,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3361
3386
|
let runningSettingsHash;
|
|
3362
3387
|
let consecutiveFailures = 0;
|
|
3363
3388
|
let idleSince;
|
|
3389
|
+
let superseded = false;
|
|
3364
3390
|
let ticks = 0;
|
|
3365
3391
|
for (; ; ) {
|
|
3366
3392
|
if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
|
|
@@ -3372,6 +3398,11 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3372
3398
|
const allowlist = allowlistOf(ports);
|
|
3373
3399
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3374
3400
|
pruneDeadSessions(fs2, paths.headroomSessionsDir, ports.isRunning);
|
|
3401
|
+
const ownsNow = ownsState();
|
|
3402
|
+
if (!ownsNow && !superseded) {
|
|
3403
|
+
ports.log("claude-use headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
3404
|
+
}
|
|
3405
|
+
superseded = !ownsNow;
|
|
3375
3406
|
const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
|
|
3376
3407
|
if (headroomPid === void 0 || crashed) {
|
|
3377
3408
|
if (crashed) {
|
|
@@ -3379,7 +3410,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3379
3410
|
headroomPid = void 0;
|
|
3380
3411
|
runningHash = void 0;
|
|
3381
3412
|
runningSettingsHash = void 0;
|
|
3382
|
-
|
|
3413
|
+
writeOwnState({
|
|
3383
3414
|
supervisorPid: ports.ownPid,
|
|
3384
3415
|
version,
|
|
3385
3416
|
installedSource,
|
|
@@ -3407,7 +3438,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3407
3438
|
`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)`
|
|
3408
3439
|
);
|
|
3409
3440
|
}
|
|
3410
|
-
|
|
3441
|
+
const recorded = writeOwnState({
|
|
3411
3442
|
supervisorPid: ports.ownPid,
|
|
3412
3443
|
headroomPid: pid,
|
|
3413
3444
|
port,
|
|
@@ -3417,7 +3448,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3417
3448
|
installedSource: config.source,
|
|
3418
3449
|
...sticky()
|
|
3419
3450
|
});
|
|
3420
|
-
|
|
3451
|
+
if (recorded) {
|
|
3452
|
+
fs2.removeRecursive(paths.headroomLockFile);
|
|
3453
|
+
}
|
|
3421
3454
|
idleSince = void 0;
|
|
3422
3455
|
ports.log(`claude-use headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
3423
3456
|
} else {
|
|
@@ -3431,7 +3464,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3431
3464
|
}
|
|
3432
3465
|
} else {
|
|
3433
3466
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3434
|
-
if (
|
|
3467
|
+
if (sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3435
3468
|
ports.log("claude-use headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3436
3469
|
ports.stopProcess(headroomPid);
|
|
3437
3470
|
headroomPid = void 0;
|
|
@@ -3440,7 +3473,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3440
3473
|
continue;
|
|
3441
3474
|
}
|
|
3442
3475
|
}
|
|
3443
|
-
const sessions =
|
|
3476
|
+
const sessions = sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid);
|
|
3444
3477
|
if (sessions.length === 0) {
|
|
3445
3478
|
idleSince ??= ports.now();
|
|
3446
3479
|
const idleMs = ports.now() - idleSince;
|
|
@@ -3449,7 +3482,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3449
3482
|
`claude-use headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
3450
3483
|
);
|
|
3451
3484
|
ports.stopProcess(headroomPid);
|
|
3452
|
-
|
|
3485
|
+
writeOwnState({
|
|
3453
3486
|
version,
|
|
3454
3487
|
...sticky()
|
|
3455
3488
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -141,10 +141,18 @@ var ConfigProfileSchema = z.strictObject({
|
|
|
141
141
|
entries: EntriesSchema.optional(),
|
|
142
142
|
launch: LaunchSchema.optional()
|
|
143
143
|
});
|
|
144
|
+
var POOL_SELECTOR_PREFIX = "pool:";
|
|
145
|
+
var POOL_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._-]*";
|
|
146
|
+
var IDENTITY_NAME_PATTERN = "[A-Za-z0-9][A-Za-z0-9._@-]*";
|
|
147
|
+
var PoolNameSchema = z.string().min(1).regex(new RegExp(`^${POOL_NAME_PATTERN}$`));
|
|
148
|
+
var IdentitySelectorSchema = z.string().min(1).regex(new RegExp(`^(${POOL_SELECTOR_PREFIX}${POOL_NAME_PATTERN}|${IDENTITY_NAME_PATTERN})$`));
|
|
149
|
+
var PoolSchema = z.strictObject({
|
|
150
|
+
identities: z.array(z.string().min(1).regex(new RegExp(`^${IDENTITY_NAME_PATTERN}$`))).min(1).refine((names) => new Set(names).size === names.length, { message: "pool members must be unique" })
|
|
151
|
+
});
|
|
144
152
|
var DirectoryRuleSchema = ConfigProfileSchema.omit({ description: true }).extend({
|
|
145
153
|
path: z.string().min(1),
|
|
146
154
|
configProfile: z.string().min(1).optional(),
|
|
147
|
-
identity:
|
|
155
|
+
identity: IdentitySelectorSchema.optional(),
|
|
148
156
|
when: WhenSchema.optional()
|
|
149
157
|
});
|
|
150
158
|
var DirectoryRulesSchema = z.strictObject({
|
|
@@ -153,7 +161,7 @@ var DirectoryRulesSchema = z.strictObject({
|
|
|
153
161
|
});
|
|
154
162
|
var PortableConfigSchema = ConfigProfileSchema.omit({ description: true }).extend({
|
|
155
163
|
configProfile: z.string().min(1).optional(),
|
|
156
|
-
identity:
|
|
164
|
+
identity: IdentitySelectorSchema.optional(),
|
|
157
165
|
when: WhenSchema.optional()
|
|
158
166
|
});
|
|
159
167
|
var HEADROOM_MODES = ["cache", "token"];
|
|
@@ -201,7 +209,8 @@ var GlobalConfigSchema = z.strictObject({
|
|
|
201
209
|
entries: EntriesSchema.optional(),
|
|
202
210
|
launch: LaunchSchema.optional(),
|
|
203
211
|
headroom: HeadroomGlobalConfigSchema.optional(),
|
|
204
|
-
frontdoor: FrontDoorGlobalConfigSchema.optional()
|
|
212
|
+
frontdoor: FrontDoorGlobalConfigSchema.optional(),
|
|
213
|
+
pools: z.record(PoolNameSchema, PoolSchema).optional()
|
|
205
214
|
});
|
|
206
215
|
var IdentitySchema = z.strictObject({
|
|
207
216
|
$schema: z.string().optional(),
|
|
@@ -2728,7 +2737,7 @@ var HeadroomStateSchema = z4.strictObject({
|
|
|
2728
2737
|
/** The last fatal error, kept across shutdowns so `headroom status` can explain a daemon that is not running. */
|
|
2729
2738
|
lastError: z4.string().optional()
|
|
2730
2739
|
});
|
|
2731
|
-
var HeadroomSessionSchema = z4.strictObject({ pid: z4.number().int().positive(), startedAt: z4.number() });
|
|
2740
|
+
var HeadroomSessionSchema = z4.strictObject({ pid: z4.number().int().positive(), startedAt: z4.number(), supervisorPid: z4.number().int().positive() });
|
|
2732
2741
|
var HeadroomLockSchema = z4.strictObject({ pid: z4.number().int().positive(), at: z4.number() });
|
|
2733
2742
|
function parseJson(raw, schema) {
|
|
2734
2743
|
if (raw === void 0 || raw.trim() === "") {
|
|
@@ -2808,6 +2817,9 @@ function listSessions(fs2, sessionsDir) {
|
|
|
2808
2817
|
}
|
|
2809
2818
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2810
2819
|
}
|
|
2820
|
+
function sessionsForSupervisor(fs2, sessionsDir, supervisorPid) {
|
|
2821
|
+
return listSessions(fs2, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
2822
|
+
}
|
|
2811
2823
|
function pruneDeadSessions(fs2, sessionsDir, isRunning) {
|
|
2812
2824
|
const removed = [];
|
|
2813
2825
|
for (const session of listSessions(fs2, sessionsDir)) {
|
|
@@ -3107,7 +3119,7 @@ function ensureHeadroom(params) {
|
|
|
3107
3119
|
if (state?.supervisorPid !== void 0 && ports.isRunning(state.supervisorPid)) {
|
|
3108
3120
|
const daemonUp = state.port !== void 0 && state.headroomPid !== void 0 && ports.isRunning(state.headroomPid);
|
|
3109
3121
|
if (daemonUp && state.port !== void 0) {
|
|
3110
|
-
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now() });
|
|
3122
|
+
writeSession(ports.fs, paths.headroomSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), supervisorPid: state.supervisorPid });
|
|
3111
3123
|
return { port: state.port };
|
|
3112
3124
|
}
|
|
3113
3125
|
if (state.lastError !== void 0) {
|
|
@@ -3244,15 +3256,28 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3244
3256
|
const { fs: fs2, paths } = ports;
|
|
3245
3257
|
let config = initialConfig;
|
|
3246
3258
|
const previousState = readHeadroomState(fs2, paths.headroomStateFile);
|
|
3259
|
+
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3260
|
+
ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3261
|
+
return 0;
|
|
3262
|
+
}
|
|
3247
3263
|
let installedSource = previousState?.installedSource;
|
|
3248
3264
|
let lastPort = previousState?.lastPort;
|
|
3249
3265
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3266
|
+
const ownsState = () => {
|
|
3267
|
+
const owner = readHeadroomState(fs2, paths.headroomStateFile)?.supervisorPid;
|
|
3268
|
+
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3269
|
+
};
|
|
3270
|
+
const writeOwnState = (state) => {
|
|
3271
|
+
if (!ownsState()) {
|
|
3272
|
+
return false;
|
|
3273
|
+
}
|
|
3274
|
+
writeHeadroomState(fs2, paths.headroomStateFile, state);
|
|
3275
|
+
return true;
|
|
3276
|
+
};
|
|
3250
3277
|
const fail = (message) => {
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
});
|
|
3255
|
-
fs2.removeRecursive(paths.headroomLockFile);
|
|
3278
|
+
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3279
|
+
fs2.removeRecursive(paths.headroomLockFile);
|
|
3280
|
+
}
|
|
3256
3281
|
ports.log(`claude-use headroom supervisor: ${message}`);
|
|
3257
3282
|
return 1;
|
|
3258
3283
|
};
|
|
@@ -3275,7 +3300,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3275
3300
|
return fail(`could not install headroom from ${config.source}: install failed (is uv installed and on PATH?)`);
|
|
3276
3301
|
}
|
|
3277
3302
|
const version = ports.headroomVersion() ?? "unknown";
|
|
3278
|
-
|
|
3303
|
+
writeOwnState({ supervisorPid: ports.ownPid, version, installedSource, ...sticky() });
|
|
3279
3304
|
ports.log(`claude-use headroom supervisor ${String(ports.ownPid)}: managing headroom on allowlist [${allowlistOf(ports).join(", ")}]`);
|
|
3280
3305
|
const orphanPid = previousState?.headroomPid;
|
|
3281
3306
|
if (orphanPid !== void 0 && ports.isRunning(orphanPid)) {
|
|
@@ -3287,6 +3312,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3287
3312
|
let runningSettingsHash;
|
|
3288
3313
|
let consecutiveFailures = 0;
|
|
3289
3314
|
let idleSince;
|
|
3315
|
+
let superseded = false;
|
|
3290
3316
|
let ticks = 0;
|
|
3291
3317
|
for (; ; ) {
|
|
3292
3318
|
if (options.tickLimit !== void 0 && ticks >= options.tickLimit) {
|
|
@@ -3298,6 +3324,11 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3298
3324
|
const allowlist = allowlistOf(ports);
|
|
3299
3325
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3300
3326
|
pruneDeadSessions(fs2, paths.headroomSessionsDir, ports.isRunning);
|
|
3327
|
+
const ownsNow = ownsState();
|
|
3328
|
+
if (!ownsNow && !superseded) {
|
|
3329
|
+
ports.log("claude-use headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
3330
|
+
}
|
|
3331
|
+
superseded = !ownsNow;
|
|
3301
3332
|
const crashed = headroomPid !== void 0 && !ports.isRunning(headroomPid);
|
|
3302
3333
|
if (headroomPid === void 0 || crashed) {
|
|
3303
3334
|
if (crashed) {
|
|
@@ -3305,7 +3336,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3305
3336
|
headroomPid = void 0;
|
|
3306
3337
|
runningHash = void 0;
|
|
3307
3338
|
runningSettingsHash = void 0;
|
|
3308
|
-
|
|
3339
|
+
writeOwnState({
|
|
3309
3340
|
supervisorPid: ports.ownPid,
|
|
3310
3341
|
version,
|
|
3311
3342
|
installedSource,
|
|
@@ -3333,7 +3364,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3333
3364
|
`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)`
|
|
3334
3365
|
);
|
|
3335
3366
|
}
|
|
3336
|
-
|
|
3367
|
+
const recorded = writeOwnState({
|
|
3337
3368
|
supervisorPid: ports.ownPid,
|
|
3338
3369
|
headroomPid: pid,
|
|
3339
3370
|
port,
|
|
@@ -3343,7 +3374,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3343
3374
|
installedSource: config.source,
|
|
3344
3375
|
...sticky()
|
|
3345
3376
|
});
|
|
3346
|
-
|
|
3377
|
+
if (recorded) {
|
|
3378
|
+
fs2.removeRecursive(paths.headroomLockFile);
|
|
3379
|
+
}
|
|
3347
3380
|
idleSince = void 0;
|
|
3348
3381
|
ports.log(`claude-use headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
3349
3382
|
} else {
|
|
@@ -3357,7 +3390,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3357
3390
|
}
|
|
3358
3391
|
} else {
|
|
3359
3392
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3360
|
-
if (
|
|
3393
|
+
if (sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3361
3394
|
ports.log("claude-use headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3362
3395
|
ports.stopProcess(headroomPid);
|
|
3363
3396
|
headroomPid = void 0;
|
|
@@ -3366,7 +3399,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3366
3399
|
continue;
|
|
3367
3400
|
}
|
|
3368
3401
|
}
|
|
3369
|
-
const sessions =
|
|
3402
|
+
const sessions = sessionsForSupervisor(fs2, paths.headroomSessionsDir, ports.ownPid);
|
|
3370
3403
|
if (sessions.length === 0) {
|
|
3371
3404
|
idleSince ??= ports.now();
|
|
3372
3405
|
const idleMs = ports.now() - idleSince;
|
|
@@ -3375,7 +3408,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3375
3408
|
`claude-use headroom supervisor: no sessions for ${String(config.idleShutdownMinutes)} minute(s); stopping headroom and exiting`
|
|
3376
3409
|
);
|
|
3377
3410
|
ports.stopProcess(headroomPid);
|
|
3378
|
-
|
|
3411
|
+
writeOwnState({
|
|
3379
3412
|
version,
|
|
3380
3413
|
...sticky()
|
|
3381
3414
|
});
|
|
@@ -422,6 +422,15 @@ export declare const ConfigProfileSchema: z.ZodObject<{
|
|
|
422
422
|
}, z.core.$strict>>;
|
|
423
423
|
}, z.core.$strict>;
|
|
424
424
|
export type ConfigProfile = z.infer<typeof ConfigProfileSchema>;
|
|
425
|
+
/** The prefix that turns a pool name into an identity selector. `:` is outside an identity name's character set, so a selector can never be mistaken for an identity. */
|
|
426
|
+
export declare const POOL_SELECTOR_PREFIX = "pool:";
|
|
427
|
+
/** A pool's own name: the identity alphabet minus `@`, since a pool is never an email address. */
|
|
428
|
+
export declare const PoolNameSchema: z.ZodString;
|
|
429
|
+
/** A named, explicit set of identities to choose among at launch. Never implicit: a pool lists its members, so an identity that bills a client is only ever picked when someone put it there. */
|
|
430
|
+
declare const PoolSchema: z.ZodObject<{
|
|
431
|
+
identities: z.ZodArray<z.ZodString>;
|
|
432
|
+
}, z.core.$strict>;
|
|
433
|
+
export type Pool = z.infer<typeof PoolSchema>;
|
|
425
434
|
/** One directory rule in `~/.claude-use/directory-rules.json`, scoped to an explicit absolute (or `~`-rooted) path. */
|
|
426
435
|
export declare const DirectoryRuleSchema: z.ZodObject<{
|
|
427
436
|
entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
|
|
@@ -657,6 +666,9 @@ export declare const GlobalConfigSchema: z.ZodObject<{
|
|
|
657
666
|
frontdoor: z.ZodOptional<z.ZodObject<{
|
|
658
667
|
idleShutdownMinutes: z.ZodOptional<z.ZodNumber>;
|
|
659
668
|
}, z.core.$strict>>;
|
|
669
|
+
pools: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
670
|
+
identities: z.ZodArray<z.ZodString>;
|
|
671
|
+
}, z.core.$strict>>>;
|
|
660
672
|
}, z.core.$strict>;
|
|
661
673
|
export type GlobalConfig = z.infer<typeof GlobalConfigSchema>;
|
|
662
674
|
/**
|
|
@@ -20,10 +20,13 @@ export declare const HeadroomStateSchema: z.ZodObject<{
|
|
|
20
20
|
export type HeadroomState = z.infer<typeof HeadroomStateSchema>;
|
|
21
21
|
/** The filesystem operations headroom coordination needs: the same shape as `FarmFs`, injected so every decision over state, sessions, and the start lock runs against an in-memory fake in tests. */
|
|
22
22
|
export type HeadroomFs = Pick<FarmFs, "mkdirp" | "mkdirPrivate" | "readFileUtf8" | "writeFileUtf8" | "writeFilePrivate" | "writeFileExclusive" | "readdir" | "removeRecursive">;
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The record held in one session-registry file. `supervisorPid` names the supervisor whose daemon the launch connected to: several supervisors can be alive at once (a superseded one keeps serving the sessions it already has), and each must judge idle shutdown and drift restarts by its own sessions only, or none would ever retire.
|
|
25
|
+
*/
|
|
24
26
|
declare const HeadroomSessionSchema: z.ZodObject<{
|
|
25
27
|
pid: z.ZodNumber;
|
|
26
28
|
startedAt: z.ZodNumber;
|
|
29
|
+
supervisorPid: z.ZodNumber;
|
|
27
30
|
}, z.core.$strict>;
|
|
28
31
|
export type HeadroomSession = z.infer<typeof HeadroomSessionSchema>;
|
|
29
32
|
/** The exclusive-create start lock's recorded holder. */
|
|
@@ -63,6 +66,8 @@ export declare function writeSession(fs: HeadroomFs, sessionsDir: string, sessio
|
|
|
63
66
|
export declare function removeSession(fs: HeadroomFs, sessionsDir: string, pid: number): void;
|
|
64
67
|
/** Every registry entry on disk, unreadable files skipped, sorted by pid: what `headroom status` prints and what the supervisor's idle and drift decisions read. */
|
|
65
68
|
export declare function listSessions(fs: HeadroomFs, sessionsDir: string): readonly HeadroomSession[];
|
|
69
|
+
/** The registry entries recorded against one supervisor: the sessions whose daemon that supervisor is responsible for keeping up. */
|
|
70
|
+
export declare function sessionsForSupervisor(fs: HeadroomFs, sessionsDir: string, supervisorPid: number): readonly HeadroomSession[];
|
|
66
71
|
/**
|
|
67
72
|
* Removes every registry entry whose pid is no longer running: a launcher that died without releasing its entry must not keep the daemon awake or block a drift restart forever. The predicate must be zombie-aware (see `realIsProcessRunning`): a launcher that exited but was never reaped still answers signal 0 as alive, which would keep its session registered indefinitely.
|
|
68
73
|
*/
|
package/dist/types/paths.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ export interface LayoutPaths {
|
|
|
58
58
|
readonly usageLogDir: string;
|
|
59
59
|
/** Directory of the latest-state snapshots, one `<identity>.json` per identity: the stable file other tools read. */
|
|
60
60
|
readonly usageSnapshotsDir: string;
|
|
61
|
+
/** The last identity picked from a pool for each directory, so a conversation stays on the account whose prompt cache is warm. */
|
|
62
|
+
readonly usagePicksFile: string;
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
63
65
|
* Resolves the current CLAUDE_USE_HOME root: the environment variable when set to a non-empty string, otherwise `~/.claude-use`. An empty string counts as unset, consistent with how this project treats empty-string environment variables elsewhere (see the ambient-credential guard).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-use",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.2.1",
|
|
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>",
|