quotacap 0.0.23 → 0.0.25
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 +29 -6
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/muse.d.ts +8 -0
- package/dist/adapters/muse.js +96 -0
- package/dist/adapters/pty.d.ts +35 -0
- package/dist/adapters/pty.js +282 -46
- package/dist/advisory/provider-names.d.ts +14 -0
- package/dist/advisory/provider-names.js +98 -0
- package/dist/advisory/snapshot.d.ts +1 -0
- package/dist/advisory/snapshot.js +7 -0
- package/dist/advisory/types.d.ts +5 -0
- package/dist/advisory/validation.d.ts +9 -0
- package/dist/advisory/validation.js +39 -0
- package/dist/cli/advise.js +2 -1
- package/dist/cli/clients.js +2 -0
- package/dist/cli/providers.d.ts +3 -0
- package/dist/cli/providers.js +174 -0
- package/dist/cli/snapshot-source.d.ts +3 -1
- package/dist/cli/snapshot-source.js +44 -5
- package/dist/cli/status.js +8 -0
- package/dist/cli/takeover.d.ts +1 -0
- package/dist/cli/takeover.js +3 -2
- package/dist/cli/update.js +5 -2
- package/dist/config.d.ts +36 -0
- package/dist/config.js +180 -1
- package/dist/diagnostics/failure.d.ts +26 -0
- package/dist/diagnostics/failure.js +417 -0
- package/dist/format/issues.d.ts +2 -0
- package/dist/format/issues.js +17 -0
- package/dist/format/markdown.js +1 -1
- package/dist/format/rows.js +2 -0
- package/dist/format/terminal.js +6 -6
- package/dist/http/server.d.ts +2 -0
- package/dist/http/server.js +75 -2
- package/dist/mcp/server.js +2 -0
- package/dist/runtime/client.d.ts +1 -0
- package/dist/runtime/client.js +21 -1
- package/dist/runtime/poll.d.ts +12 -4
- package/dist/runtime/poll.js +49 -39
- package/dist/runtime/service.js +18 -5
- package/dist/runtime/spawn.js +14 -4
- package/dist/service/index.js +4 -1
- package/dist/service/macos.d.ts +11 -0
- package/dist/service/macos.js +35 -4
- package/dist/service/systemd.d.ts +2 -0
- package/dist/service/systemd.js +12 -4
- package/dist/store/attempts.d.ts +6 -0
- package/dist/store/attempts.js +27 -20
- package/dist/store/db.js +6 -1
- package/dist/version.js +1 -1
- package/dist/web/src/App.js +1 -1
- package/dist/web/src/api.d.ts +2 -0
- package/dist/web/src/api.js +20 -0
- package/dist/web/src/components/AdviceDrawer.js +3 -4
- package/dist/web/src/components/FaultBanner.js +2 -3
- package/dist/web/src/components/ProviderCard.js +1 -2
- package/dist/web/src/components/ProviderDrawer.d.ts +8 -1
- package/dist/web/src/components/ProviderDrawer.js +103 -7
- package/dist/web/src/components/ProviderIcon.js +1 -0
- package/dist/web/src/components/ProviderRow.js +1 -2
- package/dist/web/src/components/Recommendation.js +2 -3
- package/dist/web/src/components/ResetRail.js +5 -6
- package/dist/web/src/components/SubscriptionList.js +1 -2
- package/dist/web/src/components/settings/ProvidersPanel.js +1 -2
- package/dist/web/src/pages/Onboarding.js +4 -5
- package/dist/web/src/state.d.ts +11 -0
- package/dist/web/src/state.js +18 -0
- package/dist/webAssets.js +1 -1
- package/dist/webHtml.js +1 -1
- package/package.json +3 -1
- package/web/dist/assets/index-ClNhsr1-.js +12 -0
- package/web/dist/assets/index-TJk_KDbS.css +1 -0
- package/web/dist/index.html +2 -2
- package/dist/src/adapters/agy.d.ts +0 -3
- package/dist/src/adapters/agy.js +0 -81
- package/dist/src/adapters/claude.d.ts +0 -9
- package/dist/src/adapters/claude.js +0 -30
- package/dist/src/adapters/codex.d.ts +0 -7
- package/dist/src/adapters/codex.js +0 -140
- package/dist/src/adapters/grok.d.ts +0 -7
- package/dist/src/adapters/grok.js +0 -126
- package/dist/src/adapters/index.d.ts +0 -21
- package/dist/src/adapters/index.js +0 -66
- package/dist/src/adapters/kimi.d.ts +0 -7
- package/dist/src/adapters/kimi.js +0 -76
- package/dist/src/adapters/manual.d.ts +0 -8
- package/dist/src/adapters/manual.js +0 -28
- package/dist/src/adapters/parse.d.ts +0 -1
- package/dist/src/adapters/parse.js +0 -81
- package/dist/src/adapters/pty.d.ts +0 -41
- package/dist/src/adapters/pty.js +0 -579
- package/dist/src/adapters/types.d.ts +0 -21
- package/dist/src/adapters/types.js +0 -1
- package/dist/src/advisory/engine.d.ts +0 -28
- package/dist/src/advisory/engine.js +0 -149
- package/dist/src/advisory/snapshot.d.ts +0 -22
- package/dist/src/advisory/snapshot.js +0 -213
- package/dist/src/advisory/types.d.ts +0 -63
- package/dist/src/advisory/types.js +0 -1
- package/dist/src/advisory/validate.d.ts +0 -5
- package/dist/src/advisory/validate.js +0 -18
- package/dist/src/cli/advise.d.ts +0 -3
- package/dist/src/cli/advise.js +0 -128
- package/dist/src/cli/clients.d.ts +0 -28
- package/dist/src/cli/clients.js +0 -11
- package/dist/src/cli/index.d.ts +0 -2
- package/dist/src/cli/index.js +0 -27
- package/dist/src/cli/ingest.d.ts +0 -3
- package/dist/src/cli/ingest.js +0 -31
- package/dist/src/cli/init.d.ts +0 -2
- package/dist/src/cli/init.js +0 -29
- package/dist/src/cli/runtime.d.ts +0 -33
- package/dist/src/cli/runtime.js +0 -252
- package/dist/src/cli/snapshot-source.d.ts +0 -26
- package/dist/src/cli/snapshot-source.js +0 -232
- package/dist/src/cli/status.d.ts +0 -3
- package/dist/src/cli/status.js +0 -150
- package/dist/src/cli/takeover.d.ts +0 -64
- package/dist/src/cli/takeover.js +0 -175
- package/dist/src/cli/update.d.ts +0 -61
- package/dist/src/cli/update.js +0 -392
- package/dist/src/config.d.ts +0 -47
- package/dist/src/config.js +0 -185
- package/dist/src/daemon.d.ts +0 -4
- package/dist/src/daemon.js +0 -15
- package/dist/src/format/markdown.d.ts +0 -3
- package/dist/src/format/markdown.js +0 -24
- package/dist/src/format/rows.d.ts +0 -27
- package/dist/src/format/rows.js +0 -241
- package/dist/src/format/terminal.d.ts +0 -12
- package/dist/src/format/terminal.js +0 -144
- package/dist/src/http/server.d.ts +0 -33
- package/dist/src/http/server.js +0 -314
- package/dist/src/mcp/server.d.ts +0 -46
- package/dist/src/mcp/server.js +0 -163
- package/dist/src/runtime/client.d.ts +0 -24
- package/dist/src/runtime/client.js +0 -72
- package/dist/src/runtime/owner.d.ts +0 -35
- package/dist/src/runtime/owner.js +0 -393
- package/dist/src/runtime/poll.d.ts +0 -50
- package/dist/src/runtime/poll.js +0 -229
- package/dist/src/runtime/service.d.ts +0 -24
- package/dist/src/runtime/service.js +0 -268
- package/dist/src/runtime/spawn.d.ts +0 -17
- package/dist/src/runtime/spawn.js +0 -97
- package/dist/src/runtime/token.d.ts +0 -3
- package/dist/src/runtime/token.js +0 -64
- package/dist/src/runtime/updates.d.ts +0 -74
- package/dist/src/runtime/updates.js +0 -445
- package/dist/src/runtime/versions.d.ts +0 -2
- package/dist/src/runtime/versions.js +0 -26
- package/dist/src/service/index.d.ts +0 -9
- package/dist/src/service/index.js +0 -103
- package/dist/src/service/macos.d.ts +0 -100
- package/dist/src/service/macos.js +0 -602
- package/dist/src/service/systemd.d.ts +0 -40
- package/dist/src/service/systemd.js +0 -387
- package/dist/src/store/attempts.d.ts +0 -12
- package/dist/src/store/attempts.js +0 -29
- package/dist/src/store/db.d.ts +0 -2
- package/dist/src/store/db.js +0 -74
- package/dist/src/store/quotas.d.ts +0 -6
- package/dist/src/store/quotas.js +0 -98
- package/dist/src/version.d.ts +0 -1
- package/dist/src/version.js +0 -2
- package/dist/src/webAssets.d.ts +0 -1
- package/dist/src/webAssets.js +0 -2
- package/dist/src/webHtml.d.ts +0 -1
- package/dist/src/webHtml.js +0 -2
- package/dist/tests/adapters/agy.test.d.ts +0 -1
- package/dist/tests/adapters/agy.test.js +0 -337
- package/dist/tests/adapters/claude.test.d.ts +0 -1
- package/dist/tests/adapters/claude.test.js +0 -83
- package/dist/tests/adapters/codex.test.d.ts +0 -1
- package/dist/tests/adapters/codex.test.js +0 -246
- package/dist/tests/adapters/credential-free.test.d.ts +0 -1
- package/dist/tests/adapters/credential-free.test.js +0 -211
- package/dist/tests/adapters/grok.test.d.ts +0 -1
- package/dist/tests/adapters/grok.test.js +0 -269
- package/dist/tests/adapters/kimi.test.d.ts +0 -1
- package/dist/tests/adapters/kimi.test.js +0 -235
- package/dist/tests/adapters/manual.test.d.ts +0 -1
- package/dist/tests/adapters/manual.test.js +0 -28
- package/dist/tests/adapters/pty-perms.test.d.ts +0 -1
- package/dist/tests/adapters/pty-perms.test.js +0 -78
- package/dist/tests/adapters/pty.test.d.ts +0 -1
- package/dist/tests/adapters/pty.test.js +0 -240
- package/dist/tests/adapters/registry.test.d.ts +0 -1
- package/dist/tests/adapters/registry.test.js +0 -25
- package/dist/tests/advisory/engine.test.d.ts +0 -1
- package/dist/tests/advisory/engine.test.js +0 -222
- package/dist/tests/advisory/pace.test.d.ts +0 -1
- package/dist/tests/advisory/pace.test.js +0 -60
- package/dist/tests/advisory/snapshot.test.d.ts +0 -1
- package/dist/tests/advisory/snapshot.test.js +0 -65
- package/dist/tests/advisory/validate.test.d.ts +0 -1
- package/dist/tests/advisory/validate.test.js +0 -19
- package/dist/tests/bootstrap.test.d.ts +0 -1
- package/dist/tests/bootstrap.test.js +0 -14
- package/dist/tests/cli/advise.test.d.ts +0 -1
- package/dist/tests/cli/advise.test.js +0 -367
- package/dist/tests/cli/cli.test.d.ts +0 -1
- package/dist/tests/cli/cli.test.js +0 -46
- package/dist/tests/cli/helpers.d.ts +0 -9
- package/dist/tests/cli/helpers.js +0 -93
- package/dist/tests/cli/ingest.test.d.ts +0 -1
- package/dist/tests/cli/ingest.test.js +0 -188
- package/dist/tests/cli/init.test.d.ts +0 -1
- package/dist/tests/cli/init.test.js +0 -55
- package/dist/tests/cli/launcher.test.d.ts +0 -1
- package/dist/tests/cli/launcher.test.js +0 -271
- package/dist/tests/cli/parity.test.d.ts +0 -1
- package/dist/tests/cli/parity.test.js +0 -225
- package/dist/tests/cli/snapshot-source.test.d.ts +0 -1
- package/dist/tests/cli/snapshot-source.test.js +0 -305
- package/dist/tests/cli/status.test.d.ts +0 -1
- package/dist/tests/cli/status.test.js +0 -536
- package/dist/tests/cli/takeover.test.d.ts +0 -1
- package/dist/tests/cli/takeover.test.js +0 -325
- package/dist/tests/cli/update.test.d.ts +0 -1
- package/dist/tests/cli/update.test.js +0 -382
- package/dist/tests/daemon.test.d.ts +0 -1
- package/dist/tests/daemon.test.js +0 -164
- package/dist/tests/fixtures/stable-state.d.ts +0 -22
- package/dist/tests/fixtures/stable-state.js +0 -152
- package/dist/tests/fixtures/stable-state.test.d.ts +0 -1
- package/dist/tests/fixtures/stable-state.test.js +0 -17
- package/dist/tests/format/markdown.test.d.ts +0 -1
- package/dist/tests/format/markdown.test.js +0 -49
- package/dist/tests/format/terminal.test.d.ts +0 -1
- package/dist/tests/format/terminal.test.js +0 -476
- package/dist/tests/http/api.test.d.ts +0 -1
- package/dist/tests/http/api.test.js +0 -236
- package/dist/tests/integration.test.d.ts +0 -1
- package/dist/tests/integration.test.js +0 -61
- package/dist/tests/mcp/protocol.test.d.ts +0 -1
- package/dist/tests/mcp/protocol.test.js +0 -134
- package/dist/tests/mcp/quotas.test.d.ts +0 -1
- package/dist/tests/mcp/quotas.test.js +0 -56
- package/dist/tests/mcp/server.test.d.ts +0 -1
- package/dist/tests/mcp/server.test.js +0 -7
- package/dist/tests/mcp/tools.test.d.ts +0 -1
- package/dist/tests/mcp/tools.test.js +0 -291
- package/dist/tests/runtime/cancel.test.d.ts +0 -1
- package/dist/tests/runtime/cancel.test.js +0 -264
- package/dist/tests/runtime/cli.test.d.ts +0 -1
- package/dist/tests/runtime/cli.test.js +0 -426
- package/dist/tests/runtime/config.test.d.ts +0 -1
- package/dist/tests/runtime/config.test.js +0 -144
- package/dist/tests/runtime/http.test.d.ts +0 -1
- package/dist/tests/runtime/http.test.js +0 -346
- package/dist/tests/runtime/lifecycle.test.d.ts +0 -1
- package/dist/tests/runtime/lifecycle.test.js +0 -303
- package/dist/tests/runtime/ownership.test.d.ts +0 -1
- package/dist/tests/runtime/ownership.test.js +0 -355
- package/dist/tests/runtime/poll.test.d.ts +0 -1
- package/dist/tests/runtime/poll.test.js +0 -438
- package/dist/tests/runtime/status.test.d.ts +0 -1
- package/dist/tests/runtime/status.test.js +0 -232
- package/dist/tests/runtime/updates.test.d.ts +0 -1
- package/dist/tests/runtime/updates.test.js +0 -509
- package/dist/tests/service/macos.test.d.ts +0 -1
- package/dist/tests/service/macos.test.js +0 -450
- package/dist/tests/service/systemd.test.d.ts +0 -1
- package/dist/tests/service/systemd.test.js +0 -330
- package/dist/tests/smoke/dashboard.e2e.d.ts +0 -1
- package/dist/tests/smoke/dashboard.e2e.js +0 -142
- package/dist/tests/smoke/playwright.config.d.ts +0 -2
- package/dist/tests/smoke/playwright.config.js +0 -11
- package/dist/tests/store/attempts.test.d.ts +0 -1
- package/dist/tests/store/attempts.test.js +0 -29
- package/dist/tests/store/db.test.d.ts +0 -1
- package/dist/tests/store/db.test.js +0 -96
- package/dist/tests/store/migration.test.d.ts +0 -1
- package/dist/tests/store/migration.test.js +0 -41
- package/dist/tests/web/advice-drawer.test.d.ts +0 -1
- package/dist/tests/web/advice-drawer.test.js +0 -45
- package/dist/tests/web/build.test.d.ts +0 -1
- package/dist/tests/web/build.test.js +0 -8
- package/dist/tests/web/dashboard.e2e.d.ts +0 -1
- package/dist/tests/web/dashboard.e2e.js +0 -670
- package/dist/tests/web/header.test.d.ts +0 -1
- package/dist/tests/web/header.test.js +0 -82
- package/dist/tests/web/names.test.d.ts +0 -1
- package/dist/tests/web/names.test.js +0 -19
- package/dist/tests/web/onboarding.test.d.ts +0 -1
- package/dist/tests/web/onboarding.test.js +0 -21
- package/dist/tests/web/pacing.test.d.ts +0 -1
- package/dist/tests/web/pacing.test.js +0 -62
- package/dist/tests/web/playwright.config.d.ts +0 -2
- package/dist/tests/web/playwright.config.js +0 -10
- package/dist/tests/web/provider-drawer.test.d.ts +0 -1
- package/dist/tests/web/provider-drawer.test.js +0 -68
- package/dist/tests/web/recommendation.test.d.ts +0 -1
- package/dist/tests/web/recommendation.test.js +0 -38
- package/dist/tests/web/reset-rail.test.d.ts +0 -1
- package/dist/tests/web/reset-rail.test.js +0 -63
- package/dist/tests/web/responsive.test.d.ts +0 -1
- package/dist/tests/web/responsive.test.js +0 -21
- package/dist/tests/web/settings.test.d.ts +0 -1
- package/dist/tests/web/settings.test.js +0 -32
- package/dist/tests/web/state-catalog.test.d.ts +0 -1
- package/dist/tests/web/state-catalog.test.js +0 -206
- package/dist/tests/web/state.test.d.ts +0 -1
- package/dist/tests/web/state.test.js +0 -27
- package/dist/tests/web/stub-server.d.ts +0 -37
- package/dist/tests/web/stub-server.js +0 -132
- package/dist/tests/web/theme.test.d.ts +0 -1
- package/dist/tests/web/theme.test.js +0 -12
- package/dist/web/src/names.d.ts +0 -3
- package/dist/web/src/names.js +0 -19
- package/web/dist/assets/index-BEBqgvq8.css +0 -1
- package/web/dist/assets/index-DZfttQgo.js +0 -12
package/README.md
CHANGED
|
@@ -19,10 +19,11 @@ QuotaCap helps you get more from the AI coding subscriptions you already pay for
|
|
|
19
19
|
| Provider | Mechanism | Source |
|
|
20
20
|
|---|---|---|
|
|
21
21
|
| Claude Code | `exec` — `claude -p /usage --output-format json` | Live |
|
|
22
|
-
| Antigravity | `exec` — `agy -p /usage --output-format json` (two rows:
|
|
22
|
+
| Antigravity | `exec` — `agy -p /usage --output-format json` (two rows: Antigravity for Gemini, Antigravity 3P for third-party models) | Live |
|
|
23
23
|
| Codex | `pty` — `codex --no-alt-screen` then `/status`, parse `Weekly/5h limit: X% left` | Live |
|
|
24
24
|
| Kimi Code | `pty` — `kimi` then `/usage`, parse `Weekly/5h limit: Y% used` | Live |
|
|
25
25
|
| Grok | `pty` — `grok` then `/usage`, parse `Weekly limit (plan)` + `Credits: $X` | Live |
|
|
26
|
+
| Muse Code | `pty` — `muse --trust-workspace` then `/usage`, parse `Subscription · Muse Code <plan>` + `Weekly/Current N% used` | Live |
|
|
26
27
|
|
|
27
28
|
Exec adapters run via `execFile` with an argv list. PTY adapters run via `node-pty` (`src/adapters/pty.ts`). They are TUI-fragile: a vendor text change breaks the parser and the row degrades fail-closed until the regex is fixed. Poll latency is 2–10 s per PTY provider (settle plus completion). It dominates `POST /api/refresh` and the first poll, not the steady-state 15 m timer.
|
|
28
29
|
|
|
@@ -46,12 +47,15 @@ The npm package runs the same CLI on Node. To run without a global install, repl
|
|
|
46
47
|
|
|
47
48
|
```bash
|
|
48
49
|
quotacap # opens the dashboard, starting the service if needed
|
|
49
|
-
quotacap status
|
|
50
|
+
quotacap status # summary table (pass --verbose for adapter failure guidance)
|
|
50
51
|
quotacap advise
|
|
51
52
|
quotacap update # upgrade to the latest release
|
|
53
|
+
quotacap providers list # view built-in and effective provider names
|
|
54
|
+
quotacap providers rename claude "Work" # customize provider display name
|
|
55
|
+
quotacap providers reset claude # restore built-in name (--all resets all)
|
|
52
56
|
```
|
|
53
57
|
|
|
54
|
-
No setup ceremony: config is provisioned on first use, and bare `quotacap` is the dashboard launcher. `quotacap web --foreground` and `quotacap daemon` keep the old hold-the-terminal mode for SSH, containers, and supervisors.
|
|
58
|
+
No setup ceremony: config is provisioned on first use, and bare `quotacap` is the dashboard launcher. `quotacap status --verbose` displays actionable remediation guidance and sanitized failure details for failing adapters. `status` remains strictly read-only and never triggers background polling. `quotacap providers` manages custom display names (persisted to `~/.quotacap/config.json` under `providerNames`). `quotacap web --foreground` and `quotacap daemon` keep the old hold-the-terminal mode for SSH, containers, and supervisors.
|
|
55
59
|
|
|
56
60
|
## MCP
|
|
57
61
|
|
|
@@ -78,6 +82,7 @@ The local web server listens on `127.0.0.1:8787` (configured via `QUOTACAP_URL`
|
|
|
78
82
|
| `GET` | `/api/recommendation` | None | Current advisory on which provider to use next |
|
|
79
83
|
| `GET` | `/api/token` | Same-origin | Shared secret token for the dashboard |
|
|
80
84
|
| `POST` | `/api/refresh` | `X-QuotaCap-Token` | Trigger an immediate adapter poll (debounced to 60s) |
|
|
85
|
+
| `PATCH` | `/api/providers/:id` | `X-QuotaCap-Token` | Set or clear custom provider display name override |
|
|
81
86
|
|
|
82
87
|
### Curl examples
|
|
83
88
|
|
|
@@ -94,13 +99,29 @@ curl -X POST http://localhost:8787/api/refresh \
|
|
|
94
99
|
-H "X-QuotaCap-Token: $(cat ~/.quotacap/token)"
|
|
95
100
|
```
|
|
96
101
|
|
|
102
|
+
Set or clear a provider display name override:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Set custom display name
|
|
106
|
+
curl -X PATCH http://localhost:8787/api/providers/claude \
|
|
107
|
+
-H "X-QuotaCap-Token: $(cat ~/.quotacap/token)" \
|
|
108
|
+
-H "Content-Type: application/json" \
|
|
109
|
+
-d '{"displayName": "Work Claude"}'
|
|
110
|
+
|
|
111
|
+
# Reset to built-in name
|
|
112
|
+
curl -X PATCH http://localhost:8787/api/providers/claude \
|
|
113
|
+
-H "X-QuotaCap-Token: $(cat ~/.quotacap/token)" \
|
|
114
|
+
-H "Content-Type: application/json" \
|
|
115
|
+
-d '{"displayName": null}'
|
|
116
|
+
```
|
|
117
|
+
|
|
97
118
|
## Security
|
|
98
119
|
|
|
99
|
-
QuotaCap is a local daemon. It binds to `127.0.0.1` only (`src/
|
|
120
|
+
QuotaCap is a local daemon. It binds to `127.0.0.1` only (`src/runtime/service.ts` `app.listen`). It does not listen on `0.0.0.0`. There is no LAN surface.
|
|
100
121
|
|
|
101
|
-
It owns no tokens. It never reads `~/.codex/auth.json`, `~/.kimi-code/credentials/kimi-code.json`, `~/.kimi/credentials/kimi-code.json`, `~/.grok/auth.json`, or `~/.gemini/oauth_creds.json`. It never uses `refresh_token` or `grant_type=refresh_token`. It has no hardcoded client ids. Those OAuth paths and the `.qc-bak` and `.qc-lock` helpers were removed in #14. This is asserted by `tests/adapters/credential-free.test.ts`. Each CLI owns its own session. QuotaCap only spawns the CLI and reads its stdout via `exec` (`claude`, `agy`) or PTY (`codex`, `kimi`, `grok`).
|
|
122
|
+
It owns no tokens. It never reads `~/.codex/auth.json`, `~/.kimi-code/credentials/kimi-code.json`, `~/.kimi/credentials/kimi-code.json`, `~/.grok/auth.json`, or `~/.gemini/oauth_creds.json`. It never uses `refresh_token` or `grant_type=refresh_token`. It has no hardcoded client ids. Those OAuth paths and the `.qc-bak` and `.qc-lock` helpers were removed in #14. This is asserted by `tests/adapters/credential-free.test.ts`. Each CLI owns its own session. It never reads `~/.config/muse/auth.json`, `~/.local/share/muse/sessions/`, or `~/.config/muse/tui-history.jsonl`. Each CLI owns its own session. QuotaCap only spawns the CLI and reads its stdout via `exec` (`claude`, `agy`) or PTY (`codex`, `kimi`, `grok`, `muse`).
|
|
102
123
|
|
|
103
|
-
It stores no `raw` provider payload. The `raw` column was dropped and migrated in `src/store/db.ts
|
|
124
|
+
It stores no `raw` provider payload. The `raw` column was dropped and migrated in `src/store/db.ts` `migrate`. `GET /api/quotas` and MCP `get_quotas` never return `raw` (`tests/http/api.test.ts`). History and the token live under `~/.quotacap/` with `0700` on the directory and `0600` on files.
|
|
104
125
|
|
|
105
126
|
Every request checks `Host` and `Origin`. `Host` must be loopback (`127.0.0.1`, `localhost`, `[::1]`), otherwise `403`. `Origin` when present must be loopback, otherwise `403`. Absent `Origin` passes for `curl` and MCP. `POST /api/refresh` requires `X-QuotaCap-Token` matching `~/.quotacap/token` with `crypto.timingSafeEqual` (`src/http/server.ts:isValidToken`), otherwise `401`. `GET /assets/*` is rooted with `path.resolve` and a prefix check (`tests/http/api.test.ts`).
|
|
106
127
|
|
|
@@ -134,6 +155,8 @@ The same code is an npm package and a compiled binary.
|
|
|
134
155
|
|
|
135
156
|
The Kimi adapter spawns `kimi` in your home directory so the quota modal does not depend on the project path. If Kimi shows `Trust this folder?`, QuotaCap fails closed with `untrusted workspace — run \`kimi\` there and select Trust this folder` and does not auto-trust; trust remains an explicit interactive decision.
|
|
136
157
|
|
|
158
|
+
The Muse adapter never trusts a directory of yours. It runs `muse --trust-workspace` with its working directory set to an empty folder QuotaCap owns (`~/.quotacap/muse-probe/`), so the flag has no project-local skills, rules, hooks or plugin config to load, and Muse's own flag does not persist trust. If a future Muse ignores the flag and shows the prompt anyway, the adapter aborts fail-closed rather than answering it.
|
|
159
|
+
|
|
137
160
|
The compiled binaries are built with `bun build --compile`.
|
|
138
161
|
Each `dist-bin/quotacap-<os>-<arch>.tar.gz` bundles a `pty` sidecar.
|
|
139
162
|
The sidecar is `pty/node-pty` with the matching prebuild.
|
package/dist/adapters/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { codexAdapter } from "./codex.js";
|
|
|
4
4
|
import { kimiAdapter } from "./kimi.js";
|
|
5
5
|
import { grokAdapter } from "./grok.js";
|
|
6
6
|
import { agyAdapter } from "./agy.js";
|
|
7
|
+
import { museAdapter } from "./muse.js";
|
|
7
8
|
import { installAdapterSignal, clearAdapterSignal } from "../runtime/spawn.js";
|
|
8
9
|
export const adapters = {
|
|
9
10
|
claude: claudeAdapter,
|
|
@@ -12,6 +13,7 @@ export const adapters = {
|
|
|
12
13
|
kimi: kimiAdapter,
|
|
13
14
|
grok: grokAdapter,
|
|
14
15
|
agy: agyAdapter,
|
|
16
|
+
muse: museAdapter,
|
|
15
17
|
};
|
|
16
18
|
const ADAPTER_TIMEOUTS = {
|
|
17
19
|
claude: 8000,
|
|
@@ -19,6 +21,7 @@ const ADAPTER_TIMEOUTS = {
|
|
|
19
21
|
kimi: 8000,
|
|
20
22
|
grok: 14000,
|
|
21
23
|
agy: 20000,
|
|
24
|
+
muse: 14000,
|
|
22
25
|
};
|
|
23
26
|
export async function pollAll(enabled, opts) {
|
|
24
27
|
const rawJobs = enabled.map(id => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ParsedQuota } from "./types.js";
|
|
2
|
+
export declare function museProbeDir(): string;
|
|
3
|
+
export declare function parseMuseTui(text: string, now?: Date): ParsedQuota;
|
|
4
|
+
export declare const museAdapter: {
|
|
5
|
+
id: string;
|
|
6
|
+
requiresAuth: string;
|
|
7
|
+
poll(): Promise<ParsedQuota>;
|
|
8
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseResetText } from "./parse.js";
|
|
5
|
+
import { runPty, stripAnsi } from "./pty.js";
|
|
6
|
+
import { adapterSignal } from "../runtime/spawn.js";
|
|
7
|
+
// QuotaCap-owned empty probe dir, beside the config and database. Muse loads
|
|
8
|
+
// project-local skills, rules, hooks and plugin config from cwd, so the probe
|
|
9
|
+
// never runs in $HOME or a user project — an empty dir grants nothing.
|
|
10
|
+
export function museProbeDir() {
|
|
11
|
+
return path.join(process.env.QUOTACAP_HOME ?? os.homedir(), ".quotacap", "muse-probe");
|
|
12
|
+
}
|
|
13
|
+
export function parseMuseTui(text, now = new Date()) {
|
|
14
|
+
// After ANSI stripping the panel is ONE line (cursor-addressed, no
|
|
15
|
+
// newlines), so no pattern here may anchor on ^, $ or \n.
|
|
16
|
+
const cleaned = stripAnsi(text);
|
|
17
|
+
const weekly = cleaned.match(/Weekly\s+(\d+)%\s+used/i);
|
|
18
|
+
if (!weekly)
|
|
19
|
+
throw new Error("muse: weekly usage not found in TUI output");
|
|
20
|
+
const current = cleaned.match(/Current\s+(\d+)%\s+used/i);
|
|
21
|
+
if (!current)
|
|
22
|
+
throw new Error("muse: current-window usage not found in TUI output");
|
|
23
|
+
const usedPct = parseInt(weekly[1], 10);
|
|
24
|
+
const sessionPct = parseInt(current[1], 10);
|
|
25
|
+
if (!Number.isFinite(usedPct) || usedPct < 0 || usedPct > 100)
|
|
26
|
+
throw new Error("muse: bad weekly pct");
|
|
27
|
+
if (!Number.isFinite(sessionPct) || sessionPct < 0 || sessionPct > 100)
|
|
28
|
+
throw new Error("muse: bad current pct");
|
|
29
|
+
// First match wins; overdrawn transcripts carry two identical copies.
|
|
30
|
+
// Vendor casing preserved (grok precedent: SuperGrok); the Muse Code
|
|
31
|
+
// product prefix is stripped because the product is already the row label.
|
|
32
|
+
let plan = "unknown";
|
|
33
|
+
const planMatch = cleaned.match(/Subscription\s*·\s*Muse Code\s+(.+?)\s{2,}/i);
|
|
34
|
+
if (planMatch)
|
|
35
|
+
plan = planMatch[1].trim();
|
|
36
|
+
// The Current-window reset ("Resets at 3:51 PM") is a bare clock time with
|
|
37
|
+
// no date, and Quota has no session-reset field — discarded, as claude and
|
|
38
|
+
// kimi already do. A missing or unparseable Weekly reset throws rather than
|
|
39
|
+
// estimating: an invented reset would feed the advisory a false deadline.
|
|
40
|
+
const resetMatch = cleaned.match(/Weekly[^\n]*?Resets\s+([A-Za-z]{3}\s+\d{1,2}\s+at\s+\d{1,2}:\d{2}\s*[AP]M)/i);
|
|
41
|
+
if (!resetMatch)
|
|
42
|
+
throw new Error("muse: bad weekly reset");
|
|
43
|
+
const resetRaw = resetMatch[1].trim();
|
|
44
|
+
const resetsAt = parseResetText(`resets ${resetRaw}`, now);
|
|
45
|
+
if (!resetsAt)
|
|
46
|
+
throw new Error(`muse: bad weekly reset "${resetRaw}"`);
|
|
47
|
+
const periodStart = new Date(new Date(resetsAt).getTime() - 7 * 86400000).toISOString();
|
|
48
|
+
return {
|
|
49
|
+
provider: "muse",
|
|
50
|
+
plan,
|
|
51
|
+
usedPct,
|
|
52
|
+
sessionPct,
|
|
53
|
+
resetsAt,
|
|
54
|
+
periodStart,
|
|
55
|
+
source: "tui",
|
|
56
|
+
fetchedAt: now.toISOString(),
|
|
57
|
+
raw: cleaned.slice(0, 4096),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export const museAdapter = {
|
|
61
|
+
id: "muse",
|
|
62
|
+
requiresAuth: "muse login (CLI owns credentials)",
|
|
63
|
+
async poll() {
|
|
64
|
+
const cwd = museProbeDir();
|
|
65
|
+
fs.mkdirSync(cwd, { recursive: true });
|
|
66
|
+
const transcript = await runPty({
|
|
67
|
+
file: "muse",
|
|
68
|
+
args: ["--trust-workspace"],
|
|
69
|
+
cwd,
|
|
70
|
+
// Without this, a missing binary triggers a foreground 248 MB download
|
|
71
|
+
// on every poll; with it the launcher dies fast with a clear message.
|
|
72
|
+
env: { MUSE_NO_AUTO_UPDATE: "1" },
|
|
73
|
+
cols: 140,
|
|
74
|
+
rows: 50,
|
|
75
|
+
readyRegex: /muse-spark|Muse Code \d/,
|
|
76
|
+
readyTimeoutMs: 8000,
|
|
77
|
+
settleDelayMs: 1000,
|
|
78
|
+
// Two-phase submit: "/usage" first, CR 1500ms later. A single combined
|
|
79
|
+
// write is swallowed by the slash-command autocomplete and never runs.
|
|
80
|
+
input: "/usage",
|
|
81
|
+
submitInput: "\r",
|
|
82
|
+
submitAfterMs: 1500,
|
|
83
|
+
completionRegex: /Subscription/i,
|
|
84
|
+
// Trust prompt (in case a future Muse ignores the flag), an accidental
|
|
85
|
+
// model turn from a mistimed Enter, and the unavailable-subscription
|
|
86
|
+
// fast-fail — every one aborts to a degraded row, never a 14s timeout.
|
|
87
|
+
abortOn: /Do you trust this workspace|Working \(\d+s|Subscriptions aren't currently available|subscription_unavailable/,
|
|
88
|
+
timeoutMs: 14000,
|
|
89
|
+
respondToQueries: true,
|
|
90
|
+
maxBytes: 256 * 1024,
|
|
91
|
+
signal: adapterSignal("muse"),
|
|
92
|
+
label: "muse",
|
|
93
|
+
});
|
|
94
|
+
return parseMuseTui(transcript);
|
|
95
|
+
},
|
|
96
|
+
};
|
package/dist/adapters/pty.d.ts
CHANGED
|
@@ -8,8 +8,26 @@ export interface PtyRunOptions {
|
|
|
8
8
|
rows?: number;
|
|
9
9
|
readyRegex?: RegExp;
|
|
10
10
|
readyTimeoutMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Settle delay after spawn (no readyRegex) or after readiness (with
|
|
13
|
+
* readyRegex). The post-ready default stays 200ms; pass an explicit value
|
|
14
|
+
* (e.g. muse's 1000ms) only when the TUI needs longer after signalling.
|
|
15
|
+
*/
|
|
11
16
|
settleDelayMs?: number;
|
|
12
17
|
input: string;
|
|
18
|
+
/**
|
|
19
|
+
* Second-phase input, written `submitAfterMs` after `input`. Both must be
|
|
20
|
+
* set for the second write to happen; absent by default, which keeps the
|
|
21
|
+
* historical single-write behaviour for existing adapters.
|
|
22
|
+
*/
|
|
23
|
+
submitInput?: string;
|
|
24
|
+
submitAfterMs?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Opt-in terminal-query responder: answers DSR, DA1 and OSC 4/10/11
|
|
27
|
+
* capability queries the child emits. Off by default; TUIs that never
|
|
28
|
+
* query (codex, kimi, grok) are unaffected either way.
|
|
29
|
+
*/
|
|
30
|
+
respondToQueries?: boolean;
|
|
13
31
|
completionRegex?: RegExp;
|
|
14
32
|
/** If matched, abort immediately with a clear error (e.g. trust prompts). */
|
|
15
33
|
abortOn?: RegExp;
|
|
@@ -20,6 +38,23 @@ export interface PtyRunOptions {
|
|
|
20
38
|
/** Diagnostic label for the tracked child (adapter id). */
|
|
21
39
|
label?: string;
|
|
22
40
|
}
|
|
41
|
+
export interface TerminalQueryRule {
|
|
42
|
+
/** Non-global pattern; must match the complete query including terminator. */
|
|
43
|
+
match: RegExp;
|
|
44
|
+
reply: (m: RegExpMatchArray) => string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Canned answers to terminal capability queries, table-driven so a future
|
|
48
|
+
* query type is a one-line row. Replies are static: colour answers never
|
|
49
|
+
* affect parsing because styling is stripped before matching.
|
|
50
|
+
*/
|
|
51
|
+
export declare const TERMINAL_QUERY_RESPONSES: TerminalQueryRule[];
|
|
52
|
+
/**
|
|
53
|
+
* Stateful feeder for `TERMINAL_QUERY_RESPONSES`. Consumes each matched query
|
|
54
|
+
* from an internal buffer, so a query split across chunks is answered once it
|
|
55
|
+
* completes and never answered twice. Plain output passes through silently.
|
|
56
|
+
*/
|
|
57
|
+
export declare function createTerminalQueryResponder(write: (s: string) => void): (chunk: string) => void;
|
|
23
58
|
export interface SpawnHelperRepair {
|
|
24
59
|
helper: string;
|
|
25
60
|
ok: boolean;
|