claude-use 11.54.0 → 11.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -138,12 +138,37 @@ var init_proxyBypass = __esm({
138
138
  }
139
139
  });
140
140
 
141
+ // src/statusline/blank.ts
142
+ function drawsNothing(text) {
143
+ return (0, import_string_width.default)(text.replace(BLANK_CELLS, "")) === 0;
144
+ }
145
+ var import_string_width, BLANK_CELLS;
146
+ var init_blank = __esm({
147
+ "src/statusline/blank.ts"() {
148
+ "use strict";
149
+ import_string_width = __toESM(require("string-width"), 1);
150
+ BLANK_CELLS = /[\p{Zs}\u2800\u3164\uFFA0]/gu;
151
+ }
152
+ });
153
+
154
+ // src/statusline/graphemes.ts
155
+ function graphemesOf(text) {
156
+ return Array.from(SEGMENTER.segment(text), (part) => part.segment);
157
+ }
158
+ var SEGMENTER;
159
+ var init_graphemes = __esm({
160
+ "src/statusline/graphemes.ts"() {
161
+ "use strict";
162
+ SEGMENTER = new Intl.Segmenter(void 0, { granularity: "grapheme" });
163
+ }
164
+ });
165
+
141
166
  // src/config/statusline.ts
142
167
  function distinctList(member) {
143
168
  return import_zod.z.array(member).refine((items) => new Set(items).size === items.length, "must not repeat an entry");
144
169
  }
145
170
  function isSingleVisibleGrapheme(text) {
146
- return [...GRAPHEMES.segment(text)].length === 1 && !/[\p{C}\p{Z}]/u.test(text);
171
+ return graphemesOf(text).length === 1 && (0, import_string_width2.default)(text) === 1 && !/[\p{C}\p{Z}]/u.test(text) && !drawsNothing(text);
147
172
  }
148
173
  function isPlainObject(value) {
149
174
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -165,11 +190,14 @@ function resolveStatusline(settings) {
165
190
  }
166
191
  return resolved;
167
192
  }
168
- var import_zod, STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PATH_LEVELS, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_CHARS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, LABEL_RE2, OPTIONAL_LABEL_RE, colourValue, GRAPHEMES, barCharacter, labelText, overrideText, wholePercent, SHAPES, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
193
+ var import_string_width2, import_zod, STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PATH_LEVELS, STATUSLINE_USAGE_VALUES, STATUSLINE_TIME_FORMATS, STATUSLINE_HOUR_CYCLES, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_COLUMNS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, LABEL_RE2, OPTIONAL_LABEL_RE, colourValue, barCharacter, labelText, overrideText, wholePercent, thresholdPercent, SHAPES, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
169
194
  var init_statusline = __esm({
170
195
  "src/config/statusline.ts"() {
171
196
  "use strict";
197
+ import_string_width2 = __toESM(require("string-width"), 1);
172
198
  import_zod = require("zod");
199
+ init_blank();
200
+ init_graphemes();
173
201
  STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle"];
174
202
  STATUSLINE_ROWS = ["context", "quota", "addedDirs", "gitFiles"];
175
203
  STATUSLINE_COLOUR_NAMES = [
@@ -195,20 +223,23 @@ var init_statusline = __esm({
195
223
  DEFAULT_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom"];
196
224
  DEFAULT_ROWS = ["context", "quota"];
197
225
  STATUSLINE_PATH_LEVELS = ["1", "2", "3", "full"];
226
+ STATUSLINE_USAGE_VALUES = ["used", "remaining"];
227
+ STATUSLINE_TIME_FORMATS = ["relative", "absolute", "both", "elapsed", "elapsedAndAbsolute"];
228
+ STATUSLINE_HOUR_CYCLES = ["auto", "h11", "h12", "h23", "h24"];
198
229
  MAX_PERCENT = 100;
199
230
  MAX_STATUSLINE_PALETTE_INDEX = 255;
200
- MAX_STATUSLINE_LABEL_CHARS = 12;
231
+ MAX_STATUSLINE_LABEL_COLUMNS = 12;
201
232
  MAX_BAR_CELLS = 40;
202
233
  MAX_ACCOUNT_USER_CHARS = 254;
203
234
  MAX_DISPLAY_TEXT_CHARS = 80;
204
235
  LABEL_RE2 = /^[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*$/u;
205
236
  OPTIONAL_LABEL_RE = /^(?:[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*)?$/u;
206
237
  colourValue = import_zod.z.union([import_zod.z.enum(STATUSLINE_COLOUR_NAMES), import_zod.z.number().int().min(0).max(MAX_STATUSLINE_PALETTE_INDEX), import_zod.z.string().regex(/^#[0-9a-fA-F]{6}$/, "must be a colour name, a palette index from 0 to 255 or #rrggbb")]);
207
- GRAPHEMES = new Intl.Segmenter(void 0, { granularity: "grapheme" });
208
- barCharacter = import_zod.z.string().refine(isSingleVisibleGrapheme, "must be a single visible character");
209
- labelText = import_zod.z.string().max(MAX_STATUSLINE_LABEL_CHARS).regex(LABEL_RE2, "must be visible text without control characters");
238
+ barCharacter = import_zod.z.string().refine(isSingleVisibleGrapheme, "must be a single visible character that takes one terminal column");
239
+ labelText = import_zod.z.string().refine((text) => (0, import_string_width2.default)(text) <= MAX_STATUSLINE_LABEL_COLUMNS, `must take no more than ${String(MAX_STATUSLINE_LABEL_COLUMNS)} terminal columns`).refine((text) => !drawsNothing(text), "must show something: a label of only combining marks or blank characters draws nothing").regex(LABEL_RE2, "must be visible text without control characters");
210
240
  overrideText = import_zod.z.string().max(MAX_DISPLAY_TEXT_CHARS).regex(OPTIONAL_LABEL_RE, "must be visible text without control characters");
211
241
  wholePercent = import_zod.z.number().int().min(1).max(MAX_PERCENT);
242
+ thresholdPercent = import_zod.z.number().int().min(0).max(MAX_PERCENT);
212
243
  SHAPES = {
213
244
  layout: {
214
245
  header: distinctList(import_zod.z.enum(STATUSLINE_HEADER_SEGMENTS)).describe("The header segments to draw, in drawing order; a segment left out is hidden, and an empty list hides the header."),
@@ -245,6 +276,20 @@ var init_statusline = __esm({
245
276
  filled: barCharacter.describe("The character that draws the used part of a bar."),
246
277
  empty: barCharacter.describe("The character that draws the unused part of a bar.")
247
278
  },
279
+ usage: {
280
+ value: import_zod.z.enum(STATUSLINE_USAGE_VALUES).describe("Whether a quota window's figure is the share of it used or the share left; the bar and the colour always follow the share used."),
281
+ bar: import_zod.z.boolean().describe("Whether each quota window is drawn as a bar, or as text alone."),
282
+ compact: import_zod.z.boolean().describe("Whether the quota windows are drawn as one short line of text (`5h: 25.0% (1h 30m)`) in place of bars; it takes precedence over usage.bar."),
283
+ resetLabel: import_zod.z.boolean().describe("Whether a window's reset time is introduced with `resets in` (`resets` for a wall-clock time), as in `(resets in 1h 30m)`, rather than the bare `(1h 30m)`."),
284
+ modelScoped: import_zod.z.boolean().describe("Whether the per-model weekly windows the client reports (rate_limits.model_scoped) are drawn after the account's own windows; they describe the signed-in account, so a session that selected a provider never shows them."),
285
+ timeFormat: import_zod.z.enum(STATUSLINE_TIME_FORMATS).describe("How a window's reset is shown: the time left (`1h 30m`), the wall-clock time (`at 14:30`), both, the share of the window already gone (`53% elapsed`), or that share and the wall-clock time. A window whose length is not known shows no share."),
286
+ threshold: thresholdPercent.describe("The whole percentage of use that at least one quota window must reach for the quota row to be drawn at all; 0 always draws it."),
287
+ sevenDayThreshold: thresholdPercent.describe("The whole percentage of use at which the weekly window is drawn beside the five-hour one; below it the weekly window is hidden. A weekly window with no five-hour window beside it is always drawn, and 0 always draws it.")
288
+ },
289
+ clock: {
290
+ hourCycle: import_zod.z.enum(STATUSLINE_HOUR_CYCLES).describe("The hour cycle of a wall-clock time: `auto` follows the system locale, `h23` and `h24` are 24-hour time (`h24` writes midnight as 24:00), and `h12` and `h11` are 12-hour time with am or pm (`h11` writes the hour after midnight as 0)."),
291
+ seconds: import_zod.z.boolean().describe("Whether a wall-clock time includes seconds (`14:30:07`).")
292
+ },
248
293
  colours: {
249
294
  context: colourValue.describe("The colour of the context bar and its figures below the warning threshold."),
250
295
  usage: colourValue.describe("The colour of the quota bars and their figures below the warning threshold."),
@@ -287,6 +332,8 @@ var init_statusline = __esm({
287
332
  bands: import_zod.z.strictObject(SHAPES.bands).partial().optional(),
288
333
  labels: import_zod.z.strictObject(SHAPES.labels).partial().optional(),
289
334
  bar: import_zod.z.strictObject(SHAPES.bar).partial().optional(),
335
+ usage: import_zod.z.strictObject(SHAPES.usage).partial().optional(),
336
+ clock: import_zod.z.strictObject(SHAPES.clock).partial().optional(),
290
337
  colours: import_zod.z.strictObject(SHAPES.colours).partial().optional(),
291
338
  project: import_zod.z.strictObject(SHAPES.project).partial().optional(),
292
339
  git: import_zod.z.strictObject(SHAPES.git).partial().optional(),
@@ -302,6 +349,8 @@ var init_statusline = __esm({
302
349
  bands: import_zod.z.strictObject(SHAPES.bands),
303
350
  labels: import_zod.z.strictObject(SHAPES.labels),
304
351
  bar: import_zod.z.strictObject(SHAPES.bar),
352
+ usage: import_zod.z.strictObject(SHAPES.usage),
353
+ clock: import_zod.z.strictObject(SHAPES.clock),
305
354
  colours: import_zod.z.strictObject(SHAPES.colours),
306
355
  project: import_zod.z.strictObject(SHAPES.project),
307
356
  git: import_zod.z.strictObject(SHAPES.git),
@@ -317,6 +366,8 @@ var init_statusline = __esm({
317
366
  bands: { warnAt: 70, criticalAt: 90 },
318
367
  labels: { fiveHour: "5h", sevenDay: "Wk" },
319
368
  bar: { width: 10, filled: "\u2588", empty: "\u2591" },
369
+ usage: { value: "used", bar: true, compact: false, resetLabel: false, modelScoped: false, timeFormat: "relative", threshold: 0, sevenDayThreshold: 0 },
370
+ clock: { hourCycle: "auto", seconds: false },
320
371
  colours: {
321
372
  context: "green",
322
373
  usage: "green",
@@ -3828,9 +3879,9 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3828
3879
  const watchdog = ports.startWatchdog?.();
3829
3880
  let idleSince;
3830
3881
  let ticks = 0;
3831
- const supersedingOwner = () => {
3882
+ const supersedingOwner = async () => {
3832
3883
  const owner = readFrontDoorState(fs14, paths.frontdoorStateFile)?.supervisorPid;
3833
- return owner !== void 0 && owner !== ports.ownPid && ports.isRunning(owner) ? owner : void 0;
3884
+ return owner !== void 0 && owner !== ports.ownPid && (await ports.runningAmong([owner])).has(owner) ? owner : void 0;
3834
3885
  };
3835
3886
  let supersededBy;
3836
3887
  let served;
@@ -3855,7 +3906,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3855
3906
  watchdog?.stop();
3856
3907
  ports.stopBackgroundWork?.();
3857
3908
  const drained = [direct.drain(), connect3.drain(), http3.drain()];
3858
- if (supersedingOwner() === void 0) {
3909
+ if (await supersedingOwner() === void 0) {
3859
3910
  writeFrontDoorState(fs14, paths.frontdoorStateFile, sticky());
3860
3911
  }
3861
3912
  markFrontDoorDraining(fs14, paths.frontdoorDrainingDir, ports.ownPid);
@@ -3876,7 +3927,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3876
3927
  };
3877
3928
  const shutdown = async () => {
3878
3929
  await closeServing();
3879
- if (supersedingOwner() === void 0) {
3930
+ if (await supersedingOwner() === void 0) {
3880
3931
  writeFrontDoorState(fs14, paths.frontdoorStateFile, sticky());
3881
3932
  }
3882
3933
  return 0;
@@ -3892,13 +3943,14 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3892
3943
  }
3893
3944
  ticks += 1;
3894
3945
  const listed = listFrontDoorSessions(fs14, paths.frontdoorSessionsDir);
3895
- const pruned = pruneDeadFrontDoorSessions(fs14, paths.frontdoorSessionsDir, ports.isRunning);
3946
+ const running = await ports.runningAmong(listed.map((session) => session.pid));
3947
+ const pruned = pruneDeadFrontDoorSessions(fs14, paths.frontdoorSessionsDir, (pid) => running.has(pid));
3896
3948
  ports.observeLaunchRegistry(listed, pruned);
3897
3949
  ports.checkQuotaExpiry();
3898
3950
  const prunedPids = new Set(pruned);
3899
3951
  const live = listed.filter((session) => !prunedPids.has(session.pid));
3900
3952
  if (supersededBy === void 0) {
3901
- supersededBy = supersedingOwner();
3953
+ supersededBy = await supersedingOwner();
3902
3954
  if (supersededBy !== void 0) {
3903
3955
  ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: supervisor ${String(supersededBy)} now owns the front door; taking no new sessions and exiting once the sessions this door serves have ended`);
3904
3956
  ports.doorHealth.superseded(supersededBy);
@@ -4398,7 +4450,7 @@ var init_package = __esm({
4398
4450
  "package.json"() {
4399
4451
  package_default = {
4400
4452
  name: "claude-use",
4401
- version: "11.54.0",
4453
+ version: "11.55.0",
4402
4454
  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.",
4403
4455
  license: "MIT",
4404
4456
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -4456,6 +4508,7 @@ var init_package = __esm({
4456
4508
  commander: "^15.0.0",
4457
4509
  cosmiconfig: "^10.0.1",
4458
4510
  picomatch: "^4.0.7",
4511
+ "string-width": "8.3.0",
4459
4512
  trilean: "1.7.1",
4460
4513
  undici: "7.29.1",
4461
4514
  zod: "^4.6.1"
@@ -5652,6 +5705,7 @@ function realIsProcessAlive(pid) {
5652
5705
  }
5653
5706
  }
5654
5707
  function realIsProcessZombie(pid) {
5708
+ refuseToBlockInRole("a synchronous process-table read");
5655
5709
  const result = (0, import_node_child_process2.spawnSync)("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
5656
5710
  return result.status === 0 && result.stdout.trim().startsWith("Z");
5657
5711
  }
@@ -5659,14 +5713,17 @@ function realIsProcessRunning(pid) {
5659
5713
  return realIsProcessAlive(pid) && !realIsProcessZombie(pid);
5660
5714
  }
5661
5715
  function realIsFrontDoorSupervisor(pid) {
5716
+ refuseToBlockInRole("a synchronous process-table read");
5662
5717
  const result = (0, import_node_child_process2.spawnSync)("ps", ["-o", "pid=,command=", "-p", String(pid)], { encoding: "utf8" });
5663
5718
  return result.status === 0 && parseFrontDoorSupervisorPids(result.stdout).includes(pid);
5664
5719
  }
5665
5720
  function realProcessCommandLine(pid) {
5721
+ refuseToBlockInRole("a synchronous process-table read");
5666
5722
  const result = (0, import_node_child_process2.spawnSync)("ps", ["-o", "command=", "-p", String(pid)], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
5667
5723
  return result.status === 0 ? result.stdout.trim() : void 0;
5668
5724
  }
5669
5725
  function realFrontDoorLiveness(pids) {
5726
+ refuseToBlockInRole("a synchronous process-table read");
5670
5727
  return frontDoorLivenessOf(pids, (asked) => {
5671
5728
  const result = (0, import_node_child_process2.spawnSync)("ps", ["-o", "pid=,stat=,command=", "-p", asked.map(String).join(",")], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
5672
5729
  return { status: result.status, stdout: result.stdout, stderr: result.stderr, ...result.error === void 0 ? {} : { error: result.error } };
@@ -11988,7 +12045,7 @@ var init_identityClaude = __esm({
11988
12045
  });
11989
12046
 
11990
12047
  // src/usage/providerQuota.ts
11991
- var import_zod34, MS_PER_SECOND9, SECONDS_PER_HOUR3, HOURS_PER_DAY6, DAYS_PER_MONTH, ZAI_HOST, ZAI_QUOTA_URL, ZAI_UNIT_HOURS, ZAI_UNIT_MONTHS, ZAI_UNIT_MS, ZaiLimitSchema, ZaiResponseSchema, OPENROUTER_HOST, OPENROUTER_KEY_URL, OPENROUTER_CREDITS_URL, MS_PER_DAY3, OpenRouterKeySchema, OpenRouterCreditsSchema, ANTIGRAVITY_HOST, ANTIGRAVITY_QUOTA_URL, ANTIGRAVITY_USER_URL, FIVE_HOURS2, DAYS_PER_WEEK2, FIVE_HOURS_MS, WEEK_MS, AntigravityBucketSchema, AntigravitySummarySchema, GoogleTokenInfoSchema, AntigravityUserSchema;
12048
+ var import_zod34, MS_PER_SECOND9, SECONDS_PER_HOUR3, HOURS_PER_DAY6, DAYS_PER_MONTH, ZAI_HOST, ZAI_QUOTA_URL, ZAI_UNIT_HOURS, ZAI_UNIT_MONTHS, ZAI_UNIT_MS, ZaiLimitSchema, ZaiResponseSchema, OPENROUTER_HOST, OPENROUTER_KEY_URL, OPENROUTER_CREDITS_URL, MS_PER_DAY2, OpenRouterKeySchema, OpenRouterCreditsSchema, ANTIGRAVITY_HOST, ANTIGRAVITY_QUOTA_URL, ANTIGRAVITY_USER_URL, FIVE_HOURS2, DAYS_PER_WEEK2, FIVE_HOURS_MS, WEEK_MS, AntigravityBucketSchema, AntigravitySummarySchema, GoogleTokenInfoSchema, AntigravityUserSchema;
11992
12049
  var init_providerQuota = __esm({
11993
12050
  "src/usage/providerQuota.ts"() {
11994
12051
  "use strict";
@@ -12025,7 +12082,7 @@ var init_providerQuota = __esm({
12025
12082
  OPENROUTER_HOST = "openrouter.ai";
12026
12083
  OPENROUTER_KEY_URL = `https://${OPENROUTER_HOST}/api/v1/key`;
12027
12084
  OPENROUTER_CREDITS_URL = `https://${OPENROUTER_HOST}/api/v1/credits`;
12028
- MS_PER_DAY3 = HOURS_PER_DAY6 * SECONDS_PER_HOUR3 * MS_PER_SECOND9;
12085
+ MS_PER_DAY2 = HOURS_PER_DAY6 * SECONDS_PER_HOUR3 * MS_PER_SECOND9;
12029
12086
  OpenRouterKeySchema = import_zod34.z.object({
12030
12087
  data: import_zod34.z.object({
12031
12088
  limit: import_zod34.z.number().nonnegative().nullable(),
@@ -18629,13 +18686,8 @@ function createDoorHealthPublisher(hub, now, pid) {
18629
18686
  }
18630
18687
 
18631
18688
  // src/frontdoor/quotaExpiringEvents.ts
18689
+ init_pick();
18632
18690
  init_schema2();
18633
- var MS_PER_HOUR5 = 36e5;
18634
- var MS_PER_DAY2 = 864e5;
18635
- var FIVE_HOUR_WINDOW_HOURS = 5;
18636
- var SEVEN_DAY_WINDOW_DAYS = 7;
18637
- var FIVE_HOUR_WINDOW_MS2 = FIVE_HOUR_WINDOW_HOURS * MS_PER_HOUR5;
18638
- var SEVEN_DAY_WINDOW_MS2 = SEVEN_DAY_WINDOW_DAYS * MS_PER_DAY2;
18639
18691
  var FINAL_SPAN_DENOMINATOR = 30;
18640
18692
  var QUOTA_EXPIRING_WINDOW_FRACTION = 1 / FINAL_SPAN_DENOMINATOR;
18641
18693
  function createQuotaExpiringPublisher(hub, deps) {
@@ -18655,11 +18707,11 @@ function createQuotaExpiringPublisher(hub, deps) {
18655
18707
  const unified = snapshot.providers[provider]?.rateLimit?.unified;
18656
18708
  const fiveHour = unified?.fiveHour;
18657
18709
  if (fiveHour?.utilization !== void 0 && fiveHour.resetsAt !== void 0) {
18658
- views.push({ provider, view: { window: "fiveHour", spanMs: FIVE_HOUR_WINDOW_MS2, utilization: fiveHour.utilization, resetsAtMs: Date.parse(fiveHour.resetsAt) } });
18710
+ views.push({ provider, view: { window: "fiveHour", spanMs: FIVE_HOUR_WINDOW_MS, utilization: fiveHour.utilization, resetsAtMs: Date.parse(fiveHour.resetsAt) } });
18659
18711
  }
18660
18712
  const sevenDay = unified?.sevenDay;
18661
18713
  if (sevenDay?.utilization !== void 0 && sevenDay.resetsAt !== void 0) {
18662
- views.push({ provider, view: { window: "sevenDay", spanMs: SEVEN_DAY_WINDOW_MS2, utilization: sevenDay.utilization, resetsAtMs: Date.parse(sevenDay.resetsAt) } });
18714
+ views.push({ provider, view: { window: "sevenDay", spanMs: SEVEN_DAY_WINDOW_MS, utilization: sevenDay.utilization, resetsAtMs: Date.parse(sevenDay.resetsAt) } });
18663
18715
  }
18664
18716
  }
18665
18717
  return views;
package/dist/index.mjs CHANGED
@@ -116,13 +116,39 @@ var init_proxyBypass = __esm({
116
116
  }
117
117
  });
118
118
 
119
+ // src/statusline/blank.ts
120
+ import stringWidth from "string-width";
121
+ function drawsNothing(text) {
122
+ return stringWidth(text.replace(BLANK_CELLS, "")) === 0;
123
+ }
124
+ var BLANK_CELLS;
125
+ var init_blank = __esm({
126
+ "src/statusline/blank.ts"() {
127
+ "use strict";
128
+ BLANK_CELLS = /[\p{Zs}\u2800\u3164\uFFA0]/gu;
129
+ }
130
+ });
131
+
132
+ // src/statusline/graphemes.ts
133
+ function graphemesOf(text) {
134
+ return Array.from(SEGMENTER.segment(text), (part) => part.segment);
135
+ }
136
+ var SEGMENTER;
137
+ var init_graphemes = __esm({
138
+ "src/statusline/graphemes.ts"() {
139
+ "use strict";
140
+ SEGMENTER = new Intl.Segmenter(void 0, { granularity: "grapheme" });
141
+ }
142
+ });
143
+
119
144
  // src/config/statusline.ts
145
+ import stringWidth2 from "string-width";
120
146
  import { z } from "zod";
121
147
  function distinctList(member) {
122
148
  return z.array(member).refine((items) => new Set(items).size === items.length, "must not repeat an entry");
123
149
  }
124
150
  function isSingleVisibleGrapheme(text) {
125
- return [...GRAPHEMES.segment(text)].length === 1 && !/[\p{C}\p{Z}]/u.test(text);
151
+ return graphemesOf(text).length === 1 && stringWidth2(text) === 1 && !/[\p{C}\p{Z}]/u.test(text) && !drawsNothing(text);
126
152
  }
127
153
  function isPlainObject(value) {
128
154
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -144,10 +170,12 @@ function resolveStatusline(settings) {
144
170
  }
145
171
  return resolved;
146
172
  }
147
- var STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PATH_LEVELS, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_CHARS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, LABEL_RE2, OPTIONAL_LABEL_RE, colourValue, GRAPHEMES, barCharacter, labelText, overrideText, wholePercent, SHAPES, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
173
+ var STATUSLINE_HEADER_SEGMENTS, STATUSLINE_ROWS, STATUSLINE_COLOUR_NAMES, DEFAULT_HEADER_SEGMENTS, DEFAULT_ROWS, STATUSLINE_PATH_LEVELS, STATUSLINE_USAGE_VALUES, STATUSLINE_TIME_FORMATS, STATUSLINE_HOUR_CYCLES, MAX_PERCENT, MAX_STATUSLINE_PALETTE_INDEX, MAX_STATUSLINE_LABEL_COLUMNS, MAX_BAR_CELLS, MAX_ACCOUNT_USER_CHARS, MAX_DISPLAY_TEXT_CHARS, LABEL_RE2, OPTIONAL_LABEL_RE, colourValue, barCharacter, labelText, overrideText, wholePercent, thresholdPercent, SHAPES, StatuslineSchema, ResolvedStatuslineSchema, STATUSLINE_DEFAULTS, STATUSLINE_ENV;
148
174
  var init_statusline = __esm({
149
175
  "src/config/statusline.ts"() {
150
176
  "use strict";
177
+ init_blank();
178
+ init_graphemes();
151
179
  STATUSLINE_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom", "project", "vcs", "effort", "advisor", "version", "outputStyle"];
152
180
  STATUSLINE_ROWS = ["context", "quota", "addedDirs", "gitFiles"];
153
181
  STATUSLINE_COLOUR_NAMES = [
@@ -173,20 +201,23 @@ var init_statusline = __esm({
173
201
  DEFAULT_HEADER_SEGMENTS = ["model", "provider", "route", "account", "identity", "headroom"];
174
202
  DEFAULT_ROWS = ["context", "quota"];
175
203
  STATUSLINE_PATH_LEVELS = ["1", "2", "3", "full"];
204
+ STATUSLINE_USAGE_VALUES = ["used", "remaining"];
205
+ STATUSLINE_TIME_FORMATS = ["relative", "absolute", "both", "elapsed", "elapsedAndAbsolute"];
206
+ STATUSLINE_HOUR_CYCLES = ["auto", "h11", "h12", "h23", "h24"];
176
207
  MAX_PERCENT = 100;
177
208
  MAX_STATUSLINE_PALETTE_INDEX = 255;
178
- MAX_STATUSLINE_LABEL_CHARS = 12;
209
+ MAX_STATUSLINE_LABEL_COLUMNS = 12;
179
210
  MAX_BAR_CELLS = 40;
180
211
  MAX_ACCOUNT_USER_CHARS = 254;
181
212
  MAX_DISPLAY_TEXT_CHARS = 80;
182
213
  LABEL_RE2 = /^[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*$/u;
183
214
  OPTIONAL_LABEL_RE = /^(?:[^\p{C}\p{Z}]+(?: [^\p{C}\p{Z}]+)*)?$/u;
184
215
  colourValue = z.union([z.enum(STATUSLINE_COLOUR_NAMES), z.number().int().min(0).max(MAX_STATUSLINE_PALETTE_INDEX), z.string().regex(/^#[0-9a-fA-F]{6}$/, "must be a colour name, a palette index from 0 to 255 or #rrggbb")]);
185
- GRAPHEMES = new Intl.Segmenter(void 0, { granularity: "grapheme" });
186
- barCharacter = z.string().refine(isSingleVisibleGrapheme, "must be a single visible character");
187
- labelText = z.string().max(MAX_STATUSLINE_LABEL_CHARS).regex(LABEL_RE2, "must be visible text without control characters");
216
+ barCharacter = z.string().refine(isSingleVisibleGrapheme, "must be a single visible character that takes one terminal column");
217
+ labelText = z.string().refine((text) => stringWidth2(text) <= MAX_STATUSLINE_LABEL_COLUMNS, `must take no more than ${String(MAX_STATUSLINE_LABEL_COLUMNS)} terminal columns`).refine((text) => !drawsNothing(text), "must show something: a label of only combining marks or blank characters draws nothing").regex(LABEL_RE2, "must be visible text without control characters");
188
218
  overrideText = z.string().max(MAX_DISPLAY_TEXT_CHARS).regex(OPTIONAL_LABEL_RE, "must be visible text without control characters");
189
219
  wholePercent = z.number().int().min(1).max(MAX_PERCENT);
220
+ thresholdPercent = z.number().int().min(0).max(MAX_PERCENT);
190
221
  SHAPES = {
191
222
  layout: {
192
223
  header: distinctList(z.enum(STATUSLINE_HEADER_SEGMENTS)).describe("The header segments to draw, in drawing order; a segment left out is hidden, and an empty list hides the header."),
@@ -223,6 +254,20 @@ var init_statusline = __esm({
223
254
  filled: barCharacter.describe("The character that draws the used part of a bar."),
224
255
  empty: barCharacter.describe("The character that draws the unused part of a bar.")
225
256
  },
257
+ usage: {
258
+ value: z.enum(STATUSLINE_USAGE_VALUES).describe("Whether a quota window's figure is the share of it used or the share left; the bar and the colour always follow the share used."),
259
+ bar: z.boolean().describe("Whether each quota window is drawn as a bar, or as text alone."),
260
+ compact: z.boolean().describe("Whether the quota windows are drawn as one short line of text (`5h: 25.0% (1h 30m)`) in place of bars; it takes precedence over usage.bar."),
261
+ resetLabel: z.boolean().describe("Whether a window's reset time is introduced with `resets in` (`resets` for a wall-clock time), as in `(resets in 1h 30m)`, rather than the bare `(1h 30m)`."),
262
+ modelScoped: z.boolean().describe("Whether the per-model weekly windows the client reports (rate_limits.model_scoped) are drawn after the account's own windows; they describe the signed-in account, so a session that selected a provider never shows them."),
263
+ timeFormat: z.enum(STATUSLINE_TIME_FORMATS).describe("How a window's reset is shown: the time left (`1h 30m`), the wall-clock time (`at 14:30`), both, the share of the window already gone (`53% elapsed`), or that share and the wall-clock time. A window whose length is not known shows no share."),
264
+ threshold: thresholdPercent.describe("The whole percentage of use that at least one quota window must reach for the quota row to be drawn at all; 0 always draws it."),
265
+ sevenDayThreshold: thresholdPercent.describe("The whole percentage of use at which the weekly window is drawn beside the five-hour one; below it the weekly window is hidden. A weekly window with no five-hour window beside it is always drawn, and 0 always draws it.")
266
+ },
267
+ clock: {
268
+ hourCycle: z.enum(STATUSLINE_HOUR_CYCLES).describe("The hour cycle of a wall-clock time: `auto` follows the system locale, `h23` and `h24` are 24-hour time (`h24` writes midnight as 24:00), and `h12` and `h11` are 12-hour time with am or pm (`h11` writes the hour after midnight as 0)."),
269
+ seconds: z.boolean().describe("Whether a wall-clock time includes seconds (`14:30:07`).")
270
+ },
226
271
  colours: {
227
272
  context: colourValue.describe("The colour of the context bar and its figures below the warning threshold."),
228
273
  usage: colourValue.describe("The colour of the quota bars and their figures below the warning threshold."),
@@ -265,6 +310,8 @@ var init_statusline = __esm({
265
310
  bands: z.strictObject(SHAPES.bands).partial().optional(),
266
311
  labels: z.strictObject(SHAPES.labels).partial().optional(),
267
312
  bar: z.strictObject(SHAPES.bar).partial().optional(),
313
+ usage: z.strictObject(SHAPES.usage).partial().optional(),
314
+ clock: z.strictObject(SHAPES.clock).partial().optional(),
268
315
  colours: z.strictObject(SHAPES.colours).partial().optional(),
269
316
  project: z.strictObject(SHAPES.project).partial().optional(),
270
317
  git: z.strictObject(SHAPES.git).partial().optional(),
@@ -280,6 +327,8 @@ var init_statusline = __esm({
280
327
  bands: z.strictObject(SHAPES.bands),
281
328
  labels: z.strictObject(SHAPES.labels),
282
329
  bar: z.strictObject(SHAPES.bar),
330
+ usage: z.strictObject(SHAPES.usage),
331
+ clock: z.strictObject(SHAPES.clock),
283
332
  colours: z.strictObject(SHAPES.colours),
284
333
  project: z.strictObject(SHAPES.project),
285
334
  git: z.strictObject(SHAPES.git),
@@ -295,6 +344,8 @@ var init_statusline = __esm({
295
344
  bands: { warnAt: 70, criticalAt: 90 },
296
345
  labels: { fiveHour: "5h", sevenDay: "Wk" },
297
346
  bar: { width: 10, filled: "\u2588", empty: "\u2591" },
347
+ usage: { value: "used", bar: true, compact: false, resetLabel: false, modelScoped: false, timeFormat: "relative", threshold: 0, sevenDayThreshold: 0 },
348
+ clock: { hourCycle: "auto", seconds: false },
298
349
  colours: {
299
350
  context: "green",
300
351
  usage: "green",
@@ -3805,9 +3856,9 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3805
3856
  const watchdog = ports.startWatchdog?.();
3806
3857
  let idleSince;
3807
3858
  let ticks = 0;
3808
- const supersedingOwner = () => {
3859
+ const supersedingOwner = async () => {
3809
3860
  const owner = readFrontDoorState(fs14, paths.frontdoorStateFile)?.supervisorPid;
3810
- return owner !== void 0 && owner !== ports.ownPid && ports.isRunning(owner) ? owner : void 0;
3861
+ return owner !== void 0 && owner !== ports.ownPid && (await ports.runningAmong([owner])).has(owner) ? owner : void 0;
3811
3862
  };
3812
3863
  let supersededBy;
3813
3864
  let served;
@@ -3832,7 +3883,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3832
3883
  watchdog?.stop();
3833
3884
  ports.stopBackgroundWork?.();
3834
3885
  const drained = [direct.drain(), connect3.drain(), http3.drain()];
3835
- if (supersedingOwner() === void 0) {
3886
+ if (await supersedingOwner() === void 0) {
3836
3887
  writeFrontDoorState(fs14, paths.frontdoorStateFile, sticky());
3837
3888
  }
3838
3889
  markFrontDoorDraining(fs14, paths.frontdoorDrainingDir, ports.ownPid);
@@ -3853,7 +3904,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3853
3904
  };
3854
3905
  const shutdown = async () => {
3855
3906
  await closeServing();
3856
- if (supersedingOwner() === void 0) {
3907
+ if (await supersedingOwner() === void 0) {
3857
3908
  writeFrontDoorState(fs14, paths.frontdoorStateFile, sticky());
3858
3909
  }
3859
3910
  return 0;
@@ -3869,13 +3920,14 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
3869
3920
  }
3870
3921
  ticks += 1;
3871
3922
  const listed = listFrontDoorSessions(fs14, paths.frontdoorSessionsDir);
3872
- const pruned = pruneDeadFrontDoorSessions(fs14, paths.frontdoorSessionsDir, ports.isRunning);
3923
+ const running = await ports.runningAmong(listed.map((session) => session.pid));
3924
+ const pruned = pruneDeadFrontDoorSessions(fs14, paths.frontdoorSessionsDir, (pid) => running.has(pid));
3873
3925
  ports.observeLaunchRegistry(listed, pruned);
3874
3926
  ports.checkQuotaExpiry();
3875
3927
  const prunedPids = new Set(pruned);
3876
3928
  const live = listed.filter((session) => !prunedPids.has(session.pid));
3877
3929
  if (supersededBy === void 0) {
3878
- supersededBy = supersedingOwner();
3930
+ supersededBy = await supersedingOwner();
3879
3931
  if (supersededBy !== void 0) {
3880
3932
  ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: supervisor ${String(supersededBy)} now owns the front door; taking no new sessions and exiting once the sessions this door serves have ended`);
3881
3933
  ports.doorHealth.superseded(supersededBy);
@@ -4374,7 +4426,7 @@ var init_package = __esm({
4374
4426
  "package.json"() {
4375
4427
  package_default = {
4376
4428
  name: "claude-use",
4377
- version: "11.54.0",
4429
+ version: "11.55.0",
4378
4430
  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.",
4379
4431
  license: "MIT",
4380
4432
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -4432,6 +4484,7 @@ var init_package = __esm({
4432
4484
  commander: "^15.0.0",
4433
4485
  cosmiconfig: "^10.0.1",
4434
4486
  picomatch: "^4.0.7",
4487
+ "string-width": "8.3.0",
4435
4488
  trilean: "1.7.1",
4436
4489
  undici: "7.29.1",
4437
4490
  zod: "^4.6.1"
@@ -5608,7 +5661,7 @@ var init_logStyle = __esm({
5608
5661
  });
5609
5662
 
5610
5663
  // src/realPorts.ts
5611
- import { spawn as spawn2, spawnSync } from "node:child_process";
5664
+ import { execFile, spawn as spawn2, spawnSync } from "node:child_process";
5612
5665
  import { createHash as createHash5 } from "node:crypto";
5613
5666
  import fs3 from "node:fs";
5614
5667
  import os3 from "node:os";
@@ -5633,6 +5686,7 @@ function realIsProcessAlive(pid) {
5633
5686
  }
5634
5687
  }
5635
5688
  function realIsProcessZombie(pid) {
5689
+ refuseToBlockInRole("a synchronous process-table read");
5636
5690
  const result = spawnSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
5637
5691
  return result.status === 0 && result.stdout.trim().startsWith("Z");
5638
5692
  }
@@ -5640,14 +5694,17 @@ function realIsProcessRunning(pid) {
5640
5694
  return realIsProcessAlive(pid) && !realIsProcessZombie(pid);
5641
5695
  }
5642
5696
  function realIsFrontDoorSupervisor(pid) {
5697
+ refuseToBlockInRole("a synchronous process-table read");
5643
5698
  const result = spawnSync("ps", ["-o", "pid=,command=", "-p", String(pid)], { encoding: "utf8" });
5644
5699
  return result.status === 0 && parseFrontDoorSupervisorPids(result.stdout).includes(pid);
5645
5700
  }
5646
5701
  function realProcessCommandLine(pid) {
5702
+ refuseToBlockInRole("a synchronous process-table read");
5647
5703
  const result = spawnSync("ps", ["-o", "command=", "-p", String(pid)], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
5648
5704
  return result.status === 0 ? result.stdout.trim() : void 0;
5649
5705
  }
5650
5706
  function realFrontDoorLiveness(pids) {
5707
+ refuseToBlockInRole("a synchronous process-table read");
5651
5708
  return frontDoorLivenessOf(pids, (asked) => {
5652
5709
  const result = spawnSync("ps", ["-o", "pid=,stat=,command=", "-p", asked.map(String).join(",")], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
5653
5710
  return { status: result.status, stdout: result.stdout, stderr: result.stderr, ...result.error === void 0 ? {} : { error: result.error } };
@@ -11958,7 +12015,7 @@ var init_identityClaude = __esm({
11958
12015
 
11959
12016
  // src/usage/providerQuota.ts
11960
12017
  import { z as z34 } from "zod";
11961
- var MS_PER_SECOND9, SECONDS_PER_HOUR3, HOURS_PER_DAY6, DAYS_PER_MONTH, ZAI_HOST, ZAI_QUOTA_URL, ZAI_UNIT_HOURS, ZAI_UNIT_MONTHS, ZAI_UNIT_MS, ZaiLimitSchema, ZaiResponseSchema, OPENROUTER_HOST, OPENROUTER_KEY_URL, OPENROUTER_CREDITS_URL, MS_PER_DAY3, OpenRouterKeySchema, OpenRouterCreditsSchema, ANTIGRAVITY_HOST, ANTIGRAVITY_QUOTA_URL, ANTIGRAVITY_USER_URL, FIVE_HOURS2, DAYS_PER_WEEK2, FIVE_HOURS_MS, WEEK_MS, AntigravityBucketSchema, AntigravitySummarySchema, GoogleTokenInfoSchema, AntigravityUserSchema;
12018
+ var MS_PER_SECOND9, SECONDS_PER_HOUR3, HOURS_PER_DAY6, DAYS_PER_MONTH, ZAI_HOST, ZAI_QUOTA_URL, ZAI_UNIT_HOURS, ZAI_UNIT_MONTHS, ZAI_UNIT_MS, ZaiLimitSchema, ZaiResponseSchema, OPENROUTER_HOST, OPENROUTER_KEY_URL, OPENROUTER_CREDITS_URL, MS_PER_DAY2, OpenRouterKeySchema, OpenRouterCreditsSchema, ANTIGRAVITY_HOST, ANTIGRAVITY_QUOTA_URL, ANTIGRAVITY_USER_URL, FIVE_HOURS2, DAYS_PER_WEEK2, FIVE_HOURS_MS, WEEK_MS, AntigravityBucketSchema, AntigravitySummarySchema, GoogleTokenInfoSchema, AntigravityUserSchema;
11962
12019
  var init_providerQuota = __esm({
11963
12020
  "src/usage/providerQuota.ts"() {
11964
12021
  "use strict";
@@ -11994,7 +12051,7 @@ var init_providerQuota = __esm({
11994
12051
  OPENROUTER_HOST = "openrouter.ai";
11995
12052
  OPENROUTER_KEY_URL = `https://${OPENROUTER_HOST}/api/v1/key`;
11996
12053
  OPENROUTER_CREDITS_URL = `https://${OPENROUTER_HOST}/api/v1/credits`;
11997
- MS_PER_DAY3 = HOURS_PER_DAY6 * SECONDS_PER_HOUR3 * MS_PER_SECOND9;
12054
+ MS_PER_DAY2 = HOURS_PER_DAY6 * SECONDS_PER_HOUR3 * MS_PER_SECOND9;
11998
12055
  OpenRouterKeySchema = z34.object({
11999
12056
  data: z34.object({
12000
12057
  limit: z34.number().nonnegative().nullable(),
@@ -18214,13 +18271,8 @@ function createDoorHealthPublisher(hub, now, pid) {
18214
18271
  }
18215
18272
 
18216
18273
  // src/frontdoor/quotaExpiringEvents.ts
18274
+ init_pick();
18217
18275
  init_schema2();
18218
- var MS_PER_HOUR5 = 36e5;
18219
- var MS_PER_DAY2 = 864e5;
18220
- var FIVE_HOUR_WINDOW_HOURS = 5;
18221
- var SEVEN_DAY_WINDOW_DAYS = 7;
18222
- var FIVE_HOUR_WINDOW_MS2 = FIVE_HOUR_WINDOW_HOURS * MS_PER_HOUR5;
18223
- var SEVEN_DAY_WINDOW_MS2 = SEVEN_DAY_WINDOW_DAYS * MS_PER_DAY2;
18224
18276
  var FINAL_SPAN_DENOMINATOR = 30;
18225
18277
  var QUOTA_EXPIRING_WINDOW_FRACTION = 1 / FINAL_SPAN_DENOMINATOR;
18226
18278
  function createQuotaExpiringPublisher(hub, deps) {
@@ -18240,11 +18292,11 @@ function createQuotaExpiringPublisher(hub, deps) {
18240
18292
  const unified = snapshot.providers[provider]?.rateLimit?.unified;
18241
18293
  const fiveHour = unified?.fiveHour;
18242
18294
  if (fiveHour?.utilization !== void 0 && fiveHour.resetsAt !== void 0) {
18243
- views.push({ provider, view: { window: "fiveHour", spanMs: FIVE_HOUR_WINDOW_MS2, utilization: fiveHour.utilization, resetsAtMs: Date.parse(fiveHour.resetsAt) } });
18295
+ views.push({ provider, view: { window: "fiveHour", spanMs: FIVE_HOUR_WINDOW_MS, utilization: fiveHour.utilization, resetsAtMs: Date.parse(fiveHour.resetsAt) } });
18244
18296
  }
18245
18297
  const sevenDay = unified?.sevenDay;
18246
18298
  if (sevenDay?.utilization !== void 0 && sevenDay.resetsAt !== void 0) {
18247
- views.push({ provider, view: { window: "sevenDay", spanMs: SEVEN_DAY_WINDOW_MS2, utilization: sevenDay.utilization, resetsAtMs: Date.parse(sevenDay.resetsAt) } });
18299
+ views.push({ provider, view: { window: "sevenDay", spanMs: SEVEN_DAY_WINDOW_MS, utilization: sevenDay.utilization, resetsAtMs: Date.parse(sevenDay.resetsAt) } });
18248
18300
  }
18249
18301
  }
18250
18302
  return views;