sidekick-agent-hub 0.28.2 → 0.29.0
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/README.md +76 -21
- package/dist/sidekick-cli.mjs +6 -6
- package/dist/sidekick-main.mjs +484 -480
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,16 @@ Sidekick CLI reads from `~/.config/sidekick/` (`%APPDATA%\sidekick\` on Windows)
|
|
|
8
8
|
|
|
9
9
|
## What's New
|
|
10
10
|
|
|
11
|
+
- **0.29.0: honest costs and custom prices**
|
|
12
|
+
- **Cost display:** an unpriced session or billing block reads `—` instead of `$0.0000`. Every report uses one rule: `—` when nothing was priced, `$x (est.)` for catalog estimates, and `$x (partial)` when some calls had no price.
|
|
13
|
+
- **Pricing catalog:** the usage reports and `blocks` name it in their footer (`pricingCatalog` in `--json`). `doctor` warns when only the built-in table or a stale cache is active.
|
|
14
|
+
- **Custom prices:** `cli-config.json` `pricing.models` sets your own per-model prices.
|
|
15
|
+
- **Usage reports:** they add a Reasoning column and merge a model's dated, prefixed and Bedrock spellings into one row.
|
|
16
|
+
- **Sessions:** `sessions --breakdown` prints its sub-rows and tags Codex subagents `↳ subagent of <parent>`.
|
|
17
|
+
- **Prompt costs:** `dump --prompts` shows tokens and cost per prompt (`--no-usage` skips it).
|
|
18
|
+
- **OpenCode:** costs on the OpenAI provider are no longer under-counted.
|
|
19
|
+
- **Usage cache:** it moves to `*.v3.json` and is rebuilt once.
|
|
20
|
+
- **0.28.3: Claude Ultra and the missing 5-hour limit** — on Ultra (formerly Max 20x), which has no 5-hour session limit since 2026-10-30, `sidekick quota` shows `Subscription Quota (plan: Ultra)` with the 7-Day row and a `Session no 5-hour limit on Ultra` note, plus extra meters the endpoint reports (such as `7-Day (Fable)`) and an `Extra usage` row, instead of a fake `5-Hour 0%`. The status line leads with `7d 17% resets Fri 01:00`, the dashboard, `blocks`, and `quota history` follow, and cached quota no longer freezes at the pre-change sample. Codex windows read `Weekly` instead of `Primary` (a missing second window is hidden), and a cache whose windows have all reset, such as a months-old z.ai sample, is reported as expired.
|
|
11
21
|
- **0.28.0: custom Claude homes and archived Codex sessions** — with `CLAUDE_CONFIG_DIR` set, `dashboard`, `dump`, `report`, the usage reports, `blocks`, `search`, and `doctor` read the home Claude Code writes to; Codex sessions moved to `archived_sessions/` stay visible; `dashboard` and `dump` read large or rewritten session files in bounded chunks; and the main bundle shrinks from 2.85 MB to 2.47 MB.
|
|
12
22
|
- **0.27.6: one usable copy of every login** — Sidekick no longer causes Claude Code's "Not logged in · Please run /login": Keychain writes over about 4 KB are no longer truncated, the live login keeps no second copy, `accounts switch` saves the outgoing login first and is journaled so an interrupted switch finishes or rolls back, and keep-alive never refreshes a live copy. Codex switches are refused while Codex sessions run (even with `--force`), and keep-alive refreshes through `codex app-server`. `sidekick accounts doctor` is read-only and reports Claude and Codex credential stores; `--fix` repairs them after asking (`--yes` skips, `--json` lists the repairs).
|
|
13
23
|
- **0.27.5: prompts grouped by session** — `sidekick dump --prompts` prints every prompt you typed in Claude Code and Codex sessions, grouped by session: the most recent session with prompts by default, one session with `--session <id-or-prefix>`, or every session of the project and its git worktrees with `--all` (narrowed by `--since` and `--limit`). `--replies` adds the agent's final reply to each prompt, `--signals` adds interrupts, rejected and failed tool calls, compactions, API errors, and rollbacks, and `--format` writes text, markdown, JSON, or JSONL (one session per line). OpenCode is not supported.
|
|
@@ -22,7 +32,7 @@ Sidekick CLI reads from `~/.config/sidekick/` (`%APPDATA%\sidekick\` on Windows)
|
|
|
22
32
|
- **Usage reports from session logs** — `sidekick daily`, `weekly`, `monthly`, and `sessions` compute tokens and cost for every provider straight from session logs (local calendar days, `--breakdown`, `--by-project`, `--csv`, `--json`), so CLI-only users no longer need the extension's history store; `sidekick import` backfills that store when you want `stats` and `today` to see older sessions.
|
|
23
33
|
- **`sidekick blocks`** — five-hour billing blocks with cache-inclusive totals, cost provenance, burn rate, and end-of-block projections, with the official status-line sample beside the local estimate.
|
|
24
34
|
- **Official quota through the status line** — `sidekick statusline` reads the JSON Claude Code pipes on stdin, appends context %, session cost, and cache hit rate, and persists the official limits; `quota`, `quota --all`, and `mcp get_quota_status` share one resolution order and print a `Source` row naming where the numbers came from.
|
|
25
|
-
- **`state.json`** — a public, versioned snapshot (account, quota with freshness, context including `context.totalTokens`, session cost, active billing block) written by `statusline` and the dashboard for tmux status bars and scripts.
|
|
35
|
+
- **`state.json`** — a public, versioned snapshot (account, quota with freshness, context including `context.totalTokens`, session cost (`null` when nothing was priced), active billing block) written by `statusline` and the dashboard for tmux status bars and scripts.
|
|
26
36
|
- **Scripting flags** — global `--offline` and `--output-file`, `--csv` on `stats`, `dump --list`, and `quota history`, `quota history --window 5h|7d|max`, and `--json` on the quick-capture commands and `report`; every token total is cache-inclusive and every cost carries its provenance.
|
|
27
37
|
- **`sidekick history`** — list your most recent Codex prompts across every workspace, newest first; `--path <id-or-prefix>` resolves a session to its transcript file for `less`/`jq`, and `--json` emits full ids and timestamps. `sidekick dump --list` and the session picker now read a cheap preview index with a `--limit` bound (default 50), so huge session histories list quickly.
|
|
28
38
|
- **`sidekick statusline`, `today` & `doctor`** — a cache-only one-line account/quota/burn footer, a cache-only daily brief, and cross-provider health diagnostics.
|
|
@@ -77,16 +87,16 @@ sidekick tasks add|tasks done|note add|decision add [args]
|
|
|
77
87
|
|
|
78
88
|
Most commands run one-shot queries and print to stdout; `dashboard` opens the full-screen TUI, `accounts` and `extract -i` open interactive pickers, and `mcp` serves stdio until closed. Every command except `statusline` accepts the global flags below.
|
|
79
89
|
|
|
80
|
-
| Flag | Description
|
|
81
|
-
| ---------------------- |
|
|
82
|
-
| `--json` | Machine-readable JSON output (commands that support it)
|
|
83
|
-
| `--project <path>` | Override project path (default: current working directory)
|
|
84
|
-
| `--provider <id>` | Session provider: `claude-code`, `opencode`, `codex`, or `auto` (default)
|
|
85
|
-
| `--no-color` | Disable colored output (also honors `NO_COLOR`)
|
|
86
|
-
| `--offline` | Price from the cached catalog only (also `SIDEKICK_OFFLINE=1`)
|
|
87
|
-
| `--output-file <path>` | Write a command's stdout to a file, without colour codes
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
| Flag | Description |
|
|
91
|
+
| ---------------------- | ------------------------------------------------------------------------- |
|
|
92
|
+
| `--json` | Machine-readable JSON output (commands that support it) |
|
|
93
|
+
| `--project <path>` | Override project path (default: current working directory) |
|
|
94
|
+
| `--provider <id>` | Session provider: `claude-code`, `opencode`, `codex`, or `auto` (default) |
|
|
95
|
+
| `--no-color` | Disable colored output (also honors `NO_COLOR`) |
|
|
96
|
+
| `--offline` | Price from the cached catalog only (also `SIDEKICK_OFFLINE=1`) |
|
|
97
|
+
| `--output-file <path>` | Write a command's stdout to a file, without colour codes |
|
|
98
|
+
|
|
99
|
+
Per-command: `--session <id>` follows a specific session by ID (`dashboard`, `dump`, `report`, `handoff open`), and `--replay` replays existing events from the beginning before streaming live (`dashboard` only).
|
|
90
100
|
|
|
91
101
|
## Daily Brief & Statusline
|
|
92
102
|
|
|
@@ -95,7 +105,7 @@ sidekick today
|
|
|
95
105
|
sidekick statusline
|
|
96
106
|
```
|
|
97
107
|
|
|
98
|
-
`sidekick today` prints a cache-only daily brief: yesterday's sessions/tokens/cost, open tasks, the newest decision, the latest handoff, a quota summary, and the scheduled peak-hours window. `sidekick statusline` renders the same account/quota/burn summary as a single line with no account bootstrap, pricing hydration, or quota network access, so it is fast enough to run on every agent prompt (the VS Code extension can wire it into Claude Code's `statusLine` setting). When Claude Code runs it as the status line it pipes a JSON document on stdin: Sidekick appends context usage, session cost, and prompt-cache hit rate, and persists the official five-hour
|
|
108
|
+
`sidekick today` prints a cache-only daily brief: yesterday's sessions/tokens/cost, open tasks, the newest decision, the latest handoff, a quota summary, and the scheduled peak-hours window. `sidekick statusline` renders the same account/quota/burn summary as a single line with no account bootstrap, pricing hydration, or quota network access, so it is fast enough to run on every agent prompt (the VS Code extension can wire it into Claude Code's `statusLine` setting). When Claude Code runs it as the status line it pipes a JSON document on stdin: Sidekick appends context usage, session cost, and prompt-cache hit rate, and persists the official limits (seven-day, plus five-hour on plans that have one) so every other command sees authoritative quota without a network call. Cached quota older than five minutes shows its age. Global flags `--project`, `--provider`, and `--json` apply to `today`; `statusline` takes no flags (`SIDEKICK_STATUSLINE_STDIN=0` ignores stdin).
|
|
99
109
|
|
|
100
110
|
## Doctor
|
|
101
111
|
|
|
@@ -103,7 +113,7 @@ sidekick statusline
|
|
|
103
113
|
sidekick doctor
|
|
104
114
|
```
|
|
105
115
|
|
|
106
|
-
Diagnose project identity, sessions, accounts, providers, and
|
|
116
|
+
Diagnose project identity, sessions, accounts, providers, dependencies, and the pricing catalog in one typed health report — the same diagnostics behind the VS Code `Sidekick: Run Doctor` command. It warns when only the built-in price table or a stale catalog cache is active, or when `cli-config.json` pricing has invalid entries; `--json` adds `pricingCatalog`. Global flags `--project`, `--provider`, and `--json` also apply.
|
|
107
117
|
|
|
108
118
|
## Billing Blocks
|
|
109
119
|
|
|
@@ -111,7 +121,7 @@ Diagnose project identity, sessions, accounts, providers, and dependencies in on
|
|
|
111
121
|
sidekick blocks [--active | --recent | --since <time>] [--csv] [--no-cache]
|
|
112
122
|
```
|
|
113
123
|
|
|
114
|
-
Five-hour billing blocks computed from session logs (ccusage-style: a block opens at the first usage event, aligned to the UTC hour, lasts five hours, and a longer gap opens a new one). Each block shows its cache-inclusive token total, cost
|
|
124
|
+
Five-hour billing blocks computed from session logs (ccusage-style: a block opens at the first usage event, aligned to the UTC hour, lasts five hours, and a longer gap opens a new one). Each block shows its cache-inclusive token total, cost (`—` when nothing was priced, `$x (est.)` for catalog estimates, `$x (partial)` when some calls had no price), burn rate, and — for the open block — projected end-of-block tokens and cost (`—` for an unpriced block) plus the time remaining. Sessions are read once and cached by size and mtime under `~/.config/sidekick/usage-cache/` (format v3 since 0.29.0, files `*.v3.json`; the first run after upgrading re-reads each session once). When the status line has persisted an official Claude Code rate-limit sample, it is shown beneath the local estimate for comparison (on Ultra, which has no 5-hour limit, it shows `7d N% used · resets in … · no 5-hour limit`). `--since` accepts an ISO date, `YYYY-MM-DD`, or a relative window such as `7d`. The footer names the pricing catalog, for example `Pricing: LiteLLM catalog, fetched 9h ago · fnv1a64:…`; `--json` prints the full report (including `pricingCatalog`) and `--csv` one row per block.
|
|
115
125
|
|
|
116
126
|
## Import History
|
|
117
127
|
|
|
@@ -127,7 +137,37 @@ Fold finished sessions from every provider into the history store that `sidekick
|
|
|
127
137
|
sidekick daily|weekly|monthly|sessions [--since <time>] [--until <time>] [--breakdown] [--by-project] [--utc] [--csv] [--no-cache]
|
|
128
138
|
```
|
|
129
139
|
|
|
130
|
-
Usage computed straight from session logs — no VS Code extension or history store required — for every provider with session data side by side (`--provider` narrows to one). Rows are bucketed by the time of each usage event on the local calendar (`--utc` for UTC), so a session that crosses midnight is split across both days; Claude Code subagent transcripts count toward their parent session; weeks start on Monday.
|
|
140
|
+
Usage computed straight from session logs — no VS Code extension or history store required — for every provider with session data side by side (`--provider` narrows to one). Rows are bucketed by the time of each usage event on the local calendar (`--utc` for UTC), so a session that crosses midnight is split across both days; Claude Code subagent transcripts count toward their parent session; weeks start on Monday. Report options and output:
|
|
141
|
+
|
|
142
|
+
- **Flags:** `--breakdown` adds per-model sub-rows (under each session too, for `sessions`), `--by-project` groups by project, and `--json` / `--csv` feed scripts.
|
|
143
|
+
- **Sessions:** `sessions` prints one row per session and tags Codex subagent and fork sessions `↳ subagent of <parent>`.
|
|
144
|
+
- **Models:** a model's dated, provider-prefixed and Bedrock ids share a row, and `<synthetic>` is left out.
|
|
145
|
+
- **Reasoning:** a Reasoning column appears when any row has reasoning tokens (already in Total), and `--csv` appends a `reasoning` column last.
|
|
146
|
+
- **Costs** follow the `—` / `(est.)` / `(partial)` rule, and the footer names the pricing catalog.
|
|
147
|
+
- **JSON:** `--json` adds `pricingCatalog`, plus `sessionParents` for `sessions`.
|
|
148
|
+
- **Default windows:** 30 days, 12 weeks, 12 calendar months, 30 days.
|
|
149
|
+
|
|
150
|
+
### Custom model prices
|
|
151
|
+
|
|
152
|
+
Prices for models the catalogs don't know (local or private deployments), or negotiated rates, go in `~/.config/sidekick/cli-config.json`:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"pricing": {
|
|
157
|
+
"models": {
|
|
158
|
+
"my-local-model": {
|
|
159
|
+
"inputCostPerMillion": 0.5,
|
|
160
|
+
"outputCostPerMillion": 1.5,
|
|
161
|
+
"cacheReadCostPerMillion": 0.05,
|
|
162
|
+
"cacheWriteCostPerMillion": 0.625
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"onlyIfMissing": false
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Rates are USD per million tokens. A key matches the model's dated, `[1m]`, Bedrock and provider-prefixed spellings. Omitted cache rates are charged at the input rate, which usually overstates cache reads, so set them for accurate costs. By default these prices win over the catalogs. Set `onlyIfMissing: true` to use them only where no catalog has an exact price. Invalid entries are skipped with a warning, and `doctor` lists them.
|
|
131
171
|
|
|
132
172
|
## Quick Capture
|
|
133
173
|
|
|
@@ -186,12 +226,13 @@ Export session data as text, markdown, or JSON. Token lines use "Total (incl. ca
|
|
|
186
226
|
| `--since <time>` | With `--prompts`, sessions active since an ISO date, `YYYY-MM-DD`, or `7d`/`24h` (returned whole) |
|
|
187
227
|
| `--signals` | With `--prompts`, add interrupts, rejected and failed tool calls, compactions, API errors, and rollbacks |
|
|
188
228
|
| `--replies` | With `--prompts`, add the agent's final reply to each prompt |
|
|
229
|
+
| `--no-usage` | With `--prompts`, skip the per-prompt token and cost lookup |
|
|
189
230
|
|
|
190
231
|
Global flags `--project`, `--provider`, and `--json` (the same as `--format json`) also apply.
|
|
191
232
|
|
|
192
233
|
### Prompts by session
|
|
193
234
|
|
|
194
|
-
`sidekick dump --prompts` prints every prompt typed in Claude Code and Codex sessions, grouped by session, for reading or classifying a whole session. The default is the most recent session with prompts, and `--limit <n>` takes the n most recent instead. `--session <id-or-prefix>` picks one session, and `--all` takes every session of the project and its git worktrees (it cannot be combined with `--session`), optionally narrowed by `--since 7d` (sessions active since then, returned whole) and `--limit <n>`. `--signals` adds interrupts, rejected and failed tool calls, compactions, API errors, and rollbacks, and `--replies` adds the agent's final reply to each prompt. `--format` accepts `text`, `markdown`, `json` (full result with bounds and stats), or `jsonl` (one session per line). Both providers are read unless `--provider` names one; `--provider opencode` exits with an error.
|
|
235
|
+
`sidekick dump --prompts` prints every prompt typed in Claude Code and Codex sessions, grouped by session, for reading or classifying a whole session. The default is the most recent session with prompts, and `--limit <n>` takes the n most recent instead. `--session <id-or-prefix>` picks one session, and `--all` takes every session of the project and its git worktrees (it cannot be combined with `--session`), optionally narrowed by `--since 7d` (sessions active since then, returned whole) and `--limit <n>`. `--signals` adds interrupts, rejected and failed tool calls, compactions, API errors, and rollbacks, and `--replies` adds the agent's final reply to each prompt. `--format` accepts `text`, `markdown`, `json` (full result with bounds and stats), or `jsonl` (one session per line). Both providers are read unless `--provider` names one; `--provider opencode` exits with an error. Each prompt shows the tokens and cost of the model calls that answered it (subagent calls included), and each session header its total, for example `#2 21:17:31 · 4.9M tokens · $7.02 (est.)`; JSON adds `promptUsage` / `sessionUsage`. The lookup stops after 30 seconds with a notice, and `--no-usage` skips it.
|
|
195
236
|
|
|
196
237
|
```bash
|
|
197
238
|
sidekick dump --prompts --all --since 7d --signals --replies --format jsonl > sessions.jsonl
|
|
@@ -292,25 +333,39 @@ sidekick quota
|
|
|
292
333
|
|
|
293
334
|
Provider-aware quota and rate-limit display. The command auto-detects the active provider:
|
|
294
335
|
|
|
295
|
-
- **Claude Code**: Shows Claude
|
|
296
|
-
- **Codex**: Fetches the Codex usage API on every command —
|
|
336
|
+
- **Claude Code**: Shows Claude subscription quota (Pro, Max, or Ultra), titled with the plan — the 5-hour window when the plan has one (Ultra shows `Session no 5-hour limit on Ultra` instead), the 7-day window, any extra meters the usage endpoint reports (such as `7-Day (Fable)`), and an `Extra usage` row, with color-coded progress bars, projections, and reset countdowns. Plans with a 5-hour limit also get a peak-hours summary line.
|
|
337
|
+
- **Codex**: Fetches the Codex usage API on every command — the windows the plan reports, named by length (`5-Hour`, `Weekly`), with progress bars, projected end-of-window utilization, and reset countdowns. The output also lists available **reset credits** and each credit's expiration. Session logs and cached samples are fallbacks if the API fails.
|
|
297
338
|
- **OpenCode / z.ai**: OpenCode has no native rate-limit data, but when z.ai Coding Plan credentials are available, `sidekick quota --provider opencode` can auto-route to authoritative z.ai quota (5-Hour / Weekly, with projected end-of-window utilization). Use `--provider zai` to request it explicitly. z.ai quota is read from z.ai's quota API using OpenCode's stored token, with fallback support for `ANTHROPIC_BASE_URL` and `ANTHROPIC_AUTH_TOKEN`.
|
|
298
339
|
|
|
299
340
|
Every provider renders in the same aligned table — a `now` column (current utilization), a `projected` column (estimated end-of-window utilization, or `—` when it can't be computed), and a `resets` countdown:
|
|
300
341
|
|
|
301
342
|
```
|
|
302
|
-
Subscription Quota
|
|
343
|
+
Subscription Quota (plan: Max) now projected resets
|
|
303
344
|
────────────────────────────────────────────────────────────────
|
|
304
345
|
5-Hour ████████████░░░░░░░░░░░░░░░░░░ 40% 100% 2h 15m
|
|
305
346
|
7-Day ██████████████████████░░░░░░░░ 72% 88% 4d 6h
|
|
347
|
+
Source Anthropic usage API
|
|
306
348
|
Peak ● Peak Hours — Limits Drain Faster (off-peak in 2h 45m)
|
|
307
349
|
```
|
|
308
350
|
|
|
351
|
+
On Ultra, which has no 5-hour limit:
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
Subscription Quota (plan: Ultra) now projected resets
|
|
355
|
+
────────────────────────────────────────────────────────────────────
|
|
356
|
+
7-Day █████░░░░░░░░░░░░░░░░░░░░░░░░░ 18% 141% 6d 2h
|
|
357
|
+
7-Day (Fable) ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0% — 6d 2h
|
|
358
|
+
Source Anthropic usage API
|
|
359
|
+
Session no 5-hour limit on Ultra
|
|
360
|
+
Fable may use up to 50% of the weekly limit
|
|
361
|
+
Extra usage $0.00 of $200.00 used · off (out of credits)
|
|
362
|
+
```
|
|
363
|
+
|
|
309
364
|
When quota data is unavailable, `sidekick quota` shows structured auth, rate-limit, network, server, or unexpected-failure messaging instead of a generic raw error. The dashboard Sessions panel also keeps a compact inline quota/rate-limit state visible instead of hiding the section entirely.
|
|
310
365
|
|
|
311
366
|
Codex quota commands and `sidekick mcp`'s `get_quota_status` always ask the API first, even when a fresh local sample exists. If the API fails, the newer session or cached sample is shown; equal capture times favor the cache. The `Source` row identifies the sample and its age, and a `Refresh` row explains a failed Codex API attempt. Claude and z.ai may reuse a persisted sample younger than five minutes; add `--refresh` to ask their APIs first too. Dashboard refresh schedules are unchanged.
|
|
312
367
|
|
|
313
|
-
Use `--json` for machine-readable output; it includes `resolution`, `source`, `capturedSource`, `freshness`, and `ageMs`, plus `failureKind`, `httpStatus`, and `retryAfterMs` on unavailable responses. The `failure` descriptor explains an API failure even when fallback data is available. Claude Code requires active credentials (read from the system Keychain on macOS, or `~/.claude/.credentials.json` on Linux/Windows).
|
|
368
|
+
Use `--json` for machine-readable output; it includes `resolution`, `source`, `capturedSource`, `freshness`, and `ageMs`, plus `failureKind`, `httpStatus`, and `retryAfterMs` on unavailable responses, and for Claude `fiveHour.absent`, `planLabel`, `limits`, and `extraUsage`. A cached sample whose windows have all reset is reported as expired rather than shown. The `failure` descriptor explains an API failure even when fallback data is available. Claude Code requires active credentials (read from the system Keychain on macOS, or `~/.claude/.credentials.json` on Linux/Windows).
|
|
314
369
|
|
|
315
370
|
`sidekick quota` shows the currently logged-in account email above the quota bars — resolved live from the provider's auth, so it stays correct even after a native `claude login` / `codex login`.
|
|
316
371
|
|
|
@@ -332,7 +387,7 @@ Mon ··▒▒▓█▒░· ·░░·· ·▒▓
|
|
|
332
387
|
Peak 92% · Avg 38% · Samples 612
|
|
333
388
|
```
|
|
334
389
|
|
|
335
|
-
Flags: `--weeks <n>` (1-26, default 13), `--provider claude|codex|zai` (`claude-code` and `z.ai` are accepted aliases; default all available, stacked), `--workspace <path>` (default `cwd`), `--window 5h|7d|max` (default `5h`), `--csv`. `--json` emits a `{ workspaceId, weeks, window, providers, generatedAt }` payload — the same shape consumed by the VS Code dashboard's Quota History panel. Cells are local calendar days.
|
|
390
|
+
Flags: `--weeks <n>` (1-26, default 13), `--provider claude|codex|zai` (`claude-code` and `z.ai` are accepted aliases; default all available, stacked), `--workspace <path>` (default `cwd`), `--window 5h|7d|max` (default `5h`, or `7d` when the latest Claude sample has no 5-hour window; the header says so), `--csv`. `--json` emits a `{ workspaceId, weeks, window, providers, generatedAt }` payload — the same shape consumed by the VS Code dashboard's Quota History panel. Cells are local calendar days.
|
|
336
391
|
|
|
337
392
|
History is stored at `~/.config/sidekick/quota-history/<workspaceId>/<provider>.jsonl` (mode `0600`, 60-second debounce, 91-day retention). The workspace id is `sha256(realpath)[0..16]`, so the same folder yields the same store whether sampled from the CLI or VS Code.
|
|
338
393
|
|
package/dist/sidekick-cli.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __createRequire } from "module";
|
|
3
3
|
const require = __createRequire(import.meta.url);
|
|
4
|
-
import*as U from"fs";import*as F from"fs";import*as X from"os";import*as y from"path";import*as I from"fs";import*as K from"path";import*as v from"path";import*as z from"os";var rt="SIDEKICK_CONFIG_DIR",ve=null;function ot(){return process.platform==="win32"?v.join(process.env.APPDATA||z.homedir(),"sidekick"):v.join(z.homedir(),".config","sidekick")}function p(){if(ve)return ve;let e=process.env[rt]?.trim();return e?v.resolve(e):ot()}function we(e){return v.join(p(),e)}import*as J from"crypto";import*as c from"fs";import*as W from"path";var it=1e4,B=3e3,st=12e4,xe=15e3,at=15,Se=new Int32Array(new SharedArrayBuffer(4)),ct=new Set(["EPERM","EBUSY","EACCES"]),ut=10;function dt(e){return Math.min(320,20*2**e)}function lt(e,t){if(process.platform!=="win32"||t>=ut-1)return!1;let n=e.code??"";return ct.has(n)}function ft(e,t){for(let n=0;;n++)try{c.renameSync(e,t);return}catch(r){if(!lt(r,n))throw r;Atomics.wait(Se,0,0,dt(n))}}function pt(e){let t;try{t=c.openSync(e,"r"),c.fsyncSync(t)}catch(n){let r=n.code;if(!["EISDIR","EINVAL","ENOSYS","EPERM","EACCES"].includes(r??""))throw n}finally{t!==void 0&&c.closeSync(t)}}function w(e,t){Q(e,JSON.stringify(t,null,2))}function Q(e,t,n=384){c.mkdirSync(W.dirname(e),{recursive:!0,mode:448});let r=`${e}.${process.pid}.${J.randomBytes(8).toString("hex")}.tmp`,o;try{o=c.openSync(r,"wx",n),c.writeFileSync(o,t,{encoding:"utf8"}),c.fsyncSync(o),c.closeSync(o),o=void 0,ft(r,e),pt(W.dirname(e))}catch(i){throw o!==void 0&&c.closeSync(o),c.rmSync(r,{force:!0}),i}}function ht(e,t){if(t>st)return!1;let n=Number.parseInt((e??"").split(":",1)[0],10);if(!Number.isInteger(n)||n<=0)return!1;try{return process.kill(n,0),!0}catch(r){return r.code==="EPERM"}}function x(e,t){let n=mt(e);try{return t()}finally{try{c.readFileSync(e,"utf8")===n&&c.rmSync(e,{force:!0})}catch{}}}function mt(e){let t=Date.now(),n=t+B,r=null;for(;;)try{let o=c.openSync(e,"wx",384),i=`${process.pid}:${J.randomBytes(16).toString("hex")}`;return c.writeFileSync(o,i,"utf8"),c.fsyncSync(o),c.closeSync(o),i}catch(o){if(o.code!=="EEXIST")throw o;let i=null;try{i=c.readFileSync(e,"utf8")}catch{}i!==null&&i!==r&&(r=i,n=Date.now()+B);try{let a=c.statSync(e),u=Date.now()-a.mtimeMs;u>it&&!ht(i,u)&&c.rmSync(e,{force:!0})}catch{}let s=Date.now();if(s>=n)throw new Error(`Timed out waiting for store lock: ${e} (no progress for ${B}ms)`);if(s-t>=xe)throw new Error(`Timed out waiting for store lock: ${e} (blocked synchronously for ${xe}ms)`);Atomics.wait(Se,0,0,at)}}var gt=new Set;function Ae(e){for(let t of gt)try{t(e)}catch{}}function D(){return K.join(p(),"accounts")}function G(){return K.join(D(),"accounts.json")}function yt(){return G()+".lock"}function vt(){I.mkdirSync(D(),{recursive:!0,mode:448})}function wt(e,t={}){w(G(),be(e)),t.silent||Ae("local")}function Ce(e){return vt(),x(yt(),e)}function xt(e,t={}){return Ce(()=>St(e,t))}function St(e,t={}){let n=e(_()??ke());return wt(n,t),n}function ke(){return{version:2,activeByProvider:{"claude-code":null,codex:null},accounts:[]}}function be(e){let t=ke();return t.activeByProvider["claude-code"]=e.activeByProvider?.["claude-code"]??null,t.activeByProvider.codex=e.activeByProvider?.codex??null,t.accounts=Array.isArray(e.accounts)?e.accounts.filter(n=>!!n&&(n.providerId==="claude-code"||n.providerId==="codex")&&typeof n.id=="string"&&typeof n.addedAt=="string").map(n=>({...n,label:n.label||void 0,email:n.email||void 0,providerAccountId:n.providerAccountId||void 0,metadata:n.metadata?{email:n.metadata.email||void 0,workspaceId:n.metadata.workspaceId||void 0,planType:n.metadata.planType||void 0,authMode:n.metadata.authMode||void 0,origin:n.metadata.origin||void 0}:void 0})):[],t}function At(e){return{version:2,activeByProvider:{"claude-code":e.activeAccountUuid,codex:null},accounts:e.accounts.map(t=>({id:t.uuid,providerId:"claude-code",providerAccountId:t.uuid,email:t.email,label:t.label,addedAt:t.addedAt,metadata:{email:t.email}}))}}function _(){try{let e=I.readFileSync(G(),"utf8"),t=JSON.parse(e);return t?.version===2?be(t):t?.version===1&&Array.isArray(t.accounts)?At(t):null}catch{return null}}function T(e){let t=_();return t?e?t.accounts.filter(n=>n.providerId===e):t.accounts:[]}function S(e){let t=_();if(!t)return null;let n=t.activeByProvider[e];return n?t.accounts.find(r=>r.providerId===e&&r.id===n)??null:null}function E(e,t,n={}){return xt(Ct(e,t),n)}function Ct(e,t){return n=>(n.activeByProvider[e]=t,n)}import*as Ie from"os";import*as De from"path";function V(){return De.join(Ie.homedir(),".claude")}function R(){let e=process.env.CLAUDE_CONFIG_DIR?.trim();return e||V()}function _e(e){try{let t=JSON.parse(F.readFileSync(e,"utf8"));return t?.oauthAccount&&typeof t.oauthAccount=="object"?t.oauthAccount:null}catch{return null}}function Te(e=R()){let t=y.join(e,".claude.json");return _e(t)?t:y.resolve(e)===y.resolve(V())?y.join(X.homedir(),".claude.json"):t}function kt(e){let t=e?.emailAddress,n=e?.accountUuid;return typeof t=="string"&&typeof n=="string"?{email:t,uuid:n}:null}function Ee(e=R()){return kt(_e(Te(e)))}import*as M from"fs";import*as Re from"os";import*as q from"path";function bt(){return q.join(Re.homedir(),".codex")}function It(){let e=process.env.CODEX_HOME?.trim();return e||null}function Fe(){return It()??bt()}function Me(){return Fe()}function Le(e){try{return M.readFileSync(e,"utf8")}catch{return null}}var Dt=11520*60*1e3;function _t(e){let t=e.split(".");if(t.length<2)return null;try{let n=Buffer.from(t[1],"base64url").toString("utf8");return JSON.parse(n)}catch{return null}}function Tt(e){if(!e)return null;try{let t=JSON.parse(e);return t&&typeof t=="object"?t:null}catch{return null}}function Pe(e){let t=Tt(e);if(!t)return null;let n=t.tokens?.id_token,r=n?_t(n):null,o=r?.["https://api.openai.com/profile"],i=r?.["https://api.openai.com/auth"],s=typeof r?.email=="string"?r.email:typeof o?.email=="string"?o.email:void 0,a=typeof i?.chatgpt_account_id=="string"?i.chatgpt_account_id:t.tokens?.account_id,u=typeof i?.chatgpt_plan_type=="string"?i.chatgpt_plan_type:void 0,d=t.OPENAI_API_KEY||t.auth_mode==="api_key"?"api-key":"chatgpt";return{email:s,workspaceId:a,planType:u,authMode:d}}function Rt(){return Ee()}function Ft(e){let t=T("claude-code");return t.find(n=>(n.providerAccountId??n.id)===e.uuid)??t.find(n=>(n.email??n.metadata?.email)===e.email)??null}var Hn=1440*60*1e3;function Mt(){let e=S("claude-code");return e?{uuid:e.providerAccountId??e.id,email:e.email??e.metadata?.email??"unknown",label:e.label,addedAt:e.addedAt}:null}function Oe(e={}){let t=e.selfHeal??!0,n=Rt();if(n){let o=Ft(n);if(o){let i=S("claude-code");if(t&&(!i||i.id!==o.id))try{E("claude-code",o.id,{silent:!0})}catch{}}return{email:n.email,label:o?.label,providerAccountId:n.uuid,registryAccountId:o?.id,source:"live"}}let r=Mt();return r?{email:r.email,label:r.label,providerAccountId:r.uuid,registryAccountId:r.uuid,source:"registry"}:{source:"none"}}import*as te from"path";import*as O from"fs";import*as je from"path";var Ne=5*36e5,$n=7*864e5,Lt=5*6e4,Pt=60*6e4;function L(e){return typeof e!="number"||!Number.isFinite(e)||e<0?"stale":e<Lt?"fresh":e<Pt?"aging":"stale"}function He(e){if(typeof e!="number"||!Number.isFinite(e)||e<0)return"age unknown";let t=Math.floor(e/6e4);if(t<1)return"just now";if(t<60)return`${t}m ago`;let n=Math.floor(t/60);return n<48?`${n}h ago`:`${Math.floor(n/24)}d ago`}function Y(e){return!e||e==="codex"}function ee(){return je.join(p(),"quota-snapshots.json")}function $e(){O.mkdirSync(p(),{recursive:!0,mode:448})}function Ue(){try{let e=JSON.parse(O.readFileSync(ee(),"utf8"));if(e?.version===1&&Array.isArray(e.snapshots))return e}catch{}return{version:1,snapshots:[]}}function Ot(e){$e(),w(ee(),e)}function Nt(e){return $e(),x(`${ee()}.lock`,e)}function Z(e){let t=e.capturedAt?Date.parse(e.capturedAt):NaN;return Number.isFinite(t)?t:0}function P(e){let t=Date.parse(e);return Number.isFinite(t)?t:0}function Ht(e,t){let n=Y(e.limitId),r=Y(t.limitId);if(n!==r)return n;let o=P(e.fiveHour.resetsAt),i=P(t.fiveHour.resetsAt);if(o!==i)return o>i;let s=P(e.sevenDay.resetsAt),a=P(t.sevenDay.resetsAt);if(s!==a)return s>a;let u=e.fiveHour.utilization+e.sevenDay.utilization,d=t.fiveHour.utilization+t.sevenDay.utilization;return u!==d?u>d:Z(e)>Z(t)}function C(e,t,n){Nt(()=>{let r=Ue(),o=r.snapshots.findIndex(h=>h.providerId===e&&h.accountId===t),i=o>=0?r.snapshots[o].quota:void 0,{capturedSource:s,...a}=n;delete a.ageMs,delete a.freshness;let u={...a,providerId:e,capturedAt:n.capturedAt??new Date().toISOString(),source:a.source&&a.source!=="cache"?a.source:s??"session",stale:!1,resetCredits:n.resetCredits??(e==="codex"?i?.resetCredits:void 0)};if(o>=0&&Ht(r.snapshots[o].quota,u))return;let d={providerId:e,accountId:t,quota:u};o>=0?r.snapshots[o]=d:r.snapshots.push(d),Ot(r)})}function N(e,t,n=new Date){let o=Ue().snapshots.find(d=>d.providerId===e&&d.accountId===t);if(!o)return null;let i=Z(o.quota),s=i>0?Math.max(0,n.getTime()-i):void 0,a=o.quota.capturedSource??o.quota.source,u=a&&a!=="cache"?a:void 0;return{...o.quota,providerId:e,source:"cache",...u?{capturedSource:u}:{},stale:!0,...s!==void 0?{ageMs:s}:{},freshness:L(s)}}function jt(){return T("codex")}function ze(){return S("codex")}function Be(e={}){let t=e.selfHeal??!0,n=Pe(Le(te.join(Me(),"auth.json")));if(n&&(n.email||n.workspaceId)){let o=$t(n);if(o){let i=S("codex");if(t&&(!i||i.id!==o.id))try{E("codex",o.id,{silent:!0})}catch{}}return{email:n.email??o?.email??o?.metadata?.email,label:o?.label,providerAccountId:o?.providerAccountId??n.workspaceId,registryAccountId:o?.id,source:"live"}}let r=ze();return r?{email:r.email??r.metadata?.email,label:r.label,providerAccountId:r.providerAccountId,registryAccountId:r.id,source:"registry"}:{source:"none"}}function $t(e,t={}){let n=jt();if(e?.workspaceId){let r=n.find(o=>o.metadata?.workspaceId===e.workspaceId);if(r)return r}if(e?.email){let r=n.find(o=>(o.email??o.metadata?.email)===e.email&&(!e.workspaceId||!o.metadata?.workspaceId));if(r)return r}return!e?.workspaceId&&!e?.email&&t.fallbackToActive!==!1?ze():null}function Ut(e){return e instanceof Error?e.message:String(e)}function ne(e,t={}){try{let n=Oe(t),r=Be(t),o=n.source!=="none"?{present:!0,...n.registryAccountId?{accountId:n.registryAccountId}:{},email:n.email,label:n.label??n.email}:{present:!1},i=r.source!=="none"?{present:!0,...r.registryAccountId?{accountId:r.registryAccountId}:{},email:r.email,label:r.label??r.email??r.providerAccountId}:{present:!1};return{ok:o.present||i.present,claude:o,codex:i,error:e}}catch(n){return{ok:!1,claude:{present:!1},codex:{present:!1},error:e??Ut(n)}}}import*as We from"crypto";import*as l from"fs";import*as k from"path";var zt=6e4,Bt=91,Wt=16*1024,Jt=864e5,H=new Map,re=new Map;function ie(e){let t;try{t=l.realpathSync(e)}catch{t=k.resolve(e)}return We.createHash("sha256").update(t).digest("hex").slice(0,16)}function Qt(e,t){return k.join(p(),"quota-history",e,`${t}.jsonl`)}function Kt(e){l.mkdirSync(k.join(p(),"quota-history",e),{recursive:!0,mode:448})}function oe(e){let t=e.trim();if(!t)return null;try{let n=JSON.parse(t);return n&&typeof n=="object"&&typeof n.timestamp=="string"&&n.fiveHour&&n.sevenDay?n:null}catch{return null}}function Gt(e){let t;try{t=l.openSync(e,"r");let n=l.fstatSync(t);if(n.size===0)return null;let r=4096,o=n.size,i=Buffer.alloc(0);for(;o>0;){let s=Math.min(r,o),a=Buffer.alloc(s);l.readSync(t,a,0,s,o-s),i=Buffer.concat([a,i]),o-=s;let d=i.toString("utf8").replace(/\n+$/,""),h=d.lastIndexOf(`
|
|
5
|
-
`);if(
|
|
6
|
-
`),s=[],a=0;for(let u of i){if(!u.trim())continue;let d=
|
|
4
|
+
import*as B from"fs";import*as F from"fs";import*as Z from"os";import*as w from"path";import*as D from"fs";import*as V from"path";import*as x from"path";import*as Q from"os";var mt="SIDEKICK_CONFIG_DIR",Ae=null;function yt(){return process.platform==="win32"?x.join(process.env.APPDATA||Q.homedir(),"sidekick"):x.join(Q.homedir(),".config","sidekick")}function m(){if(Ae)return Ae;let e=process.env[mt]?.trim();return e?x.resolve(e):yt()}function Ce(e){return x.join(m(),e)}import*as q from"crypto";import*as c from"fs";import*as K from"path";var gt=1e4,J=3e3,wt=12e4,ke=15e3,vt=15,be=new Int32Array(new SharedArrayBuffer(4)),xt=new Set(["EPERM","EBUSY","EACCES"]),St=10;function At(e){return Math.min(320,20*2**e)}function Ct(e,t){if(process.platform!=="win32"||t>=St-1)return!1;let n=e.code??"";return xt.has(n)}function kt(e,t){for(let n=0;;n++)try{c.renameSync(e,t);return}catch(r){if(!Ct(r,n))throw r;Atomics.wait(be,0,0,At(n))}}function bt(e){let t;try{t=c.openSync(e,"r"),c.fsyncSync(t)}catch(n){let r=n.code;if(!["EISDIR","EINVAL","ENOSYS","EPERM","EACCES"].includes(r??""))throw n}finally{t!==void 0&&c.closeSync(t)}}function S(e,t){G(e,JSON.stringify(t,null,2))}function G(e,t,n=384){c.mkdirSync(K.dirname(e),{recursive:!0,mode:448});let r=`${e}.${process.pid}.${q.randomBytes(8).toString("hex")}.tmp`,o;try{o=c.openSync(r,"wx",n),c.writeFileSync(o,t,{encoding:"utf8"}),c.fsyncSync(o),c.closeSync(o),o=void 0,kt(r,e),bt(K.dirname(e))}catch(i){throw o!==void 0&&c.closeSync(o),c.rmSync(r,{force:!0}),i}}function _t(e,t){if(t>wt)return!1;let n=Number.parseInt((e??"").split(":",1)[0],10);if(!Number.isInteger(n)||n<=0)return!1;try{return process.kill(n,0),!0}catch(r){return r.code==="EPERM"}}function A(e,t){let n=It(e);try{return t()}finally{try{c.readFileSync(e,"utf8")===n&&c.rmSync(e,{force:!0})}catch{}}}function It(e){let t=Date.now(),n=t+J,r=null;for(;;)try{let o=c.openSync(e,"wx",384),i=`${process.pid}:${q.randomBytes(16).toString("hex")}`;return c.writeFileSync(o,i,"utf8"),c.fsyncSync(o),c.closeSync(o),i}catch(o){if(o.code!=="EEXIST")throw o;let i=null;try{i=c.readFileSync(e,"utf8")}catch{}i!==null&&i!==r&&(r=i,n=Date.now()+J);try{let a=c.statSync(e),u=Date.now()-a.mtimeMs;u>gt&&!_t(i,u)&&c.rmSync(e,{force:!0})}catch{}let s=Date.now();if(s>=n)throw new Error(`Timed out waiting for store lock: ${e} (no progress for ${J}ms)`);if(s-t>=ke)throw new Error(`Timed out waiting for store lock: ${e} (blocked synchronously for ${ke}ms)`);Atomics.wait(be,0,0,vt)}}var Dt=new Set;function _e(e){for(let t of Dt)try{t(e)}catch{}}function T(){return V.join(m(),"accounts")}function X(){return V.join(T(),"accounts.json")}function Tt(){return X()+".lock"}function Et(){D.mkdirSync(T(),{recursive:!0,mode:448})}function Mt(e,t={}){S(X(),Te(e)),t.silent||_e("local")}function Ie(e){return Et(),A(Tt(),e)}function Rt(e,t={}){return Ie(()=>Lt(e,t))}function Lt(e,t={}){let n=e(E()??De());return Mt(n,t),n}function De(){return{version:2,activeByProvider:{"claude-code":null,codex:null},accounts:[]}}function Te(e){let t=De();return t.activeByProvider["claude-code"]=e.activeByProvider?.["claude-code"]??null,t.activeByProvider.codex=e.activeByProvider?.codex??null,t.accounts=Array.isArray(e.accounts)?e.accounts.filter(n=>!!n&&(n.providerId==="claude-code"||n.providerId==="codex")&&typeof n.id=="string"&&typeof n.addedAt=="string").map(n=>({...n,label:n.label||void 0,email:n.email||void 0,providerAccountId:n.providerAccountId||void 0,metadata:n.metadata?{email:n.metadata.email||void 0,workspaceId:n.metadata.workspaceId||void 0,planType:n.metadata.planType||void 0,authMode:n.metadata.authMode||void 0,origin:n.metadata.origin||void 0}:void 0})):[],t}function Ft(e){return{version:2,activeByProvider:{"claude-code":e.activeAccountUuid,codex:null},accounts:e.accounts.map(t=>({id:t.uuid,providerId:"claude-code",providerAccountId:t.uuid,email:t.email,label:t.label,addedAt:t.addedAt,metadata:{email:t.email}}))}}function E(){try{let e=D.readFileSync(X(),"utf8"),t=JSON.parse(e);return t?.version===2?Te(t):t?.version===1&&Array.isArray(t.accounts)?Ft(t):null}catch{return null}}function M(e){let t=E();return t?e?t.accounts.filter(n=>n.providerId===e):t.accounts:[]}function C(e){let t=E();if(!t)return null;let n=t.activeByProvider[e];return n?t.accounts.find(r=>r.providerId===e&&r.id===n)??null:null}function R(e,t,n={}){return Rt(Pt(e,t),n)}function Pt(e,t){return n=>(n.activeByProvider[e]=t,n)}import*as Ee from"os";import*as Me from"path";function Y(){return Me.join(Ee.homedir(),".claude")}function L(){let e=process.env.CLAUDE_CONFIG_DIR?.trim();return e||Y()}function Re(e){try{let t=JSON.parse(F.readFileSync(e,"utf8"));return t?.oauthAccount&&typeof t.oauthAccount=="object"?t.oauthAccount:null}catch{return null}}function Le(e=L()){let t=w.join(e,".claude.json");return Re(t)?t:w.resolve(e)===w.resolve(Y())?w.join(Z.homedir(),".claude.json"):t}function Ot(e){let t=e?.emailAddress,n=e?.accountUuid;return typeof t=="string"&&typeof n=="string"?{email:t,uuid:n}:null}function Fe(e=L()){return Ot(Re(Le(e)))}import*as P from"fs";import*as Pe from"os";import*as ee from"path";function Nt(){return ee.join(Pe.homedir(),".codex")}function $t(){let e=process.env.CODEX_HOME?.trim();return e||null}function Oe(){return $t()??Nt()}function Ne(){return Oe()}function $e(e){try{return P.readFileSync(e,"utf8")}catch{return null}}var Ht=11520*60*1e3;function jt(e){let t=e.split(".");if(t.length<2)return null;try{let n=Buffer.from(t[1],"base64url").toString("utf8");return JSON.parse(n)}catch{return null}}function Ut(e){if(!e)return null;try{let t=JSON.parse(e);return t&&typeof t=="object"?t:null}catch{return null}}function He(e){let t=Ut(e);if(!t)return null;let n=t.tokens?.id_token,r=n?jt(n):null,o=r?.["https://api.openai.com/profile"],i=r?.["https://api.openai.com/auth"],s=typeof r?.email=="string"?r.email:typeof o?.email=="string"?o.email:void 0,a=typeof i?.chatgpt_account_id=="string"?i.chatgpt_account_id:t.tokens?.account_id,u=typeof i?.chatgpt_plan_type=="string"?i.chatgpt_plan_type:void 0,d=t.OPENAI_API_KEY||t.auth_mode==="api_key"?"api-key":"chatgpt";return{email:s,workspaceId:a,planType:u,authMode:d}}function Wt(){return Fe()}function Bt(e){let t=M("claude-code");return t.find(n=>(n.providerAccountId??n.id)===e.uuid)??t.find(n=>(n.email??n.metadata?.email)===e.email)??null}var er=1440*60*1e3;function Qt(){let e=C("claude-code");return e?{uuid:e.providerAccountId??e.id,email:e.email??e.metadata?.email??"unknown",label:e.label,addedAt:e.addedAt}:null}function je(e={}){let t=e.selfHeal??!0,n=Wt();if(n){let o=Bt(n);if(o){let i=C("claude-code");if(t&&(!i||i.id!==o.id))try{R("claude-code",o.id,{silent:!0})}catch{}}return{email:n.email,label:o?.label,providerAccountId:n.uuid,registryAccountId:o?.id,source:"live"}}let r=Qt();return r?{email:r.email,label:r.label,providerAccountId:r.uuid,registryAccountId:r.uuid,source:"registry"}:{source:"none"}}import*as ie from"path";import*as $ from"fs";import*as Ge from"path";var We=5*36e5,Be=7*864e5,Jt=5*6e4,Kt=60*6e4;function O(e){return typeof e!="number"||!Number.isFinite(e)||e<0?"stale":e<Jt?"fresh":e<Kt?"aging":"stale"}var Ue=36e5,ze=864e5;function Qe(e){return!!e&&e?.absent!==!0}function te(e){return!e||e.absent===!0?!1:!!e.resetsAt||e.utilization>0}function qt(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function Je(e,t){let n=qt(e);return n===void 0?t:n%ze===0?`${n/ze}d`:n%Ue===0?`${n/Ue}h`:n%6e4===0?`${n/6e4}m`:t}function N(e){return(e.limits??[]).filter(t=>t.kind!=="session"&&t.kind!=="weekly_all")}function Ke(e){if(typeof e!="number"||!Number.isFinite(e)||e<0)return"age unknown";let t=Math.floor(e/6e4);if(t<1)return"just now";if(t<60)return`${t}m ago`;let n=Math.floor(t/60);return n<48?`${n}h ago`:`${Math.floor(n/24)}d ago`}function ne(e){return!e||e==="codex"}function oe(){return Ge.join(m(),"quota-snapshots.json")}function Ve(){$.mkdirSync(m(),{recursive:!0,mode:448})}function Xe(){try{let e=JSON.parse($.readFileSync(oe(),"utf8"));if(e?.version===1&&Array.isArray(e.snapshots))return e}catch{}return{version:1,snapshots:[]}}function Gt(e){Ve(),S(oe(),e)}function Vt(e){return Ve(),A(`${oe()}.lock`,e)}function v(e){let t=e.capturedAt?Date.parse(e.capturedAt):NaN;return Number.isFinite(t)?t:0}function re(e){let t=Date.parse(e);return Number.isFinite(t)?t:0}function qe(e,t,n){let r=re(e.resetsAt),o=re(t.resetsAt);if(r!==o)return r>0&&n>0&&r<=n?!1:r>o}function Xt(e,t){return!!e.fiveHour.absent!=!!t.fiveHour.absent||!!e.sevenDay.absent!=!!t.sevenDay.absent}function Yt(e,t){let n=ne(e.limitId),r=ne(t.limitId);if(n!==r)return n;if(Xt(e,t))return v(e)>v(t);let o=v(t),i=qe(e.fiveHour,t.fiveHour,o);if(i!==void 0)return i;let s=qe(e.sevenDay,t.sevenDay,o);if(s!==void 0)return s;let a=e.fiveHour.utilization+e.sevenDay.utilization,u=t.fiveHour.utilization+t.sevenDay.utilization;return a!==u?a>u:v(e)>v(t)}function Zt(e,t){let n={...t};if(n.planLabel===void 0&&e.planLabel!==void 0&&(n.planLabel=e.planLabel),n.planType===void 0&&e.planType!==void 0&&(n.planType=e.planType),n.extraUsage===void 0&&e.extraUsage!==void 0&&(n.extraUsage=e.extraUsage),n.limits===void 0&&e.limits!==void 0){let r=v(t),o=N(e).filter(i=>re(i.resetsAt)>r);o.length>0&&(n.limits=o)}return n}function b(e,t,n){Vt(()=>{let r=Xe(),o=r.snapshots.findIndex(f=>f.providerId===e&&f.accountId===t),i=o>=0?r.snapshots[o].quota:void 0,{capturedSource:s,...a}=n;delete a.ageMs,delete a.freshness;let u={...a,providerId:e,capturedAt:n.capturedAt??new Date().toISOString(),source:a.source&&a.source!=="cache"?a.source:s??"session",stale:!1,resetCredits:n.resetCredits??(e==="codex"?i?.resetCredits:void 0)};if(o>=0&&Yt(r.snapshots[o].quota,u))return;let d={providerId:e,accountId:t,quota:i?Zt(i,u):u};o>=0?r.snapshots[o]=d:r.snapshots.push(d),Gt(r)})}function H(e,t,n=new Date){let o=Xe().snapshots.find(d=>d.providerId===e&&d.accountId===t);if(!o)return null;let i=v(o.quota),s=i>0?Math.max(0,n.getTime()-i):void 0,a=o.quota.capturedSource??o.quota.source,u=a&&a!=="cache"?a:void 0;return{...o.quota,providerId:e,source:"cache",...u?{capturedSource:u}:{},stale:!0,...s!==void 0?{ageMs:s}:{},freshness:O(s)}}function en(){return M("codex")}function Ye(){return C("codex")}function Ze(e={}){let t=e.selfHeal??!0,n=He($e(ie.join(Ne(),"auth.json")));if(n&&(n.email||n.workspaceId)){let o=tn(n);if(o){let i=C("codex");if(t&&(!i||i.id!==o.id))try{R("codex",o.id,{silent:!0})}catch{}}return{email:n.email??o?.email??o?.metadata?.email,label:o?.label,providerAccountId:o?.providerAccountId??n.workspaceId,registryAccountId:o?.id,source:"live"}}let r=Ye();return r?{email:r.email??r.metadata?.email,label:r.label,providerAccountId:r.providerAccountId,registryAccountId:r.id,source:"registry"}:{source:"none"}}function tn(e,t={}){let n=en();if(e?.workspaceId){let r=n.find(o=>o.metadata?.workspaceId===e.workspaceId);if(r)return r}if(e?.email){let r=n.find(o=>(o.email??o.metadata?.email)===e.email&&(!e.workspaceId||!o.metadata?.workspaceId));if(r)return r}return!e?.workspaceId&&!e?.email&&t.fallbackToActive!==!1?Ye():null}function nn(e){return e instanceof Error?e.message:String(e)}function se(e,t={}){try{let n=je(t),r=Ze(t),o=n.source!=="none"?{present:!0,...n.registryAccountId?{accountId:n.registryAccountId}:{},email:n.email,label:n.label??n.email}:{present:!1},i=r.source!=="none"?{present:!0,...r.registryAccountId?{accountId:r.registryAccountId}:{},email:r.email,label:r.label??r.email??r.providerAccountId}:{present:!1};return{ok:o.present||i.present,claude:o,codex:i,error:e}}catch(n){return{ok:!1,claude:{present:!1},codex:{present:!1},error:e??nn(n)}}}import*as et from"crypto";import*as l from"fs";import*as _ from"path";var rn=6e4,on=91,sn=16*1024,an=864e5,j=new Map,ae=new Map;function de(e){let t;try{t=l.realpathSync(e)}catch{t=_.resolve(e)}return et.createHash("sha256").update(t).digest("hex").slice(0,16)}function cn(e,t){return _.join(m(),"quota-history",e,`${t}.jsonl`)}function un(e){l.mkdirSync(_.join(m(),"quota-history",e),{recursive:!0,mode:448})}function ce(e){let t=e.trim();if(!t)return null;try{let n=JSON.parse(t);return n&&typeof n=="object"&&typeof n.timestamp=="string"&&n.fiveHour&&n.sevenDay?n:null}catch{return null}}function dn(e){let t;try{t=l.openSync(e,"r");let n=l.fstatSync(t);if(n.size===0)return null;let r=4096,o=n.size,i=Buffer.alloc(0);for(;o>0;){let s=Math.min(r,o),a=Buffer.alloc(s);l.readSync(t,a,0,s,o-s),i=Buffer.concat([a,i]),o-=s;let d=i.toString("utf8").replace(/\n+$/,""),f=d.lastIndexOf(`
|
|
5
|
+
`);if(f>=0){let h=d.slice(f+1),I=ce(h);return I&&Date.parse(I.timestamp)||null}if(o===0){let h=ce(d);return h&&Date.parse(h.timestamp)||null}}return null}catch(n){return n?.code==="ENOENT",null}finally{if(t!==void 0)try{l.closeSync(t)}catch{}}}function ln(e,t){return A(`${e}.lock`,t)}function fn(e,t){G(e,t,384)}function pn(e,t){let n;try{n=l.statSync(e)}catch{return{kept:0,pruned:0}}if(n.size===0)return{kept:0,pruned:0};let r=Date.now()-t*an,i=l.readFileSync(e,"utf8").split(`
|
|
6
|
+
`),s=[],a=0;for(let u of i){if(!u.trim())continue;let d=ce(u);if(!d){a+=1;continue}let f=Date.parse(d.timestamp);Number.isFinite(f)&&f>=r?s.push(u):a+=1}return a>0&&fn(e,s.length>0?s.join(`
|
|
7
7
|
`)+`
|
|
8
|
-
`:""),{kept:s.length,pruned:a}}function
|
|
9
|
-
`;
|
|
10
|
-
`),
|
|
8
|
+
`:""),{kept:s.length,pruned:a}}function ue(e){return{utilization:e.utilization,resetsAt:e.resetsAt,...e.absent?{absent:!0}:{}}}function U(e){return ue(e)}function hn(e){let t=tt(e.runtimeProvider);return{fiveHour:ue(e.fiveHour),sevenDay:ue(e.sevenDay),available:e.available,error:e.error,providerId:t,source:e.source??"session",capturedAt:e.timestamp,stale:e.stale}}function tt(e){return e==="claude"?"claude-code":e==="zai"?"zai":"codex"}async function mn(e,t,n){let r=ae.get(t);if(r===void 0){let s=dn(t);s!==null&&(r=s,ae.set(t,s))}let o=Date.parse(e.timestamp);if(r!==void 0&&Number.isFinite(o)&&o-r<n.minIntervalMs)return;un(e.workspaceId);let i=JSON.stringify(e)+`
|
|
9
|
+
`;ln(t,()=>{l.appendFileSync(t,i,{encoding:"utf8",mode:384});try{l.statSync(t).size>=sn&&pn(t,n.retentionDays)}catch{}}),ae.set(t,Number.isFinite(o)?o:Date.now());try{let s=tt(e.runtimeProvider);b(s,e.providerId,hn(e))}catch{}}async function le(e,t={}){let n={minIntervalMs:t.minIntervalMs??rn,retentionDays:t.retentionDays??on},r=cn(e.workspaceId,e.runtimeProvider),i=(j.get(r)??Promise.resolve()).then(()=>mn(e,r,n)).catch(()=>{});j.set(r,i);try{await i}finally{j.get(r)===i&&j.delete(r)}}function nt(e,t,n){if(n<=0)return null;let r=t-e;return r<=0?0:r/n}function ot(e){return e.accounts.claude.present?{providerId:"claude-code",accountId:e.accounts.claude.accountId,accountLabel:e.accounts.claude.label??e.accounts.claude.email??"claude",quota:e.claudeQuota??null}:e.accounts.codex.present?{providerId:"codex",accountId:e.accounts.codex.accountId,accountLabel:e.accounts.codex.label??e.accounts.codex.email??"codex",quota:e.codexQuota??null}:null}function pe(e){let t=ot(e),n=e.now??new Date,r=gn(e.live);if(!t)return fe(["acct:none","quota unavailable",...r]);let o=xn(t.accountLabel),i=t.quota;if(!i?.available)return fe([`acct:${o}`,"quota unavailable",...r]);let s=rt(i.fiveHour,"5h",We),a=rt(i.sevenDay,"7d",Be),u=Qe(i.fiveHour)||!te(i.sevenDay),d=u?s:a,f=[`acct:${o}`,yn(d,n)],h=vn(d,i.capturedAt,n);return h!=null&&f.push(`~${An(h)} left`),u&&te(i.sevenDay)&&i.sevenDay.utilization>0&&f.push(`${a.label} ${Math.round(z(i.sevenDay.utilization))}%`),i.freshness&&i.freshness!=="fresh"&&f.push(`(${Ke(i.ageMs)})`),fe([...f,...r])}function rt(e,t,n){let r=typeof e.windowMs=="number"&&Number.isFinite(e.windowMs)&&e.windowMs>0?e.windowMs:n;return{label:Je(e.windowMs,t),window:e,windowMs:r}}function yn(e,t){let n=`${e.label} ${Math.round(z(e.window.utilization))}%`,r=Sn(e.window.resetsAt,t);return r?`${n} resets ${r}`:n}function gn(e){if(!e)return[];let t=[],n=e.contextWindow?.usedPercentage;typeof n=="number"&&t.push(`ctx ${Math.round(z(n))}%`);let r=e.cost?.totalCostUsd;typeof r=="number"&&r>0&&t.push(`$${wn(r)}`);let o=e.promptCache?.hitRatio;return typeof o=="number"&&t.push(`cache ${Math.round(z(o*100))}%`),t}function fe(e){return e.join(" \xB7 ")}function wn(e){return e>=100?e.toFixed(0):e>=10?e.toFixed(1):e.toFixed(2)}function vn(e,t,n){let{window:r,windowMs:o}=e;if(r.absent)return null;let i=Date.parse(r.resetsAt);if(!Number.isFinite(i))return null;let s=n.getTime();if(i<=s)return null;let a=t?Date.parse(t):n.getTime(),u=Number.isFinite(a)?a:n.getTime();if(u>=i)return null;let d=(u-(i-o))/6e4;if(d<=0||r.utilization<=0)return null;let f=r.utilization/d,h=nt(r.utilization,100,f),I=(i-s)/6e4;return h!==null&&h<I?h:null}function xn(e){let t=e.trim();return(t.includes("@")?t.split("@")[0]:t).replace(/\s+/g,"-").slice(0,24)||"unknown"}function z(e){return Number.isFinite(e)?Math.min(100,Math.max(0,e)):0}function Sn(e,t){if(!e)return"";let n=new Date(e);if(Number.isNaN(n.getTime()))return"";let r=new Intl.DateTimeFormat(void 0,{hour:"2-digit",minute:"2-digit",hour12:!1}).format(n);return n.getTime()-t.getTime()<=864e5?r:`${new Intl.DateTimeFormat(void 0,{weekday:"short"}).format(n)} ${r}`}function An(e){let t=Math.max(0,Math.round(e));if(t<60)return`${t}min`;let n=Math.floor(t/60);if(n>=48){let o=Math.floor(n/24),i=n%24;return i?`${o}d${i}h`:`${o}d`}let r=t%60;return r?`${n}h${r}m`:`${n}h`}function g(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function p(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function he(e){return e===null?null:p(e)}function y(e){return typeof e=="string"&&e.length>0?e:void 0}function it(e){return typeof e=="boolean"?e:void 0}function me(e){if(!g(e))return;let t=p(e.used_percentage),n=p(e.resets_at);if(!(t===void 0||n===void 0))return{usedPercentage:t,resetsAt:n}}function k(e){return Object.values(e).some(t=>t!==void 0)?e:void 0}function ye(e){if(typeof e!="string"||!e.trim())return null;let t;try{t=JSON.parse(e)}catch{return null}if(!g(t))return null;let n=g(t.model)?t.model:void 0,r=g(t.workspace)?t.workspace:void 0,o=g(t.context_window)?t.context_window:void 0,i=g(t.cost)?t.cost:void 0,s=g(t.rate_limits)?t.rate_limits:void 0,a=g(t.prompt_cache)?t.prompt_cache:void 0;return{sessionId:y(t.session_id),transcriptPath:y(t.transcript_path),cwd:y(t.cwd),model:n?k({id:y(n.id),displayName:y(n.display_name)}):void 0,workspace:r?k({currentDir:y(r.current_dir),projectDir:y(r.project_dir),gitWorktree:y(r.git_worktree)}):void 0,contextWindow:o?k({usedPercentage:he(o.used_percentage),remainingPercentage:he(o.remaining_percentage),contextWindowSize:p(o.context_window_size),totalInputTokens:p(o.total_input_tokens),totalOutputTokens:p(o.total_output_tokens)}):void 0,cost:i?k({totalCostUsd:p(i.total_cost_usd),totalDurationMs:p(i.total_duration_ms),totalApiDurationMs:p(i.total_api_duration_ms),totalLinesAdded:p(i.total_lines_added),totalLinesRemoved:p(i.total_lines_removed)}):void 0,rateLimits:s?k({fiveHour:me(s.five_hour),sevenDay:me(s.seven_day),spendLimit:me(s.spend_limit)}):void 0,promptCache:a?k({warm:it(a.warm),cachingObserved:it(a.caching_observed),hitRatio:he(a.hit_ratio),requests:p(a.requests),misses:p(a.misses),expectedRebuilds:p(a.expected_rebuilds)}):void 0,raw:t}}function st(e,t,n){if(e)return{utilization:Math.max(0,e.usedPercentage),resetsAt:new Date(e.resetsAt*1e3).toISOString()};if(t?.absent)return{utilization:0,resetsAt:"",absent:!0};let r=t?Date.parse(t.resetsAt):NaN;return t&&Number.isFinite(r)&&r>n?t:{utilization:0,resetsAt:""}}function ge(e,t={}){let n=e.rateLimits;if(!n?.fiveHour&&!n?.sevenDay)return null;let r=t.now??new Date,o=t.fallback??void 0;return{fiveHour:st(n.fiveHour,o?.fiveHour,r.getTime()),sevenDay:st(n.sevenDay,o?.sevenDay,r.getTime()),available:!0,providerId:"claude-code",source:"statusline",capturedAt:r.toISOString(),stale:!1}}import*as ut from"fs";var ve=1,dt="state.json";function xe(){return Ce(dt)}function lt(e=xe()){try{let t=JSON.parse(ut.readFileSync(e,"utf8"));return t&&t.schemaVersion===ve?t:null}catch{return null}}function we(e){if(Array.isArray(e))return`[${e.map(we).join(",")}]`;if(e&&typeof e=="object"){let t=e;return`{${Object.keys(t).sort().filter(r=>t[r]!==void 0).map(r=>`${JSON.stringify(r)}:${we(t[r])}`).join(",")}}`}return JSON.stringify(e)??"null"}function at(e){return we({...e,schemaVersion:void 0,writtenAt:void 0})}function Se(e,t={}){let n=t.filePath??xe();try{let r=lt(n);if(r&&at(r)===at(e))return!1;let o={schemaVersion:ve,writtenAt:(t.now??new Date).toISOString(),...e};return S(n,o),!0}catch{return!1}}function W(e,t=Date.now()){if(!e||!e.available)return null;let n=typeof e.ageMs=="number"?e.ageMs:null,r=e.freshness??null;if(n===null&&e.capturedAt){let i=Date.parse(e.capturedAt);Number.isFinite(i)&&(n=Math.max(0,t-i),r??=O(n))}let o=N(e).map(i=>({kind:i.kind,label:i.label,utilization:i.utilization,resetsAt:i.resetsAt}));return{fiveHour:ct(e.fiveHour),sevenDay:ct(e.sevenDay),source:e.source??null,capturedSource:e.capturedSource??null,capturedAt:e.capturedAt??null,ageMs:n,freshness:r,...e.planLabel?{planLabel:e.planLabel}:{},...o.length>0?{extraLimits:o}:{}}}function ct(e){return{utilization:e.utilization,resetsAt:e.resetsAt,...e.absent?{absent:!0}:{}}}function Cn(e=process.env){if(e.SIDEKICK_STATUSLINE_STDIN==="0")return null;try{if(process.stdin.isTTY)return null;let t=B.fstatSync(0);return t.isFIFO()||t.isFile()||t.isSocket()?ye(B.readFileSync(0,"utf8")):null}catch{return null}}function kn(e,t){return e?!!e.fiveHour.absent==!!t.fiveHour.absent&&e.fiveHour.utilization===t.fiveHour.utilization&&e.fiveHour.resetsAt===t.fiveHour.resetsAt&&e.sevenDay.utilization===t.sevenDay.utilization&&e.sevenDay.resetsAt===t.sevenDay.resetsAt:!1}async function bn(e,t,n,r){if(!kn(n,t)){try{b("claude-code",e,t)}catch{}try{let o=r.workspace?.currentDir??r.cwd??process.cwd();await le({timestamp:t.capturedAt??new Date().toISOString(),runtimeProvider:"claude",providerId:e,workspaceId:de(o),fiveHour:U(t.fiveHour),sevenDay:U(t.sevenDay),available:!0,source:"statusline",stale:!1})}catch{}}}function _n(e,t,n,r){let o=e.claude.present?{providerId:"claude-code",id:e.claude.accountId??null,label:e.claude.label??null}:e.codex.present?{providerId:"codex",id:e.codex.accountId??null,label:e.codex.label??null}:null,i=r?.contextWindow?{usedPercentage:r.contextWindow.usedPercentage??null,contextWindowSize:r.contextWindow.contextWindowSize??null,totalInputTokens:r.contextWindow.totalInputTokens??null,totalOutputTokens:r.contextWindow.totalOutputTokens??null}:null,s=r?{sessionId:r.sessionId??null,cwd:r.workspace?.currentDir??r.cwd??null,model:r.model?.id??null,costUsd:r.cost?.totalCostUsd??null,durationMs:r.cost?.totalDurationMs??null,linesAdded:r.cost?.totalLinesAdded??null,linesRemoved:r.cost?.totalLinesRemoved??null,promptCacheHitRatio:r.promptCache?.hitRatio??null}:null;return{writer:"statusline",account:o,quota:{claude:W(t),codex:W(n)},context:i,session:s,billingBlock:null}}async function ft(){let e=Cn(),t=se(void 0,{selfHeal:!1}),n=t.claude.accountId,r=n?H("claude-code",n):null,o=r,i=t.codex.accountId?H("codex",t.codex.accountId):null,s=null;if(e){let a=ge(e,{fallback:r});a&&(o=a,n&&(s=bn(n,a,r,e)))}process.stdout.write(`${pe({accounts:t,claudeQuota:o,codexQuota:i,live:e})}
|
|
10
|
+
`),Se(_n(t,o,i,e)),s&&await s}var pt=["--project","--provider","--output-file"],In=["--json","--no-color","--offline"];function Dn(e){for(let t=0;t<e.length;t++){let n=e[t];if(!In.includes(n)){if(pt.includes(n)){t++;continue}if(!pt.some(r=>n.startsWith(`${r}=`)))return n}}}function ht(e){return Dn(e)==="statusline"}ht(process.argv.slice(2))?ft():await import("./sidekick-main.mjs");
|