@narumitw/pi-usage 0.60.1 → 0.60.3

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.ts CHANGED
@@ -1254,6 +1254,26 @@ function normalizeWindow(row, fields) {
1254
1254
  }
1255
1255
  const total = nonnegativeInteger(row[fields.totalField], fields.totalField);
1256
1256
  const count = nonnegativeInteger(row[fields.countField], fields.countField);
1257
+ if (total === 0) {
1258
+ if (count !== 0) {
1259
+ throw new Error(`MiniMax Token Plan ${fields.label} counts were inconsistent.`);
1260
+ }
1261
+ if (percent === void 0) {
1262
+ throw new Error(`MiniMax Token Plan ${fields.label} returned no quota and no percent.`);
1263
+ }
1264
+ return {
1265
+ id: fields.id,
1266
+ label: fields.label,
1267
+ groupId: fields.groupId,
1268
+ groupLabel: fields.groupLabel,
1269
+ remaining: percent,
1270
+ used: 100 - percent,
1271
+ limit: 0,
1272
+ unit: "percent",
1273
+ windowMinutes,
1274
+ resetsAt
1275
+ };
1276
+ }
1257
1277
  const resolved = resolveQuotaCounts(count, total, percent);
1258
1278
  if (!resolved) throw new Error(`MiniMax Token Plan ${fields.label} counts were inconsistent.`);
1259
1279
  return {
@@ -3418,7 +3438,7 @@ function formatMiniMaxReport(lines, report2) {
3418
3438
  if (bucket.groupId !== previousGroup) lines.push(`${bucket.groupLabel ?? "Token Plan"}:`);
3419
3439
  previousGroup = bucket.groupId;
3420
3440
  const reset = bucket.resetsAt ? ` (resets ${formatReset(bucket.resetsAt)})` : "";
3421
- const value = bucket.period === "unlimited" ? "unlimited" : bucket.limit && bucket.remaining !== void 0 ? `${bucket.remaining} of ${bucket.limit} left \xB7 ${percentRemaining(bucket)}%${reset}` : "unavailable";
3441
+ const value = bucket.period === "unlimited" ? "unlimited" : bucket.unit === "percent" && bucket.remaining !== void 0 ? `${bucket.remaining}% remaining${reset}` : bucket.limit !== void 0 && bucket.remaining !== void 0 ? `${bucket.remaining} of ${bucket.limit} left \xB7 ${percentRemaining(bucket)}%${reset}` : "unavailable";
3422
3442
  lines.push(`${`${bucket.label}:`.padEnd(VALUE_COLUMN)}${value}`);
3423
3443
  }
3424
3444
  }
@@ -3439,7 +3459,11 @@ function formatMiniMaxStatusline(report2, model) {
3439
3459
  parts.push(`unlimited ${window}`);
3440
3460
  continue;
3441
3461
  }
3442
- if (!bucket.limit || bucket.remaining === void 0) continue;
3462
+ if (bucket.unit === "percent" && bucket.remaining !== void 0) {
3463
+ parts.push(`${bucket.remaining}% ${window}`);
3464
+ continue;
3465
+ }
3466
+ if (bucket.limit === void 0 || bucket.remaining === void 0) continue;
3443
3467
  parts.push(`${percentRemaining(bucket)}% ${window}`);
3444
3468
  }
3445
3469
  return parts.length > 1 ? parts.join(" ") : void 0;
@@ -3451,22 +3475,26 @@ function selectMiniMaxGroup(report2, model) {
3451
3475
  )
3452
3476
  ];
3453
3477
  if (groups.length <= 1) return groups[0];
3454
- if (model?.provider !== report2.providerId) return void 0;
3455
- const modelKeys = [model.id, model.name].map(normalizeMiniMaxModelKey).filter((key) => key !== void 0);
3456
- const candidates = groups.map((group) => {
3457
- const bucket = report2.buckets.find((candidate) => candidate.groupId === group);
3458
- const patterns = [bucket?.groupLabel, ...bucket?.modelKeys ?? [], group].map(normalizeMiniMaxModelKey).filter((key) => key !== void 0);
3459
- return { group, patterns };
3460
- });
3461
- const exact = candidates.find(
3462
- ({ patterns }) => patterns.some((pattern) => !pattern.includes("*") && modelKeys.includes(pattern))
3463
- );
3464
- if (exact) return exact.group;
3465
- return candidates.find(
3466
- ({ patterns }) => patterns.some(
3467
- (pattern) => pattern.includes("*") && modelKeys.some((key) => wildcardKeyMatches(pattern, key))
3468
- )
3469
- )?.group;
3478
+ if (model && model.provider !== report2.providerId) return void 0;
3479
+ if (model) {
3480
+ const modelKeys = [model.id, model.name].map(normalizeMiniMaxModelKey).filter((key) => key !== void 0);
3481
+ const candidates = groups.map((group) => {
3482
+ const bucket = report2.buckets.find((candidate) => candidate.groupId === group);
3483
+ const patterns = [bucket?.groupLabel, ...bucket?.modelKeys ?? [], group].map(normalizeMiniMaxModelKey).filter((key) => key !== void 0);
3484
+ return { group, patterns };
3485
+ });
3486
+ const exact = candidates.find(
3487
+ ({ patterns }) => patterns.some((pattern) => !pattern.includes("*") && modelKeys.includes(pattern))
3488
+ );
3489
+ if (exact) return exact.group;
3490
+ const wildcard = candidates.find(
3491
+ ({ patterns }) => patterns.some(
3492
+ (pattern) => pattern.includes("*") && modelKeys.some((key) => wildcardKeyMatches(pattern, key))
3493
+ )
3494
+ );
3495
+ if (wildcard) return wildcard.group;
3496
+ }
3497
+ return groups.find((group) => group === "general");
3470
3498
  }
3471
3499
  function normalizeMiniMaxModelKey(value) {
3472
3500
  const key = value?.toLowerCase().replace(/[^a-z0-9*]+/gu, "");
@@ -4177,6 +4205,7 @@ function isTimeoutError(error) {
4177
4205
  }
4178
4206
 
4179
4207
  // src/usage-settings-ui.ts
4208
+ import { stripVTControlCharacters } from "node:util";
4180
4209
  import {
4181
4210
  getSettingsListTheme
4182
4211
  } from "@earendil-works/pi-coding-agent";
@@ -4189,11 +4218,25 @@ import {
4189
4218
  } from "@earendil-works/pi-tui";
4190
4219
  var OFF = "Off";
4191
4220
  var ON = "On";
4221
+ function fitSettingsRows(lines, maxRows) {
4222
+ if (lines.length <= maxRows) return lines;
4223
+ const content = lines.filter((line) => stripVTControlCharacters(line).trim().length > 0);
4224
+ if (content.length <= maxRows) return content;
4225
+ const selectedIndex = content.findIndex(
4226
+ (line) => /^[→›]\s/u.test(stripVTControlCharacters(line))
4227
+ );
4228
+ if (selectedIndex < 0) return content.slice(0, maxRows);
4229
+ return content.map((line, index) => ({ index, line })).sort(
4230
+ (left, right) => Math.abs(left.index - selectedIndex) - Math.abs(right.index - selectedIndex) || left.index - right.index
4231
+ ).slice(0, maxRows).sort((left, right) => left.index - right.index).map(({ line }) => line);
4232
+ }
4192
4233
  async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent, onApplied) {
4193
4234
  if (ctx.mode !== "tui") {
4194
4235
  if (ctx.hasUI) ctx.ui.notify(`Edit settings manually: ${settingsRuntime.get().path}`, "info");
4195
4236
  return false;
4196
4237
  }
4238
+ const { HorizontalRule } = await import("@narumitw/pi-tui-kit");
4239
+ if (parentSignal.aborted || !isCurrent()) return false;
4197
4240
  return await ctx.ui.custom((tui, theme, _keybindings, done) => {
4198
4241
  const localController = new AbortController();
4199
4242
  const signal = AbortSignal.any([parentSignal, localController.signal]);
@@ -4218,6 +4261,7 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
4218
4261
  }
4219
4262
  ];
4220
4263
  const container = new Container();
4264
+ const rule = new HorizontalRule({ ruleStyle: (text) => theme.fg("border", text) });
4221
4265
  container.addChild(new Text(theme.fg("accent", theme.bold("pi-usage Settings")), 1, 1));
4222
4266
  let settingsList;
4223
4267
  const cancel = () => {
@@ -4268,7 +4312,16 @@ async function showUsageSettings(ctx, settingsRuntime, parentSignal, isCurrent,
4268
4312
  container.addChild(settingsList);
4269
4313
  parentSignal.addEventListener("abort", cancel, { once: true });
4270
4314
  return {
4271
- render: (width) => container.render(width),
4315
+ render(width) {
4316
+ const content = container.render(width);
4317
+ const terminalRows = Number.isFinite(tui.terminal?.rows) ? Math.floor(tui.terminal.rows) : 24;
4318
+ const availableRows = Math.max(1, terminalRows - 3);
4319
+ if (content.length + 2 > availableRows) {
4320
+ return fitSettingsRows(content, availableRows);
4321
+ }
4322
+ const [ruleLine = ""] = rule.render(width);
4323
+ return [ruleLine, ...content, ruleLine];
4324
+ },
4272
4325
  invalidate: () => container.invalidate(),
4273
4326
  handleInput(data) {
4274
4327
  if (closing) return;
@@ -4370,10 +4423,8 @@ function usageExtension(pi, dependencies = {}) {
4370
4423
  return;
4371
4424
  }
4372
4425
  if (outcome.state.status !== "ready") {
4373
- if (safeSetStatus(
4374
- ctx,
4375
- outcome.state.status === "auth-unavailable" ? "auth unavailable" : outcome.state.status === "selection-required" ? "selection required" : "usage error"
4376
- )) {
4426
+ const chip = outcome.state.status === "auth-unavailable" ? "auth unavailable" : outcome.state.status === "selection-required" ? "selection required" : `usage err: ${outcome.state.message.slice(0, 50)}`;
4427
+ if (safeSetStatus(ctx, chip)) {
4377
4428
  if (shouldSchedule && sessionActive) scheduleStatusRefresh(ctx, model);
4378
4429
  }
4379
4430
  return;
@@ -4737,7 +4788,9 @@ function usageExtension(pi, dependencies = {}) {
4737
4788
  const startStatusRefresh = (ctx, model, force) => {
4738
4789
  void refreshCurrentStatus(ctx, model, force).catch((error) => {
4739
4790
  if (isStaleExtensionContextError(error) || isAbortError3(error)) return;
4740
- safeSetStatus(ctx, "usage error");
4791
+ const message = errorMessage(error);
4792
+ console.error("[pi-usage] refresh failed:", message);
4793
+ safeSetStatus(ctx, `usage err: ${message.slice(0, 50)}`);
4741
4794
  });
4742
4795
  };
4743
4796
  const runMenuOperation = async (ctx, label, parentSignal, operation, cancellable = true) => {