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
|
@@ -33,6 +33,8 @@ export interface UsageWindow {
|
|
|
33
33
|
startsAt?: number;
|
|
34
34
|
windowDurationMs?: number;
|
|
35
35
|
fixedWindow?: boolean;
|
|
36
|
+
/** Display-only assumed start; the marker rides it, never pace. */
|
|
37
|
+
displayStartsAt?: number;
|
|
36
38
|
}
|
|
37
39
|
/** `usage` endpoint value: the node half owns this shape. */
|
|
38
40
|
export interface ProviderUsage {
|
|
@@ -19,13 +19,14 @@ import { ProviderAccountManager } from './ProviderAccountManager.js';
|
|
|
19
19
|
import { providerSettingsCss } from './provider-settings-styles.js';
|
|
20
20
|
import { ExternalUsageCards } from './ExternalUsageCards.js';
|
|
21
21
|
import { CursorCard } from './CursorCard.js';
|
|
22
|
+
import { OllamaCard } from './OllamaCard.js';
|
|
22
23
|
import { UsageMeter } from './UsageMeter.js';
|
|
23
|
-
import { ResetCreditsDisclosure, ResetCreditsErrorLine, showsResetCredits } from './reset-credits-view.js';
|
|
24
|
+
import { ResetCreditsDisclosure, ResetCreditsErrorLine, showsResetCredits, } from './reset-credits-view.js';
|
|
24
25
|
import { displayUsedPercent } from './usage-pace.js';
|
|
25
26
|
import { UsageBadgeDisplaySetting } from './UsageBadgeDisplaySetting.js';
|
|
26
27
|
import { USAGE_BADGE_REFRESH_EVENT } from './usage-badge-preferences.js';
|
|
27
28
|
import { subscriptionCardStyles as cardStyles } from './subscription-card-styles.js';
|
|
28
|
-
import { callSubscriptionsAuth, SubscriptionsAuthError } from './subscriptions-rpc.js';
|
|
29
|
+
import { callSubscriptionsAuth, SubscriptionsAuthError, } from './subscriptions-rpc.js';
|
|
29
30
|
export { callSubscriptionsAuth } from './subscriptions-rpc.js';
|
|
30
31
|
/** Poll cadence while a provider login attempt is busy. */
|
|
31
32
|
const POLL_INTERVAL_MS = 2000;
|
|
@@ -49,7 +50,7 @@ function messageOf(error) {
|
|
|
49
50
|
}
|
|
50
51
|
/** Copy a keyed map without the entries whose key is not in `live`. */
|
|
51
52
|
function dropStale(map, live) {
|
|
52
|
-
const stale = Object.keys(map).filter(key => !live.has(key));
|
|
53
|
+
const stale = Object.keys(map).filter((key) => !live.has(key));
|
|
53
54
|
if (stale.length === 0)
|
|
54
55
|
return map;
|
|
55
56
|
const next = { ...map };
|
|
@@ -73,61 +74,128 @@ function fallbackTranslate(key, params) {
|
|
|
73
74
|
}
|
|
74
75
|
const styles = {
|
|
75
76
|
section: {
|
|
76
|
-
display: 'flex',
|
|
77
|
+
display: 'flex',
|
|
78
|
+
flexDirection: 'column',
|
|
79
|
+
gap: 12,
|
|
80
|
+
maxWidth: 560,
|
|
77
81
|
color: 'var(--dsw-alias-label-primary)',
|
|
78
82
|
},
|
|
79
|
-
intro: {
|
|
80
|
-
|
|
83
|
+
intro: {
|
|
84
|
+
margin: 0,
|
|
85
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
86
|
+
fontSize: 14,
|
|
87
|
+
lineHeight: '22px',
|
|
88
|
+
},
|
|
89
|
+
card: {
|
|
90
|
+
...cardStyles.card,
|
|
81
91
|
border: '0.5px solid var(--dsw-alias-settings-card-stroke, var(--dsw-alias-border-l4))',
|
|
82
|
-
borderRadius: 'var(--dsw-radius-xl, 20px)',
|
|
92
|
+
borderRadius: 'var(--dsw-radius-xl, 20px)',
|
|
93
|
+
background: 'var(--dsw-alias-settings-card-fill, var(--dsw-alias-bg-layer-2))',
|
|
83
94
|
},
|
|
84
95
|
cardHeader: cardStyles.header,
|
|
85
96
|
dot: cardStyles.dot,
|
|
86
97
|
name: cardStyles.name,
|
|
87
98
|
statusLine: cardStyles.status,
|
|
88
|
-
clientVersion: {
|
|
99
|
+
clientVersion: {
|
|
100
|
+
fontSize: 12,
|
|
101
|
+
lineHeight: '18px',
|
|
102
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
103
|
+
fontVariantNumeric: 'tabular-nums',
|
|
104
|
+
},
|
|
89
105
|
errorLine: cardStyles.error,
|
|
90
106
|
actions: cardStyles.actions,
|
|
91
|
-
usage: {
|
|
107
|
+
usage: {
|
|
108
|
+
...cardStyles.usage,
|
|
109
|
+
borderTop: '0.5px solid var(--dsw-alias-border-l2)',
|
|
110
|
+
},
|
|
92
111
|
usageHeader: cardStyles.usageHeader,
|
|
93
112
|
usageTitle: cardStyles.usageTitle,
|
|
94
113
|
usagePlan: cardStyles.usagePlan,
|
|
95
|
-
usageRefresh: {
|
|
114
|
+
usageRefresh: {
|
|
115
|
+
minHeight: 22,
|
|
116
|
+
height: 22,
|
|
117
|
+
padding: '0 8px',
|
|
118
|
+
marginLeft: 'auto',
|
|
119
|
+
},
|
|
96
120
|
usageRow: cardStyles.usageRow,
|
|
97
121
|
usageMeta: cardStyles.usageMeta,
|
|
98
|
-
accountRow: {
|
|
122
|
+
accountRow: {
|
|
123
|
+
...cardStyles.account,
|
|
124
|
+
border: '0.5px solid var(--dsw-alias-border-l2)',
|
|
125
|
+
borderRadius: 'var(--dsw-radius-lg, 16px)',
|
|
126
|
+
},
|
|
99
127
|
accountHeader: cardStyles.accountHeader,
|
|
100
128
|
accountName: cardStyles.accountName,
|
|
101
129
|
starButton: {
|
|
102
|
-
border: 'none',
|
|
103
|
-
|
|
130
|
+
border: 'none',
|
|
131
|
+
padding: 0,
|
|
132
|
+
width: 28,
|
|
133
|
+
font: 'inherit',
|
|
134
|
+
fontSize: 14,
|
|
135
|
+
lineHeight: '20px',
|
|
136
|
+
cursor: 'pointer',
|
|
104
137
|
color: 'var(--dsw-alias-state-warn-label)',
|
|
105
138
|
},
|
|
106
139
|
deviceCode: {
|
|
107
|
-
marginTop: 4,
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
marginTop: 4,
|
|
141
|
+
display: 'flex',
|
|
142
|
+
flexDirection: 'column',
|
|
143
|
+
gap: 6,
|
|
144
|
+
border: '0.5px solid var(--dsw-alias-border-l2)',
|
|
145
|
+
borderRadius: 'var(--dsw-radius-lg, 16px)',
|
|
146
|
+
padding: '10px 12px',
|
|
147
|
+
background: 'var(--dsw-alias-bg-layer-1)',
|
|
110
148
|
},
|
|
111
149
|
deviceCodeText: {
|
|
112
|
-
fontFamily: 'monospace',
|
|
113
|
-
|
|
150
|
+
fontFamily: 'monospace',
|
|
151
|
+
fontSize: 18,
|
|
152
|
+
lineHeight: '24px',
|
|
153
|
+
letterSpacing: 2,
|
|
154
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
155
|
+
userSelect: 'all',
|
|
114
156
|
},
|
|
115
157
|
manual: { fontSize: 13, lineHeight: '20px' },
|
|
116
|
-
manualRow: {
|
|
158
|
+
manualRow: {
|
|
159
|
+
display: 'flex',
|
|
160
|
+
flexWrap: 'wrap',
|
|
161
|
+
gap: 8,
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
marginTop: 8,
|
|
164
|
+
},
|
|
117
165
|
manualInput: { flex: '1 1 200px', minWidth: 0 },
|
|
118
166
|
modalOverlay: {
|
|
119
|
-
position: 'fixed',
|
|
120
|
-
|
|
121
|
-
|
|
167
|
+
position: 'fixed',
|
|
168
|
+
inset: 0,
|
|
169
|
+
zIndex: 1000,
|
|
170
|
+
display: 'flex',
|
|
171
|
+
alignItems: 'center',
|
|
172
|
+
justifyContent: 'center',
|
|
173
|
+
padding: 16,
|
|
174
|
+
background: 'var(--dsw-alias-bg-mask-1, #0006)',
|
|
175
|
+
backdropFilter: 'var(--dsw-mask-blur, none)',
|
|
122
176
|
},
|
|
123
177
|
modal: {
|
|
124
|
-
width: 460,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
178
|
+
width: 460,
|
|
179
|
+
maxWidth: '100%',
|
|
180
|
+
maxHeight: '90vh',
|
|
181
|
+
overflowY: 'auto',
|
|
182
|
+
boxSizing: 'border-box',
|
|
183
|
+
display: 'flex',
|
|
184
|
+
flexDirection: 'column',
|
|
185
|
+
gap: 12,
|
|
186
|
+
padding: '16px 18px',
|
|
187
|
+
borderRadius: 'var(--dsw-radius-panel, 28px)',
|
|
188
|
+
background: 'var(--dsw-alias-bg-layer-2)',
|
|
189
|
+
border: 0,
|
|
190
|
+
boxShadow: 'var(--dsw-elevation-prominent)',
|
|
128
191
|
},
|
|
129
192
|
modalHeader: { display: 'flex', alignItems: 'center', gap: 8 },
|
|
130
|
-
modalTitle: {
|
|
193
|
+
modalTitle: {
|
|
194
|
+
fontWeight: 500,
|
|
195
|
+
fontSize: 16,
|
|
196
|
+
lineHeight: '24px',
|
|
197
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
198
|
+
},
|
|
131
199
|
};
|
|
132
200
|
/** Status dot color for one provider state. */
|
|
133
201
|
function dotColor(status) {
|
|
@@ -137,16 +205,6 @@ function dotColor(status) {
|
|
|
137
205
|
return 'var(--dsw-alias-state-success-primary)';
|
|
138
206
|
return 'var(--dsw-alias-label-dimmed)';
|
|
139
207
|
}
|
|
140
|
-
/**
|
|
141
|
-
* Whether a provider has at least one connected account. Multi-account made
|
|
142
|
-
* "logged in" a property of the account list rather than a flag, so every
|
|
143
|
-
* logged-in test goes through this one predicate.
|
|
144
|
-
* @param status - the provider's last reported state, possibly absent.
|
|
145
|
-
* @returns true when the provider serves at least one account.
|
|
146
|
-
*/
|
|
147
|
-
function hasAccount(status) {
|
|
148
|
-
return (status?.accounts.length ?? 0) > 0;
|
|
149
|
-
}
|
|
150
208
|
/**
|
|
151
209
|
* One-line status text for one provider state.
|
|
152
210
|
* @param t - section translate.
|
|
@@ -171,7 +229,10 @@ function statusText(t, status) {
|
|
|
171
229
|
export function clientVersionText(t, clientVersion) {
|
|
172
230
|
if (clientVersion === undefined)
|
|
173
231
|
return '';
|
|
174
|
-
return t('clientVersion', {
|
|
232
|
+
return t('clientVersion', {
|
|
233
|
+
version: clientVersion.version,
|
|
234
|
+
source: t(CLIENT_VERSION_SOURCES[clientVersion.source]),
|
|
235
|
+
});
|
|
175
236
|
}
|
|
176
237
|
/**
|
|
177
238
|
* Localized label of one usage window (kind, plus the model scope when named).
|
|
@@ -182,8 +243,12 @@ export function clientVersionText(t, clientVersion) {
|
|
|
182
243
|
function usageWindowLabel(t, window) {
|
|
183
244
|
const base = window.kind === 'session'
|
|
184
245
|
? t('usageSession')
|
|
185
|
-
: window.kind === 'weekly'
|
|
186
|
-
|
|
246
|
+
: window.kind === 'weekly'
|
|
247
|
+
? t('usageWeekly')
|
|
248
|
+
: t('usageWindow');
|
|
249
|
+
return window.scope !== undefined && window.scope !== ''
|
|
250
|
+
? `${base} · ${window.scope}`
|
|
251
|
+
: base;
|
|
187
252
|
}
|
|
188
253
|
/** Meter fill color lives with the pace logic: fresh 90%+ is red, pace presets add a yellow lead threshold. */
|
|
189
254
|
export { usageBarColor } from './usage-pace.js';
|
|
@@ -198,7 +263,11 @@ export function SubscriptionsSection(props) {
|
|
|
198
263
|
const [statuses, setStatuses] = useState({});
|
|
199
264
|
const [errors, setErrors] = useState({});
|
|
200
265
|
const [manualDrafts, setManualDrafts] = useState({
|
|
201
|
-
codex: '',
|
|
266
|
+
codex: '',
|
|
267
|
+
claude: '',
|
|
268
|
+
grok: '',
|
|
269
|
+
copilot: '',
|
|
270
|
+
antigravity: '',
|
|
202
271
|
});
|
|
203
272
|
/** Pending device-flow codes (copilot), shown while the attempt polls. */
|
|
204
273
|
const [deviceCodes, setDeviceCodes] = useState({});
|
|
@@ -268,7 +337,8 @@ export function SubscriptionsSection(props) {
|
|
|
268
337
|
return;
|
|
269
338
|
setStatuses(response.providers);
|
|
270
339
|
const signature = PROVIDERS.map(({ id }) => `${id}:${response.providers[id].accounts
|
|
271
|
-
.map(account => `${account.key}:${account.isDefault}`)
|
|
340
|
+
.map((account) => `${account.key}:${account.isDefault}`)
|
|
341
|
+
.join(',')}`).join(';');
|
|
272
342
|
if (signature !== usageRosterSignatureRef.current) {
|
|
273
343
|
usageRosterSignatureRef.current = signature;
|
|
274
344
|
window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
|
|
@@ -294,7 +364,9 @@ export function SubscriptionsSection(props) {
|
|
|
294
364
|
const startPolling = useCallback((provider) => {
|
|
295
365
|
if (pollersRef.current.has(provider))
|
|
296
366
|
return;
|
|
297
|
-
pollersRef.current.set(provider, setInterval(() => {
|
|
367
|
+
pollersRef.current.set(provider, setInterval(() => {
|
|
368
|
+
void refresh();
|
|
369
|
+
}, POLL_INTERVAL_MS));
|
|
298
370
|
}, [refresh]);
|
|
299
371
|
// Initial load; every busy provider (e.g. an attempt started before a page
|
|
300
372
|
// reload) resumes polling. Teardown clears pollers and the mounted guard.
|
|
@@ -313,6 +385,9 @@ export function SubscriptionsSection(props) {
|
|
|
313
385
|
});
|
|
314
386
|
return () => {
|
|
315
387
|
mountedRef.current = false;
|
|
388
|
+
// The ref holds pollers started after this effect began. Copying .current
|
|
389
|
+
// at setup would clear an empty set and leave those intervals running.
|
|
390
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- read the live set at unmount
|
|
316
391
|
for (const poller of pollersRef.current.values())
|
|
317
392
|
clearInterval(poller);
|
|
318
393
|
pollersRef.current.clear();
|
|
@@ -336,13 +411,16 @@ export function SubscriptionsSection(props) {
|
|
|
336
411
|
if (rpc === undefined || usageInflightRef.current.has(key))
|
|
337
412
|
return;
|
|
338
413
|
const read = (async () => {
|
|
339
|
-
setUsageLoading(prev => ({ ...prev, [key]: true }));
|
|
414
|
+
setUsageLoading((prev) => ({ ...prev, [key]: true }));
|
|
340
415
|
try {
|
|
341
|
-
const usage = await callSubscriptionsAuth(rpc, 'usage', { provider, account, ...force ? { force: true } : {} });
|
|
416
|
+
const usage = await callSubscriptionsAuth(rpc, 'usage', { provider, account, ...(force ? { force: true } : {}) });
|
|
342
417
|
if (!mountedRef.current)
|
|
343
418
|
return;
|
|
344
|
-
setUsages(prev => ({ ...prev, [key]: usage }));
|
|
345
|
-
setObservedAt(prev => ({
|
|
419
|
+
setUsages((prev) => ({ ...prev, [key]: usage }));
|
|
420
|
+
setObservedAt((prev) => ({
|
|
421
|
+
...prev,
|
|
422
|
+
[key]: usage.observedAt ?? Date.now(),
|
|
423
|
+
}));
|
|
346
424
|
window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
|
|
347
425
|
setUsageErrors((prev) => {
|
|
348
426
|
const next = { ...prev };
|
|
@@ -352,12 +430,12 @@ export function SubscriptionsSection(props) {
|
|
|
352
430
|
}
|
|
353
431
|
catch (error) {
|
|
354
432
|
if (mountedRef.current)
|
|
355
|
-
setUsageErrors(prev => ({ ...prev, [key]: messageOf(error) }));
|
|
433
|
+
setUsageErrors((prev) => ({ ...prev, [key]: messageOf(error) }));
|
|
356
434
|
}
|
|
357
435
|
finally {
|
|
358
436
|
usageInflightRef.current.delete(key);
|
|
359
437
|
if (mountedRef.current)
|
|
360
|
-
setUsageLoading(prev => ({ ...prev, [key]: false }));
|
|
438
|
+
setUsageLoading((prev) => ({ ...prev, [key]: false }));
|
|
361
439
|
}
|
|
362
440
|
})();
|
|
363
441
|
usageInflightRef.current.set(key, read);
|
|
@@ -385,7 +463,10 @@ export function SubscriptionsSection(props) {
|
|
|
385
463
|
}
|
|
386
464
|
}
|
|
387
465
|
async function consumeReset() {
|
|
388
|
-
if (!rpc ||
|
|
466
|
+
if (!rpc ||
|
|
467
|
+
!resetConfirmation ||
|
|
468
|
+
!resetAcknowledged ||
|
|
469
|
+
resetInflight.current)
|
|
389
470
|
return;
|
|
390
471
|
const { account, ticket } = resetConfirmation;
|
|
391
472
|
resetInflight.current = true;
|
|
@@ -417,8 +498,8 @@ export function SubscriptionsSection(props) {
|
|
|
417
498
|
void loadUsage(id, account.key);
|
|
418
499
|
}
|
|
419
500
|
}
|
|
420
|
-
setUsages(prev => dropStale(prev, live));
|
|
421
|
-
setUsageErrors(prev => dropStale(prev, live));
|
|
501
|
+
setUsages((prev) => dropStale(prev, live));
|
|
502
|
+
setUsageErrors((prev) => dropStale(prev, live));
|
|
422
503
|
}, [statuses, usages, usageErrors, loadUsage]);
|
|
423
504
|
const login = useCallback(async (provider, method) => {
|
|
424
505
|
if (rpc === undefined)
|
|
@@ -427,9 +508,10 @@ export function SubscriptionsSection(props) {
|
|
|
427
508
|
try {
|
|
428
509
|
const response = await callSubscriptionsAuth(rpc, 'login', {
|
|
429
510
|
provider,
|
|
430
|
-
...method === undefined ? {} : { method },
|
|
511
|
+
...(method === undefined ? {} : { method }),
|
|
431
512
|
});
|
|
432
|
-
if (typeof response.authorizeUrl === 'string' &&
|
|
513
|
+
if (typeof response.authorizeUrl === 'string' &&
|
|
514
|
+
response.authorizeUrl === '') {
|
|
433
515
|
// Instant login (e.g. imported from Claude Code credentials)
|
|
434
516
|
await refresh();
|
|
435
517
|
return;
|
|
@@ -440,14 +522,25 @@ export function SubscriptionsSection(props) {
|
|
|
440
522
|
if (!mountedRef.current)
|
|
441
523
|
return;
|
|
442
524
|
// Optimistic busy so Cancel and the manual fallback appear before the first poll tick.
|
|
443
|
-
setStatuses(prev => ({
|
|
525
|
+
setStatuses((prev) => ({
|
|
444
526
|
...prev,
|
|
445
|
-
[provider]: {
|
|
527
|
+
[provider]: {
|
|
528
|
+
accounts: prev[provider]?.accounts ?? [],
|
|
529
|
+
...prev[provider],
|
|
530
|
+
busy: true,
|
|
531
|
+
},
|
|
446
532
|
}));
|
|
447
|
-
if (typeof response.userCode === 'string' &&
|
|
533
|
+
if (typeof response.userCode === 'string' &&
|
|
534
|
+
response.userCode.length > 0) {
|
|
448
535
|
// Device flow: show the code card instead of opening the page blind —
|
|
449
536
|
// the user copies the code first, then opens the verification page.
|
|
450
|
-
setDeviceCodes(prev => ({
|
|
537
|
+
setDeviceCodes((prev) => ({
|
|
538
|
+
...prev,
|
|
539
|
+
[provider]: {
|
|
540
|
+
userCode: response.userCode,
|
|
541
|
+
verificationUrl: response.authorizeUrl,
|
|
542
|
+
},
|
|
543
|
+
}));
|
|
451
544
|
}
|
|
452
545
|
else {
|
|
453
546
|
window.open(response.authorizeUrl, '_blank', 'noopener');
|
|
@@ -457,7 +550,7 @@ export function SubscriptionsSection(props) {
|
|
|
457
550
|
catch (error) {
|
|
458
551
|
setProviderError(provider, messageOf(error));
|
|
459
552
|
}
|
|
460
|
-
}, [rpc, t, setProviderError, startPolling]);
|
|
553
|
+
}, [rpc, t, setProviderError, startPolling, refresh]);
|
|
461
554
|
const cancel = useCallback(async (provider) => {
|
|
462
555
|
if (rpc === undefined)
|
|
463
556
|
return;
|
|
@@ -478,9 +571,12 @@ export function SubscriptionsSection(props) {
|
|
|
478
571
|
return;
|
|
479
572
|
setProviderError(provider, undefined);
|
|
480
573
|
try {
|
|
481
|
-
await callSubscriptionsAuth(rpc, 'manual', {
|
|
574
|
+
await callSubscriptionsAuth(rpc, 'manual', {
|
|
575
|
+
provider,
|
|
576
|
+
input,
|
|
577
|
+
});
|
|
482
578
|
if (mountedRef.current)
|
|
483
|
-
setManualDrafts(prev => ({ ...prev, [provider]: '' }));
|
|
579
|
+
setManualDrafts((prev) => ({ ...prev, [provider]: '' }));
|
|
484
580
|
}
|
|
485
581
|
catch (error) {
|
|
486
582
|
setProviderError(provider, messageOf(error));
|
|
@@ -494,7 +590,10 @@ export function SubscriptionsSection(props) {
|
|
|
494
590
|
return;
|
|
495
591
|
setProviderError(provider, undefined);
|
|
496
592
|
try {
|
|
497
|
-
await callSubscriptionsAuth(rpc, 'logout', {
|
|
593
|
+
await callSubscriptionsAuth(rpc, 'logout', {
|
|
594
|
+
provider,
|
|
595
|
+
account,
|
|
596
|
+
});
|
|
498
597
|
}
|
|
499
598
|
catch (error) {
|
|
500
599
|
setProviderError(provider, messageOf(error));
|
|
@@ -506,7 +605,10 @@ export function SubscriptionsSection(props) {
|
|
|
506
605
|
return;
|
|
507
606
|
setProviderError(provider, undefined);
|
|
508
607
|
try {
|
|
509
|
-
await callSubscriptionsAuth(rpc, 'setDefault', {
|
|
608
|
+
await callSubscriptionsAuth(rpc, 'setDefault', {
|
|
609
|
+
provider,
|
|
610
|
+
account,
|
|
611
|
+
});
|
|
510
612
|
}
|
|
511
613
|
catch (error) {
|
|
512
614
|
setProviderError(provider, messageOf(error));
|
|
@@ -514,16 +616,19 @@ export function SubscriptionsSection(props) {
|
|
|
514
616
|
await refresh();
|
|
515
617
|
}, [rpc, setProviderError, refresh]);
|
|
516
618
|
const copyDeviceCode = useCallback((provider, userCode) => {
|
|
517
|
-
void navigator.clipboard
|
|
619
|
+
void navigator.clipboard
|
|
620
|
+
?.writeText(userCode)
|
|
621
|
+
.then(() => {
|
|
518
622
|
if (!mountedRef.current)
|
|
519
623
|
return;
|
|
520
624
|
setCopiedCode(provider);
|
|
521
625
|
setTimeout(() => {
|
|
522
626
|
if (mountedRef.current) {
|
|
523
|
-
setCopiedCode(current => current === provider ? undefined : current);
|
|
627
|
+
setCopiedCode((current) => current === provider ? undefined : current);
|
|
524
628
|
}
|
|
525
629
|
}, 1500);
|
|
526
|
-
})
|
|
630
|
+
})
|
|
631
|
+
.catch(() => undefined);
|
|
527
632
|
}, []);
|
|
528
633
|
if (rpc === undefined) {
|
|
529
634
|
return _jsx("p", { style: styles.intro, children: t('unavailable') });
|
|
@@ -533,34 +638,103 @@ export function SubscriptionsSection(props) {
|
|
|
533
638
|
const busy = status?.busy === true;
|
|
534
639
|
const deviceCode = deviceCodes[id];
|
|
535
640
|
const accounts = status?.accounts ?? [];
|
|
536
|
-
return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.cardHeader, children: [_jsx("span", { style: { ...styles.dot, background: dotColor(status) } }), _jsx("span", { style: styles.name, children: name }), status?.clientVersion !== undefined && (_jsx("span", { style: styles.clientVersion, title: t('clientVersionHint'), children: clientVersionText(t, status.clientVersion) }))] }), _jsx("p", { style: styles.statusLine, children: statusText(t, status) }), status?.detail !== undefined && status.detail !== '' && (_jsx("p", { style: styles.statusLine, children: status.detail })), errors[id] !== undefined && _jsx("p", { style: styles.errorLine, children: errors[id] }), accounts.map((account) => {
|
|
641
|
+
return (_jsxs("div", { style: styles.card, children: [_jsxs("div", { style: styles.cardHeader, children: [_jsx("span", { style: { ...styles.dot, background: dotColor(status) } }), _jsx("span", { style: styles.name, children: name }), status?.clientVersion !== undefined && (_jsx("span", { style: styles.clientVersion, title: t('clientVersionHint'), children: clientVersionText(t, status.clientVersion) }))] }), _jsx("p", { style: styles.statusLine, children: statusText(t, status) }), status?.detail !== undefined && status.detail !== '' && (_jsx("p", { style: styles.statusLine, children: status.detail })), errors[id] !== undefined && (_jsx("p", { style: styles.errorLine, children: errors[id] })), accounts.map((account) => {
|
|
537
642
|
const usageKey = `${id}:${account.key}`;
|
|
538
643
|
const usage = usages[usageKey];
|
|
539
644
|
const usageError = usageErrors[usageKey];
|
|
540
645
|
const display = account.account ?? account.key;
|
|
541
646
|
const sortedCredits = [...(usage?.resetCredits ?? [])].sort((a, b) => (a.expiresAt ?? Infinity) - (b.expiresAt ?? Infinity));
|
|
542
|
-
const nextReset = sortedCredits.find(c => c.expiresAt === undefined || c.expiresAt > Date.now());
|
|
543
|
-
const resetBlock = usage
|
|
647
|
+
const nextReset = sortedCredits.find((c) => c.expiresAt === undefined || c.expiresAt > Date.now());
|
|
648
|
+
const resetBlock = usage
|
|
649
|
+
? resetActionBlock(usage)
|
|
650
|
+
: 'resetUseRefresh';
|
|
544
651
|
const resetDisabled = resetBusy || resetBlock !== undefined;
|
|
545
652
|
// Providers without a usage endpoint answer supported:false — no block.
|
|
546
|
-
const showUsage = usage?.supported !== false
|
|
547
|
-
|
|
653
|
+
const showUsage = usage?.supported !== false &&
|
|
654
|
+
(usage !== undefined ||
|
|
655
|
+
usageError !== undefined ||
|
|
656
|
+
usageLoading[usageKey] === true);
|
|
548
657
|
return (_jsxs("div", { style: styles.accountRow, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("button", { type: "button", style: styles.starButton, title: account.isDefault ? t('defaultBadge') : t('setDefault'), onClick: () => {
|
|
549
658
|
if (!account.isDefault)
|
|
550
659
|
void setDefault(id, account.key);
|
|
551
|
-
}, children: account.isDefault ? '★' : '☆' }), _jsx("span", { style: styles.accountName, children: display }), account.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: account.plan })), account.expiresAt !== undefined && (_jsx("span", { style: styles.statusLine, children: t('accountExpires', {
|
|
660
|
+
}, children: account.isDefault ? '★' : '☆' }), _jsx("span", { style: styles.accountName, children: display }), account.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: account.plan })), account.expiresAt !== undefined && (_jsx("span", { style: styles.statusLine, children: t('accountExpires', {
|
|
661
|
+
date: new Date(account.expiresAt).toLocaleString(),
|
|
662
|
+
}) })), _jsx("button", { type: "button", style: { marginLeft: 'auto', flexShrink: 0 }, onClick: () => {
|
|
663
|
+
void logout(id, account.key, display, name);
|
|
664
|
+
}, children: t('logout') })] }), showUsage && (_jsxs("div", { style: styles.usage, children: [_jsxs("div", { style: styles.usageHeader, children: [_jsx("span", { style: styles.usageTitle, children: t('usageTitle') }), usage?.plan !== undefined && (_jsx("span", { style: styles.usagePlan, children: t('usagePlan', { plan: usage.plan }) })), _jsx("button", { type: "button", style: styles.usageRefresh, disabled: usageLoading[usageKey] === true, onClick: () => {
|
|
665
|
+
void loadUsage(id, account.key, true);
|
|
666
|
+
}, children: t('usageRefresh') })] }), usage === undefined && usageError === undefined && (_jsx("p", { style: styles.statusLine, children: t('usageLoading') })), usageError !== undefined && (_jsx("p", { style: styles.errorLine, children: t('usageError', { message: usageError }) })), usage?.windows !== undefined &&
|
|
667
|
+
usage.windows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageEmpty') })), (usage?.windows ?? []).map((window, index) => {
|
|
552
668
|
const percent = displayUsedPercent(window.usedPercent);
|
|
553
|
-
return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: usageWindowLabel(t, window) }), _jsxs("span", { children: [percent === undefined
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
669
|
+
return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: usageWindowLabel(t, window) }), _jsxs("span", { children: [percent === undefined
|
|
670
|
+
? t('usageMeterInvalid')
|
|
671
|
+
: `${percent}%`, window.resetsAt !== undefined &&
|
|
672
|
+
` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx(UsageMeter, { window: window, t: t, observedAt: observedAt[usageKey], stale: usageError !== undefined || usage.stale === true })] }, index));
|
|
673
|
+
}), (id === 'codex' || id === 'claude') &&
|
|
674
|
+
usage?.resetCreditsError !== undefined && (_jsx(ResetCreditsErrorLine, { provider: id, message: usage.resetCreditsError, t: t })), (id === 'codex' || id === 'claude') &&
|
|
675
|
+
showsResetCredits(id, usage?.resetCredits) && (_jsx(ResetCreditsDisclosure, { mode: id === 'claude' ? 'grants' : 'credits', credits: sortedCredits, t: t, ...(nextReset !== undefined
|
|
676
|
+
? { nextCredit: nextReset }
|
|
677
|
+
: {}), ...(id === 'codex'
|
|
678
|
+
? {
|
|
679
|
+
useAction: {
|
|
680
|
+
accountKey: account.key,
|
|
681
|
+
busy: resetBusy && resetAccount === account.key,
|
|
682
|
+
disabled: resetDisabled,
|
|
683
|
+
title: t(resetBusy
|
|
684
|
+
? 'resetUseChecking'
|
|
685
|
+
: (resetBlock ?? 'resetUseReady')),
|
|
686
|
+
label: t('resetUseButton'),
|
|
687
|
+
onUse: () => {
|
|
688
|
+
if (!resetDisabled)
|
|
689
|
+
void prepareReset(account.key);
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
}
|
|
693
|
+
: {}), ...(resetAccount === account.key &&
|
|
694
|
+
resetMessage !== ''
|
|
695
|
+
? { statusMessage: resetMessage }
|
|
696
|
+
: {}) }))] }))] }, account.key));
|
|
697
|
+
}), _jsxs("div", { style: styles.actions, children: [!busy && accounts.length === 0 && (_jsx("button", { type: "button", onClick: () => {
|
|
698
|
+
void login(id);
|
|
699
|
+
}, children: t('login') })), !busy && accounts.length > 0 && id === 'claude' && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => {
|
|
700
|
+
void login(id, 'oauth');
|
|
701
|
+
}, children: t('addAccountOAuth') }), _jsx("button", { type: "button", onClick: () => {
|
|
702
|
+
void login(id, 'keychain');
|
|
703
|
+
}, children: t('addAccountKeychain') })] })), !busy && accounts.length > 0 && id !== 'claude' && (_jsx("button", { type: "button", onClick: () => {
|
|
704
|
+
void login(id);
|
|
705
|
+
}, children: t('addAccount') })), _jsx("button", { type: "button", "aria-haspopup": "dialog", onClick: () => setManagedProvider({ id, name }), children: t('accountsManage') }), busy && (_jsx("button", { type: "button", onClick: () => {
|
|
706
|
+
void cancel(id);
|
|
707
|
+
}, children: t('cancel') }))] }), !busy && accounts.length > 0 && (_jsx("p", { style: styles.statusLine, children: t('addAccountHint') })), busy && deviceCode !== undefined && (_jsxs("div", { style: styles.deviceCode, children: [_jsx("span", { style: styles.statusLine, children: t('deviceCodePrompt') }), _jsx("span", { style: styles.deviceCodeText, children: deviceCode.userCode }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { type: "button", onClick: () => {
|
|
708
|
+
copyDeviceCode(id, deviceCode.userCode);
|
|
709
|
+
}, children: copiedCode === id
|
|
710
|
+
? t('deviceCodeCopied')
|
|
711
|
+
: t('deviceCodeCopy') }), _jsx("button", { type: "button", onClick: () => {
|
|
712
|
+
window.open(deviceCode.verificationUrl, '_blank', 'noopener');
|
|
713
|
+
}, children: t('deviceCodeOpenPage') })] })] })), busy && deviceCode === undefined && (_jsxs("details", { style: styles.manual, children: [_jsx("summary", { children: t('manualSummary') }), _jsxs("div", { style: styles.manualRow, children: [_jsx("input", { style: styles.manualInput, value: manualDrafts[id], placeholder: t('manualPlaceholder'), onChange: (event) => setManualDrafts((prev) => ({
|
|
714
|
+
...prev,
|
|
715
|
+
[id]: event.target.value,
|
|
716
|
+
})) }), _jsx("button", { type: "button", onClick: () => {
|
|
717
|
+
void submitManual(id);
|
|
718
|
+
}, children: t('submit') })] })] }))] }, id));
|
|
719
|
+
}), _jsx(CursorCard, { rpc: rpc, t: t }), _jsx(OllamaCard, { rpc: rpc, t: t }), _jsx(ExternalUsageCards, { rpc: rpc, t: t }), managedProvider && (_jsx(ProviderAccountManager, { provider: managedProvider.id, name: managedProvider.name, rpc: rpc, t: t, onClose: () => setManagedProvider(undefined) })), _jsx("style", { children: '.subscriptions-reset-dialog::backdrop { background: var(--dsw-alias-bg-mask-1, #0006); backdrop-filter: var(--dsw-mask-blur, none); }' }), _jsx("dialog", { ref: resetDialogRef, className: "subscriptions-reset-dialog", "aria-labelledby": "subscriptions-reset-title", onCancel: () => setResetConfirmation(undefined), onClose: () => setResetConfirmation(undefined), style: {
|
|
720
|
+
...styles.modal,
|
|
721
|
+
display: resetConfirmation ? 'flex' : 'none',
|
|
722
|
+
margin: 'auto',
|
|
723
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
724
|
+
}, children: resetConfirmation && (_jsxs(_Fragment, { children: [_jsx("h3", { id: "subscriptions-reset-title", style: { ...styles.modalTitle, margin: 0 }, children: t('resetUseConfirmTitle') }), _jsx("p", { style: { ...styles.statusLine, overflowWrap: 'anywhere' }, children: statuses.codex?.accounts.find((a) => a.key === resetConfirmation.account)?.account ?? resetConfirmation.account }), _jsxs("div", { style: { ...styles.usageMeta, gap: 12 }, children: [_jsx("span", { children: t('usageWeekly') }), _jsxs("strong", { children: [Math.round(resetConfirmation.weeklyUsedPercent), "%"] })] }), _jsx("div", { style: styles.usageMeta, children: _jsx("span", { children: resetConfirmation.creditExpiresAt === undefined
|
|
725
|
+
? t('resetCreditExpiryUnknown')
|
|
726
|
+
: t('resetCreditExpires', {
|
|
727
|
+
date: new Date(resetConfirmation.creditExpiresAt).toLocaleString(),
|
|
728
|
+
}) }) }), _jsx("p", { style: { ...styles.statusLine, margin: '4px 0' }, children: t('resetUseWarning') }), _jsxs("label", { style: {
|
|
729
|
+
display: 'flex',
|
|
730
|
+
gap: 8,
|
|
731
|
+
alignItems: 'flex-start',
|
|
732
|
+
fontSize: 12,
|
|
733
|
+
}, children: [_jsx("input", { type: "checkbox", checked: resetAcknowledged, onChange: (e) => setResetAcknowledged(e.target.checked) }), t('resetUseAcknowledge')] }), _jsxs("div", { className: "dsh-subscription-dialog-actions", style: {
|
|
734
|
+
...styles.actions,
|
|
735
|
+
justifyContent: 'flex-end',
|
|
736
|
+
marginTop: 8,
|
|
737
|
+
}, children: [_jsx("button", { type: "button", autoFocus: true, onClick: () => setResetConfirmation(undefined), children: t('cancel') }), _jsx("button", { type: "button", className: "dsh-subscription-primary", disabled: !resetAcknowledged || resetBusy, onClick: () => {
|
|
738
|
+
void consumeReset();
|
|
739
|
+
}, children: t('resetUseConfirm') })] })] })) })] }));
|
|
566
740
|
}
|