@goodandready/dsh-subscriptions 0.6.5 → 0.6.7
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 +16 -0
- package/README.ru.md +16 -0
- package/README.zh.md +9 -0
- package/lib/client.js +143 -3
- package/lib/history.js +78 -2
- package/lib/routes/status.js +125 -7
- package/lib/vendors/claude.js +54 -1
- package/package.json +6 -6
- package/lib/ratelimit-parser.js +0 -0
- package/lib/ui-styles.js +0 -119
package/README.md
CHANGED
|
@@ -186,6 +186,13 @@ const res = await ctx.subscriptions.request('codex', '/backend-api/codex/images/
|
|
|
186
186
|
* **Frosted Panel**: Hovering reveals a `backdrop-filter: blur(28px)` panel listing every account with usage bars. Clicking the bubble refreshes quota data; data also refreshes every 60 s.
|
|
187
187
|
* **Settings Card Polish (`v0.4.18`)**: Chevron switched to the core `IconChevronDownOutline14` primitive; explicit settings snapshot states (loading / unavailable + Retry) guard against phantom input.
|
|
188
188
|
|
|
189
|
+
### 16. ⚡ ClineBot-Inspired UI & Hardened Telemetry (`v0.6.6`)
|
|
190
|
+
* **Live Status Badge Bar**: Instant latency check to the host (`Host online (XX ms)`), real-time connected account indicators, and pool size at a glance.
|
|
191
|
+
* **One-Click Smoke Test (Ping)**: Test live connectivity to the active upstream subscription provider and measure real round-trip latency.
|
|
192
|
+
* **Session Telemetry Dashboard**: Visual stat cards displaying successful/total requests, average latency, session success rate, and last request activity.
|
|
193
|
+
* **Curated Models Catalog**: Quick preview of supported model families with context windows and capability tags (Vision, Reasoning, Hybrid).
|
|
194
|
+
* **Non-Blocking Storage**: `HistoryStore` debounces disk persistence asynchronously to prevent blocking the Node.js event loop during high-throughput streaming.
|
|
195
|
+
|
|
189
196
|
---
|
|
190
197
|
|
|
191
198
|
## 📦 Quick Installation
|
|
@@ -236,6 +243,15 @@ dsh-subscriptions:
|
|
|
236
243
|
|
|
237
244
|
---
|
|
238
245
|
|
|
246
|
+
## 🧠 Claude Adaptive Thinking & Effort (Added in v0.6.7)
|
|
247
|
+
|
|
248
|
+
Supports Anthropic's adaptive thinking (`thinking: { type: "adaptive" }`) and reasoning effort steering (`output_config: { effort }`):
|
|
249
|
+
- **Model Support**: Automatically gates effort levels for Opus 4.6+, Opus 4.7+, Opus 5 (`low`, `medium`, `high`, `xhigh`, `max`) and Sonnet 4.6+, Sonnet 5 (`low`, `medium`, `high`).
|
|
250
|
+
- **Safe Fallback**: Models that do not support adaptive thinking (Haiku, Fable, 4.5 and earlier) reject the parameter cleanly and leave requests unaugmented to prevent 400 Bad Request API errors.
|
|
251
|
+
- **Dynamic Catalog**: The provider model catalog exports matching `reasoning.efforts` options to the DeepSeek Harness interface.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
239
255
|
## 🌐 Localization
|
|
240
256
|
|
|
241
257
|
The plugin source language is English only. Russian and other translations are provided at runtime by separate language plugins (for example the russification plugin), which translate the registered locale keys - the package itself ships no bundled translations (Changed in 0.6.1).
|
package/README.ru.md
CHANGED
|
@@ -162,6 +162,13 @@ const res = await ctx.subscriptions.request('codex', '/backend-api/codex/images/
|
|
|
162
162
|
* **Reasoning vs Standard**: 429 на reasoning-модели (claude `*thinking*`, grok `*reasoning*`, все codex-модели) охлаждает только семейство reasoning этого аккаунта — standard-модели того же аккаунта продолжают работать. Legacy-кулдауны (от старых версий) по-прежнему блокируют весь аккаунт до истечения.
|
|
163
163
|
* **Скрыть устаревшие модели (`hideDeprecatedModels`)**: убирает из нативного селектора id с `test`/`preview`/`dev`/`alpha`/`beta`/`legacy` (действует и на живые каталоги, и на статичный фолбэк).
|
|
164
164
|
|
|
165
|
+
### 12. ⚡ Дизайн в стиле ClineBot и неблокирующая телеметрия (`v0.6.6`)
|
|
166
|
+
* **Строка статуса с бейджами**: Мгновенный замер задержки хоста (`Host online (XX ms)`), количество активных подключений и размер пула.
|
|
167
|
+
* **Интерактивный Smoke Test (Ping)**: Проверка связи с провайдером в один клик с измерением реального времени отклика в миллисекундах.
|
|
168
|
+
* **Панель телеметрии сессии**: 4 карточки метрик (успешных/всего запросов, средняя задержка, процент успешности, последняя активность).
|
|
169
|
+
* **Каталог моделей подписок**: Компактный список поддерживаемых моделей с размером контекста (128k, 200k, 1M) и тегами возможностей.
|
|
170
|
+
* **Асинхронная история**: Неблокирующий дебаунсинг записи `HistoryStore`, исключающий просадки event loop при интенсивном стриминге.
|
|
171
|
+
|
|
165
172
|
---
|
|
166
173
|
|
|
167
174
|
## 📦 Быстрая установка
|
|
@@ -209,6 +216,15 @@ dsh-subscriptions:
|
|
|
209
216
|
|
|
210
217
|
---
|
|
211
218
|
|
|
219
|
+
## 🧠 Адаптивное мышление Claude и уровни усилия (Добавлено в v0.6.7)
|
|
220
|
+
|
|
221
|
+
Поддержка адаптивного мышления Anthropic (`thinking: { type: "adaptive" }`) и управления глубиной рассуждений (`output_config: { effort }`):
|
|
222
|
+
- **Поддержка моделей**: Автоматическое определение уровней усилия для Opus 4.6+, Opus 4.7+, Opus 5 (`low`, `medium`, `high`, `xhigh`, `max`) и Sonnet 4.6+, Sonnet 5 (`low`, `medium`, `high`).
|
|
223
|
+
- **Безопасный фоллбэк**: Модели без поддержки адаптивного рассуждения (Haiku, Fable, 4.5 и старше) не перегружаются параметрами во избежание ошибок Anthropic API (400 Bad Request).
|
|
224
|
+
- **Динамический каталог**: В каталоге моделей регистрируется секция `reasoning.efforts`, доступная для выбора в интерфейсе DeepSeek Harness.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
212
228
|
## 🌐 Локализация
|
|
213
229
|
|
|
214
230
|
Исходный язык плагина - только английский. Русский и другие переводы во время работы предоставляют отдельные языковые плагины (например, плагин русификации), переводящие зарегистрированные ключи локали, - сам пакет не содержит встроенных переводов (Changed in 0.6.1).
|
package/README.zh.md
CHANGED
|
@@ -138,6 +138,15 @@ dsh plugin --profile web add @goodandready/dsh-subscriptions
|
|
|
138
138
|
|
|
139
139
|
---
|
|
140
140
|
|
|
141
|
+
## 🧠 Claude 自适应思考与推理深度控制 (v0.6.7 新增)
|
|
142
|
+
|
|
143
|
+
支持 Anthropic 自适应思考 (`thinking: { type: "adaptive" }`) 及推理深度调节 (`output_config: { effort }`):
|
|
144
|
+
- **模型支持**: 针对 Opus 4.6+、Opus 4.7+、Opus 5 (`low`, `medium`, `high`, `xhigh`, `max`) 及 Sonnet 4.6+、Sonnet 5 (`low`, `medium`, `high`) 自动提供对应推理级别。
|
|
145
|
+
- **安全降级**: 不支持自适应思考的模型(如 Haiku、Fable、4.5 及更早版本)保持原始请求,防止出现 400 Bad Request 错误。
|
|
146
|
+
- **动态目录**: 模型列表自动暴露 `reasoning.efforts`,供 DeepSeek Harness 界面选择。
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
141
150
|
## 🌐 本地化
|
|
142
151
|
|
|
143
152
|
插件源语言仅为英语。俄语及其他翻译由独立的语言插件(如俄化插件)在运行时对注册的本地化键进行翻译,包本身不内置翻译(Changed in 0.6.1)。
|
package/lib/client.js
CHANGED
|
@@ -111,6 +111,27 @@ window.__ModuleLoader__.load({
|
|
|
111
111
|
'.dsub-details{margin-top:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 12px;background:var(--dsw-alias-bg-layer-2)}' +
|
|
112
112
|
'.dsub-summary{cursor:pointer;font-weight:600;font-size:12.5px;color:var(--dsw-alias-label-secondary);user-select:none}' +
|
|
113
113
|
'.dsub-summary:hover{color:var(--dsw-alias-label-primary)}' +
|
|
114
|
+
'.dsub-header-bar{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;padding:8px 0 12px;margin-bottom:8px;border-bottom:1px solid var(--dsw-alias-border-l2)}' +
|
|
115
|
+
'.dsub-badge{font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);display:inline-flex;align-items:center;gap:5px;font-weight:500}' +
|
|
116
|
+
'.dsub-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:rgba(16,185,129,0.08)}' +
|
|
117
|
+
'.dsub-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:rgba(245,158,11,0.08)}' +
|
|
118
|
+
'.dsub-badge-bad{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);background:rgba(239,68,68,0.08)}' +
|
|
119
|
+
'.dsub-badge-neutral{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}' +
|
|
120
|
+
'.dsub-section-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:10px;margin-top:12px}' +
|
|
121
|
+
'.dsub-section-title{font-size:14.5px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;gap:8px}' +
|
|
122
|
+
'.dsub-section-desc{font-size:12px;color:var(--dsw-alias-label-secondary);margin-top:-4px;line-height:1.4}' +
|
|
123
|
+
'.dsub-grid-2{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:10px}' +
|
|
124
|
+
'.dsub-stat-box{padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);display:flex;flex-direction:column;gap:3px}' +
|
|
125
|
+
'.dsub-stat-val{font-size:16.5px;font-weight:700;color:var(--dsw-alias-label-primary)}' +
|
|
126
|
+
'.dsub-stat-lbl{font-size:11.5px;color:var(--dsw-alias-label-secondary)}' +
|
|
127
|
+
'.dsub-alert-ok{padding:8px 12px;border-radius:8px;background:rgba(16,185,129,0.09);color:var(--dsw-alias-state-success-primary);font-size:12.5px;border:1px solid rgba(16,185,129,0.25)}' +
|
|
128
|
+
'.dsub-alert-bad{padding:8px 12px;border-radius:8px;background:rgba(239,68,68,0.09);color:var(--dsw-alias-state-error-primary);font-size:12.5px;border:1px solid rgba(239,68,68,0.25)}' +
|
|
129
|
+
'.dsub-btn-primary{background:var(--dsw-alias-label-primary) !important;color:var(--dsw-alias-bg-layer-3) !important;border-color:transparent !important;font-weight:600}' +
|
|
130
|
+
'.dsub-btn-primary:hover:not(:disabled){opacity:0.9}' +
|
|
131
|
+
'.dsub-model-grid{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}' +
|
|
132
|
+
'.dsub-model-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;border-radius:6px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);font-size:11.5px;color:var(--dsw-alias-label-primary)}' +
|
|
133
|
+
'.dsub-model-chip code{font-family:ui-monospace,monospace;font-weight:600}' +
|
|
134
|
+
'.dsub-model-tag{font-size:10px;padding:1px 4px;border-radius:3px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font-weight:600}' +
|
|
114
135
|
'.dsub-heroBars{display:flex;flex-direction:column;gap:8px;margin-top:10px}' +
|
|
115
136
|
'.dsub-barLabelRow{display:flex;justify-content:space-between;font-size:11.5px;color:var(--dsw-alias-label-secondary);margin-bottom:3px}' +
|
|
116
137
|
'.dsub-barTrack{height:6px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}' +
|
|
@@ -353,6 +374,21 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
353
374
|
'probeIntervalMinHint': 'Interval for background checks to refresh account status and quota balance.',
|
|
354
375
|
'notifyLimits': 'Notify on rate limits',
|
|
355
376
|
'notifyLimitsHint': 'Show notification alerts when account usage reaches quota ceiling.',
|
|
377
|
+
'hostOnline': 'Host online',
|
|
378
|
+
'hostOffline': 'Host unreachable',
|
|
379
|
+
'statsTitle': 'Session Telemetry',
|
|
380
|
+
'statsRequests': 'Successful / Total Requests',
|
|
381
|
+
'statsLatency': 'Avg Response Latency',
|
|
382
|
+
'statsSuccessRate': 'Session Success Rate',
|
|
383
|
+
'statsLastActivity': 'Last Activity',
|
|
384
|
+
'smokeTitle': 'Connection Smoke Test (Ping)',
|
|
385
|
+
'smokeDesc': 'Test live upstream connectivity and measure round-trip latency to the subscription provider.',
|
|
386
|
+
'runSmoke': '⚡ Run Smoke Test (Ping)',
|
|
387
|
+
'smokeTesting': 'Pinging…',
|
|
388
|
+
'smokeOk': 'Smoke test passed',
|
|
389
|
+
'smokeFail': 'Smoke test failed',
|
|
390
|
+
'modelsCatalogTitle': 'Curated Subscription Models',
|
|
391
|
+
'modelsCatalogDesc': 'Available model catalog across active subscriptions in DeepSeek Harness.',
|
|
356
392
|
'resetCredits': 'Reset credits',
|
|
357
393
|
'resetCreditsHint': 'ChatGPT reset cards: emergency reset for the 5-hour window. Requires a 5s confirmation.',
|
|
358
394
|
'resetCreditsAction': 'Reset ChatGPT Limit',
|
|
@@ -650,6 +686,32 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
650
686
|
.catch((e) => setDiag('error: ' + String(e && e.message || e)))
|
|
651
687
|
}
|
|
652
688
|
const [checkRes, setCheckRes] = React.useState({})
|
|
689
|
+
const [hostPing, setHostPing] = React.useState({ ok: true, latencyMs: null })
|
|
690
|
+
const [telemetry, setTelemetry] = React.useState(null)
|
|
691
|
+
const [smoke, setSmoke] = React.useState({ testing: false, result: null })
|
|
692
|
+
|
|
693
|
+
const reloadTelemetry = () => {
|
|
694
|
+
fetch('/dsh-subscriptions/telemetry', { cache: 'no-store' })
|
|
695
|
+
.then((r) => r.json())
|
|
696
|
+
.then((d) => { if (d && d.ok && d.telemetry) setTelemetry(d.telemetry) })
|
|
697
|
+
.catch(() => {})
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const runSmokeTest = async (provider, index) => {
|
|
701
|
+
setSmoke({ testing: true, result: null })
|
|
702
|
+
try {
|
|
703
|
+
const res = await fetch('/dsh-subscriptions/smoke', {
|
|
704
|
+
method: 'POST',
|
|
705
|
+
headers: { 'Content-Type': 'application/json' },
|
|
706
|
+
body: JSON.stringify({ provider, index }),
|
|
707
|
+
})
|
|
708
|
+
const d = await res.json()
|
|
709
|
+
setSmoke({ testing: false, result: d })
|
|
710
|
+
reloadTelemetry()
|
|
711
|
+
} catch (e) {
|
|
712
|
+
setSmoke({ testing: false, result: { ok: false, error: { message: String(e && e.message || e) } } })
|
|
713
|
+
}
|
|
714
|
+
}
|
|
653
715
|
const [checking, setChecking] = React.useState({})
|
|
654
716
|
const [saved, setSaved] = React.useState(false)
|
|
655
717
|
const [tokenDraft, setTokenDraft] = React.useState({})
|
|
@@ -663,9 +725,21 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
663
725
|
setProviders((data && data.providers) || [])
|
|
664
726
|
}
|
|
665
727
|
|
|
666
|
-
const reload = () =>
|
|
667
|
-
|
|
668
|
-
|
|
728
|
+
const reload = () => {
|
|
729
|
+
const t0 = Date.now()
|
|
730
|
+
return fetch('/dsh-subscriptions/config', { cache: 'no-store' })
|
|
731
|
+
.then((res) => res.json())
|
|
732
|
+
.then((data) => {
|
|
733
|
+
setHostPing({ ok: true, latencyMs: Date.now() - t0 })
|
|
734
|
+
applyPayload(data)
|
|
735
|
+
reloadTelemetry()
|
|
736
|
+
return data
|
|
737
|
+
})
|
|
738
|
+
.catch((err) => {
|
|
739
|
+
setHostPing({ ok: false, latencyMs: null })
|
|
740
|
+
throw err
|
|
741
|
+
})
|
|
742
|
+
}
|
|
669
743
|
|
|
670
744
|
React.useEffect(() => {
|
|
671
745
|
let alive = true
|
|
@@ -871,7 +945,59 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
871
945
|
{ id: 'antigravity', name: 'Antigravity' },
|
|
872
946
|
]
|
|
873
947
|
|
|
948
|
+
const connectedCount = accounts.filter((a) => a.configured).length
|
|
949
|
+
|
|
874
950
|
return React.createElement('div', { className: 'dsub-wrap' },
|
|
951
|
+
React.createElement('div', { className: 'dsub-header-bar' },
|
|
952
|
+
React.createElement('div', { className: 'dsub-row', style: { marginTop: 0 } },
|
|
953
|
+
React.createElement('span', {
|
|
954
|
+
className: 'dsub-badge ' + (hostPing.ok ? 'dsub-badge-ok' : 'dsub-badge-bad')
|
|
955
|
+
}, hostPing.ok
|
|
956
|
+
? (hostPing.latencyMs != null ? 'Host online (' + hostPing.latencyMs + ' ms)' : t('hostOnline'))
|
|
957
|
+
: t('hostOffline')
|
|
958
|
+
),
|
|
959
|
+
React.createElement('span', {
|
|
960
|
+
className: 'dsub-badge ' + (connectedCount > 0 ? 'dsub-badge-ok' : 'dsub-badge-warn')
|
|
961
|
+
}, connectedCount > 0
|
|
962
|
+
? (connectedCount + ' ' + t('subsLogged'))
|
|
963
|
+
: t('subsNotLogged')
|
|
964
|
+
),
|
|
965
|
+
React.createElement('span', {
|
|
966
|
+
className: 'dsub-badge dsub-badge-neutral'
|
|
967
|
+
}, 'Pool: ' + accounts.length + ' accounts'),
|
|
968
|
+
),
|
|
969
|
+
React.createElement('button', {
|
|
970
|
+
type: 'button',
|
|
971
|
+
className: 'dsub-mini dsub-btn-primary',
|
|
972
|
+
disabled: smoke.testing,
|
|
973
|
+
onClick: () => runSmokeTest(),
|
|
974
|
+
}, smoke.testing ? t('smokeTesting') : t('runSmoke')),
|
|
975
|
+
),
|
|
976
|
+
smoke.result ? React.createElement('div', {
|
|
977
|
+
className: smoke.result.ok ? 'dsub-alert-ok' : 'dsub-alert-bad',
|
|
978
|
+
style: { marginBottom: '12px' }
|
|
979
|
+
}, smoke.result.ok
|
|
980
|
+
? ('🟢 ' + t('smokeOk') + ': ' + (smoke.result.provider || 'Provider').toUpperCase() + ' · ' + (smoke.result.latencyMs != null ? smoke.result.latencyMs + ' ms' : 'OK') + (smoke.result.label ? ' (' + smoke.result.label + ')' : ''))
|
|
981
|
+
: ('❌ ' + t('smokeFail') + ': ' + (smoke.result.error && (smoke.result.error.message || smoke.result.error.code) || 'Unknown error'))
|
|
982
|
+
) : null,
|
|
983
|
+
telemetry && telemetry.totalRequests > 0 ? React.createElement('div', { className: 'dsub-grid-2', style: { marginBottom: '14px' } },
|
|
984
|
+
React.createElement('div', { className: 'dsub-stat-box' },
|
|
985
|
+
React.createElement('div', { className: 'dsub-stat-val' }, telemetry.successRequests + ' / ' + telemetry.totalRequests),
|
|
986
|
+
React.createElement('div', { className: 'dsub-stat-lbl' }, t('statsRequests')),
|
|
987
|
+
),
|
|
988
|
+
React.createElement('div', { className: 'dsub-stat-box' },
|
|
989
|
+
React.createElement('div', { className: 'dsub-stat-val' }, (telemetry.avgLatencyMs || 0) + ' ms'),
|
|
990
|
+
React.createElement('div', { className: 'dsub-stat-lbl' }, t('statsLatency')),
|
|
991
|
+
),
|
|
992
|
+
React.createElement('div', { className: 'dsub-stat-box' },
|
|
993
|
+
React.createElement('div', { className: 'dsub-stat-val' }, (telemetry.successRate || 100) + '%'),
|
|
994
|
+
React.createElement('div', { className: 'dsub-stat-lbl' }, t('statsSuccessRate')),
|
|
995
|
+
),
|
|
996
|
+
React.createElement('div', { className: 'dsub-stat-box' },
|
|
997
|
+
React.createElement('div', { className: 'dsub-stat-val' }, telemetry.lastRequestAt ? Math.round((Date.now() - telemetry.lastRequestAt) / 60000) + 'm ago' : '—'),
|
|
998
|
+
React.createElement('div', { className: 'dsub-stat-lbl' }, t('statsLastActivity')),
|
|
999
|
+
),
|
|
1000
|
+
) : null,
|
|
875
1001
|
React.createElement('div', { className: 'dsub-block' },
|
|
876
1002
|
React.createElement('div', { className: 'dsub-h' }, t('title')),
|
|
877
1003
|
React.createElement('div', { className: 'dsub-sub' },
|
|
@@ -1011,6 +1137,20 @@ const cssId = 'dsh-subscriptions/settings.module.css'
|
|
|
1011
1137
|
}),
|
|
1012
1138
|
) : null,
|
|
1013
1139
|
),
|
|
1140
|
+
React.createElement('details', { className: 'dsub-group' },
|
|
1141
|
+
React.createElement('summary', { className: 'dsub-groupHead' }, '🎯 ' + t('modelsCatalogTitle')),
|
|
1142
|
+
React.createElement('div', { className: 'dsub-sub', style: { marginTop: '4px' } }, t('modelsCatalogDesc')),
|
|
1143
|
+
React.createElement('div', { className: 'dsub-model-grid' },
|
|
1144
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'gpt-4o'), React.createElement('span', { className: 'dsub-model-tag' }, '128k · Vision')),
|
|
1145
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'o1 / o3-mini'), React.createElement('span', { className: 'dsub-model-tag' }, '200k · Reasoning')),
|
|
1146
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'claude-3-7-sonnet'), React.createElement('span', { className: 'dsub-model-tag' }, '200k · Hybrid')),
|
|
1147
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'claude-3-5-sonnet'), React.createElement('span', { className: 'dsub-model-tag' }, '200k · Coding')),
|
|
1148
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'grok-beta'), React.createElement('span', { className: 'dsub-model-tag' }, '128k · Speed')),
|
|
1149
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'gemini-2.5-pro'), React.createElement('span', { className: 'dsub-model-tag' }, '1M · Context')),
|
|
1150
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'kimi-k1.5'), React.createElement('span', { className: 'dsub-model-tag' }, '128k · Math')),
|
|
1151
|
+
React.createElement('div', { className: 'dsub-model-chip' }, React.createElement('code', null, 'glm-4-plus'), React.createElement('span', { className: 'dsub-model-tag' }, '128k · Chinese')),
|
|
1152
|
+
),
|
|
1153
|
+
),
|
|
1014
1154
|
React.createElement('details', { className: 'dsub-details' },
|
|
1015
1155
|
React.createElement('summary', { className: 'dsub-summary' }, t('advancedSettings')),
|
|
1016
1156
|
React.createElement('div', { className: 'dsub-block' },
|
package/lib/history.js
CHANGED
|
@@ -4,17 +4,24 @@ import { homedir } from 'node:os'
|
|
|
4
4
|
|
|
5
5
|
// #65: request and cost history. Stored in
|
|
6
6
|
// ~/.dsh/storages/dsh-subscriptions/history.json (JSON array, newest first).
|
|
7
|
-
// All IO is best-effort
|
|
7
|
+
// All IO is best-effort: a write failure must never crash the harness.
|
|
8
8
|
|
|
9
9
|
export const DEFAULT_HISTORY_DIR = join(homedir(), '.dsh', 'storages', 'dsh-subscriptions')
|
|
10
10
|
|
|
11
11
|
export class HistoryStore {
|
|
12
|
-
constructor(dir = DEFAULT_HISTORY_DIR, ttlMs = 7 * 24 * 60 * 60 * 1000) {
|
|
12
|
+
constructor(dir = DEFAULT_HISTORY_DIR, ttlMs = 7 * 24 * 60 * 60 * 1000, debounceMs = 0) {
|
|
13
13
|
this.dir = dir
|
|
14
14
|
this.ttlMs = ttlMs
|
|
15
|
+
this.debounceMs = debounceMs
|
|
15
16
|
this.path = join(dir, 'history.json')
|
|
16
17
|
this.rows = []
|
|
18
|
+
this._persistTimer = null
|
|
17
19
|
this._load()
|
|
20
|
+
|
|
21
|
+
if (typeof process !== 'undefined' && typeof process.on === 'function') {
|
|
22
|
+
this._exitHandler = () => this.flush()
|
|
23
|
+
process.on('beforeExit', this._exitHandler)
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
_load() {
|
|
@@ -36,6 +43,24 @@ export class HistoryStore {
|
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
_persist() {
|
|
46
|
+
if (this.debounceMs > 0) {
|
|
47
|
+
if (this._persistTimer) return
|
|
48
|
+
this._persistTimer = setTimeout(() => {
|
|
49
|
+
this._persistTimer = null
|
|
50
|
+
this.flush()
|
|
51
|
+
}, this.debounceMs)
|
|
52
|
+
if (typeof this._persistTimer.unref === 'function') this._persistTimer.unref()
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
this.flush()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Write pending updates to disk immediately. */
|
|
59
|
+
flush() {
|
|
60
|
+
if (this._persistTimer) {
|
|
61
|
+
clearTimeout(this._persistTimer)
|
|
62
|
+
this._persistTimer = null
|
|
63
|
+
}
|
|
39
64
|
try {
|
|
40
65
|
mkdirSync(this.dir, { recursive: true })
|
|
41
66
|
writeFileSync(this.path, JSON.stringify(this.rows))
|
|
@@ -63,4 +88,55 @@ export class HistoryStore {
|
|
|
63
88
|
size() {
|
|
64
89
|
return this.rows.length
|
|
65
90
|
}
|
|
91
|
+
|
|
92
|
+
/** Summarize session and 24-hour telemetry for UI stat cards. */
|
|
93
|
+
telemetrySummary() {
|
|
94
|
+
const total = this.rows.length
|
|
95
|
+
if (total === 0) {
|
|
96
|
+
return {
|
|
97
|
+
totalRequests: 0,
|
|
98
|
+
requests24h: 0,
|
|
99
|
+
successRequests: 0,
|
|
100
|
+
errorRequests: 0,
|
|
101
|
+
successRate: 100,
|
|
102
|
+
avgLatencyMs: 0,
|
|
103
|
+
lastLatencyMs: 0,
|
|
104
|
+
lastRequestAt: null,
|
|
105
|
+
lastProvider: null,
|
|
106
|
+
lastModel: null,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
let success = 0
|
|
110
|
+
let error = 0
|
|
111
|
+
let totalMs = 0
|
|
112
|
+
let latencyCount = 0
|
|
113
|
+
const now = Date.now()
|
|
114
|
+
const last24h = now - 24 * 60 * 60 * 1000
|
|
115
|
+
let count24h = 0
|
|
116
|
+
|
|
117
|
+
for (const r of this.rows) {
|
|
118
|
+
if (r.ts >= last24h) count24h++
|
|
119
|
+
const st = Number(r.status || 0)
|
|
120
|
+
if (st >= 200 && st < 400) success++
|
|
121
|
+
else if (st >= 400) error++
|
|
122
|
+
if (typeof r.ms === 'number' && r.ms > 0) {
|
|
123
|
+
totalMs += r.ms
|
|
124
|
+
latencyCount++
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const first = this.rows[0] // newest first
|
|
129
|
+
return {
|
|
130
|
+
totalRequests: total,
|
|
131
|
+
requests24h: count24h,
|
|
132
|
+
successRequests: success,
|
|
133
|
+
errorRequests: error,
|
|
134
|
+
successRate: total > 0 ? Math.round((success / total) * 100) : 100,
|
|
135
|
+
avgLatencyMs: latencyCount > 0 ? Math.round(totalMs / latencyCount) : 0,
|
|
136
|
+
lastLatencyMs: (first && typeof first.ms === 'number') ? first.ms : 0,
|
|
137
|
+
lastRequestAt: (first && first.ts) || null,
|
|
138
|
+
lastProvider: (first && first.provider) || null,
|
|
139
|
+
lastModel: (first && first.model) || null,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
66
142
|
}
|
package/lib/routes/status.js
CHANGED
|
@@ -32,7 +32,7 @@ export function registerStatusRoutes(ctx, state) {
|
|
|
32
32
|
ctx.effect(() => ctx.webServer.register({
|
|
33
33
|
kind: 'exact',
|
|
34
34
|
path: '/dsh-subscriptions/config',
|
|
35
|
-
handler: async (req, res) => {
|
|
35
|
+
handler: safeJsonHandler(async (req, res) => {
|
|
36
36
|
if (req.method === 'GET') {
|
|
37
37
|
writeJson(res, 200, await configResponse())
|
|
38
38
|
return
|
|
@@ -67,10 +67,7 @@ export function registerStatusRoutes(ctx, state) {
|
|
|
67
67
|
} catch (e) {
|
|
68
68
|
writeJson(res, 400, { ok: false, error: { code: 'save', message: String(e && e.message || e) } })
|
|
69
69
|
}
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
}),
|
|
74
71
|
}), 'dsh-subscriptions: /config')
|
|
75
72
|
|
|
76
73
|
|
|
@@ -239,7 +236,7 @@ export function registerStatusRoutes(ctx, state) {
|
|
|
239
236
|
ctx.effect(() => ctx.webServer.register({
|
|
240
237
|
kind: 'exact',
|
|
241
238
|
path: '/dsh-subscriptions/history',
|
|
242
|
-
handler: async (req, res) => {
|
|
239
|
+
handler: safeJsonHandler(async (req, res) => {
|
|
243
240
|
if (req.method !== 'GET') {
|
|
244
241
|
writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET only' } })
|
|
245
242
|
return
|
|
@@ -251,6 +248,127 @@ export function registerStatusRoutes(ctx, state) {
|
|
|
251
248
|
}
|
|
252
249
|
const limit = Math.min(Number(queryOf(req).get('limit') || '10'), 100)
|
|
253
250
|
writeJson(res, 200, { ok: true, total: history.size(), items: history.recent(limit) })
|
|
254
|
-
},
|
|
251
|
+
}),
|
|
255
252
|
}), 'dsh-subscriptions: /history')
|
|
253
|
+
|
|
254
|
+
// Telemetry summary route for UI stat cards (requests, latency, success rate)
|
|
255
|
+
ctx.effect(() => ctx.webServer.register({
|
|
256
|
+
kind: 'exact',
|
|
257
|
+
path: '/dsh-subscriptions/telemetry',
|
|
258
|
+
handler: safeJsonHandler(async (req, res) => {
|
|
259
|
+
if (req.method !== 'GET') {
|
|
260
|
+
writeJson(res, 405, { ok: false, error: { code: 'method', message: 'GET only' } })
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
const summary = typeof history.telemetrySummary === 'function'
|
|
264
|
+
? history.telemetrySummary()
|
|
265
|
+
: { totalRequests: history.size(), requests24h: 0, successRequests: 0, errorRequests: 0, successRate: 100, avgLatencyMs: 0 }
|
|
266
|
+
writeJson(res, 200, { ok: true, telemetry: summary })
|
|
267
|
+
}),
|
|
268
|
+
}), 'dsh-subscriptions: /telemetry')
|
|
269
|
+
|
|
270
|
+
// Live smoke ping test for active subscription providers
|
|
271
|
+
ctx.effect(() => ctx.webServer.register({
|
|
272
|
+
kind: 'exact',
|
|
273
|
+
path: '/dsh-subscriptions/smoke',
|
|
274
|
+
handler: safeJsonHandler(async (req, res) => {
|
|
275
|
+
if (req.method !== 'POST') {
|
|
276
|
+
writeJson(res, 405, { ok: false, error: { code: 'method', message: 'POST only' } })
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
let payload = {}
|
|
280
|
+
try { payload = JSON.parse((await readBody(req, 64 * 1024)).toString('utf8') || '{}') } catch {}
|
|
281
|
+
|
|
282
|
+
let provider = payload.provider
|
|
283
|
+
let index = payload.index || 1
|
|
284
|
+
|
|
285
|
+
if (!provider) {
|
|
286
|
+
const accounts = await accountsView()
|
|
287
|
+
const connected = accounts.find((a) => a.configured)
|
|
288
|
+
if (connected) {
|
|
289
|
+
provider = connected.provider
|
|
290
|
+
index = connected.index || 1
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!provider || !isProvider(provider)) {
|
|
295
|
+
writeJson(res, 400, { ok: false, error: { code: 'no_connected_accounts', message: 'No connected subscription account found to test' } })
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const ref = oauthRef(provider, index)
|
|
300
|
+
const info = await store.describeRef(ref)
|
|
301
|
+
if (!info.configured) {
|
|
302
|
+
writeJson(res, 400, { ok: false, provider, index, ref, error: { code: 'not_connected', message: 'Account is not connected' } })
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const t0 = Date.now()
|
|
307
|
+
let blob
|
|
308
|
+
try {
|
|
309
|
+
blob = await store.loadBlob(ref)
|
|
310
|
+
blob = await store.ensureFresh(provider, blob, ref)
|
|
311
|
+
} catch (e) {
|
|
312
|
+
const latencyMs = Date.now() - t0
|
|
313
|
+
history.add({ provider, ref, path: "/smoke", status: 401, ms: latencyMs })
|
|
314
|
+
writeJson(res, 502, { ok: false, provider, index, ref, latencyMs, error: { code: 'auth_failed', message: String(e && e.message || e) } })
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const vendor = getVendor(provider)
|
|
319
|
+
if (typeof vendor.check === 'function') {
|
|
320
|
+
let capturedQuota = null
|
|
321
|
+
const probeFetch = async (url, init) => {
|
|
322
|
+
const res2 = await fetch(url, init)
|
|
323
|
+
try {
|
|
324
|
+
const snap = quotaSnapshot(provider, res2.headers, null, Date.now())
|
|
325
|
+
if (snap) { capturedQuota = snap; store.rememberQuota(ref, snap) }
|
|
326
|
+
} catch {}
|
|
327
|
+
return res2
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
await vendor.check(blob, vendorConfig(provider, live()), probeFetch)
|
|
331
|
+
const latencyMs = Date.now() - t0
|
|
332
|
+
history.add({ provider, ref, path: "/smoke", status: 200, ms: latencyMs })
|
|
333
|
+
writeJson(res, 200, {
|
|
334
|
+
ok: true,
|
|
335
|
+
provider,
|
|
336
|
+
index,
|
|
337
|
+
ref,
|
|
338
|
+
latencyMs,
|
|
339
|
+
label: pmL(blob.label),
|
|
340
|
+
email: pmE(blob.email),
|
|
341
|
+
quota: capturedQuota || info.quota || null,
|
|
342
|
+
usagePercent: info.usagePercent ?? null,
|
|
343
|
+
})
|
|
344
|
+
return
|
|
345
|
+
} catch (e) {
|
|
346
|
+
const latencyMs = Date.now() - t0
|
|
347
|
+
history.add({ provider, ref, path: "/smoke", status: 502, ms: latencyMs })
|
|
348
|
+
writeJson(res, 502, {
|
|
349
|
+
ok: false,
|
|
350
|
+
provider,
|
|
351
|
+
index,
|
|
352
|
+
ref,
|
|
353
|
+
latencyMs,
|
|
354
|
+
error: { code: e && e.code ? e.code : 'VENDOR', message: String(e && e.message || e).slice(0, 300) },
|
|
355
|
+
})
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const latencyMs = Date.now() - t0
|
|
361
|
+
history.add({ provider, ref, path: "/smoke", status: 200, ms: latencyMs })
|
|
362
|
+
writeJson(res, 200, {
|
|
363
|
+
ok: true,
|
|
364
|
+
provider,
|
|
365
|
+
index,
|
|
366
|
+
ref,
|
|
367
|
+
latencyMs,
|
|
368
|
+
label: pmL(blob.label),
|
|
369
|
+
email: pmE(blob.email),
|
|
370
|
+
quota: info.quota || null,
|
|
371
|
+
})
|
|
372
|
+
}),
|
|
373
|
+
}), 'dsh-subscriptions: /smoke')
|
|
256
374
|
}
|
package/lib/vendors/claude.js
CHANGED
|
@@ -19,6 +19,38 @@ export function providerInfo() {
|
|
|
19
19
|
return { id, name: 'Claude' }
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// Thinking-effort support follows Anthropic's current semantics:
|
|
23
|
+
// - Adaptive thinking (`thinking: { type: 'adaptive' }`) is the recommended /
|
|
24
|
+
// only on-mode for current generations; depth and token spend are steered by
|
|
25
|
+
// `output_config: { effort }`.
|
|
26
|
+
// - Effort is accepted on Opus 4.6+ and Sonnet 4.6+; `xhigh` was added on
|
|
27
|
+
// Opus 4.7; `max` is Opus-tier only.
|
|
28
|
+
// - Sonnet 4.5 / Haiku / Fable families reject effort (400), and models older
|
|
29
|
+
// than the adaptive generation use the legacy budget_tokens knob, so no
|
|
30
|
+
// effort control is advertised there. Model ids with a major version only
|
|
31
|
+
// (e.g. `claude-opus-5`) are treated as the latest of their family.
|
|
32
|
+
const BASE_EFFORT_LEVELS = ['low', 'medium', 'high']
|
|
33
|
+
|
|
34
|
+
export function effortLevelsForModel(modelId) {
|
|
35
|
+
const m = /^claude-(opus|sonnet|haiku|fable)(?:-(\d+))?(?:-(\d+))?/.exec(String(modelId || ''))
|
|
36
|
+
if (!m) return null
|
|
37
|
+
const [, family, majorRaw, minorRaw] = m
|
|
38
|
+
if (family === 'haiku' || family === 'fable') return null
|
|
39
|
+
const major = majorRaw ? Number(majorRaw) : null
|
|
40
|
+
const minor = minorRaw ? Number(minorRaw) : null
|
|
41
|
+
const atLeast = (wantMajor, wantMinor) => (
|
|
42
|
+
major === null || major > wantMajor || (major === wantMajor && (minor === null || minor >= wantMinor))
|
|
43
|
+
)
|
|
44
|
+
// Adaptive thinking / effort exist from Opus 4.6 and Sonnet 4.6 onward.
|
|
45
|
+
if (!atLeast(4, 6)) return null
|
|
46
|
+
const levels = [...BASE_EFFORT_LEVELS]
|
|
47
|
+
if (family === 'opus') {
|
|
48
|
+
if (atLeast(4, 7)) levels.push('xhigh')
|
|
49
|
+
levels.push('max')
|
|
50
|
+
}
|
|
51
|
+
return levels
|
|
52
|
+
}
|
|
53
|
+
|
|
22
54
|
export function defaults() {
|
|
23
55
|
return {
|
|
24
56
|
clientId: '9d1c250a-e61b-44d9-88ed-5944d1962f5e',
|
|
@@ -88,7 +120,27 @@ export async function refresh(cfg, blob, fetchImpl) {
|
|
|
88
120
|
}
|
|
89
121
|
|
|
90
122
|
export async function listModels(blob, cfg) {
|
|
91
|
-
|
|
123
|
+
const raw = cfg.models && cfg.models.length ? cfg.models : defaults().models
|
|
124
|
+
const entries = raw.map((entry) => {
|
|
125
|
+
const model = typeof entry === 'string' ? { id: entry, name: entry } : { ...entry }
|
|
126
|
+
const levels = effortLevelsForModel(model.id || model.name)
|
|
127
|
+
if (!levels) return model
|
|
128
|
+
return { ...model, reasoning: { efforts: levels.map((level) => ({ id: level, name: level })) } }
|
|
129
|
+
})
|
|
130
|
+
return modelCatalog(id, entries)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Advertise adaptive thinking + effort only for models whose capability table
|
|
134
|
+
// declares the chosen level; anything else (unsupported family, 'off', no
|
|
135
|
+
// selection) leaves the request untouched so behavior stays opt-in.
|
|
136
|
+
export function applyClaudeThinking(payload, options) {
|
|
137
|
+
const effort = options && String(options.reasoningEffort || '')
|
|
138
|
+
if (!effort || effort === 'off') return payload
|
|
139
|
+
const levels = effortLevelsForModel(payload && payload.model)
|
|
140
|
+
if (!levels || !levels.includes(effort)) return payload
|
|
141
|
+
payload.thinking = { type: 'adaptive' }
|
|
142
|
+
payload.output_config = { effort }
|
|
143
|
+
return payload
|
|
92
144
|
}
|
|
93
145
|
|
|
94
146
|
|
|
@@ -114,6 +166,7 @@ export async function usage(blob, _cfg, fetchImpl) {
|
|
|
114
166
|
|
|
115
167
|
export async function* streamOnce({ blob, options, fetchImpl, headers, config, signal }) {
|
|
116
168
|
const payload = anthropicPayload(options, config.systemPrefix || IDENTITY)
|
|
169
|
+
applyClaudeThinking(payload, options)
|
|
117
170
|
const res = await fetchImpl(API, {
|
|
118
171
|
method: 'POST',
|
|
119
172
|
headers: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-subscriptions",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
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,10 +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
|
-
"typecheck": "tsc -p tsconfig.checkjs.json"
|
|
40
|
-
},
|
|
41
37
|
"dsh": {
|
|
42
38
|
"bundle": {
|
|
43
39
|
"patch": "./cordis.patch.yml"
|
|
@@ -66,5 +62,9 @@
|
|
|
66
62
|
"@deepseek-ai/dsh-llm": "0.1.0-rc.8",
|
|
67
63
|
"@types/node": "^22.20.2",
|
|
68
64
|
"typescript": "^7.0.2"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"test": "eslint lib/ && node --test test/*.test.mjs",
|
|
68
|
+
"typecheck": "tsc -p tsconfig.checkjs.json"
|
|
69
69
|
}
|
|
70
|
-
}
|
|
70
|
+
}
|
package/lib/ratelimit-parser.js
DELETED
|
File without changes
|
package/lib/ui-styles.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
export const CSS =
|
|
2
|
-
'.dsub-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;transition:border-color .16s,background .16s}' +
|
|
3
|
-
'.dsub-card:hover{border-color:var(--dsw-alias-label-dimmed)}' +
|
|
4
|
-
'.dsub-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}' +
|
|
5
|
-
'.dsub-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:18px 20px;display:flex}' +
|
|
6
|
-
'.dsub-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}' +
|
|
7
|
-
'.dsub-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}' +
|
|
8
|
-
'.dsub-name{color:var(--dsw-alias-label-primary);font-size:16px;font-weight:600;line-height:1.4}' +
|
|
9
|
-
'.dsub-description{color:var(--dsw-alias-label-secondary);font-size:13px}' +
|
|
10
|
-
'.dsub-chev{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}' +
|
|
11
|
-
'.dsub-chevOpen{transform:rotate(180deg)}' +
|
|
12
|
-
'.dsub-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 20px;padding-bottom:12px}' +
|
|
13
|
-
'.dsub-block{margin-top:10px}' +
|
|
14
|
-
'.dsub-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:8px}' +
|
|
15
|
-
'.dsub-grow{flex:1 1 180px;min-width:140px}' +
|
|
16
|
-
'.dsub-helpBox{margin-top:8px;padding:12px 14px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);font-size:12px;line-height:1.6}' +
|
|
17
|
-
'.dsub-helpHead{display:flex;align-items:center;justify-content:space-between;font-weight:600;margin-bottom:8px;color:var(--dsw-alias-brand-primary)}' +
|
|
18
|
-
'.dsub-helpStep{margin-bottom:6px;color:var(--dsw-alias-label-primary)}' +
|
|
19
|
-
'.dsub-btnActive{background:var(--dsw-alias-brand-primary);color:#fff;border-color:var(--dsw-alias-brand-primary)}' +
|
|
20
|
-
'.dsub-h{font-size:13.5px;font-weight:600;color:var(--dsw-alias-label-primary)}' +
|
|
21
|
-
'.dsub-sub{font-size:12px;color:var(--dsw-alias-label-secondary)}' +
|
|
22
|
-
'.dsub-dim{font-size:11.5px;color:var(--dsw-alias-label-tertiary)}' +
|
|
23
|
-
'.dsub-mini{appearance:none;font:inherit;font-size:12px;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:3px 8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);transition:all .15s}' +
|
|
24
|
-
'.dsub-mini:hover{background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-label-dimmed)}' +
|
|
25
|
-
'.dsub-ok{font-size:12px;color:var(--dsw-alias-state-success-primary)}' +
|
|
26
|
-
'.dsub-bad{font-size:12px;color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;word-break:break-word;max-width:100%}' +
|
|
27
|
-
'.dsub-hint{overflow-wrap:anywhere;word-break:break-word;max-width:100%}' +
|
|
28
|
-
'.dsub-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 14px;font-size:13px;font-weight:500;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);transition:all .15s}' +
|
|
29
|
-
'.dsub-btn:hover{background:var(--dsw-alias-bg-layer-1);border-color:var(--dsw-alias-label-dimmed)}' +
|
|
30
|
-
'.dsub-inp{height:36px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;min-width:180px}' +
|
|
31
|
-
'.dsub-inp:focus{outline:none;border-color:var(--dsw-alias-brand-primary)}' +
|
|
32
|
-
'.dsub-box{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:12px 14px;margin-top:8px;background:var(--dsw-alias-bg-layer-2)}' +
|
|
33
|
-
'.dsub-cooldown{display:inline-block;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-state-warning-primary);color:#000;font-size:11px;font-weight:600}' +
|
|
34
|
-
'.dsub-bar{position:relative;height:10px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);overflow:hidden;flex:1;min-width:80px}' +
|
|
35
|
-
'.dsub-barFill{height:100%;border-radius:3px;background:var(--dsw-alias-state-success-primary);transition:width .2s}' +
|
|
36
|
-
'.dsub-barWarn .dsub-barFill{background:var(--dsw-alias-state-warning-primary)}' +
|
|
37
|
-
'.dsub-barFull .dsub-barFill{background:var(--dsw-alias-state-error-primary)}' +
|
|
38
|
-
'.dsub-barRow{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--dsw-alias-label-secondary)}' +
|
|
39
|
-
'.dsub-manual{display:flex;flex-direction:column;gap:6px;margin-top:4px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l2)}' +
|
|
40
|
-
'.dsub-verify{font-size:12px;color:var(--dsw-alias-state-warning-primary)}' +
|
|
41
|
-
'.dsub-verify a{color:var(--dsw-alias-brand-primary)}' +
|
|
42
|
-
'.dsub-pill{display:inline-flex;align-items:center;gap:7px;height:28px;padding:0 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:12px;font-weight:600;cursor:pointer;box-shadow:0 1px 3px rgba(0,0,0,.15),inset 0 1px 0 rgba(255,255,255,.06);transition:all .16s ease;user-select:none}' +
|
|
43
|
-
'.dsub-pill:hover{background:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-tertiary);transform:translateY(-1px);box-shadow:0 3px 8px rgba(0,0,0,.25),inset 0 1px 0 rgba(255,255,255,.1)}' +
|
|
44
|
-
'.dsub-pill:active{transform:translateY(0)}' +
|
|
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
|
-
'.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 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
|
-
'.dsub-ledOff{background:var(--dsw-alias-label-tertiary,#6b7280)}' +
|
|
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
|
-
'.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)}' +
|
|
53
|
-
'@keyframes dsubFadeIn{from{opacity:0}to{opacity:1}}' +
|
|
54
|
-
'@keyframes dsubScaleIn{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}' +
|
|
55
|
-
'.dsub-modalHead{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-bottom:14px;border-bottom:1px solid var(--dsw-alias-border-l2)}' +
|
|
56
|
-
'.dsub-modalTitleWrap{display:flex;align-items:center;gap:10px}' +
|
|
57
|
-
'.dsub-modalIcon{width:30px;height:30px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);display:grid;place-items:center;color:var(--dsw-alias-brand-primary);font-size:14px}' +
|
|
58
|
-
'.dsub-modalBadge{font-size:11px;font-weight:600;padding:2px 7px;border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary)}' +
|
|
59
|
-
'.dsub-heroCard{margin-top:14px;padding:14px 16px;border-radius:12px;background:linear-gradient(135deg,var(--dsw-alias-bg-layer-2) 0%,var(--dsw-alias-bg-layer-3) 100%);border:1px solid var(--dsw-alias-border-l1);box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}' +
|
|
60
|
-
'.dsub-heroHead{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}' +
|
|
61
|
-
'.dsub-heroTitle{font-size:14px;font-weight:700;display:flex;align-items:center;gap:6px}' +
|
|
62
|
-
'.dsub-heroModel{font-size:11.5px;color:var(--dsw-alias-label-secondary);font-family:ui-monospace,monospace}' +
|
|
63
|
-
'.dsub-heroBars{display:flex;flex-direction:column;gap:8px;margin-top:10px}' +
|
|
64
|
-
'.dsub-barLabelRow{display:flex;justify-content:space-between;font-size:11.5px;color:var(--dsw-alias-label-secondary);margin-bottom:3px}' +
|
|
65
|
-
'.dsub-barTrack{height:10px;border-radius:999px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}' +
|
|
66
|
-
'.dsub-barFillGrad{height:100%;border-radius:3px;transition:width .3s ease}' +
|
|
67
|
-
'.dsub-panel-box{margin-top:10px;padding:14px 16px;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: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
|
-
'.dsub-panel-title{font-size:16px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between}' +
|
|
70
|
-
'.dsub-panel-desc{font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.4}' +
|
|
71
|
-
'.dsub-usage-grid{display:flex;flex-direction:column;gap:8px;margin-top:4px}' +
|
|
72
|
-
'.dsub-usage-card{display:flex;flex-direction:column;gap:5px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1)}' +
|
|
73
|
-
'.dsub-usage-head{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary)}' +
|
|
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
|
-
'.dsub-usage-fill{height:100%;border-radius:999px;transition:width .3s ease}' +
|
|
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:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 8%,transparent);padding:3px 10px;border-radius:999px;font-size:12px;font-weight:600;border-style:solid;border-width:1px}' +
|
|
78
|
-
'.dsub-tag-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:color-mix(in srgb,var(--dsw-alias-state-warning-primary) 8%,transparent);padding:3px 10px;border-radius:999px;font-size:12px;font-weight:600;border-style:solid;border-width:1px}' +
|
|
79
|
-
'.dsub-tag-bad{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 8%,transparent);padding:3px 10px;border-radius:999px;font-size:12px;font-weight:600;border-style:solid;border-width:1px}' +
|
|
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
|
-
'.dsub-ref-tag{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;color:var(--dsw-alias-label-secondary)}' +
|
|
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: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
|
-
'.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
|
-
'.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
|
-
'.dsub-accountCard:hover{background:var(--dsw-alias-bg-layer-3);border-color:var(--dsw-alias-label-tertiary)}' +
|
|
88
|
-
'.dsub-brandBadge{width:32px;height:32px;border-radius:8px;display:grid;place-items:center;font-size:12px;font-weight:700;flex-shrink:0}' +
|
|
89
|
-
'.dsub-brandCodex{background:rgba(16,185,129,.15);color:#10b981;border:1px solid rgba(16,185,129,.3)}' +
|
|
90
|
-
'.dsub-brandClaude{background:rgba(245,158,11,.15);color:#f59e0b;border:1px solid rgba(245,158,11,.3)}' +
|
|
91
|
-
'.dsub-brandGrok{background:rgba(168,85,247,.15);color:#c084fc;border:1px solid rgba(168,85,247,.3)}' +
|
|
92
|
-
'.dsub-brandAgy{background:rgba(59,130,246,.15);color:#60a5fa;border:1px solid rgba(59,130,246,.3)}' +
|
|
93
|
-
'.dsub-brandOllama{background:rgba(107,114,128,.15);color:#9ca3af;border:1px solid rgba(107,114,128,.3)}' +
|
|
94
|
-
'.dsub-brandKimi{background:rgba(236,72,153,.15);color:#f472b6;border:1px solid rgba(236,72,153,.3)}' +
|
|
95
|
-
'.dsub-brandGlm{background:rgba(20,184,166,.15);color:#2dd4bf;border:1px solid rgba(20,184,166,.3)}' +
|
|
96
|
-
'.dsub-planBadge{display:inline-flex;align-items:center;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;background:rgba(99,102,241,.15);color:#818cf8;border:1px solid rgba(99,102,241,.3);margin-left:6px}' +
|
|
97
|
-
|
|
98
|
-
'.dsub-accInfo{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}' +
|
|
99
|
-
'.dsub-accNameRow{display:flex;align-items:center;justify-content:space-between;gap:8px}' +
|
|
100
|
-
'.dsub-accName{font-size:13px;font-weight:600}' +
|
|
101
|
-
'.dsub-accStatusTag{font-size:10.5px;font-weight:600;padding:1px 6px;border-radius:4px}' +
|
|
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
|
-
'.dsub-statusOff{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-tertiary)}' +
|
|
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
|
-
'.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}' +
|
|
108
|
-
'.dsub-btnPrimary:hover{opacity:.9}' +
|
|
109
|
-
'.dsub-btnSec{display:inline-flex;align-items:center;gap:5px;padding:5px 10px;border-radius:7px;font-size:11.5px;font-weight:500;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;text-decoration:none;transition:all .15s}' +
|
|
110
|
-
'.dsub-btnSec:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-tertiary)}' +
|
|
111
|
-
'.dsub-cq{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-secondary);padding:0 6px}' +
|
|
112
|
-
'.dsub-cqBar{position:relative;width:40px;height:5px;border-radius:3px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}' +
|
|
113
|
-
'.dsub-cqBarFill{height:100%;border-radius:3px;background:var(--dsw-alias-state-success-primary)}' +
|
|
114
|
-
'.dsub-cqBarWarn .dsub-cqBarFill{background:var(--dsw-alias-state-warning-primary)}' +
|
|
115
|
-
'.dsub-cqBarFull .dsub-cqBarFill{background:var(--dsw-alias-state-error-primary)}' +
|
|
116
|
-
'.dsub-cqB{font-variant-numeric:tabular-nums;font-weight:600;color:var(--dsw-alias-label-primary)}' +
|
|
117
|
-
'.dsub-diag{font:11px/1.5 ui-monospace,SFMono-Regular,monospace;max-height:260px;overflow:auto;white-space:pre-wrap;word-break:break-word;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:10px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}'
|
|
118
|
-
|
|
119
|
-
const cssId = 'dsh-subscriptions/settings.module.css'
|