dsh-subscriptions 0.1.5 → 0.2.2
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 +212 -154
- package/lib/auth/claude-code-creds.js +31 -9
- package/lib/auth/device-flow.js +16 -9
- package/lib/auth/oauth-flow.js +13 -10
- package/lib/auth/rpc.d.ts +73 -2
- package/lib/auth/rpc.js +124 -23
- package/lib/auth/store.js +80 -31
- package/lib/client/CursorCard.d.ts +1 -1
- package/lib/client/CursorCard.js +50 -16
- package/lib/client/ExternalUsageCards.d.ts +1 -1
- package/lib/client/ExternalUsageCards.js +37 -16
- package/lib/client/ImageGallery.d.ts +2 -2
- package/lib/client/ImageGallery.js +92 -33
- package/lib/client/ImageGenerateToolview.js +50 -14
- package/lib/client/OllamaCard.d.ts +13 -0
- package/lib/client/OllamaCard.js +94 -0
- package/lib/client/OllamaSettingsManager.d.ts +24 -0
- package/lib/client/OllamaSettingsManager.js +329 -0
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +109 -26
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +257 -46
- package/lib/client/SubscriptionUsageBadge.d.ts +5 -11
- package/lib/client/SubscriptionUsageBadge.js +301 -112
- package/lib/client/SubscriptionsSection.d.ts +2 -0
- package/lib/client/SubscriptionsSection.js +259 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +69 -27
- package/lib/client/VideoGenerateToolview.js +69 -17
- package/lib/client/account-preferences.d.ts +8 -0
- package/lib/client/account-preferences.js +20 -5
- package/lib/client/host-contract.d.ts +11 -2
- package/lib/client/host-contract.js +20 -3
- package/lib/client/index.d.ts +5 -5
- package/lib/client/index.js +34 -12
- package/lib/client/locales.d.ts +78 -0
- package/lib/client/locales.js +121 -34
- package/lib/client/ollama-draft.d.ts +229 -0
- package/lib/client/ollama-draft.js +255 -0
- package/lib/client/reset-action.js +6 -4
- package/lib/client/reset-credits-view.d.ts +1 -1
- package/lib/client/reset-credits-view.js +66 -17
- package/lib/client/subscription-card-styles.js +94 -24
- package/lib/client/usage-color-preferences.js +4 -2
- package/lib/client/usage-pace.d.ts +7 -0
- package/lib/client/usage-pace.js +43 -10
- package/lib/client.js +1647 -266
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +28 -3
- package/lib/index.js +9329 -8217
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +14 -1
- package/lib/provider-settings.js +64 -20
- package/lib/providers/account-preferences.js +51 -22
- package/lib/providers/accounts.js +15 -9
- package/lib/providers/antigravity-oauth-client.js +2 -2
- package/lib/providers/antigravity.js +141 -68
- package/lib/providers/catalog-store.js +120 -41
- package/lib/providers/claude-cli-version.js +3 -3
- package/lib/providers/claude.d.ts +23 -3
- package/lib/providers/claude.js +167 -92
- package/lib/providers/codex-client-version.js +3 -3
- package/lib/providers/codex-search.js +40 -13
- package/lib/providers/codex.js +196 -112
- package/lib/providers/common.d.ts +14 -0
- package/lib/providers/common.js +118 -38
- package/lib/providers/copilot.js +143 -82
- package/lib/providers/cursor-adapter.js +42 -18
- package/lib/providers/cursor-auth.d.ts +1 -1
- package/lib/providers/cursor-auth.js +69 -24
- package/lib/providers/cursor-model-settings.js +14 -5
- package/lib/providers/cursor-usage.js +27 -13
- package/lib/providers/external-usage-controller.d.ts +9 -2
- package/lib/providers/external-usage-controller.js +30 -7
- package/lib/providers/external-usage.d.ts +8 -0
- package/lib/providers/external-usage.js +140 -20
- package/lib/providers/grok.js +152 -89
- package/lib/providers/image-pool.js +29 -11
- package/lib/providers/minimax-usage.js +31 -11
- package/lib/providers/npm-cli-version.js +10 -3
- package/lib/providers/ollama-settings.d.ts +90 -0
- package/lib/providers/ollama-settings.js +213 -0
- package/lib/providers/ollama-web.d.ts +46 -0
- package/lib/providers/ollama-web.js +231 -0
- package/lib/providers/ollama.d.ts +107 -0
- package/lib/providers/ollama.js +608 -0
- package/lib/providers/pool-family.js +11 -3
- package/lib/providers/pool-health.js +28 -8
- package/lib/providers/pool-usage.js +16 -9
- package/lib/providers/pool.d.ts +8 -0
- package/lib/providers/pool.js +81 -42
- package/lib/providers/rate-limit.js +8 -2
- package/lib/providers/replay.js +40 -17
- package/lib/providers/reset-redemption.js +18 -6
- package/lib/tools/image-generate.js +123 -65
- package/lib/tools/registration.js +5 -2
- package/lib/tools/video-generate.js +51 -31
- package/lib/tools/x-search.js +50 -22
- package/lib/translate/anthropic.js +85 -37
- package/lib/translate/antigravity-schema.js +71 -28
- package/lib/translate/antigravity-thinking.js +22 -9
- package/lib/translate/antigravity.js +141 -50
- package/lib/translate/chat-completions.js +67 -23
- package/lib/translate/resolved.js +55 -21
- package/lib/translate/responses.js +69 -29
- package/lib/translate/sse.js +4 -1
- package/package.json +26 -4
package/README.md
CHANGED
|
@@ -11,27 +11,45 @@ reported quota in **Settings → Subscriptions**.
|
|
|
11
11
|
## What it adds
|
|
12
12
|
|
|
13
13
|
- **Six subscription model sources:** Connect ChatGPT/Codex, Claude, Grok,
|
|
14
|
-
GitHub Copilot, Google Antigravity, or Cursor and choose their models in
|
|
15
|
-
|
|
16
|
-
- **
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
and
|
|
14
|
+
GitHub Copilot, Google Antigravity, or Cursor and choose their models in DSH's
|
|
15
|
+
picker. These routes use account sign-in, without provider API keys.
|
|
16
|
+
- **Ollama Cloud key route:** Add the `ollama-cloud` route with an
|
|
17
|
+
`OLLAMA_API_KEY` credential. The route ships no model catalog: configure it on
|
|
18
|
+
the Settings → Subscriptions card, which fetches the cloud list, ticks the
|
|
19
|
+
models to keep, and edits each one's display name, order, default reasoning
|
|
20
|
+
effort, and local context window. The picker shows exactly the ticked list, so
|
|
21
|
+
an unconfigured route offers no model. The card also edits the base URL, the
|
|
22
|
+
credential reference, and the web search/fetch switch, and persists them under
|
|
23
|
+
the DSH home directory. No OAuth sign-in. A hand-written `llm-pi-ai` route
|
|
24
|
+
with the same id collides with this one: delete the manual block when enabling
|
|
25
|
+
the plugin route. Stop sequences are unsupported on this route, consistent
|
|
26
|
+
with the other adapters. Web search/fetch stay off until the card's switch is
|
|
27
|
+
on (or a profile sets `ollama.web`), so untouched installs keep their existing
|
|
28
|
+
web provider; a profile still pins them to `ollama-cloud`. Not ported from the
|
|
29
|
+
standalone plugin: its transient-error reclassification, per-image request
|
|
30
|
+
quotas, and per-model usage counts. See the
|
|
31
|
+
[key-route note](.agents/notes/implemented/provider/2026-10-10-ollama-cloud-key-route.md).
|
|
32
|
+
- **Account and model control:** Refresh catalogs and choose visible models. For
|
|
33
|
+
Codex, Claude, Grok, Copilot, and Antigravity, manage multiple accounts and
|
|
34
|
+
same-provider pools. Pools can use available quota to select an account and
|
|
35
|
+
fail over before a reply starts. See
|
|
36
|
+
[account management](docs/account-management.md).
|
|
20
37
|
- **Quota while you work:** See reported usage windows and reset times on
|
|
21
38
|
provider cards and in an optional session-footer pill. The same view reads
|
|
22
|
-
OpenCode Go and Kimi Code usage through keys already configured in
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
39
|
+
OpenCode Go and Kimi Code usage through keys already configured in **Settings
|
|
40
|
+
→ Models** (their model routes remain built into DSH), and Ollama Cloud usage
|
|
41
|
+
through its `OLLAMA_API_KEY` credential (its route ships in this plugin).
|
|
42
|
+
GitHub Copilot has no usage endpoint.
|
|
43
|
+
- **Provider tools:** Use Codex web search, Grok X search, ChatGPT or Grok image
|
|
44
|
+
generation and editing, and Grok video generation when the matching provider
|
|
45
|
+
is enabled.
|
|
28
46
|
|
|
29
47
|
## Screenshots
|
|
30
48
|
|
|
31
|
-
Captured against DSH 0.2.0-rc.2 in the web UI with a local stub serving
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
Captured against DSH 0.2.0-rc.2 in the web UI with a local stub serving frozen
|
|
50
|
+
provider responses, so each image shows a product state rather than a real
|
|
51
|
+
account. Identities in the images are fabricated, and the quota percentages,
|
|
52
|
+
catalogs, and reset countdowns are sample data.
|
|
35
53
|
|
|
36
54
|
### Subscriptions
|
|
37
55
|
|
|
@@ -46,21 +64,23 @@ Logged-in providers join the session model picker with their live catalogs.
|
|
|
46
64
|
|
|
47
65
|

|
|
48
66
|
|
|
49
|
-
Models that advertise reasoning levels get an **Effort** selector in the
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
Models that advertise reasoning levels get an **Effort** selector in the same
|
|
68
|
+
menu. Levels and defaults come from the provider's live catalog; without
|
|
69
|
+
discovery Codex falls back to a built-in list, and a configured default
|
|
70
|
+
overrides either.
|
|
53
71
|
|
|
54
72
|

|
|
55
73
|
|
|
56
|
-
Codex models whose catalog advertises the fast tier get a **Speed** toggle
|
|
57
|
-
|
|
74
|
+
Codex models whose catalog advertises the fast tier get a **Speed** toggle in
|
|
75
|
+
the composer's tool row: Standard or Fast, per session.
|
|
58
76
|
|
|
59
77
|

|
|
60
78
|
|
|
61
|
-
**Settings → Subscriptions → provider → Manage** edits account aliases,
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
**Settings → Subscriptions → provider → Manage** edits account aliases, pool
|
|
80
|
+
participation, model allowlists, and the per-model effort, context, and tool
|
|
81
|
+
settings. A context value only lowers the window a session plans against, never
|
|
82
|
+
above what the provider advertises; Codex and Claude support it, and it does not
|
|
83
|
+
enlarge the API's capacity.
|
|
64
84
|
|
|
65
85
|

|
|
66
86
|
|
|
@@ -68,85 +88,81 @@ and tool settings.
|
|
|
68
88
|
|
|
69
89
|
### Quota while you work
|
|
70
90
|
|
|
71
|
-
The composer's stats row gains a **provider usage** pill showing the
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
91
|
+
The composer's stats row gains a **provider usage** pill showing the used
|
|
92
|
+
percentage and reset window for the provider of the session's current model. It
|
|
93
|
+
shows at most one provider; switching to a non-subscription model keeps the most
|
|
94
|
+
recent subscription selected in the mounted conversation view, or stays hidden
|
|
95
|
+
if there is none. **Always show** instead rotates through the providers that
|
|
96
|
+
report usage every 10 seconds while the current model has no quota source, so
|
|
97
|
+
the pill never disappears.
|
|
78
98
|
|
|
79
99
|

|
|
80
100
|
|
|
81
|
-
Click the pill to expand every provider and account that reports usage —
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
elapsed
|
|
92
|
-
turns
|
|
93
|
-
and
|
|
94
|
-
|
|
95
|
-
cursor without a pace warning. Choose Standard, Relaxed, or Remaining colors
|
|
96
|
-
in **Settings → Subscriptions**.
|
|
101
|
+
Click the pill to expand every provider and account that reports usage — the
|
|
102
|
+
default account is starred, and the current provider is listed first. A provider
|
|
103
|
+
with no usage endpoint, such as GitHub Copilot, has no row, and neither do
|
|
104
|
+
accounts without a usage window. Antigravity previews only the current model's
|
|
105
|
+
windows; the other model windows stay in a closed disclosure.
|
|
106
|
+
|
|
107
|
+
Quota bars include a dark elapsed-time cursor whenever the provider's timing
|
|
108
|
+
places the window in time, including at 0% used and for stale or old readings.
|
|
109
|
+
Compare the colored fill (quota used) with the cursor (time elapsed); hover for
|
|
110
|
+
the percentages and reset countdown. Standard coloring turns the fill orange
|
|
111
|
+
when a fresh reading leads elapsed time by 10 points, and Relaxed uses 15; 90%
|
|
112
|
+
used turns it red and takes priority. Windows without known timing omit the
|
|
113
|
+
cursor, and a stale or old reading keeps its cursor without a pace warning.
|
|
114
|
+
Choose Standard, Relaxed, or Remaining colors in **Settings → Subscriptions**.
|
|
97
115
|
|
|
98
116
|

|
|
99
117
|
|
|
100
118
|
#### Codex usage limit resets
|
|
101
119
|
|
|
102
|
-
The Codex account card shows banked full-reset credits and their expirations
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
The Codex account card shows banked full-reset credits and their expirations in
|
|
121
|
+
a closed disclosure. A failed optional lookup reports an error without hiding
|
|
122
|
+
ordinary quota windows. **Use…** selects the earliest-expiring available credit
|
|
123
|
+
and requires a fresh unscoped weekly reading of at least 80%, then an explicit
|
|
124
|
+
confirmation and acknowledgment. Confirmation expires after 60 seconds; the
|
|
125
|
+
server rechecks usage and credit availability before submission.
|
|
108
126
|
|
|
109
127
|
One reset consumes one credit and resets weekly and 5-hour limits. This
|
|
110
|
-
interface cannot undo it. An unconfirmed outcome blocks further redemption
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
interface cannot undo it. An unconfirmed outcome blocks further redemption for
|
|
129
|
+
that account in the current host process. Restarting clears that block, but must
|
|
130
|
+
not be used to bypass it: check credits and limits in Codex before retrying,
|
|
131
|
+
because the first submission may already have spent a credit. The private
|
|
132
|
+
endpoint is covered by offline fakes, not live-account checks. See the
|
|
133
|
+
[manual reset decision](.agents/notes/implemented/provider/2026-10-04-codex-manual-reset.md)
|
|
116
134
|
for the retry boundary and verification limits.
|
|
117
135
|
|
|
118
136
|
#### Claude usage limit resets
|
|
119
137
|
|
|
120
|
-
The Claude account card shows banked limit resets from Anthropic's
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
138
|
+
The Claude account card shows banked limit resets from Anthropic's `cedar_ember`
|
|
139
|
+
program, in the same closed disclosure Codex uses. It lists one row per grant,
|
|
140
|
+
not one per credit, and the summary counts remaining resets by summing
|
|
141
|
+
`resetsLeft` over grants that are neither expired nor paused. Each row states
|
|
142
|
+
whether that grant is available, not yet usable, paused, exhausted, or expired,
|
|
143
|
+
with its remaining count and when it lapses. That time is the grant's expiry: it
|
|
144
|
+
is not a window reset and does not move the elapsed-time cursor.
|
|
127
145
|
|
|
128
146
|
This is **display only**. `claimable` is shown as data on a text row, and the
|
|
129
147
|
page has no control that spends a Claude reset, because the response shape is
|
|
130
148
|
not an Anthropic contract. An ineligible account, a grant-less block, or a
|
|
131
|
-
rejected optional query leaves ordinary session and weekly windows untouched
|
|
132
|
-
|
|
133
|
-
|
|
149
|
+
rejected optional query leaves ordinary session and weekly windows untouched and
|
|
150
|
+
shows no list; a failed lookup shows a fixed sentence rather than the provider's
|
|
151
|
+
text. No live account was used to verify what Anthropic returns. See
|
|
134
152
|
[the evidence and limits](docs/claude-banked-resets.md) and the
|
|
135
153
|
[display decision](.agents/notes/implemented/provider/2026-10-08-claude-banked-reset-display.md).
|
|
136
154
|
|
|
137
155
|
## Install
|
|
138
156
|
|
|
139
|
-
Current source is tested with DSH `0.1.7-rc.2`, `0.2.0-rc.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
`
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
[docs/
|
|
147
|
-
|
|
148
|
-
layers. With `dsh` available, install the plugin into the web profile from
|
|
149
|
-
npm:
|
|
157
|
+
Current source is tested with DSH `0.1.7-rc.2`, `0.2.0-rc.2`, and `0.2.1-rc.2`.
|
|
158
|
+
The package's peers admit `>=0.1.7-rc.2 <0.3.0-0`: untested versions inside that
|
|
159
|
+
range install but are unsupported, and versions outside it need an explicit
|
|
160
|
+
`dsh plugin allow-version` exemption. `dsh plugin add` checks those ranges with
|
|
161
|
+
`includePrerelease` semantics, so the admitted set covers every host build from
|
|
162
|
+
`0.1.7-rc.2` through the whole `0.2.x` line, prereleases included, and blocks
|
|
163
|
+
the `0.3.0` line. See [docs/compatibility.md](docs/compatibility.md) for the
|
|
164
|
+
support window and [docs/testing.md](docs/testing.md) for the test layers. With
|
|
165
|
+
`dsh` available, install the plugin into the web profile from npm:
|
|
150
166
|
|
|
151
167
|
```sh
|
|
152
168
|
dsh plugin --profile web add dsh-subscriptions
|
|
@@ -159,47 +175,47 @@ dsh plugin --profile web add github:goodboys-ai/dsh-subscriptions
|
|
|
159
175
|
```
|
|
160
176
|
|
|
161
177
|
Git installs run this package's `prepare` script to build the host and browser
|
|
162
|
-
bundles. If a Git install stops with `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
bundles. If a Git install stops with `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` (DSH
|
|
179
|
+
`0.1.7-rc.2` does this by default), add this package to the web profile's
|
|
180
|
+
`pnpm-workspace.yaml` (`~/.dsh/profiles/web/` unless `DSH_HOME` is set) and
|
|
181
|
+
repeat the command:
|
|
166
182
|
|
|
167
183
|
```yaml
|
|
168
184
|
allowBuilds:
|
|
169
185
|
'dsh-subscriptions': true
|
|
170
186
|
```
|
|
171
187
|
|
|
172
|
-
(If a previous Git install used the old scoped name, replace its
|
|
173
|
-
|
|
188
|
+
(If a previous Git install used the old scoped name, replace its `allowBuilds`
|
|
189
|
+
key with the one above.)
|
|
174
190
|
|
|
175
191
|
Restart `dsh web` after installation. Open **Settings → Subscriptions** to
|
|
176
|
-
connect providers, manage accounts and model lists, and inspect available
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
keys are configured
|
|
192
|
+
connect providers, manage accounts and model lists, and inspect available quota.
|
|
193
|
+
The session footer quota pill can be shown or hidden there for the current
|
|
194
|
+
browser. OpenCode Go, Kimi Code, and Ollama Cloud cards show usage when their
|
|
195
|
+
API keys are configured.
|
|
180
196
|
|
|
181
|
-
To update an installation, run the same `dsh plugin --profile web add` command
|
|
197
|
+
To update an installation, run the same `dsh plugin --profile web add` command
|
|
198
|
+
again and restart `dsh web`.
|
|
182
199
|
|
|
183
|
-
Without a version suffix, the npm command installs the `latest` dist-tag and
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
[release policy](docs/compatibility.md#plugin-versioning).
|
|
200
|
+
Without a version suffix, the npm command installs the `latest` dist-tag and the
|
|
201
|
+
GitHub command installs the current default branch at install time. Pin a
|
|
202
|
+
release by appending `@<version>` to the npm spec or the tag to the GitHub spec
|
|
203
|
+
(`#vX.Y.Z`); see the [release policy](docs/compatibility.md#plugin-versioning).
|
|
188
204
|
|
|
189
205
|
## Verification and limits
|
|
190
206
|
|
|
191
|
-
CI builds, tests, and boots a packed tarball on all supported DSH versions;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
207
|
+
CI builds, tests, and boots a packed tarball on all supported DSH versions; it
|
|
208
|
+
does not run the GitHub source install's `prepare` step. Offline provider tests
|
|
209
|
+
check recorded API responses, so a passing CI run cannot prove that a provider
|
|
210
|
+
still accepts live sign-in or model requests. See [testing](docs/testing.md) for
|
|
211
|
+
the checks and their limits.
|
|
196
212
|
|
|
197
|
-
- **ChatGPT/Codex, Claude, Grok, GitHub Copilot, and Google Antigravity:**
|
|
198
|
-
|
|
213
|
+
- **ChatGPT/Codex, Claude, Grok, GitHub Copilot, and Google Antigravity:** The
|
|
214
|
+
port includes the tool-message and icon fixes tracked in
|
|
199
215
|
[upstream issue #117](https://github.com/V1ki/dsh-plugin-subscriptions/issues/117).
|
|
200
|
-
A manual check in an isolated `0.1.7-rc.2` web profile found the client
|
|
201
|
-
|
|
202
|
-
|
|
216
|
+
A manual check in an isolated `0.1.7-rc.2` web profile found the client bundle
|
|
217
|
+
served and the auth and usage-status RPCs answering. Live sign-in and model
|
|
218
|
+
requests on the supported DSH lines remain to be checked.
|
|
203
219
|
- **Cursor:** Browser sign-in and live model discovery were verified in an
|
|
204
220
|
isolated `0.1.7-rc.2` profile. A successful live model request remains to be
|
|
205
221
|
verified. Its transport comes from
|
|
@@ -209,28 +225,36 @@ that a provider still accepts live sign-in or model requests. See
|
|
|
209
225
|
- **OpenCode Go and Kimi Code:** Live usage responses were verified. Their API
|
|
210
226
|
keys stay on the DSH host; the browser receives configured status and quota
|
|
211
227
|
windows, not the keys.
|
|
228
|
+
- **Ollama Cloud:** Unit and fixture coverage only (chat wire, discovery mapping
|
|
229
|
+
and wire-id normalization, the settings store and its RPC endpoints, usage
|
|
230
|
+
parsing, web providers, host E2E fixtures). The card's rendering has no
|
|
231
|
+
browser test. Live chat, listing, usage, and web calls against `ollama.com`
|
|
232
|
+
remain to be verified with a real key.
|
|
212
233
|
|
|
213
|
-
Cursor uses one connected account and a live model catalog. Its model
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
usage.
|
|
234
|
+
Cursor uses one connected account and a live model catalog. Its model visibility
|
|
235
|
+
selection persists under the DSH home directory, and automatic display follows
|
|
236
|
+
newly discovered models. It ships no fallback model list and does not import the
|
|
237
|
+
local `cursor-agent` credential cache. Individual usage comes from Cursor
|
|
238
|
+
dashboard endpoints; the documented Admin API covers team usage.
|
|
219
239
|
|
|
220
|
-
The plugin follows DSH network settings. Its former proxy configuration and
|
|
221
|
-
|
|
240
|
+
The plugin follows DSH network settings. Its former proxy configuration and UI
|
|
241
|
+
have been retired; an old plugin proxy config file is ignored.
|
|
222
242
|
|
|
223
243
|
## Claude prompt-cache TTL
|
|
224
244
|
|
|
225
245
|
Set `claudePromptCacheTtl: '5m'` (default) or `'1h'` in the plugin config and
|
|
226
|
-
restart DSH.
|
|
227
|
-
|
|
228
|
-
hour
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
restart DSH. The Claude **Manage** dialog can override this for the Claude
|
|
247
|
+
route: its "Prompt cache lifetime" picker offers Follow config, 5 minutes, or 1
|
|
248
|
+
hour, and a saved choice applies from the next request without a restart. Follow
|
|
249
|
+
config leaves the config value in charge. This affects Claude only; other
|
|
250
|
+
providers keep their own caching. Five minutes omits the wire `ttl` field,
|
|
251
|
+
preserving the existing request. One hour adds `ttl: "1h"` to the tools+system
|
|
252
|
+
prefix and up to three conversation breakpoints. All marks share one TTL:
|
|
253
|
+
Anthropic rejects a one-hour mark after a five-minute mark. Requests classified
|
|
254
|
+
with `purpose` as compaction or session-title keep five minutes. Unclassified
|
|
255
|
+
calls, including subagents and calls from hosts that do not supply `purpose`,
|
|
256
|
+
follow the configured TTL. The TTL setting does not require a newer host
|
|
257
|
+
capability or dependency.
|
|
234
258
|
|
|
235
259
|
Cache reads have the same API price for either TTL. One-hour writes are priced
|
|
236
260
|
at 2× base input instead of 1.25×. They may pay off with reusable prefixes and
|
|
@@ -251,35 +275,58 @@ live-account verification of this fork.
|
|
|
251
275
|
|
|
252
276
|
**Settings → Subscriptions → Usage coloring** offers one browser-local dropdown:
|
|
253
277
|
|
|
254
|
-
- **Standard** (default): yellow when used quota leads elapsed time by at least
|
|
278
|
+
- **Standard** (default): yellow when used quota leads elapsed time by at least
|
|
279
|
+
10 percentage points.
|
|
255
280
|
- **Relaxed**: yellow at a lead of at least 15 points.
|
|
256
281
|
- **Remaining quota only**: no yellow pace warnings.
|
|
257
282
|
|
|
258
|
-
Every preset shows reported usage of 90% or more in red, before considering
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
283
|
+
Every preset shows reported usage of 90% or more in red, before considering
|
|
284
|
+
pace. For example, 40% used after 25% of a window is a 15-point lead, not a
|
|
285
|
+
consumption forecast. Preferences apply to all meters, survive reloads and
|
|
286
|
+
synchronize across tabs of the same origin. They change display only, never
|
|
287
|
+
account routing or quota enforcement.
|
|
288
|
+
|
|
289
|
+
A vertical marker shows elapsed time only for an explicit valid start/end
|
|
290
|
+
interval or a provider-verified fixed duration. A session/weekly label alone
|
|
291
|
+
does not establish a fixed window; rolling or unknown intervals have no marker.
|
|
292
|
+
Claude's recognized five-hour/seven-day buckets and Codex's reported quota
|
|
293
|
+
durations supply fixed-window metadata. Unknown timing falls back to green below
|
|
294
|
+
90%, without a marker or yellow warning.
|
|
295
|
+
|
|
296
|
+
The marker depends on the window's timing alone, not on the reading: it stays at
|
|
297
|
+
0% used, for an invalid percentage, and when a refresh failed or the reading is
|
|
298
|
+
more than five minutes old. Such a reading is not set against the current time,
|
|
299
|
+
so it gets no yellow pace warning and its tooltip omits the ahead/behind
|
|
300
|
+
comparison. It retains the last reported green/red color with an accessible
|
|
301
|
+
stale label; refresh to confirm it. Once the reset time has passed the window
|
|
302
|
+
has no position and the marker disappears. Invalid percentages show an
|
|
303
|
+
unavailable state without a fabricated fill. Tooltips explain remaining quota,
|
|
304
|
+
time progress and reset countdown where available. See the
|
|
305
|
+
[decision record](.agents/notes/implemented/architecture/2026-10-01-usage-pace-colors.md)
|
|
306
|
+
and the
|
|
307
|
+
[cursor note](.agents/notes/implemented/architecture/2026-10-06-usage-cursor-timing-only.md)
|
|
308
|
+
for rationale and verification limits.
|
|
263
309
|
|
|
264
310
|
### MiniMax subscription usage
|
|
265
311
|
|
|
266
|
-
Configure `MINIMAX_API_KEY` for the global service or
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
region's `/v1/token_plan/remains` endpoint and displays finite per-model
|
|
271
|
-
with API-provided start/end times. It does not add model routing or OAuth
|
|
272
|
-
Rows are labelled with the model name, and the plugin shows the windows
|
|
273
|
-
returns for each model. MiniMax documents a five-hour and a weekly
|
|
274
|
-
non-video models and only a weekly one for video. Standard
|
|
275
|
-
on time-based plans report an explicit remaining percentage
|
|
276
|
-
and that percentage is enough for a row. Unlimited,
|
|
277
|
-
unsupported quotas are omitted (a status-3 row also
|
|
278
|
-
does not include); if none remain, the card reports
|
|
279
|
-
inventing a percentage. Explicit remaining percentages
|
|
280
|
-
responses follow the official CLI's remaining-count
|
|
281
|
-
live-account response was used in verification, so
|
|
282
|
-
come from the account or the plan rather than from
|
|
312
|
+
Configure `MINIMAX_API_KEY` for the global service or `MINIMAX_CN_API_KEY` for
|
|
313
|
+
China, using DSH's standard model-key configuration. The plugin reuses those
|
|
314
|
+
credential references; no separate subscription ref is required. Quota reads
|
|
315
|
+
require subscription keys, not `sk-api-*` pay-as-you-go keys. The plugin reads
|
|
316
|
+
the region's `/v1/token_plan/remains` endpoint and displays finite per-model
|
|
317
|
+
quotas with API-provided start/end times. It does not add model routing or OAuth
|
|
318
|
+
login. Rows are labelled with the model name, and the plugin shows the windows
|
|
319
|
+
the API returns for each model. MiniMax documents a five-hour and a weekly
|
|
320
|
+
window for non-video models and only a weekly one for video. Standard
|
|
321
|
+
(non-video) models on time-based plans report an explicit remaining percentage
|
|
322
|
+
beside zero counts, and that percentage is enough for a row. Unlimited,
|
|
323
|
+
boosted-above-100% and unsupported quotas are omitted (a status-3 row also
|
|
324
|
+
covers a model the plan does not include); if none remain, the card reports
|
|
325
|
+
unavailable rather than inventing a percentage. Explicit remaining percentages
|
|
326
|
+
take priority; older responses follow the official CLI's remaining-count
|
|
327
|
+
compatibility convention. No live-account response was used in verification, so
|
|
328
|
+
a missing model may still come from the account or the plan rather than from
|
|
329
|
+
this plugin.
|
|
283
330
|
|
|
284
331
|
## Development
|
|
285
332
|
|
|
@@ -289,16 +336,27 @@ pnpm build
|
|
|
289
336
|
pnpm test
|
|
290
337
|
```
|
|
291
338
|
|
|
292
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for the PR workflow, test boundaries,
|
|
293
|
-
|
|
339
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the PR workflow, test boundaries, and
|
|
340
|
+
isolated DSH checks.
|
|
294
341
|
|
|
295
342
|
The tests and checks (unit and integration tests, the host-export and host
|
|
296
|
-
contract checks, the boot smoke, and the host E2E) are documented in
|
|
297
|
-
support window and release process
|
|
298
|
-
[docs/compatibility.md](docs/compatibility.md).
|
|
299
|
-
|
|
300
|
-
The package is published to npm as `dsh-subscriptions`, starting with the first
|
|
343
|
+
contract checks, the boot smoke, and the host E2E) are documented in
|
|
344
|
+
[docs/testing.md](docs/testing.md); the DSH support window and release process
|
|
345
|
+
in [docs/compatibility.md](docs/compatibility.md).
|
|
346
|
+
|
|
347
|
+
The package is published to npm as `dsh-subscriptions`, starting with the first
|
|
348
|
+
release under the new name (GitHub source installation remains supported). The
|
|
349
|
+
`cordis.patch.yml` bundle entry and browser module ID use the same package name.
|
|
350
|
+
Earlier releases were published as `@goodboys-ai/dsh-subscription-hub`; that
|
|
351
|
+
name is deprecated and points here. If the old scoped package is installed,
|
|
352
|
+
remove it before adding the new one — installing both registers the same
|
|
353
|
+
adapters twice and breaks plugin load.
|
|
301
354
|
|
|
302
355
|
## Origins
|
|
303
356
|
|
|
304
|
-
This project started from
|
|
357
|
+
This project started from
|
|
358
|
+
[V1ki/dsh-plugin-subscriptions](https://github.com/V1ki/dsh-plugin-subscriptions)
|
|
359
|
+
at `090d964` under its MIT license and now includes upstream `0.9.5`
|
|
360
|
+
(`75a8346`). The upstream English and Chinese READMEs are kept in
|
|
361
|
+
[`docs/upstream-README.md`](docs/upstream-README.md) and
|
|
362
|
+
[`docs/upstream-README.zh.md`](docs/upstream-README.zh.md).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
const PRIMARY_SERVICE = 'Claude Code-credentials';
|
|
@@ -8,19 +8,30 @@ function toSession(data) {
|
|
|
8
8
|
// Empty-string tokens (seen from a corrupted Keychain item left by a Claude
|
|
9
9
|
// Code logout) pass the typeof gate but are useless and would poison the
|
|
10
10
|
// auth store — a single such entry fails every provider's status read.
|
|
11
|
-
if (typeof data.accessToken !== 'string' ||
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
if (typeof data.accessToken !== 'string' ||
|
|
12
|
+
data.accessToken.length === 0 ||
|
|
13
|
+
typeof data.refreshToken !== 'string' ||
|
|
14
|
+
data.refreshToken.length === 0 ||
|
|
15
|
+
typeof data.expiresAt !== 'number' ||
|
|
16
|
+
!Number.isFinite(data.expiresAt)) {
|
|
14
17
|
return undefined;
|
|
15
18
|
}
|
|
16
|
-
const scopes = Array.isArray(data.scopes)
|
|
19
|
+
const scopes = Array.isArray(data.scopes)
|
|
20
|
+
? data.scopes.join(' ')
|
|
21
|
+
: typeof data.scopes === 'string'
|
|
22
|
+
? data.scopes
|
|
23
|
+
: DEFAULT_SCOPES;
|
|
17
24
|
return {
|
|
18
25
|
accessToken: data.accessToken,
|
|
19
26
|
refreshToken: data.refreshToken,
|
|
20
27
|
expiresAt: Math.trunc(data.expiresAt),
|
|
21
28
|
scopes,
|
|
22
|
-
...typeof data.emailAddress === 'string'
|
|
23
|
-
|
|
29
|
+
...(typeof data.emailAddress === 'string'
|
|
30
|
+
? { emailAddress: data.emailAddress }
|
|
31
|
+
: {}),
|
|
32
|
+
...(typeof data.subscriptionType === 'string'
|
|
33
|
+
? { subscriptionType: data.subscriptionType }
|
|
34
|
+
: {}),
|
|
24
35
|
};
|
|
25
36
|
}
|
|
26
37
|
function parseBlob(raw) {
|
|
@@ -117,7 +128,16 @@ export function writeBackClaudeCodeCredentials(next, expectedPriorAccessToken) {
|
|
|
117
128
|
return false;
|
|
118
129
|
const account = getKeychainAccountName() ?? PRIMARY_SERVICE;
|
|
119
130
|
try {
|
|
120
|
-
execFileSync('/usr/bin/security', [
|
|
131
|
+
execFileSync('/usr/bin/security', [
|
|
132
|
+
'add-generic-password',
|
|
133
|
+
'-s',
|
|
134
|
+
PRIMARY_SERVICE,
|
|
135
|
+
'-a',
|
|
136
|
+
account,
|
|
137
|
+
'-w',
|
|
138
|
+
updated,
|
|
139
|
+
'-U',
|
|
140
|
+
], {
|
|
121
141
|
timeout: 2000,
|
|
122
142
|
stdio: 'ignore',
|
|
123
143
|
});
|
|
@@ -163,7 +183,9 @@ export function writeBackClaudeCodeCredentials(next, expectedPriorAccessToken) {
|
|
|
163
183
|
*/
|
|
164
184
|
export async function refreshClaudeSynced(session, doRefresh) {
|
|
165
185
|
const fromSource = readClaudeCodeCredentials();
|
|
166
|
-
const base = fromSource !== undefined && fromSource.accessToken !== session.accessToken
|
|
186
|
+
const base = fromSource !== undefined && fromSource.accessToken !== session.accessToken
|
|
187
|
+
? fromSource
|
|
188
|
+
: session;
|
|
167
189
|
if (base.expiresAt > Date.now() + 60_000)
|
|
168
190
|
return base;
|
|
169
191
|
const next = await doRefresh(base);
|
package/lib/auth/device-flow.js
CHANGED
|
@@ -70,18 +70,24 @@ export class DeviceFlowManager {
|
|
|
70
70
|
const response = await fetchFn(spec.deviceCodeUrl, {
|
|
71
71
|
method: 'POST',
|
|
72
72
|
headers: {
|
|
73
|
-
|
|
73
|
+
accept: 'application/json',
|
|
74
74
|
'content-type': 'application/x-www-form-urlencoded',
|
|
75
75
|
},
|
|
76
|
-
body: new URLSearchParams({
|
|
76
|
+
body: new URLSearchParams({
|
|
77
|
+
client_id: spec.clientId,
|
|
78
|
+
scope: spec.scope,
|
|
79
|
+
}).toString(),
|
|
77
80
|
});
|
|
78
81
|
if (!response.ok) {
|
|
79
82
|
throw new Error(`${provider} device-code request failed (HTTP ${String(response.status)})`);
|
|
80
83
|
}
|
|
81
|
-
const wire = await parseProviderJson(response, 'device code request');
|
|
82
|
-
if (typeof wire.device_code !== 'string' ||
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
const wire = (await parseProviderJson(response, 'device code request'));
|
|
85
|
+
if (typeof wire.device_code !== 'string' ||
|
|
86
|
+
wire.device_code.length === 0 ||
|
|
87
|
+
typeof wire.user_code !== 'string' ||
|
|
88
|
+
wire.user_code.length === 0 ||
|
|
89
|
+
typeof wire.verification_uri !== 'string' ||
|
|
90
|
+
wire.verification_uri.length === 0) {
|
|
85
91
|
throw new Error(`${provider} device-code response is missing device_code/user_code/verification_uri`);
|
|
86
92
|
}
|
|
87
93
|
const intervalSec = typeof wire.interval === 'number' && wire.interval > 0
|
|
@@ -124,7 +130,7 @@ export class DeviceFlowManager {
|
|
|
124
130
|
const pollResponse = await fetchFn(spec.tokenUrl, {
|
|
125
131
|
method: 'POST',
|
|
126
132
|
headers: {
|
|
127
|
-
|
|
133
|
+
accept: 'application/json',
|
|
128
134
|
'content-type': 'application/x-www-form-urlencoded',
|
|
129
135
|
},
|
|
130
136
|
body: new URLSearchParams({
|
|
@@ -134,8 +140,9 @@ export class DeviceFlowManager {
|
|
|
134
140
|
}).toString(),
|
|
135
141
|
signal: controller.signal,
|
|
136
142
|
});
|
|
137
|
-
const result = await parseProviderJson(pollResponse, 'device token poll');
|
|
138
|
-
if (typeof result.access_token === 'string' &&
|
|
143
|
+
const result = (await parseProviderJson(pollResponse, 'device token poll'));
|
|
144
|
+
if (typeof result.access_token === 'string' &&
|
|
145
|
+
result.access_token.length > 0) {
|
|
139
146
|
settle(undefined, result.access_token);
|
|
140
147
|
return;
|
|
141
148
|
}
|