dsh-subscriptions 0.2.0 → 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 -180
- 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.js +74 -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 +1 -1
- package/lib/client/OllamaCard.js +26 -8
- package/lib/client/OllamaSettingsManager.js +113 -30
- package/lib/client/ProviderAccountManager.d.ts +1 -1
- package/lib/client/ProviderAccountManager.js +164 -49
- package/lib/client/ProviderModelEditor.js +107 -26
- package/lib/client/SpeedSelect.d.ts +62 -14
- package/lib/client/SpeedSelect.js +257 -46
- package/lib/client/SubscriptionUsageBadge.d.ts +4 -10
- package/lib/client/SubscriptionUsageBadge.js +300 -112
- package/lib/client/SubscriptionsSection.js +258 -85
- package/lib/client/UsageBadgeDisplaySetting.js +35 -12
- package/lib/client/UsageMeter.d.ts +1 -1
- package/lib/client/UsageMeter.js +62 -24
- 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 +10 -0
- package/lib/client/locales.js +61 -51
- package/lib/client/ollama-draft.js +21 -12
- 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.js +30 -12
- package/lib/client.js +287 -47
- package/lib/client.js.map +1 -1
- package/lib/http.js +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.js +118 -64
- package/lib/model-defaults.js +4 -2
- package/lib/provider-settings.d.ts +12 -0
- package/lib/provider-settings.js +58 -16
- 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 +9 -2
- package/lib/providers/claude.js +153 -90
- 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.js +71 -39
- 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.js +14 -5
- package/lib/providers/external-usage.js +59 -23
- 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.js +24 -8
- package/lib/providers/ollama-web.js +29 -11
- package/lib/providers/ollama.js +129 -66
- 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
|
@@ -21,12 +21,12 @@ import { ExternalUsageCards } from './ExternalUsageCards.js';
|
|
|
21
21
|
import { CursorCard } from './CursorCard.js';
|
|
22
22
|
import { OllamaCard } from './OllamaCard.js';
|
|
23
23
|
import { UsageMeter } from './UsageMeter.js';
|
|
24
|
-
import { ResetCreditsDisclosure, ResetCreditsErrorLine, showsResetCredits } from './reset-credits-view.js';
|
|
24
|
+
import { ResetCreditsDisclosure, ResetCreditsErrorLine, showsResetCredits, } from './reset-credits-view.js';
|
|
25
25
|
import { displayUsedPercent } from './usage-pace.js';
|
|
26
26
|
import { UsageBadgeDisplaySetting } from './UsageBadgeDisplaySetting.js';
|
|
27
27
|
import { USAGE_BADGE_REFRESH_EVENT } from './usage-badge-preferences.js';
|
|
28
28
|
import { subscriptionCardStyles as cardStyles } from './subscription-card-styles.js';
|
|
29
|
-
import { callSubscriptionsAuth, SubscriptionsAuthError } from './subscriptions-rpc.js';
|
|
29
|
+
import { callSubscriptionsAuth, SubscriptionsAuthError, } from './subscriptions-rpc.js';
|
|
30
30
|
export { callSubscriptionsAuth } from './subscriptions-rpc.js';
|
|
31
31
|
/** Poll cadence while a provider login attempt is busy. */
|
|
32
32
|
const POLL_INTERVAL_MS = 2000;
|
|
@@ -50,7 +50,7 @@ function messageOf(error) {
|
|
|
50
50
|
}
|
|
51
51
|
/** Copy a keyed map without the entries whose key is not in `live`. */
|
|
52
52
|
function dropStale(map, live) {
|
|
53
|
-
const stale = Object.keys(map).filter(key => !live.has(key));
|
|
53
|
+
const stale = Object.keys(map).filter((key) => !live.has(key));
|
|
54
54
|
if (stale.length === 0)
|
|
55
55
|
return map;
|
|
56
56
|
const next = { ...map };
|
|
@@ -74,61 +74,128 @@ function fallbackTranslate(key, params) {
|
|
|
74
74
|
}
|
|
75
75
|
const styles = {
|
|
76
76
|
section: {
|
|
77
|
-
display: 'flex',
|
|
77
|
+
display: 'flex',
|
|
78
|
+
flexDirection: 'column',
|
|
79
|
+
gap: 12,
|
|
80
|
+
maxWidth: 560,
|
|
78
81
|
color: 'var(--dsw-alias-label-primary)',
|
|
79
82
|
},
|
|
80
|
-
intro: {
|
|
81
|
-
|
|
83
|
+
intro: {
|
|
84
|
+
margin: 0,
|
|
85
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
86
|
+
fontSize: 14,
|
|
87
|
+
lineHeight: '22px',
|
|
88
|
+
},
|
|
89
|
+
card: {
|
|
90
|
+
...cardStyles.card,
|
|
82
91
|
border: '0.5px solid var(--dsw-alias-settings-card-stroke, var(--dsw-alias-border-l4))',
|
|
83
|
-
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))',
|
|
84
94
|
},
|
|
85
95
|
cardHeader: cardStyles.header,
|
|
86
96
|
dot: cardStyles.dot,
|
|
87
97
|
name: cardStyles.name,
|
|
88
98
|
statusLine: cardStyles.status,
|
|
89
|
-
clientVersion: {
|
|
99
|
+
clientVersion: {
|
|
100
|
+
fontSize: 12,
|
|
101
|
+
lineHeight: '18px',
|
|
102
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
103
|
+
fontVariantNumeric: 'tabular-nums',
|
|
104
|
+
},
|
|
90
105
|
errorLine: cardStyles.error,
|
|
91
106
|
actions: cardStyles.actions,
|
|
92
|
-
usage: {
|
|
107
|
+
usage: {
|
|
108
|
+
...cardStyles.usage,
|
|
109
|
+
borderTop: '0.5px solid var(--dsw-alias-border-l2)',
|
|
110
|
+
},
|
|
93
111
|
usageHeader: cardStyles.usageHeader,
|
|
94
112
|
usageTitle: cardStyles.usageTitle,
|
|
95
113
|
usagePlan: cardStyles.usagePlan,
|
|
96
|
-
usageRefresh: {
|
|
114
|
+
usageRefresh: {
|
|
115
|
+
minHeight: 22,
|
|
116
|
+
height: 22,
|
|
117
|
+
padding: '0 8px',
|
|
118
|
+
marginLeft: 'auto',
|
|
119
|
+
},
|
|
97
120
|
usageRow: cardStyles.usageRow,
|
|
98
121
|
usageMeta: cardStyles.usageMeta,
|
|
99
|
-
accountRow: {
|
|
122
|
+
accountRow: {
|
|
123
|
+
...cardStyles.account,
|
|
124
|
+
border: '0.5px solid var(--dsw-alias-border-l2)',
|
|
125
|
+
borderRadius: 'var(--dsw-radius-lg, 16px)',
|
|
126
|
+
},
|
|
100
127
|
accountHeader: cardStyles.accountHeader,
|
|
101
128
|
accountName: cardStyles.accountName,
|
|
102
129
|
starButton: {
|
|
103
|
-
border: 'none',
|
|
104
|
-
|
|
130
|
+
border: 'none',
|
|
131
|
+
padding: 0,
|
|
132
|
+
width: 28,
|
|
133
|
+
font: 'inherit',
|
|
134
|
+
fontSize: 14,
|
|
135
|
+
lineHeight: '20px',
|
|
136
|
+
cursor: 'pointer',
|
|
105
137
|
color: 'var(--dsw-alias-state-warn-label)',
|
|
106
138
|
},
|
|
107
139
|
deviceCode: {
|
|
108
|
-
marginTop: 4,
|
|
109
|
-
|
|
110
|
-
|
|
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)',
|
|
111
148
|
},
|
|
112
149
|
deviceCodeText: {
|
|
113
|
-
fontFamily: 'monospace',
|
|
114
|
-
|
|
150
|
+
fontFamily: 'monospace',
|
|
151
|
+
fontSize: 18,
|
|
152
|
+
lineHeight: '24px',
|
|
153
|
+
letterSpacing: 2,
|
|
154
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
155
|
+
userSelect: 'all',
|
|
115
156
|
},
|
|
116
157
|
manual: { fontSize: 13, lineHeight: '20px' },
|
|
117
|
-
manualRow: {
|
|
158
|
+
manualRow: {
|
|
159
|
+
display: 'flex',
|
|
160
|
+
flexWrap: 'wrap',
|
|
161
|
+
gap: 8,
|
|
162
|
+
alignItems: 'center',
|
|
163
|
+
marginTop: 8,
|
|
164
|
+
},
|
|
118
165
|
manualInput: { flex: '1 1 200px', minWidth: 0 },
|
|
119
166
|
modalOverlay: {
|
|
120
|
-
position: 'fixed',
|
|
121
|
-
|
|
122
|
-
|
|
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)',
|
|
123
176
|
},
|
|
124
177
|
modal: {
|
|
125
|
-
width: 460,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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)',
|
|
129
191
|
},
|
|
130
192
|
modalHeader: { display: 'flex', alignItems: 'center', gap: 8 },
|
|
131
|
-
modalTitle: {
|
|
193
|
+
modalTitle: {
|
|
194
|
+
fontWeight: 500,
|
|
195
|
+
fontSize: 16,
|
|
196
|
+
lineHeight: '24px',
|
|
197
|
+
color: 'var(--dsw-alias-label-primary)',
|
|
198
|
+
},
|
|
132
199
|
};
|
|
133
200
|
/** Status dot color for one provider state. */
|
|
134
201
|
function dotColor(status) {
|
|
@@ -138,16 +205,6 @@ function dotColor(status) {
|
|
|
138
205
|
return 'var(--dsw-alias-state-success-primary)';
|
|
139
206
|
return 'var(--dsw-alias-label-dimmed)';
|
|
140
207
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Whether a provider has at least one connected account. Multi-account made
|
|
143
|
-
* "logged in" a property of the account list rather than a flag, so every
|
|
144
|
-
* logged-in test goes through this one predicate.
|
|
145
|
-
* @param status - the provider's last reported state, possibly absent.
|
|
146
|
-
* @returns true when the provider serves at least one account.
|
|
147
|
-
*/
|
|
148
|
-
function hasAccount(status) {
|
|
149
|
-
return (status?.accounts.length ?? 0) > 0;
|
|
150
|
-
}
|
|
151
208
|
/**
|
|
152
209
|
* One-line status text for one provider state.
|
|
153
210
|
* @param t - section translate.
|
|
@@ -172,7 +229,10 @@ function statusText(t, status) {
|
|
|
172
229
|
export function clientVersionText(t, clientVersion) {
|
|
173
230
|
if (clientVersion === undefined)
|
|
174
231
|
return '';
|
|
175
|
-
return t('clientVersion', {
|
|
232
|
+
return t('clientVersion', {
|
|
233
|
+
version: clientVersion.version,
|
|
234
|
+
source: t(CLIENT_VERSION_SOURCES[clientVersion.source]),
|
|
235
|
+
});
|
|
176
236
|
}
|
|
177
237
|
/**
|
|
178
238
|
* Localized label of one usage window (kind, plus the model scope when named).
|
|
@@ -183,8 +243,12 @@ export function clientVersionText(t, clientVersion) {
|
|
|
183
243
|
function usageWindowLabel(t, window) {
|
|
184
244
|
const base = window.kind === 'session'
|
|
185
245
|
? t('usageSession')
|
|
186
|
-
: window.kind === 'weekly'
|
|
187
|
-
|
|
246
|
+
: window.kind === 'weekly'
|
|
247
|
+
? t('usageWeekly')
|
|
248
|
+
: t('usageWindow');
|
|
249
|
+
return window.scope !== undefined && window.scope !== ''
|
|
250
|
+
? `${base} · ${window.scope}`
|
|
251
|
+
: base;
|
|
188
252
|
}
|
|
189
253
|
/** Meter fill color lives with the pace logic: fresh 90%+ is red, pace presets add a yellow lead threshold. */
|
|
190
254
|
export { usageBarColor } from './usage-pace.js';
|
|
@@ -199,7 +263,11 @@ export function SubscriptionsSection(props) {
|
|
|
199
263
|
const [statuses, setStatuses] = useState({});
|
|
200
264
|
const [errors, setErrors] = useState({});
|
|
201
265
|
const [manualDrafts, setManualDrafts] = useState({
|
|
202
|
-
codex: '',
|
|
266
|
+
codex: '',
|
|
267
|
+
claude: '',
|
|
268
|
+
grok: '',
|
|
269
|
+
copilot: '',
|
|
270
|
+
antigravity: '',
|
|
203
271
|
});
|
|
204
272
|
/** Pending device-flow codes (copilot), shown while the attempt polls. */
|
|
205
273
|
const [deviceCodes, setDeviceCodes] = useState({});
|
|
@@ -269,7 +337,8 @@ export function SubscriptionsSection(props) {
|
|
|
269
337
|
return;
|
|
270
338
|
setStatuses(response.providers);
|
|
271
339
|
const signature = PROVIDERS.map(({ id }) => `${id}:${response.providers[id].accounts
|
|
272
|
-
.map(account => `${account.key}:${account.isDefault}`)
|
|
340
|
+
.map((account) => `${account.key}:${account.isDefault}`)
|
|
341
|
+
.join(',')}`).join(';');
|
|
273
342
|
if (signature !== usageRosterSignatureRef.current) {
|
|
274
343
|
usageRosterSignatureRef.current = signature;
|
|
275
344
|
window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
|
|
@@ -295,7 +364,9 @@ export function SubscriptionsSection(props) {
|
|
|
295
364
|
const startPolling = useCallback((provider) => {
|
|
296
365
|
if (pollersRef.current.has(provider))
|
|
297
366
|
return;
|
|
298
|
-
pollersRef.current.set(provider, setInterval(() => {
|
|
367
|
+
pollersRef.current.set(provider, setInterval(() => {
|
|
368
|
+
void refresh();
|
|
369
|
+
}, POLL_INTERVAL_MS));
|
|
299
370
|
}, [refresh]);
|
|
300
371
|
// Initial load; every busy provider (e.g. an attempt started before a page
|
|
301
372
|
// reload) resumes polling. Teardown clears pollers and the mounted guard.
|
|
@@ -314,6 +385,9 @@ export function SubscriptionsSection(props) {
|
|
|
314
385
|
});
|
|
315
386
|
return () => {
|
|
316
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
|
|
317
391
|
for (const poller of pollersRef.current.values())
|
|
318
392
|
clearInterval(poller);
|
|
319
393
|
pollersRef.current.clear();
|
|
@@ -337,13 +411,16 @@ export function SubscriptionsSection(props) {
|
|
|
337
411
|
if (rpc === undefined || usageInflightRef.current.has(key))
|
|
338
412
|
return;
|
|
339
413
|
const read = (async () => {
|
|
340
|
-
setUsageLoading(prev => ({ ...prev, [key]: true }));
|
|
414
|
+
setUsageLoading((prev) => ({ ...prev, [key]: true }));
|
|
341
415
|
try {
|
|
342
|
-
const usage = await callSubscriptionsAuth(rpc, 'usage', { provider, account, ...force ? { force: true } : {} });
|
|
416
|
+
const usage = await callSubscriptionsAuth(rpc, 'usage', { provider, account, ...(force ? { force: true } : {}) });
|
|
343
417
|
if (!mountedRef.current)
|
|
344
418
|
return;
|
|
345
|
-
setUsages(prev => ({ ...prev, [key]: usage }));
|
|
346
|
-
setObservedAt(prev => ({
|
|
419
|
+
setUsages((prev) => ({ ...prev, [key]: usage }));
|
|
420
|
+
setObservedAt((prev) => ({
|
|
421
|
+
...prev,
|
|
422
|
+
[key]: usage.observedAt ?? Date.now(),
|
|
423
|
+
}));
|
|
347
424
|
window.dispatchEvent(new Event(USAGE_BADGE_REFRESH_EVENT));
|
|
348
425
|
setUsageErrors((prev) => {
|
|
349
426
|
const next = { ...prev };
|
|
@@ -353,12 +430,12 @@ export function SubscriptionsSection(props) {
|
|
|
353
430
|
}
|
|
354
431
|
catch (error) {
|
|
355
432
|
if (mountedRef.current)
|
|
356
|
-
setUsageErrors(prev => ({ ...prev, [key]: messageOf(error) }));
|
|
433
|
+
setUsageErrors((prev) => ({ ...prev, [key]: messageOf(error) }));
|
|
357
434
|
}
|
|
358
435
|
finally {
|
|
359
436
|
usageInflightRef.current.delete(key);
|
|
360
437
|
if (mountedRef.current)
|
|
361
|
-
setUsageLoading(prev => ({ ...prev, [key]: false }));
|
|
438
|
+
setUsageLoading((prev) => ({ ...prev, [key]: false }));
|
|
362
439
|
}
|
|
363
440
|
})();
|
|
364
441
|
usageInflightRef.current.set(key, read);
|
|
@@ -386,7 +463,10 @@ export function SubscriptionsSection(props) {
|
|
|
386
463
|
}
|
|
387
464
|
}
|
|
388
465
|
async function consumeReset() {
|
|
389
|
-
if (!rpc ||
|
|
466
|
+
if (!rpc ||
|
|
467
|
+
!resetConfirmation ||
|
|
468
|
+
!resetAcknowledged ||
|
|
469
|
+
resetInflight.current)
|
|
390
470
|
return;
|
|
391
471
|
const { account, ticket } = resetConfirmation;
|
|
392
472
|
resetInflight.current = true;
|
|
@@ -418,8 +498,8 @@ export function SubscriptionsSection(props) {
|
|
|
418
498
|
void loadUsage(id, account.key);
|
|
419
499
|
}
|
|
420
500
|
}
|
|
421
|
-
setUsages(prev => dropStale(prev, live));
|
|
422
|
-
setUsageErrors(prev => dropStale(prev, live));
|
|
501
|
+
setUsages((prev) => dropStale(prev, live));
|
|
502
|
+
setUsageErrors((prev) => dropStale(prev, live));
|
|
423
503
|
}, [statuses, usages, usageErrors, loadUsage]);
|
|
424
504
|
const login = useCallback(async (provider, method) => {
|
|
425
505
|
if (rpc === undefined)
|
|
@@ -428,9 +508,10 @@ export function SubscriptionsSection(props) {
|
|
|
428
508
|
try {
|
|
429
509
|
const response = await callSubscriptionsAuth(rpc, 'login', {
|
|
430
510
|
provider,
|
|
431
|
-
...method === undefined ? {} : { method },
|
|
511
|
+
...(method === undefined ? {} : { method }),
|
|
432
512
|
});
|
|
433
|
-
if (typeof response.authorizeUrl === 'string' &&
|
|
513
|
+
if (typeof response.authorizeUrl === 'string' &&
|
|
514
|
+
response.authorizeUrl === '') {
|
|
434
515
|
// Instant login (e.g. imported from Claude Code credentials)
|
|
435
516
|
await refresh();
|
|
436
517
|
return;
|
|
@@ -441,14 +522,25 @@ export function SubscriptionsSection(props) {
|
|
|
441
522
|
if (!mountedRef.current)
|
|
442
523
|
return;
|
|
443
524
|
// Optimistic busy so Cancel and the manual fallback appear before the first poll tick.
|
|
444
|
-
setStatuses(prev => ({
|
|
525
|
+
setStatuses((prev) => ({
|
|
445
526
|
...prev,
|
|
446
|
-
[provider]: {
|
|
527
|
+
[provider]: {
|
|
528
|
+
accounts: prev[provider]?.accounts ?? [],
|
|
529
|
+
...prev[provider],
|
|
530
|
+
busy: true,
|
|
531
|
+
},
|
|
447
532
|
}));
|
|
448
|
-
if (typeof response.userCode === 'string' &&
|
|
533
|
+
if (typeof response.userCode === 'string' &&
|
|
534
|
+
response.userCode.length > 0) {
|
|
449
535
|
// Device flow: show the code card instead of opening the page blind —
|
|
450
536
|
// the user copies the code first, then opens the verification page.
|
|
451
|
-
setDeviceCodes(prev => ({
|
|
537
|
+
setDeviceCodes((prev) => ({
|
|
538
|
+
...prev,
|
|
539
|
+
[provider]: {
|
|
540
|
+
userCode: response.userCode,
|
|
541
|
+
verificationUrl: response.authorizeUrl,
|
|
542
|
+
},
|
|
543
|
+
}));
|
|
452
544
|
}
|
|
453
545
|
else {
|
|
454
546
|
window.open(response.authorizeUrl, '_blank', 'noopener');
|
|
@@ -458,7 +550,7 @@ export function SubscriptionsSection(props) {
|
|
|
458
550
|
catch (error) {
|
|
459
551
|
setProviderError(provider, messageOf(error));
|
|
460
552
|
}
|
|
461
|
-
}, [rpc, t, setProviderError, startPolling]);
|
|
553
|
+
}, [rpc, t, setProviderError, startPolling, refresh]);
|
|
462
554
|
const cancel = useCallback(async (provider) => {
|
|
463
555
|
if (rpc === undefined)
|
|
464
556
|
return;
|
|
@@ -479,9 +571,12 @@ export function SubscriptionsSection(props) {
|
|
|
479
571
|
return;
|
|
480
572
|
setProviderError(provider, undefined);
|
|
481
573
|
try {
|
|
482
|
-
await callSubscriptionsAuth(rpc, 'manual', {
|
|
574
|
+
await callSubscriptionsAuth(rpc, 'manual', {
|
|
575
|
+
provider,
|
|
576
|
+
input,
|
|
577
|
+
});
|
|
483
578
|
if (mountedRef.current)
|
|
484
|
-
setManualDrafts(prev => ({ ...prev, [provider]: '' }));
|
|
579
|
+
setManualDrafts((prev) => ({ ...prev, [provider]: '' }));
|
|
485
580
|
}
|
|
486
581
|
catch (error) {
|
|
487
582
|
setProviderError(provider, messageOf(error));
|
|
@@ -495,7 +590,10 @@ export function SubscriptionsSection(props) {
|
|
|
495
590
|
return;
|
|
496
591
|
setProviderError(provider, undefined);
|
|
497
592
|
try {
|
|
498
|
-
await callSubscriptionsAuth(rpc, 'logout', {
|
|
593
|
+
await callSubscriptionsAuth(rpc, 'logout', {
|
|
594
|
+
provider,
|
|
595
|
+
account,
|
|
596
|
+
});
|
|
499
597
|
}
|
|
500
598
|
catch (error) {
|
|
501
599
|
setProviderError(provider, messageOf(error));
|
|
@@ -507,7 +605,10 @@ export function SubscriptionsSection(props) {
|
|
|
507
605
|
return;
|
|
508
606
|
setProviderError(provider, undefined);
|
|
509
607
|
try {
|
|
510
|
-
await callSubscriptionsAuth(rpc, 'setDefault', {
|
|
608
|
+
await callSubscriptionsAuth(rpc, 'setDefault', {
|
|
609
|
+
provider,
|
|
610
|
+
account,
|
|
611
|
+
});
|
|
511
612
|
}
|
|
512
613
|
catch (error) {
|
|
513
614
|
setProviderError(provider, messageOf(error));
|
|
@@ -515,16 +616,19 @@ export function SubscriptionsSection(props) {
|
|
|
515
616
|
await refresh();
|
|
516
617
|
}, [rpc, setProviderError, refresh]);
|
|
517
618
|
const copyDeviceCode = useCallback((provider, userCode) => {
|
|
518
|
-
void navigator.clipboard
|
|
619
|
+
void navigator.clipboard
|
|
620
|
+
?.writeText(userCode)
|
|
621
|
+
.then(() => {
|
|
519
622
|
if (!mountedRef.current)
|
|
520
623
|
return;
|
|
521
624
|
setCopiedCode(provider);
|
|
522
625
|
setTimeout(() => {
|
|
523
626
|
if (mountedRef.current) {
|
|
524
|
-
setCopiedCode(current => current === provider ? undefined : current);
|
|
627
|
+
setCopiedCode((current) => current === provider ? undefined : current);
|
|
525
628
|
}
|
|
526
629
|
}, 1500);
|
|
527
|
-
})
|
|
630
|
+
})
|
|
631
|
+
.catch(() => undefined);
|
|
528
632
|
}, []);
|
|
529
633
|
if (rpc === undefined) {
|
|
530
634
|
return _jsx("p", { style: styles.intro, children: t('unavailable') });
|
|
@@ -534,34 +638,103 @@ export function SubscriptionsSection(props) {
|
|
|
534
638
|
const busy = status?.busy === true;
|
|
535
639
|
const deviceCode = deviceCodes[id];
|
|
536
640
|
const accounts = status?.accounts ?? [];
|
|
537
|
-
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) => {
|
|
538
642
|
const usageKey = `${id}:${account.key}`;
|
|
539
643
|
const usage = usages[usageKey];
|
|
540
644
|
const usageError = usageErrors[usageKey];
|
|
541
645
|
const display = account.account ?? account.key;
|
|
542
646
|
const sortedCredits = [...(usage?.resetCredits ?? [])].sort((a, b) => (a.expiresAt ?? Infinity) - (b.expiresAt ?? Infinity));
|
|
543
|
-
const nextReset = sortedCredits.find(c => c.expiresAt === undefined || c.expiresAt > Date.now());
|
|
544
|
-
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';
|
|
545
651
|
const resetDisabled = resetBusy || resetBlock !== undefined;
|
|
546
652
|
// Providers without a usage endpoint answer supported:false — no block.
|
|
547
|
-
const showUsage = usage?.supported !== false
|
|
548
|
-
|
|
653
|
+
const showUsage = usage?.supported !== false &&
|
|
654
|
+
(usage !== undefined ||
|
|
655
|
+
usageError !== undefined ||
|
|
656
|
+
usageLoading[usageKey] === true);
|
|
549
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: () => {
|
|
550
658
|
if (!account.isDefault)
|
|
551
659
|
void setDefault(id, account.key);
|
|
552
|
-
}, 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) => {
|
|
553
668
|
const percent = displayUsedPercent(window.usedPercent);
|
|
554
|
-
return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: usageWindowLabel(t, window) }), _jsxs("span", { children: [percent === undefined
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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') })] })] })) })] }));
|
|
567
740
|
}
|