@arandevcode/opencode-quota-extended 0.2.2 → 0.2.4

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 (49) hide show
  1. package/README-es.md +19 -13
  2. package/README.md +18 -12
  3. package/bin/quota.mjs +8 -1
  4. package/dist/lib/balance.d.ts +16 -0
  5. package/dist/lib/balance.d.ts.map +1 -0
  6. package/dist/lib/balance.js +45 -0
  7. package/dist/lib/balance.js.map +1 -0
  8. package/dist/lib/opencode-go-config.d.ts +6 -1
  9. package/dist/lib/opencode-go-config.d.ts.map +1 -1
  10. package/dist/lib/opencode-go-config.js +29 -3
  11. package/dist/lib/opencode-go-config.js.map +1 -1
  12. package/dist/lib/pace.d.ts +28 -0
  13. package/dist/lib/pace.d.ts.map +1 -0
  14. package/dist/lib/pace.js +46 -0
  15. package/dist/lib/pace.js.map +1 -0
  16. package/dist/lib/refresh-scheduler.d.ts +20 -0
  17. package/dist/lib/refresh-scheduler.d.ts.map +1 -0
  18. package/dist/lib/refresh-scheduler.js +57 -0
  19. package/dist/lib/refresh-scheduler.js.map +1 -0
  20. package/dist/lib/types.d.ts +19 -0
  21. package/dist/lib/types.d.ts.map +1 -1
  22. package/dist/providers/opencode-go.d.ts +8 -4
  23. package/dist/providers/opencode-go.d.ts.map +1 -1
  24. package/dist/providers/opencode-go.js +167 -43
  25. package/dist/providers/opencode-go.js.map +1 -1
  26. package/dist/tui.d.ts.map +1 -1
  27. package/dist/tui.tsx +58 -83
  28. package/dist/ui/SidebarPanel.d.ts.map +1 -1
  29. package/dist/ui/SidebarPanel.tsx +2 -13
  30. package/dist/ui/cards.d.ts.map +1 -1
  31. package/dist/ui/cards.tsx +24 -4
  32. package/images/opencode_quota_extended_cli.png +0 -0
  33. package/images/opencode_quota_extended_sidebar.png +0 -0
  34. package/package.json +7 -3
  35. package/scripts/clean.mjs +8 -0
  36. package/scripts/postbuild.mjs +56 -0
  37. package/scripts/setup.mjs +53 -0
  38. package/dist/lib/sanitize.d.ts +0 -10
  39. package/dist/lib/sanitize.d.ts.map +0 -1
  40. package/dist/lib/sanitize.js +0 -14
  41. package/dist/lib/sanitize.js.map +0 -1
  42. package/dist/lib/throttle.d.ts +0 -18
  43. package/dist/lib/throttle.d.ts.map +0 -1
  44. package/dist/lib/throttle.js +0 -26
  45. package/dist/lib/throttle.js.map +0 -1
  46. package/dist/lib/toast-format.d.ts +0 -24
  47. package/dist/lib/toast-format.d.ts.map +0 -1
  48. package/dist/lib/toast-format.js +0 -79
  49. package/dist/lib/toast-format.js.map +0 -1
package/README-es.md CHANGED
@@ -121,7 +121,7 @@ Registra un plugin TUI que renderiza un panel `sidebar_content` en la posición
121
121
  |---|---|---|
122
122
  | **OpenAI** | 5h móvil, Semanal, Code Review | `auth.json` → entrada `openai` / `chatgpt` |
123
123
  | **GitHub Copilot** | Solicitudes Premium (ventana única) | `auth.json` → entrada `github-copilot` |
124
- | **OpenCode Go** | 5h, Semanal, Mensual | Archivo de configuración o vars de entorno |
124
+ | **OpenCode Go** | 5h, Semanal, Mensual + gasto | API key personal (o cookie de sesión legacy) |
125
125
 
126
126
  Colores de semáforo: ≥50% `theme.success` (verde), 25-49% `theme.warning` (naranja), <25% `theme.error` (rojo).
127
127
 
@@ -129,38 +129,43 @@ Colores de semáforo: ≥50% `theme.success` (verde), 25-49% `theme.warning` (na
129
129
 
130
130
  Lee credenciales del archivo de autenticación de OpenCode (`~/.local/share/opencode/auth.json`). **No** almacena, solicita ni distribuye secretos.
131
131
 
132
- **OpenCode Go** requiere configuración adicional — es un poco tricky porque no hay API oficial todavía.
132
+ **OpenCode Go** usa una API key personal. El plugin llama al mismo endpoint del console que el dashboard y lee las tres ventanas de cuota, el gasto en dólares por ventana y el balance de crédito prepago.
133
133
 
134
134
  Crea `~/.config/opencode/opencode-quota/opencode-go.json`:
135
135
 
136
136
  ```json
137
137
  {
138
- "workspaceId": "wrk_abc123...",
139
- "authCookie": "Fe26.2..."
138
+ "apiKey": "oc_sk_..."
140
139
  }
141
140
  ```
142
141
 
143
142
  | Campo | Descripción | Cómo obtenerlo |
144
143
  |---|---|---|
145
- | `workspaceId` | ID de tu workspace en OpenCode | Entra en [opencode.ai/go](https://opencode.ai/go). La URL es `https://opencode.ai/workspace/wrk_XXX/go` — copia la parte `wrk_XXX`. |
146
- | `authCookie` | Cookie de sesión para autenticación | Abre DevTools (F12) → Application → Cookies → `opencode.ai` → busca la cookie `auth` (empieza con `Fe26.2...`). O ejecuta en la consola: `document.cookie.match(/(?:^\|;\s*)auth=([^;]+)/)?.[1]` |
144
+ | `apiKey` | API key personal de OpenCode | Abre [opencode.ai/console](https://opencode.ai/console) → **Personal API keys** → crea una key con permisos **All**. Copia el valor `oc_sk_...`. No uses una service account: no puede leer billing. |
147
145
 
148
- **Múltiples workspaces/cuentas:** cualquier archivo `opencode-go-<sufijo>.json` en el mismo directorio añade una sección extra `Opencode GO (<sufijo>)` (p. ej. `opencode-go-work.json` → **Opencode GO (work)**). El `opencode-go.json` base siempre se muestra como **OpenCode Go**.
146
+ La key se guarda con permisos `600` y solo se envía a `opencode.ai`. Una key **All** muestra cuota, gasto y balance. Una key `read` no sirve para este plugin: no puede llamar a `go/status`.
147
+
148
+ **Múltiples workspaces/cuentas:** cualquier archivo `opencode-go-<sufijo>.json` en el mismo directorio añade una sección extra `Opencode GO (<sufijo>)` (p. ej. `opencode-go-work.json` → **Opencode GO (work)**). El `opencode-go.json` base siempre se muestra como **OpenCode Go**. Los archivos `opencode-go-<sufijo>-key.json` que mantiene el switch de cuentas los ignora el plugin de cuota.
149
149
 
150
150
  ```sh
151
- # p. ej. un segundo workspace
151
+ # p. ej. una segunda cuenta
152
152
  cp ~/.config/opencode/opencode-quota/opencode-go.json \
153
153
  ~/.config/opencode/opencode-quota/opencode-go-work.json
154
- # luego edita workspaceId/authCookie dentro de opencode-go-work.json
154
+ # luego sustituye apiKey dentro de opencode-go-work.json
155
155
  ```
156
156
 
157
157
  **Setup automatizado** (recomendado):
158
158
  ```sh
159
- npm run setup
159
+ npm run setup # cuenta base -> opencode-go.json
160
+ npm run setup work # cuenta extra -> opencode-go-work.json
160
161
  ```
161
- Te solicita ambos valores y crea el archivo automáticamente. También muestra un bookmarklet que puedes arrastrar a la barra de favoritos y pinchar en opencode.ai para extraer la auth cookie con un clic.
162
+ Te solicita la API key, escribe el archivo con permisos `600` e indica dónde crearla.
163
+
164
+ También puedes usar `OPENCODE_GO_API_KEY` en vez de un archivo.
165
+
166
+ **Legacy:** `{ "workspaceId": "wrk_...", "authCookie": "st_..." }` sigue aceptándose por compatibilidad, pero la cookie de sesión del navegador caduca. Mejor usa la API key.
162
167
 
163
- **Consejo:** mantén una pestaña del navegador con sesión iniciada en opencode.ai mientras usas este plugin — la auth cookie puede expirar.
168
+ **Migrar una cuenta existente:** si ya tienes `opencode-go-<Cuenta>.json`, sustituye todo su contenido por `{ "apiKey": "oc_sk_..." }`. No hacen falta `workspaceId` ni `authCookie`. `npm run setup <Cuenta>` respeta mayúsculas: el sufijo debe coincidir exactamente con el nombre del archivo.
164
169
 
165
170
  ### Relación con [`@slkiser/opencode-quota`](https://github.com/slkiser/opencode-quota)
166
171
 
@@ -169,7 +174,8 @@ Te solicita ambos valores y crea el archivo automáticamente. También muestra u
169
174
  ### Solución de problemas
170
175
 
171
176
  - **El panel no aparece**: `grep '@arandevcode/opencode-quota-extended' ~/.local/share/opencode/log/*.log`
172
- - **OpenCode Go muestra "skipped"**: confirma que el archivo sidecar o vars de entorno están configurados
177
+ - **OpenCode Go muestra "skipped"**: confirma que el archivo sidecar o `OPENCODE_GO_API_KEY` está configurado
178
+ - **OpenCode Go muestra 401/403**: la key no es válida, ha caducado o le faltan permisos; crea una key personal **All** (las service account no pueden leer billing)
173
179
  - **Bug antiguo de pinning `@opentui/solid`**: usa `^0.4.1 || ^0.5.0` y reinstala limpio (`rm -rf node_modules && npm ci`)
174
180
 
175
181
  ### Desarrollo
package/README.md CHANGED
@@ -122,7 +122,7 @@ Registers a TUI plugin that renders a `sidebar_content` panel at order 145, show
122
122
  |---|---|---|
123
123
  | **OpenAI** | 5h rolling, Weekly, Code Review | `auth.json` → `openai` / `chatgpt` entry |
124
124
  | **GitHub Copilot** | Premium requests (single window) | `auth.json` → `github-copilot` entry |
125
- | **OpenCode Go** | 5h, Weekly, Monthly | Config file or env vars |
125
+ | **OpenCode Go** | 5h, Weekly, Monthly + spend | Personal API key (or legacy session cookie) |
126
126
 
127
127
  Traffic-light colors: ≥50% `theme.success` (green), 25-49% `theme.warning` (orange), <25% `theme.error` (red).
128
128
 
@@ -130,38 +130,43 @@ Traffic-light colors: ≥50% `theme.success` (green), 25-49% `theme.warning` (or
130
130
 
131
131
  Reads credentials from OpenCode's runtime auth file (`~/.local/share/opencode/auth.json`). Does **not** store, prompt for, or distribute secrets.
132
132
 
133
- **OpenCode Go** requires additional setup — it's a bit tricky because there's no official API yet.
133
+ **OpenCode Go** uses a personal API key. The plugin calls the same console endpoint as the dashboard and reads the three quota windows, the dollar spend per window, and the prepaid credit balance.
134
134
 
135
135
  Create `~/.config/opencode/opencode-quota/opencode-go.json`:
136
136
 
137
137
  ```json
138
138
  {
139
- "workspaceId": "wrk_abc123...",
140
- "authCookie": "Fe26.2..."
139
+ "apiKey": "oc_sk_..."
141
140
  }
142
141
  ```
143
142
 
144
143
  | Field | Description | How to get it |
145
144
  |---|---|---|
146
- | `workspaceId` | Your OpenCode workspace ID | Log in at [opencode.ai/go](https://opencode.ai/go). The URL is `https://opencode.ai/workspace/wrk_XXX/go` — copy the `wrk_XXX` part. |
147
- | `authCookie` | Session cookie for auth | Open DevTools (F12) → Application → Cookies → `opencode.ai` → find the `auth` cookie (starts with `Fe26.2...`). Or run this in the console: `document.cookie.match(/(?:^\|;\s*)auth=([^;]+)/)?.[1]` |
145
+ | `apiKey` | Personal OpenCode API key | Open [opencode.ai/console](https://opencode.ai/console) → **Personal API keys** → create a key with **All** permissions. Copy the `oc_sk_...` value. Do not use a service-account key: service accounts cannot read billing. |
148
146
 
149
- **Multiple workspaces/accounts:** any `opencode-go-<suffix>.json` file in the same directory adds an extra `Opencode GO (<suffix>)` section (e.g. `opencode-go-work.json` → **Opencode GO (work)**). The base `opencode-go.json` always renders as **OpenCode Go**.
147
+ The key is stored with mode `600` and is only sent to `opencode.ai`. An **All** key shows quota, dollar spend and credit balance. A `read` key is not enough for this plugin: it cannot call `go/status`.
148
+
149
+ **Multiple workspaces/accounts:** any `opencode-go-<suffix>.json` file in the same directory adds an extra `Opencode GO (<suffix>)` section (e.g. `opencode-go-work.json` → **Opencode GO (work)**). The base `opencode-go.json` always renders as **OpenCode Go**. The `opencode-go-<suffix>-key.json` files maintained by the account switcher are ignored by the quota plugin.
150
150
 
151
151
  ```sh
152
152
  # e.g. a second workspace
153
153
  cp ~/.config/opencode/opencode-quota/opencode-go.json \
154
154
  ~/.config/opencode/opencode-quota/opencode-go-work.json
155
- # then edit workspaceId/authCookie inside opencode-go-work.json
155
+ # then replace apiKey inside opencode-go-work.json
156
156
  ```
157
157
 
158
158
  **Automated setup** (recommended):
159
159
  ```sh
160
- npm run setup
160
+ npm run setup # base account -> opencode-go.json
161
+ npm run setup work # extra account -> opencode-go-work.json
161
162
  ```
162
- This prompts for both values and creates the file for you. It also prints a bookmarklet you can drag to your bookmarks bar and click while on opencode.ai to extract the auth cookie with one click.
163
+ This prompts for the API key, writes the file with mode `600`, and prints where to create the key.
164
+
165
+ You can also use `OPENCODE_GO_API_KEY` instead of a file.
166
+
167
+ **Legacy:** `{ "workspaceId": "wrk_...", "authCookie": "st_..." }` is still accepted for backwards compatibility, but the browser session cookie expires. Prefer the API key.
163
168
 
164
- **Tip:** keep a browser tab logged into opencode.ai while using this plugin — the auth cookie may expire.
169
+ **Migrating an existing account:** if you already have `opencode-go-<Account>.json`, replace its entire contents with `{ "apiKey": "oc_sk_..." }`. No `workspaceId` or `authCookie` needed. `npm run setup <Account>` is case-sensitive: the suffix must match the filename exactly.
165
170
 
166
171
  ### Relationship with [`@slkiser/opencode-quota`](https://github.com/slkiser/opencode-quota)
167
172
 
@@ -170,7 +175,8 @@ This prompts for both values and creates the file for you. It also prints a book
170
175
  ### Troubleshooting
171
176
 
172
177
  - **Panel doesn't appear**: `grep '@arandevcode/opencode-quota-extended' ~/.local/share/opencode/log/*.log`
173
- - **OpenCode Go shows "skipped"**: confirm sidecar file or env vars are set
178
+ - **OpenCode Go shows "skipped"**: confirm the sidecar file or `OPENCODE_GO_API_KEY` is set
179
+ - **OpenCode Go shows 401/403**: the key is invalid, expired or lacks permissions; create a personal **All** key (service-account keys cannot read billing)
174
180
  - **Old `@opentui/solid` pinning bug**: use `^0.4.1 || ^0.5.0` and clean-install (`rm -rf node_modules && npm ci`)
175
181
 
176
182
  ### Development
package/bin/quota.mjs CHANGED
@@ -6,6 +6,7 @@ import { loadCardsConfig } from "../dist/config/loader.js";
6
6
  import { collectSnapshot } from "../dist/providers/registry.js";
7
7
  import { renderProgressBar, trafficLight } from "../dist/ui/progress-bar.js";
8
8
  import { formatResetCombined } from "../dist/lib/reset-format.js";
9
+ import { formatBalance, formatWindowSpend } from "../dist/lib/balance.js";
9
10
 
10
11
  const INDENT = 4;
11
12
 
@@ -13,6 +14,7 @@ const COLORS = {
13
14
  ok: "\x1b[32m",
14
15
  warning: "\x1b[33m",
15
16
  error: "\x1b[31m",
17
+ accent: "\x1b[36m",
16
18
  };
17
19
  const RESET = "\x1b[0m";
18
20
  const BOLD = "\x1b[1m";
@@ -74,7 +76,10 @@ async function main() {
74
76
  continue;
75
77
  }
76
78
 
77
- console.log(` ${BOLD}${provider.label}${RESET}`);
79
+ const credit = formatBalance(provider.balance);
80
+ console.log(
81
+ ` ${BOLD}${provider.label}${RESET}${credit ? ` ${COLORS.accent}${credit}${RESET}` : ""}`,
82
+ );
78
83
 
79
84
  for (const [i, win] of provider.windows.entries()) {
80
85
  if (i > 0) console.log();
@@ -91,6 +96,8 @@ async function main() {
91
96
  const leftStr = `${color}${pct}% left${RESET}`;
92
97
  const rightStr = `${DIM}${usedPct(pct)}${RESET}`;
93
98
  console.log(" ".repeat(INDENT) + padRight(leftStr, rightStr));
99
+ const spend = formatWindowSpend(win);
100
+ if (spend) console.log(" ".repeat(INDENT) + DIM + spend + RESET);
94
101
  }
95
102
  console.log();
96
103
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Prepaid credit balance formatting (OpenCode Go).
3
+ *
4
+ * The provider reports `availableMicroCents` as a decimal string because it can
5
+ * exceed the safe integer range; 1e8 microcents make one US dollar. Only the
6
+ * dollar amount and the auto-recharge flag are ever rendered — never the raw
7
+ * microcents, and never anything that came from the account credentials.
8
+ */
9
+ import type { ProviderBalance, QuotaWindow } from "./types.js";
10
+ /** Available credit in dollars (2 decimals), or null when unreadable. */
11
+ export declare function balanceUsd(balance: ProviderBalance | undefined): number | null;
12
+ /** "$9.18 credit", plus a note when credits are drawn or auto-recharge is on. */
13
+ export declare function formatBalance(balance: ProviderBalance | undefined): string;
14
+ /** "$2.64 / $30.00" for a window that exposes both money fields. */
15
+ export declare function formatWindowSpend(window: QuotaWindow): string;
16
+ //# sourceMappingURL=balance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"balance.d.ts","sourceRoot":"","sources":["../../src/lib/balance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/D,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAM9E;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,CAO1E;AAcD,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAK7D"}
@@ -0,0 +1,45 @@
1
+ /** Available credit in dollars (2 decimals), or null when unreadable. */
2
+ export function balanceUsd(balance) {
3
+ if (!balance)
4
+ return null;
5
+ const raw = balance.availableMicroCents;
6
+ const micro = typeof raw === "string" ? Number(raw.trim()) : raw;
7
+ if (typeof micro !== "number" || !Number.isFinite(micro))
8
+ return null;
9
+ return Math.round(micro / 1e6) / 100;
10
+ }
11
+ /** "$9.18 credit", plus a note when credits are drawn or auto-recharge is on. */
12
+ export function formatBalance(balance) {
13
+ const usd = balanceUsd(balance);
14
+ if (usd === null || !balance)
15
+ return "";
16
+ const parts = [`$${usd.toFixed(2)} credit`];
17
+ if (balance.useBalance === true)
18
+ parts.push("draws on credits");
19
+ if (balance.autoRecharge?.enabled)
20
+ parts.push("auto-recharge");
21
+ return parts.join(" · ");
22
+ }
23
+ /** Dollars from a microcents decimal string, using cents precision. */
24
+ function microCentsUsd(raw) {
25
+ if (typeof raw !== "string" || raw.trim() === "")
26
+ return null;
27
+ try {
28
+ const micro = BigInt(raw.trim());
29
+ if (micro < 0n)
30
+ return null;
31
+ return Number((micro + 500000n) / 1000000n) / 100;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ /** "$2.64 / $30.00" for a window that exposes both money fields. */
38
+ export function formatWindowSpend(window) {
39
+ const spent = microCentsUsd(window.spentMicroCents);
40
+ const limit = microCentsUsd(window.limitMicroCents);
41
+ if (spent === null || limit === null || limit <= 0)
42
+ return "";
43
+ return `$${spent.toFixed(2)} / $${limit.toFixed(2)}`;
44
+ }
45
+ //# sourceMappingURL=balance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"balance.js","sourceRoot":"","sources":["../../src/lib/balance.ts"],"names":[],"mappings":"AAUA,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,OAAoC;IAC7D,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,mBAAmB,CAAC;IACxC,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,OAAoC;IAChE,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAChC,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,YAAY,EAAE,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC/D,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,uEAAuE;AACvE,SAAS,aAAa,CAAC,GAAuB;IAC5C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,IAAI,KAAK,GAAG,EAAE;YAAE,OAAO,IAAI,CAAC;QAC5B,OAAO,MAAM,CAAC,CAAC,KAAK,GAAG,OAAQ,CAAC,GAAG,QAAU,CAAC,GAAG,GAAG,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9D,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AACvD,CAAC"}
@@ -1,7 +1,12 @@
1
1
  export type OpenCodeGoConfig = {
2
+ auth: "api-key";
3
+ apiKey: string;
4
+ } | {
5
+ auth: "cookie";
2
6
  workspaceId: string;
3
7
  authCookie: string;
4
8
  };
9
+ export type OpenCodeGoMissingField = "apiKey" | "workspaceId" | "authCookie";
5
10
  export type OpenCodeGoConfigResolution = {
6
11
  state: "configured";
7
12
  config: OpenCodeGoConfig;
@@ -9,7 +14,7 @@ export type OpenCodeGoConfigResolution = {
9
14
  } | {
10
15
  state: "incomplete";
11
16
  source: string;
12
- missing: "workspaceId" | "authCookie";
17
+ missing: OpenCodeGoMissingField;
13
18
  } | {
14
19
  state: "invalid";
15
20
  source: string;
@@ -1 +1 @@
1
- {"version":3,"file":"opencode-go-config.d.ts","sourceRoot":"","sources":["../../src/lib/opencode-go-config.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,gBAAgB,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,0BAA0B,GAClC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAAA;CAAE,GAC9E;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;CACf;AAoGD,gFAAgF;AAChF,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAInF;AAED,qDAAqD;AACrD,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,0BAA0B,CAAC,CAErC;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAwBjF;AAKD,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAMzF;AAOD,wBAAsB,+BAA+B,CACnD,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,0BAA0B,CAAC,CAOrC;AAED,uEAAuE;AACvE,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
1
+ {"version":3,"file":"opencode-go-config.d.ts","sourceRoot":"","sources":["../../src/lib/opencode-go-config.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;AAE7E,MAAM,MAAM,0BAA0B,GAClC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,sBAAsB,CAAA;CAAE,GACxE;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtB,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,0CAA0C;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;CACf;AA2HD,gFAAgF;AAChF,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAInF;AAED,qDAAqD;AACrD,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,0BAA0B,CAAC,CAErC;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CA0BjF;AAKD,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAMzF;AAOD,wBAAsB,+BAA+B,CACnD,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,0BAA0B,CAAC,CAOrC;AAED,uEAAuE;AACvE,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
@@ -37,12 +37,20 @@ function getInstanceCandidatePaths(id) {
37
37
  return getSidecarDirCandidates().map((d) => join(d, `${EXTRA_PREFIX}${suffix}.json`));
38
38
  }
39
39
  function fromEnv() {
40
+ const apiKey = process.env.OPENCODE_GO_API_KEY?.trim();
41
+ if (apiKey) {
42
+ return { state: "configured", config: { auth: "api-key", apiKey }, source: "env" };
43
+ }
40
44
  const workspaceId = process.env.OPENCODE_GO_WORKSPACE_ID?.trim();
41
45
  const authCookie = process.env.OPENCODE_GO_AUTH_COOKIE?.trim();
42
46
  if (!workspaceId && !authCookie)
43
47
  return { state: "none" };
44
48
  if (workspaceId && authCookie) {
45
- return { state: "configured", config: { workspaceId, authCookie }, source: "env" };
49
+ return {
50
+ state: "configured",
51
+ config: { auth: "cookie", workspaceId, authCookie },
52
+ source: "env",
53
+ };
46
54
  }
47
55
  return {
48
56
  state: "incomplete",
@@ -79,19 +87,34 @@ async function readFileResolution(path) {
79
87
  error: err instanceof Error ? err.message : String(err),
80
88
  };
81
89
  }
90
+ if (typeof parsed === "string") {
91
+ const apiKey = parsed.trim();
92
+ if (apiKey) {
93
+ return { state: "configured", config: { auth: "api-key", apiKey }, source: path };
94
+ }
95
+ return { state: "invalid", source: path, error: "Config key is empty" };
96
+ }
82
97
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
83
98
  return { state: "invalid", source: path, error: "Config must be a JSON object" };
84
99
  }
85
100
  const obj = parsed;
101
+ const apiKey = typeof obj.apiKey === "string" ? obj.apiKey.trim() : "";
86
102
  const workspaceId = typeof obj.workspaceId === "string" ? obj.workspaceId.trim() : "";
87
103
  const authCookie = typeof obj.authCookie === "string" ? obj.authCookie.trim() : "";
104
+ if (apiKey) {
105
+ return { state: "configured", config: { auth: "api-key", apiKey }, source: path };
106
+ }
88
107
  if (workspaceId && authCookie) {
89
- return { state: "configured", config: { workspaceId, authCookie }, source: path };
108
+ return {
109
+ state: "configured",
110
+ config: { auth: "cookie", workspaceId, authCookie },
111
+ source: path,
112
+ };
90
113
  }
91
114
  return {
92
115
  state: "incomplete",
93
116
  source: path,
94
- missing: workspaceId ? "authCookie" : "workspaceId",
117
+ missing: workspaceId ? "authCookie" : authCookie ? "workspaceId" : "apiKey",
95
118
  };
96
119
  }
97
120
  async function resolveFromPaths(paths) {
@@ -139,6 +162,9 @@ export async function discoverOpenCodeGoInstances() {
139
162
  const suffix = name.slice(EXTRA_PREFIX.length, -".json".length);
140
163
  if (!suffix || seen.has(suffix))
141
164
  continue;
165
+ // Helper files maintained by the account switcher are not quota configs.
166
+ if (suffix === "active" || suffix.endsWith("-key"))
167
+ continue;
142
168
  seen.add(suffix);
143
169
  instances.push({
144
170
  id: `${EXTRA_PREFIX}${suffix}`,
@@ -1 +1 @@
1
- {"version":3,"file":"opencode-go-config.js","sourceRoot":"","sources":["../../src/lib/opencode-go-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"opencode-go-config.js","sourceRoot":"","sources":["../../src/lib/opencode-go-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Quota pace — how usage compares to the passage of time in a window.
3
+ *
4
+ * Every window resets on a known schedule (5h, weekly, monthly). When a window
5
+ * carries its reset time we can say whether the remaining share is ahead of or
6
+ * behind the clock: spending slower than time passes is margin, faster is
7
+ * "ahead". This is the same reading Jarvis shows, but text-only — no overlay on
8
+ * the progress bar, so it works at any terminal width.
9
+ */
10
+ import type { QuotaWindow } from "./types.js";
11
+ export type QuotaPace = "buffer" | "on-pace" | "burn";
12
+ export interface QuotaPaceReading {
13
+ pace: QuotaPace;
14
+ /** Percentage-point gap between remaining quota and time left (rounded). */
15
+ deltaPercent: number;
16
+ /** The same gap expressed as time, in ms. */
17
+ deltaMs: number;
18
+ }
19
+ /** Window length when the id/label names a known schedule, else null. */
20
+ export declare function windowDurationMs(window: Pick<QuotaWindow, "id" | "label">): number | null;
21
+ /**
22
+ * Compare remaining quota to the time left in the window. Returns null when the
23
+ * schedule or reset time is unknown, or there is no reading to compare.
24
+ */
25
+ export declare function quotaPace(window: QuotaWindow, now?: number): QuotaPaceReading | null;
26
+ /** One short line: "margin +6% · 3h 12m", "ahead -6% · 1d 2h" or "on pace". */
27
+ export declare function formatPace(window: QuotaWindow, now?: number): string;
28
+ //# sourceMappingURL=pace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pace.d.ts","sourceRoot":"","sources":["../../src/lib/pace.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAMzF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,GAAE,MAAmB,GAAG,gBAAgB,GAAG,IAAI,CAUhG;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAWhF"}
@@ -0,0 +1,46 @@
1
+ import { clampPercent, formatDuration } from "./reset-format.js";
2
+ const HOUR_MS = 60 * 60 * 1000;
3
+ const DAY_MS = 24 * HOUR_MS;
4
+ /** Window length when the id/label names a known schedule, else null. */
5
+ export function windowDurationMs(window) {
6
+ const key = `${window.id} ${window.label}`.toLowerCase();
7
+ if (/\b5h\b|five[_ -]?hour/.test(key))
8
+ return 5 * HOUR_MS;
9
+ if (/weekly|seven[_ -]?day|\bweek\b/.test(key))
10
+ return 7 * DAY_MS;
11
+ if (/monthly|\bmonth\b/.test(key))
12
+ return 30 * DAY_MS;
13
+ return null;
14
+ }
15
+ /**
16
+ * Compare remaining quota to the time left in the window. Returns null when the
17
+ * schedule or reset time is unknown, or there is no reading to compare.
18
+ */
19
+ export function quotaPace(window, now = Date.now()) {
20
+ const duration = windowDurationMs(window);
21
+ const resetAt = window.resetTimeIso ? Date.parse(window.resetTimeIso) : Number.NaN;
22
+ const remaining = window.percentRemaining;
23
+ if (duration == null || !Number.isFinite(resetAt) || !Number.isFinite(remaining))
24
+ return null;
25
+ const exactTimeLeft = Math.min(100, Math.max(0, ((resetAt - now) / duration) * 100));
26
+ const deltaPercent = clampPercent(remaining) - clampPercent(exactTimeLeft);
27
+ const pace = deltaPercent > 1 ? "buffer" : deltaPercent < -1 ? "burn" : "on-pace";
28
+ const deltaMs = Math.round((Math.abs(Math.min(100, Math.max(0, remaining)) - exactTimeLeft) / 100) * duration);
29
+ return { pace, deltaPercent, deltaMs };
30
+ }
31
+ /** One short line: "margin +6% · 3h 12m", "ahead -6% · 1d 2h" or "on pace". */
32
+ export function formatPace(window, now = Date.now()) {
33
+ const reading = quotaPace(window, now);
34
+ if (!reading)
35
+ return "";
36
+ const { pace, deltaPercent, deltaMs } = reading;
37
+ if (pace === "on-pace")
38
+ return "on pace";
39
+ const word = pace === "buffer" ? "margin" : "ahead";
40
+ const sign = deltaPercent > 0 ? "+" : "";
41
+ const gap = formatDuration(deltaMs);
42
+ return gap
43
+ ? `${word} ${sign}${deltaPercent}% · ${gap}`
44
+ : `${word} ${sign}${deltaPercent}%`;
45
+ }
46
+ //# sourceMappingURL=pace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pace.js","sourceRoot":"","sources":["../../src/lib/pace.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/B,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,CAAC;AAY5B,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,MAAyC;IACxE,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;IACzD,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IAC1D,IAAI,gCAAgC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAClE,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAmB,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IACrE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;IACnF,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAC1C,IAAI,QAAQ,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9F,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IACrF,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IAC3E,MAAM,IAAI,GAAc,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7F,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,GAAG,aAAa,CAAC,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;IAC/G,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,MAAmB,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;IACtE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAChD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;IACpD,MAAM,IAAI,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,MAAM,GAAG,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,GAAG;QACR,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,YAAY,OAAO,GAAG,EAAE;QAC5C,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,YAAY,GAAG,CAAC;AACxC,CAAC"}
@@ -0,0 +1,20 @@
1
+ export type RefreshSchedulerOptions = {
2
+ refresh: () => void;
3
+ intervalMs: number;
4
+ activityDelayMs?: number;
5
+ minGapMs?: number;
6
+ now?: () => number;
7
+ };
8
+ export type RefreshScheduler = {
9
+ start: () => void;
10
+ schedule: (delay?: number, force?: boolean) => void;
11
+ dispose: () => void;
12
+ };
13
+ /**
14
+ * Coalesces bursts of usage events into a single refresh and enforces a
15
+ * minimum gap between attempts. A streaming message can emit many
16
+ * `message.updated` events per second; without this every one of them would
17
+ * fan out to the provider APIs.
18
+ */
19
+ export declare function createRefreshScheduler(options: RefreshSchedulerOptions): RefreshScheduler;
20
+ //# sourceMappingURL=refresh-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-scheduler.d.ts","sourceRoot":"","sources":["../../src/lib/refresh-scheduler.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAoDzF"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Coalesces bursts of usage events into a single refresh and enforces a
3
+ * minimum gap between attempts. A streaming message can emit many
4
+ * `message.updated` events per second; without this every one of them would
5
+ * fan out to the provider APIs.
6
+ */
7
+ export function createRefreshScheduler(options) {
8
+ const { refresh, intervalMs, activityDelayMs = 1_500, minGapMs = 20_000, now = Date.now, } = options;
9
+ let interval = null;
10
+ let timer = null;
11
+ let lastRefreshAt = 0;
12
+ let disposed = false;
13
+ const run = () => {
14
+ if (disposed)
15
+ return;
16
+ lastRefreshAt = now();
17
+ refresh();
18
+ };
19
+ const schedule = (delay = activityDelayMs, force = false) => {
20
+ if (disposed || timer !== null)
21
+ return;
22
+ const wait = force
23
+ ? delay
24
+ : Math.max(delay, minGapMs - (now() - lastRefreshAt));
25
+ timer = setTimeout(() => {
26
+ timer = null;
27
+ if (disposed)
28
+ return;
29
+ if (!force && now() - lastRefreshAt < minGapMs) {
30
+ schedule(0);
31
+ return;
32
+ }
33
+ run();
34
+ }, wait);
35
+ };
36
+ return {
37
+ start() {
38
+ if (disposed || interval !== null)
39
+ return;
40
+ interval = setInterval(() => {
41
+ if (now() - lastRefreshAt >= minGapMs)
42
+ run();
43
+ }, intervalMs);
44
+ },
45
+ schedule,
46
+ dispose() {
47
+ disposed = true;
48
+ if (interval !== null)
49
+ clearInterval(interval);
50
+ if (timer !== null)
51
+ clearTimeout(timer);
52
+ interval = null;
53
+ timer = null;
54
+ },
55
+ };
56
+ }
57
+ //# sourceMappingURL=refresh-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-scheduler.js","sourceRoot":"","sources":["../../src/lib/refresh-scheduler.ts"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgC;IACrE,MAAM,EACJ,OAAO,EACP,UAAU,EACV,eAAe,GAAG,KAAK,EACvB,QAAQ,GAAG,MAAM,EACjB,GAAG,GAAG,IAAI,CAAC,GAAG,GACf,GAAG,OAAO,CAAC;IAEZ,IAAI,QAAQ,GAA0C,IAAI,CAAC;IAC3D,IAAI,KAAK,GAAyC,IAAI,CAAC;IACvD,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,MAAM,GAAG,GAAG,GAAG,EAAE;QACf,IAAI,QAAQ;YAAE,OAAO;QACrB,aAAa,GAAG,GAAG,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,KAAK,GAAG,eAAe,EAAE,KAAK,GAAG,KAAK,EAAE,EAAE;QAC1D,IAAI,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO;QACvC,MAAM,IAAI,GAAG,KAAK;YAChB,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC;QACxD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,QAAQ;gBAAE,OAAO;YACrB,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,GAAG,aAAa,GAAG,QAAQ,EAAE,CAAC;gBAC/C,QAAQ,CAAC,CAAC,CAAC,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,GAAG,EAAE,CAAC;QACR,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC,CAAC;IAEF,OAAO;QACL,KAAK;YACH,IAAI,QAAQ,IAAI,QAAQ,KAAK,IAAI;gBAAE,OAAO;YAC1C,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;gBAC1B,IAAI,GAAG,EAAE,GAAG,aAAa,IAAI,QAAQ;oBAAE,GAAG,EAAE,CAAC;YAC/C,CAAC,EAAE,UAAU,CAAC,CAAC;QACjB,CAAC;QACD,QAAQ;QACR,OAAO;YACL,QAAQ,GAAG,IAAI,CAAC;YAChB,IAAI,QAAQ,KAAK,IAAI;gBAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,KAAK,KAAK,IAAI;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YACxC,QAAQ,GAAG,IAAI,CAAC;YAChB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -24,9 +24,26 @@ export interface QuotaWindow {
24
24
  used?: number;
25
25
  /** Remaining quota for the window, if exposed by the provider. */
26
26
  remaining?: number;
27
+ /** Money spent in this window, in microcents (1e8 microcents = 1 USD). */
28
+ spentMicroCents?: string;
29
+ /** Money limit for this window, in microcents (1e8 microcents = 1 USD). */
30
+ limitMicroCents?: string;
27
31
  }
28
32
  /** Status of a single provider fetch. */
29
33
  export type ProviderStatus = "ok" | "no-auth" | "error" | "skipped" | "loading";
34
+ /** Prepaid credit balance of an account, when the provider exposes it (OpenCode Go). */
35
+ export interface ProviderBalance {
36
+ /** Available credit in microcents (1e8 microcents = 1 USD), as a decimal string. */
37
+ availableMicroCents: string;
38
+ /** Whether usage may draw on credits once a plan limit is reached; null if unknown. */
39
+ useBalance: boolean | null;
40
+ /** Auto-recharge settings, when readable. */
41
+ autoRecharge?: {
42
+ enabled: boolean;
43
+ thresholdDollars?: number;
44
+ rechargeAmountDollars?: number;
45
+ };
46
+ }
30
47
  /** A single provider's quota result. */
31
48
  export interface ProviderQuota {
32
49
  /** Stable provider id, e.g. "openai", "github-copilot", "opencode-go". */
@@ -41,6 +58,8 @@ export interface ProviderQuota {
41
58
  windows: QuotaWindow[];
42
59
  /** Timestamp the snapshot was taken. */
43
60
  fetchedAt: string;
61
+ /** Credit balance, when the provider exposes it and the lookup succeeded. */
62
+ balance?: ProviderBalance;
44
63
  }
45
64
  /** Aggregate snapshot used by the TUI sidebar and the server tool. */
46
65
  export interface CardsSnapshot {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uEAAuE;AACvE,MAAM,MAAM,QAAQ,GAChB,IAAI,GACJ,QAAQ,GACR,SAAS,GACT,aAAa,GACb,SAAS,CAAC;AAEd,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,wBAAwB;IACxB,EAAE,EAAE,QAAQ,CAAC;IACb,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qGAAqG;IACrG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yCAAyC;AACzC,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,SAAS,GACT,OAAO,GACP,SAAS,GACT,SAAS,CAAC;AAEd,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uEAAuE;AACvE,MAAM,MAAM,QAAQ,GAChB,IAAI,GACJ,QAAQ,GACR,SAAS,GACT,aAAa,GACb,SAAS,CAAC;AAEd,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,wBAAwB;IACxB,EAAE,EAAE,QAAQ,CAAC;IACb,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qGAAqG;IACrG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,yCAAyC;AACzC,MAAM,MAAM,cAAc,GACtB,IAAI,GACJ,SAAS,GACT,OAAO,GACP,SAAS,GACT,SAAS,CAAC;AAEd,wFAAwF;AACxF,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,6CAA6C;IAC7C,YAAY,CAAC,EAAE;QACb,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC;CACH;AAED,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,aAAa,EAAE,CAAC;CAC5B"}