quotacap 0.0.31 → 0.0.33

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.
@@ -22,7 +22,7 @@ function parseGroup(g, provider, rawJsonString, now) {
22
22
  throw new Error("agy: bad weekly reset_time");
23
23
  const resetsAt = resetDate.toISOString();
24
24
  const periodStart = new Date(resetDate.getTime() - 7 * 86400000).toISOString();
25
- const fiveHour = g.buckets.find((b) => b.window === "5h");
25
+ const fiveHour = g.buckets.find((b) => b.window === "5h" || b.id?.includes("5h") || /5[- ]?h|five[- ]?hour/i.test(b.name ?? ""));
26
26
  const sessionPct = fiveHour
27
27
  ? usedPctFromRemaining(fiveHour.remaining_fraction) ?? undefined
28
28
  : undefined;
@@ -1,8 +1,9 @@
1
1
  import { trackedExecFile } from "../runtime/spawn.js";
2
2
  import { parseResetText } from "./parse.js";
3
3
  export function parseClaudeUsage(result, now = new Date()) {
4
- const sessionMatch = result.match(/Current session:\s+(\d+)% used[^·]*·\s*resets\s+([^\n(]+?)\s*\(/);
5
- const weeklyMatch = result.match(/Current week \(all models\):\s+(\d+)% used[^·]*·\s*resets\s+([^\n(]+?)\s*\(/);
4
+ const sessionMatch = result.match(/Current session:\s+(\d+)%\s+used/i);
5
+ const weeklyMatch = result.match(/Current week \(all models\):\s+(\d+)%\s+used/i) ??
6
+ result.match(/Current week[^\d%]*(\d+)%\s+used/i);
6
7
  const usedPct = weeklyMatch ? parseInt(weeklyMatch[1], 10) : 0;
7
8
  const sessionPct = sessionMatch ? parseInt(sessionMatch[1], 10) : undefined;
8
9
  let resetsAt = parseResetText(result, now);
@@ -10,22 +10,33 @@ export function parseKimiTui(text, now = new Date()) {
10
10
  if (!w)
11
11
  throw new Error("kimi: weekly limit not found in TUI output");
12
12
  const f = cleaned.match(fiveRe);
13
- if (!f)
13
+ let sessionPct = null;
14
+ let fiveRaw = null;
15
+ if (f) {
16
+ sessionPct = parseInt(f[1], 10);
17
+ fiveRaw = f[2].trim();
18
+ }
19
+ else {
20
+ const fallback = cleaned.match(/5h limit[^\d%]*(\d+)%\s+used/i);
21
+ if (fallback)
22
+ sessionPct = parseInt(fallback[1], 10);
23
+ }
24
+ if (sessionPct === null)
14
25
  throw new Error("kimi: 5h limit not found in TUI output");
15
26
  const usedPct = parseInt(w[1], 10);
16
- const sessionPct = parseInt(f[1], 10);
17
27
  if (!Number.isFinite(usedPct) || usedPct < 0 || usedPct > 100)
18
28
  throw new Error("kimi: bad weekly pct");
19
29
  if (!Number.isFinite(sessionPct) || sessionPct < 0 || sessionPct > 100)
20
30
  throw new Error("kimi: bad 5h pct");
21
31
  const weeklyRaw = w[2].trim();
22
- const fiveRaw = f[2].trim();
23
32
  const weeklyIso = parseResetText(`resets ${weeklyRaw}`, now);
24
33
  if (!weeklyIso)
25
34
  throw new Error(`kimi: bad weekly reset "${weeklyRaw}"`);
26
- const fiveIso = parseResetText(`resets ${fiveRaw}`, now);
27
- if (!fiveIso)
28
- throw new Error(`kimi: bad 5h reset "${fiveRaw}"`);
35
+ if (fiveRaw) {
36
+ const fiveIso = parseResetText(`resets ${fiveRaw}`, now);
37
+ if (!fiveIso)
38
+ throw new Error(`kimi: bad 5h reset "${fiveRaw}"`);
39
+ }
29
40
  let plan = "unknown";
30
41
  const paren = cleaned.match(/Weekly limit\s*\(([^)]+)\)/i);
31
42
  if (paren) {
@@ -5,10 +5,18 @@ export function parseManualUsage(provider, text, now = new Date()) {
5
5
  const parsedReset = parseResetText(text, now);
6
6
  const resetsAt = parsedReset ?? new Date(now.getTime() + 3 * 86400000).toISOString();
7
7
  const periodStart = (parsedReset ? new Date(new Date(resetsAt).getTime() - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
8
+ let sessionPct;
9
+ const sm = text.match(/(?:5h|5-hour|session|current)[^\d%\n]*(\d+)%\s*used/i);
10
+ if (sm) {
11
+ const v = parseInt(sm[1], 10);
12
+ if (Number.isFinite(v) && v >= 0 && v <= 100)
13
+ sessionPct = v;
14
+ }
8
15
  return {
9
16
  provider,
10
17
  plan: "unknown",
11
18
  usedPct,
19
+ sessionPct,
12
20
  resetsAt,
13
21
  periodStart,
14
22
  source: "manual",
@@ -3,9 +3,10 @@
3
3
  // string order.
4
4
  export function parseVersion(v) {
5
5
  const s = v.startsWith("v") ? v.slice(1) : v;
6
- if (!/^\d+(\.\d+)*$/.test(s))
6
+ const match = s.match(/^(\d+(?:\.\d+)*)(?:-[0-9a-fA-F]{7,40}(?:-dirty)?)?$/);
7
+ if (!match || !match[1])
7
8
  return null;
8
- return s.split(".").map(Number);
9
+ return match[1].split(".").map(Number);
9
10
  }
10
11
  export function compareVersions(a, b) {
11
12
  const pa = parseVersion(a);
@@ -18,7 +19,21 @@ export function compareVersions(a, b) {
18
19
  if (x !== y)
19
20
  return x < y ? -1 : 1;
20
21
  }
21
- return 0;
22
+ if (a === b)
23
+ return 0;
24
+ const aNorm = a.startsWith("v") ? a.slice(1) : a;
25
+ const bNorm = b.startsWith("v") ? b.slice(1) : b;
26
+ if (aNorm === bNorm)
27
+ return 0;
28
+ const aHasSuffix = aNorm.includes("-");
29
+ const bHasSuffix = bNorm.includes("-");
30
+ if (!aHasSuffix && !bHasSuffix)
31
+ return 0;
32
+ if (aHasSuffix && !bHasSuffix)
33
+ return 1;
34
+ if (!aHasSuffix && bHasSuffix)
35
+ return -1;
36
+ return aNorm < bNorm ? -1 : 1;
22
37
  }
23
38
  if (a === b)
24
39
  return 0;
@@ -254,7 +254,10 @@ function bootoutQuiet(run, uid, print) {
254
254
  run(["bootout", bootoutTarget(uid)]);
255
255
  }
256
256
  catch (e) {
257
- if (/could not find service/i.test(String(e?.message ?? e))) {
257
+ // "No such process" is launchd's other not-running signal: the job record
258
+ // exists but its process is already gone. Same goal state as a missing
259
+ // record, so tolerate it the same way (systemd's stopQuiet already does).
260
+ if (/could not find service|no such process/i.test(String(e?.message ?? e))) {
258
261
  print("service job is not loaded; nothing to stop");
259
262
  return;
260
263
  }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // generated by scripts/build-embed.mjs — do not edit
2
- export const VERSION = "0.0.31";
2
+ export const VERSION = "0.0.33";
@@ -358,7 +358,7 @@ export function ProviderDrawer({ provider, asOf, recommendation, onClose, onRena
358
358
  margin: "0 var(--s4) var(--s3)",
359
359
  fontSize: "var(--t-2)",
360
360
  color: "var(--ink-soft)",
361
- }, children: provider.description })), _jsxs("section", { className: "dsec", "aria-label": "Weekly limit", children: [_jsx("h3", { children: "Weekly limit" }), _jsx("div", { className: "pcard-topline", children: _jsx(Badge, { provider: provider }) }), provider.quota ? (_jsxs(_Fragment, { children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), underline && _jsx("div", { className: "pcard-underline", children: underline })] })) : (_jsx("p", { className: "cell-s", children: "No readings yet." }))] }), model.sessionPct !== undefined && model.sessionPct > 0 && (_jsxs("section", { className: "dsec", "aria-label": shortWindowLabel(provider.id) ?? "Session", children: [_jsx("h3", { children: shortWindowLabel(provider.id) ?? "Session" }), _jsx("div", { className: "pcard-topline", children: _jsxs("span", { className: "used", children: [model.sessionPct, "% used"] }) }), _jsx("div", { className: "track", role: "img", "aria-label": `${model.sessionPct} percent of ${(shortWindowLabel(provider.id) ?? "session").toLowerCase()} used`, children: _jsx("div", { className: "fill", style: {
361
+ }, children: provider.description })), _jsxs("section", { className: "dsec", "aria-label": "Weekly limit", children: [_jsx("h3", { children: "Weekly limit" }), _jsx("div", { className: "pcard-topline", children: _jsx(Badge, { provider: provider }) }), provider.quota ? (_jsxs(_Fragment, { children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), underline && _jsx("div", { className: "pcard-underline", children: underline })] })) : (_jsx("p", { className: "cell-s", children: "No readings yet." }))] }), model.sessionPct !== undefined && model.sessionPct !== null && Number.isFinite(model.sessionPct) && (_jsxs("section", { className: "dsec", "aria-label": shortWindowLabel(provider.id) ?? "5h Limit", children: [_jsx("h3", { children: shortWindowLabel(provider.id) ?? "5h Limit" }), _jsx("div", { className: "pcard-topline", children: _jsxs("span", { className: "used", children: [model.sessionPct, "% used"] }) }), _jsx("div", { className: "track", role: "img", "aria-label": `${model.sessionPct} percent of ${(shortWindowLabel(provider.id) ?? "5h limit").toLowerCase()} used`, children: _jsx("div", { className: "fill", style: {
362
362
  width: `${Math.min(100, Math.max(0, model.sessionPct))}%`,
363
363
  background: "var(--fill-ontrack)",
364
364
  ["--fill-edge"]: "var(--edge-ontrack)",
@@ -10,7 +10,7 @@ export function ProviderRow({ provider, recommended, asOf, onSelect, }) {
10
10
  const plan = displayPlan(quota?.plan);
11
11
  const shortWindow = shortWindowLabel(provider.id);
12
12
  const reset = resetCellLines(provider, asOfMs);
13
- return (_jsxs("div", { "data-testid": `provider-row-${provider.id}`, className: `lrow ${recommended ? "is-recommended is-top-pick" : ""}`, onClick: () => onSelect(provider.id), children: [_jsxs("div", { className: "prov", "data-label": "Provider", children: [_jsx("span", { className: "dot", style: { background: `var(--p-${provider.id.split(":")[0]}, var(--surface-raised))` }, "aria-hidden": "true" }), _jsxs("span", { children: [_jsxs("span", { className: "nm", children: [name, recommended && (_jsx("span", { className: "rec-icon", title: "Recommended for next session", role: "img", "aria-label": "recommended", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z" }) }) }))] }), plan && _jsx("span", { className: "pl", children: plan }), quota?.sessionPct !== undefined && quota.sessionPct !== null && (_jsxs("span", { className: "src", children: [shortWindow ?? "Session", " \u00B7 ", quota.sessionPct, "% used"] }))] })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? _jsx(PaceBar, { provider: provider, asOf: asOf }) : "—" }), _jsxs("div", { "data-label": "Reset", className: "reset-cell", children: [_jsx("span", { className: "cell-b", children: reset.top }), reset.bottom && _jsx("span", { className: "cell-s", children: reset.bottom })] }), _jsxs("div", { "data-label": "Pace", children: [_jsx(Badge, { provider: provider }), lines.length > 0 && (_jsx("div", { style: { marginTop: 2 }, children: lines.map((l) => (_jsxs("div", { className: "cell-s paceline", title: l.tip, children: [_jsx("span", { children: l.label }), _jsx("span", { className: "cell-b", children: l.value })] }, l.label))) }))] }), _jsx("button", { type: "button", className: "rowbtn", onClick: (e) => {
13
+ return (_jsxs("div", { "data-testid": `provider-row-${provider.id}`, className: `lrow ${recommended ? "is-recommended is-top-pick" : ""}`, onClick: () => onSelect(provider.id), children: [_jsxs("div", { className: "prov", "data-label": "Provider", children: [_jsx("span", { className: "dot", style: { background: `var(--p-${provider.id.split(":")[0]}, var(--surface-raised))` }, "aria-hidden": "true" }), _jsxs("span", { children: [_jsxs("span", { className: "nm", children: [name, recommended && (_jsx("span", { className: "rec-icon", title: "Recommended for next session", role: "img", "aria-label": "recommended", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.3", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "m12 3-1.9 5.8a2 2 0 0 1-1.3 1.3L3 12l5.8 1.9a2 2 0 0 1 1.3 1.3L12 21l1.9-5.8a2 2 0 0 1 1.3-1.3L21 12l-5.8-1.9a2 2 0 0 1-1.3-1.3z" }) }) }))] }), plan && _jsx("span", { className: "pl", children: plan }), quota?.sessionPct !== undefined && quota.sessionPct !== null && (_jsxs("span", { className: "src", children: [shortWindow ?? "5h Limit", " \u00B7 ", quota.sessionPct, "% used"] }))] })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? _jsx(PaceBar, { provider: provider, asOf: asOf }) : "—" }), _jsxs("div", { "data-label": "Reset", className: "reset-cell", children: [_jsx("span", { className: "cell-b", children: reset.top }), reset.bottom && _jsx("span", { className: "cell-s", children: reset.bottom })] }), _jsxs("div", { "data-label": "Pace", children: [_jsx(Badge, { provider: provider }), lines.length > 0 && (_jsx("div", { style: { marginTop: 2 }, children: lines.map((l) => (_jsxs("div", { className: "cell-s paceline", title: l.tip, children: [_jsx("span", { children: l.label }), _jsx("span", { className: "cell-b", children: l.value })] }, l.label))) }))] }), _jsx("button", { type: "button", className: "rowbtn", onClick: (e) => {
14
14
  e.stopPropagation();
15
15
  onSelect(provider.id);
16
16
  }, children: "Inspect" }), _jsx("span", { className: "sr-only", children: forecastLine(provider) })] }));
@@ -115,10 +115,9 @@ export declare function providerSubtitle(p: Pick<ProviderView, "displayName" | "
115
115
  /** Plan for display. Vendors that expose no tier report "unknown", which never renders. */
116
116
  export declare function displayPlan(plan: string | null | undefined): string | null;
117
117
  /**
118
- * Short-window display name, mirroring each vendor's own /usage or /status
119
- * wording: codex, kimi, and agy call it a "5h" or "Five Hour" limit, claude
120
- * a "Current session" (a 5-hour rolling window), muse a "Current" window.
121
- * Null when the provider reports no short window (grok, manual).
118
+ * Short-window display name ("5h Limit"). Codex, Kimi, and Agy report a 5h limit;
119
+ * Claude (current session) and Muse (current window) report an equivalent 5-hour
120
+ * rolling window. Returns null when the provider reports no short window (grok, manual).
122
121
  */
123
122
  export declare function shortWindowLabel(id: string): string | null;
124
123
  /** "read Xm ago" from the last good snapshot's server timestamp. */
@@ -51,21 +51,18 @@ export function displayPlan(plan) {
51
51
  return plan;
52
52
  }
53
53
  /**
54
- * Short-window display name, mirroring each vendor's own /usage or /status
55
- * wording: codex, kimi, and agy call it a "5h" or "Five Hour" limit, claude
56
- * a "Current session" (a 5-hour rolling window), muse a "Current" window.
57
- * Null when the provider reports no short window (grok, manual).
54
+ * Short-window display name ("5h Limit"). Codex, Kimi, and Agy report a 5h limit;
55
+ * Claude (current session) and Muse (current window) report an equivalent 5-hour
56
+ * rolling window. Returns null when the provider reports no short window (grok, manual).
58
57
  */
59
58
  export function shortWindowLabel(id) {
60
59
  switch (id.split(":")[0]) {
61
60
  case "codex":
62
61
  case "kimi":
63
62
  case "agy":
64
- return "5-hour limit";
65
63
  case "claude":
66
- return "Current session";
67
64
  case "muse":
68
- return "Current window";
65
+ return "5h Limit";
69
66
  default:
70
67
  return null;
71
68
  }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ // HMR iteration (`npm run dev:web`): the dashboard fetches same-origin /api/*,
6
+ // so forward API traffic to a locally running daemon. Override when the daemon
7
+ // binds somewhere other than the default 8787.
8
+ const devApi = process.env.QUOTACAP_DEV_API || "http://127.0.0.1:8787";
9
+ export default defineConfig({
10
+ plugins: [react()],
11
+ root: path.dirname(fileURLToPath(import.meta.url)),
12
+ build: { outDir: "dist" },
13
+ server: {
14
+ port: 5173,
15
+ proxy: {
16
+ "/api": devApi,
17
+ "/health": devApi,
18
+ },
19
+ },
20
+ });