@goodandready/dsh-clinebot 0.6.14 → 0.6.15

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to `@goodandready/dsh-clinebot` will be documented in this f
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.15] - 2026-10-21
9
+
10
+ ### Added
11
+ - **Multimodal budgeting** (#257): the adapter now runs `optimizePromptCacheAndBudget`, honors `offloadSeenImages` and `maxPayloadMb`, and records `offloadedImages` in session telemetry.
12
+ - **Model controls** (#258): per-model **Ping** calls `POST /models/probe` and shows TTFT; **Apply visibility** calls `POST /models/visible`.
13
+ - **Price auto-sync switch** (#259): `priceAutoSync` is available in settings and the price card, and is documented in all three READMEs.
14
+
15
+ ### Fixed
16
+ - **Write-route source gate** (#261): POST/PUT without `Origin`/`Referer` are rejected fail-closed, closing the dsh-lanmode loopback-bridge write bypass.
17
+ - Removed test-only exports (`getDefaultModelIds`, `resetPriceSyncState`, `clearAccountCooldown`, `releaseSession`) and wired `configReader` into the host path.
18
+ - Added `.gitattributes` (`* text=auto eol=lf`) and normalized tracked files.
19
+
8
20
  ## [0.6.14] - 2026-10-21
9
21
 
10
22
  ### Fixed
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <h3>Native ClineBot / ClinePass Provider Companion for DeepSeek Harness</h3>
6
6
 
7
- <p><b>0.6.14</b> hardens the settings source gate, restores token-only theming and cleans up the audit findings. Chat runs on the native <code>ctx.llm</code> adapter and requires DSH <code>0.2.1-rc.2</code>; the legacy <code>llm-pi-ai</code>, loopback proxy and <code>llm/stream</code> path are removed.</p>
7
+ <p><b>0.6.15</b> connects multimodal budgeting, exposes model probe/visibility controls, adds the price auto-sync switch and closes the remaining audit hygiene/security findings. Chat runs on the native <code>ctx.llm</code> adapter and requires DSH <code>0.2.1-rc.2</code>; the legacy <code>llm-pi-ai</code>, loopback proxy and <code>llm/stream</code> path are removed.</p>
8
8
 
9
9
  <p align="center">
10
10
  <a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
@@ -122,7 +122,11 @@ graph LR
122
122
  * **`lib/models.js`**:
123
123
  Dynamic subscription catalogue, curated fallback properties, reasoning efforts, custom models and plan parsing (`parsePlanIncludedModels`, `getAllModels`, `getDynamicModels`).
124
124
  * **`lib/model-prices.js`**:
125
- Explicit local cost estimates with per-model overrides. Unknown models stay `unpriced` instead of being reported as $0.
125
+ Explicit local cost estimates with per-model overrides. Unknown models stay `unpriced` instead of being reported as $0. When a newly discovered model has no price, the optional `priceAutoSync` setting fetches the public models.dev catalog (at most once per missing set every 6 hours); turn it off in the price card or set `priceAutoSync: false` while keeping the manual Sync button.
126
+ * **`lib/multimodal-budget.js`**:
127
+ Offloads images from earlier turns into stable text placeholders and enforces `maxPayloadMb`, reducing prompt-cache churn and oversized ClinePass requests. `offloadedImages` is included in session telemetry.
128
+ * **Model tools in Settings → ClineBot → Models**:
129
+ The per-model **Ping** button calls `POST /models/probe` and shows TTFT; **Apply visibility** calls `POST /models/visible` after choosing the visible models and plan-only filter.
126
130
  * **`lib/stats-storage.js`**:
127
131
  Persistent JSON telemetry v2: cache buckets, estimated cost, TTFT/latency, sessions and day/month/model/account aggregates. v1 files upgrade on read.
128
132
  * **`lib/account-pool.js` / `lib/session-router.js`**:
@@ -199,6 +203,7 @@ dsh-clinebot:
199
203
  statsPath: ~/.dsh/clinebot-stats.json
200
204
  priceCatalogUrl: https://models.dev/api.json
201
205
  priceCatalogPath: ~/.dsh/clinebot-prices.json
206
+ priceAutoSync: true
202
207
  eventLogPath: ~/.dsh/clinebot-events.json
203
208
  statsRetentionDays: 95
204
209
  dailyBudgetUsd: 0
@@ -226,6 +231,7 @@ dsh-clinebot:
226
231
  | `statsPath` | `string` | `"~/.dsh/clinebot-stats.json"` | Persistent on-disk path for token usage analytics |
227
232
  | `priceCatalogUrl` | `string` | `"https://models.dev/api.json"` | Public price catalog used by the optional sync |
228
233
  | `priceCatalogPath` | `string` | `"~/.dsh/clinebot-prices.json"` | Local cache for the public price catalog |
234
+ | `priceAutoSync` | `boolean` | `true` | Fetch models.dev for newly discovered models without a price; the manual Sync button remains available when off |
229
235
  | `eventLogPath` | `string` | `"~/.dsh/clinebot-events.json"` | Ring buffer for account cooldown/recovery/routing history |
230
236
  | `statsRetentionDays` | `number` | `95` | Retention for day/hour/session telemetry; 0 keeps everything |
231
237
  | `dailyBudgetUsd` | `number` | `0` | Optional estimated daily spend guard; 0 disables it |
package/README.ru.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <h3>Нативное подключение провайдера ClineBot / ClinePass для DeepSeek Harness</h3>
6
6
 
7
- <p><b>0.6.14</b> закрывает находки аудита: усилен source gate настроек, тема снова использует только токены DSH, мёртвый код и артефакты убраны. Чат работает через нативный адаптер <code>ctx.llm</code>, требуется DSH <code>0.2.1-rc.2</code>; старые <code>llm-pi-ai</code>, loopback-прокси и <code>llm/stream</code> удалены.</p>
7
+ <p><b>0.6.15</b> подключает бюджетирование мультимодальных запросов, добавляет замер моделей и управление видимостью, переключатель автосинхронизации цен и закрывает оставшиеся находки аудита. Чат работает через нативный адаптер <code>ctx.llm</code>, требуется DSH <code>0.2.1-rc.2</code>; старые <code>llm-pi-ai</code>, loopback-прокси и <code>llm/stream</code> удалены.</p>
8
8
 
9
9
  <p align="center">
10
10
  <a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
@@ -108,6 +108,7 @@ graph LR
108
108
  ## ✨ Структура модулей и возможности
109
109
 
110
110
  * **`lib/models.js`**: динамический каталог моделей подписки и встроенных шаблонов с поддержкой уровней рассуждения (`Off` / `Low` / `Medium` / `High` / `Max`), контекстных лимитов, пользовательских моделей (`customModels`) и парсинга тарифа (`parsePlanIncludedModels`, `getAllModels`, `getDynamicModels`).
111
+ * **Цены и модели**: кнопка **Ping** у модели вызывает `POST /models/probe` и показывает TTFT; кнопка **Применить видимость** сохраняет выбор видимых моделей и фильтр тарифа через `POST /models/visible`. Публичные цены могут автоматически подгружаться из models.dev для новых моделей; это отключается переключателем `priceAutoSync` (или `priceAutoSync: false`), ручная синхронизация остаётся доступной.
111
112
  * **`lib/cline-client.js`**:
112
113
  * `fetchUsageLimits`: параллельный опрос `GET /users/me/plan/usage-limits`, `GET /users/me/plan` и `GET /users/me` с кэшированием в памяти.
113
114
  * `sessionStats` / `recordSessionRequest`: счетчики сессии (запросы, токены, задержка, время).
package/README.zh.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <h3>适用于 DeepSeek Harness 的 ClineBot / ClinePass 原生模型提供商伴侣插件</h3>
6
6
 
7
- <p><b>0.6.14</b> 修复审计发现:加固设置 source gate,恢复仅使用 DSH 主题令牌的样式,并清理死代码和残留文件。聊天使用原生 <code>ctx.llm</code> 适配器,需要 DSH <code>0.2.1-rc.2</code>;旧的 <code>llm-pi-ai</code>、回环代理和 <code>llm/stream</code> 已删除。</p>
7
+ <p><b>0.6.15</b> 接入多模态预算、模型延迟/可见性控制、价格自动同步开关,并修复其余审计问题。聊天使用原生 <code>ctx.llm</code> 适配器,需要 DSH <code>0.2.1-rc.2</code>;旧的 <code>llm-pi-ai</code>、回环代理和 <code>llm/stream</code> 已删除。</p>
8
8
 
9
9
  <p align="center">
10
10
  <a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
@@ -108,6 +108,7 @@ graph LR
108
108
  ## ✨ 核心模块与功能
109
109
 
110
110
  * **`lib/models.js`**:管理动态订阅模型目录与内置精选模板(提供完整的 `Off` / `Low` / `Medium` / `High` / `Max` 思考强度映射、上游 200k 上下文容量标注、自定义模型支持)以及套餐模型动态解析器(`parsePlanIncludedModels`, `getAllModels`, `getDynamicModels`)。
111
+ * **价格与模型**:模型行中的 **Ping** 调用 `POST /models/probe` 并显示 TTFT;**应用可见模型** 通过 `POST /models/visible` 保存可见模型和套餐过滤。新模型缺少价格时可通过 `priceAutoSync` 开关自动从 models.dev 获取公开价格;关闭后仍可使用手动同步。
111
112
  * **`lib/cline-client.js`**:
112
113
  * `fetchUsageLimits`:高效并发轮询 `GET /users/me/plan/usage-limits`、`GET /users/me/plan` 与 `GET /users/me` 并进行内存缓存。
113
114
  * `sessionStats` / `recordSessionRequest`:内存级会话度量记录器(请求次数、Token 估算、延迟、时间戳)。
package/lib/access.js CHANGED
@@ -24,5 +24,17 @@ export function assertTrustedSettingsRequest(req, res, ctx = null) {
24
24
  writeJson(res, 403, { ok: false, error: 'Forbidden' })
25
25
  return false
26
26
  }
27
+
28
+ // A loopback-only caller is not enough for state changes: dsh-lanmode can
29
+ // proxy LAN/Tailscale clients into the host over loopback. Browsers attach
30
+ // Origin/Referer; non-browser writes without one are rejected fail-closed.
31
+ const method = String(req?.method || 'GET').toUpperCase()
32
+ if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {
33
+ const origin = req?.headers?.origin || req?.headers?.referer
34
+ if (typeof origin !== 'string' || origin.length === 0) {
35
+ writeJson(res, 403, { ok: false, error: 'Forbidden' })
36
+ return false
37
+ }
38
+ }
27
39
  return true
28
40
  }
@@ -17,6 +17,7 @@ export function recordAttemptTelemetry({
17
17
  latencyMs,
18
18
  aborted = false,
19
19
  options,
20
+ offloadedImages = 0,
20
21
  }) {
21
22
  const promptTokens = Number(usage?.promptTokens || 0)
22
23
  const completionTokens = Number(usage?.completionTokens || 0)
@@ -41,6 +42,7 @@ export function recordAttemptTelemetry({
41
42
  completionTokens,
42
43
  reasoningTokens,
43
44
  model,
45
+ offloadedImages,
44
46
  ...(isError ? { error: is429 ? 'rate limit' : 'stream error' } : {}),
45
47
  })
46
48
  } catch {
package/lib/adapter.js CHANGED
@@ -15,6 +15,7 @@ import { recordAttemptTelemetry } from './adapter-telemetry.js'
15
15
  import { getBudgetSpend } from './stats-storage.js'
16
16
  import { budgetStatus, budgetErrorMessage } from './budget.js'
17
17
  import { buildOpenAiPayload } from './adapter-messages.js'
18
+ import { optimizePromptCacheAndBudget } from './multimodal-budget.js'
18
19
  import { streamJsonCompletion, streamOpenAiChunks } from './adapter-stream.js'
19
20
  import {
20
21
  abortError,
@@ -263,8 +264,14 @@ export class ClineBotAdapter extends LlmAdapter {
263
264
  const attachments = this.resolveAttachments
264
265
  ? this.resolveAttachments()
265
266
  : attachmentStore(this.ctx)
267
+ const optimized = optimizePromptCacheAndBudget(options.messages, {
268
+ offloadSeenImages: pub.offloadSeenImages,
269
+ maxPayloadBytes: Number(pub.maxPayloadMb || 40) * 1024 * 1024,
270
+ })
271
+ const requestOptions = { ...options, offloadedImages: optimized.offloadedCount }
266
272
  const payload = await buildOpenAiPayload({
267
273
  ...options,
274
+ messages: optimized.messages,
268
275
  model,
269
276
  signal,
270
277
  }, { attachments })
@@ -444,7 +451,7 @@ export class ClineBotAdapter extends LlmAdapter {
444
451
  isError: false,
445
452
  ttftMs,
446
453
  latencyMs: Date.now() - startedAt,
447
- options,
454
+ options: requestOptions,
448
455
  })
449
456
  return
450
457
  }
@@ -473,7 +480,7 @@ export class ClineBotAdapter extends LlmAdapter {
473
480
  isError: false,
474
481
  ttftMs,
475
482
  latencyMs: Date.now() - startedAt,
476
- options,
483
+ options: requestOptions,
477
484
  })
478
485
  return
479
486
  } catch (error) {
@@ -486,7 +493,7 @@ export class ClineBotAdapter extends LlmAdapter {
486
493
  isError: true,
487
494
  ttftMs,
488
495
  latencyMs: Date.now() - startedAt,
489
- options,
496
+ options: requestOptions,
490
497
  aborted: true,
491
498
  })
492
499
  throw abortError()
@@ -512,7 +519,7 @@ export class ClineBotAdapter extends LlmAdapter {
512
519
  is429: normalized.code === 'RATE_LIMIT',
513
520
  ttftMs,
514
521
  latencyMs: Date.now() - startedAt,
515
- options,
522
+ options: requestOptions,
516
523
  })
517
524
  throw normalized
518
525
  }
@@ -549,7 +556,10 @@ export class ClineBotAdapter extends LlmAdapter {
549
556
  * }} entry
550
557
  */
551
558
  recordAttempt(entry) {
552
- recordAttemptTelemetry(entry)
559
+ recordAttemptTelemetry({
560
+ ...entry,
561
+ offloadedImages: entry?.options?.offloadedImages || 0,
562
+ })
553
563
  }
554
564
  }
555
565
 
package/lib/client.js CHANGED
@@ -12,10 +12,10 @@ window.__ModuleLoader__.load({
12
12
  const NS="dsh-clinebot";const ROUTE_PREFIX="/dsh-clinebot";const SNAPSHOT_READY=Object.freeze({status:"ready",view:null});function readConfigForms(ctx){if(!ctx||typeof ctx.get!=="function")return void 0;try{return ctx.get("configForms")||void 0}catch{return void 0}}function useConfigFormsSnapshot(ctx){const scope=React.useMemo(()=>{try{return readConfigForms(ctx)?.get?.(NS)||void 0}catch{return void 0}},[ctx]);const subscribe=React.useMemo(()=>cb=>{try{return scope?.subscribe?scope.subscribe(cb)||(()=>{}):()=>{}}catch{return()=>{}}},[scope]);const getSnapshot=React.useCallback(()=>{try{return scope?.getSnapshot?.()||SNAPSHOT_READY}catch{return SNAPSHOT_READY}},[scope]);return React.useSyncExternalStore(subscribe,getSnapshot,()=>SNAPSHOT_READY)||SNAPSHOT_READY}
13
13
 
14
14
  // --- src/client/locales.js ---
15
- const en={nav:"ClineBot",title:"ClineBot",subtitle:"ClinePass subscription with dynamic plan models.","settings.loading":"Loading ClineBot settings\u2026","settings.retry":"Retry","settings.unavailable":"ClineBot settings unavailable (host namespace is not ready).","api.timeout":"Request timed out ({seconds}s)","header.title":"ClineBot (ClinePass) Provider","header.sub":"Connect models under your active ClinePass subscription. OpenAI API compatible, automated rolling limit tracking, and instant auto-registration in DSH.","badge.online":"Host online ({latency} ms)","badge.offline":"Host unreachable","badge.key_ok":"Key set ({source})","badge.key_missing":"Key missing","badge.registered":"DSH Registered ({count} models)","badge.not_registered":"Not registered","key.title":"Authorization & API Key","key.desc":"The key is saved directly to DSH Credentials storage (~/.dsh/.credentials.yaml) and never exposed in plaintext configuration files.","key.placeholder_has":"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022","key.placeholder_empty":"Paste ClinePass key (cline_...)","key.show":"Show","key.hide":"Hide","key.save":"Save Key","key.saving":"Saving\u2026","key.login_fast":"Open API Keys \u2197","key.verifying":"Validating key with Cline\u2026","key.verified":"Valid key for {email} ({plan})","key.invalid":"Key validation failed: {error}","key.env_label":"Credential environment name: ","key.get_key":"Get key in app.cline.bot console \u2197","key.saved_msg":"Key saved to DSH Credentials. Validation: {status}","key.empty_err":"Please enter an API key before saving","accounts.title":"Multi-Account Failover Pool","accounts.desc":"Add extra accounts. When an account hits 429 rate limits, ClineBot switches to the next available account automatically.","accounts.active_badge":"Active","accounts.pinned_badge":"Pinned","accounts.pin_btn":"Pin Active","accounts.add_btn":"+ Add Account","accounts.label_placeholder":"Account Label (e.g. Work)","accounts.env_placeholder":"CLINEBOT_API_KEY_2","accounts.delete_btn":"Delete","accounts.delete_confirm":'Are you sure you want to delete account "{label}"?',"accounts.save_btn":"Save Account","accounts.cancel_btn":"Cancel","accounts.quota_used":"{pct}% used","accounts.last_failover":"Last failover: {from} \u2192 {to} ({reason})","accounts.mode_label":"Pool Strategy","accounts.mode_least_used":"Least Used (Quota)","accounts.mode_round_robin":"Round Robin (Sequential)","accounts.history_show":"History","accounts.history_hide":"Hide history","accounts.history_title":"Account event history","accounts.history_hint":"Recent cooldowns, recoveries and routing decisions. Newest first, last 50.","accounts.history_empty":"No account events yet.","accounts.history_loading":"Loading history\u2026","accounts.history_until":"until {time}","accounts.event_cooldown":"Cooldown","accounts.event_recover":"Recovered","accounts.event_route":"Routed","accounts.routes_title":"Per-Model Account Routing","accounts.routes_desc":"Optionally route specific models to designated accounts in the pool.","accounts.route_model_placeholder":"Model ID (e.g. cline-pass/deepseek-v4-pro)","accounts.route_account_placeholder":"Target Account","accounts.route_btn_add":"Add Route","accounts.actions":"Account actions","accounts.configured":"Configured","accounts.missing_key":"Missing key","accounts.source":"Source: {source}","accounts.quota_5h":"5h {pct}%","accounts.rename":"Rename","accounts.rename_save":"Save name","accounts.rename_placeholder":"Account label","accounts.clear_key":"Clear stored key","accounts.clear_confirm":"Clear the stored key for {name}? The account row stays.","accounts.remove_confirm":"Remove {name} from the pool?","accounts.confirm":"Confirm","accounts.cancel":"Cancel","accounts.unpin":"Unpin","accounts.no_accounts":"No accounts configured yet.","accounts.route_remove":"Remove route","quota.title":"ClinePass Quota & Rate Limits","quota.desc":"Official rolling window request limits from ClinePass","quota.account":"Account: {email} \xB7 Plan: {plan}","quota.refresh":"Refresh Quotas","quota.refreshing":"Refreshing\u2026","quota.refreshed_msg":"Subscription quotas refreshed","quota.window_5h":"\u23F1 5-Hour Rolling Limit","quota.window_weekly":"Weekly Window","quota.used":"{pct}% used","quota.reset_at":"Reset: {time}","quota.window_monthly":"Monthly Window","quota.countdown":"Resets in {time}","quota.resetting":"Resetting\u2026","quota.warning_low":"Quota is below 10%. Requests may be throttled soon.","quota.forecast_no_usage":"No usage yet","quota.forecast_holds":"Holds until reset at the current pace","quota.forecast_left":"~{time} left at the current pace","quota.forecast_exhausted":"Exhausted","quota.exhausted_5h":"5-hour rolling limit is almost exhausted ({percentUsed}%). New requests may be rejected until quota reset.","quota.warning_5h":"Low quota warning: {percentUsed}% of 5-hour limit consumed (<10% remaining).","quota.warning_weekly":"Low weekly quota warning: {percentUsed}% consumed ({remainingPercent}% remaining).","quota.warning_monthly":"Low monthly quota warning: {percentUsed}% consumed ({remainingPercent}% remaining).","models.reasoning_tag":"Reasoning","models.effort_low":"Low","models.effort_medium":"Medium","models.effort_high":"High","models.effort_max":"Max","models.caps_vision":"Vision","models.caps_general":"General","models.caps_coding":"Coding","diag.smoke_result":"Latency: {latency} ms | Model: {model}\nResponse: {preview}","diag.empty":"(empty)","ui.error_title":"ClineBot UI Error: ","ui.retry":"Retry","update.check_failed":"Failed to check for updates","update.retry":"Retry check","models.filter_by_plan":"Hide models not in active plan","models.filter_by_plan_hint":"Automatically filter chat model picker to models included in your active ClinePass subscription plan.","models.visible_models":"Visible Models in Chat","models.visible_models_hint":"Choose which models appear in the DSH chat selector (empty means all supported models).","models.in_plan":"In Plan","models.out_of_plan":"Out of Plan","models.group_plan":"Included in plan","models.group_other":"Other models","models.group_custom":"Custom models","models.plan_only":"Plan Only","models.show_all":"Show All","models.clean_stale":"Clean Stale ({count})","models.models_selected":"{selected} of {total} selected for chat","models.title":"Model Picker in DSH ({enabled} of {total} active)","models.desc":"Catalog synchronizes directly from ClinePass subscription. New plan models are enabled automatically. Uncheck models to hide them from DSH chat picker. Thinking effort is selectable in the composer model menu.","models.reasoning_tooltip":"Supports thinking effort selection in composer model menu (Off / Low / Medium / High / Max)","models.sync":"Check Plan Models","models.syncing":"Checking\u2026","models.synced_msg":"Synchronized subscription models: {total} ({discovered} discovered from ClinePass)","models.verified":"Verified with plan","models.unverified":"Not synced (fallback catalog)","models.synced_at":"Synced: {date}","models.search_placeholder":"Search models by name, ID or description\u2026","models.filter_all":"All","models.filter_vision":"Vision","models.filter_coding":"Coding","models.filter_recommended":"Recommended","models.filter_disabled":"Disabled","models.all":"All","models.vision":"Vision Only","models.coding":"Coding Only","models.recommended":"Recommended","models.th_active":"On","models.th_name":"Model Name","models.th_id":"Model ID","models.th_ctx":"Context","models.th_caps":"Capabilities","models.th_route":"Account Route","models.route_auto":"Auto / Pool","models.btn_probe":"Ping","models.star":"Star","models.auto_new":"New","models.apply_original_all":"Original Specs","models.reset_contexts_all":"\u21BA Reset Contexts","models.context_updated_msg":"Model context limits updated and synchronized to DSH.","models.ctx_default":"Default ({ctx} / {max})","models.ctx_original":"Original ({ctx} / {max} \xB7 {provider})","models.ctx_edit":"Edit Limits","models.ctx_window_label":"Context (e.g. 200000)","models.ctx_max_tokens_label":"Max Out (e.g. 16384)","models.ctx_apply_all":"All","models.ctx_apply_all_title":"Apply these limits across all models","models.ctx_custom_placeholder":"Tokens (e.g. 2000000)","models.ctx_save":"Save","models.ctx_cancel":"Cancel","models.custom_ctx_label":"Context","models.custom_max_label":"Max Out","models.ctx_tag_original":"Original","models.ctx_tag_custom":"Custom","stats.title":"Current DSH Session Telemetry","stats.desc":"Local monitoring of requests, network latency, and estimated token usage via ClinePass.","stats.requests":"Successful / Total Requests","stats.tokens":"Estimated Session Tokens","stats.reasoning_tokens":"Reasoning Tokens (Thinking)","stats.export_csv":"Export CSV","stats.export_json":"Export JSON","stats.latency":"Last Request Latency","stats.last_req":"Last Request Time","models.custom_add_title":"Custom Models","models.custom_id":"Model ID (e.g. anthropic/claude-3-7-sonnet)","models.custom_name":"Display Name (optional)","models.custom_btn_add":"Add Model","models.custom_btn_del":"Delete","models.effort_auto":"Auto Effort","stats.errors429":"429 Rate Limits (Failover)","stats.top_models":"Top Models by Token Usage","stats.top_accounts":"Top Accounts by Token Usage","stats.cache_saved":"Cache savings: \u2248 ${amount}","stats.reset_btn":"Reset Stats","stats.confirm_reset":"Confirm Reset?","stats.cancel_reset":"Cancel","diag.title":"Diagnostics & Auto-Registration","diag.desc":"ClineBot registers into DSH Models automatically when a key is configured. You can run a connection smoke test here.","diag.smoke_btn":"Run Smoke Test (Ping)","diag.smoke_testing":"Testing\u2026","update.available":"Update available: v{latestVersion} (current: v{currentVersion})","update.btn":"Update Now","update.updating":"Updating\u2026","update.done":"Successfully updated to v{version}! Please restart DSH.","update.failed":"Update failed: {error}","update.checking":"Checking for updates\u2026","update.up_to_date":"Up to date","sidebar.quota_title":"Cline Quota","sidebar.show_quota":"Show quota meter in sidebar","sidebar.show_quota_hint":"Display compact quota meter in DSH sidebar footer.","dashboard.title":"Cline Quota & Status","dashboard.subtitle":"Real-time subscription quota, rolling limits, and account pool status.","dashboard.close":"Close dashboard","dashboard.refresh":"Refresh","dashboard.refreshing":"Refreshing\u2026","dashboard.rolling_window":"5-Hour Window","dashboard.weekly_window":"Weekly Window","dashboard.remaining":"remaining","dashboard.used":"used","dashboard.account_pool":"Active Routing Account","dashboard.credits":"Credit Balance","dashboard.updated_at":"Updated at","composer.cost_title":"ClineBot Session Cost","composer.last_request":"Last request","composer.free":"Free","composer.cost_breakdown":"Token & Cost Breakdown","composer.prompt_tok":"Prompt Tokens","composer.completion_tok":"Completion Tokens","composer.reasoning_tok":"Reasoning Tokens","composer.est_cost":"Estimated Cost","composer.unpriced":"unpriced","composer.cache_read":"Cache Read","composer.cache_write":"Cache Write","composer.price_source":"Price","composer.price_estimate":"estimate","composer.price_override":"override","composer.price_public":"public catalog","discovery.title":"Discovered Local Keys","discovery.import_btn":"Import from {source}","discovery.imported":"Imported to accounts pool","discovery.scanning":"Scanning for local Cline keys\u2026","settings.unsaved":"{count} unsaved change(s)","settings.saving":"Saving\u2026","settings.saved":"Settings saved","settings.save_failed":"Could not save settings","settings.save":"Save","settings.discard":"Discard","settings.save_invalid_base_url":"Base URL must be https:// or a localhost address.","settings.save_invalid_number":"Timeouts and payload limits must be positive numbers.","settings.models_group":"Models & context","settings.models_group_hint":"Plan filter, visible models, context windows, output limits and reasoning defaults.","settings.integrations":"Integrations & display","settings.integrations_desc":"Surfaces outside the chat request that reuse this provider.","settings.web_search":"Serve web search with ClineBot","settings.web_search_hint":"Use the ClinePass endpoint for the DSH web_search tool.","settings.sidebar_quota":"Show quota meter in the sidebar","settings.sidebar_quota_hint":"Display the compact plan and rolling-window meter above Settings.","settings.composer_cost":"Show session cost in the composer","settings.composer_cost_hint":"Display the estimated token cost beside the composer.","settings.advanced":"Advanced","settings.advanced_desc":"Connection, timeouts and storage paths.","settings.base_url":"API base URL","settings.base_url_hint":"ClinePass API root. Remote endpoints must use https://.","settings.timeout":"Connect timeout (ms)","settings.timeout_hint":"Maximum wait for the upstream response headers.","settings.smoke_timeout":"Smoke-test timeout (ms)","settings.smoke_timeout_hint":"Maximum wait for the diagnostic chat completion.","settings.stream_idle":"Stream idle timeout (ms)","settings.stream_idle_hint":"Maximum gap between SSE chunks before the stream is treated as dead.","settings.max_payload":"Max multimodal payload (MB)","settings.max_payload_hint":"Oldest images are evicted once the request exceeds this budget.","settings.stats_retention":"Stats retention (days)","settings.stats_retention_hint":"Trim day/hour/session telemetry older than this. 0 keeps everything.","settings.daily_budget":"Daily budget (USD)","settings.daily_budget_hint":"Estimated spend guard for the current UTC day. 0 disables it.","settings.monthly_budget":"Monthly budget (USD)","settings.monthly_budget_hint":"Estimated spend guard for the current UTC month. 0 disables it.","settings.stats_path":"Stats file path","settings.stats_path_hint":"Persistent JSON telemetry path (supports ~/).","settings.models_cache_path":"Models cache path","settings.models_cache_path_hint":"Offline model catalogue cache (supports ~/).","providerCard.title":"ClineBot (ClinePass)","providerCard.hint":"Paste a ClinePass API key or manage the full account pool in Settings \u2192 ClineBot.","providerCard.keyPresent":"Key configured","providerCard.keyMissing":"Key missing","providerCard.routeActive":"Route active","providerCard.placeholder":"Paste API key","providerCard.replace":"Replace API key","providerCard.save":"Save key","providerCard.saving":"Saving\u2026","providerCard.saved":"Key saved and validated","providerCard.failed":"Could not save the key","providerCard.show":"Show","providerCard.hide":"Hide","providerCard.openSettings":"Open Settings \u2192 ClineBot for accounts, models and quotas.","dashboard.tab_quota":"Quota","dashboard.tab_analytics":"Analytics","dashboard.auto_account":"Automatic (least used)","analytics.title":"Usage analytics","analytics.requests":"Requests","analytics.tokens":"Tokens","analytics.cost":"Estimated cost","analytics.success":"Success rate","analytics.ttft":"Avg TTFT","analytics.loading":"Loading analytics\u2026","analytics.no_data":"No analytics yet. Send a few messages first.","analytics.unpriced":"{count} request(s) have no price estimate and are excluded from cost totals.","analytics.group_model":"Models","analytics.group_day":"Days","analytics.group_account":"Accounts","analytics.group_session":"Sessions","analytics.cache_saved":"Cache Savings","analytics.heatmap":"Request heatmap","analytics.heatmap_hint":"Requests per hour over the last 7 days.","analytics.heatmap_less":"Less","analytics.heatmap_more":"More","analytics.heatmap_empty":"No hourly data yet. Send a message to start the heatmap.","analytics.input":"Input","analytics.output":"Output","analytics.reasoning":"Reasoning","analytics.cache_read":"Cache read","analytics.cache_write":"Cache write","analytics.avg_ttft":"Avg TTFT","analytics.avg_latency":"Avg latency","analytics.p50_latency":"P50 latency","analytics.p95_latency":"P95 latency","analytics.heatmap_selected":"{day} {hour}:00 \u2014 {count} requests. Click the cell again to clear.","analytics.errors":"Errors","analytics.no_key":"ClinePass key is not configured.","analytics.no_key_hint":"Open Settings \u2192 ClineBot and paste a key, or import a discovered local key.","analytics.health_error":"ClinePass endpoint is unreachable.","analytics.not_registered":"ClineBot provider is not registered with DSH.","analytics.not_registered_hint":"Open Settings \u2192 ClineBot and check Diagnostics.","analytics.stale":"Showing the last successful snapshot.","analytics.range":"Range","analytics.range_24h":"24 hours","analytics.range_7":"7 days","analytics.range_30":"30 days","analytics.range_90":"90 days","analytics.from":"From","analytics.to":"To","analytics.model_filter":"Filter by model","analytics.account_filter":"Filter by account","analytics.all_models":"All models","analytics.all_accounts":"All accounts","analytics.errors_only":"Errors only","analytics.heatmap_all_models":"All models","analytics.heatmap_peak":"Peak","analytics.heatmap_by_hour":"By hour","analytics.heatmap_model":"Heatmap model","quota.no_windows":"No quota windows reported.","prices.title":"Price overrides","prices.desc":"USD per 1M tokens. Blank uses the local estimate.","prices.show":"Show prices","prices.hide":"Hide prices","prices.model":"Model","prices.input":"Input","prices.output":"Output","prices.cache_read":"Cache read","prices.cache_write":"Cache write","prices.auto":"auto","prices.empty":"No models available.","prices.hint":"These rates affect the composer and local analytics only; they are not an invoice.","prices.public_hint":"Sync uses the public models.dev catalog as an estimate; your overrides always win.","prices.sync":"Sync public prices","prices.syncing":"Syncing\u2026","prices.synced":"Public catalog synced: {count} models","prices.sync_failed":"Price sync failed: {error}"};function makeT(dict,fallback){return function t(key,vars){if(key==="quota.countdown"&&vars?.time==="resetting"){return dict&&dict["quota.resetting"]||fallback&&fallback["quota.resetting"]||"\u6B63\u5728\u91CD\u7F6E"}let val=dict&&dict[key]||fallback&&fallback[key]||key;if(vars&&typeof val==="string"){for(const k of Object.keys(vars)){val=val.replace(new RegExp("\\{"+k+"\\}","g"),String(vars[k]))}}return val}}
15
+ const en={nav:"ClineBot",title:"ClineBot",subtitle:"ClinePass subscription with dynamic plan models.","settings.loading":"Loading ClineBot settings\u2026","settings.retry":"Retry","settings.unavailable":"ClineBot settings unavailable (host namespace is not ready).","api.timeout":"Request timed out ({seconds}s)","header.title":"ClineBot (ClinePass) Provider","header.sub":"Connect models under your active ClinePass subscription. OpenAI API compatible, automated rolling limit tracking, and instant auto-registration in DSH.","badge.online":"Host online ({latency} ms)","badge.offline":"Host unreachable","badge.key_ok":"Key set ({source})","badge.key_missing":"Key missing","badge.registered":"DSH Registered ({count} models)","badge.not_registered":"Not registered","key.title":"Authorization & API Key","key.desc":"The key is saved directly to DSH Credentials storage (~/.dsh/.credentials.yaml) and never exposed in plaintext configuration files.","key.placeholder_has":"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022","key.placeholder_empty":"Paste ClinePass key (cline_...)","key.show":"Show","key.hide":"Hide","key.save":"Save Key","key.saving":"Saving\u2026","key.login_fast":"Open API Keys \u2197","key.verifying":"Validating key with Cline\u2026","key.verified":"Valid key for {email} ({plan})","key.invalid":"Key validation failed: {error}","key.env_label":"Credential environment name: ","key.get_key":"Get key in app.cline.bot console \u2197","key.saved_msg":"Key saved to DSH Credentials. Validation: {status}","key.empty_err":"Please enter an API key before saving","accounts.title":"Multi-Account Failover Pool","accounts.desc":"Add extra accounts. When an account hits 429 rate limits, ClineBot switches to the next available account automatically.","accounts.active_badge":"Active","accounts.pinned_badge":"Pinned","accounts.pin_btn":"Pin Active","accounts.add_btn":"+ Add Account","accounts.label_placeholder":"Account Label (e.g. Work)","accounts.env_placeholder":"CLINEBOT_API_KEY_2","accounts.delete_btn":"Delete","accounts.delete_confirm":'Are you sure you want to delete account "{label}"?',"accounts.save_btn":"Save Account","accounts.cancel_btn":"Cancel","accounts.quota_used":"{pct}% used","accounts.last_failover":"Last failover: {from} \u2192 {to} ({reason})","accounts.mode_label":"Pool Strategy","accounts.mode_least_used":"Least Used (Quota)","accounts.mode_round_robin":"Round Robin (Sequential)","accounts.history_show":"History","accounts.history_hide":"Hide history","accounts.history_title":"Account event history","accounts.history_hint":"Recent cooldowns, recoveries and routing decisions. Newest first, last 50.","accounts.history_empty":"No account events yet.","accounts.history_loading":"Loading history\u2026","accounts.history_until":"until {time}","accounts.event_cooldown":"Cooldown","accounts.event_recover":"Recovered","accounts.event_route":"Routed","accounts.routes_title":"Per-Model Account Routing","accounts.routes_desc":"Optionally route specific models to designated accounts in the pool.","accounts.route_model_placeholder":"Model ID (e.g. cline-pass/deepseek-v4-pro)","accounts.route_account_placeholder":"Target Account","accounts.route_btn_add":"Add Route","accounts.actions":"Account actions","accounts.configured":"Configured","accounts.missing_key":"Missing key","accounts.source":"Source: {source}","accounts.quota_5h":"5h {pct}%","accounts.rename":"Rename","accounts.rename_save":"Save name","accounts.rename_placeholder":"Account label","accounts.clear_key":"Clear stored key","accounts.clear_confirm":"Clear the stored key for {name}? The account row stays.","accounts.remove_confirm":"Remove {name} from the pool?","accounts.confirm":"Confirm","accounts.cancel":"Cancel","accounts.unpin":"Unpin","accounts.no_accounts":"No accounts configured yet.","accounts.route_remove":"Remove route","quota.title":"ClinePass Quota & Rate Limits","quota.desc":"Official rolling window request limits from ClinePass","quota.account":"Account: {email} \xB7 Plan: {plan}","quota.refresh":"Refresh Quotas","quota.refreshing":"Refreshing\u2026","quota.refreshed_msg":"Subscription quotas refreshed","quota.window_5h":"\u23F1 5-Hour Rolling Limit","quota.window_weekly":"Weekly Window","quota.used":"{pct}% used","quota.reset_at":"Reset: {time}","quota.window_monthly":"Monthly Window","quota.countdown":"Resets in {time}","quota.resetting":"Resetting\u2026","quota.warning_low":"Quota is below 10%. Requests may be throttled soon.","quota.forecast_no_usage":"No usage yet","quota.forecast_holds":"Holds until reset at the current pace","quota.forecast_left":"~{time} left at the current pace","quota.forecast_exhausted":"Exhausted","quota.exhausted_5h":"5-hour rolling limit is almost exhausted ({percentUsed}%). New requests may be rejected until quota reset.","quota.warning_5h":"Low quota warning: {percentUsed}% of 5-hour limit consumed (<10% remaining).","quota.warning_weekly":"Low weekly quota warning: {percentUsed}% consumed ({remainingPercent}% remaining).","quota.warning_monthly":"Low monthly quota warning: {percentUsed}% consumed ({remainingPercent}% remaining).","models.reasoning_tag":"Reasoning","models.effort_low":"Low","models.effort_medium":"Medium","models.effort_high":"High","models.effort_max":"Max","models.caps_vision":"Vision","models.caps_general":"General","models.caps_coding":"Coding","diag.smoke_result":"Latency: {latency} ms | Model: {model}\nResponse: {preview}","diag.empty":"(empty)","ui.error_title":"ClineBot UI Error: ","ui.retry":"Retry","update.check_failed":"Failed to check for updates","update.retry":"Retry check","models.filter_by_plan":"Hide models not in active plan","models.filter_by_plan_hint":"Automatically filter chat model picker to models included in your active ClinePass subscription plan.","models.visible_models":"Visible Models in Chat","models.visible_models_hint":"Choose which models appear in the DSH chat selector (empty means all supported models).","models.in_plan":"In Plan","models.out_of_plan":"Out of Plan","models.group_plan":"Included in plan","models.group_other":"Other models","models.group_custom":"Custom models","models.plan_only":"Plan Only","models.show_all":"Show All","models.clean_stale":"Clean Stale ({count})","models.models_selected":"{selected} of {total} selected for chat","models.title":"Model Picker in DSH ({enabled} of {total} active)","models.desc":"Catalog synchronizes directly from ClinePass subscription. New plan models are enabled automatically. Uncheck models to hide them from DSH chat picker. Thinking effort is selectable in the composer model menu.","models.reasoning_tooltip":"Supports thinking effort selection in composer model menu (Off / Low / Medium / High / Max)","models.sync":"Check Plan Models","models.syncing":"Checking\u2026","models.synced_msg":"Synchronized subscription models: {total} ({discovered} discovered from ClinePass)","models.verified":"Verified with plan","models.unverified":"Not synced (fallback catalog)","models.synced_at":"Synced: {date}","models.search_placeholder":"Search models by name, ID or description\u2026","models.filter_all":"All","models.filter_vision":"Vision","models.filter_coding":"Coding","models.filter_recommended":"Recommended","models.filter_disabled":"Disabled","models.all":"All","models.vision":"Vision Only","models.coding":"Coding Only","models.recommended":"Recommended","models.th_active":"On","models.th_name":"Model Name","models.th_id":"Model ID","models.th_ctx":"Context","models.th_caps":"Capabilities","models.th_route":"Account Route","models.route_auto":"Auto / Pool","models.btn_probe":"Ping","models.probing":"Pinging\u2026","models.probe_result":"TTFT {ms} ms","models.probe_failed":"Probe failed: {error}","models.visible_apply":"Apply visibility","models.visible_applying":"Applying\u2026","models.visible_applied":"Visible models applied","models.visible_failed":"Visibility failed: {error}","models.star":"Star","models.auto_new":"New","models.apply_original_all":"Original Specs","models.reset_contexts_all":"\u21BA Reset Contexts","models.context_updated_msg":"Model context limits updated and synchronized to DSH.","models.ctx_default":"Default ({ctx} / {max})","models.ctx_original":"Original ({ctx} / {max} \xB7 {provider})","models.ctx_edit":"Edit Limits","models.ctx_window_label":"Context (e.g. 200000)","models.ctx_max_tokens_label":"Max Out (e.g. 16384)","models.ctx_apply_all":"All","models.ctx_apply_all_title":"Apply these limits across all models","models.ctx_custom_placeholder":"Tokens (e.g. 2000000)","models.ctx_save":"Save","models.ctx_cancel":"Cancel","models.custom_ctx_label":"Context","models.custom_max_label":"Max Out","models.ctx_tag_original":"Original","models.ctx_tag_custom":"Custom","stats.title":"Current DSH Session Telemetry","stats.desc":"Local monitoring of requests, network latency, and estimated token usage via ClinePass.","stats.requests":"Successful / Total Requests","stats.tokens":"Estimated Session Tokens","stats.reasoning_tokens":"Reasoning Tokens (Thinking)","stats.export_csv":"Export CSV","stats.export_json":"Export JSON","stats.latency":"Last Request Latency","stats.last_req":"Last Request Time","models.custom_add_title":"Custom Models","models.custom_id":"Model ID (e.g. anthropic/claude-3-7-sonnet)","models.custom_name":"Display Name (optional)","models.custom_btn_add":"Add Model","models.custom_btn_del":"Delete","models.effort_auto":"Auto Effort","stats.errors429":"429 Rate Limits (Failover)","stats.top_models":"Top Models by Token Usage","stats.top_accounts":"Top Accounts by Token Usage","stats.cache_saved":"Cache savings: \u2248 ${amount}","stats.reset_btn":"Reset Stats","stats.confirm_reset":"Confirm Reset?","stats.cancel_reset":"Cancel","diag.title":"Diagnostics & Auto-Registration","diag.desc":"ClineBot registers into DSH Models automatically when a key is configured. You can run a connection smoke test here.","diag.smoke_btn":"Run Smoke Test (Ping)","diag.smoke_testing":"Testing\u2026","update.available":"Update available: v{latestVersion} (current: v{currentVersion})","update.btn":"Update Now","update.updating":"Updating\u2026","update.done":"Successfully updated to v{version}! Please restart DSH.","update.failed":"Update failed: {error}","update.checking":"Checking for updates\u2026","update.up_to_date":"Up to date","sidebar.quota_title":"Cline Quota","sidebar.show_quota":"Show quota meter in sidebar","sidebar.show_quota_hint":"Display compact quota meter in DSH sidebar footer.","dashboard.title":"Cline Quota & Status","dashboard.subtitle":"Real-time subscription quota, rolling limits, and account pool status.","dashboard.close":"Close dashboard","dashboard.refresh":"Refresh","dashboard.refreshing":"Refreshing\u2026","dashboard.rolling_window":"5-Hour Window","dashboard.weekly_window":"Weekly Window","dashboard.remaining":"remaining","dashboard.used":"used","dashboard.account_pool":"Active Routing Account","dashboard.credits":"Credit Balance","dashboard.updated_at":"Updated at","composer.cost_title":"ClineBot Session Cost","composer.last_request":"Last request","composer.free":"Free","composer.cost_breakdown":"Token & Cost Breakdown","composer.prompt_tok":"Prompt Tokens","composer.completion_tok":"Completion Tokens","composer.reasoning_tok":"Reasoning Tokens","composer.est_cost":"Estimated Cost","composer.unpriced":"unpriced","composer.cache_read":"Cache Read","composer.cache_write":"Cache Write","composer.price_source":"Price","composer.price_estimate":"estimate","composer.price_override":"override","composer.price_public":"public catalog","discovery.title":"Discovered Local Keys","discovery.import_btn":"Import from {source}","discovery.imported":"Imported to accounts pool","discovery.scanning":"Scanning for local Cline keys\u2026","settings.unsaved":"{count} unsaved change(s)","settings.saving":"Saving\u2026","settings.saved":"Settings saved","settings.save_failed":"Could not save settings","settings.save":"Save","settings.discard":"Discard","settings.save_invalid_base_url":"Base URL must be https:// or a localhost address.","settings.save_invalid_number":"Timeouts and payload limits must be positive numbers.","settings.models_group":"Models & context","settings.models_group_hint":"Plan filter, visible models, context windows, output limits and reasoning defaults.","settings.integrations":"Integrations & display","settings.integrations_desc":"Surfaces outside the chat request that reuse this provider.","settings.web_search":"Serve web search with ClineBot","settings.web_search_hint":"Use the ClinePass endpoint for the DSH web_search tool.","settings.sidebar_quota":"Show quota meter in the sidebar","settings.sidebar_quota_hint":"Display the compact plan and rolling-window meter above Settings.","settings.composer_cost":"Show session cost in the composer","settings.composer_cost_hint":"Display the estimated token cost beside the composer.","settings.advanced":"Advanced","settings.advanced_desc":"Connection, timeouts and storage paths.","settings.base_url":"API base URL","settings.base_url_hint":"ClinePass API root. Remote endpoints must use https://.","settings.timeout":"Connect timeout (ms)","settings.timeout_hint":"Maximum wait for the upstream response headers.","settings.smoke_timeout":"Smoke-test timeout (ms)","settings.smoke_timeout_hint":"Maximum wait for the diagnostic chat completion.","settings.stream_idle":"Stream idle timeout (ms)","settings.stream_idle_hint":"Maximum gap between SSE chunks before the stream is treated as dead.","settings.max_payload":"Max multimodal payload (MB)","settings.max_payload_hint":"Oldest images are evicted once the request exceeds this budget.","settings.stats_retention":"Stats retention (days)","settings.stats_retention_hint":"Trim day/hour/session telemetry older than this. 0 keeps everything.","settings.daily_budget":"Daily budget (USD)","settings.daily_budget_hint":"Estimated spend guard for the current UTC day. 0 disables it.","settings.monthly_budget":"Monthly budget (USD)","settings.monthly_budget_hint":"Estimated spend guard for the current UTC month. 0 disables it.","settings.stats_path":"Stats file path","settings.stats_path_hint":"Persistent JSON telemetry path (supports ~/).","settings.models_cache_path":"Models cache path","settings.models_cache_path_hint":"Offline model catalogue cache (supports ~/).","providerCard.title":"ClineBot (ClinePass)","providerCard.hint":"Paste a ClinePass API key or manage the full account pool in Settings \u2192 ClineBot.","providerCard.keyPresent":"Key configured","providerCard.keyMissing":"Key missing","providerCard.routeActive":"Route active","providerCard.placeholder":"Paste API key","providerCard.replace":"Replace API key","providerCard.save":"Save key","providerCard.saving":"Saving\u2026","providerCard.saved":"Key saved and validated","providerCard.failed":"Could not save the key","providerCard.show":"Show","providerCard.hide":"Hide","providerCard.openSettings":"Open Settings \u2192 ClineBot for accounts, models and quotas.","dashboard.tab_quota":"Quota","dashboard.tab_analytics":"Analytics","dashboard.auto_account":"Automatic (least used)","analytics.title":"Usage analytics","analytics.requests":"Requests","analytics.tokens":"Tokens","analytics.cost":"Estimated cost","analytics.success":"Success rate","analytics.ttft":"Avg TTFT","analytics.loading":"Loading analytics\u2026","analytics.no_data":"No analytics yet. Send a few messages first.","analytics.unpriced":"{count} request(s) have no price estimate and are excluded from cost totals.","analytics.group_model":"Models","analytics.group_day":"Days","analytics.group_account":"Accounts","analytics.group_session":"Sessions","analytics.cache_saved":"Cache Savings","analytics.heatmap":"Request heatmap","analytics.heatmap_hint":"Requests per hour over the last 7 days.","analytics.heatmap_less":"Less","analytics.heatmap_more":"More","analytics.heatmap_empty":"No hourly data yet. Send a message to start the heatmap.","analytics.input":"Input","analytics.output":"Output","analytics.reasoning":"Reasoning","analytics.cache_read":"Cache read","analytics.cache_write":"Cache write","analytics.avg_ttft":"Avg TTFT","analytics.avg_latency":"Avg latency","analytics.p50_latency":"P50 latency","analytics.p95_latency":"P95 latency","analytics.heatmap_selected":"{day} {hour}:00 \u2014 {count} requests. Click the cell again to clear.","analytics.errors":"Errors","analytics.no_key":"ClinePass key is not configured.","analytics.no_key_hint":"Open Settings \u2192 ClineBot and paste a key, or import a discovered local key.","analytics.health_error":"ClinePass endpoint is unreachable.","analytics.not_registered":"ClineBot provider is not registered with DSH.","analytics.not_registered_hint":"Open Settings \u2192 ClineBot and check Diagnostics.","analytics.stale":"Showing the last successful snapshot.","analytics.range":"Range","analytics.range_24h":"24 hours","analytics.range_7":"7 days","analytics.range_30":"30 days","analytics.range_90":"90 days","analytics.from":"From","analytics.to":"To","analytics.model_filter":"Filter by model","analytics.account_filter":"Filter by account","analytics.all_models":"All models","analytics.all_accounts":"All accounts","analytics.errors_only":"Errors only","analytics.heatmap_all_models":"All models","analytics.heatmap_peak":"Peak","analytics.heatmap_by_hour":"By hour","analytics.heatmap_model":"Heatmap model","quota.no_windows":"No quota windows reported.","prices.title":"Price overrides","prices.desc":"USD per 1M tokens. Blank uses the local estimate.","prices.show":"Show prices","prices.hide":"Hide prices","prices.model":"Model","prices.input":"Input","prices.output":"Output","prices.cache_read":"Cache read","prices.cache_write":"Cache write","prices.auto":"auto","prices.empty":"No models available.","prices.hint":"These rates affect the composer and local analytics only; they are not an invoice.","prices.auto_sync":"Auto-sync public prices for new models","prices.auto_sync_hint":"Fetches models.dev only when a newly discovered model has no price; manual Sync still works when off.","prices.public_hint":"Sync uses the public models.dev catalog as an estimate; your overrides always win.","prices.sync":"Sync public prices","prices.syncing":"Syncing\u2026","prices.synced":"Public catalog synced: {count} models","prices.sync_failed":"Price sync failed: {error}"};function makeT(dict,fallback){return function t(key,vars){if(key==="quota.countdown"&&vars?.time==="resetting"){return dict&&dict["quota.resetting"]||fallback&&fallback["quota.resetting"]||"\u6B63\u5728\u91CD\u7F6E"}let val=dict&&dict[key]||fallback&&fallback[key]||key;if(vars&&typeof val==="string"){for(const k of Object.keys(vars)){val=val.replace(new RegExp("\\{"+k+"\\}","g"),String(vars[k]))}}return val}}
16
16
 
17
17
  // --- src/client/locales-zh.js ---
18
- const zh={nav:"ClineBot",title:"ClineBot",subtitle:"ClinePass \u8BA2\u9605\u670D\u52A1\uFF0C\u5305\u542B\u52A8\u6001\u5957\u9910\u6A21\u578B\u3002","settings.loading":"\u6B63\u5728\u52A0\u8F7D ClineBot \u8BBE\u7F6E\u2026","settings.retry":"\u91CD\u8BD5","settings.unavailable":"ClineBot \u8BBE\u7F6E\u4E0D\u53EF\u7528\uFF08\u5BBF\u4E3B\u547D\u540D\u7A7A\u95F4\u5C1A\u672A\u5C31\u7EEA\uFF09\u3002","api.timeout":"\u8BF7\u6C42\u8D85\u65F6\uFF08{seconds}\u79D2\uFF09","header.title":"ClineBot (ClinePass) \u6A21\u578B\u670D\u52A1\u5546","header.sub":"\u901A\u8FC7 ClinePass \u8BA2\u9605\u8FDE\u63A5\u4E3B\u6D41\u63A8\u7406\u4E0E\u7F16\u7A0B\u6A21\u578B\u3002\u5B8C\u5168\u517C\u5BB9 OpenAI \u63A5\u53E3\uFF0C\u81EA\u52A8\u8FFD\u8E2A\u6EDA\u52A8\u9650\u989D\u5E76\u5728 DSH \u4E2D\u5373\u65F6\u751F\u6548\u3002","badge.online":"\u670D\u52A1\u5728\u7EBF ({latency} ms)","badge.offline":"\u4E3B\u673A\u65E0\u6CD5\u8FDE\u63A5","badge.key_ok":"\u5BC6\u94A5\u6B63\u5E38 ({source})","badge.key_missing":"\u672A\u914D\u7F6E\u5BC6\u94A5","badge.registered":"\u5DF2\u63A5\u5165 DSH ({count} \u4E2A\u6A21\u578B)","badge.not_registered":"\u672A\u63A5\u5165 DSH","key.title":"\u6388\u6743\u4E0E API \u5BC6\u94A5","key.desc":"\u5BC6\u94A5\u76F4\u63A5\u52A0\u5BC6\u4FDD\u5B58\u5728 DSH \u51ED\u636E\u7BA1\u7406\u5668 (~/.dsh/.credentials.yaml)\uFF0C\u7EDD\u4E0D\u4F1A\u4EE5\u660E\u6587\u5F62\u5F0F\u66B4\u9732\u5728\u666E\u901A\u914D\u7F6E\u6587\u4EF6\u4E2D\u3002","key.placeholder_has":"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022","key.placeholder_empty":"\u7C98\u8D34 ClinePass \u5BC6\u94A5 (cline_...)","key.show":"\u663E\u793A","key.hide":"\u9690\u85CF","key.save":"\u4FDD\u5B58\u5BC6\u94A5","key.saving":"\u6B63\u5728\u4FDD\u5B58\u2026","key.login_fast":"\u6253\u5F00 API \u5BC6\u94A5\u9875 \u2197","key.verifying":"\u6B63\u5728\u8FDE\u63A5 Cline \u9A8C\u8BC1\u5BC6\u94A5\u2026","key.verified":"\u5BC6\u94A5\u6709\u6548\uFF1A{email} ({plan})","key.invalid":"\u5BC6\u94A5\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF1A{error}","key.env_label":"\u51ED\u636E\u73AF\u5883\u53D8\u91CF\u540D\u79F0\uFF1A","key.get_key":"\u524D\u5F80 app.cline.bot \u63A7\u5236\u53F0\u83B7\u53D6\u5BC6\u94A5 \u2197","key.saved_msg":"\u5BC6\u94A5\u5DF2\u6210\u529F\u4FDD\u5B58\u81F3 DSH \u51ED\u636E\u670D\u52A1\u3002\u9A8C\u8BC1\u72B6\u6001\uFF1A{status}","key.empty_err":"\u4FDD\u5B58\u524D\u8BF7\u8F93\u5165 API \u5BC6\u94A5","accounts.title":"\u591A\u8D26\u53F7\u6545\u969C\u8F6C\u79FB\u4E0E\u8F6E\u6362\u6C60 (Failover)","accounts.desc":"\u6DFB\u52A0\u5907\u7528\u8D26\u53F7\u3002\u5F53\u67D0\u4E2A\u8D26\u53F7\u89E6\u53D1 429 \u901F\u7387\u9650\u5236\u6216\u914D\u989D\u8017\u5C3D\u65F6\uFF0CClineBot \u4F1A\u667A\u80FD\u65E0\u7F1D\u5207\u6362\u81F3\u5269\u4F59\u989D\u5EA6\u6700\u9AD8\u7684\u5907\u7528\u8D26\u53F7\u3002","accounts.active_badge":"\u5F53\u524D\u6D3B\u52A8","accounts.pinned_badge":"\u56FA\u5B9A\u4F7F\u7528","accounts.pin_btn":"\u8BBE\u4E3A\u6D3B\u52A8","accounts.add_btn":"+ \u6DFB\u52A0\u8D26\u53F7","accounts.label_placeholder":"\u8D26\u53F7\u6807\u7B7E\uFF08\u4F8B\u5982\uFF1A\u5DE5\u4F5C\u8D26\u53F7\uFF09","accounts.env_placeholder":"CLINEBOT_API_KEY_2","accounts.delete_btn":"\u5220\u9664","accounts.delete_confirm":"\u786E\u5B9A\u8981\u5220\u9664\u8D26\u53F7\u201C{label}\u201D\u5417\uFF1F","accounts.save_btn":"\u4FDD\u5B58\u8D26\u53F7","accounts.cancel_btn":"\u53D6\u6D88","accounts.quota_used":"\u5DF2\u7528 {pct}%","accounts.last_failover":"\u6700\u8FD1\u6545\u969C\u8F6C\u79FB\uFF1A{from} \u2192 {to} ({reason})","accounts.mode_label":"\u8C03\u5EA6\u7B56\u7565","accounts.mode_least_used":"\u4F4E\u5360\u7528\u4F18\u5148 (Least Used)","accounts.mode_round_robin":"\u987A\u5E8F\u8F6E\u8BE2 (Round Robin)","accounts.history_show":"\u5386\u53F2","accounts.history_hide":"\u9690\u85CF\u5386\u53F2","accounts.history_title":"\u8D26\u6237\u4E8B\u4EF6\u5386\u53F2","accounts.history_hint":"\u6700\u8FD1\u7684\u51B7\u5374\u3001\u6062\u590D\u548C\u8DEF\u7531\u51B3\u7B56\u3002\u6700\u65B0\u5728\u524D\uFF0C\u6700\u591A 50 \u6761\u3002","accounts.history_empty":"\u6682\u65E0\u8D26\u6237\u4E8B\u4EF6\u3002","accounts.history_loading":"\u6B63\u5728\u52A0\u8F7D\u5386\u53F2\u2026","accounts.history_until":"\u81F3 {time}","accounts.event_cooldown":"\u51B7\u5374","accounts.event_recover":"\u5DF2\u6062\u590D","accounts.event_route":"\u5DF2\u8DEF\u7531","accounts.routes_title":"\u7279\u5B9A\u6A21\u578B\u4E13\u5C5E\u8DEF\u7531 (Per-Model)","accounts.routes_desc":"\u53EF\u9488\u5BF9\u7279\u5B9A\u6A21\u578B\u6307\u5B9A\u7ED1\u5B9A\u7684\u8D26\u53F7\u51ED\u636E\uFF08\u5982\u5C06\u6602\u8D35\u6A21\u578B\u6307\u5411\u7279\u5B9A\u8D26\u53F7\uFF09\u3002","accounts.route_model_placeholder":"\u6A21\u578B ID (\u4F8B\u5982 cline-pass/deepseek-v4-pro)","accounts.route_account_placeholder":"\u76EE\u6807\u8D26\u53F7","accounts.route_btn_add":"\u6DFB\u52A0\u8DEF\u7531","accounts.actions":"\u8D26\u6237\u64CD\u4F5C","accounts.configured":"\u5DF2\u914D\u7F6E","accounts.missing_key":"\u7F3A\u5C11\u5BC6\u94A5","accounts.source":"\u6765\u6E90\uFF1A{source}","accounts.quota_5h":"5\u5C0F\u65F6 {pct}%","accounts.rename":"\u91CD\u547D\u540D","accounts.rename_save":"\u4FDD\u5B58\u540D\u79F0","accounts.rename_placeholder":"\u8D26\u6237\u540D\u79F0","accounts.clear_key":"\u6E05\u9664\u5DF2\u5B58\u5BC6\u94A5","accounts.clear_confirm":"\u6E05\u9664 {name} \u7684\u5DF2\u5B58\u5BC6\u94A5\uFF1F\u8D26\u6237\u884C\u4F1A\u4FDD\u7559\u3002","accounts.remove_confirm":"\u4ECE\u6C60\u4E2D\u79FB\u9664 {name}\uFF1F","accounts.confirm":"\u786E\u8BA4","accounts.cancel":"\u53D6\u6D88","accounts.unpin":"\u53D6\u6D88\u56FA\u5B9A","accounts.no_accounts":"\u5C1A\u672A\u914D\u7F6E\u8D26\u6237\u3002","accounts.route_remove":"\u79FB\u9664\u8DEF\u7531","quota.title":"ClinePass \u7528\u91CF\u914D\u989D\u4E0E\u9650\u989D\u76D1\u63A7","quota.desc":"\u6765\u81EA ClinePass \u5B98\u65B9\u7684\u5B9E\u65F6\u6EDA\u52A8\u7A97\u53E3\u7528\u91CF\u9650\u989D","quota.account":"\u8D26\u53F7\uFF1A{email} \xB7 \u5957\u9910\uFF1A{plan}","quota.refresh":"\u5237\u65B0\u914D\u989D","quota.refreshing":"\u6B63\u5728\u5237\u65B0\u2026","quota.refreshed_msg":"\u8BA2\u9605\u7528\u91CF\u914D\u989D\u5DF2\u5237\u65B0","quota.window_5h":"\u23F1 5 \u5C0F\u65F6\u6EDA\u52A8\u9650\u989D","quota.window_weekly":"\u6BCF\u5468\u6EDA\u52A8\u7A97\u53E3","quota.used":"\u5DF2\u4F7F\u7528 {pct}%","quota.reset_at":"\u91CD\u7F6E\u65F6\u95F4\uFF1A{time}","quota.window_monthly":"\u6BCF\u6708\u6EDA\u52A8\u7A97\u53E3","quota.countdown":"{time}\u540E\u91CD\u7F6E","quota.resetting":"\u6B63\u5728\u91CD\u7F6E","quota.warning_low":"\u989D\u5EA6\u4F4E\u4E8E 10%\uFF0C\u8BF7\u6C42\u53EF\u80FD\u5F88\u5FEB\u4F1A\u53D7\u5230\u9650\u5236\u3002","quota.forecast_no_usage":"\u5C1A\u672A\u4F7F\u7528","quota.forecast_holds":"\u6309\u5F53\u524D\u901F\u5EA6\u53EF\u575A\u6301\u5230\u91CD\u7F6E","quota.forecast_left":"\u6309\u5F53\u524D\u901F\u5EA6\u7EA6\u5269 {time}","quota.forecast_exhausted":"\u5DF2\u8017\u5C3D","quota.exhausted_5h":"5 \u5C0F\u65F6\u6EDA\u52A8\u9650\u989D\u5DF2\u63A5\u8FD1\u8017\u5C3D\uFF08\u5DF2\u7528 {percentUsed}%\uFF09\u3002\u5728\u989D\u5EA6\u91CD\u7F6E\u524D\u65B0\u8BF7\u6C42\u53EF\u80FD\u4F1A\u88AB\u62D2\u7EDD\u3002","quota.warning_5h":"\u4F4E\u989D\u5EA6\u8B66\u544A\uFF1A5 \u5C0F\u65F6\u9650\u989D\u5DF2\u6D88\u8017 {percentUsed}%\uFF08\u5269\u4F59\u4E0D\u8DB3 10%\uFF09\u3002","quota.warning_weekly":"\u5468\u9650\u989D\u504F\u4F4E\u8B66\u544A\uFF1A\u5DF2\u6D88\u8017 {percentUsed}%\uFF08\u5269\u4F59 {remainingPercent}%\uFF09\u3002","quota.warning_monthly":"\u6708\u9650\u989D\u504F\u4F4E\u8B66\u544A\uFF1A\u5DF2\u6D88\u8017 {percentUsed}%\uFF08\u5269\u4F59 {remainingPercent}%\uFF09\u3002","models.reasoning_tag":"\u6DF1\u5EA6\u601D\u8003","models.effort_low":"\u4F4E","models.effort_medium":"\u4E2D","models.effort_high":"\u9AD8","models.effort_max":"\u6700\u9AD8","models.caps_vision":"\u89C6\u89C9","models.caps_general":"\u901A\u7528","models.caps_coding":"\u7F16\u7A0B","diag.smoke_result":"\u5EF6\u8FDF\uFF1A{latency} ms | \u6A21\u578B\uFF1A{model}\n\u54CD\u5E94\uFF1A{preview}","diag.empty":"(\u7A7A)","ui.error_title":"ClineBot \u754C\u9762\u9519\u8BEF\uFF1A","ui.retry":"\u91CD\u8BD5","update.check_failed":"\u68C0\u67E5\u66F4\u65B0\u5931\u8D25","update.retry":"\u91CD\u8BD5\u68C0\u67E5","models.filter_by_plan":"\u9690\u85CF\u672A\u8BA2\u9605\u6A21\u578B","models.filter_by_plan_hint":"\u5728\u804A\u5929\u6A21\u578B\u9009\u62E9\u5668\u4E2D\u81EA\u52A8\u9690\u85CF\u5F53\u524D ClinePass \u8BA2\u9605\u5957\u9910\u672A\u5305\u542B\u7684\u6A21\u578B\u3002","models.visible_models":"\u804A\u5929\u53EF\u89C1\u6A21\u578B","models.visible_models_hint":"\u9009\u62E9\u5728 DSH \u804A\u5929\u9009\u62E9\u5668\u4E2D\u663E\u793A\u7684\u6A21\u578B\uFF08\u4E3A\u7A7A\u65F6\u663E\u793A\u5168\u90E8\u53EF\u7528\u6A21\u578B\uFF09\u3002","models.in_plan":"\u5957\u9910\u5185","models.out_of_plan":"\u5957\u9910\u5916","models.group_plan":"\u5957\u9910\u5185\u6A21\u578B","models.group_other":"\u5176\u4ED6\u6A21\u578B","models.group_custom":"\u81EA\u5B9A\u4E49\u6A21\u578B","models.plan_only":"\u4EC5\u5957\u9910\u6A21\u578B","models.show_all":"\u663E\u793A\u5168\u90E8","models.clean_stale":"\u6E05\u7406\u8FC7\u671F ({count})","models.models_selected":"\u5DF2\u9009\u62E9 {selected} / {total} \u4E2A\u6A21\u578B\u7528\u4E8E\u804A\u5929","models.title":"DSH \u6A21\u578B\u9009\u62E9\u5217\u8868\uFF08\u5DF2\u542F\u7528 {enabled}/{total}\uFF09","models.desc":"\u6A21\u578B\u76EE\u5F55\u4E0E ClinePass \u5B98\u65B9\u8BA2\u9605\u5B9E\u65F6\u540C\u6B65\u3002\u65B0\u8BA2\u9605\u6A21\u578B\u9ED8\u8BA4\u81EA\u52A8\u542F\u7528\u3002\u53D6\u6D88\u52FE\u9009\u53EF\u5728 DSH \u5BF9\u8BDD\u6A21\u578B\u5217\u8868\u4E2D\u9690\u85CF\u4E0D\u9700\u8981\u7684\u6A21\u578B\u3002\u652F\u6301\u5728\u5BF9\u8BDD\u8F93\u5165\u6846\u4E0A\u65B9\u5207\u6362\u63A8\u7406\u601D\u8003\u5F3A\u5EA6\uFF08Effort\uFF09\u3002","models.reasoning_tooltip":"\u652F\u6301\u5728\u5BF9\u8BDD\u6846\u4E0A\u65B9\u6A21\u578B\u83DC\u5355\u4E2D\u9009\u62E9\u601D\u8003\u5F3A\u5EA6\uFF08Off / Low / Medium / High / Max\uFF09","models.sync":"\u540C\u6B65\u8BA2\u9605\u6A21\u578B","models.syncing":"\u6B63\u5728\u540C\u6B65\u2026","models.synced_msg":"\u5DF2\u6210\u529F\u540C\u6B65\u8BA2\u9605\u6A21\u578B\uFF1A\u5171 {total} \u4E2A\uFF08\u53D1\u73B0 {discovered} \u4E2A\u65B0\u6A21\u578B\uFF09","models.verified":"\u5DF2\u4E0E\u5957\u9910\u540C\u6B65","models.unverified":"\u672A\u540C\u6B65\uFF08\u5907\u7528\u76EE\u5F55\uFF09","models.synced_at":"\u540C\u6B65\u65F6\u95F4\uFF1A{date}","models.search_placeholder":"\u6309\u540D\u79F0\u3001ID \u6216\u63CF\u8FF0\u641C\u7D22\u6A21\u578B\u2026","models.filter_all":"\u5168\u90E8","models.filter_vision":"\u4EC5\u89C6\u89C9","models.filter_coding":"\u4EC5\u7F16\u7A0B","models.filter_recommended":"\u63A8\u8350","models.filter_disabled":"\u5DF2\u7981\u7528","models.all":"\u5168\u90E8\u6A21\u578B","models.vision":"\u4EC5\u89C6\u89C9 (Vision)","models.coding":"\u4EC5\u7F16\u7A0B (Coding)","models.recommended":"\u63A8\u8350\u6A21\u578B","models.th_active":"\u542F\u7528","models.th_name":"\u6A21\u578B\u540D\u79F0","models.th_id":"\u6A21\u578B\u6807\u8BC6\u7B26 (ID)","models.th_ctx":"\u4E0A\u4E0B\u6587\u7A97\u53E3","models.th_caps":"\u7279\u6027\u80FD\u529B","models.th_route":"\u6307\u5B9A\u8DEF\u7531\u8D26\u53F7","models.route_auto":"\u81EA\u52A8 / \u9ED8\u8BA4\u6C60","models.btn_probe":"\u6D4B\u8BD5\u5EF6\u8FDF","models.star":"\u63A8\u8350","models.auto_new":"\u65B0\u4E0A\u7EBF","models.apply_original_all":"\u539F\u5382\u89C4\u683C","models.reset_contexts_all":"\u21BA \u91CD\u7F6E\u4E0A\u4E0B\u6587","models.context_updated_msg":"\u6A21\u578B\u4E0A\u4E0B\u6587\u9650\u5236\u5DF2\u66F4\u65B0\u5E76\u540C\u6B65\u751F\u6548\u81F3 DSH\u3002","models.ctx_default":"\u9ED8\u8BA4 ({ctx} / {max})","models.ctx_original":"\u539F\u5382 ({ctx} / {max} \xB7 {provider})","models.ctx_edit":"\u7F16\u8F91\u9650\u5236","models.ctx_window_label":"\u4E0A\u4E0B\u6587 (\u5982 200000)","models.ctx_max_tokens_label":"\u6700\u5927\u8F93\u51FA (\u5982 16384)","models.ctx_apply_all":"\u5168\u90E8","models.ctx_apply_all_title":"\u5C06\u6B64\u9650\u5236\u6279\u91CF\u5E94\u7528\u5230\u6240\u6709\u6A21\u578B","models.ctx_custom_placeholder":"Token \u6570\u91CF (\u5982 2000000)","models.ctx_save":"\u4FDD\u5B58","models.ctx_cancel":"\u53D6\u6D88","models.custom_ctx_label":"\u4E0A\u4E0B\u6587","models.custom_max_label":"\u6700\u5927\u8F93\u51FA","models.ctx_tag_original":"\u539F\u5382","models.ctx_tag_custom":"\u81EA\u5B9A\u4E49","stats.title":"\u5F53\u524D DSH \u4F1A\u8BDD\u8BF7\u6C42\u9065\u6D4B","stats.desc":"\u672C\u5730\u5B9E\u65F6\u76D1\u63A7\u8BF7\u6C42\u6B21\u6570\u3001\u7F51\u7EDC\u5EF6\u8FDF\u4EE5\u53CA\u901A\u8FC7 ClinePass \u6D88\u8017\u7684\u4F30\u7B97 Token \u6570\u91CF\u3002","stats.requests":"\u6210\u529F / \u603B\u8BF7\u6C42\u6B21\u6570","stats.tokens":"\u4F1A\u8BDD\u7D2F\u8BA1 Token \u4F30\u7B97","stats.reasoning_tokens":"\u6DF1\u5EA6\u601D\u8003 Token \u6D88\u8017","stats.export_csv":"\u5BFC\u51FA CSV","stats.export_json":"\u5BFC\u51FA JSON","stats.latency":"\u6700\u8FD1\u4E00\u6B21\u8BF7\u6C42\u5EF6\u8FDF","stats.last_req":"\u6700\u8FD1\u8BF7\u6C42\u65F6\u95F4","models.custom_add_title":"\u81EA\u5B9A\u4E49\u6A21\u578B","models.custom_id":"\u6A21\u578B ID (\u4F8B\u5982 anthropic/claude-3-7-sonnet)","models.custom_name":"\u663E\u793A\u540D\u79F0 (\u53EF\u9009)","models.custom_btn_add":"\u6DFB\u52A0\u6A21\u578B","models.custom_btn_del":"\u5220\u9664","models.effort_auto":"\u81EA\u52A8\u601D\u8003\u5F3A\u5EA6","stats.errors429":"429 \u9650\u6D41\u4FDD\u62A4\u6B21\u6570","stats.top_models":"Token \u6D88\u8017\u6700\u9AD8\u6A21\u578B","stats.top_accounts":"Token \u6D88\u8017\u6700\u9AD8\u8D26\u6237","stats.cache_saved":"\u7F13\u5B58\u8282\u7701\uFF1A\u2248 ${amount}","stats.reset_btn":"\u91CD\u7F6E\u7EDF\u8BA1","stats.confirm_reset":"\u786E\u8BA4\u91CD\u7F6E\uFF1F","stats.cancel_reset":"\u53D6\u6D88","diag.title":"\u8BCA\u65AD\u5DE5\u5177\u4E0E\u670D\u52A1\u63A5\u5165","diag.desc":"\u914D\u7F6E API \u5BC6\u94A5\u540E\uFF0CClineBot \u4F1A\u81EA\u52A8\u5411 DSH \u6CE8\u518C\u6A21\u578B\u670D\u52A1\u3002\u60A8\u53EF\u4EE5\u5728\u6B64\u8FDB\u884C\u8FDE\u901A\u6027\u6D4B\u8BD5\u3002","diag.smoke_btn":"\u6267\u884C\u8FDE\u901A\u6027\u5192\u70DF\u6D4B\u8BD5 (Ping)","diag.smoke_testing":"\u6B63\u5728\u6D4B\u8BD5\u2026","update.available":"\u53D1\u73B0\u65B0\u7248\u672C v{latestVersion}\uFF08\u5F53\u524D\u7248\u672C v{currentVersion}\uFF09","update.btn":"\u7ACB\u5373\u66F4\u65B0","update.updating":"\u6B63\u5728\u66F4\u65B0\u2026","update.done":"\u66F4\u65B0\u6210\u529F\u81F3 v{version}\uFF01\u8BF7\u91CD\u542F DSH \u670D\u52A1\u751F\u6548\u3002","update.failed":"\u66F4\u65B0\u5931\u8D25\uFF1A{error}","update.checking":"\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026","update.up_to_date":"\u5DF2\u662F\u6700\u65B0\u7248\u672C","sidebar.quota_title":"Cline \u989D\u5EA6","sidebar.show_quota":"\u5728\u4FA7\u8FB9\u680F\u663E\u793A\u989D\u5EA6\u6761","sidebar.show_quota_hint":"\u5728 DSH \u4FA7\u8FB9\u680F\u5E95\u90E8\u5E38\u9A7B\u663E\u793A\u989D\u5EA6 mini \u8FDB\u5EA6\u6307\u793A\u5668\u3002","dashboard.title":"Cline \u989D\u5EA6\u4E0E\u72B6\u6001","dashboard.subtitle":"\u5B9E\u65F6\u8BA2\u9605\u989D\u5EA6\u3001\u6ED1\u52A8\u7A97\u53E3\u9650\u5236\u4E0E\u8D26\u6237\u6C60\u72B6\u6001\u3002","dashboard.close":"\u5173\u95ED\u9762\u677F","dashboard.refresh":"\u5237\u65B0","dashboard.refreshing":"\u6B63\u5728\u5237\u65B0\u2026","dashboard.rolling_window":"5\u5C0F\u65F6\u6ED1\u52A8\u7A97\u53E3","dashboard.weekly_window":"\u6BCF\u5468\u914D\u989D\u7A97\u53E3","dashboard.remaining":"\u5269\u4F59","dashboard.used":"\u5DF2\u7528","dashboard.account_pool":"\u5F53\u524D\u627F\u8F7D\u8D26\u6237","dashboard.credits":"\u70B9\u6570\u4F59\u989D","dashboard.updated_at":"\u66F4\u65B0\u4E8E","composer.cost_title":"ClineBot \u4F1A\u8BDD\u6210\u672C","composer.last_request":"\u6700\u8FD1\u4E00\u6B21\u8BF7\u6C42","composer.free":"\u514D\u8D39","composer.cost_breakdown":"Token \u4E0E\u8D39\u7528\u660E\u7EC6","composer.prompt_tok":"\u63D0\u793A\u8BCD Token","composer.completion_tok":"\u8865\u5168 Token","composer.reasoning_tok":"\u601D\u8003\u63A8\u7406 Token","composer.est_cost":"\u9884\u4F30\u82B1\u8D39","composer.unpriced":"\u65E0\u4EF7\u683C","composer.cache_read":"\u7F13\u5B58\u8BFB\u53D6","composer.cache_write":"\u7F13\u5B58\u5199\u5165","composer.price_source":"\u4EF7\u683C\u6765\u6E90","composer.price_estimate":"\u4F30\u7B97","composer.price_override":"\u8986\u76D6","composer.price_public":"\u516C\u5F00\u76EE\u5F55","discovery.title":"\u53D1\u73B0\u672C\u5730\u5BC6\u94A5","discovery.import_btn":"\u4ECE {source} \u5BFC\u5165","discovery.imported":"\u5DF2\u6210\u529F\u5BFC\u5165\u8D26\u6237\u6C60","discovery.scanning":"\u6B63\u5728\u626B\u63CF\u672C\u5730 Cline \u5BC6\u94A5\u2026","settings.unsaved":"{count} \u9879\u672A\u4FDD\u5B58\u66F4\u6539","settings.saving":"\u6B63\u5728\u4FDD\u5B58\u2026","settings.saved":"\u8BBE\u7F6E\u5DF2\u4FDD\u5B58","settings.save_failed":"\u65E0\u6CD5\u4FDD\u5B58\u8BBE\u7F6E","settings.save":"\u4FDD\u5B58","settings.discard":"\u653E\u5F03","settings.save_invalid_base_url":"Base URL \u5FC5\u987B\u4F7F\u7528 https:// \u6216 localhost\u3002","settings.save_invalid_number":"\u8D85\u65F6\u548C\u8D1F\u8F7D\u9650\u5236\u5FC5\u987B\u662F\u6B63\u6570\u3002","settings.models_group":"\u6A21\u578B\u4E0E\u4E0A\u4E0B\u6587","settings.models_group_hint":"\u5957\u9910\u8FC7\u6EE4\u3001\u53EF\u89C1\u6A21\u578B\u3001\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u8F93\u51FA\u4E0A\u9650\u548C\u63A8\u7406\u9ED8\u8BA4\u503C\u3002","settings.integrations":"\u96C6\u6210\u4E0E\u663E\u793A","settings.integrations_desc":"\u804A\u5929\u8BF7\u6C42\u4E4B\u5916\u590D\u7528\u6B64\u670D\u52A1\u5546\u7684\u754C\u9762\u5165\u53E3\u3002","settings.web_search":"\u4F7F\u7528 ClineBot \u63D0\u4F9B\u7F51\u9875\u641C\u7D22","settings.web_search_hint":"\u4E3A DSH web_search \u5DE5\u5177\u4F7F\u7528 ClinePass \u7AEF\u70B9\u3002","settings.sidebar_quota":"\u5728\u4FA7\u8FB9\u680F\u663E\u793A\u914D\u989D","settings.sidebar_quota_hint":"\u5728\u8BBE\u7F6E\u4E0A\u65B9\u663E\u793A\u7D27\u51D1\u7684\u5957\u9910\u4E0E\u6EDA\u52A8\u7A97\u53E3\u914D\u989D\u3002","settings.composer_cost":"\u5728\u8F93\u5165\u6846\u663E\u793A\u4F1A\u8BDD\u6210\u672C","settings.composer_cost_hint":"\u5728\u8F93\u5165\u6846\u65C1\u663E\u793A\u9884\u4F30 token \u6210\u672C\u3002","settings.advanced":"\u9AD8\u7EA7","settings.advanced_desc":"\u8FDE\u63A5\u3001\u8D85\u65F6\u548C\u5B58\u50A8\u8DEF\u5F84\u3002","settings.base_url":"API \u57FA\u7840\u5730\u5740","settings.base_url_hint":"ClinePass API \u6839\u5730\u5740\u3002\u8FDC\u7A0B\u7AEF\u70B9\u5FC5\u987B\u4F7F\u7528 https://\u3002","settings.timeout":"\u8FDE\u63A5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09","settings.timeout_hint":"\u7B49\u5F85\u4E0A\u6E38\u54CD\u5E94\u5934\u7684\u6700\u5927\u65F6\u95F4\u3002","settings.smoke_timeout":"\u70DF\u96FE\u6D4B\u8BD5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09","settings.smoke_timeout_hint":"\u7B49\u5F85\u8BCA\u65AD\u804A\u5929\u8865\u5168\u7684\u6700\u5927\u65F6\u95F4\u3002","settings.stream_idle":"\u6D41\u7A7A\u95F2\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09","settings.stream_idle_hint":"SSE \u6570\u636E\u5757\u4E4B\u95F4\u7684\u6700\u5927\u95F4\u9694\uFF0C\u8D85\u8FC7\u540E\u89C6\u4E3A\u65AD\u6D41\u3002","settings.max_payload":"\u591A\u6A21\u6001\u8D1F\u8F7D\u4E0A\u9650\uFF08MB\uFF09","settings.max_payload_hint":"\u8BF7\u6C42\u8D85\u8FC7\u9884\u7B97\u65F6\u4F1A\u6DD8\u6C70\u6700\u65E7\u7684\u56FE\u7247\u3002","settings.stats_retention":"\u7EDF\u8BA1\u4FDD\u7559\u5929\u6570","settings.stats_retention_hint":"\u8D85\u8FC7\u8BE5\u5929\u6570\u7684\u65E5/\u5C0F\u65F6/\u4F1A\u8BDD\u9065\u6D4B\u4F1A\u88AB\u6E05\u7406\u30020 \u8868\u793A\u6C38\u4E45\u4FDD\u7559\u3002","settings.daily_budget":"\u6BCF\u65E5\u9884\u7B97\uFF08USD\uFF09","settings.daily_budget_hint":"\u6309\u5F53\u524D UTC \u65E5\u4F30\u7B97\u7684\u82B1\u8D39\u4E0A\u9650\u30020 \u8868\u793A\u5173\u95ED\u3002","settings.monthly_budget":"\u6BCF\u6708\u9884\u7B97\uFF08USD\uFF09","settings.monthly_budget_hint":"\u6309\u5F53\u524D UTC \u6708\u4F30\u7B97\u7684\u82B1\u8D39\u4E0A\u9650\u30020 \u8868\u793A\u5173\u95ED\u3002","settings.stats_path":"\u7EDF\u8BA1\u6587\u4EF6\u8DEF\u5F84","settings.stats_path_hint":"\u6301\u4E45\u5316 JSON \u9065\u6D4B\u8DEF\u5F84\uFF08\u652F\u6301 ~/\uFF09\u3002","settings.models_cache_path":"\u6A21\u578B\u7F13\u5B58\u8DEF\u5F84","settings.models_cache_path_hint":"\u79BB\u7EBF\u6A21\u578B\u76EE\u5F55\u7F13\u5B58\uFF08\u652F\u6301 ~/\uFF09\u3002","providerCard.title":"ClineBot\uFF08ClinePass\uFF09","providerCard.hint":"\u7C98\u8D34 ClinePass API \u5BC6\u94A5\uFF0C\u6216\u524D\u5F80\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u7BA1\u7406\u5B8C\u6574\u8D26\u6237\u6C60\u3002","providerCard.keyPresent":"\u5BC6\u94A5\u5DF2\u914D\u7F6E","providerCard.keyMissing":"\u7F3A\u5C11\u5BC6\u94A5","providerCard.routeActive":"\u8DEF\u7531\u5DF2\u542F\u7528","providerCard.placeholder":"\u7C98\u8D34 API \u5BC6\u94A5","providerCard.replace":"\u66FF\u6362 API \u5BC6\u94A5","providerCard.save":"\u4FDD\u5B58\u5BC6\u94A5","providerCard.saving":"\u6B63\u5728\u4FDD\u5B58\u2026","providerCard.saved":"\u5BC6\u94A5\u5DF2\u4FDD\u5B58\u5E76\u9A8C\u8BC1","providerCard.failed":"\u65E0\u6CD5\u4FDD\u5B58\u5BC6\u94A5","providerCard.show":"\u663E\u793A","providerCard.hide":"\u9690\u85CF","providerCard.openSettings":"\u524D\u5F80\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u7BA1\u7406\u8D26\u6237\u3001\u6A21\u578B\u548C\u914D\u989D\u3002","dashboard.tab_quota":"\u914D\u989D","dashboard.tab_analytics":"\u5206\u6790","dashboard.auto_account":"\u81EA\u52A8\uFF08\u6700\u5C11\u4F7F\u7528\uFF09","analytics.title":"\u7528\u91CF\u5206\u6790","analytics.requests":"\u8BF7\u6C42\u6570","analytics.tokens":"Token","analytics.cost":"\u9884\u4F30\u6210\u672C","analytics.success":"\u6210\u529F\u7387","analytics.ttft":"\u5E73\u5747\u9996\u5B57\u5EF6\u8FDF","analytics.loading":"\u6B63\u5728\u52A0\u8F7D\u5206\u6790\u2026","analytics.no_data":"\u6682\u65E0\u5206\u6790\u6570\u636E\uFF0C\u8BF7\u5148\u53D1\u9001\u51E0\u6761\u6D88\u606F\u3002","analytics.unpriced":"{count} \u4E2A\u8BF7\u6C42\u6CA1\u6709\u4EF7\u683C\u4F30\u7B97\uFF0C\u672A\u8BA1\u5165\u6210\u672C\u5408\u8BA1\u3002","analytics.group_model":"\u6A21\u578B","analytics.group_day":"\u65E5\u671F","analytics.group_account":"\u8D26\u6237","analytics.group_session":"\u4F1A\u8BDD","analytics.cache_saved":"\u7F13\u5B58\u8282\u7701","analytics.heatmap":"\u8BF7\u6C42\u70ED\u529B\u56FE","analytics.heatmap_hint":"\u6700\u8FD1 7 \u5929\u6BCF\u5C0F\u65F6\u7684\u8BF7\u6C42\u6570\u3002","analytics.heatmap_less":"\u5C11","analytics.heatmap_more":"\u591A","analytics.heatmap_empty":"\u6682\u65E0\u6309\u5C0F\u65F6\u6570\u636E\u3002\u53D1\u9001\u4E00\u6761\u6D88\u606F\u540E\u5F00\u59CB\u8BB0\u5F55\u3002","analytics.input":"\u8F93\u5165","analytics.output":"\u8F93\u51FA","analytics.reasoning":"\u63A8\u7406","analytics.cache_read":"\u7F13\u5B58\u8BFB\u53D6","analytics.cache_write":"\u7F13\u5B58\u5199\u5165","analytics.avg_ttft":"\u5E73\u5747\u9996\u5B57\u5EF6\u8FDF","analytics.avg_latency":"\u5E73\u5747\u5EF6\u8FDF","analytics.errors":"\u9519\u8BEF","analytics.p50_latency":"P50 \u5EF6\u8FDF","analytics.p95_latency":"P95 \u5EF6\u8FDF","analytics.heatmap_selected":"{day} {hour}:00 \u2014 {count} \u6B21\u8BF7\u6C42\u3002\u518D\u6B21\u70B9\u51FB\u53EF\u6E05\u9664\u3002","analytics.no_key":"\u5C1A\u672A\u914D\u7F6E ClinePass \u5BC6\u94A5\u3002","analytics.no_key_hint":"\u6253\u5F00\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u7C98\u8D34\u5BC6\u94A5\uFF0C\u6216\u5BFC\u5165\u5DF2\u53D1\u73B0\u7684\u672C\u5730\u5BC6\u94A5\u3002","analytics.health_error":"ClinePass \u7AEF\u70B9\u4E0D\u53EF\u8FBE\u3002","analytics.not_registered":"ClineBot \u63D0\u4F9B\u5546\u672A\u5728 DSH \u4E2D\u6CE8\u518C\u3002","analytics.not_registered_hint":"\u6253\u5F00\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u68C0\u67E5\u8BCA\u65AD\u3002","analytics.stale":"\u6B63\u5728\u663E\u793A\u4E0A\u6B21\u6210\u529F\u83B7\u53D6\u7684\u6570\u636E\u3002","analytics.range":"\u8303\u56F4","analytics.range_24h":"24 \u5C0F\u65F6","analytics.range_7":"7 \u5929","analytics.range_30":"30 \u5929","analytics.range_90":"90 \u5929","analytics.from":"\u5F00\u59CB","analytics.to":"\u7ED3\u675F","analytics.model_filter":"\u6309\u6A21\u578B\u7B5B\u9009","analytics.account_filter":"\u6309\u8D26\u6237\u7B5B\u9009","analytics.all_models":"\u6240\u6709\u6A21\u578B","analytics.all_accounts":"\u6240\u6709\u8D26\u6237","analytics.errors_only":"\u4EC5\u9519\u8BEF","analytics.heatmap_all_models":"\u6240\u6709\u6A21\u578B","analytics.heatmap_peak":"\u5CF0\u503C","analytics.heatmap_by_hour":"\u6309\u5C0F\u65F6","analytics.heatmap_model":"\u70ED\u529B\u56FE\u6A21\u578B","quota.no_windows":"\u672A\u8FD4\u56DE\u914D\u989D\u7A97\u53E3\u3002","prices.title":"\u4EF7\u683C\u8986\u76D6","prices.desc":"\u6BCF 100 \u4E07 token \u7684\u7F8E\u5143\u4EF7\u683C\u3002\u7559\u7A7A\u4F7F\u7528\u672C\u5730\u4F30\u7B97\u3002","prices.show":"\u663E\u793A\u4EF7\u683C","prices.hide":"\u9690\u85CF\u4EF7\u683C","prices.model":"\u6A21\u578B","prices.input":"\u8F93\u5165","prices.output":"\u8F93\u51FA","prices.cache_read":"\u7F13\u5B58\u8BFB\u53D6","prices.cache_write":"\u7F13\u5B58\u5199\u5165","prices.auto":"\u81EA\u52A8","prices.empty":"\u6682\u65E0\u6A21\u578B\u3002","prices.hint":"\u8FD9\u4E9B\u4EF7\u683C\u53EA\u5F71\u54CD\u8F93\u5165\u6846\u6210\u672C\u548C\u672C\u5730\u5206\u6790\uFF0C\u4E0D\u662F\u8D26\u5355\u3002","prices.public_hint":"\u540C\u6B65\u4F7F\u7528\u516C\u5F00\u7684 models.dev \u76EE\u5F55\u4F5C\u4E3A\u4F30\u7B97\uFF1B\u624B\u52A8\u8986\u76D6\u59CB\u7EC8\u4F18\u5148\u3002","prices.sync":"\u540C\u6B65\u516C\u5F00\u4EF7\u683C","prices.syncing":"\u6B63\u5728\u540C\u6B65\u2026","prices.synced":"\u516C\u5F00\u76EE\u5F55\u5DF2\u540C\u6B65\uFF1A{count} \u4E2A\u6A21\u578B","prices.sync_failed":"\u4EF7\u683C\u540C\u6B65\u5931\u8D25\uFF1A{error}"};
18
+ const zh={nav:"ClineBot",title:"ClineBot",subtitle:"ClinePass \u8BA2\u9605\u670D\u52A1\uFF0C\u5305\u542B\u52A8\u6001\u5957\u9910\u6A21\u578B\u3002","settings.loading":"\u6B63\u5728\u52A0\u8F7D ClineBot \u8BBE\u7F6E\u2026","settings.retry":"\u91CD\u8BD5","settings.unavailable":"ClineBot \u8BBE\u7F6E\u4E0D\u53EF\u7528\uFF08\u5BBF\u4E3B\u547D\u540D\u7A7A\u95F4\u5C1A\u672A\u5C31\u7EEA\uFF09\u3002","api.timeout":"\u8BF7\u6C42\u8D85\u65F6\uFF08{seconds}\u79D2\uFF09","header.title":"ClineBot (ClinePass) \u6A21\u578B\u670D\u52A1\u5546","header.sub":"\u901A\u8FC7 ClinePass \u8BA2\u9605\u8FDE\u63A5\u4E3B\u6D41\u63A8\u7406\u4E0E\u7F16\u7A0B\u6A21\u578B\u3002\u5B8C\u5168\u517C\u5BB9 OpenAI \u63A5\u53E3\uFF0C\u81EA\u52A8\u8FFD\u8E2A\u6EDA\u52A8\u9650\u989D\u5E76\u5728 DSH \u4E2D\u5373\u65F6\u751F\u6548\u3002","badge.online":"\u670D\u52A1\u5728\u7EBF ({latency} ms)","badge.offline":"\u4E3B\u673A\u65E0\u6CD5\u8FDE\u63A5","badge.key_ok":"\u5BC6\u94A5\u6B63\u5E38 ({source})","badge.key_missing":"\u672A\u914D\u7F6E\u5BC6\u94A5","badge.registered":"\u5DF2\u63A5\u5165 DSH ({count} \u4E2A\u6A21\u578B)","badge.not_registered":"\u672A\u63A5\u5165 DSH","key.title":"\u6388\u6743\u4E0E API \u5BC6\u94A5","key.desc":"\u5BC6\u94A5\u76F4\u63A5\u52A0\u5BC6\u4FDD\u5B58\u5728 DSH \u51ED\u636E\u7BA1\u7406\u5668 (~/.dsh/.credentials.yaml)\uFF0C\u7EDD\u4E0D\u4F1A\u4EE5\u660E\u6587\u5F62\u5F0F\u66B4\u9732\u5728\u666E\u901A\u914D\u7F6E\u6587\u4EF6\u4E2D\u3002","key.placeholder_has":"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022","key.placeholder_empty":"\u7C98\u8D34 ClinePass \u5BC6\u94A5 (cline_...)","key.show":"\u663E\u793A","key.hide":"\u9690\u85CF","key.save":"\u4FDD\u5B58\u5BC6\u94A5","key.saving":"\u6B63\u5728\u4FDD\u5B58\u2026","key.login_fast":"\u6253\u5F00 API \u5BC6\u94A5\u9875 \u2197","key.verifying":"\u6B63\u5728\u8FDE\u63A5 Cline \u9A8C\u8BC1\u5BC6\u94A5\u2026","key.verified":"\u5BC6\u94A5\u6709\u6548\uFF1A{email} ({plan})","key.invalid":"\u5BC6\u94A5\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF1A{error}","key.env_label":"\u51ED\u636E\u73AF\u5883\u53D8\u91CF\u540D\u79F0\uFF1A","key.get_key":"\u524D\u5F80 app.cline.bot \u63A7\u5236\u53F0\u83B7\u53D6\u5BC6\u94A5 \u2197","key.saved_msg":"\u5BC6\u94A5\u5DF2\u6210\u529F\u4FDD\u5B58\u81F3 DSH \u51ED\u636E\u670D\u52A1\u3002\u9A8C\u8BC1\u72B6\u6001\uFF1A{status}","key.empty_err":"\u4FDD\u5B58\u524D\u8BF7\u8F93\u5165 API \u5BC6\u94A5","accounts.title":"\u591A\u8D26\u53F7\u6545\u969C\u8F6C\u79FB\u4E0E\u8F6E\u6362\u6C60 (Failover)","accounts.desc":"\u6DFB\u52A0\u5907\u7528\u8D26\u53F7\u3002\u5F53\u67D0\u4E2A\u8D26\u53F7\u89E6\u53D1 429 \u901F\u7387\u9650\u5236\u6216\u914D\u989D\u8017\u5C3D\u65F6\uFF0CClineBot \u4F1A\u667A\u80FD\u65E0\u7F1D\u5207\u6362\u81F3\u5269\u4F59\u989D\u5EA6\u6700\u9AD8\u7684\u5907\u7528\u8D26\u53F7\u3002","accounts.active_badge":"\u5F53\u524D\u6D3B\u52A8","accounts.pinned_badge":"\u56FA\u5B9A\u4F7F\u7528","accounts.pin_btn":"\u8BBE\u4E3A\u6D3B\u52A8","accounts.add_btn":"+ \u6DFB\u52A0\u8D26\u53F7","accounts.label_placeholder":"\u8D26\u53F7\u6807\u7B7E\uFF08\u4F8B\u5982\uFF1A\u5DE5\u4F5C\u8D26\u53F7\uFF09","accounts.env_placeholder":"CLINEBOT_API_KEY_2","accounts.delete_btn":"\u5220\u9664","accounts.delete_confirm":"\u786E\u5B9A\u8981\u5220\u9664\u8D26\u53F7\u201C{label}\u201D\u5417\uFF1F","accounts.save_btn":"\u4FDD\u5B58\u8D26\u53F7","accounts.cancel_btn":"\u53D6\u6D88","accounts.quota_used":"\u5DF2\u7528 {pct}%","accounts.last_failover":"\u6700\u8FD1\u6545\u969C\u8F6C\u79FB\uFF1A{from} \u2192 {to} ({reason})","accounts.mode_label":"\u8C03\u5EA6\u7B56\u7565","accounts.mode_least_used":"\u4F4E\u5360\u7528\u4F18\u5148 (Least Used)","accounts.mode_round_robin":"\u987A\u5E8F\u8F6E\u8BE2 (Round Robin)","accounts.history_show":"\u5386\u53F2","accounts.history_hide":"\u9690\u85CF\u5386\u53F2","accounts.history_title":"\u8D26\u6237\u4E8B\u4EF6\u5386\u53F2","accounts.history_hint":"\u6700\u8FD1\u7684\u51B7\u5374\u3001\u6062\u590D\u548C\u8DEF\u7531\u51B3\u7B56\u3002\u6700\u65B0\u5728\u524D\uFF0C\u6700\u591A 50 \u6761\u3002","accounts.history_empty":"\u6682\u65E0\u8D26\u6237\u4E8B\u4EF6\u3002","accounts.history_loading":"\u6B63\u5728\u52A0\u8F7D\u5386\u53F2\u2026","accounts.history_until":"\u81F3 {time}","accounts.event_cooldown":"\u51B7\u5374","accounts.event_recover":"\u5DF2\u6062\u590D","accounts.event_route":"\u5DF2\u8DEF\u7531","accounts.routes_title":"\u7279\u5B9A\u6A21\u578B\u4E13\u5C5E\u8DEF\u7531 (Per-Model)","accounts.routes_desc":"\u53EF\u9488\u5BF9\u7279\u5B9A\u6A21\u578B\u6307\u5B9A\u7ED1\u5B9A\u7684\u8D26\u53F7\u51ED\u636E\uFF08\u5982\u5C06\u6602\u8D35\u6A21\u578B\u6307\u5411\u7279\u5B9A\u8D26\u53F7\uFF09\u3002","accounts.route_model_placeholder":"\u6A21\u578B ID (\u4F8B\u5982 cline-pass/deepseek-v4-pro)","accounts.route_account_placeholder":"\u76EE\u6807\u8D26\u53F7","accounts.route_btn_add":"\u6DFB\u52A0\u8DEF\u7531","accounts.actions":"\u8D26\u6237\u64CD\u4F5C","accounts.configured":"\u5DF2\u914D\u7F6E","accounts.missing_key":"\u7F3A\u5C11\u5BC6\u94A5","accounts.source":"\u6765\u6E90\uFF1A{source}","accounts.quota_5h":"5\u5C0F\u65F6 {pct}%","accounts.rename":"\u91CD\u547D\u540D","accounts.rename_save":"\u4FDD\u5B58\u540D\u79F0","accounts.rename_placeholder":"\u8D26\u6237\u540D\u79F0","accounts.clear_key":"\u6E05\u9664\u5DF2\u5B58\u5BC6\u94A5","accounts.clear_confirm":"\u6E05\u9664 {name} \u7684\u5DF2\u5B58\u5BC6\u94A5\uFF1F\u8D26\u6237\u884C\u4F1A\u4FDD\u7559\u3002","accounts.remove_confirm":"\u4ECE\u6C60\u4E2D\u79FB\u9664 {name}\uFF1F","accounts.confirm":"\u786E\u8BA4","accounts.cancel":"\u53D6\u6D88","accounts.unpin":"\u53D6\u6D88\u56FA\u5B9A","accounts.no_accounts":"\u5C1A\u672A\u914D\u7F6E\u8D26\u6237\u3002","accounts.route_remove":"\u79FB\u9664\u8DEF\u7531","quota.title":"ClinePass \u7528\u91CF\u914D\u989D\u4E0E\u9650\u989D\u76D1\u63A7","quota.desc":"\u6765\u81EA ClinePass \u5B98\u65B9\u7684\u5B9E\u65F6\u6EDA\u52A8\u7A97\u53E3\u7528\u91CF\u9650\u989D","quota.account":"\u8D26\u53F7\uFF1A{email} \xB7 \u5957\u9910\uFF1A{plan}","quota.refresh":"\u5237\u65B0\u914D\u989D","quota.refreshing":"\u6B63\u5728\u5237\u65B0\u2026","quota.refreshed_msg":"\u8BA2\u9605\u7528\u91CF\u914D\u989D\u5DF2\u5237\u65B0","quota.window_5h":"\u23F1 5 \u5C0F\u65F6\u6EDA\u52A8\u9650\u989D","quota.window_weekly":"\u6BCF\u5468\u6EDA\u52A8\u7A97\u53E3","quota.used":"\u5DF2\u4F7F\u7528 {pct}%","quota.reset_at":"\u91CD\u7F6E\u65F6\u95F4\uFF1A{time}","quota.window_monthly":"\u6BCF\u6708\u6EDA\u52A8\u7A97\u53E3","quota.countdown":"{time}\u540E\u91CD\u7F6E","quota.resetting":"\u6B63\u5728\u91CD\u7F6E","quota.warning_low":"\u989D\u5EA6\u4F4E\u4E8E 10%\uFF0C\u8BF7\u6C42\u53EF\u80FD\u5F88\u5FEB\u4F1A\u53D7\u5230\u9650\u5236\u3002","quota.forecast_no_usage":"\u5C1A\u672A\u4F7F\u7528","quota.forecast_holds":"\u6309\u5F53\u524D\u901F\u5EA6\u53EF\u575A\u6301\u5230\u91CD\u7F6E","quota.forecast_left":"\u6309\u5F53\u524D\u901F\u5EA6\u7EA6\u5269 {time}","quota.forecast_exhausted":"\u5DF2\u8017\u5C3D","quota.exhausted_5h":"5 \u5C0F\u65F6\u6EDA\u52A8\u9650\u989D\u5DF2\u63A5\u8FD1\u8017\u5C3D\uFF08\u5DF2\u7528 {percentUsed}%\uFF09\u3002\u5728\u989D\u5EA6\u91CD\u7F6E\u524D\u65B0\u8BF7\u6C42\u53EF\u80FD\u4F1A\u88AB\u62D2\u7EDD\u3002","quota.warning_5h":"\u4F4E\u989D\u5EA6\u8B66\u544A\uFF1A5 \u5C0F\u65F6\u9650\u989D\u5DF2\u6D88\u8017 {percentUsed}%\uFF08\u5269\u4F59\u4E0D\u8DB3 10%\uFF09\u3002","quota.warning_weekly":"\u5468\u9650\u989D\u504F\u4F4E\u8B66\u544A\uFF1A\u5DF2\u6D88\u8017 {percentUsed}%\uFF08\u5269\u4F59 {remainingPercent}%\uFF09\u3002","quota.warning_monthly":"\u6708\u9650\u989D\u504F\u4F4E\u8B66\u544A\uFF1A\u5DF2\u6D88\u8017 {percentUsed}%\uFF08\u5269\u4F59 {remainingPercent}%\uFF09\u3002","models.reasoning_tag":"\u6DF1\u5EA6\u601D\u8003","models.effort_low":"\u4F4E","models.effort_medium":"\u4E2D","models.effort_high":"\u9AD8","models.effort_max":"\u6700\u9AD8","models.caps_vision":"\u89C6\u89C9","models.caps_general":"\u901A\u7528","models.caps_coding":"\u7F16\u7A0B","diag.smoke_result":"\u5EF6\u8FDF\uFF1A{latency} ms | \u6A21\u578B\uFF1A{model}\n\u54CD\u5E94\uFF1A{preview}","diag.empty":"(\u7A7A)","ui.error_title":"ClineBot \u754C\u9762\u9519\u8BEF\uFF1A","ui.retry":"\u91CD\u8BD5","update.check_failed":"\u68C0\u67E5\u66F4\u65B0\u5931\u8D25","update.retry":"\u91CD\u8BD5\u68C0\u67E5","models.filter_by_plan":"\u9690\u85CF\u672A\u8BA2\u9605\u6A21\u578B","models.filter_by_plan_hint":"\u5728\u804A\u5929\u6A21\u578B\u9009\u62E9\u5668\u4E2D\u81EA\u52A8\u9690\u85CF\u5F53\u524D ClinePass \u8BA2\u9605\u5957\u9910\u672A\u5305\u542B\u7684\u6A21\u578B\u3002","models.visible_models":"\u804A\u5929\u53EF\u89C1\u6A21\u578B","models.visible_models_hint":"\u9009\u62E9\u5728 DSH \u804A\u5929\u9009\u62E9\u5668\u4E2D\u663E\u793A\u7684\u6A21\u578B\uFF08\u4E3A\u7A7A\u65F6\u663E\u793A\u5168\u90E8\u53EF\u7528\u6A21\u578B\uFF09\u3002","models.in_plan":"\u5957\u9910\u5185","models.out_of_plan":"\u5957\u9910\u5916","models.group_plan":"\u5957\u9910\u5185\u6A21\u578B","models.group_other":"\u5176\u4ED6\u6A21\u578B","models.group_custom":"\u81EA\u5B9A\u4E49\u6A21\u578B","models.plan_only":"\u4EC5\u5957\u9910\u6A21\u578B","models.show_all":"\u663E\u793A\u5168\u90E8","models.clean_stale":"\u6E05\u7406\u8FC7\u671F ({count})","models.models_selected":"\u5DF2\u9009\u62E9 {selected} / {total} \u4E2A\u6A21\u578B\u7528\u4E8E\u804A\u5929","models.title":"DSH \u6A21\u578B\u9009\u62E9\u5217\u8868\uFF08\u5DF2\u542F\u7528 {enabled}/{total}\uFF09","models.desc":"\u6A21\u578B\u76EE\u5F55\u4E0E ClinePass \u5B98\u65B9\u8BA2\u9605\u5B9E\u65F6\u540C\u6B65\u3002\u65B0\u8BA2\u9605\u6A21\u578B\u9ED8\u8BA4\u81EA\u52A8\u542F\u7528\u3002\u53D6\u6D88\u52FE\u9009\u53EF\u5728 DSH \u5BF9\u8BDD\u6A21\u578B\u5217\u8868\u4E2D\u9690\u85CF\u4E0D\u9700\u8981\u7684\u6A21\u578B\u3002\u652F\u6301\u5728\u5BF9\u8BDD\u8F93\u5165\u6846\u4E0A\u65B9\u5207\u6362\u63A8\u7406\u601D\u8003\u5F3A\u5EA6\uFF08Effort\uFF09\u3002","models.reasoning_tooltip":"\u652F\u6301\u5728\u5BF9\u8BDD\u6846\u4E0A\u65B9\u6A21\u578B\u83DC\u5355\u4E2D\u9009\u62E9\u601D\u8003\u5F3A\u5EA6\uFF08Off / Low / Medium / High / Max\uFF09","models.sync":"\u540C\u6B65\u8BA2\u9605\u6A21\u578B","models.syncing":"\u6B63\u5728\u540C\u6B65\u2026","models.synced_msg":"\u5DF2\u6210\u529F\u540C\u6B65\u8BA2\u9605\u6A21\u578B\uFF1A\u5171 {total} \u4E2A\uFF08\u53D1\u73B0 {discovered} \u4E2A\u65B0\u6A21\u578B\uFF09","models.verified":"\u5DF2\u4E0E\u5957\u9910\u540C\u6B65","models.unverified":"\u672A\u540C\u6B65\uFF08\u5907\u7528\u76EE\u5F55\uFF09","models.synced_at":"\u540C\u6B65\u65F6\u95F4\uFF1A{date}","models.search_placeholder":"\u6309\u540D\u79F0\u3001ID \u6216\u63CF\u8FF0\u641C\u7D22\u6A21\u578B\u2026","models.filter_all":"\u5168\u90E8","models.filter_vision":"\u4EC5\u89C6\u89C9","models.filter_coding":"\u4EC5\u7F16\u7A0B","models.filter_recommended":"\u63A8\u8350","models.filter_disabled":"\u5DF2\u7981\u7528","models.all":"\u5168\u90E8\u6A21\u578B","models.vision":"\u4EC5\u89C6\u89C9 (Vision)","models.coding":"\u4EC5\u7F16\u7A0B (Coding)","models.recommended":"\u63A8\u8350\u6A21\u578B","models.th_active":"\u542F\u7528","models.th_name":"\u6A21\u578B\u540D\u79F0","models.th_id":"\u6A21\u578B\u6807\u8BC6\u7B26 (ID)","models.th_ctx":"\u4E0A\u4E0B\u6587\u7A97\u53E3","models.th_caps":"\u7279\u6027\u80FD\u529B","models.th_route":"\u6307\u5B9A\u8DEF\u7531\u8D26\u53F7","models.route_auto":"\u81EA\u52A8 / \u9ED8\u8BA4\u6C60","models.btn_probe":"\u6D4B\u8BD5\u5EF6\u8FDF","models.probing":"\u6D4B\u8BD5\u4E2D\u2026","models.probe_result":"\u9996\u5B57\u5EF6\u8FDF {ms} ms","models.probe_failed":"\u6D4B\u8BD5\u5931\u8D25\uFF1A{error}","models.visible_apply":"\u5E94\u7528\u53EF\u89C1\u6A21\u578B","models.visible_applying":"\u5E94\u7528\u4E2D\u2026","models.visible_applied":"\u53EF\u89C1\u6A21\u578B\u5DF2\u5E94\u7528","models.visible_failed":"\u5E94\u7528\u5931\u8D25\uFF1A{error}","models.star":"\u63A8\u8350","models.auto_new":"\u65B0\u4E0A\u7EBF","models.apply_original_all":"\u539F\u5382\u89C4\u683C","models.reset_contexts_all":"\u21BA \u91CD\u7F6E\u4E0A\u4E0B\u6587","models.context_updated_msg":"\u6A21\u578B\u4E0A\u4E0B\u6587\u9650\u5236\u5DF2\u66F4\u65B0\u5E76\u540C\u6B65\u751F\u6548\u81F3 DSH\u3002","models.ctx_default":"\u9ED8\u8BA4 ({ctx} / {max})","models.ctx_original":"\u539F\u5382 ({ctx} / {max} \xB7 {provider})","models.ctx_edit":"\u7F16\u8F91\u9650\u5236","models.ctx_window_label":"\u4E0A\u4E0B\u6587 (\u5982 200000)","models.ctx_max_tokens_label":"\u6700\u5927\u8F93\u51FA (\u5982 16384)","models.ctx_apply_all":"\u5168\u90E8","models.ctx_apply_all_title":"\u5C06\u6B64\u9650\u5236\u6279\u91CF\u5E94\u7528\u5230\u6240\u6709\u6A21\u578B","models.ctx_custom_placeholder":"Token \u6570\u91CF (\u5982 2000000)","models.ctx_save":"\u4FDD\u5B58","models.ctx_cancel":"\u53D6\u6D88","models.custom_ctx_label":"\u4E0A\u4E0B\u6587","models.custom_max_label":"\u6700\u5927\u8F93\u51FA","models.ctx_tag_original":"\u539F\u5382","models.ctx_tag_custom":"\u81EA\u5B9A\u4E49","stats.title":"\u5F53\u524D DSH \u4F1A\u8BDD\u8BF7\u6C42\u9065\u6D4B","stats.desc":"\u672C\u5730\u5B9E\u65F6\u76D1\u63A7\u8BF7\u6C42\u6B21\u6570\u3001\u7F51\u7EDC\u5EF6\u8FDF\u4EE5\u53CA\u901A\u8FC7 ClinePass \u6D88\u8017\u7684\u4F30\u7B97 Token \u6570\u91CF\u3002","stats.requests":"\u6210\u529F / \u603B\u8BF7\u6C42\u6B21\u6570","stats.tokens":"\u4F1A\u8BDD\u7D2F\u8BA1 Token \u4F30\u7B97","stats.reasoning_tokens":"\u6DF1\u5EA6\u601D\u8003 Token \u6D88\u8017","stats.export_csv":"\u5BFC\u51FA CSV","stats.export_json":"\u5BFC\u51FA JSON","stats.latency":"\u6700\u8FD1\u4E00\u6B21\u8BF7\u6C42\u5EF6\u8FDF","stats.last_req":"\u6700\u8FD1\u8BF7\u6C42\u65F6\u95F4","models.custom_add_title":"\u81EA\u5B9A\u4E49\u6A21\u578B","models.custom_id":"\u6A21\u578B ID (\u4F8B\u5982 anthropic/claude-3-7-sonnet)","models.custom_name":"\u663E\u793A\u540D\u79F0 (\u53EF\u9009)","models.custom_btn_add":"\u6DFB\u52A0\u6A21\u578B","models.custom_btn_del":"\u5220\u9664","models.effort_auto":"\u81EA\u52A8\u601D\u8003\u5F3A\u5EA6","stats.errors429":"429 \u9650\u6D41\u4FDD\u62A4\u6B21\u6570","stats.top_models":"Token \u6D88\u8017\u6700\u9AD8\u6A21\u578B","stats.top_accounts":"Token \u6D88\u8017\u6700\u9AD8\u8D26\u6237","stats.cache_saved":"\u7F13\u5B58\u8282\u7701\uFF1A\u2248 ${amount}","stats.reset_btn":"\u91CD\u7F6E\u7EDF\u8BA1","stats.confirm_reset":"\u786E\u8BA4\u91CD\u7F6E\uFF1F","stats.cancel_reset":"\u53D6\u6D88","diag.title":"\u8BCA\u65AD\u5DE5\u5177\u4E0E\u670D\u52A1\u63A5\u5165","diag.desc":"\u914D\u7F6E API \u5BC6\u94A5\u540E\uFF0CClineBot \u4F1A\u81EA\u52A8\u5411 DSH \u6CE8\u518C\u6A21\u578B\u670D\u52A1\u3002\u60A8\u53EF\u4EE5\u5728\u6B64\u8FDB\u884C\u8FDE\u901A\u6027\u6D4B\u8BD5\u3002","diag.smoke_btn":"\u6267\u884C\u8FDE\u901A\u6027\u5192\u70DF\u6D4B\u8BD5 (Ping)","diag.smoke_testing":"\u6B63\u5728\u6D4B\u8BD5\u2026","update.available":"\u53D1\u73B0\u65B0\u7248\u672C v{latestVersion}\uFF08\u5F53\u524D\u7248\u672C v{currentVersion}\uFF09","update.btn":"\u7ACB\u5373\u66F4\u65B0","update.updating":"\u6B63\u5728\u66F4\u65B0\u2026","update.done":"\u66F4\u65B0\u6210\u529F\u81F3 v{version}\uFF01\u8BF7\u91CD\u542F DSH \u670D\u52A1\u751F\u6548\u3002","update.failed":"\u66F4\u65B0\u5931\u8D25\uFF1A{error}","update.checking":"\u6B63\u5728\u68C0\u67E5\u66F4\u65B0\u2026","update.up_to_date":"\u5DF2\u662F\u6700\u65B0\u7248\u672C","sidebar.quota_title":"Cline \u989D\u5EA6","sidebar.show_quota":"\u5728\u4FA7\u8FB9\u680F\u663E\u793A\u989D\u5EA6\u6761","sidebar.show_quota_hint":"\u5728 DSH \u4FA7\u8FB9\u680F\u5E95\u90E8\u5E38\u9A7B\u663E\u793A\u989D\u5EA6 mini \u8FDB\u5EA6\u6307\u793A\u5668\u3002","dashboard.title":"Cline \u989D\u5EA6\u4E0E\u72B6\u6001","dashboard.subtitle":"\u5B9E\u65F6\u8BA2\u9605\u989D\u5EA6\u3001\u6ED1\u52A8\u7A97\u53E3\u9650\u5236\u4E0E\u8D26\u6237\u6C60\u72B6\u6001\u3002","dashboard.close":"\u5173\u95ED\u9762\u677F","dashboard.refresh":"\u5237\u65B0","dashboard.refreshing":"\u6B63\u5728\u5237\u65B0\u2026","dashboard.rolling_window":"5\u5C0F\u65F6\u6ED1\u52A8\u7A97\u53E3","dashboard.weekly_window":"\u6BCF\u5468\u914D\u989D\u7A97\u53E3","dashboard.remaining":"\u5269\u4F59","dashboard.used":"\u5DF2\u7528","dashboard.account_pool":"\u5F53\u524D\u627F\u8F7D\u8D26\u6237","dashboard.credits":"\u70B9\u6570\u4F59\u989D","dashboard.updated_at":"\u66F4\u65B0\u4E8E","composer.cost_title":"ClineBot \u4F1A\u8BDD\u6210\u672C","composer.last_request":"\u6700\u8FD1\u4E00\u6B21\u8BF7\u6C42","composer.free":"\u514D\u8D39","composer.cost_breakdown":"Token \u4E0E\u8D39\u7528\u660E\u7EC6","composer.prompt_tok":"\u63D0\u793A\u8BCD Token","composer.completion_tok":"\u8865\u5168 Token","composer.reasoning_tok":"\u601D\u8003\u63A8\u7406 Token","composer.est_cost":"\u9884\u4F30\u82B1\u8D39","composer.unpriced":"\u65E0\u4EF7\u683C","composer.cache_read":"\u7F13\u5B58\u8BFB\u53D6","composer.cache_write":"\u7F13\u5B58\u5199\u5165","composer.price_source":"\u4EF7\u683C\u6765\u6E90","composer.price_estimate":"\u4F30\u7B97","composer.price_override":"\u8986\u76D6","composer.price_public":"\u516C\u5F00\u76EE\u5F55","discovery.title":"\u53D1\u73B0\u672C\u5730\u5BC6\u94A5","discovery.import_btn":"\u4ECE {source} \u5BFC\u5165","discovery.imported":"\u5DF2\u6210\u529F\u5BFC\u5165\u8D26\u6237\u6C60","discovery.scanning":"\u6B63\u5728\u626B\u63CF\u672C\u5730 Cline \u5BC6\u94A5\u2026","settings.unsaved":"{count} \u9879\u672A\u4FDD\u5B58\u66F4\u6539","settings.saving":"\u6B63\u5728\u4FDD\u5B58\u2026","settings.saved":"\u8BBE\u7F6E\u5DF2\u4FDD\u5B58","settings.save_failed":"\u65E0\u6CD5\u4FDD\u5B58\u8BBE\u7F6E","settings.save":"\u4FDD\u5B58","settings.discard":"\u653E\u5F03","settings.save_invalid_base_url":"Base URL \u5FC5\u987B\u4F7F\u7528 https:// \u6216 localhost\u3002","settings.save_invalid_number":"\u8D85\u65F6\u548C\u8D1F\u8F7D\u9650\u5236\u5FC5\u987B\u662F\u6B63\u6570\u3002","settings.models_group":"\u6A21\u578B\u4E0E\u4E0A\u4E0B\u6587","settings.models_group_hint":"\u5957\u9910\u8FC7\u6EE4\u3001\u53EF\u89C1\u6A21\u578B\u3001\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u8F93\u51FA\u4E0A\u9650\u548C\u63A8\u7406\u9ED8\u8BA4\u503C\u3002","settings.integrations":"\u96C6\u6210\u4E0E\u663E\u793A","settings.integrations_desc":"\u804A\u5929\u8BF7\u6C42\u4E4B\u5916\u590D\u7528\u6B64\u670D\u52A1\u5546\u7684\u754C\u9762\u5165\u53E3\u3002","settings.web_search":"\u4F7F\u7528 ClineBot \u63D0\u4F9B\u7F51\u9875\u641C\u7D22","settings.web_search_hint":"\u4E3A DSH web_search \u5DE5\u5177\u4F7F\u7528 ClinePass \u7AEF\u70B9\u3002","settings.sidebar_quota":"\u5728\u4FA7\u8FB9\u680F\u663E\u793A\u914D\u989D","settings.sidebar_quota_hint":"\u5728\u8BBE\u7F6E\u4E0A\u65B9\u663E\u793A\u7D27\u51D1\u7684\u5957\u9910\u4E0E\u6EDA\u52A8\u7A97\u53E3\u914D\u989D\u3002","settings.composer_cost":"\u5728\u8F93\u5165\u6846\u663E\u793A\u4F1A\u8BDD\u6210\u672C","settings.composer_cost_hint":"\u5728\u8F93\u5165\u6846\u65C1\u663E\u793A\u9884\u4F30 token \u6210\u672C\u3002","settings.advanced":"\u9AD8\u7EA7","settings.advanced_desc":"\u8FDE\u63A5\u3001\u8D85\u65F6\u548C\u5B58\u50A8\u8DEF\u5F84\u3002","settings.base_url":"API \u57FA\u7840\u5730\u5740","settings.base_url_hint":"ClinePass API \u6839\u5730\u5740\u3002\u8FDC\u7A0B\u7AEF\u70B9\u5FC5\u987B\u4F7F\u7528 https://\u3002","settings.timeout":"\u8FDE\u63A5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09","settings.timeout_hint":"\u7B49\u5F85\u4E0A\u6E38\u54CD\u5E94\u5934\u7684\u6700\u5927\u65F6\u95F4\u3002","settings.smoke_timeout":"\u70DF\u96FE\u6D4B\u8BD5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09","settings.smoke_timeout_hint":"\u7B49\u5F85\u8BCA\u65AD\u804A\u5929\u8865\u5168\u7684\u6700\u5927\u65F6\u95F4\u3002","settings.stream_idle":"\u6D41\u7A7A\u95F2\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09","settings.stream_idle_hint":"SSE \u6570\u636E\u5757\u4E4B\u95F4\u7684\u6700\u5927\u95F4\u9694\uFF0C\u8D85\u8FC7\u540E\u89C6\u4E3A\u65AD\u6D41\u3002","settings.max_payload":"\u591A\u6A21\u6001\u8D1F\u8F7D\u4E0A\u9650\uFF08MB\uFF09","settings.max_payload_hint":"\u8BF7\u6C42\u8D85\u8FC7\u9884\u7B97\u65F6\u4F1A\u6DD8\u6C70\u6700\u65E7\u7684\u56FE\u7247\u3002","settings.stats_retention":"\u7EDF\u8BA1\u4FDD\u7559\u5929\u6570","settings.stats_retention_hint":"\u8D85\u8FC7\u8BE5\u5929\u6570\u7684\u65E5/\u5C0F\u65F6/\u4F1A\u8BDD\u9065\u6D4B\u4F1A\u88AB\u6E05\u7406\u30020 \u8868\u793A\u6C38\u4E45\u4FDD\u7559\u3002","settings.daily_budget":"\u6BCF\u65E5\u9884\u7B97\uFF08USD\uFF09","settings.daily_budget_hint":"\u6309\u5F53\u524D UTC \u65E5\u4F30\u7B97\u7684\u82B1\u8D39\u4E0A\u9650\u30020 \u8868\u793A\u5173\u95ED\u3002","settings.monthly_budget":"\u6BCF\u6708\u9884\u7B97\uFF08USD\uFF09","settings.monthly_budget_hint":"\u6309\u5F53\u524D UTC \u6708\u4F30\u7B97\u7684\u82B1\u8D39\u4E0A\u9650\u30020 \u8868\u793A\u5173\u95ED\u3002","settings.stats_path":"\u7EDF\u8BA1\u6587\u4EF6\u8DEF\u5F84","settings.stats_path_hint":"\u6301\u4E45\u5316 JSON \u9065\u6D4B\u8DEF\u5F84\uFF08\u652F\u6301 ~/\uFF09\u3002","settings.models_cache_path":"\u6A21\u578B\u7F13\u5B58\u8DEF\u5F84","settings.models_cache_path_hint":"\u79BB\u7EBF\u6A21\u578B\u76EE\u5F55\u7F13\u5B58\uFF08\u652F\u6301 ~/\uFF09\u3002","providerCard.title":"ClineBot\uFF08ClinePass\uFF09","providerCard.hint":"\u7C98\u8D34 ClinePass API \u5BC6\u94A5\uFF0C\u6216\u524D\u5F80\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u7BA1\u7406\u5B8C\u6574\u8D26\u6237\u6C60\u3002","providerCard.keyPresent":"\u5BC6\u94A5\u5DF2\u914D\u7F6E","providerCard.keyMissing":"\u7F3A\u5C11\u5BC6\u94A5","providerCard.routeActive":"\u8DEF\u7531\u5DF2\u542F\u7528","providerCard.placeholder":"\u7C98\u8D34 API \u5BC6\u94A5","providerCard.replace":"\u66FF\u6362 API \u5BC6\u94A5","providerCard.save":"\u4FDD\u5B58\u5BC6\u94A5","providerCard.saving":"\u6B63\u5728\u4FDD\u5B58\u2026","providerCard.saved":"\u5BC6\u94A5\u5DF2\u4FDD\u5B58\u5E76\u9A8C\u8BC1","providerCard.failed":"\u65E0\u6CD5\u4FDD\u5B58\u5BC6\u94A5","providerCard.show":"\u663E\u793A","providerCard.hide":"\u9690\u85CF","providerCard.openSettings":"\u524D\u5F80\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u7BA1\u7406\u8D26\u6237\u3001\u6A21\u578B\u548C\u914D\u989D\u3002","dashboard.tab_quota":"\u914D\u989D","dashboard.tab_analytics":"\u5206\u6790","dashboard.auto_account":"\u81EA\u52A8\uFF08\u6700\u5C11\u4F7F\u7528\uFF09","analytics.title":"\u7528\u91CF\u5206\u6790","analytics.requests":"\u8BF7\u6C42\u6570","analytics.tokens":"Token","analytics.cost":"\u9884\u4F30\u6210\u672C","analytics.success":"\u6210\u529F\u7387","analytics.ttft":"\u5E73\u5747\u9996\u5B57\u5EF6\u8FDF","analytics.loading":"\u6B63\u5728\u52A0\u8F7D\u5206\u6790\u2026","analytics.no_data":"\u6682\u65E0\u5206\u6790\u6570\u636E\uFF0C\u8BF7\u5148\u53D1\u9001\u51E0\u6761\u6D88\u606F\u3002","analytics.unpriced":"{count} \u4E2A\u8BF7\u6C42\u6CA1\u6709\u4EF7\u683C\u4F30\u7B97\uFF0C\u672A\u8BA1\u5165\u6210\u672C\u5408\u8BA1\u3002","analytics.group_model":"\u6A21\u578B","analytics.group_day":"\u65E5\u671F","analytics.group_account":"\u8D26\u6237","analytics.group_session":"\u4F1A\u8BDD","analytics.cache_saved":"\u7F13\u5B58\u8282\u7701","analytics.heatmap":"\u8BF7\u6C42\u70ED\u529B\u56FE","analytics.heatmap_hint":"\u6700\u8FD1 7 \u5929\u6BCF\u5C0F\u65F6\u7684\u8BF7\u6C42\u6570\u3002","analytics.heatmap_less":"\u5C11","analytics.heatmap_more":"\u591A","analytics.heatmap_empty":"\u6682\u65E0\u6309\u5C0F\u65F6\u6570\u636E\u3002\u53D1\u9001\u4E00\u6761\u6D88\u606F\u540E\u5F00\u59CB\u8BB0\u5F55\u3002","analytics.input":"\u8F93\u5165","analytics.output":"\u8F93\u51FA","analytics.reasoning":"\u63A8\u7406","analytics.cache_read":"\u7F13\u5B58\u8BFB\u53D6","analytics.cache_write":"\u7F13\u5B58\u5199\u5165","analytics.avg_ttft":"\u5E73\u5747\u9996\u5B57\u5EF6\u8FDF","analytics.avg_latency":"\u5E73\u5747\u5EF6\u8FDF","analytics.errors":"\u9519\u8BEF","analytics.p50_latency":"P50 \u5EF6\u8FDF","analytics.p95_latency":"P95 \u5EF6\u8FDF","analytics.heatmap_selected":"{day} {hour}:00 \u2014 {count} \u6B21\u8BF7\u6C42\u3002\u518D\u6B21\u70B9\u51FB\u53EF\u6E05\u9664\u3002","analytics.no_key":"\u5C1A\u672A\u914D\u7F6E ClinePass \u5BC6\u94A5\u3002","analytics.no_key_hint":"\u6253\u5F00\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u7C98\u8D34\u5BC6\u94A5\uFF0C\u6216\u5BFC\u5165\u5DF2\u53D1\u73B0\u7684\u672C\u5730\u5BC6\u94A5\u3002","analytics.health_error":"ClinePass \u7AEF\u70B9\u4E0D\u53EF\u8FBE\u3002","analytics.not_registered":"ClineBot \u63D0\u4F9B\u5546\u672A\u5728 DSH \u4E2D\u6CE8\u518C\u3002","analytics.not_registered_hint":"\u6253\u5F00\u201C\u8BBE\u7F6E \u2192 ClineBot\u201D\u68C0\u67E5\u8BCA\u65AD\u3002","analytics.stale":"\u6B63\u5728\u663E\u793A\u4E0A\u6B21\u6210\u529F\u83B7\u53D6\u7684\u6570\u636E\u3002","analytics.range":"\u8303\u56F4","analytics.range_24h":"24 \u5C0F\u65F6","analytics.range_7":"7 \u5929","analytics.range_30":"30 \u5929","analytics.range_90":"90 \u5929","analytics.from":"\u5F00\u59CB","analytics.to":"\u7ED3\u675F","analytics.model_filter":"\u6309\u6A21\u578B\u7B5B\u9009","analytics.account_filter":"\u6309\u8D26\u6237\u7B5B\u9009","analytics.all_models":"\u6240\u6709\u6A21\u578B","analytics.all_accounts":"\u6240\u6709\u8D26\u6237","analytics.errors_only":"\u4EC5\u9519\u8BEF","analytics.heatmap_all_models":"\u6240\u6709\u6A21\u578B","analytics.heatmap_peak":"\u5CF0\u503C","analytics.heatmap_by_hour":"\u6309\u5C0F\u65F6","analytics.heatmap_model":"\u70ED\u529B\u56FE\u6A21\u578B","quota.no_windows":"\u672A\u8FD4\u56DE\u914D\u989D\u7A97\u53E3\u3002","prices.title":"\u4EF7\u683C\u8986\u76D6","prices.desc":"\u6BCF 100 \u4E07 token \u7684\u7F8E\u5143\u4EF7\u683C\u3002\u7559\u7A7A\u4F7F\u7528\u672C\u5730\u4F30\u7B97\u3002","prices.show":"\u663E\u793A\u4EF7\u683C","prices.hide":"\u9690\u85CF\u4EF7\u683C","prices.model":"\u6A21\u578B","prices.input":"\u8F93\u5165","prices.output":"\u8F93\u51FA","prices.cache_read":"\u7F13\u5B58\u8BFB\u53D6","prices.cache_write":"\u7F13\u5B58\u5199\u5165","prices.auto":"\u81EA\u52A8","prices.empty":"\u6682\u65E0\u6A21\u578B\u3002","prices.hint":"\u8FD9\u4E9B\u4EF7\u683C\u53EA\u5F71\u54CD\u8F93\u5165\u6846\u6210\u672C\u548C\u672C\u5730\u5206\u6790\uFF0C\u4E0D\u662F\u8D26\u5355\u3002","prices.auto_sync":"\u65B0\u6A21\u578B\u81EA\u52A8\u540C\u6B65\u516C\u5F00\u4EF7\u683C","prices.auto_sync_hint":"\u4EC5\u5F53\u65B0\u53D1\u73B0\u7684\u6A21\u578B\u6CA1\u6709\u4EF7\u683C\u65F6\u8BBF\u95EE models.dev\uFF1B\u5173\u95ED\u540E\u624B\u52A8\u540C\u6B65\u4ECD\u53EF\u7528\u3002","prices.public_hint":"\u540C\u6B65\u4F7F\u7528\u516C\u5F00\u7684 models.dev \u76EE\u5F55\u4F5C\u4E3A\u4F30\u7B97\uFF1B\u624B\u52A8\u8986\u76D6\u59CB\u7EC8\u4F18\u5148\u3002","prices.sync":"\u540C\u6B65\u516C\u5F00\u4EF7\u683C","prices.syncing":"\u6B63\u5728\u540C\u6B65\u2026","prices.synced":"\u516C\u5F00\u76EE\u5F55\u5DF2\u540C\u6B65\uFF1A{count} \u4E2A\u6A21\u578B","prices.sync_failed":"\u4EF7\u683C\u540C\u6B65\u5931\u8D25\uFF1A{error}"};
19
19
 
20
20
  // --- src/client/primitives.js ---
21
21
  let primitives=null;try{primitives=require("@deepseek-ai/dsh-client-ui-primitives")||null}catch(_){primitives=null}const Switch=primitives&&primitives.Switch||null;const Modal=primitives&&primitives.Modal||null;function BooleanControl(props){const{checked,disabled,label,onChange}=props;if(Switch){return React.createElement(Switch,{checked:!!checked,disabled:!!disabled,label,onChange:next=>{if(typeof onChange==="function")onChange(next)}})}return React.createElement("input",{type:"checkbox",style:{cursor:disabled?"not-allowed":"pointer",width:"16px",height:"16px",flex:"none"},checked:!!checked,disabled:!!disabled,"aria-label":label,onChange:e=>{if(typeof onChange==="function")onChange(e.target.checked)}})}
@@ -429,13 +429,16 @@ function QuotaSection({keyPresent,status,usage,busy,handleRefreshQuota,showSideb
429
429
  function CustomModelsSub({customModels=[],onConfigUpdate,busy,t}){const[newId,setNewId]=React.useState("");const[newName,setNewName]=React.useState("");const[newCtx,setNewCtx]=React.useState("");const[newMax,setNewMax]=React.useState("");const[isExpanded,setIsExpanded]=React.useState(false);function handleAdd(){const trimmedId=newId.trim();const trimmedName=newName.trim()||trimmedId;if(!trimmedId)return;const parsedCtx=parseInt(newCtx,10);const parsedMax=parseInt(newMax,10);const nextList=[...customModels.filter(m=>m.id!==trimmedId),{id:trimmedId,name:trimmedName,contextLength:parsedCtx>0?parsedCtx:128e3,maxTokens:parsedMax>0?parsedMax:8192,category:"general",isCustom:true}];if(typeof onConfigUpdate==="function"){onConfigUpdate({customModels:nextList})}setNewId("");setNewName("");setNewCtx("");setNewMax("")}function handleDelete(id){const nextList=customModels.filter(m=>m.id!==id);if(typeof onConfigUpdate==="function"){onConfigUpdate({customModels:nextList})}}return React.createElement("div",{style:{marginTop:"16px",paddingTop:"14px",borderTop:"1px solid var(--dsw-alias-border-l2)"}},React.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",cursor:"pointer"},onClick:()=>setIsExpanded(!isExpanded)},React.createElement("span",{style:{fontWeight:500,fontSize:"13px",color:"var(--dsw-alias-label-primary)"}},React.createElement(CbIcon,{name:"plus",size:13}),` ${t("models.custom_add_title")} (${customModels.length})`),React.createElement("span",{style:{fontSize:"12px",color:"var(--dsw-alias-label-secondary)"}},React.createElement(CbIcon,{name:"chevron",size:13,className:isExpanded?"cb-icon cb-icon-chevron-open":"cb-icon"}))),isExpanded?React.createElement("div",{style:{marginTop:"12px",display:"flex",flexDirection:"column",gap:"10px"}},React.createElement("div",{style:{display:"flex",gap:"8px",flexWrap:"wrap"}},React.createElement("input",{type:"text",className:"cb-input",style:{flex:"2 1 140px"},placeholder:t("models.custom_id"),value:newId,disabled:!!busy,onChange:e=>setNewId(e.target.value)}),React.createElement("input",{type:"text",className:"cb-input",style:{flex:"2 1 140px"},placeholder:t("models.custom_name"),value:newName,disabled:!!busy,onChange:e=>setNewName(e.target.value)}),React.createElement("input",{type:"number",className:"cb-input",style:{flex:"1 1 90px"},placeholder:t("models.custom_ctx_label"),title:t("models.custom_ctx_label"),value:newCtx,disabled:!!busy,onChange:e=>setNewCtx(e.target.value)}),React.createElement("input",{type:"number",className:"cb-input",style:{flex:"1 1 80px"},placeholder:t("models.custom_max_label"),title:t("models.custom_max_label"),value:newMax,disabled:!!busy,onChange:e=>setNewMax(e.target.value)}),React.createElement("button",{type:"button",className:"cb-btn cb-btn-primary",disabled:!newId.trim()||!!busy,onClick:handleAdd},t("models.custom_btn_add"))),customModels.length>0?React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"6px",marginTop:"6px"}},customModels.map(cm=>React.createElement("div",{key:cm.id,style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"6px 10px",background:"var(--dsw-alias-bg-layer-2)",border:"1px solid var(--dsw-alias-border-l2)",borderRadius:"6px",fontSize:"12px"}},React.createElement("div",null,React.createElement("span",{style:{fontWeight:500}},cm.name||cm.id),React.createElement("span",{style:{color:"var(--dsw-alias-label-secondary)",marginLeft:"8px"}},`(${cm.id})`),React.createElement("span",{style:{color:"var(--dsw-alias-label-secondary)",marginLeft:"8px",fontSize:"11px"}},`[${cm.contextLength||128e3} / ${cm.maxTokens||8192}]`)),React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"2px 8px",fontSize:"11px"},disabled:!!busy,onClick:()=>handleDelete(cm.id)},t("models.custom_btn_del"))))):null):null)}
430
430
 
431
431
  // --- src/client/components/visible-models.js ---
432
- function formatModelLimits(num){num=Number(num)||0;if(num>=1e6){const m=num/1e6;return`${m%1===0?m:m.toFixed(1)}M`}if(num>=1e3){const k=num/1e3;return`${k%1===0?k:k.toFixed(1)}K`}return String(num)}function VisibleModelsSub({modelsList=[],visibleModels=[],filterModelsByPlan=true,onConfigUpdate,busy,t}){const[open,setOpen]=React.useState(false);const[search,setSearch]=React.useState("");const allModelIds=modelsList.map(m=>m.id);const allModelIdSet=new Set(allModelIds);const staleIds=Array.isArray(visibleModels)?visibleModels.filter(id=>!allModelIdSet.has(id)):[];const isAllVisible=!Array.isArray(visibleModels)||visibleModels.length===0;const count=isAllVisible?modelsList.length:visibleModels.length;function handleToggleModel(modelId){if(typeof onConfigUpdate!=="function")return;if(isAllVisible){const next=allModelIds.filter(id=>id!==modelId);onConfigUpdate({visibleModels:next})}else{let next;if(visibleModels.includes(modelId)){next=visibleModels.filter(id=>id!==modelId)}else{next=[...visibleModels,modelId]}onConfigUpdate({visibleModels:next})}}function handleSelectOnlyPlan(){if(typeof onConfigUpdate!=="function")return;const planOnly=modelsList.filter(m=>m.inPlan).map(m=>m.id);onConfigUpdate({visibleModels:planOnly.length>0?planOnly:allModelIds})}function handleShowAll(){if(typeof onConfigUpdate!=="function")return;onConfigUpdate({visibleModels:[]})}function handleCleanStale(){if(typeof onConfigUpdate!=="function")return;const cleaned=visibleModels.filter(id=>allModelIdSet.has(id));onConfigUpdate({visibleModels:cleaned})}const filtered=modelsList.filter(m=>{if(!search.trim())return true;const q=search.trim().toLowerCase();return m.name&&m.name.toLowerCase().includes(q)||m.id&&m.id.toLowerCase().includes(q)||m.category&&m.category.toLowerCase().includes(q)});return React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"10px",padding:"12px 14px",border:"1px solid var(--dsw-alias-border-l2)",borderRadius:"8px",background:"var(--dsw-alias-bg-layer-2)",marginBottom:"14px"}},React.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:"16px"}},React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"10px",minWidth:0}},React.createElement("span",{style:{fontSize:"14px",lineHeight:"20px",color:"var(--dsw-alias-label-primary)"}},t("models.filter_by_plan")),React.createElement(BooleanControl,{checked:!!filterModelsByPlan,disabled:!!busy,label:t("models.filter_by_plan"),onChange:next=>onConfigUpdate&&onConfigUpdate({filterModelsByPlan:next})})),React.createElement("div",{style:{display:"flex",gap:"6px",alignItems:"center",flexWrap:"wrap"}},!isAllVisible?React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 8px",fontSize:"12px"},disabled:!!busy,onClick:handleShowAll},t("models.show_all")):null,React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 8px",fontSize:"12px"},disabled:!!busy,onClick:handleSelectOnlyPlan},t("models.plan_only")),staleIds.length>0?React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 8px",fontSize:"12px",color:"var(--dsw-alias-state-warn-primary)"},disabled:!!busy,onClick:handleCleanStale},t("models.clean_stale",{count:staleIds.length})):null,React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 10px",fontSize:"12px"},onClick:()=>setOpen(!open)},React.createElement(CbIcon,{name:"chevron",size:12,className:open?"cb-icon cb-icon-chevron-open":"cb-icon"}),` ${t("models.models_selected",{selected:count,total:modelsList.length})}`))),React.createElement("div",{style:{fontSize:"12px",color:"var(--dsw-alias-label-secondary)",marginTop:"-4px"}},t("models.filter_by_plan_hint")),open?React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"8px",paddingTop:"8px",borderTop:"1px solid var(--dsw-alias-border-l2)"}},React.createElement("input",{type:"text",className:"cb-input",style:{height:"30px",fontSize:"12px"},placeholder:t("models.search_placeholder"),value:search,onChange:e=>setSearch(e.target.value)}),React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"4px",maxHeight:"240px",overflowY:"auto",paddingRight:"4px"}},filtered.map(m=>{const isChecked=isAllVisible||visibleModels.includes(m.id);const ctxStr=`${formatModelLimits(m.contextLength)} / ${formatModelLimits(m.maxTokens)}`;return React.createElement("label",{key:m.id,style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"5px 8px",borderRadius:"6px",background:isChecked?"var(--dsw-alias-bg-layer-3)":"transparent",cursor:"pointer",fontSize:"12px",opacity:m.inPlan?1:.65}},React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px",flex:1,minWidth:0}},React.createElement("input",{type:"checkbox",checked:isChecked,onChange:()=>handleToggleModel(m.id)}),React.createElement("span",{style:{fontWeight:"500",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},m.name),React.createElement("span",{style:{color:"var(--dsw-alias-label-secondary)",fontSize:"11px"}},`(${m.id})`)),React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"6px"}},React.createElement("span",{className:"cb-badge",style:{fontSize:"11px",padding:"2px 6px",fontWeight:"600"},title:"Context Window / Max Output Tokens"},ctxStr),m.inPlan?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{fontSize:"10px",padding:"1px 6px"}},t("models.in_plan")):React.createElement("span",{className:"cb-badge",style:{fontSize:"10px",padding:"1px 6px",color:"var(--dsw-alias-label-secondary)"}},t("models.out_of_plan")),!m.isContextOverridden?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{fontSize:"10px",padding:"1px 5px"}},t("models.ctx_tag_original")):null))}))):null)}
432
+ function formatModelLimits(num){num=Number(num)||0;if(num>=1e6){const m=num/1e6;return`${m%1===0?m:m.toFixed(1)}M`}if(num>=1e3){const k=num/1e3;return`${k%1===0?k:k.toFixed(1)}K`}return String(num)}function VisibleModelsSub({modelsList=[],visibleModels=[],filterModelsByPlan=true,onConfigUpdate,onApplyVisible,busy,t}){const[open,setOpen]=React.useState(false);const[search,setSearch]=React.useState("");const[applyState,setApplyState]=React.useState({busy:false,message:""});const allModelIds=modelsList.map(m=>m.id);const allModelIdSet=new Set(allModelIds);const staleIds=Array.isArray(visibleModels)?visibleModels.filter(id=>!allModelIdSet.has(id)):[];const isAllVisible=!Array.isArray(visibleModels)||visibleModels.length===0;const count=isAllVisible?modelsList.length:visibleModels.length;function handleToggleModel(modelId){if(typeof onConfigUpdate!=="function")return;if(isAllVisible){const next=allModelIds.filter(id=>id!==modelId);onConfigUpdate({visibleModels:next})}else{let next;if(visibleModels.includes(modelId)){next=visibleModels.filter(id=>id!==modelId)}else{next=[...visibleModels,modelId]}onConfigUpdate({visibleModels:next})}}function handleSelectOnlyPlan(){if(typeof onConfigUpdate!=="function")return;const planOnly=modelsList.filter(m=>m.inPlan).map(m=>m.id);onConfigUpdate({visibleModels:planOnly.length>0?planOnly:allModelIds})}function handleShowAll(){if(typeof onConfigUpdate!=="function")return;onConfigUpdate({visibleModels:[]})}function handleCleanStale(){if(typeof onConfigUpdate!=="function")return;const cleaned=visibleModels.filter(id=>allModelIdSet.has(id));onConfigUpdate({visibleModels:cleaned})}async function applyVisible(){if(typeof onApplyVisible!=="function")return;setApplyState({busy:true,message:""});try{await onApplyVisible({visibleModels:isAllVisible?[]:visibleModels,filterModelsByPlan});setApplyState({busy:false,message:t("models.visible_applied")})}catch(err){setApplyState({busy:false,message:t("models.visible_failed",{error:String(err?.message||err)})})}}const filtered=modelsList.filter(m=>{if(!search.trim())return true;const q=search.trim().toLowerCase();return m.name&&m.name.toLowerCase().includes(q)||m.id&&m.id.toLowerCase().includes(q)||m.category&&m.category.toLowerCase().includes(q)});return React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"10px",padding:"12px 14px",border:"1px solid var(--dsw-alias-border-l2)",borderRadius:"8px",background:"var(--dsw-alias-bg-layer-2)",marginBottom:"14px"}},React.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",flexWrap:"wrap",gap:"16px"}},React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"10px",minWidth:0}},React.createElement("span",{style:{fontSize:"14px",lineHeight:"20px",color:"var(--dsw-alias-label-primary)"}},t("models.filter_by_plan")),React.createElement(BooleanControl,{checked:!!filterModelsByPlan,disabled:!!busy,label:t("models.filter_by_plan"),onChange:next=>onConfigUpdate&&onConfigUpdate({filterModelsByPlan:next})})),React.createElement("div",{style:{display:"flex",gap:"6px",alignItems:"center",flexWrap:"wrap"}},!isAllVisible?React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 8px",fontSize:"12px"},disabled:!!busy,onClick:handleShowAll},t("models.show_all")):null,React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 8px",fontSize:"12px"},disabled:!!busy,onClick:handleSelectOnlyPlan},t("models.plan_only")),staleIds.length>0?React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 8px",fontSize:"12px",color:"var(--dsw-alias-state-warn-primary)"},disabled:!!busy,onClick:handleCleanStale},t("models.clean_stale",{count:staleIds.length})):null,React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 10px",fontSize:"12px"},disabled:!!busy||applyState.busy,onClick:applyVisible},applyState.busy?t("models.visible_applying"):t("models.visible_apply")),React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"4px 10px",fontSize:"12px"},onClick:()=>setOpen(!open)},React.createElement(CbIcon,{name:"chevron",size:12,className:open?"cb-icon cb-icon-chevron-open":"cb-icon"}),` ${t("models.models_selected",{selected:count,total:modelsList.length})}`))),React.createElement("div",{style:{fontSize:"12px",color:"var(--dsw-alias-label-secondary)",marginTop:"-4px"}},t("models.filter_by_plan_hint")),applyState.message?React.createElement("div",{className:"cb-hint",style:{fontSize:"11px"}},applyState.message):null,open?React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"8px",paddingTop:"8px",borderTop:"1px solid var(--dsw-alias-border-l2)"}},React.createElement("input",{type:"text",className:"cb-input",style:{height:"30px",fontSize:"12px"},placeholder:t("models.search_placeholder"),value:search,onChange:e=>setSearch(e.target.value)}),React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"4px",maxHeight:"240px",overflowY:"auto",paddingRight:"4px"}},filtered.map(m=>{const isChecked=isAllVisible||visibleModels.includes(m.id);const ctxStr=`${formatModelLimits(m.contextLength)} / ${formatModelLimits(m.maxTokens)}`;return React.createElement("label",{key:m.id,style:{display:"flex",alignItems:"center",justifyContent:"space-between",padding:"5px 8px",borderRadius:"6px",background:isChecked?"var(--dsw-alias-bg-layer-3)":"transparent",cursor:"pointer",fontSize:"12px",opacity:m.inPlan?1:.65}},React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"8px",flex:1,minWidth:0}},React.createElement("input",{type:"checkbox",checked:isChecked,onChange:()=>handleToggleModel(m.id)}),React.createElement("span",{style:{fontWeight:"500",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}},m.name),React.createElement("span",{style:{color:"var(--dsw-alias-label-secondary)",fontSize:"11px"}},`(${m.id})`)),React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"6px"}},React.createElement("span",{className:"cb-badge",style:{fontSize:"11px",padding:"2px 6px",fontWeight:"600"},title:"Context Window / Max Output Tokens"},ctxStr),m.inPlan?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{fontSize:"10px",padding:"1px 6px"}},t("models.in_plan")):React.createElement("span",{className:"cb-badge",style:{fontSize:"10px",padding:"1px 6px",color:"var(--dsw-alias-label-secondary)"}},t("models.out_of_plan")),!m.isContextOverridden?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{fontSize:"10px",padding:"1px 5px"}},t("models.ctx_tag_original")):null))}))):null)}
433
433
 
434
434
  // --- src/client/components/provider-card.js ---
435
435
  function ProviderCard(props){const row=props?.provider||{};const providerId=typeof row==="string"?row:row.provider||props?.providerId||"";const settingsNs=row&&typeof row==="object"?row.settingsNs||"":"";if(providerId!=="clinebot"&&settingsNs!==NS)return null;return null}
436
436
 
437
+ // --- src/client/components/models-probe.js ---
438
+ function ModelProbeButton({modelId,busy,t}){const[state,setState]=React.useState({busy:false,result:null,error:""});async function probe(){setState({busy:true,result:null,error:""});try{const response=await fetchWithTimeout(`${ROUTE_PREFIX}/models/probe`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({modelId})});const data=await response.json().catch(()=>({}));if(!response.ok||!data.ok)throw new Error(data.error||`HTTP ${response.status}`);setState({busy:false,result:data,error:""})}catch(err){setState({busy:false,result:null,error:String(err?.message||err)})}}return React.createElement("span",{className:"cb-row",style:{gap:"4px",marginTop:"2px"}},React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"1px 6px",fontSize:"10px"},disabled:!!busy||state.busy,title:t("models.btn_probe"),onClick:probe},state.busy?t("models.probing"):t("models.btn_probe")),state.result?React.createElement("span",{className:"cb-hint",style:{fontSize:"10px"}},t("models.probe_result",{ms:state.result.latencyMs??"\u2014"})):null,state.error?React.createElement("span",{className:"cb-hint",style:{fontSize:"10px",color:"var(--dsw-alias-state-error-primary)"}},t("models.probe_failed",{error:state.error})):null)}
439
+
437
440
  // --- src/client/components/models-section.js ---
438
- function ModelsSection({modelsList,disabledSet,enabledCount,keyPresent,busy,handleSyncPlanModels,handleUpdateModelContext,handleSetModelsFilter,handleToggleModel,planSynced,planSyncedAt,defaultModelWarning,customModels,modelReasoningDefaults,visibleModels,filterModelsByPlan,onConfigUpdate,onUpdateReasoningEffort,t}){const syncDateStr=planSyncedAt?new Date(planSyncedAt).toLocaleDateString():"";const[search,setSearch]=React.useState("");const[viewFilter,setViewFilter]=React.useState("all");const[editingModelId,setEditingModelId]=React.useState(null);const[customCtxVal,setCustomCtxVal]=React.useState("");const[customMaxVal,setCustomMaxVal]=React.useState("");function formatCtx(num){num=Number(num)||2e5;if(num>=1e6){const m=num/1e6;return`${m%1===0?m:m.toFixed(1)}M`}if(num>=1e3){const k=num/1e3;return`${k%1===0?k:k.toFixed(1)}K`}return String(num)}const filteredModels=modelsList.filter(m=>{if(viewFilter==="vision"){const hasVision=Boolean(m.input?.includes("image")||m.input?.includes("vision"));if(!hasVision)return false}else if(viewFilter==="coding"){if(m.category!=="coding")return false}else if(viewFilter==="recommended"){if(!m.recommended)return false}else if(viewFilter==="disabled"){if(!disabledSet.has(m.id))return false}if(search.trim()){const q=search.trim().toLowerCase();const name=String(m.name||"").toLowerCase();const id=String(m.id||"").toLowerCase();const desc=String(m.description||"").toLowerCase();if(!name.includes(q)&&!id.includes(q)&&!desc.includes(q))return false}return true});const groupFor=m=>m.isCustom?"custom":m.inPlan?"plan":"other";const orderedModels=[...filteredModels].sort((a,b)=>{const order={plan:0,other:1,custom:2};return order[groupFor(a)]-order[groupFor(b)]});let lastGroup="";return React.createElement("div",{className:"cb-section-card"},defaultModelWarning?React.createElement("div",{className:"cb-alert-bad cb-inline-alert",style:{marginBottom:"12px"}},React.createElement(CbIcon,{name:"warning",size:14}),` ${defaultModelWarning}`):null,React.createElement(VisibleModelsSub,{modelsList,visibleModels,filterModelsByPlan,onConfigUpdate,busy,t}),React.createElement("div",{className:"cb-section-title"},React.createElement("span",null,t("models.title",{enabled:enabledCount,total:modelsList.length}),planSynced?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{marginLeft:"8px",fontSize:"11px"}},t("models.verified")):React.createElement("span",{className:"cb-badge cb-badge-warn",style:{marginLeft:"8px",fontSize:"11px"}},t("models.unverified")),syncDateStr?React.createElement("span",{style:{marginLeft:"8px",fontSize:"11px",color:"var(--dsw-alias-label-secondary)"}},t("models.synced_at",{date:syncDateStr})):null),React.createElement("div",{className:"cb-row"},React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy||!keyPresent,onClick:handleSyncPlanModels},busy==="sync-models"?t("models.syncing"):t("models.sync")),React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy,title:t("models.apply_original_all"),onClick:()=>typeof handleUpdateModelContext==="function"&&handleUpdateModelContext({mode:"all-original"})},t("models.apply_original_all")),React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy,title:t("models.reset_contexts_all"),onClick:()=>typeof handleUpdateModelContext==="function"&&handleUpdateModelContext({mode:"all-default"})},t("models.reset_contexts_all")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("all")},t("models.all")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("vision")},t("models.vision")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("coding")},t("models.coding")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("recommended")},t("models.recommended")))),React.createElement("div",{className:"cb-section-desc"},t("models.desc")),React.createElement("div",{style:{display:"flex",gap:"8px",alignItems:"center",flexWrap:"wrap",marginBottom:"12px"}},React.createElement("input",{type:"text",className:"cb-input",style:{flex:"1 1 200px"},placeholder:t("models.search_placeholder"),value:search,onChange:e=>setSearch(e.target.value)}),React.createElement("div",{className:"cb-row",style:{gap:"4px"}},React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="all"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("all")},t("models.filter_all")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="vision"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("vision")},t("models.filter_vision")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="coding"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("coding")},t("models.filter_coding")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="recommended"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("recommended")},t("models.filter_recommended")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="disabled"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("disabled")},t("models.filter_disabled")))),React.createElement("table",{className:"cb-table"},React.createElement("thead",null,React.createElement("tr",null,React.createElement("th",{style:{width:"40px"}},t("models.th_active")),React.createElement("th",null,t("models.th_name")),React.createElement("th",null,t("models.th_id")),React.createElement("th",null,t("models.th_ctx")),React.createElement("th",null,t("models.th_caps")))),React.createElement("tbody",null,orderedModels.map(m=>{const groupKey=groupFor(m);const showGroup=groupKey!==lastGroup;lastGroup=groupKey;const isEnabled=!disabledSet.has(m.id);const defaultCtx=m.defaultContextLength||m.clineContextLength||2e5;const origCtx=m.originalContextLength||2e5;const defaultMax=m.defaultMaxTokens||8192;const origMax=m.originalMaxTokens||defaultMax;const origProv=m.originalProvider||"Original";const isOverridden=Boolean(m.isContextOverridden);const isOriginal=isOverridden&&(m.contextLength===origCtx&&(m.maxTokens||defaultMax)===origMax);const row=React.createElement("tr",{key:m.id},React.createElement("td",null,React.createElement("input",{type:"checkbox",checked:isEnabled,onChange:()=>handleToggleModel(m.id)})),React.createElement("td",null,React.createElement("strong",null,m.name),m.recommended?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{marginLeft:"6px"}},t("models.star")):null,m.isCustom?React.createElement("span",{className:"cb-badge",style:{marginLeft:"6px",opacity:.8}},t("models.auto_new")):null),React.createElement("td",null,React.createElement("code",null,m.id)),React.createElement("td",null,editingModelId===m.id?React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"6px",background:"var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 4%, transparent))",padding:"6px",borderRadius:"6px",border:"1px solid var(--dsw-alias-border-l2)"}},React.createElement("div",{style:{display:"flex",gap:"4px",flexWrap:"wrap"}},React.createElement("button",{type:"button",className:`cb-btn ${!isOverridden?"cb-btn-active":""}`,style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>{if(typeof handleUpdateModelContext==="function"){handleUpdateModelContext({modelId:m.id,reset:true})}setEditingModelId(null)}},t("models.ctx_default",{ctx:formatCtx(defaultCtx),max:formatCtx(defaultMax)})),React.createElement("button",{type:"button",className:`cb-btn ${isOriginal?"cb-btn-active":""}`,style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>{if(typeof handleUpdateModelContext==="function"){handleUpdateModelContext({modelId:m.id,contextLength:origCtx,maxTokens:origMax})}setEditingModelId(null)}},t("models.ctx_original",{ctx:formatCtx(origCtx),max:formatCtx(origMax),provider:origProv}))),React.createElement("div",{style:{display:"flex",gap:"4px",alignItems:"center",flexWrap:"wrap"}},React.createElement("input",{type:"number",className:"cb-input",style:{height:"24px",fontSize:"11px",padding:"0 6px",width:"90px"},placeholder:t("models.ctx_window_label"),title:t("models.ctx_window_label"),value:customCtxVal,onChange:e=>setCustomCtxVal(e.target.value)}),React.createElement("input",{type:"number",className:"cb-input",style:{height:"24px",fontSize:"11px",padding:"0 6px",width:"75px"},placeholder:t("models.ctx_max_tokens_label"),title:t("models.ctx_max_tokens_label"),value:customMaxVal,onChange:e=>setCustomMaxVal(e.target.value)}),React.createElement("button",{type:"button",className:"cb-btn cb-btn-primary",style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>{const cVal=parseInt(customCtxVal,10);const mVal=parseInt(customMaxVal,10);if(typeof handleUpdateModelContext==="function"){const payload={modelId:m.id};if(cVal>0)payload.contextLength=cVal;if(mVal>0)payload.maxTokens=mVal;handleUpdateModelContext(payload)}setEditingModelId(null)}},t("models.ctx_save")),React.createElement("button",{type:"button",className:"cb-btn",title:t("models.ctx_apply_all_title"),style:{padding:"2px 5px",fontSize:"11px"},onClick:()=>{const cVal=parseInt(customCtxVal,10);const mVal=parseInt(customMaxVal,10);if(typeof handleUpdateModelContext==="function"){const payload={mode:"apply-all"};if(cVal>0)payload.contextLength=cVal;if(mVal>0)payload.maxTokens=mVal;handleUpdateModelContext(payload)}setEditingModelId(null)}},t("models.ctx_apply_all")),React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>setEditingModelId(null)},t("models.ctx_cancel")))):React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"6px"}},React.createElement("span",{style:{fontWeight:"600",cursor:"pointer"},onClick:()=>{setCustomCtxVal(String(m.contextLength||defaultCtx));setCustomMaxVal(String(m.maxTokens||defaultMax));setEditingModelId(m.id)}},`${formatCtx(m.contextLength||defaultCtx)} / ${formatCtx(m.maxTokens||defaultMax)}`),isOverridden?React.createElement("span",{className:isOriginal?"cb-badge cb-badge-ok":"cb-badge cb-badge-warn",style:{fontSize:"10px",padding:"1px 6px",cursor:"pointer"},onClick:()=>{setCustomCtxVal(String(m.contextLength||defaultCtx));setCustomMaxVal(String(m.maxTokens||defaultMax));setEditingModelId(m.id)}},isOriginal?t("models.ctx_tag_original"):t("models.ctx_tag_custom")):null,React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"2px 6px",fontSize:"11px",lineHeight:"1",border:"none",background:"transparent"},title:t("models.ctx_edit"),onClick:()=>{setCustomCtxVal(String(m.contextLength||defaultCtx));setCustomMaxVal(String(m.maxTokens||defaultMax));setEditingModelId(m.id)}},React.createElement(CbIcon,{name:"edit",size:13})))),React.createElement("td",null,(()=>{const hasReasoning=Boolean(Array.isArray(m.reasoningEfforts)&&m.reasoningEfforts.length>0||m.reasoningEfforts&&typeof m.reasoningEfforts==="object"&&Object.keys(m.reasoningEfforts).length>0||m.reasoning||m.category==="reasoning");const category=m.category&&m.category!=="reasoning"?m.category:hasReasoning?null:"general";const categoryLabel=category==="general"?t("models.caps_general"):category==="coding"?t("models.caps_coding"):category;const hasVision=Boolean(m.input?.includes("image")||m.input?.includes("vision"));return React.createElement(React.Fragment,null,category?React.createElement("span",{className:"cb-badge"},categoryLabel):null,hasVision?React.createElement("span",{className:"cb-badge",style:{marginLeft:category?"4px":"0"}},t("models.caps_vision")):null,hasReasoning?React.createElement(React.Fragment,null,React.createElement("span",{className:"cb-badge cb-badge-ok",style:{marginLeft:category||hasVision?"4px":"0"},title:t("models.reasoning_tooltip")},t("models.reasoning_tag")),React.createElement("select",{className:"cb-select",style:{marginLeft:"4px",fontSize:"10px",padding:"1px 3px"},value:modelReasoningDefaults&&modelReasoningDefaults[m.id]||"",disabled:!!busy,onChange:e=>{if(typeof onUpdateReasoningEffort==="function"){onUpdateReasoningEffort(m.id,e.target.value)}}},React.createElement("option",{value:""},t("models.effort_auto")),React.createElement("option",{value:"low"},t("models.effort_low")),React.createElement("option",{value:"medium"},t("models.effort_medium")),React.createElement("option",{value:"high"},t("models.effort_high")),React.createElement("option",{value:"max"},t("models.effort_max")))):null)})()));const groupCount=orderedModels.filter(entry=>groupFor(entry)===groupKey).length;return React.createElement(React.Fragment,{key:`group-wrap-${m.id}`},showGroup?React.createElement("tr",{className:"cb-model-group"},React.createElement("td",{colSpan:5},`${t(`models.group_${groupKey}`)} \xB7 ${groupCount}`)):null,row)}))),typeof CustomModelsSub==="function"?React.createElement(CustomModelsSub,{customModels:customModels||[],onConfigUpdate,busy,t}):null)}
441
+ function ModelsSection({modelsList,disabledSet,enabledCount,keyPresent,busy,handleSyncPlanModels,handleUpdateModelContext,handleSetModelsFilter,handleToggleModel,planSynced,planSyncedAt,defaultModelWarning,customModels,modelReasoningDefaults,visibleModels,filterModelsByPlan,onConfigUpdate,onApplyVisible,onUpdateReasoningEffort,t}){const syncDateStr=planSyncedAt?new Date(planSyncedAt).toLocaleDateString():"";const[search,setSearch]=React.useState("");const[viewFilter,setViewFilter]=React.useState("all");const[editingModelId,setEditingModelId]=React.useState(null);const[customCtxVal,setCustomCtxVal]=React.useState("");const[customMaxVal,setCustomMaxVal]=React.useState("");function formatCtx(num){num=Number(num)||2e5;if(num>=1e6){const m=num/1e6;return`${m%1===0?m:m.toFixed(1)}M`}if(num>=1e3){const k=num/1e3;return`${k%1===0?k:k.toFixed(1)}K`}return String(num)}const filteredModels=modelsList.filter(m=>{if(viewFilter==="vision"){const hasVision=Boolean(m.input?.includes("image")||m.input?.includes("vision"));if(!hasVision)return false}else if(viewFilter==="coding"){if(m.category!=="coding")return false}else if(viewFilter==="recommended"){if(!m.recommended)return false}else if(viewFilter==="disabled"){if(!disabledSet.has(m.id))return false}if(search.trim()){const q=search.trim().toLowerCase();const name=String(m.name||"").toLowerCase();const id=String(m.id||"").toLowerCase();const desc=String(m.description||"").toLowerCase();if(!name.includes(q)&&!id.includes(q)&&!desc.includes(q))return false}return true});const groupFor=m=>m.isCustom?"custom":m.inPlan?"plan":"other";const orderedModels=[...filteredModels].sort((a,b)=>{const order={plan:0,other:1,custom:2};return order[groupFor(a)]-order[groupFor(b)]});let lastGroup="";return React.createElement("div",{className:"cb-section-card"},defaultModelWarning?React.createElement("div",{className:"cb-alert-bad cb-inline-alert",style:{marginBottom:"12px"}},React.createElement(CbIcon,{name:"warning",size:14}),` ${defaultModelWarning}`):null,React.createElement(VisibleModelsSub,{modelsList,visibleModels,filterModelsByPlan,onConfigUpdate,onApplyVisible,busy,t}),React.createElement("div",{className:"cb-section-title"},React.createElement("span",null,t("models.title",{enabled:enabledCount,total:modelsList.length}),planSynced?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{marginLeft:"8px",fontSize:"11px"}},t("models.verified")):React.createElement("span",{className:"cb-badge cb-badge-warn",style:{marginLeft:"8px",fontSize:"11px"}},t("models.unverified")),syncDateStr?React.createElement("span",{style:{marginLeft:"8px",fontSize:"11px",color:"var(--dsw-alias-label-secondary)"}},t("models.synced_at",{date:syncDateStr})):null),React.createElement("div",{className:"cb-row"},React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy||!keyPresent,onClick:handleSyncPlanModels},busy==="sync-models"?t("models.syncing"):t("models.sync")),React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy,title:t("models.apply_original_all"),onClick:()=>typeof handleUpdateModelContext==="function"&&handleUpdateModelContext({mode:"all-original"})},t("models.apply_original_all")),React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy,title:t("models.reset_contexts_all"),onClick:()=>typeof handleUpdateModelContext==="function"&&handleUpdateModelContext({mode:"all-default"})},t("models.reset_contexts_all")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("all")},t("models.all")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("vision")},t("models.vision")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("coding")},t("models.coding")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>handleSetModelsFilter("recommended")},t("models.recommended")))),React.createElement("div",{className:"cb-section-desc"},t("models.desc")),React.createElement("div",{style:{display:"flex",gap:"8px",alignItems:"center",flexWrap:"wrap",marginBottom:"12px"}},React.createElement("input",{type:"text",className:"cb-input",style:{flex:"1 1 200px"},placeholder:t("models.search_placeholder"),value:search,onChange:e=>setSearch(e.target.value)}),React.createElement("div",{className:"cb-row",style:{gap:"4px"}},React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="all"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("all")},t("models.filter_all")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="vision"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("vision")},t("models.filter_vision")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="coding"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("coding")},t("models.filter_coding")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="recommended"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("recommended")},t("models.filter_recommended")),React.createElement("button",{type:"button",className:`cb-btn ${viewFilter==="disabled"?"cb-btn-active":""}`,style:{padding:"4px 8px",fontSize:"11px"},onClick:()=>setViewFilter("disabled")},t("models.filter_disabled")))),React.createElement("table",{className:"cb-table"},React.createElement("thead",null,React.createElement("tr",null,React.createElement("th",{style:{width:"40px"}},t("models.th_active")),React.createElement("th",null,t("models.th_name")),React.createElement("th",null,t("models.th_id")),React.createElement("th",null,t("models.th_ctx")),React.createElement("th",null,t("models.th_caps")))),React.createElement("tbody",null,orderedModels.map(m=>{const groupKey=groupFor(m);const showGroup=groupKey!==lastGroup;lastGroup=groupKey;const isEnabled=!disabledSet.has(m.id);const defaultCtx=m.defaultContextLength||m.clineContextLength||2e5;const origCtx=m.originalContextLength||2e5;const defaultMax=m.defaultMaxTokens||8192;const origMax=m.originalMaxTokens||defaultMax;const origProv=m.originalProvider||"Original";const isOverridden=Boolean(m.isContextOverridden);const isOriginal=isOverridden&&(m.contextLength===origCtx&&(m.maxTokens||defaultMax)===origMax);const row=React.createElement("tr",{key:m.id},React.createElement("td",null,React.createElement("input",{type:"checkbox",checked:isEnabled,onChange:()=>handleToggleModel(m.id)})),React.createElement("td",null,React.createElement("strong",null,m.name),React.createElement(ModelProbeButton,{modelId:m.id,busy,t}),m.recommended?React.createElement("span",{className:"cb-badge cb-badge-ok",style:{marginLeft:"6px"}},t("models.star")):null,m.isCustom?React.createElement("span",{className:"cb-badge",style:{marginLeft:"6px",opacity:.8}},t("models.auto_new")):null),React.createElement("td",null,React.createElement("code",null,m.id)),React.createElement("td",null,editingModelId===m.id?React.createElement("div",{style:{display:"flex",flexDirection:"column",gap:"6px",background:"var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, currentColor 4%, transparent))",padding:"6px",borderRadius:"6px",border:"1px solid var(--dsw-alias-border-l2)"}},React.createElement("div",{style:{display:"flex",gap:"4px",flexWrap:"wrap"}},React.createElement("button",{type:"button",className:`cb-btn ${!isOverridden?"cb-btn-active":""}`,style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>{if(typeof handleUpdateModelContext==="function"){handleUpdateModelContext({modelId:m.id,reset:true})}setEditingModelId(null)}},t("models.ctx_default",{ctx:formatCtx(defaultCtx),max:formatCtx(defaultMax)})),React.createElement("button",{type:"button",className:`cb-btn ${isOriginal?"cb-btn-active":""}`,style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>{if(typeof handleUpdateModelContext==="function"){handleUpdateModelContext({modelId:m.id,contextLength:origCtx,maxTokens:origMax})}setEditingModelId(null)}},t("models.ctx_original",{ctx:formatCtx(origCtx),max:formatCtx(origMax),provider:origProv}))),React.createElement("div",{style:{display:"flex",gap:"4px",alignItems:"center",flexWrap:"wrap"}},React.createElement("input",{type:"number",className:"cb-input",style:{height:"24px",fontSize:"11px",padding:"0 6px",width:"90px"},placeholder:t("models.ctx_window_label"),title:t("models.ctx_window_label"),value:customCtxVal,onChange:e=>setCustomCtxVal(e.target.value)}),React.createElement("input",{type:"number",className:"cb-input",style:{height:"24px",fontSize:"11px",padding:"0 6px",width:"75px"},placeholder:t("models.ctx_max_tokens_label"),title:t("models.ctx_max_tokens_label"),value:customMaxVal,onChange:e=>setCustomMaxVal(e.target.value)}),React.createElement("button",{type:"button",className:"cb-btn cb-btn-primary",style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>{const cVal=parseInt(customCtxVal,10);const mVal=parseInt(customMaxVal,10);if(typeof handleUpdateModelContext==="function"){const payload={modelId:m.id};if(cVal>0)payload.contextLength=cVal;if(mVal>0)payload.maxTokens=mVal;handleUpdateModelContext(payload)}setEditingModelId(null)}},t("models.ctx_save")),React.createElement("button",{type:"button",className:"cb-btn",title:t("models.ctx_apply_all_title"),style:{padding:"2px 5px",fontSize:"11px"},onClick:()=>{const cVal=parseInt(customCtxVal,10);const mVal=parseInt(customMaxVal,10);if(typeof handleUpdateModelContext==="function"){const payload={mode:"apply-all"};if(cVal>0)payload.contextLength=cVal;if(mVal>0)payload.maxTokens=mVal;handleUpdateModelContext(payload)}setEditingModelId(null)}},t("models.ctx_apply_all")),React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"2px 6px",fontSize:"11px"},onClick:()=>setEditingModelId(null)},t("models.ctx_cancel")))):React.createElement("div",{style:{display:"flex",alignItems:"center",gap:"6px"}},React.createElement("span",{style:{fontWeight:"600",cursor:"pointer"},onClick:()=>{setCustomCtxVal(String(m.contextLength||defaultCtx));setCustomMaxVal(String(m.maxTokens||defaultMax));setEditingModelId(m.id)}},`${formatCtx(m.contextLength||defaultCtx)} / ${formatCtx(m.maxTokens||defaultMax)}`),isOverridden?React.createElement("span",{className:isOriginal?"cb-badge cb-badge-ok":"cb-badge cb-badge-warn",style:{fontSize:"10px",padding:"1px 6px",cursor:"pointer"},onClick:()=>{setCustomCtxVal(String(m.contextLength||defaultCtx));setCustomMaxVal(String(m.maxTokens||defaultMax));setEditingModelId(m.id)}},isOriginal?t("models.ctx_tag_original"):t("models.ctx_tag_custom")):null,React.createElement("button",{type:"button",className:"cb-btn",style:{padding:"2px 6px",fontSize:"11px",lineHeight:"1",border:"none",background:"transparent"},title:t("models.ctx_edit"),onClick:()=>{setCustomCtxVal(String(m.contextLength||defaultCtx));setCustomMaxVal(String(m.maxTokens||defaultMax));setEditingModelId(m.id)}},React.createElement(CbIcon,{name:"edit",size:13})))),React.createElement("td",null,(()=>{const hasReasoning=Boolean(Array.isArray(m.reasoningEfforts)&&m.reasoningEfforts.length>0||m.reasoningEfforts&&typeof m.reasoningEfforts==="object"&&Object.keys(m.reasoningEfforts).length>0||m.reasoning||m.category==="reasoning");const category=m.category&&m.category!=="reasoning"?m.category:hasReasoning?null:"general";const categoryLabel=category==="general"?t("models.caps_general"):category==="coding"?t("models.caps_coding"):category;const hasVision=Boolean(m.input?.includes("image")||m.input?.includes("vision"));return React.createElement(React.Fragment,null,category?React.createElement("span",{className:"cb-badge"},categoryLabel):null,hasVision?React.createElement("span",{className:"cb-badge",style:{marginLeft:category?"4px":"0"}},t("models.caps_vision")):null,hasReasoning?React.createElement(React.Fragment,null,React.createElement("span",{className:"cb-badge cb-badge-ok",style:{marginLeft:category||hasVision?"4px":"0"},title:t("models.reasoning_tooltip")},t("models.reasoning_tag")),React.createElement("select",{className:"cb-select",style:{marginLeft:"4px",fontSize:"10px",padding:"1px 3px"},value:modelReasoningDefaults&&modelReasoningDefaults[m.id]||"",disabled:!!busy,onChange:e=>{if(typeof onUpdateReasoningEffort==="function"){onUpdateReasoningEffort(m.id,e.target.value)}}},React.createElement("option",{value:""},t("models.effort_auto")),React.createElement("option",{value:"low"},t("models.effort_low")),React.createElement("option",{value:"medium"},t("models.effort_medium")),React.createElement("option",{value:"high"},t("models.effort_high")),React.createElement("option",{value:"max"},t("models.effort_max")))):null)})()));const groupCount=orderedModels.filter(entry=>groupFor(entry)===groupKey).length;return React.createElement(React.Fragment,{key:`group-wrap-${m.id}`},showGroup?React.createElement("tr",{className:"cb-model-group"},React.createElement("td",{colSpan:5},`${t(`models.group_${groupKey}`)} \xB7 ${groupCount}`)):null,row)}))),typeof CustomModelsSub==="function"?React.createElement(CustomModelsSub,{customModels:customModels||[],onConfigUpdate,busy,t}):null)}
439
442
 
440
443
  // --- src/client/components/stats-section.js ---
441
444
  function StatsSection({status,onResetStats,busy,t}){const[confirmReset,setConfirmReset]=React.useState(false);const s=status?.statsSummary||{};const totals=s.totals||{};const sess=status?.sessionStats||{};const requests=typeof totals.requests==="number"?totals.requests:typeof s.requests==="number"?s.requests:0;const totalTokens=typeof totals.totalTokens==="number"?totals.totalTokens:typeof s.totalTokens==="number"?s.totalTokens:0;const errors429=typeof totals.rateLimited429==="number"?totals.rateLimited429:typeof s.errors429==="number"?s.errors429:typeof totals.failed==="number"?totals.failed:0;const reasoning=Number(totals.reasoningTokens||sess.reasoningTokensEst||0);const topModels=Object.entries(s.byModel||{}).sort((a,b)=>{const bTokens=Number(b[1]?.totalTokens??b[1]?.tokens??0);const aTokens=Number(a[1]?.totalTokens??a[1]?.tokens??0);return bTokens-aTokens}).slice(0,5);const topAccounts=Object.entries(s.byAccount||{}).sort((a,b)=>Number(b[1]?.totalTokens||0)-Number(a[1]?.totalTokens||0)).slice(0,5);return React.createElement("section",{className:"cb-section-card"},React.createElement("div",{className:"cb-section-head"},React.createElement("div",{className:"cb-section-title"},t("stats.title")),React.createElement("div",{className:"cb-section-actions"},React.createElement("a",{href:"/dsh-clinebot/stats/export?format=json",download:"clinebot-stats.json",className:"cb-btn cb-btn-link"},React.createElement(CbIcon,{name:"download",size:13}),` ${t("stats.export_json")}`),React.createElement("a",{href:"/dsh-clinebot/stats/export",download:"clinebot-stats.csv",className:"cb-btn cb-btn-link"},React.createElement(CbIcon,{name:"download",size:13}),` ${t("stats.export_csv")}`),confirmReset?React.createElement(React.Fragment,null,React.createElement("button",{type:"button",className:"cb-btn cb-btn-warn",disabled:!!busy,onClick:()=>{setConfirmReset(false);if(typeof onResetStats==="function")onResetStats()}},t("stats.confirm_reset")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>setConfirmReset(false)},t("stats.cancel_reset"))):React.createElement("button",{type:"button",className:"cb-btn",disabled:!!busy,onClick:()=>setConfirmReset(true)},t("stats.reset_btn")))),React.createElement("div",{className:"cb-section-desc"},t("stats.desc")),React.createElement("div",{className:"cb-stats-grid"},React.createElement("div",{className:"cb-stat-box"},React.createElement("div",{className:"cb-stat-val"},`${requests} (${sess.successfulRequests||0} sess)`),React.createElement("div",{className:"cb-stat-lbl"},t("stats.requests"))),React.createElement("div",{className:"cb-stat-box"},React.createElement("div",{className:"cb-stat-val"},Number(totalTokens).toLocaleString()),React.createElement("div",{className:"cb-stat-lbl"},reasoning>0?`${t("stats.tokens")} (${reasoning.toLocaleString()} reasoning)`:t("stats.tokens"))),React.createElement("div",{className:"cb-stat-box"},React.createElement("div",{className:"cb-stat-val"},`${errors429}`),React.createElement("div",{className:"cb-stat-lbl"},t("stats.errors429"))),React.createElement("div",{className:"cb-stat-box"},React.createElement("div",{className:"cb-stat-val"},sess.lastLatencyMs?`${sess.lastLatencyMs} ms`:"\u2014"),React.createElement("div",{className:"cb-stat-lbl"},t("stats.latency")))),topModels.length>0?React.createElement("div",{className:"cb-model-list"},React.createElement("div",{className:"cb-model-list-title"},t("stats.top_models")),topModels.map(([modelId,data])=>React.createElement("div",{className:"cb-model-row",key:modelId},React.createElement("span",{className:"cb-mono"},modelId),React.createElement("span",{className:"cb-muted"},`${data.requests||0} reqs \xB7 ${Number(data.totalTokens??data.tokens??0).toLocaleString()} tok${data.reasoningTokens?` (${Number(data.reasoningTokens).toLocaleString()} reasoning)`:""}`)))):null,topAccounts.length>0?React.createElement("div",{className:"cb-model-list"},React.createElement("div",{className:"cb-model-list-title"},t("stats.top_accounts")),topAccounts.map(([accountId,data])=>React.createElement("div",{className:"cb-model-row",key:accountId},React.createElement("span",{className:"cb-mono"},accountId),React.createElement("span",{className:"cb-muted"},`${data.requests||0} reqs \xB7 ${Number(data.totalTokens||0).toLocaleString()} tok \xB7 \u2248 $${Number(data.costUsd||0).toFixed(4)}`)))):null,Number(totals.cacheSavingsUsd||0)>0?React.createElement("div",{className:"cb-hint"},t("stats.cache_saved",{amount:Number(totals.cacheSavingsUsd||0).toFixed(4)})):null)}
@@ -447,7 +450,7 @@ function DiagSection({keyPresent,busy,smokeResult,handleSmoke,t}){return React.c
447
450
  function MiniRing({percent,size=18,warn=false}){const r=7;const c=2*Math.PI*r;const pct=Math.max(0,Math.min(100,Number(percent)||0));const offset=c-pct/100*c;const strokeColor=warn?"var(--dsw-alias-state-error-primary)":"var(--dsw-alias-brand-primary)";return React.createElement("svg",{className:"cb-glyph",width:size,height:size,viewBox:"0 0 20 20",fill:"none"},React.createElement("circle",{cx:10,cy:10,r,stroke:"var(--dsw-alias-bg-layer-2)",strokeWidth:2.5}),React.createElement("circle",{cx:10,cy:10,r,stroke:strokeColor,strokeWidth:2.5,strokeDasharray:c,strokeDashoffset:offset,strokeLinecap:"round",transform:"rotate(-90 10 10)",style:{transition:"stroke-dashoffset 0.3s ease"}}))}function SidebarQuota({ctx,wide,t:propT,open:openPanel}){const t=propT||makeT(en,en);const[data,setData]=React.useState(null);const[open,setOpen]=React.useState(false);const configSnapshot=useConfigFormsSnapshot(ctx);const show=configSnapshot?.value?.showSidebarQuota!==false;const loadQuota=React.useCallback(async()=>{try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/usage`,{cache:"no-store"});if(!res.ok)return;const json=await res.json().catch(()=>null);if(json&&json.ok)setData(json)}catch{}},[]);React.useEffect(()=>{if(!show)return void 0;let timer=null;const scheduleNext=()=>{timer=setTimeout(async()=>{await loadQuota();scheduleNext()},6e4)};loadQuota();scheduleNext();return()=>{if(timer)clearTimeout(timer)}},[loadQuota,show]);if(!show)return null;const windowEntries=[{key:"fiveHour",label:t("quota.window_5h"),value:data?.windows?.fiveHour},{key:"weekly",label:t("quota.window_weekly"),value:data?.windows?.weekly},{key:"monthly",label:t("quota.window_monthly"),value:data?.windows?.monthly}];const windowPct=window=>{const value=Number(window.value?.percentUsed);return Number.isFinite(value)?Math.max(0,Math.min(100,Math.round(value))):null};const measured=windowEntries.map(window=>({...window,pct:windowPct(window)})).filter(window=>window.pct!==null);const pct=measured.length?Math.max(...measured.map(window=>window.pct)):0;const isWarn=measured.some(window=>window.pct>=85);const planName=String(data?.plan||t("title")).replace(/\s*\(\$[^)]*\)\s*$/,"");const accountLabel=data?.account?` \xB7 ${data.account}`:"";const tooltip=`${planName}${accountLabel} \xB7 ${windowEntries.map(window=>{const value=windowPct(window);return`${window.label} ${value===null?"\u2014":`${value}% ${t("dashboard.used")}`}`}).join(" \xB7 ")}`;const handleOpen=()=>{if(typeof openPanel==="function"){try{if(openPanel()!==false)return}catch{}}setOpen(true)};const rows=windowEntries;const dialog=React.createElement(DashboardDialog,{t,open,onClose:()=>setOpen(false)});if(!wide){return React.createElement(React.Fragment,null,React.createElement("button",{type:"button",className:"cb-railButton","aria-label":tooltip,"aria-expanded":open,title:tooltip,onClick:handleOpen},React.createElement(MiniRing,{percent:pct,warn:isWarn,size:20})),dialog)}return React.createElement(React.Fragment,null,React.createElement("button",{type:"button",className:"cb-foot","aria-label":tooltip,"aria-expanded":open,title:tooltip,onClick:handleOpen},React.createElement("div",{className:"cb-footTop"},React.createElement(MiniRing,{percent:pct,warn:isWarn,size:16}),React.createElement("span",{className:"cb-footName"},planName),React.createElement("span",{className:isWarn?"cb-footPct cb-footPctWarn":"cb-footPct"},`${pct}%`)),rows.map(window=>{const value=windowPct(window);const hasData=value!==null;const warn=hasData&&value>=85;const reset=hasData&&typeof formatResetCountdown==="function"?formatResetCountdown(window.value?.resetsAt):"";const resetText=reset?` \xB7 ${reset==="resetting"?t("quota.resetting"):reset}`:"";return React.createElement("div",{className:hasData?"cb-footRow":"cb-footRow cb-footRowMissing",key:window.key},React.createElement("div",{className:"cb-footHead"},React.createElement("span",{className:"cb-footLabel"},window.label),React.createElement("span",{className:hasData?"cb-footAmount":"cb-footAmount cb-footAmountMissing"},hasData?`${100-value}% ${t("dashboard.remaining")}${resetText}`:"\u2014")),React.createElement("span",{className:"cb-footBar"},React.createElement("span",{className:warn?"cb-footFill cb-footFillWarn":"cb-footFill",style:{width:hasData?`${Math.min(100,Math.max(2,value))}%`:"0%"}})),hasData?React.createElement("span",{className:"cb-footForecast"},quotaForecast(window.value,window.key==="fiveHour"?5*60*60*1e3:window.key==="weekly"?7*24*60*60*1e3:30*24*60*60*1e3,t)):null)}),rows.some(window=>windowPct(window)!==null)?null:React.createElement("span",{className:"cb-footMessage"},data?.error||t("quota.no_windows"))),dialog)}
448
451
 
449
452
  // --- src/client/components/price-section.js ---
450
- function PriceSection({modelsList=[],priceOverrides={},onConfigUpdate,t}){const[expanded,setExpanded]=React.useState(false);const[syncState,setSyncState]=React.useState({busy:false,message:""});const models=Array.isArray(modelsList)?modelsList:[];const syncPrices=async()=>{setSyncState({busy:true,message:""});try{const response=await fetchWithTimeout(`${ROUTE_PREFIX}/prices/sync`,{method:"POST"});const data=await response.json().catch(()=>({}));if(!response.ok||!data.ok)throw new Error(data.error||`HTTP ${response.status}`);setSyncState({busy:false,message:t("prices.synced",{count:data.count})})}catch(err){setSyncState({busy:false,message:t("prices.sync_failed",{error:String(err?.message||err)})})}};const update=(modelId,field,raw)=>{const next={...priceOverrides||{}};const current={...next[modelId]||{}};const value=String(raw??"").trim();const numeric=Number(value);if(value===""||!Number.isFinite(numeric)||numeric<0)delete current[field];else current[field]=numeric;if(Object.keys(current).length===0)delete next[modelId];else next[modelId]=current;onConfigUpdate({priceOverrides:next})};return React.createElement("div",{className:"cb-price-section"},React.createElement("div",{className:"cb-price-head"},React.createElement("div",{className:"cb-price-copy"},React.createElement("div",{className:"cb-section-title"},t("prices.title")),React.createElement("div",{className:"cb-section-desc"},t("prices.desc"))),React.createElement("div",{className:"cb-row",style:{gap:"6px"}},React.createElement("button",{type:"button",className:"cb-btn",disabled:syncState.busy,onClick:syncPrices},syncState.busy?t("prices.syncing"):t("prices.sync")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>setExpanded(value=>!value)},expanded?t("prices.hide"):t("prices.show")))),expanded?React.createElement("div",{className:"cb-price-table"},React.createElement("div",{className:"cb-price-head-row"},React.createElement("span",null,t("prices.model")),React.createElement("span",null,t("prices.input")),React.createElement("span",null,t("prices.output")),React.createElement("span",null,t("prices.cache_read")),React.createElement("span",null,t("prices.cache_write"))),models.length===0?React.createElement("div",{className:"cb-hint"},t("prices.empty")):models.map(model=>{const override=priceOverrides?.[model.id]||{};return React.createElement("div",{className:"cb-price-row",key:model.id},React.createElement("span",{className:"cb-mono",title:model.id},model.name||model.id),["input","output","cacheRead","cacheWrite"].map(field=>React.createElement("input",{key:field,className:"cb-input cb-price-input",type:"number",min:"0",step:"0.001",value:override[field]===void 0?"":String(override[field]),placeholder:t("prices.auto"),onChange:event=>update(model.id,field,event.target.value)})))}),React.createElement("div",{className:"cb-hint"},t("prices.public_hint")),React.createElement("div",{className:"cb-hint"},t("prices.hint")),syncState.message?React.createElement("div",{className:"cb-hint"},syncState.message):null):null)}
453
+ function PriceSection({modelsList=[],priceOverrides={},priceAutoSync=true,onConfigUpdate,t}){const[expanded,setExpanded]=React.useState(false);const[syncState,setSyncState]=React.useState({busy:false,message:""});const models=Array.isArray(modelsList)?modelsList:[];const syncPrices=async()=>{setSyncState({busy:true,message:""});try{const response=await fetchWithTimeout(`${ROUTE_PREFIX}/prices/sync`,{method:"POST"});const data=await response.json().catch(()=>({}));if(!response.ok||!data.ok)throw new Error(data.error||`HTTP ${response.status}`);setSyncState({busy:false,message:t("prices.synced",{count:data.count})})}catch(err){setSyncState({busy:false,message:t("prices.sync_failed",{error:String(err?.message||err)})})}};const update=(modelId,field,raw)=>{const next={...priceOverrides||{}};const current={...next[modelId]||{}};const value=String(raw??"").trim();const numeric=Number(value);if(value===""||!Number.isFinite(numeric)||numeric<0)delete current[field];else current[field]=numeric;if(Object.keys(current).length===0)delete next[modelId];else next[modelId]=current;onConfigUpdate({priceOverrides:next})};return React.createElement("div",{className:"cb-price-section"},React.createElement("div",{className:"cb-price-head"},React.createElement("div",{className:"cb-price-copy"},React.createElement("div",{className:"cb-section-title"},t("prices.title")),React.createElement("div",{className:"cb-section-desc"},t("prices.desc")),React.createElement(BooleanControl,{checked:priceAutoSync!==false,label:t("prices.auto_sync"),onChange:next=>onConfigUpdate&&onConfigUpdate({priceAutoSync:next})}),React.createElement("div",{className:"cb-hint"},t("prices.auto_sync_hint"))),React.createElement("div",{className:"cb-row",style:{gap:"6px"}},React.createElement("button",{type:"button",className:"cb-btn",disabled:syncState.busy,onClick:syncPrices},syncState.busy?t("prices.syncing"):t("prices.sync")),React.createElement("button",{type:"button",className:"cb-btn",onClick:()=>setExpanded(value=>!value)},expanded?t("prices.hide"):t("prices.show")))),expanded?React.createElement("div",{className:"cb-price-table"},React.createElement("div",{className:"cb-price-head-row"},React.createElement("span",null,t("prices.model")),React.createElement("span",null,t("prices.input")),React.createElement("span",null,t("prices.output")),React.createElement("span",null,t("prices.cache_read")),React.createElement("span",null,t("prices.cache_write"))),models.length===0?React.createElement("div",{className:"cb-hint"},t("prices.empty")):models.map(model=>{const override=priceOverrides?.[model.id]||{};return React.createElement("div",{className:"cb-price-row",key:model.id},React.createElement("span",{className:"cb-mono",title:model.id},model.name||model.id),["input","output","cacheRead","cacheWrite"].map(field=>React.createElement("input",{key:field,className:"cb-input cb-price-input",type:"number",min:"0",step:"0.001",value:override[field]===void 0?"":String(override[field]),placeholder:t("prices.auto"),onChange:event=>update(model.id,field,event.target.value)})))}),React.createElement("div",{className:"cb-hint"},t("prices.public_hint")),React.createElement("div",{className:"cb-hint"},t("prices.hint")),syncState.message?React.createElement("div",{className:"cb-hint"},syncState.message):null):null)}
451
454
 
452
455
  // --- src/client/components/analytics-heatmap.js ---
453
456
  function heatWeekdayLabels(){return Array.from({length:7},(_,index)=>{const date=new Date(2024,0,1+index);return date.toLocaleDateString(void 0,{weekday:"short"})})}function heatHourLabel(hour){return hour%3===0?String(hour).padStart(2,"0"):""}function HeatmapView({heatmap,model,onModelChange,onSelect,selected,t}){if(!heatmap||!Array.isArray(heatmap.grid)){return React.createElement("div",{className:"cb-hint"},t("analytics.heatmap_empty"))}const grid=heatmap.grid;const hours=Array.isArray(heatmap.hours)?heatmap.hours:Array(24).fill(0);const total=Number(heatmap.total)||0;const models=Array.isArray(heatmap.models)?heatmap.models:[];const max=Math.max(1,...grid.flat());const maxHours=Math.max(1,...hours);const peakHour=hours.reduce((best,value,hour)=>value>(hours[best]||0)?hour:best,0);const peak=hours[peakHour]||0;const level=value=>{const requests=Number(value)||0;if(requests<=0)return 0;return Math.min(4,Math.max(1,Math.ceil(requests/max*4)))};const weekdays=heatWeekdayLabels();return React.createElement("div",{className:"cb-heat-wrap"},React.createElement("div",{className:"cb-heat-toolbar"},React.createElement("span",{className:"cb-badge"},`${total} ${t("analytics.requests")}`),models.length>1?React.createElement("select",{className:"cb-heat-select",value:model||"",title:t("analytics.heatmap_model"),onChange:event=>onModelChange&&onModelChange(event.target.value)},React.createElement("option",{value:""},`${t("analytics.heatmap_all_models")} (${total})`),models.map(entry=>React.createElement("option",{key:entry.model,value:entry.model},`${entry.model} (${entry.total})`))):null,peak>0?React.createElement("span",{className:"cb-hint"},`${t("analytics.heatmap_peak")}: ${String(peakHour).padStart(2,"0")}:00 \xB7 ${peak}`):null),React.createElement("div",{className:"cb-heat-scroll"},React.createElement("div",{className:"cb-heat"},React.createElement("span",{className:"cb-heat-corner"}),hours.map((_,hour)=>React.createElement("span",{className:"cb-heat-hour",key:`hour-${hour}`},heatHourLabel(hour))),weekdays.map((label,day)=>React.createElement(React.Fragment,{key:`day-${day}`},React.createElement("span",{className:"cb-heat-day"},label),(grid[day]||[]).map((value,hour)=>React.createElement("button",{key:`cell-${day}-${hour}`,type:"button",className:`cb-heat-cell cb-heat-l${level(value)}${selected?.day===day&&selected?.hour===hour?" cb-heat-selected":""}`,title:`${label} ${String(hour).padStart(2,"0")}:00 \u2014 ${value} ${t("analytics.requests")}`,"aria-label":`${label} ${String(hour).padStart(2,"0")}:00 \u2014 ${value}`,"aria-pressed":selected?.day===day&&selected?.hour===hour,onClick:()=>onSelect&&onSelect({day,hour,value,label})}))))),React.createElement("p",{className:"cb-hour-strip-head"},t("analytics.heatmap_by_hour")),React.createElement("div",{className:"cb-hour-strip"},React.createElement("span"),hours.map((value,hour)=>React.createElement("span",{className:"cb-hour-col",key:`strip-${hour}`,title:`${String(hour).padStart(2,"0")}:00 \u2014 ${value}`},React.createElement("span",{className:`cb-hour-bar cb-heat-l${level(value)}`,style:{height:`${Math.max(2,Math.round(28*(Number(value)||0)/maxHours))}px`}})))),React.createElement("div",{className:"cb-hour-labels"},React.createElement("span"),hours.map((_,hour)=>React.createElement("span",{key:`label-${hour}`},heatHourLabel(hour))))),React.createElement("div",{className:"cb-heat-legend"},React.createElement("span",null,t("analytics.heatmap_less")),[0,1,2,3,4].map(bucket=>React.createElement("span",{key:bucket,className:`cb-heat-swatch cb-heat-l${bucket}`})),React.createElement("span",null,t("analytics.heatmap_more"))),total===0?React.createElement("div",{className:"cb-hint"},t("analytics.heatmap_empty")):null)}
@@ -465,7 +468,7 @@ function DashboardDialog(props){const t=props?.t||makeT(en,en);const panel=props
465
468
  function formatTokens(count){const n=Number(count)||0;if(n>=1e6)return(n/1e6).toFixed(1)+"M";if(n>=1e3)return(n/1e3).toFixed(1)+"k";return String(n)}function findPrice(modelId,prices){if(!modelId||!Array.isArray(prices))return null;const exact=prices.find(entry=>entry&&entry.id===modelId);if(exact)return exact;return prices.find(entry=>entry&&entry.id&&(modelId===entry.id||modelId.endsWith(`/${entry.id}`)||modelId.includes(entry.id)))||null}function calculateCost(tokens,modelId,prices){const pTok=Number(tokens?.promptTokens||tokens?.inputTokens||0);const cTok=Number(tokens?.completionTokens||tokens?.outputTokens||0);const rTok=Number(tokens?.reasoningTokens||0);const cacheRead=Number(tokens?.cacheReadTokens||tokens?.cacheRead||0);const cacheWrite=Number(tokens?.cacheWriteTokens||tokens?.cacheWrite||0);const total=pTok+cTok+rTok;const price=findPrice(modelId,prices);if(!price)return{cost:0,priced:false,source:null,pTok,cTok,rTok,cacheRead,cacheWrite,total};const uncachedInput=Math.max(0,pTok-cacheRead-cacheWrite);const cost=(uncachedInput*Number(price.input||0)+cTok*Number(price.output||0)+cacheRead*Number(price.cacheRead||0)+cacheWrite*Number(price.cacheWrite||0))/1e6;return{cost,priced:true,source:price.source||"estimate",pTok,cTok,rTok,cacheRead,cacheWrite,total}}const COST_ANCHOR='[data-slot="conversation.composer.dock"]';const STATS_ROOT="[data-composer-stats]";const STATS_USAGE='[data-composer-stat="usage"]';const STATS_ANY="[data-composer-stat]";const DIALOG_TRIGGER='button[aria-haspopup="dialog"]';const MISSES_BEFORE_WARNING=3;function ComposerCostDock(props){if(typeof props?.useProjection!=="function")return null;return React.createElement(ComposerCostEntry,props)}function ComposerCostEntry(props){const t=props?.t||makeT(en,en);const configSnapshot=useConfigFormsSnapshot(props?.ctx);const[stats,setStats]=React.useState(null);const[prices,setPrices]=React.useState([]);const[sessionStats,setSessionStats]=React.useState(null);const markerRef=React.useRef(null);const projectedTokens=props.useProjection("tokenUsage");const selection=props.useProjection("modelSelection");const sessionId=String(props?.sessionId||"");const priceOverridesKey=JSON.stringify(configSnapshot?.value?.priceOverrides||{});React.useEffect(()=>{let active=true;fetchWithTimeout(`${ROUTE_PREFIX}/prices`,{cache:"no-store"}).then(response=>response.json()).then(data=>{if(active&&data?.ok&&Array.isArray(data.models))setPrices(data.models)}).catch(()=>{});return()=>{active=false}},[priceOverridesKey]);React.useEffect(()=>{if(!sessionId){setSessionStats(null);return void 0}let active=true;const loadSession=async()=>{try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/stats/session?sessionId=${encodeURIComponent(sessionId)}`,{cache:"no-store"});const data=await res.json().catch(()=>null);if(active&&data?.ok)setSessionStats(data.session||null)}catch{}};loadSession();let timer=null;const schedule=()=>{timer=setTimeout(async()=>{await loadSession();if(active)schedule()},1e4)};schedule();return()=>{active=false;if(timer)clearTimeout(timer)}},[sessionId,projectedTokens]);React.useEffect(()=>{if(projectedTokens||sessionId)return void 0;let active=true;const loadStats=async()=>{try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/stats`,{cache:"no-store"});if(!res.ok)return;const data=await res.json().catch(()=>null);const totals=data?.stats?.totals||data?.stats?.summary||null;if(active&&totals)setStats(totals)}catch{}};loadStats();const timer=setTimeout(loadStats,3e4);return()=>{active=false;clearTimeout(timer)}},[projectedTokens,sessionId]);const selected=selection?.lastUsed??selection?.next??(selection?.provider||selection?.model?selection:null);const provider=String(selected?.provider||"");const modelId=String(selected?.model||selected?.id||"");const isClineBot=(provider===""||provider==="clinebot"||provider==="llm-clinebot")&&(provider==="clinebot"||provider==="llm-clinebot"||modelId.startsWith("cline-pass/"));const session=sessionStats&&Number(sessionStats.requests||0)>0?sessionStats:null;const sessionTokens=session?{promptTokens:Number(session.promptTokens||0),completionTokens:Number(session.completionTokens||0),reasoningTokens:Number(session.reasoningTokens||0),cacheReadTokens:Number(session.cacheReadTokens||0),cacheWriteTokens:Number(session.cacheWriteTokens||0)}:null;const rawTokens=sessionTokens||(sessionId?null:projectedTokens||{promptTokens:stats?.promptTokens||0,completionTokens:stats?.completionTokens||0,reasoningTokens:stats?.reasoningTokens||0,cacheReadTokens:stats?.cacheReadTokens||0,cacheWriteTokens:stats?.cacheWriteTokens||0});const cacheRead=Number(rawTokens?.cacheReadTokens||0);const cacheWrite=Number(rawTokens?.cacheWriteTokens||0);const uncachedInput=Number(rawTokens?.uncachedInputTokens);const promptTokens=rawTokens?Number.isFinite(uncachedInput)?uncachedInput+cacheRead+cacheWrite:Number(rawTokens.promptTokens||rawTokens.inputTokens||0):0;const tokens={promptTokens,completionTokens:Number(rawTokens?.completionTokens||rawTokens?.outputTokens||0),reasoningTokens:Number(rawTokens?.reasoningTokens||0),cacheReadTokens:cacheRead,cacheWriteTokens:cacheWrite};const calculated=calculateCost(tokens,session?.model||modelId,prices);const cost=session?Number(session.costUsd||0):calculated.cost;const priced=session?Number(session.pricedRequests||0)>0:calculated.priced;const source=session?findPrice(session.model||modelId,prices)?.source||"estimate":calculated.source;const{pTok,cTok,rTok}=calculated;const total=session?Number(session.totalTokens||pTok+cTok):calculated.total;const last=session?.last||null;const lastCost=last?Number(last.costUsd||0):0;const lastPriced=Boolean(last?.priced);const costValue=priced?cost===0?t("composer.free"):cost<1e-4?"< $0.0001":`$${cost.toFixed(4)}`:t("composer.unpriced");const costDisplay=priced&&source!=="override"&&cost>0?`\u2248 ${costValue}`:costValue;const lastCostValue=last?lastPriced?lastCost===0?t("composer.free"):lastCost<1e-4?"< $0.0001":`$${lastCost.toFixed(4)}`:t("composer.unpriced"):"";const title=[`${t("composer.cost_title")} ${costDisplay}`,...last?[`${t("composer.last_request")} ${lastCostValue}`]:[],`${t("composer.prompt_tok")} ${formatTokens(pTok)}`,`${t("composer.completion_tok")} ${formatTokens(cTok)}`,...rTok>0?[`${t("composer.reasoning_tok")} ${formatTokens(rTok)}`]:[],...cacheRead>0?[`${t("composer.cache_read")} ${formatTokens(cacheRead)}`]:[],...cacheWrite>0?[`${t("composer.cache_write")} ${formatTokens(cacheWrite)}`]:[],...priced?[`${t("composer.price_source")} ${source==="override"?t("composer.price_override"):source==="public"?t("composer.price_public"):t("composer.price_estimate")}`]:[]].join(" \xB7 ");const inject=isClineBot&&priced&&total>0;React.useEffect(()=>{if(!inject||typeof document==="undefined")return void 0;const marker=markerRef.current;if(!marker)return void 0;const scope=marker.closest(COST_ANCHOR);if(!scope)return void 0;let host=null;let root=null;let valueNode=null;let misses=0;const apply=()=>{if(!root||root.parentNode===null){const usageAnchor=scope.querySelector(STATS_USAGE)||scope.querySelector(STATS_ANY);const statsRoot=usageAnchor||scope.querySelector(STATS_ROOT)||scope;const triggers=statsRoot.querySelectorAll(DIALOG_TRIGGER);const button=triggers[triggers.length-1]||usageAnchor;if(!button){misses+=1;if(misses===MISSES_BEFORE_WARNING){console.warn("[dsh-clinebot] no native token-usage pill found for the session cost")}return}misses=0;host=button;root=document.createElement("span");root.setAttribute("data-clinebot-session-cost","");root.style.display="inline-flex";root.style.alignItems="center";const separator=document.createElement("span");separator.textContent="\xB7";separator.setAttribute("aria-hidden","true");separator.style.color="var(--dsw-alias-separator-primary)";separator.style.margin="0 6px 0 0";valueNode=document.createElement("span");valueNode.style.fontWeight="500";root.appendChild(separator);root.appendChild(valueNode);button.appendChild(root)}if(root.parentNode!==host)host.appendChild(root);if(valueNode.textContent!==costDisplay)valueNode.textContent=costDisplay;if(root.title!==title)root.title=title};apply();const observer=typeof window!=="undefined"&&typeof window.MutationObserver==="function"?new window.MutationObserver(apply):null;observer?.observe(document.body,{childList:true});let timer=null;const tick=()=>{apply();timer=setTimeout(tick,2e3)};timer=setTimeout(tick,2e3);return()=>{observer?.disconnect();if(timer)clearTimeout(timer);if(root?.parentNode)root.parentNode.removeChild(root)}},[inject,costDisplay,title]);if(configSnapshot?.value?.showComposerCost===false)return null;if(total===0&&!stats)return null;if(!inject)return null;return React.createElement("span",{ref:markerRef,style:{display:"none"},"data-clinebot-cost-anchor":""})}
466
469
 
467
470
  // --- src/client/settings-page.js ---
468
- function SettingsPage(props){const ctx=props?.ctx;const t=props?.t||makeT(en,en);const configSnapshot=useConfigFormsSnapshot(ctx);const[status,setStatus]=React.useState(null);const[draft,setDraft]=React.useState(null);const[busy,setBusy]=React.useState("");const[err,setErr]=React.useState(""),[msg,setMsg]=React.useState("");const[smokeResult,setSmokeResult]=React.useState(null);const[updateState,setUpdateState]=React.useState({status:"idle",checking:false,updating:false,currentVersion:"",latestVersion:"",updateAvailable:false,canAutoUpdate:true,latestCheckFailed:false,lastCheckedAt:null,error:"",notice:""});const[apiKeyInput,setApiKeyInput]=React.useState(""),[showKey,setShowKey]=React.useState(false);React.useEffect(()=>{ensureCss()},[]);const pendingRef=React.useRef({});const load=React.useCallback(async()=>{setErr("");const[res,statsRes]=await Promise.all([fetchWithTimeout(`${ROUTE_PREFIX}/status`,{cache:"no-store"}),fetchWithTimeout(`${ROUTE_PREFIX}/stats`,{cache:"no-store"}).catch(()=>null)]);const data=await res.json().catch(()=>({}));if(!res.ok)throw new Error(data.error||`HTTP ${res.status}`);const rawStats=statsRes&&statsRes.ok?await statsRes.json().catch(()=>null):null;const statsObj=rawStats&&typeof rawStats==="object"&&rawStats.stats?rawStats.stats:rawStats||data.statsSummary||{};setStatus({...data,statsSummary:statsObj});setDraft({...data.config||{},...pendingRef.current||{}})},[]);React.useEffect(()=>{load().catch(e=>setErr(String(e.message||e)))},[load]);const staged=useStagedConfig({status,draft,setDraft,load,t,pendingRef});const modelSelection=useModelSelection({status,draft,setDraft,load,setErr});const checkUpdate=React.useCallback(async()=>{setUpdateState(s=>({...s,checking:true,status:"checking",error:""}));try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/update`);if(!res.ok)throw new Error(`HTTP ${res.status}`);const data=await res.json().catch(()=>({}));if(data.latestCheckFailed){setUpdateState(s=>({...s,checking:false,status:"error",latestCheckFailed:true,currentVersion:data.currentVersion||s.currentVersion,error:t("update.check_failed")}));return}const isAvailable=Boolean(data.updateAvailable);setUpdateState(s=>({...s,checking:false,status:isAvailable?"available":"current",currentVersion:data.currentVersion||s.currentVersion,latestVersion:data.latestVersion||"",updateAvailable:isAvailable,latestCheckFailed:false,lastCheckedAt:Date.now(),canAutoUpdate:data.canAutoUpdate!==false,error:""}))}catch(err2){setUpdateState(s=>({...s,checking:false,status:"error",latestCheckFailed:true,error:String(err2?.message||err2||t("update.check_failed"))}))}},[t]);React.useEffect(()=>{checkUpdate()},[checkUpdate]);async function handleTriggerUpdate(){if(updateState.updating)return;setUpdateState(s=>({...s,updating:true,error:"",notice:""}));try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/update`,{method:"POST",headers:{"x-dsh-plugin-update":"1"}});const data=await res.json().catch(()=>({}));if(!res.ok||data.ok===false||data.error)throw new Error(data.error||`HTTP ${res.status}`);const newVer=data.updatedVersion||updateState.latestVersion||updateState.currentVersion;setUpdateState(s=>({...s,updating:false,status:"current",updateAvailable:false,currentVersion:newVer,lastCheckedAt:Date.now(),notice:t("update.done",{version:newVer})}));setTimeout(()=>checkUpdate(),2e3)}catch(err2){setUpdateState(s=>({...s,updating:false,status:s.updateAvailable?"available":"error",error:t("update.failed",{error:String(err2?.message||err2)})}))}}async function performAction(busyKey,fn){setBusy(busyKey);setErr("");setMsg("");try{await fn()}catch(e){setErr(String(e.message||e))}finally{setBusy("")}}const{handlePinAccount,handleAddAccount,handleDeleteAccount,handleRenameAccount,handleClearKey}=createAccountActions({performAction,load,t,setMsg});async function handleSaveKey(){const keyVal=String(apiKeyInput||"").trim();if(!keyVal){setErr(t("key.empty_err"));return}await performAction("save-key",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/save-key`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:keyVal,apiKeyEnv:draft?.apiKeyEnv})});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setApiKeyInput("");setMsg(t("key.saved_msg",{status:data.validated?"OK":"Notice (check console)"}));await load()})}async function handleRefreshQuota(){await performAction("refresh-quota",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/usage`);const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setStatus(prev=>prev?{...prev,usage:data}:prev);setMsg(t("quota.refreshed_msg"))})}async function handleUpdateModelContext(payload){await performAction("update-context",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/models/context`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload)});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setMsg(t("models.context_updated_msg"));await load()})}async function handleSyncPlanModels(){await performAction("sync-models",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/models/sync`,{method:"POST"});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setMsg(t("models.synced_msg",{total:data.totalModelsCount,discovered:data.discoveredCount}));await load()})}async function handleSmoke(){setSmokeResult(null);await performAction("smoke",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/smoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:draft?.defaultModel})});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setSmokeResult(data)})}const[verifyStatus,setVerifyStatus]=React.useState({state:"idle",email:"",plan:"",error:""});const verifyTimerRef=typeof React.useRef==="function"?React.useRef(null):{current:null};React.useEffect(()=>{const raw=String(apiKeyInput||"").trim();if(!raw||raw.length<10){setVerifyStatus({state:"idle",email:"",plan:"",error:""});return}if(verifyTimerRef.current)clearTimeout(verifyTimerRef.current);verifyTimerRef.current=setTimeout(async()=>{setVerifyStatus({state:"verifying",email:"",plan:"",error:""});try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/key/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:raw})});const data=await res.json().catch(()=>({}));if(data.ok&&data.valid){setVerifyStatus({state:"valid",email:data.email,plan:data.plan,error:""})}else{setVerifyStatus({state:"invalid",email:"",plan:"",error:data.error||"Verification failed"})}}catch(err2){setVerifyStatus({state:"invalid",email:"",plan:"",error:String(err2?.message||err2)})}},500);return()=>{if(verifyTimerRef.current)clearTimeout(verifyTimerRef.current)}},[apiKeyInput]);function handleFastLogin(){window.open("https://app.cline.bot/settings/api-keys","_blank")}async function handleConfigPatch(patch){setDraft(d=>({...d||{},...patch||{}}));await performAction("patch-config",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/config`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({config:patch})});const data=await res.json().catch(()=>({}));if(!res.ok||data.ok===false)throw new Error(data.error||`HTTP ${res.status}`);await load()})}async function handleResetStats(){await performAction("reset-stats",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/stats/reset`,{method:"POST"});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);await load()})}if(!status||!draft){if(err){return React.createElement("div",{className:"cb-page"},React.createElement("div",{className:"cb-alert cb-alert-err"},`${t("settings.loading")}: ${err}`),React.createElement("button",{type:"button",className:"cb-btn",style:{marginTop:"12px"},onClick:()=>{setErr("");load().catch(e=>setErr(String(e.message||e)))}},t("settings.retry")))}return React.createElement("div",{className:"cb-page"},t("settings.loading"))}const healthOk=!!status.health?.ok;const keyPresent=!!status.key?.present;const isRegistered=!!status.isRegistered;const modelsList=status.availableModels||[];const disabledSet=new Set(draft.disabledModels||[]);const enabledCount=modelsList.filter(m=>!disabledSet.has(m.id)).length;const usage=status.usage;return React.createElement("div",{className:"cb-page"},React.createElement("div",{className:"cb-header"},React.createElement("div",{className:"cb-page-title"},t("header.title"),React.createElement("span",{className:`cb-badge ${healthOk?"cb-badge-ok":"cb-badge-bad"}`},healthOk?t("badge.online",{latency:status.health?.latencyMs}):t("badge.offline")),React.createElement("span",{className:`cb-badge ${keyPresent?"cb-badge-ok":"cb-badge-warn"}`},keyPresent?t("badge.key_ok",{source:status.key?.source}):t("badge.key_missing")),React.createElement("span",{className:`cb-badge ${isRegistered?"cb-badge-ok":"cb-badge-warn"}`},isRegistered?t("badge.registered",{count:enabledCount}):t("badge.not_registered"))),React.createElement("div",{className:"cb-page-sub"},t("header.sub"))),React.createElement(UpdateBanner,{updateState,handleTriggerUpdate,handleCheckUpdate:checkUpdate,t}),configSnapshot?.status==="unavailable"?React.createElement("div",{className:"cb-alert-bad",style:{marginBottom:"12px"}},t("settings.unavailable")):null,err?React.createElement("div",{className:"cb-alert-bad"},err):null,msg?React.createElement("div",{className:"cb-alert-ok"},msg):null,React.createElement(KeySection,{keyPresent,apiKeyInput,setApiKeyInput,showKey,setShowKey,busy,draft,handleSaveKey,handleFastLogin,verifyStatus,t}),React.createElement(AccountsSection,{status,busy,handlePinAccount,handleAddAccount,handleDeleteAccount,handleRenameAccount,handleClearKey,accountMode:draft?.accountMode||status?.config?.accountMode||"least_used",handleUpdateAccountMode:mode=>handleConfigPatch({accountMode:mode}),modelRoutes:draft?.modelRoutes||status?.config?.modelRoutes||{},onUpdateModelRoutes:routes=>handleConfigPatch({modelRoutes:routes}),t}),React.createElement(QuotaSection,{keyPresent,status,usage,busy,handleRefreshQuota,showSidebarQuota:draft?.showSidebarQuota!==false,onToggleSidebarQuota:value=>staged.stage({showSidebarQuota:value}),t}),React.createElement(CollapsibleGroup,{title:t("settings.models_group"),description:t("settings.models_group_hint"),badge:`${enabledCount}/${modelsList.length}`,divided:true,defaultOpen:false},React.createElement(ModelsSection,{modelsList,disabledSet,enabledCount,keyPresent,busy,handleSyncPlanModels,handleUpdateModelContext,handleSetModelsFilter:modelSelection.handleSetModelsFilter,handleToggleModel:modelSelection.handleToggleModel,planSynced:!!(status?.config?.planSynced||status?.config?.dynamicModels&&status.config.dynamicModels.length>0),planSyncedAt:status?.config?.planSyncedAt||0,defaultModelWarning:status?.config?.defaultModelWarning||"",customModels:draft?.customModels||[],modelReasoningDefaults:draft?.modelReasoningDefaults||{},visibleModels:draft?.visibleModels||[],filterModelsByPlan:draft?.filterModelsByPlan!==false,onConfigUpdate:staged.stage,onUpdateReasoningEffort:(modelId,effort)=>{const next={...draft?.modelReasoningDefaults||{}};if(!effort)delete next[modelId];else next[modelId]=effort;staged.stage({modelReasoningDefaults:next})},t}),React.createElement(PriceSection,{modelsList,priceOverrides:draft?.priceOverrides||{},onConfigUpdate:staged.stage,t})),React.createElement(StatsSection,{status,onResetStats:handleResetStats,busy,t}),React.createElement(IntegrationsSection,{draft,onStage:staged.stage,t}),React.createElement(DiagSection,{keyPresent,busy,smokeResult,handleSmoke,t}),React.createElement(AdvancedSection,{draft,onStage:staged.stage,t}),React.createElement(SaveBar,{pendingCount:staged.pendingCount,saving:staged.saving,saveState:staged.saveState,saveError:staged.saveError,invalid:staged.invalid,validationError:staged.validationError,onSave:staged.save,onDiscard:staged.discard,t}))}
471
+ function SettingsPage(props){const ctx=props?.ctx;const t=props?.t||makeT(en,en);const configSnapshot=useConfigFormsSnapshot(ctx);const[status,setStatus]=React.useState(null);const[draft,setDraft]=React.useState(null);const[busy,setBusy]=React.useState("");const[err,setErr]=React.useState(""),[msg,setMsg]=React.useState("");const[smokeResult,setSmokeResult]=React.useState(null);const[updateState,setUpdateState]=React.useState({status:"idle",checking:false,updating:false,currentVersion:"",latestVersion:"",updateAvailable:false,canAutoUpdate:true,latestCheckFailed:false,lastCheckedAt:null,error:"",notice:""});const[apiKeyInput,setApiKeyInput]=React.useState(""),[showKey,setShowKey]=React.useState(false);React.useEffect(()=>{ensureCss()},[]);const pendingRef=React.useRef({});const load=React.useCallback(async()=>{setErr("");const[res,statsRes]=await Promise.all([fetchWithTimeout(`${ROUTE_PREFIX}/status`,{cache:"no-store"}),fetchWithTimeout(`${ROUTE_PREFIX}/stats`,{cache:"no-store"}).catch(()=>null)]);const data=await res.json().catch(()=>({}));if(!res.ok)throw new Error(data.error||`HTTP ${res.status}`);const rawStats=statsRes&&statsRes.ok?await statsRes.json().catch(()=>null):null;const statsObj=rawStats&&typeof rawStats==="object"&&rawStats.stats?rawStats.stats:rawStats||data.statsSummary||{};setStatus({...data,statsSummary:statsObj});setDraft({...data.config||{},...pendingRef.current||{}})},[]);React.useEffect(()=>{load().catch(e=>setErr(String(e.message||e)))},[load]);const staged=useStagedConfig({status,draft,setDraft,load,t,pendingRef});const modelSelection=useModelSelection({status,draft,setDraft,load,setErr});const checkUpdate=React.useCallback(async()=>{setUpdateState(s=>({...s,checking:true,status:"checking",error:""}));try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/update`);if(!res.ok)throw new Error(`HTTP ${res.status}`);const data=await res.json().catch(()=>({}));if(data.latestCheckFailed){setUpdateState(s=>({...s,checking:false,status:"error",latestCheckFailed:true,currentVersion:data.currentVersion||s.currentVersion,error:t("update.check_failed")}));return}const isAvailable=Boolean(data.updateAvailable);setUpdateState(s=>({...s,checking:false,status:isAvailable?"available":"current",currentVersion:data.currentVersion||s.currentVersion,latestVersion:data.latestVersion||"",updateAvailable:isAvailable,latestCheckFailed:false,lastCheckedAt:Date.now(),canAutoUpdate:data.canAutoUpdate!==false,error:""}))}catch(err2){setUpdateState(s=>({...s,checking:false,status:"error",latestCheckFailed:true,error:String(err2?.message||err2||t("update.check_failed"))}))}},[t]);React.useEffect(()=>{checkUpdate()},[checkUpdate]);async function handleTriggerUpdate(){if(updateState.updating)return;setUpdateState(s=>({...s,updating:true,error:"",notice:""}));try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/update`,{method:"POST",headers:{"x-dsh-plugin-update":"1"}});const data=await res.json().catch(()=>({}));if(!res.ok||data.ok===false||data.error)throw new Error(data.error||`HTTP ${res.status}`);const newVer=data.updatedVersion||updateState.latestVersion||updateState.currentVersion;setUpdateState(s=>({...s,updating:false,status:"current",updateAvailable:false,currentVersion:newVer,lastCheckedAt:Date.now(),notice:t("update.done",{version:newVer})}));setTimeout(()=>checkUpdate(),2e3)}catch(err2){setUpdateState(s=>({...s,updating:false,status:s.updateAvailable?"available":"error",error:t("update.failed",{error:String(err2?.message||err2)})}))}}async function performAction(busyKey,fn){setBusy(busyKey);setErr("");setMsg("");try{await fn()}catch(e){setErr(String(e.message||e))}finally{setBusy("")}}const{handlePinAccount,handleAddAccount,handleDeleteAccount,handleRenameAccount,handleClearKey}=createAccountActions({performAction,load,t,setMsg});async function handleSaveKey(){const keyVal=String(apiKeyInput||"").trim();if(!keyVal){setErr(t("key.empty_err"));return}await performAction("save-key",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/save-key`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:keyVal,apiKeyEnv:draft?.apiKeyEnv})});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setApiKeyInput("");setMsg(t("key.saved_msg",{status:data.validated?"OK":"Notice (check console)"}));await load()})}async function handleRefreshQuota(){await performAction("refresh-quota",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/usage`);const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setStatus(prev=>prev?{...prev,usage:data}:prev);setMsg(t("quota.refreshed_msg"))})}async function handleUpdateModelContext(payload){await performAction("update-context",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/models/context`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload)});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setMsg(t("models.context_updated_msg"));await load()})}async function handleSyncPlanModels(){await performAction("sync-models",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/models/sync`,{method:"POST"});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setMsg(t("models.synced_msg",{total:data.totalModelsCount,discovered:data.discoveredCount}));await load()})}async function handleApplyVisible(payload){await performAction("visible-models",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/models/visible`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(payload||{})});const data=await res.json().catch(()=>({}));if(!res.ok||data.ok===false)throw new Error(data.error||`HTTP ${res.status}`);await load();setMsg(t("models.visible_applied"))})}async function handleSmoke(){setSmokeResult(null);await performAction("smoke",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/smoke`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:draft?.defaultModel})});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);setSmokeResult(data)})}const[verifyStatus,setVerifyStatus]=React.useState({state:"idle",email:"",plan:"",error:""});const verifyTimerRef=typeof React.useRef==="function"?React.useRef(null):{current:null};React.useEffect(()=>{const raw=String(apiKeyInput||"").trim();if(!raw||raw.length<10){setVerifyStatus({state:"idle",email:"",plan:"",error:""});return}if(verifyTimerRef.current)clearTimeout(verifyTimerRef.current);verifyTimerRef.current=setTimeout(async()=>{setVerifyStatus({state:"verifying",email:"",plan:"",error:""});try{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/key/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:raw})});const data=await res.json().catch(()=>({}));if(data.ok&&data.valid){setVerifyStatus({state:"valid",email:data.email,plan:data.plan,error:""})}else{setVerifyStatus({state:"invalid",email:"",plan:"",error:data.error||"Verification failed"})}}catch(err2){setVerifyStatus({state:"invalid",email:"",plan:"",error:String(err2?.message||err2)})}},500);return()=>{if(verifyTimerRef.current)clearTimeout(verifyTimerRef.current)}},[apiKeyInput]);function handleFastLogin(){window.open("https://app.cline.bot/settings/api-keys","_blank")}async function handleConfigPatch(patch){setDraft(d=>({...d||{},...patch||{}}));await performAction("patch-config",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/config`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({config:patch})});const data=await res.json().catch(()=>({}));if(!res.ok||data.ok===false)throw new Error(data.error||`HTTP ${res.status}`);await load()})}async function handleResetStats(){await performAction("reset-stats",async()=>{const res=await fetchWithTimeout(`${ROUTE_PREFIX}/stats/reset`,{method:"POST"});const data=await res.json().catch(()=>({}));if(!res.ok||!data.ok)throw new Error(data.error||`HTTP ${res.status}`);await load()})}if(!status||!draft){if(err){return React.createElement("div",{className:"cb-page"},React.createElement("div",{className:"cb-alert cb-alert-err"},`${t("settings.loading")}: ${err}`),React.createElement("button",{type:"button",className:"cb-btn",style:{marginTop:"12px"},onClick:()=>{setErr("");load().catch(e=>setErr(String(e.message||e)))}},t("settings.retry")))}return React.createElement("div",{className:"cb-page"},t("settings.loading"))}const healthOk=!!status.health?.ok;const keyPresent=!!status.key?.present;const isRegistered=!!status.isRegistered;const modelsList=status.availableModels||[];const disabledSet=new Set(draft.disabledModels||[]);const enabledCount=modelsList.filter(m=>!disabledSet.has(m.id)).length;const usage=status.usage;return React.createElement("div",{className:"cb-page"},React.createElement("div",{className:"cb-header"},React.createElement("div",{className:"cb-page-title"},t("header.title"),React.createElement("span",{className:`cb-badge ${healthOk?"cb-badge-ok":"cb-badge-bad"}`},healthOk?t("badge.online",{latency:status.health?.latencyMs}):t("badge.offline")),React.createElement("span",{className:`cb-badge ${keyPresent?"cb-badge-ok":"cb-badge-warn"}`},keyPresent?t("badge.key_ok",{source:status.key?.source}):t("badge.key_missing")),React.createElement("span",{className:`cb-badge ${isRegistered?"cb-badge-ok":"cb-badge-warn"}`},isRegistered?t("badge.registered",{count:enabledCount}):t("badge.not_registered"))),React.createElement("div",{className:"cb-page-sub"},t("header.sub"))),React.createElement(UpdateBanner,{updateState,handleTriggerUpdate,handleCheckUpdate:checkUpdate,t}),configSnapshot?.status==="unavailable"?React.createElement("div",{className:"cb-alert-bad",style:{marginBottom:"12px"}},t("settings.unavailable")):null,err?React.createElement("div",{className:"cb-alert-bad"},err):null,msg?React.createElement("div",{className:"cb-alert-ok"},msg):null,React.createElement(KeySection,{keyPresent,apiKeyInput,setApiKeyInput,showKey,setShowKey,busy,draft,handleSaveKey,handleFastLogin,verifyStatus,t}),React.createElement(AccountsSection,{status,busy,handlePinAccount,handleAddAccount,handleDeleteAccount,handleRenameAccount,handleClearKey,accountMode:draft?.accountMode||status?.config?.accountMode||"least_used",handleUpdateAccountMode:mode=>handleConfigPatch({accountMode:mode}),modelRoutes:draft?.modelRoutes||status?.config?.modelRoutes||{},onUpdateModelRoutes:routes=>handleConfigPatch({modelRoutes:routes}),t}),React.createElement(QuotaSection,{keyPresent,status,usage,busy,handleRefreshQuota,showSidebarQuota:draft?.showSidebarQuota!==false,onToggleSidebarQuota:value=>staged.stage({showSidebarQuota:value}),t}),React.createElement(CollapsibleGroup,{title:t("settings.models_group"),description:t("settings.models_group_hint"),badge:`${enabledCount}/${modelsList.length}`,divided:true,defaultOpen:false},React.createElement(ModelsSection,{modelsList,disabledSet,enabledCount,keyPresent,busy,handleSyncPlanModels,handleUpdateModelContext,handleSetModelsFilter:modelSelection.handleSetModelsFilter,handleToggleModel:modelSelection.handleToggleModel,planSynced:!!(status?.config?.planSynced||status?.config?.dynamicModels&&status.config.dynamicModels.length>0),planSyncedAt:status?.config?.planSyncedAt||0,defaultModelWarning:status?.config?.defaultModelWarning||"",customModels:draft?.customModels||[],modelReasoningDefaults:draft?.modelReasoningDefaults||{},visibleModels:draft?.visibleModels||[],filterModelsByPlan:draft?.filterModelsByPlan!==false,onConfigUpdate:staged.stage,onApplyVisible:handleApplyVisible,onUpdateReasoningEffort:(modelId,effort)=>{const next={...draft?.modelReasoningDefaults||{}};if(!effort)delete next[modelId];else next[modelId]=effort;staged.stage({modelReasoningDefaults:next})},t}),React.createElement(PriceSection,{modelsList,priceOverrides:draft?.priceOverrides||{},priceAutoSync:draft?.priceAutoSync!==false,onConfigUpdate:staged.stage,t})),React.createElement(StatsSection,{status,onResetStats:handleResetStats,busy,t}),React.createElement(IntegrationsSection,{draft,onStage:staged.stage,t}),React.createElement(DiagSection,{keyPresent,busy,smokeResult,handleSmoke,t}),React.createElement(AdvancedSection,{draft,onStage:staged.stage,t}),React.createElement(SaveBar,{pendingCount:staged.pendingCount,saving:staged.saving,saveState:staged.saveState,saveError:staged.saveError,invalid:staged.invalid,validationError:staged.validationError,onSave:staged.save,onDiscard:staged.discard,t}))}
469
472
 
470
473
  // --- src/client/entry.js ---
471
474
  function ensureSettingsForm(ctx){const svc=readConfigForms(ctx);if(!svc||typeof svc.get!=="function")return;try{svc.get(NS)}catch(err){console.warn("[dsh-clinebot] configForms.get failed:",err)}}function apply(ctx){ensureCss();const addLocale=(locale,dictionary)=>{try{return ctx.locale.register(NS,locale,dictionary)}catch(err){console.warn(`[dsh-clinebot] Failed to register locale dictionary for ns "${NS}", locale "${locale}":`,err);return()=>{}}};if(ctx.locale&&ctx.locale.register){if(typeof ctx.effect==="function"){ctx.effect(()=>{const undo=[addLocale("en",en),addLocale("zh",zh)];return()=>{for(const off of undo)off()}},"dsh-clinebot: dictionaries")}else{addLocale("en",en);addLocale("zh",zh)}}if(typeof ctx.effect==="function"){ctx.effect(()=>{ensureSettingsForm(ctx)},"dsh-clinebot: open the settings form for this namespace")}function registerSlotWhenReady(slotName,registerFn){if(!ctx.slots)return;if(typeof ctx.slots.inject==="function"){try{ctx.slots.inject(slotName,()=>{try{return registerFn()}catch(err){console.warn("[dsh-clinebot] Error registering slot "+slotName+":",err)}});return}catch(err){console.warn("[dsh-clinebot] Failed to inject slot "+slotName+":",err)}}if(typeof ctx.slots.register==="function"){try{registerFn()}catch(err){console.warn("[dsh-clinebot] Failed direct registration for "+slotName+":",err)}}}function selectLayoutPanel(id){try{const layout=typeof ctx.get==="function"?ctx.get("layout"):ctx.layout;if(!layout||typeof layout.selectPanel!=="function")return false;layout.selectPanel(id);return true}catch(err){console.warn("[dsh-clinebot] layout.selectPanel failed:",err);return false}}const openPanel=()=>selectLayoutPanel("clinebot-panel");const closePanel=()=>{if(selectLayoutPanel(null))return;selectLayoutPanel("conversation")};registerSlotWhenReady("settings.section",()=>ctx.slots.register({name:"settings.section",id:NS,order:30,label:()=>{try{const t=ctx.locale&&typeof ctx.locale.bind==="function"?ctx.locale.bind(NS):null;const label=t?t("nav"):"";return label&&label!=="nav"?label:"ClineBot"}catch(_){return"ClineBot"}},locale:NS,inject:()=>({ctx})},props=>React.createElement(ErrorBoundary,{t:props&&props.t},React.createElement(SettingsPage,{...props,ctx:props&&props.ctx||ctx}))));registerSlotWhenReady("sidebar.footer.action",()=>ctx.slots.register({name:"sidebar.footer.action",id:"clinebot-quota",order:20,locale:NS,inject:()=>({ctx,open:openPanel})},props=>React.createElement(ErrorBoundary,{t:props&&props.t},React.createElement(SidebarQuota,{...props,ctx:props&&props.ctx||ctx}))));registerSlotWhenReady("main",()=>ctx.slots.register({name:"main",key:"clinebot-panel",locale:NS,inject:()=>({ctx,open:openPanel,close:closePanel})},props=>React.createElement(ErrorBoundary,{t:props&&props.t},React.createElement(DashboardDialog,{...props,panel:true,ctx:props&&props.ctx||ctx,onClose:props&&props.close||closePanel}))));registerSlotWhenReady("settings.models.provider-card",()=>ctx.slots.register({name:"settings.models.provider-card",key:NS,locale:NS,inject:()=>({ctx})},props=>React.createElement(ErrorBoundary,{t:props&&props.t},React.createElement(ProviderCard,{...props,ctx:props&&props.ctx||ctx}))));registerSlotWhenReady("conversation.composer.dock",()=>ctx.slots.register({name:"conversation.composer.dock",id:"clinebot-cost",order:10,locale:NS,inject:()=>({ctx})},props=>React.createElement(ErrorBoundary,{t:props&&props.t},React.createElement(ComposerCostDock,{...props,ctx:props&&props.ctx||ctx}))))}module.exports={apply,inject:["slots","locale","configForms"]};
package/lib/config.js CHANGED
@@ -62,6 +62,8 @@ export const Config = z.object({
62
62
  .description('Public model price catalog URL used by the optional price sync.').volatile(),
63
63
  priceCatalogPath: z.string().default('~/.dsh/clinebot-prices.json')
64
64
  .description('Local cache for the public price catalog.').volatile(),
65
+ priceAutoSync: z.boolean().default(true)
66
+ .description('Automatically fetch the public price catalog when a newly discovered model has no price.').volatile(),
65
67
  eventLogPath: z.string().default('~/.dsh/clinebot-events.json')
66
68
  .description('Local ring buffer for account cooldown, recovery and routing events.').volatile(),
67
69
  enabledModels: z.array(z.string()).default([])
@@ -252,6 +254,7 @@ export function publicConfig(cfg) {
252
254
  statsPath: String(cfg?.statsPath || '~/.dsh/clinebot-stats.json'),
253
255
  priceCatalogUrl: String(cfg?.priceCatalogUrl || 'https://models.dev/api.json'),
254
256
  priceCatalogPath: String(cfg?.priceCatalogPath || '~/.dsh/clinebot-prices.json'),
257
+ priceAutoSync: cfg?.priceAutoSync !== false,
255
258
  eventLogPath: String(cfg?.eventLogPath || '~/.dsh/clinebot-events.json'),
256
259
  statsRetentionDays: Number.isFinite(Number(cfg?.statsRetentionDays))
257
260
  ? Math.max(0, Math.min(3650, Number(cfg.statsRetentionDays)))
package/lib/index.js CHANGED
@@ -38,7 +38,7 @@ export function configReader(config) {
38
38
  export { NS, Config, sessionStats, recordSessionRequest, resetSessionStats, rotateToNextAccount }
39
39
 
40
40
  export function apply(ctx, config) {
41
- let currentConfig = config || {}
41
+ let currentConfig = configReader(config)() || {}
42
42
  const overrides = {}
43
43
  const getConfig = () => {
44
44
  if (!currentConfig) return currentConfig
package/lib/models.js CHANGED
@@ -414,10 +414,6 @@ export function isSupportedModel(id, dynamicModels = [], modelContextOverrides =
414
414
  return findModel(id, dynamicModels, modelContextOverrides, customModels) !== null
415
415
  }
416
416
 
417
- export function getDefaultModelIds(dynamicModels = [], modelContextOverrides = [], customModels = []) {
418
- return getAllModels(dynamicModels, modelContextOverrides, customModels).map((m) => m.id)
419
- }
420
-
421
417
  /**
422
418
  * Filter out models explicitly disabled by user.
423
419
  * Any newly introduced model (not in disabledModelIds) is active by default.
package/lib/price-sync.js CHANGED
@@ -82,11 +82,6 @@ export function loadPublicPriceCache(cachePath) {
82
82
  let lastMissingSignature = ''
83
83
  let lastAttemptAt = 0
84
84
 
85
- export function resetPriceSyncState() {
86
- lastMissingSignature = ''
87
- lastAttemptAt = 0
88
- }
89
-
90
85
  /**
91
86
  * Refresh the public catalog only when one of the active models is missing.
92
87
  * A repeated missing set is retried at most once per interval, so an unknown
@@ -96,6 +91,7 @@ export async function ensurePublicPricesForModels(modelIds, {
96
91
  url = DEFAULT_URL,
97
92
  cachePath = '~/.dsh/clinebot-prices.json',
98
93
  fetchImpl,
94
+ enabled = true,
99
95
  timeoutMs = 8000,
100
96
  minIntervalMs = 6 * 60 * 60 * 1000,
101
97
  now = Date.now(),
@@ -103,6 +99,7 @@ export async function ensurePublicPricesForModels(modelIds, {
103
99
  const ids = [...new Set((Array.isArray(modelIds) ? modelIds : []).map((id) => String(id || '').trim()).filter(Boolean))]
104
100
  const missing = ids.filter((id) => !getPublicPriceCatalog()[normalizeModelKey(id)])
105
101
  if (missing.length === 0) return { synced: false, missing: [], count: 0 }
102
+ if (enabled === false) return { synced: false, skipped: true, missing, count: 0 }
106
103
  if (!fetchImpl && process.env.CLINEBOT_DISABLE_PRICE_AUTOSYNC === '1') {
107
104
  return { synced: false, skipped: true, missing, count: 0 }
108
105
  }
@@ -248,6 +248,7 @@ export async function autoDiscoverPlanModels(ctx, { live, getSettingsApi, syncPr
248
248
  await ensurePublicPricesForModels(usageData.dynamicModels.map((model) => model.id), {
249
249
  url: pub.priceCatalogUrl,
250
250
  cachePath: pub.priceCatalogPath,
251
+ enabled: pub.priceAutoSync !== false,
251
252
  }).catch(() => {})
252
253
  }
253
254
  return
@@ -66,6 +66,7 @@ export function registerModelsRoutes(ctx, { live, getSettingsApi, syncProviderSt
66
66
  priceSync = await ensurePublicPricesForModels(addedModels.map((model) => model.id), {
67
67
  url: pub.priceCatalogUrl,
68
68
  cachePath: pub.priceCatalogPath,
69
+ enabled: pub.priceAutoSync !== false,
69
70
  })
70
71
  } catch {
71
72
  /* price catalog refresh is best-effort */
@@ -112,18 +112,6 @@ export function markAccountCooldown(accountId, durationMs = 60000, resetsAt = nu
112
112
  emitAccountEvent({ type: 'cooldown', accountId, reason, until })
113
113
  }
114
114
 
115
- /**
116
- * Clear cooldown for an account manually.
117
- *
118
- * @param {string} accountId
119
- */
120
- export function clearAccountCooldown(accountId) {
121
- if (accountId && cooldowns.has(accountId)) {
122
- cooldowns.delete(accountId)
123
- emitAccountEvent({ type: 'recover', accountId, reason: 'manual' })
124
- }
125
- }
126
-
127
115
  export function matchesAccount(acc, identifier) {
128
116
  if (!acc || !identifier) return false
129
117
  return acc.id === identifier || acc.apiKeyEnv === identifier || acc.label === identifier
@@ -362,17 +350,6 @@ export function resolveSessionAccount(sessionId, accounts = [], defaultActiveId
362
350
  }
363
351
  }
364
352
 
365
- /**
366
- * Explicitly release a session.
367
- *
368
- * @param {string} sessionId
369
- */
370
- export function releaseSession(sessionId) {
371
- if (sessionId) {
372
- sessions.delete(sessionId)
373
- }
374
- }
375
-
376
353
  /**
377
354
  * Clear all sessions and cooldowns (for test environments).
378
355
  */
package/lib/telemetry.js CHANGED
@@ -10,6 +10,7 @@ export const sessionStats = {
10
10
  promptTokensEst: 0,
11
11
  completionTokensEst: 0,
12
12
  reasoningTokensEst: 0,
13
+ offloadedImages: 0,
13
14
  totalTokensEst: 0,
14
15
  lastLatencyMs: null,
15
16
  lastRequestAt: null,
@@ -26,6 +27,7 @@ export function recordSessionRequest({
26
27
  promptTokens = 0,
27
28
  completionTokens = 0,
28
29
  reasoningTokens = 0,
30
+ offloadedImages = 0,
29
31
  model,
30
32
  }) {
31
33
  sessionStats.totalRequests += 1
@@ -43,6 +45,7 @@ export function recordSessionRequest({
43
45
  sessionStats.promptTokensEst += Number(promptTokens) || 0
44
46
  sessionStats.completionTokensEst += Number(completionTokens) || 0
45
47
  sessionStats.reasoningTokensEst = (sessionStats.reasoningTokensEst || 0) + (Number(reasoningTokens) || 0)
48
+ sessionStats.offloadedImages = (sessionStats.offloadedImages || 0) + (Number(offloadedImages) || 0)
46
49
  sessionStats.totalTokensEst += (Number(promptTokens) || 0) + (Number(completionTokens) || 0)
47
50
 
48
51
  if (model) {
@@ -74,6 +77,7 @@ export function resetSessionStats() {
74
77
  sessionStats.promptTokensEst = 0
75
78
  sessionStats.completionTokensEst = 0
76
79
  sessionStats.reasoningTokensEst = 0
80
+ sessionStats.offloadedImages = 0
77
81
  sessionStats.totalTokensEst = 0
78
82
  sessionStats.lastLatencyMs = null
79
83
  sessionStats.lastRequestAt = null
@@ -9,7 +9,7 @@
9
9
  import { createRequire } from 'node:module'
10
10
 
11
11
  const require = createRequire(import.meta.url)
12
- let packageVersion = '0.6.14'
12
+ let packageVersion = '0.6.15'
13
13
  try {
14
14
  packageVersion = require('../../package.json').version || packageVersion
15
15
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-clinebot",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "description": "DeepSeek Harness companion for ClineBot / ClinePass: dynamic subscription models sync, quota exhaustion warnings, session metrics, dedicated settings page, live usage limits, and /cline slash-command.",
5
5
  "license": "MIT",
6
6
  "type": "module",