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.
- package/dist/adapters/agy.js +1 -1
- package/dist/adapters/claude.js +3 -2
- package/dist/adapters/kimi.js +17 -6
- package/dist/adapters/manual.js +8 -0
- package/dist/runtime/versions.js +18 -3
- package/dist/service/macos.js +4 -1
- package/dist/version.js +1 -1
- package/dist/web/src/components/ProviderDrawer.js +1 -1
- package/dist/web/src/components/ProviderRow.js +1 -1
- package/dist/web/src/state.d.ts +3 -4
- package/dist/web/src/state.js +4 -7
- package/dist/web/vite.config.d.ts +2 -0
- package/dist/web/vite.config.js +20 -0
- package/dist/webAssets.js +1 -1
- package/dist/webHtml.js +1 -1
- package/package.json +7 -2
- package/web/dist/assets/{index-hA7pT5M5.js → index-4-7ERxB2.js} +1 -1
- package/web/dist/index.html +1 -1
package/dist/adapters/agy.js
CHANGED
|
@@ -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;
|
package/dist/adapters/claude.js
CHANGED
|
@@ -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+)
|
|
5
|
-
const weeklyMatch = result.match(/Current week \(all models\):\s+(\d+)
|
|
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);
|
package/dist/adapters/kimi.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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) {
|
package/dist/adapters/manual.js
CHANGED
|
@@ -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",
|
package/dist/runtime/versions.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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;
|
package/dist/service/macos.js
CHANGED
|
@@ -254,7 +254,10 @@ function bootoutQuiet(run, uid, print) {
|
|
|
254
254
|
run(["bootout", bootoutTarget(uid)]);
|
|
255
255
|
}
|
|
256
256
|
catch (e) {
|
|
257
|
-
|
|
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.
|
|
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
|
|
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 ?? "
|
|
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) })] }));
|
package/dist/web/src/state.d.ts
CHANGED
|
@@ -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,
|
|
119
|
-
*
|
|
120
|
-
*
|
|
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. */
|
package/dist/web/src/state.js
CHANGED
|
@@ -51,21 +51,18 @@ export function displayPlan(plan) {
|
|
|
51
51
|
return plan;
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
* Short-window display name,
|
|
55
|
-
*
|
|
56
|
-
*
|
|
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 "
|
|
65
|
+
return "5h Limit";
|
|
69
66
|
default:
|
|
70
67
|
return null;
|
|
71
68
|
}
|
|
@@ -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
|
+
});
|