@goodandready/dsh-subscriptions 0.6.0 → 0.6.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.
@@ -161,7 +161,7 @@ export function createSubscriptionsService(deps) {
161
161
  else err.code = "VENDOR"
162
162
  throw err
163
163
  }
164
- // #65: записать в историю запросов и стоимости.
164
+ // #65: record in the request and cost history.
165
165
  if (typeof deps.recordHistory === "function") {
166
166
  try {
167
167
  deps.recordHistory({
package/lib/ui-styles.js CHANGED
@@ -44,9 +44,9 @@ export const CSS =
44
44
  '.dsub-pill:active{transform:translateY(0)}' +
45
45
  '.dsub-pillTag{padding:1px 5px;border-radius:4px;font-size:10.5px;font-weight:700;letter-spacing:.02em;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary)}' +
46
46
  '.dsub-led{width:7px;height:7px;border-radius:50%;flex:none;transition:all .2s ease}' +
47
- '.dsub-ledOk{background:var(--dsw-alias-state-success-primary,#10b981);box-shadow:0 0 6px rgba(16,185,129,.7)}' +
48
- '.dsub-ledWarn{background:var(--dsw-alias-state-warning-primary,#f59e0b);box-shadow:0 0 6px rgba(245,158,11,.7)}' +
49
- '.dsub-ledBad{background:var(--dsw-alias-state-error-primary,#ef4444);box-shadow:0 0 6px rgba(239,68,68,.7)}' +
47
+ '.dsub-ledOk{background:var(--dsw-alias-state-success-primary,#10b981);box-shadow:0 0 6px color-mix(in srgb,var(--dsw-alias-state-success-primary) 70%,transparent)}' +
48
+ '.dsub-ledWarn{background:var(--dsw-alias-state-warning-primary,#f59e0b);box-shadow:0 0 6px color-mix(in srgb,var(--dsw-alias-state-warning-primary) 70%,transparent)}' +
49
+ '.dsub-ledBad{background:var(--dsw-alias-state-error-primary,#ef4444);box-shadow:0 0 6px color-mix(in srgb,var(--dsw-alias-state-error-primary) 70%,transparent)}' +
50
50
  '.dsub-ledOff{background:var(--dsw-alias-label-tertiary,#6b7280)}' +
51
51
  '.dsub-modalWrap{position:fixed;inset:0;z-index:10020;display:grid;place-items:center;background:rgba(0,0,0,.65);backdrop-filter:blur(10px);animation:dsubFadeIn .16s ease-out}' +
52
52
  '.dsub-modal{width:min(520px,94vw);max-height:min(85vh,720px);overflow-y:auto;border:1px solid var(--dsw-alias-border-l1);border-radius:16px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);box-shadow:0 24px 64px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.07);padding:20px;animation:dsubScaleIn .18s cubic-bezier(.16,1,.3,1)}' +
@@ -65,7 +65,7 @@ export const CSS =
65
65
  '.dsub-barTrack{height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}' +
66
66
  '.dsub-barFillGrad{height:100%;border-radius:3px;transition:width .3s ease}' +
67
67
  '.dsub-panel-box{margin-top:10px;padding:12px 14px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);display:flex;flex-direction:column;gap:8px}' +
68
- '.dsub-panel-warn{border-color:rgba(245,158,11,0.4);background:rgba(245,158,11,0.05)}' +
68
+ '.dsub-panel-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 40%,transparent);background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 5%,transparent)}' +
69
69
  '.dsub-panel-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between}' +
70
70
  '.dsub-panel-desc{font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.4}' +
71
71
  '.dsub-usage-grid{display:flex;flex-direction:column;gap:8px;margin-top:4px}' +
@@ -74,14 +74,14 @@ export const CSS =
74
74
  '.dsub-usage-track{width:100%;height:8px;border-radius:999px;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);overflow:hidden}' +
75
75
  '.dsub-usage-fill{height:100%;border-radius:999px;transition:width .3s ease}' +
76
76
  '.dsub-usage-meta{display:flex;justify-content:space-between;align-items:center;font-size:11px;color:var(--dsw-alias-label-secondary)}' +
77
- '.dsub-tag-ok{border-color:rgba(16,185,129,0.3);color:var(--dsw-alias-state-success-primary);background:rgba(16,185,129,0.08);padding:1px 7px;border-radius:999px;font-size:11px;font-weight:600;border-style:solid;border-width:1px}' +
78
- '.dsub-tag-warn{border-color:rgba(245,158,11,0.4);color:var(--dsw-alias-state-warning-primary);background:rgba(245,158,11,0.08);padding:1px 7px;border-radius:999px;font-size:11px;font-weight:600;border-style:solid;border-width:1px}' +
79
- '.dsub-tag-bad{border-color:rgba(239,68,68,0.4);color:var(--dsw-alias-state-error-primary);background:rgba(239,68,68,0.08);padding:1px 7px;border-radius:999px;font-size:11px;font-weight:600;border-style:solid;border-width:1px}' +
77
+ '.dsub-tag-ok{border-color:color-mix(in srgb,var(--dsw-alias-state-success-primary) 30%,transparent);color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent);padding:1px 7px;border-radius:999px;font-size:11px;font-weight:600;border-style:solid;border-width:1px}' +
78
+ '.dsub-tag-warn{border-color:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 40%,transparent);color:var(--dsw-alias-state-warning-primary);background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 8%,transparent);padding:1px 7px;border-radius:999px;font-size:11px;font-weight:600;border-style:solid;border-width:1px}' +
79
+ '.dsub-tag-bad{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);padding:1px 7px;border-radius:999px;font-size:11px;font-weight:600;border-style:solid;border-width:1px}' +
80
80
  '.dsub-slot-meta-row{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:12px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2);font-size:12px}' +
81
81
  '.dsub-ref-tag{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;color:var(--dsw-alias-label-secondary)}' +
82
82
  '.dsub-ref-tag code{padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);font-family:ui-monospace,monospace;color:var(--dsw-alias-label-primary);font-size:11px}' +
83
- '.dsub-btn-danger{border-color:rgba(239,68,68,0.4)!important;color:var(--dsw-alias-state-error-primary)!important}' +
84
- '.dsub-btn-danger:hover:not(:disabled){background:rgba(239,68,68,0.12)!important}' +
83
+ '.dsub-btn-danger{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 40%,transparent)!important;color:var(--dsw-alias-state-error-primary)!important}' +
84
+ '.dsub-btn-danger:hover:not(:disabled){background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent)!important}' +
85
85
  '.dsub-poolTitle{margin-top:16px;margin-bottom:8px;font-size:11.5px;font-weight:700;color:var(--dsw-alias-label-secondary);text-transform:uppercase;letter-spacing:.05em}' +
86
86
  '.dsub-accountCard{display:flex;align-items:center;gap:12px;padding:10px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2);margin-top:6px;transition:border-color .15s,background .15s}' +
87
87
  '.dsub-accountCard:hover{background:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-tertiary)}' +
@@ -99,9 +99,9 @@ export const CSS =
99
99
  '.dsub-accNameRow{display:flex;align-items:center;justify-content:space-between;gap:8px}' +
100
100
  '.dsub-accName{font-size:13px;font-weight:600}' +
101
101
  '.dsub-accStatusTag{font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:4px}' +
102
- '.dsub-statusOk{background:rgba(16,185,129,.15);color:#34d399}' +
103
- '.dsub-statusWarn{background:rgba(245,158,11,.15);color:#fbbf24}' +
104
- '.dsub-statusBad{background:rgba(239,68,68,.15);color:#f87171}' +
102
+ '.dsub-statusOk{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 15%,transparent);color:var(--dsw-alias-state-success-primary)}' +
103
+ '.dsub-statusWarn{background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 15%,transparent);color:var(--dsw-alias-state-warning-primary)}' +
104
+ '.dsub-statusBad{background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 15%,transparent);color:var(--dsw-alias-state-error-primary)}' +
105
105
  '.dsub-statusOff{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-tertiary)}' +
106
106
  '.dsub-modalFoot{display:flex;align-items:center;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2)}' +
107
107
  '.dsub-btnPrimary{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border-radius:8px;font-size:12px;font-weight:600;background:var(--dsw-alias-brand-primary,#3b82f6);color:#fff;border:0;cursor:pointer;transition:opacity .15s}' +
package/lib/usage.js CHANGED
@@ -58,7 +58,7 @@ const WINDOW_LABELS = {
58
58
  weekly_limit_7_days: { ru: "7д", en: "7d" },
59
59
  primary: { ru: "осн.", en: "primary" },
60
60
  secondary: { ru: "втор.", en: "secondary" },
61
- // codex: vendor API отдаёт primary_window/secondary_window для UI читаем как 5h/7d
61
+ // codex: the vendor API returns primary_window/secondary_window - surfaced as 5h/7d in the UI
62
62
  primary_window: { ru: "5ч", en: "5h" },
63
63
  secondary_window: { ru: "7д", en: "7d" },
64
64
  monthly: { ru: "месяц", en: "month" },
@@ -1,11 +1,11 @@
1
- // Фабрика вендоров из декларативного профиля.
1
+ // Vendor factory from a declarative profile.
2
2
  //
3
- // Покрывает семейство OpenAI-Responses-совместимых подписок: авторизация PKCE,
4
- // form- или json-токен-эндпоинт, каталог моделей, usage, стрим через /responses.
5
- // Нестандартные протоколы (Anthropic messages, Cloud Code Assist) фабрикой не
6
- // описываются для них есть рукописные модули.
3
+ // Covers the OpenAI-Responses-compatible subscription family: PKCE auth,
4
+ // form- or json-token endpoint, model catalog, usage, streaming via /responses.
5
+ // Non-standard protocols (Anthropic messages, Cloud Code Assist) are not
6
+ // expressed by the factory - hand-written modules cover them.
7
7
  //
8
- // Новый провайдер = запись customVendors в Config, не код.
8
+ // A new provider = a customVendors entry in Config, not code.
9
9
 
10
10
  import { buildAuthorizeUrl } from "./oauth.js"
11
11
  import { codexResponsesBody, modelCatalog } from "./messages.js"
@@ -18,7 +18,7 @@ function defaultModels(models) {
18
18
  }
19
19
 
20
20
  /**
21
- * Собирает объект вендора из профиля. Профиль данные из Config:
21
+ * Builds a vendor object from a profile. A profile is data from Config:
22
22
  * { id, displayName, authUrl, tokenUrl, baseUrl, scope, tokenStyle,
23
23
  * clientId, redirectUri, modelsPath, models, usagePath, headers }
24
24
  */
@@ -104,8 +104,8 @@ export function createVendorFromProfile(profile) {
104
104
  }
105
105
  },
106
106
  async usage() {
107
- // Универсального usage-эндпоинта у произвольных сервисов нет квота
108
- // наполняется из ratelimit-заголовков через adapter quotaFetch.
107
+ // Arbitrary services have no universal usage endpoint - the quota
108
+ // snapshot is filled from ratelimit headers via the adapter quotaFetch.
109
109
  return null
110
110
  },
111
111
  check: undefined,
@@ -129,14 +129,14 @@ export function createVendorFromProfile(profile) {
129
129
  }
130
130
  }
131
131
 
132
- /** Валидирует профиль из Config; бросает при невалидных полях. */
132
+ /** Validates the profile from Config; throws on invalid fields. */
133
133
  export function validateProfile(raw) {
134
134
  const p = raw && typeof raw === "object" ? raw : {}
135
135
  for (const key of ["id", "authUrl", "tokenUrl", "baseUrl", "clientId"]) {
136
- if (!p[key] || typeof p[key] !== "string") throw new Error(`customVendors[${p.id || "?"}]: поле ${key} обязательно`)
136
+ if (!p[key] || typeof p[key] !== "string") throw new Error(`customVendors[${p.id || "?"}]: field ${key} is required`)
137
137
  }
138
- if (!/^[a-z][a-z0-9_]*$/.test(p.id)) throw new Error(`customVendors: id "${p.id}" должен быть [a-z][a-z0-9_]*`)
139
- if (p.tokenStyle && !["form", "json"].includes(p.tokenStyle)) throw new Error(`customVendors[${p.id}]: tokenStyle должен быть form|json`)
138
+ if (!/^[a-z][a-z0-9_]*$/.test(p.id)) throw new Error(`customVendors: id "${p.id}" must match [a-z][a-z0-9_]*`)
139
+ if (p.tokenStyle && !["form", "json"].includes(p.tokenStyle)) throw new Error(`customVendors[${p.id}]: tokenStyle must be form|json`)
140
140
  return {
141
141
  id: p.id,
142
142
  displayName: p.displayName || p.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-subscriptions",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Use ChatGPT Codex, Claude, Grok, Antigravity, Kimi, GLM, Cursor, Kiro, Copilot, Qwen, ERNIE, Spark, JetBrains, Perplexity, Replit, and Cody subscriptions as DeepSeek Harness LLM providers via OAuth.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,9 +34,6 @@
34
34
  "bugs": {
35
35
  "url": "https://github.com/GooDAnDReaDY/dsh-subscriptions/issues"
36
36
  },
37
- "scripts": {
38
- "test": "eslint lib/ && node --test test/*.test.mjs"
39
- },
40
37
  "dsh": {
41
38
  "bundle": {
42
39
  "patch": "./cordis.patch.yml"
@@ -63,5 +60,8 @@
63
60
  },
64
61
  "devDependencies": {
65
62
  "@deepseek-ai/dsh-llm": "0.1.0-rc.8"
63
+ },
64
+ "scripts": {
65
+ "test": "eslint lib/ && node --test test/*.test.mjs"
66
66
  }
67
- }
67
+ }
package/lib/ui-i18n.js DELETED
@@ -1,256 +0,0 @@
1
- export function defineLocales() {
2
- const en = {
3
- 'notConnected': 'Not connected',
4
- 'verifyAccount': 'Verify account',
5
- 'coolingDown': 'Cooling down',
6
- 'usageFull': 'Usage 100%',
7
- 'connected': 'Connected',
8
- 'title': 'Subscriptions',
9
- 'cardIntro': 'Subscription accounts, rotation and limits.',
10
- 'show': 'Show',
11
- 'hide': 'Hide',
12
- 'intro': 'Log in with a consumer subscription. Tokens stay on the host credentials store. The browser never reads them back. After Connect, if the provider lands on localhost or a vendor page, paste the redirected URL or the code here.',
13
- 'useOrigin': 'Use this Web UI origin as OAuth redirect_uri',
14
- 'useOriginHint': 'Leave off unless you registered your own OAuth client for this origin. Vendor CLI clients usually require their published redirect URI plus the paste step.',
15
- 'privacyMask': 'Mask emails and account identifiers',
16
- 'privacyMaskHint': 'For demos and screen sharing: emails show as j***n@example.com.',
17
- 'resetCredits': 'Reset credits',
18
- 'resetCreditsHint': 'ChatGPT reset cards: emergency reset for the 5-hour window. Requires a 5s confirmation.',
19
- 'resetCreditsAction': 'Reset ChatGPT Limit',
20
- 'usageLimitsTitle': 'Usage Limits',
21
- 'slotDetailsTitle': 'Slot Details',
22
- 'resetAvailable': 'Reset attempts available',
23
- 'resetExpires': 'earliest expires',
24
- 'resetAck': 'I understand one attempt will be consumed',
25
- 'resetWait': 'confirm enabled in',
26
- 'resetReady': 'ready',
27
- 'resetGo': 'Reset now',
28
- 'resetBusy': 'resetting…',
29
- 'resetDone': 'quota reset',
30
- 'resetNothing': 'server says nothing needs a reset (no attempt consumed)',
31
- 'resetNoCredit': 'no usable credit (not consumed)',
32
- 'resetRedeemed': 'already redeemed earlier',
33
- 'diagGenerate': 'Generate diagnostics report',
34
- 'diagCopy': 'Copy report',
35
- 'diagIssues': 'Open issue tracker',
36
- 'diagHint': 'The report has no tokens, emails, proxy addresses or personal data.',
37
- 'subsPill': 'SUBS',
38
- 'subsModalTitle': 'Subscription Hub',
39
- 'subsActiveHero': 'Active Subscription',
40
- 'subsPoolTitle': 'Provider Accounts',
41
- 'subsRefresh': 'Refresh',
42
- 'subsOpenSettings': 'All Settings →',
43
- 'subsFastMode': 'Fast Mode 1.5x',
44
- 'subsHealthy': 'Healthy',
45
- 'subsFree': 'free',
46
- 'subsUsed': 'used',
47
- 'subsModalClose': 'Close',
48
- 'subsLogged': 'connected',
49
- 'subsNotLogged': 'not connected',
50
- 'subsCooldown': 'cooldown',
51
- 'subsOpenSettings': 'Open settings',
52
- 'hudTitle': 'Subscription balance',
53
- 'settingsLoading': 'Loading settings…',
54
- 'settingsUnavailable': 'Settings are unavailable',
55
- 'settingsRetry': 'Retry',
56
- 'hudHint': 'drag · edge docks · click refresh',
57
- 'fcCalibrating': 'calibrating…',
58
- 'fcIdle': 'no usage',
59
- 'fcHours': 'h',
60
- 'fcMinutes': 'm',
61
- 'reconnect': 'Reconnect',
62
- 'connect': 'Connect',
63
- 'disconnect': 'Disconnect',
64
- 'removeSlot': 'Remove slot',
65
- 'pastePlaceholder': 'Paste redirected URL or code',
66
- 'submitCode': 'Submit code',
67
- 'proxyPlaceholder': 'Per-account proxy (http://, https://, socks5://) - optional',
68
- 'proxyCheck': 'Check proxy',
69
- 'proxyOk': 'proxy ok',
70
- 'proxyFail': 'proxy fail',
71
- 'deviceLogin': 'Device login',
72
- 'deviceHint': 'Headless: open the link, enter the code, keep this tab open.',
73
- 'deviceCopy': 'Copy code',
74
- 'devicePending': 'Waiting for confirmation',
75
- 'deviceAuthorized': 'Signed in',
76
- 'deviceExpired': 'Expired - start again',
77
- 'verifyPrefix': 'Google requires one-time account verification. ',
78
- 'verifyLink': 'Open verification link',
79
- 'verifySuffix': ' then reconnect.',
80
- 'addAccount': '+ Add account',
81
- 'save': 'Save',
82
- 'saved': 'Saved',
83
- 'loading': 'Loading\u2026',
84
- 'accountLabel': 'Account',
85
- 'plan': 'Plan',
86
- 'storedAs': 'Stored as',
87
- 'switchLabel': 'Provider',
88
- 'chipActive': 'Subscriptions',
89
- 'expiryLabel': 'expires',
90
- 'slashLogin': 'Login to a subscription provider',
91
- 'slashLogout': 'Log out a subscription provider',
92
- 'slashStatus': 'Subscription status',
93
- 'none': 'none',
94
- 'forecast': '≈',
95
- 'resetCredits': 'Сброс лимитов ChatGPT',
96
- 'resetCreditsHint': 'Экстренный сброс 5-часового окна ChatGPT Plus/Pro. Требует 5 секунд подтверждения перед списанием.',
97
- 'resetCreditsAction': 'Сбросить лимит ChatGPT',
98
- 'resetAvailable': 'Доступно сбросов',
99
- 'resetExpires': 'срок действия',
100
- 'resetAck': 'Я понимаю, что одна попытка сброса будет списана',
101
- 'resetWait': 'подтверждение через',
102
- 'resetReady': 'готово к сбросу',
103
- 'resetGo': 'Сбросить лимит сейчас',
104
- 'resetBusy': 'Сброс…',
105
- 'resetDone': 'Лимит успешно сброшен',
106
- 'resetNothing': 'Лимит не требует сброса',
107
- 'resetNoCredit': 'Нет доступных попыток сброса',
108
- 'resetRedeemed': 'Попытка уже использована',
109
- 'windowPrimary': 'Окно 5 часов',
110
- 'windowSecondary': 'Окно 7 дней',
111
- 'usageLimitsTitle': 'Лимиты использования',
112
- 'slotDetailsTitle': 'Параметры слота',
113
- 'resetLabel': 'reset',
114
- 'check': 'Check',
115
- 'checking': 'Checking…',
116
- 'importToken': 'Save token',
117
- 'importTokenPlace': 'Paste token or API key',
118
- 'importLocalCli': '📥 From CLI',
119
- 'importLocalCliTitle': 'Auto-import token from locally installed CLI on server',
120
- 'importLocalSuccess': 'Successfully imported token from local CLI!',
121
- 'howToGetToken': '❓ Instructions',
122
- 'howToGetTokenTitle': 'How to get token / API key for this provider',
123
- 'instructionsTitle': 'How to obtain token',
124
- 'close': 'Close',
125
- 'reconnectRequired': 'Reconnect required',
126
- 'manualTitle': 'If the browser did not come back',
127
- 'windowPrimary': 'Primary window',
128
- 'windowSecondary': 'Secondary window',
129
- }
130
- const ru = {
131
- 'check': 'Проверить',
132
- 'checking': 'Проверяю…',
133
- 'importToken': 'Сохранить',
134
- 'importTokenPlace': 'Вставить токен или API-ключ',
135
- 'importLocalCli': '📥 Из CLI',
136
- 'importLocalCliTitle': 'Автоматически считать токен из установленного на сервере CLI',
137
- 'importLocalSuccess': 'Токен успешно загружен из локального CLI!',
138
- 'howToGetToken': '❓ Инструкция',
139
- 'howToGetTokenTitle': 'Как получить токен или API-ключ для этого провайдера',
140
- 'instructionsTitle': 'Инструкция по получению токена',
141
- 'close': 'Закрыть',
142
- 'reconnectRequired': 'Нужно переподключить',
143
- 'manualTitle': 'Если браузер не вернулся сам',
144
- 'windowPrimary': 'Основное окно',
145
- 'windowSecondary': 'Дополнительное окно',
146
- 'notConnected': 'Не подключено',
147
- 'verifyAccount': 'Требуется проверка',
148
- 'coolingDown': 'Пауза после лимита',
149
- 'usageFull': 'Лимит исчерпан',
150
- 'connected': 'Подключено',
151
- 'title': 'Подписки',
152
- 'cardIntro': 'Аккаунты подписок, ротация и лимиты.',
153
- 'show': 'Показать',
154
- 'hide': 'Скрыть',
155
- 'intro': 'Вход по обычной пользовательской подписке. Токены остаются в хранилище учётных данных харнесса, браузер их обратно не читает. Если после «Подключить» провайдер увёл на localhost или на свою страницу, вставьте сюда адрес перехода или код.',
156
- 'useOrigin': 'Использовать адрес этого веб-интерфейса как redirect_uri',
157
- 'useOriginHint': 'Включайте, только если зарегистрировали собственного клиента OAuth на этот адрес. Штатным консольным клиентам провайдеров нужен их опубликованный адрес возврата и вставка кода вручную.',
158
- 'privacyMask': 'Маскировать email и учётные записи',
159
- 'privacyMaskHint': 'Для демонстрации экрана: email отображается как j***n@example.com.',
160
- 'resetCredits': 'Карты сброса',
161
- 'resetCreditsHint': 'Карты сброса квоты ChatGPT: показывает, сколько доступно. Списание одной карты — осознанное действие с 5-секундным подтверждением.',
162
- 'resetAvailable': 'Доступно попыток сброса',
163
- 'resetExpires': 'ближайшая истекает',
164
- 'resetAck': 'Я понимаю, что будет списана 1 попытка',
165
- 'resetWait': 'кнопка активна через',
166
- 'resetReady': 'готово',
167
- 'resetGo': 'Сбросить',
168
- 'resetBusy': 'сбрасываю…',
169
- 'resetDone': 'квота сброшена',
170
- 'resetNothing': 'сервер сообщает: сброс не нужен (попытка не списана)',
171
- 'resetNoCredit': 'нет пригодной карты (не списано)',
172
- 'resetRedeemed': 'эта карта уже использована ранее',
173
- 'diagGenerate': 'Сгенерировать диагностический отчёт',
174
- 'diagCopy': 'Скопировать отчёт',
175
- 'diagIssues': 'Открыть трекер задач',
176
- 'diagHint': 'В отчёте нет токенов, email, адресов прокси и персональных данных.',
177
- 'subsPill': 'SUBS',
178
- 'subsModalTitle': 'Консоль подписок',
179
- 'subsActiveHero': 'Активная подписка',
180
- 'subsPoolTitle': 'Пул аккаунтов',
181
- 'subsRefresh': 'Обновить',
182
- 'subsOpenSettings': 'Все настройки →',
183
- 'subsFastMode': 'Режим Fast 1.5x',
184
- 'subsHealthy': 'Исправен',
185
- 'subsFree': 'свободно',
186
- 'subsUsed': 'израсходовано',
187
- 'subsModalClose': 'Закрыть',
188
- 'subsLogged': 'подключен',
189
- 'subsNotLogged': 'не подключен',
190
- 'subsCooldown': 'кулдаун',
191
- 'subsOpenSettings': 'Открыть настройки',
192
- 'hudTitle': 'Остаток подписки',
193
- 'settingsLoading': 'Загрузка настроек…',
194
- 'settingsUnavailable': 'Настройки недоступны',
195
- 'settingsRetry': 'Повторить',
196
- 'hudHint': 'перетащи · прилипает к краям · клик — обновить',
197
- 'fcCalibrating': 'калибровка…',
198
- 'fcIdle': 'нет расхода',
199
- 'fcHours': 'ч',
200
- 'fcMinutes': 'мин',
201
- 'reconnect': 'Переподключить',
202
- 'connect': 'Подключить',
203
- 'disconnect': 'Отключить',
204
- 'removeSlot': 'Убрать место',
205
- 'pastePlaceholder': 'Адрес перехода или код',
206
- 'submitCode': 'Отправить код',
207
- 'proxyPlaceholder': 'Прокси аккаунта (http://, https://, socks5://) - необязательно',
208
- 'proxyCheck': 'Проверить прокси',
209
- 'proxyOk': 'прокси ок',
210
- 'proxyFail': 'прокси недоступен',
211
- 'deviceLogin': 'Вход по коду',
212
- 'deviceHint': 'Headless: откройте ссылку, введите код, держите вкладку открытой.',
213
- 'deviceCopy': 'Скопировать код',
214
- 'devicePending': 'Ожидание подтверждения',
215
- 'deviceAuthorized': 'Вход выполнен',
216
- 'deviceExpired': 'Истёк - начните заново',
217
- 'verifyPrefix': 'Google требует однократной проверки аккаунта. ',
218
- 'verifyLink': 'Открыть страницу проверки',
219
- 'verifySuffix': ' затем подключитесь заново.',
220
- 'addAccount': '+ Добавить аккаунт',
221
- 'save': 'Сохранить',
222
- 'saved': 'Сохранено',
223
- 'loading': 'Загрузка…',
224
- 'accountLabel': 'Аккаунт',
225
- 'plan': 'Тариф',
226
- 'storedAs': 'Хранится как',
227
- 'switchLabel': 'Провайдер',
228
- 'chipActive': 'Подписки',
229
- 'expiryLabel': 'окончание',
230
- 'slashLogin': 'Вход в провайдера подписки',
231
- 'slashLogout': 'Выйти из провайдера подписки',
232
- 'slashStatus': 'Статус подписок',
233
- 'none': 'нет',
234
- 'forecast': '≈',
235
- 'resetCredits': 'Сброс лимитов ChatGPT',
236
- 'resetCreditsHint': 'Экстренный сброс 5-часового окна ChatGPT Plus/Pro. Требует 5 секунд подтверждения перед списанием.',
237
- 'resetCreditsAction': 'Сбросить лимит ChatGPT',
238
- 'resetAvailable': 'Доступно сбросов',
239
- 'resetExpires': 'срок действия',
240
- 'resetAck': 'Я понимаю, что одна попытка сброса будет списана',
241
- 'resetWait': 'подтверждение через',
242
- 'resetReady': 'готово к сбросу',
243
- 'resetGo': 'Сбросить лимит сейчас',
244
- 'resetBusy': 'Сброс…',
245
- 'resetDone': 'Лимит успешно сброшен',
246
- 'resetNothing': 'Лимит не требует сброса',
247
- 'resetNoCredit': 'Нет доступных попыток сброса',
248
- 'resetRedeemed': 'Попытка уже использована',
249
- 'windowPrimary': 'Окно 5 часов',
250
- 'windowSecondary': 'Окно 7 дней',
251
- 'usageLimitsTitle': 'Лимиты использования',
252
- 'slotDetailsTitle': 'Параметры слота',
253
- }
254
-
255
- return { en, ru }
256
- }