quotacap 0.0.41 → 0.0.42

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.
Files changed (33) hide show
  1. package/dist/adapters/claude.js +1 -1
  2. package/dist/adapters/index.d.ts +2 -2
  3. package/dist/adapters/opencode-go.d.ts +1 -1
  4. package/dist/adapters/opencode-go.js +8 -1
  5. package/dist/cli/providers.js +2 -1
  6. package/dist/config.d.ts +5 -23
  7. package/dist/format/rows.d.ts +1 -1
  8. package/dist/format/rows.js +9 -6
  9. package/dist/mcp/server.d.ts +2 -2
  10. package/dist/version.js +1 -1
  11. package/dist/web/src/App.js +1 -1
  12. package/dist/web/src/components/AdviceDrawer.js +1 -1
  13. package/dist/web/src/components/OpencodeGoBanner.d.ts +1 -1
  14. package/dist/web/src/components/OpencodeGoBanner.js +9 -2
  15. package/dist/web/src/components/PaceBar.d.ts +9 -4
  16. package/dist/web/src/components/PaceBar.js +26 -7
  17. package/dist/web/src/components/ProviderCard.js +2 -3
  18. package/dist/web/src/components/ProviderDrawer.js +3 -3
  19. package/dist/web/src/components/ProviderIcon.js +1 -1
  20. package/dist/web/src/components/ProviderRow.d.ts +1 -1
  21. package/dist/web/src/components/ProviderRow.js +2 -4
  22. package/dist/web/src/components/Recommendation.js +1 -1
  23. package/dist/web/src/components/ResetRail.js +1 -1
  24. package/dist/web/src/components/WindowsRow.d.ts +1 -1
  25. package/dist/web/src/components/WindowsRow.js +4 -4
  26. package/dist/web/src/components/settings/SettingsDrawer.js +1 -1
  27. package/dist/webAssets.js +1 -1
  28. package/dist/webHtml.js +1 -1
  29. package/package.json +5 -5
  30. package/web/dist/assets/{index-Dk6l5h27.css → index-C6ltEeGL.css} +1 -1
  31. package/web/dist/assets/index-DRDDTtxF.js +13 -0
  32. package/web/dist/index.html +2 -2
  33. package/web/dist/assets/index-BUK8xUHm.js +0 -12
@@ -13,7 +13,7 @@ export function parseClaudeUsage(result, now = new Date()) {
13
13
  const resets = new Date(resetsAt).getTime();
14
14
  const periodStart = (parsedReset ? new Date(resets - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
15
15
  return {
16
- provider: "claude", plan: "max", weeklyPct: usedPct, fiveHourPct: sessionPct,
16
+ provider: "claude", plan: "unknown", weeklyPct: usedPct, fiveHourPct: sessionPct,
17
17
  resetsAt, periodStart, source: "cli", fetchedAt: now.toISOString(), raw: result,
18
18
  };
19
19
  }
@@ -9,13 +9,13 @@ export declare function pollAll(enabled: string[], opts?: PollAllOptions): Promi
9
9
  value: unknown;
10
10
  reason?: undefined;
11
11
  } | {
12
+ value?: undefined;
12
13
  provider: string;
13
14
  status: "skipped";
14
15
  reason: any;
15
- value?: undefined;
16
16
  } | {
17
+ value?: undefined;
17
18
  provider: string;
18
19
  status: "rejected";
19
20
  reason: any;
20
- value?: undefined;
21
21
  })[]>;
@@ -1,5 +1,5 @@
1
1
  import type { ParsedQuota } from "./types.js";
2
- export declare const OPENCODE_GO_CONSENT_NOTICE = "QuotaCap will read the API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` \u2192 `opencode` fallback). It is read in-memory, read-only once per poll (every 15 minutes by default). It is sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`. It is never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`). Revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling. If you prefer no file access, set `OPENCODE_API_KEY` instead.";
2
+ export declare const OPENCODE_GO_CONSENT_NOTICE: string;
3
3
  export declare function openCodeGoAuthPath(): string;
4
4
  /** Detection is existence only — no content read before enablement. */
5
5
  export declare function opencodeGoDetected(): boolean;
@@ -8,7 +8,14 @@ import path from "node:path";
8
8
  import { adapterSignal } from "../runtime/spawn.js";
9
9
  const USAGE_URL = "https://opencode.ai/zen/go/v1/usage";
10
10
  const POLL_TIMEOUT_MS = 8000;
11
- export const OPENCODE_GO_CONSENT_NOTICE = "QuotaCap will read the API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` → `opencode` fallback). It is read in-memory, read-only once per poll (every 15 minutes by default). It is sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`. It is never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`). Revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling. If you prefer no file access, set `OPENCODE_API_KEY` instead.";
11
+ export const OPENCODE_GO_CONSENT_NOTICE = [
12
+ "• What is read: API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` → `opencode` fallback).",
13
+ "• Read frequency: in-memory, read-only once per poll (every 15 minutes by default).",
14
+ "• Request destination: sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`.",
15
+ "• Never-list: never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`).",
16
+ "• Revocation instructions: revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling.",
17
+ "• Optional environment override: if you prefer no file access, set `OPENCODE_API_KEY` instead.",
18
+ ].join("\n");
12
19
  export function openCodeGoAuthPath() {
13
20
  return path.join(os.homedir(), ".local", "share", "opencode", "auth.json");
14
21
  }
@@ -248,7 +248,8 @@ export function registerProvidersCommand(program, deps = {}) {
248
248
  return;
249
249
  }
250
250
  }
251
- console.log(`enabled ${id} — restart quotacap daemon to apply`);
251
+ console.log(`enabled ${id}`);
252
+ console.log("Next: run quotacap service restart to start polling");
252
253
  });
253
254
  providers
254
255
  .command("disable <id>")
package/dist/config.d.ts CHANGED
@@ -1,35 +1,17 @@
1
1
  import { z } from "zod";
2
2
  export declare function getConfigPath(p?: string): string;
3
3
  export declare function getDbPath(p?: string): string;
4
- export declare const ProviderDisplayNameSchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
4
+ export declare const ProviderDisplayNameSchema: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
5
5
  declare const ConfigSchema: z.ZodObject<{
6
6
  port: z.ZodDefault<z.ZodNumber>;
7
7
  pollMinutes: z.ZodDefault<z.ZodNumber>;
8
8
  catalogTtlHours: z.ZodDefault<z.ZodNumber>;
9
- enabledProviders: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
- knownProviders: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
11
- providerNames: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>>;
9
+ enabledProviders: z.ZodDefault<z.ZodArray<z.ZodString>>;
10
+ knownProviders: z.ZodDefault<z.ZodArray<z.ZodString>>;
11
+ providerNames: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
12
12
  opencodeGoConsentAt: z.ZodDefault<z.ZodNullable<z.ZodString>>;
13
13
  experimentalIngest: z.ZodOptional<z.ZodBoolean>;
14
- }, "strip", z.ZodTypeAny, {
15
- port: number;
16
- pollMinutes: number;
17
- catalogTtlHours: number;
18
- enabledProviders: string[];
19
- knownProviders: string[];
20
- providerNames: Record<string, string>;
21
- opencodeGoConsentAt: string | null;
22
- experimentalIngest?: boolean | undefined;
23
- }, {
24
- port?: number | undefined;
25
- pollMinutes?: number | undefined;
26
- catalogTtlHours?: number | undefined;
27
- enabledProviders?: string[] | undefined;
28
- knownProviders?: string[] | undefined;
29
- providerNames?: Record<string, string> | undefined;
30
- opencodeGoConsentAt?: string | null | undefined;
31
- experimentalIngest?: boolean | undefined;
32
- }>;
14
+ }, z.core.$strip>;
33
15
  export type Config = z.infer<typeof ConfigSchema>;
34
16
  /**
35
17
  * Manual ingest (CLI `ingest`, POST /api/ingest) is off unless this returns
@@ -21,7 +21,7 @@ export declare function weeklyPctOf(q: {
21
21
  usedPct?: number;
22
22
  } | null | undefined): number | undefined;
23
23
  export declare function fmtDuration(ms: number): string;
24
- export declare function weekdayOf(iso: string | undefined): string;
24
+ export declare function dayOf(iso: string | undefined): string;
25
25
  export declare function countdownText(ps: ProviderSnapshot, now: Date): string;
26
26
  export declare function elapsedPct(ps: ProviderSnapshot, now: Date): number | null;
27
27
  export declare function usedPctOf(ps: ProviderSnapshot): number | null;
@@ -23,11 +23,14 @@ export function fmtDuration(ms) {
23
23
  return `${Math.floor(ms / HOUR_MS)}h`;
24
24
  return `${Math.max(1, Math.floor(ms / MIN_MS))}m`;
25
25
  }
26
- // Short weekday of an ISO instant in the locked English copy, e.g. "Tue".
27
- // Pinned to en-US: the sentence it feeds ("unused until Tue") is English, so
28
- // a non-English machine must not print a localized weekday inside it.
29
- export function weekdayOf(iso) {
30
- return new Date(iso ?? "").toLocaleDateString("en-US", { weekday: "short" });
26
+ // Short weekday plus calendar date of an ISO instant in the locked English
27
+ // copy, e.g. "Tue Sep 15". Pinned to en-US: the sentence it feeds
28
+ // ("unused until Tue Sep 15") is English, so a non-English machine must not
29
+ // print localized parts inside it. A bare weekday scans as this coming week,
30
+ // which misreads a monthly reset sitting weeks out.
31
+ export function dayOf(iso) {
32
+ const when = new Date(iso ?? "");
33
+ return `${when.toLocaleDateString("en-US", { weekday: "short" })} ${when.toLocaleDateString("en-US", { day: "numeric", month: "short" })}`;
31
34
  }
32
35
  export function countdownText(ps, now) {
33
36
  const resetsMs = msOf(ps.quota?.resetsAt);
@@ -140,7 +143,7 @@ export function forecastText(ps, now) {
140
143
  return "no readings yet";
141
144
  let text;
142
145
  if (adv.bindingWindow === "monthly" && adv.bindingRemaining <= 0) {
143
- text = `unused until ${weekdayOf(ps.quota?.monthlyResetsAt)}`;
146
+ text = `unused until ${dayOf(ps.quota?.monthlyResetsAt)}`;
144
147
  }
145
148
  else if (adv.bindingWindow === "monthly") {
146
149
  text = `${Math.round(adv.bindingRemaining)}% of month left`;
@@ -29,10 +29,10 @@ export declare const tools: ({
29
29
  inputSchema: {
30
30
  type: string;
31
31
  properties: {
32
+ task?: undefined;
32
33
  provider: {
33
34
  type: string;
34
35
  };
35
- task?: undefined;
36
36
  };
37
37
  required?: undefined;
38
38
  };
@@ -42,10 +42,10 @@ export declare const tools: ({
42
42
  inputSchema: {
43
43
  type: string;
44
44
  properties: {
45
+ task?: undefined;
45
46
  provider: {
46
47
  type: string;
47
48
  };
48
- task?: undefined;
49
49
  };
50
50
  required: string[];
51
51
  };
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.41";
2
+ export const VERSION = "0.0.42";
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useState } from "react";
3
3
  import { createRoot } from "react-dom/client";
4
4
  import "./theme.css";
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef } from "react";
3
3
  import { paceBadge, timeLeft } from "./PaceBar.js";
4
4
  import { ProviderIcon, providerTint } from "./ProviderIcon.js";
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import type { ProviderView, RuntimeView } from "../state.js";
3
- export declare const OPENCODE_GO_CONSENT_COPY = "QuotaCap will read the API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` \u2192 `opencode` fallback). It is read in-memory, read-only once per poll (every 15 minutes by default). It is sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`. It is never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`). Revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling. If you prefer no file access, set `OPENCODE_API_KEY` instead.";
3
+ export declare const OPENCODE_GO_CONSENT_COPY: string;
4
4
  export declare function showOpenCodeGoBanner(runtime: Pick<RuntimeView, "detectedProviders"> | undefined, providers: Array<Pick<ProviderView, "id" | "enabled">>, suppressed: boolean): boolean;
5
5
  export declare function OpencodeGoBanner({ onEnable }: {
6
6
  onEnable: () => void;
@@ -1,6 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from "react";
3
- export const OPENCODE_GO_CONSENT_COPY = "QuotaCap will read the API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` → `opencode` fallback). It is read in-memory, read-only once per poll (every 15 minutes by default). It is sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`. It is never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`). Revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling. If you prefer no file access, set `OPENCODE_API_KEY` instead.";
3
+ export const OPENCODE_GO_CONSENT_COPY = [
4
+ "• What is read: API key OpenCode already stored when you ran `opencode auth login -p opencode-go` (`~/.local/share/opencode/auth.json`, `opencode-go` → `opencode` fallback).",
5
+ "• Read frequency: in-memory, read-only once per poll (every 15 minutes by default).",
6
+ "• Request destination: sent only as `Authorization: Bearer` to `https://opencode.ai/zen/go/v1/usage` to fetch `rolling/weekly/monthly` `percent` + `resetsAt`.",
7
+ "• Never-list: never used to make chat or model requests, never written or rotated, never stored in the `quotacap` DB, never returned by the API or MCP, and never logged (errors show `[redacted]`).",
8
+ "• Revocation instructions: revoke at `https://opencode.ai/auth` or `opencode providers logout opencode-go` and run `quotacap providers disable opencode-go` to stop polling.",
9
+ "• Optional environment override: if you prefer no file access, set `OPENCODE_API_KEY` instead.",
10
+ ].join("\n");
4
11
  export function showOpenCodeGoBanner(runtime, providers, suppressed) {
5
12
  const detected = runtime?.detectedProviders?.includes("opencode-go") ?? false;
6
13
  const row = providers.find((p) => p.id === "opencode-go");
@@ -32,5 +39,5 @@ export function ConsentModal({ onConfirm, onCancel, busy, }) {
32
39
  padding: "var(--s4) var(--s5)",
33
40
  maxWidth: 560,
34
41
  margin: "var(--s4)",
35
- }, children: [_jsx("h2", { id: "opencode-go-consent-title", style: { fontSize: "var(--t-4)", margin: "0 0 var(--s3)" }, children: "Enable OpenCode Go?" }), _jsx("p", { style: { fontSize: "var(--t-2)", color: "var(--ink-soft)", margin: "0 0 var(--s4)" }, children: OPENCODE_GO_CONSENT_COPY }), _jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--s2)" }, children: [_jsx("button", { type: "button", className: "btn btn-quiet", onClick: onCancel, disabled: busy, children: "Cancel" }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: onConfirm, disabled: busy, children: busy ? "Enabling…" : "Enable" })] })] }) }));
42
+ }, children: [_jsx("h2", { id: "opencode-go-consent-title", style: { fontSize: "var(--t-4)", margin: "0 0 var(--s3)" }, children: "Enable OpenCode Go?" }), _jsx("p", { style: { fontSize: "var(--t-2)", color: "var(--ink-soft)", margin: "0 0 var(--s4)", whiteSpace: "pre-line" }, children: OPENCODE_GO_CONSENT_COPY }), _jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: "var(--s2)" }, children: [_jsx("button", { type: "button", className: "btn btn-quiet", onClick: onCancel, disabled: busy, children: "Cancel" }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: onConfirm, disabled: busy, children: busy ? "Enabling…" : "Enable" })] })] }) }));
36
43
  }
@@ -63,10 +63,15 @@ export declare function exclusionDetail(reason: ExclusionReason): string | null;
63
63
  export declare function isEstimated(provider: ProviderView): boolean;
64
64
  /** Weekday plus 24h clock, e.g. "Thu 21:00". Null when unparseable. */
65
65
  export declare function resetClock(resetsAt: string, locale?: string): string | null;
66
- /** Short weekday in the locked English copy, e.g. "Tue". Mirrors weekdayOf in
67
- * src/format/rows.ts, including its pinned en-US locale, so the CLI and the
68
- * dashboard word the same sentence identically on any machine. */
69
- export declare function resetWeekday(iso: string | undefined): string;
66
+ /** Weekday, calendar date and 24h clock, e.g. "Thu Oct 22 23:05". Monthly
67
+ * resets sit weeks out, where resetClock's weekday-only reading ("Thu 23:05")
68
+ * scans as this coming Thursday. Null when unparseable. */
69
+ export declare function resetDateClock(resetsAt: string, locale?: string): string | null;
70
+ /** Short weekday plus calendar date in the locked English copy, e.g.
71
+ * "Tue Sep 15". Mirrors dayOf in src/format/rows.ts, including its pinned
72
+ * en-US locale, so the CLI and the dashboard word the same sentence
73
+ * identically on any machine. */
74
+ export declare function resetDay(iso: string | undefined): string;
70
75
  /** "6d 12h" style time left from server timestamps. Null when unparseable. */
71
76
  export declare function timeLeft(resetsAt: string, asOfMs: number): string | null;
72
77
  /**
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ageDuration } from "../state.js";
3
3
  /**
4
4
  * Pace-state badge from the mapping table. Exclusion always wins; a blocked
@@ -112,11 +112,30 @@ export function resetClock(resetsAt, locale) {
112
112
  });
113
113
  return `${day} ${time}`;
114
114
  }
115
- /** Short weekday in the locked English copy, e.g. "Tue". Mirrors weekdayOf in
116
- * src/format/rows.ts, including its pinned en-US locale, so the CLI and the
117
- * dashboard word the same sentence identically on any machine. */
118
- export function resetWeekday(iso) {
119
- return new Date(iso ?? "").toLocaleDateString("en-US", { weekday: "short" });
115
+ /** Weekday, calendar date and 24h clock, e.g. "Thu Oct 22 23:05". Monthly
116
+ * resets sit weeks out, where resetClock's weekday-only reading ("Thu 23:05")
117
+ * scans as this coming Thursday. Null when unparseable. */
118
+ export function resetDateClock(resetsAt, locale) {
119
+ const ms = Date.parse(resetsAt);
120
+ if (!Number.isFinite(ms))
121
+ return null;
122
+ const when = new Date(ms);
123
+ const day = when.toLocaleDateString(locale, { weekday: "short" });
124
+ const date = when.toLocaleDateString(locale, { day: "numeric", month: "short" });
125
+ const time = when.toLocaleTimeString(locale, {
126
+ hour: "2-digit",
127
+ minute: "2-digit",
128
+ hour12: false,
129
+ });
130
+ return `${day} ${date} ${time}`;
131
+ }
132
+ /** Short weekday plus calendar date in the locked English copy, e.g.
133
+ * "Tue Sep 15". Mirrors dayOf in src/format/rows.ts, including its pinned
134
+ * en-US locale, so the CLI and the dashboard word the same sentence
135
+ * identically on any machine. */
136
+ export function resetDay(iso) {
137
+ const when = new Date(iso ?? "");
138
+ return `${when.toLocaleDateString("en-US", { weekday: "short" })} ${when.toLocaleDateString("en-US", { day: "numeric", month: "short" })}`;
120
139
  }
121
140
  /** "6d 12h" style time left from server timestamps. Null when unparseable. */
122
141
  export function timeLeft(resetsAt, asOfMs) {
@@ -360,7 +379,7 @@ export function forecastLine(provider) {
360
379
  if (!advisory)
361
380
  return null;
362
381
  if (advisory.bindingWindow === "monthly" && advisory.bindingRemaining <= 0) {
363
- return `unused until ${resetWeekday(provider.quota.monthlyResetsAt)}`;
382
+ return `unused until ${resetDay(provider.quota.monthlyResetsAt)}`;
364
383
  }
365
384
  if (advisory.bindingWindow === "monthly") {
366
385
  return `${Math.round(advisory.bindingRemaining)}% of month left`;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ageDuration, displayPlan } from "../state.js";
2
+ import { ageDuration } from "../state.js";
3
3
  import { Badge, PACE_TIPS, PaceBar, isEstimated, paceCells, resetClock, resetCountdown, timeLeft, } from "./PaceBar.js";
4
4
  import { ProviderIcon, providerTint } from "./ProviderIcon.js";
5
5
  import { lastClose } from "./ClosedWeeks.js";
@@ -20,7 +20,6 @@ export function ProviderCard({ provider, recommended, asOf, onSelect, }) {
20
20
  const updated = ageDuration(provider.ageMs);
21
21
  const resetsIn = quota ? timeLeft(quota.resetsAt, asOfMs) : null;
22
22
  const name = provider.displayName;
23
- const plan = displayPlan(quota?.plan);
24
23
  const last = lastClose(provider);
25
24
  return (_jsxs("article", { "data-testid": `provider-card-${provider.id}`, role: "button", tabIndex: 0, "aria-label": `${name} details`, onClick: () => onSelect(provider.id), onKeyDown: (e) => {
26
25
  // Space or Enter aimed at the windows chevron must not also open the drawer.
@@ -28,5 +27,5 @@ export function ProviderCard({ provider, recommended, asOf, onSelect, }) {
28
27
  e.preventDefault();
29
28
  onSelect(provider.id);
30
29
  }
31
- }, className: `pcard ${recommended ? "is-recommended is-top-pick" : ""}`, children: [_jsxs("div", { className: "pcard-head", children: [_jsx("span", { className: "picon", style: providerTint(provider.id), "aria-hidden": "true", children: _jsx(ProviderIcon, { id: provider.id, title: name }) }), _jsxs("div", { className: "who", children: [_jsxs("b", { 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 }), updated && _jsxs("span", { className: "src", children: ["Updated ", updated, " ago"] })] }), _jsx(Badge, { provider: provider })] }), _jsxs("div", { className: "pcard-body", children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), _jsx(WindowsRow, { provider: provider, asOf: asOf, variant: "card" }), _jsxs("div", { className: "pstats", children: [_jsxs("div", { title: PACE_TIPS.avg, children: [_jsx("span", { className: "l", children: "Avg pace" }), _jsx("span", { className: "v", children: pace.avg })] }), _jsxs("div", { title: PACE_TIPS.recent, children: [_jsx("span", { className: "l", children: "24h pace" }), _jsx("span", { className: "v", children: pace.recent })] }), _jsxs("div", { title: PACE_TIPS.ideal, children: [_jsx("span", { className: "l", children: "Ideal pace" }), _jsx("span", { className: "v", children: advisory ? `${advisory.idealRate.toFixed(1)}%/day` : "—" })] }), _jsxs("div", { children: [_jsx("span", { className: "l", children: "Resets in" }), _jsx("span", { className: "v", children: resetsIn ? `${resetsIn}${isEstimated(provider) ? " (est.)" : ""}` : resetCountdown(provider, asOfMs) })] })] })] }), _jsxs("div", { className: "pcard-foot", children: [_jsxs("div", { className: "foot-meta", children: [_jsx("span", { children: resetsDate(provider) ?? resetCountdown(provider, asOfMs) }), last && (_jsxs("span", { className: "last-week", children: ["Last week ", last.leftoverPct, "% leftover", last.resetsAtEstimated ? " (est.)" : ""] }))] }), _jsx("span", { className: "rowbtn", "aria-hidden": "true", children: "Inspect" })] })] }));
30
+ }, className: `pcard ${recommended ? "is-recommended is-top-pick" : ""}`, children: [_jsxs("div", { className: "pcard-head", children: [_jsx("span", { className: "picon", style: providerTint(provider.id), "aria-hidden": "true", children: _jsx(ProviderIcon, { id: provider.id, title: name }) }), _jsxs("div", { className: "who", children: [_jsxs("b", { 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" }) }) }))] }), updated && _jsxs("span", { className: "src", children: ["Updated ", updated, " ago"] })] }), _jsx(Badge, { provider: provider })] }), _jsxs("div", { className: "pcard-body", children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), _jsx(WindowsRow, { provider: provider, asOf: asOf, variant: "card" }), _jsxs("div", { className: "pstats", children: [_jsxs("div", { title: PACE_TIPS.avg, children: [_jsx("span", { className: "l", children: "Avg pace" }), _jsx("span", { className: "v", children: pace.avg })] }), _jsxs("div", { title: PACE_TIPS.recent, children: [_jsx("span", { className: "l", children: "24h pace" }), _jsx("span", { className: "v", children: pace.recent })] }), _jsxs("div", { title: PACE_TIPS.ideal, children: [_jsx("span", { className: "l", children: "Ideal pace" }), _jsx("span", { className: "v", children: advisory ? `${advisory.idealRate.toFixed(1)}%/day` : "—" })] }), _jsxs("div", { children: [_jsx("span", { className: "l", children: "Resets in" }), _jsx("span", { className: "v", children: resetsIn ? `${resetsIn}${isEstimated(provider) ? " (est.)" : ""}` : resetCountdown(provider, asOfMs) })] })] })] }), _jsxs("div", { className: "pcard-foot", children: [_jsxs("div", { className: "foot-meta", children: [_jsx("span", { children: resetsDate(provider) ?? resetCountdown(provider, asOfMs) }), last && (_jsxs("span", { className: "last-week", children: ["Last week ", last.leftoverPct, "% leftover", last.resetsAtEstimated ? " (est.)" : ""] }))] }), _jsx("span", { className: "rowbtn", "aria-hidden": "true", children: "Inspect" })] })] }));
32
31
  }
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { ageDuration, displayPlan, providerSubtitle, shortWindowLabel } from "../state.js";
4
- import { Badge, PaceBar, barGeometry, isEstimated, paceBadge, paceFigures, resetClock, timeLeft, } from "./PaceBar.js";
4
+ import { Badge, PaceBar, barGeometry, isEstimated, paceBadge, paceFigures, resetClock, resetDateClock, timeLeft, } from "./PaceBar.js";
5
5
  import { ProviderIcon, providerTint } from "./ProviderIcon.js";
6
6
  import { ClosedWeeks } from "./ClosedWeeks.js";
7
7
  import { renameProvider } from "../api.js";
@@ -166,7 +166,7 @@ export function monthlyMeta(provider) {
166
166
  const q = provider.quota;
167
167
  if (q?.monthlyKind !== "included" || !q.monthlyResetsAt)
168
168
  return null;
169
- const clock = resetClock(q.monthlyResetsAt);
169
+ const clock = resetDateClock(q.monthlyResetsAt);
170
170
  const base = clock ? `Resets ${clock}` : "Reset unknown";
171
171
  const adv = provider.advisory;
172
172
  if (q.monthlyStatus === "exhausted" || (adv?.bindingWindow === "monthly" && adv.bindingRemaining === 0)) {
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /** Brand-mark paths from the signed-off concept. Unknown ids fall back to a letter. */
3
3
  const PATHS = {
4
4
  kimi: 'M12 7v5l3 2M12 12m-8.5 0a8.5 8.5 0 1 0 17 0a8.5 8.5 0 1 0-17 0',
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { type ProviderView } from "../state.js";
2
+ import type { ProviderView } from "../state.js";
3
3
  export declare function ProviderRow({ provider, recommended, asOf, onSelect, }: {
4
4
  provider: ProviderView;
5
5
  recommended: boolean;
@@ -1,5 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { displayPlan } from "../state.js";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
2
  import { Badge, PaceBar, forecastLine, paceLines, resetCellLines, } from "./PaceBar.js";
4
3
  import { lastClose } from "./ClosedWeeks.js";
5
4
  import { WindowsRow } from "./WindowsRow.js";
@@ -9,10 +8,9 @@ export function ProviderRow({ provider, recommended, asOf, onSelect, }) {
9
8
  const asOfMs = Date.parse(asOf);
10
9
  const lines = paceLines(advisory);
11
10
  const name = provider.displayName;
12
- const plan = displayPlan(quota?.plan);
13
11
  const reset = resetCellLines(provider, asOfMs);
14
12
  const last = lastClose(provider);
15
- 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 })] })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? (_jsxs(_Fragment, { children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), _jsx(WindowsRow, { provider: provider, asOf: asOf, variant: "row" })] })) : "—" }), _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 }), last && (_jsxs("span", { className: "cell-s", children: ["last week ", last.leftoverPct, "% leftover", last.resetsAtEstimated ? " (est.)" : ""] }))] }), _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" }), _jsx("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" }) }) }))] }) })] }), _jsx("div", { "data-label": "Used vs elapsed", children: quota ? (_jsxs(_Fragment, { children: [_jsx(PaceBar, { provider: provider, asOf: asOf }), _jsx(WindowsRow, { provider: provider, asOf: asOf, variant: "row" })] })) : "—" }), _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 }), last && (_jsxs("span", { className: "cell-s", children: ["last week ", last.leftoverPct, "% leftover", last.resetsAtEstimated ? " (est.)" : ""] }))] }), _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) => {
16
14
  e.stopPropagation();
17
15
  onSelect(provider.id);
18
16
  }, children: "Inspect" }), _jsx("span", { className: "sr-only", children: forecastLine(provider) })] }));
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ageLabel } from "../state.js";
3
3
  import { timeLeft } from "./PaceBar.js";
4
4
  import { ProviderIcon, providerTint } from "./ProviderIcon.js";
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { paceBadge, resetClock, resetCountdown, timeLeft } from "./PaceBar.js";
4
4
  export const RAIL_DAYS = 7;
@@ -10,7 +10,7 @@ export interface WindowUnit {
10
10
  pct: number;
11
11
  /** Presentation-only heat: < 80 none, 80–99 hot, 100 or exhausted full. */
12
12
  heat: "" | "hot" | "full";
13
- /** Tag time outside the pill: "in 7d", or the weekday alone when the month is exhausted. */
13
+ /** Tag time outside the pill: "in 7d", or the weekday plus date when the month is exhausted. */
14
14
  time: string | null;
15
15
  /** Expanded card track's reset line. */
16
16
  reset: string | null;
@@ -1,6 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
- import { resetClock, resetWeekday } from "./PaceBar.js";
3
+ import { resetDateClock, resetDay } from "./PaceBar.js";
4
4
  const MIN_MS = 60_000;
5
5
  const HOUR_MS = 60 * MIN_MS;
6
6
  const DAY_MS = 24 * HOUR_MS;
@@ -34,10 +34,10 @@ export function windowUnits(provider, asOfMs) {
34
34
  if (q.monthlyKind === "included" && typeof q.monthlyPct === "number" && q.monthlyResetsAt) {
35
35
  const exhausted = q.monthlyStatus === "exhausted";
36
36
  const left = windowCountdown(q.monthlyResetsAt, asOfMs);
37
- const clock = resetClock(q.monthlyResetsAt);
37
+ const clock = resetDateClock(q.monthlyResetsAt);
38
38
  units.push({ win: "month", tag: "Mth", cardLabel: "Monthly", rowLabel: "Month",
39
39
  pct: q.monthlyPct, heat: heatOf(q.monthlyPct, exhausted),
40
- time: exhausted ? resetWeekday(q.monthlyResetsAt) : left ? `in ${left}` : null,
40
+ time: exhausted ? resetDay(q.monthlyResetsAt) : left ? `in ${left}` : null,
41
41
  reset: exhausted ? (clock ? `resets ${clock}` : null) : left ? `in ${left}` : null });
42
42
  }
43
43
  return units;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
  import { ProvidersPanel } from "./ProvidersPanel.js";
4
4
  import { CliIntegrationPanel } from "./CliIntegrationPanel.js";