dsh-plugin-subscriptions 0.5.2 → 0.5.3

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.
Files changed (41) hide show
  1. package/README.md +36 -1
  2. package/README.zh.md +36 -1
  3. package/lib/auth/rpc.d.ts +29 -12
  4. package/lib/auth/rpc.js +29 -6
  5. package/lib/auth/store.d.ts +75 -17
  6. package/lib/auth/store.js +148 -27
  7. package/lib/client/SubscriptionsSection.d.ts +9 -3
  8. package/lib/client/SubscriptionsSection.js +93 -65
  9. package/lib/client/locales.d.ts +18 -10
  10. package/lib/client/locales.js +18 -10
  11. package/lib/client.js +250 -127
  12. package/lib/client.js.map +1 -1
  13. package/lib/index.d.ts +21 -0
  14. package/lib/index.js +1482 -168
  15. package/lib/providers/accounts.d.ts +102 -0
  16. package/lib/providers/accounts.js +123 -0
  17. package/lib/providers/claude.d.ts +22 -4
  18. package/lib/providers/claude.js +91 -11
  19. package/lib/providers/codex.d.ts +24 -3
  20. package/lib/providers/codex.js +116 -17
  21. package/lib/providers/common.d.ts +17 -0
  22. package/lib/providers/common.js +67 -3
  23. package/lib/providers/copilot.d.ts +22 -3
  24. package/lib/providers/copilot.js +91 -12
  25. package/lib/providers/grok.d.ts +24 -4
  26. package/lib/providers/grok.js +100 -14
  27. package/lib/providers/pool-family.d.ts +56 -0
  28. package/lib/providers/pool-family.js +45 -0
  29. package/lib/providers/pool-health.d.ts +74 -0
  30. package/lib/providers/pool-health.js +148 -0
  31. package/lib/providers/pool-usage.d.ts +57 -0
  32. package/lib/providers/pool-usage.js +130 -0
  33. package/lib/providers/pool.d.ts +107 -0
  34. package/lib/providers/pool.js +371 -0
  35. package/lib/tools/image-generate.d.ts +3 -3
  36. package/lib/tools/image-generate.js +2 -1
  37. package/lib/tools/video-generate.d.ts +2 -2
  38. package/lib/tools/video-generate.js +2 -1
  39. package/lib/tools/x-search.d.ts +2 -2
  40. package/lib/tools/x-search.js +2 -1
  41. package/package.json +1 -1
@@ -2,12 +2,18 @@ import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client';
2
2
  import type { SubscriptionsKey } from './locales.js';
3
3
  /** Subscription provider ids, fixed by the node half's OAuth adapters. */
4
4
  export type SubscriptionProvider = 'codex' | 'claude' | 'grok' | 'copilot';
5
+ /** One logged-in account as answered by the `status` endpoint. */
6
+ export interface AccountStatus {
7
+ key: string;
8
+ account?: string;
9
+ expiresAt?: number;
10
+ plan?: string;
11
+ isDefault: boolean;
12
+ }
5
13
  /** One provider's login state as answered by the `status` endpoint. */
6
14
  export interface ProviderStatus {
7
- loggedIn: boolean;
8
15
  busy: boolean;
9
- expiresAt?: number;
10
- account?: string;
16
+ accounts: AccountStatus[];
11
17
  detail?: string;
12
18
  }
13
19
  /** One rate-limit window as answered by the `usage` endpoint. */
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /**
3
3
  * Subscriptions settings section: one card per subscription provider with an
4
4
  * OAuth login/logout flow driven by the node half's `/subscriptions-auth` RPC
@@ -54,6 +54,16 @@ export async function callSubscriptionsAuth(rpc, endpoint, payload) {
54
54
  function messageOf(error) {
55
55
  return error instanceof Error ? error.message : String(error);
56
56
  }
57
+ /** Copy a keyed map without the entries whose key is not in `live`. */
58
+ function dropStale(map, live) {
59
+ const stale = Object.keys(map).filter(key => !live.has(key));
60
+ if (stale.length === 0)
61
+ return map;
62
+ const next = { ...map };
63
+ for (const key of stale)
64
+ delete next[key];
65
+ return next;
66
+ }
57
67
  /**
58
68
  * English-dictionary fallback for a missing inject `t` (standalone renders);
59
69
  * the slot inject always supplies the locale-bound one.
@@ -114,6 +124,18 @@ const styles = {
114
124
  display: 'flex', justifyContent: 'space-between', gap: 8,
115
125
  fontSize: 12, lineHeight: '18px', color: 'var(--dsw-alias-label-tertiary)',
116
126
  },
127
+ accountRow: {
128
+ display: 'flex', flexDirection: 'column', gap: 6,
129
+ border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8,
130
+ padding: '8px 10px', marginTop: 4,
131
+ },
132
+ accountHeader: { display: 'flex', alignItems: 'center', gap: 8 },
133
+ accountName: { fontSize: 13, lineHeight: '20px', color: 'var(--dsw-alias-label-primary)', userSelect: 'all' },
134
+ starButton: {
135
+ border: 'none', background: 'transparent', padding: 0,
136
+ font: 'inherit', fontSize: 14, lineHeight: '20px', cursor: 'pointer',
137
+ color: 'var(--dsw-alias-state-warn-label)',
138
+ },
117
139
  usageTrack: {
118
140
  height: 6, borderRadius: 3, overflow: 'hidden',
119
141
  background: 'var(--dsw-alias-bg-layer-1)', border: '1px solid var(--dsw-alias-border-l2)',
@@ -171,7 +193,7 @@ const styles = {
171
193
  function dotColor(status) {
172
194
  if (status?.busy === true)
173
195
  return 'var(--dsw-alias-state-warn-label)';
174
- if (status?.loggedIn === true)
196
+ if ((status?.accounts.length ?? 0) > 0)
175
197
  return 'var(--dsw-alias-state-success-primary)';
176
198
  return 'var(--dsw-alias-label-dimmed)';
177
199
  }
@@ -186,20 +208,8 @@ function statusText(t, status) {
186
208
  return t('checking');
187
209
  if (status.busy)
188
210
  return t('loginInProgress');
189
- if (status.loggedIn) {
190
- const params = {};
191
- if (status.account !== undefined)
192
- params.account = status.account;
193
- if (status.expiresAt !== undefined)
194
- params.date = new Date(status.expiresAt).toLocaleString();
195
- if (params.account !== undefined && params.date !== undefined)
196
- return t('loggedInAccountExpires', params);
197
- if (params.account !== undefined)
198
- return t('loggedInAccount', params);
199
- if (params.date !== undefined)
200
- return t('loggedInExpires', params);
201
- return t('loggedIn');
202
- }
211
+ if (status.accounts.length > 0)
212
+ return t('loggedInCount', { count: status.accounts.length });
203
213
  return t('notLoggedIn');
204
214
  }
205
215
  /**
@@ -256,12 +266,13 @@ export function SubscriptionsSection(props) {
256
266
  /** Pending device-flow codes (copilot), shown while the attempt polls. */
257
267
  const [deviceCodes, setDeviceCodes] = useState({});
258
268
  const [copiedCode, setCopiedCode] = useState(undefined);
269
+ /** Usage snapshots keyed `${provider}:${accountKey}` — every account tracks its own windows. */
259
270
  const [usages, setUsages] = useState({});
260
271
  const [usageErrors, setUsageErrors] = useState({});
261
272
  const [usageLoading, setUsageLoading] = useState({});
262
273
  const mountedRef = useRef(true);
263
274
  const pollersRef = useRef(new Map());
264
- /** Providers with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
275
+ /** Accounts with a `usage` call in flight; guards the auto-fetch effect against re-entry. */
265
276
  const usageInflightRef = useRef(new Set());
266
277
  /** Proxy config as last answered by `proxyGet`/`proxySet`. */
267
278
  const [proxy, setProxy] = useState(undefined);
@@ -315,7 +326,7 @@ export function SubscriptionsSection(props) {
315
326
  setStatuses(response.providers);
316
327
  for (const { id } of PROVIDERS) {
317
328
  const status = response.providers[id];
318
- if (status.loggedIn || !status.busy) {
329
+ if (status.accounts.length > 0 || !status.busy) {
319
330
  stopPolling(id);
320
331
  // The attempt settled (success, timeout, or cancel): drop the code card.
321
332
  setDeviceCodes((prev) => {
@@ -355,64 +366,58 @@ export function SubscriptionsSection(props) {
355
366
  pollersRef.current.clear();
356
367
  };
357
368
  }, [refresh, startPolling]);
358
- const loadUsage = useCallback(async (provider) => {
359
- if (rpc === undefined || usageInflightRef.current.has(provider))
369
+ const loadUsage = useCallback(async (provider, account) => {
370
+ const key = `${provider}:${account}`;
371
+ if (rpc === undefined || usageInflightRef.current.has(key))
360
372
  return;
361
- usageInflightRef.current.add(provider);
362
- setUsageLoading(prev => ({ ...prev, [provider]: true }));
373
+ usageInflightRef.current.add(key);
374
+ setUsageLoading(prev => ({ ...prev, [key]: true }));
363
375
  try {
364
- const usage = await callSubscriptionsAuth(rpc, 'usage', { provider });
376
+ const usage = await callSubscriptionsAuth(rpc, 'usage', { provider, account });
365
377
  if (!mountedRef.current)
366
378
  return;
367
- setUsages(prev => ({ ...prev, [provider]: usage }));
379
+ setUsages(prev => ({ ...prev, [key]: usage }));
368
380
  setUsageErrors((prev) => {
369
381
  const next = { ...prev };
370
- delete next[provider];
382
+ delete next[key];
371
383
  return next;
372
384
  });
373
385
  }
374
386
  catch (error) {
375
387
  if (mountedRef.current)
376
- setUsageErrors(prev => ({ ...prev, [provider]: messageOf(error) }));
388
+ setUsageErrors(prev => ({ ...prev, [key]: messageOf(error) }));
377
389
  }
378
390
  finally {
379
- usageInflightRef.current.delete(provider);
391
+ usageInflightRef.current.delete(key);
380
392
  if (mountedRef.current)
381
- setUsageLoading(prev => ({ ...prev, [provider]: false }));
393
+ setUsageLoading(prev => ({ ...prev, [key]: false }));
382
394
  }
383
395
  }, [rpc]);
384
- // Fetch usage once a provider is logged in; drop the cached snapshot on
385
- // logout so a re-login refetches. A failed lookup does not auto-retry — the
386
- // per-card Refresh button is the retry path.
396
+ // Fetch usage once an account is logged in; drop the snapshots of accounts
397
+ // that vanished so a re-login refetches. A failed lookup does not auto-retry
398
+ // — the per-account Refresh button is the retry path.
387
399
  useEffect(() => {
400
+ const live = new Set();
388
401
  for (const { id } of PROVIDERS) {
389
- const status = statuses[id];
390
- if (status === undefined)
391
- continue;
392
- if (status.loggedIn) {
393
- if (usages[id] === undefined && usageErrors[id] === undefined)
394
- void loadUsage(id);
395
- }
396
- else if (usages[id] !== undefined || usageErrors[id] !== undefined) {
397
- setUsages((prev) => {
398
- const next = { ...prev };
399
- delete next[id];
400
- return next;
401
- });
402
- setUsageErrors((prev) => {
403
- const next = { ...prev };
404
- delete next[id];
405
- return next;
406
- });
402
+ for (const account of statuses[id]?.accounts ?? []) {
403
+ const key = `${id}:${account.key}`;
404
+ live.add(key);
405
+ if (usages[key] === undefined && usageErrors[key] === undefined)
406
+ void loadUsage(id, account.key);
407
407
  }
408
408
  }
409
+ setUsages(prev => dropStale(prev, live));
410
+ setUsageErrors(prev => dropStale(prev, live));
409
411
  }, [statuses, usages, usageErrors, loadUsage]);
410
- const login = useCallback(async (provider) => {
412
+ const login = useCallback(async (provider, method) => {
411
413
  if (rpc === undefined)
412
414
  return;
413
415
  setProviderError(provider, undefined);
414
416
  try {
415
- const response = await callSubscriptionsAuth(rpc, 'login', { provider });
417
+ const response = await callSubscriptionsAuth(rpc, 'login', {
418
+ provider,
419
+ ...method === undefined ? {} : { method },
420
+ });
416
421
  if (typeof response.authorizeUrl === 'string' && response.authorizeUrl === '') {
417
422
  // Instant login (e.g. imported from Claude Code credentials)
418
423
  await refresh();
@@ -424,7 +429,10 @@ export function SubscriptionsSection(props) {
424
429
  if (!mountedRef.current)
425
430
  return;
426
431
  // Optimistic busy so Cancel and the manual fallback appear before the first poll tick.
427
- setStatuses(prev => ({ ...prev, [provider]: { ...prev[provider], busy: true, loggedIn: false } }));
432
+ setStatuses(prev => ({
433
+ ...prev,
434
+ [provider]: { accounts: prev[provider]?.accounts ?? [], ...prev[provider], busy: true },
435
+ }));
428
436
  if (typeof response.userCode === 'string' && response.userCode.length > 0) {
429
437
  // Device flow: show the code card instead of opening the page blind —
430
438
  // the user copies the code first, then opens the verification page.
@@ -468,20 +476,32 @@ export function SubscriptionsSection(props) {
468
476
  }
469
477
  await refresh();
470
478
  }, [rpc, manualDrafts, setProviderError, refresh]);
471
- const logout = useCallback(async (provider, name) => {
479
+ const logout = useCallback(async (provider, account, display, name) => {
472
480
  if (rpc === undefined)
473
481
  return;
474
- if (!window.confirm(t('logoutConfirm', { provider: name })))
482
+ if (!window.confirm(t('logoutAccountConfirm', { provider: name, account: display })))
475
483
  return;
476
484
  setProviderError(provider, undefined);
477
485
  try {
478
- await callSubscriptionsAuth(rpc, 'logout', { provider });
486
+ await callSubscriptionsAuth(rpc, 'logout', { provider, account });
479
487
  }
480
488
  catch (error) {
481
489
  setProviderError(provider, messageOf(error));
482
490
  }
483
491
  await refresh();
484
492
  }, [rpc, t, setProviderError, refresh]);
493
+ const setDefault = useCallback(async (provider, account) => {
494
+ if (rpc === undefined)
495
+ return;
496
+ setProviderError(provider, undefined);
497
+ try {
498
+ await callSubscriptionsAuth(rpc, 'setDefault', { provider, account });
499
+ }
500
+ catch (error) {
501
+ setProviderError(provider, messageOf(error));
502
+ }
503
+ await refresh();
504
+ }, [rpc, setProviderError, refresh]);
485
505
  const copyDeviceCode = useCallback((provider, userCode) => {
486
506
  void navigator.clipboard?.writeText(userCode).then(() => {
487
507
  if (!mountedRef.current)
@@ -593,16 +613,24 @@ export function SubscriptionsSection(props) {
593
613
  const status = statuses[id];
594
614
  const busy = status?.busy === true;
595
615
  const deviceCode = deviceCodes[id];
596
- const usage = usages[id];
597
- const usageError = usageErrors[id];
598
- // Providers without a usage endpoint answer supported:false — no block.
599
- const showUsage = status?.loggedIn === true && usage?.supported !== false
600
- && (usage !== undefined || usageError !== undefined || usageLoading[id] === true);
601
- 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 })] }), _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] }), _jsxs("div", { style: styles.actions, children: [!busy && status?.loggedIn !== true && (_jsx("button", { type: "button", style: styles.button, onClick: () => { void login(id); }, children: t('login') })), busy && (_jsx("button", { type: "button", style: styles.button, onClick: () => { void cancel(id); }, children: t('cancel') })), status?.loggedIn === true && (_jsx("button", { type: "button", style: styles.button, onClick: () => { void logout(id, name); }, 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, ...usageLoading[id] === true ? { opacity: 0.5, cursor: 'default' } : {} }, disabled: usageLoading[id] === true, onClick: () => { void loadUsage(id); }, 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 && usage.windows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageEmpty') })), (usage?.windows ?? []).map((window, index) => {
602
- const percent = Math.min(100, Math.max(0, window.usedPercent));
603
- return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: usageWindowLabel(t, window) }), _jsxs("span", { children: [`${String(Math.round(percent))}%`, window.resetsAt !== undefined
604
- && ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx("div", { style: styles.usageTrack, children: _jsx("div", { style: { ...styles.usageFill, width: `${String(percent)}%`, background: usageBarColor(percent) } }) })] }, index));
605
- })] })), 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", style: styles.button, onClick: () => { copyDeviceCode(id, deviceCode.userCode); }, children: copiedCode === id ? t('deviceCodeCopied') : t('deviceCodeCopy') }), _jsx("button", { type: "button", style: styles.button, onClick: () => { window.open(deviceCode.verificationUrl, '_blank', 'noopener'); }, 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 => ({ ...prev, [id]: event.target.value })) }), _jsx("button", { type: "button", style: styles.button, onClick: () => { void submitManual(id); }, children: t('submit') })] })] }))] }, id));
616
+ const accounts = status?.accounts ?? [];
617
+ 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 })] }), _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) => {
618
+ const usageKey = `${id}:${account.key}`;
619
+ const usage = usages[usageKey];
620
+ const usageError = usageErrors[usageKey];
621
+ const display = account.account ?? account.key;
622
+ // Providers without a usage endpoint answer supported:false — no block.
623
+ const showUsage = usage?.supported !== false
624
+ && (usage !== undefined || usageError !== undefined || usageLoading[usageKey] === true);
625
+ 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: () => {
626
+ if (!account.isDefault)
627
+ void setDefault(id, account.key);
628
+ }, 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', { date: new Date(account.expiresAt).toLocaleString() }) })), _jsx("button", { type: "button", style: { ...styles.button, marginLeft: 'auto', flexShrink: 0 }, onClick: () => { void logout(id, account.key, display, name); }, 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, ...usageLoading[usageKey] === true ? { opacity: 0.5, cursor: 'default' } : {} }, disabled: usageLoading[usageKey] === true, onClick: () => { void loadUsage(id, account.key); }, 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 && usage.windows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageEmpty') })), (usage?.windows ?? []).map((window, index) => {
629
+ const percent = Math.min(100, Math.max(0, window.usedPercent));
630
+ return (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageMeta, children: [_jsx("span", { children: usageWindowLabel(t, window) }), _jsxs("span", { children: [`${String(Math.round(percent))}%`, window.resetsAt !== undefined
631
+ && ` · ${t('usageResets', { date: new Date(window.resetsAt).toLocaleString() })}`] })] }), _jsx("div", { style: styles.usageTrack, children: _jsx("div", { style: { ...styles.usageFill, width: `${String(percent)}%`, background: usageBarColor(percent) } }) })] }, index));
632
+ })] }))] }, account.key));
633
+ }), _jsxs("div", { style: styles.actions, children: [!busy && accounts.length === 0 && (_jsx("button", { type: "button", style: styles.button, onClick: () => { void login(id); }, children: t('login') })), !busy && accounts.length > 0 && id === 'claude' && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", style: styles.button, onClick: () => { void login(id, 'oauth'); }, children: t('addAccountOAuth') }), _jsx("button", { type: "button", style: styles.button, onClick: () => { void login(id, 'keychain'); }, children: t('addAccountKeychain') })] })), !busy && accounts.length > 0 && id !== 'claude' && (_jsx("button", { type: "button", style: styles.button, onClick: () => { void login(id); }, children: t('addAccount') })), busy && (_jsx("button", { type: "button", style: styles.button, onClick: () => { void cancel(id); }, 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", style: styles.button, onClick: () => { copyDeviceCode(id, deviceCode.userCode); }, children: copiedCode === id ? t('deviceCodeCopied') : t('deviceCodeCopy') }), _jsx("button", { type: "button", style: styles.button, onClick: () => { window.open(deviceCode.verificationUrl, '_blank', 'noopener'); }, 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 => ({ ...prev, [id]: event.target.value })) }), _jsx("button", { type: "button", style: styles.button, onClick: () => { void submitManual(id); }, children: t('submit') })] })] }))] }, id));
606
634
  }), proxyOpen && (_jsx("div", { style: styles.modalOverlay, onClick: () => setProxyOpen(false), children: _jsxs("div", { style: styles.modal, onClick: event => event.stopPropagation(), children: [_jsxs("div", { style: styles.modalHeader, children: [_jsx("span", { style: styles.modalTitle, children: t('proxyDialogTitle') }), _jsx("button", { type: "button", style: { ...styles.button, marginLeft: 'auto' }, onClick: () => setProxyOpen(false), children: t('proxyDialogClose') })] }), _jsxs("label", { style: styles.proxyCheck, children: [_jsx("input", { type: "checkbox", checked: proxyEnabled, onChange: event => setProxyEnabled(event.target.checked) }), _jsx("span", { children: t('proxyEnabled') })] }), _jsxs("label", { style: styles.proxyField, children: [_jsx("span", { style: styles.proxyLabel, children: t('proxyUrl') }), _jsx("input", { style: styles.proxyInput, value: proxyUrl, placeholder: t('proxyUrlPlaceholder'), onChange: event => setProxyUrl(event.target.value) }), _jsx("p", { style: styles.proxyHint, children: t('proxyUrlHint') })] }), _jsxs("label", { style: styles.proxyField, children: [_jsx("span", { style: styles.proxyLabel, children: t('proxyUsername') }), _jsx("input", { style: styles.proxyInput, value: proxyUsername, placeholder: t('proxyUsernamePlaceholder'), onChange: event => setProxyUsername(event.target.value) })] }), _jsxs("div", { style: styles.proxyField, children: [_jsx("span", { style: styles.proxyLabel, children: t('proxyPassword') }), _jsx("input", { type: "password", style: styles.proxyInput, value: proxyPassword, placeholder: t('proxyPasswordPlaceholder'), onChange: event => setProxyPassword(event.target.value) }), _jsxs("label", { style: styles.proxyCheck, children: [_jsx("input", { type: "checkbox", checked: proxyClearPassword, onChange: event => setProxyClearPassword(event.target.checked) }), _jsx("span", { children: t('proxyClearPassword') })] })] }), _jsxs("label", { style: styles.proxyField, children: [_jsx("span", { style: styles.proxyLabel, children: t('proxyBypass') }), _jsx("input", { style: styles.proxyInput, value: proxyBypass, placeholder: t('proxyBypassPlaceholder'), onChange: event => setProxyBypass(event.target.value) }), _jsx("p", { style: styles.proxyHint, children: t('proxyBypassHint') })] }), _jsx("p", { style: styles.proxyHint, children: t('proxyNote') }), proxyMessage !== undefined && (_jsx("p", { style: { ...styles.proxyMessage, color: messageColor(proxyMessage.tone) }, children: proxyMessage.text })), proxyTestResult !== undefined && (_jsx("p", { style: {
607
635
  ...styles.proxyMessage,
608
636
  color: proxyTestResult.ok
@@ -6,15 +6,19 @@ export declare const en: {
6
6
  unavailable: string;
7
7
  checking: string;
8
8
  loginInProgress: string;
9
- loggedIn: string;
10
- loggedInAccount: string;
11
- loggedInExpires: string;
12
- loggedInAccountExpires: string;
13
9
  notLoggedIn: string;
10
+ loggedInCount: string;
11
+ accountExpires: string;
12
+ defaultBadge: string;
13
+ setDefault: string;
14
+ addAccount: string;
15
+ addAccountOAuth: string;
16
+ addAccountKeychain: string;
17
+ addAccountHint: string;
14
18
  login: string;
15
19
  cancel: string;
16
20
  logout: string;
17
- logoutConfirm: string;
21
+ logoutAccountConfirm: string;
18
22
  manualSummary: string;
19
23
  manualPlaceholder: string;
20
24
  submit: string;
@@ -91,15 +95,19 @@ export declare const zh: {
91
95
  unavailable: string;
92
96
  checking: string;
93
97
  loginInProgress: string;
94
- loggedIn: string;
95
- loggedInAccount: string;
96
- loggedInExpires: string;
97
- loggedInAccountExpires: string;
98
98
  notLoggedIn: string;
99
+ loggedInCount: string;
100
+ accountExpires: string;
101
+ defaultBadge: string;
102
+ setDefault: string;
103
+ addAccount: string;
104
+ addAccountOAuth: string;
105
+ addAccountKeychain: string;
106
+ addAccountHint: string;
99
107
  login: string;
100
108
  cancel: string;
101
109
  logout: string;
102
- logoutConfirm: string;
110
+ logoutAccountConfirm: string;
103
111
  manualSummary: string;
104
112
  manualPlaceholder: string;
105
113
  submit: string;
@@ -6,15 +6,19 @@ export const en = {
6
6
  unavailable: 'Connection unavailable; subscription status cannot be loaded.',
7
7
  checking: 'Checking…',
8
8
  loginInProgress: 'Login in progress…',
9
- loggedIn: 'Logged in',
10
- loggedInAccount: 'Logged in as {account}',
11
- loggedInExpires: 'Logged in · expires {date}',
12
- loggedInAccountExpires: 'Logged in as {account} · expires {date}',
13
9
  notLoggedIn: 'Not logged in',
10
+ loggedInCount: '{count} account(s) connected',
11
+ accountExpires: 'expires {date}',
12
+ defaultBadge: 'Default',
13
+ setDefault: 'Set as default',
14
+ addAccount: 'Add account',
15
+ addAccountOAuth: 'Browser authorization',
16
+ addAccountKeychain: 'Import Claude Code',
17
+ addAccountHint: 'Browser authorization signs in whichever account the browser currently uses — switch accounts there first (or use an incognito window with the manual code below) to add a different one.',
14
18
  login: 'Log in',
15
19
  cancel: 'Cancel',
16
20
  logout: 'Log out',
17
- logoutConfirm: 'Log out of {provider}?',
21
+ logoutAccountConfirm: 'Log out {account} of {provider}?',
18
22
  manualSummary: 'Browser flow not working? Paste the callback URL or code',
19
23
  manualPlaceholder: 'Paste the callback URL or code',
20
24
  submit: 'Submit',
@@ -91,15 +95,19 @@ export const zh = {
91
95
  unavailable: '连接不可用,无法加载订阅状态。',
92
96
  checking: '查询中…',
93
97
  loginInProgress: '登录中…',
94
- loggedIn: '已登录',
95
- loggedInAccount: '已登录:{account}',
96
- loggedInExpires: '已登录 · 过期时间 {date}',
97
- loggedInAccountExpires: '已登录:{account} · 过期时间 {date}',
98
98
  notLoggedIn: '未登录',
99
+ loggedInCount: '已连接 {count} 个账号',
100
+ accountExpires: '过期时间 {date}',
101
+ defaultBadge: '默认',
102
+ setDefault: '设为默认',
103
+ addAccount: '添加账号',
104
+ addAccountOAuth: '浏览器授权',
105
+ addAccountKeychain: '导入 Claude Code',
106
+ addAccountHint: '浏览器授权以浏览器当前登录的账号为准;要添加不同账号,请先在浏览器里切换账号,或用无痕窗口走下方手动授权码。',
99
107
  login: '登录',
100
108
  cancel: '取消',
101
109
  logout: '退出登录',
102
- logoutConfirm: '确定退出 {provider} 的登录吗?',
110
+ logoutAccountConfirm: '确定退出 {provider} 的账号 {account} 吗?',
103
111
  manualSummary: '浏览器流程无法完成?粘贴回调 URL 或授权码',
104
112
  manualPlaceholder: '粘贴回调 URL 或授权码',
105
113
  submit: '提交',