@gehennawu/dsh-service 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +7 -7
- package/README.md +7 -7
- package/client.js +40 -13
- package/index.js +24 -2
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -10,7 +10,7 @@ A service-control and operations plugin for self-hosted DSH Web. Provides safe r
|
|
|
10
10
|
|
|
11
11
|
The Settings panel "Service Control" page has nine top-level tabs: **Overview, Notifications, Health, Model stats, Quota lookup, Backups, Skills, Subagents, Restart**; the Restart, Quota lookup, Skills, and Subagents tabs can each enable a quick entry at the bottom of the settings left navigation (off by default).
|
|
12
12
|
|
|
13
|
-
The plugin also appears under **Plugins → Plugin configuration**, with nine host-level switches
|
|
13
|
+
The plugin also appears under **Plugins → Plugin configuration**, with nine host-level switches: **Health diagnostics, Model statistics, Quota lookup, Backup maintenance, Task notifications, Skill manager, Subagent model, Mobile adaptation, and the `/healthz` liveness endpoint** (all enabled by default except Mobile adaptation, which is off by default). Disabling one hides its UI, stops the associated polling/subscriptions, and makes the Host reject that capability; Overview and Restart remain available. All nine switches are live settings: disabling or re-enabling them requires neither a page reload nor a DSH Web restart. Statistics refreshes, quota requests, or backup operations already in flight are allowed to finish; quota re-enablement preserves existing cache, TTL, and backoff state, so its UI and calls return immediately but a new upstream request is not guaranteed at once.
|
|
14
14
|
|
|
15
15
|
### Version and updates
|
|
16
16
|
|
|
@@ -41,7 +41,7 @@ The plugin also appears under **Plugins → Plugin configuration**, with nine ho
|
|
|
41
41
|
|
|
42
42
|
- 7-day stacked bar chart of input/output/cache tokens with blue/orange/teal legend
|
|
43
43
|
- Filter by project; hover for exact values
|
|
44
|
-
- Model breakdown as horizontal stacked bars (same legend colors) with a "Today / Last 7 days / All time" toggle on the right of the list header (default:
|
|
44
|
+
- Model breakdown as horizontal stacked bars (same legend colors) with a "Today / Last 7 days / All time" toggle on the right of the list header (default: today): Today aggregates only the current day, Last 7 days ranks within the chart's window, and All time covers every date in the index (bounded by session retention); each row keeps `x times · Cache hit x% · Input xM token · Output xM token`
|
|
45
45
|
- Steps whose provider reports no token usage are excluded from the statistics
|
|
46
46
|
- Last-24-hour model/tool error statistics, collapsed by default
|
|
47
47
|
|
|
@@ -53,7 +53,7 @@ The plugin also appears under **Plugins → Plugin configuration**, with nine ho
|
|
|
53
53
|
- A quota ring inside the conversation composer follows the provider selected by the current session and shows the tightest budget window as a percentage (green below 80%, amber at or above); clicking opens a panel headed by the provider name, with a bar and used percentage for each window and reset times on their own lines; on narrow viewports (phones) the panel switches to a viewport-centered floating layer — fully visible, scrolling internally when too tall — and rotates or resizing wider flips it back to the anchored placement above the ring
|
|
54
54
|
- A "Show a remote quota entry in the settings left navigation" switch lives in the Quota lookup tab (off by default), mirroring the Restart entry
|
|
55
55
|
- Built-in adaptations: **OpenCode Go** (`{baseURL}/usage`), **Zhipu GLM Coding Plan / zai-coding-cn** (official monitor `quota/limit` endpoint with three windows — 5-hour rolling tokens, weekly tokens, monthly MCP quota; an idle 5-hour window hides its reset time, matching the official console), **OpenRouter** (credits used %), **Kimi/Moonshot** and **SiliconFlow** (CNY balance text), **DeepSeek Platform** (official balance, see next bullet), **Xiaomi MiMo Token Plan** (subscription plan quota, see below); dialects that natively report remaining percentage flip the panel word to "Remaining" and invert the warn threshold; transient network errors retry automatically and the Zhipu dual-domain candidate chain switches automatically; the provider-to-kind mapping lives in `DSH_HOME/dsh-service-quota.json`, and known services are auto-detected from their baseURL (e.g. opencode.ai, bigmodel.cn, api.deepseek.com, token-plan-cn.xiaomimimo.com) with no manual picking; no upstream request is ever made for unadapted or disabled providers; disabling can be done via "Disable" in a card's footer or by writing `"<provider>": null` in the config file (both are equivalent). Zhipu reset cards have no API-key-queryable endpoint yet — you can add any number of them via each provider card's "Add reset card" button on the tab (name plus expiry time, down to the minute), and remove each one independently — the ring panel shows them too (stored in the same config file); expired cards are flagged automatically
|
|
56
|
-
- **DeepSeek official balance with peak/off-peak hint (deepseek)**: calls DeepSeek Platform's official `GET /user/balance` and shows the total balance per currency (CNY→¥, USD→$), plus a separate "Granted balance" line whenever unexpired granted credit is positive; each DeepSeek card embeds a **peak/off-peak block** — a status badge on top (orange = "Peak now · standard price", green = "Off-peak now · half price") with a countdown to the next switch in Beijing time (e.g. "Peak pricing from 09:00 (in 12 h 41 min)", correctly spanning weekends to Monday's morning peak), below it a two-segment ribbon: the first block is the remainder of the current period and the second is the next opposite period (spanning days when needed — e.g. Friday evening draws straight to Monday's morning peak), widths proportional to actual durations with the left edge marking "now"; blocks are tagged inline as Peak / Off-peak, advancing every 30 seconds, and a caption stating the official rule "off-peak price is half the peak price"; the ring panel shows a compact version without the caption. Credentials resolve via the `DEEPSEEK_API_KEY` hint from the DSH credential store or environment variables, or can be written directly through the card's credential form. DSH's built-in `@deepseek-ai/dsh-llm-deepseek` official channel (the DeepSeek V4 models in the model picker, route id `deepseek-official`) needs **no extra settings route** — the quota lookup merges that runtime channel automatically and adapts it to the official balance endpoint, so the ring also appears when you switch to one of its models
|
|
56
|
+
- **DeepSeek official balance with peak/off-peak hint (deepseek)**: calls DeepSeek Platform's official `GET /user/balance` and shows the total balance per currency (CNY→¥, USD→$), plus a separate "Granted balance" line whenever unexpired granted credit is positive; each DeepSeek card embeds a **peak/off-peak block** — a status badge on top (orange = "Peak now · standard price", green = "Off-peak now · half price") with a countdown to the next switch in Beijing time (e.g. "Peak pricing from 09:00 (in 12 h 41 min)", correctly spanning weekends to Monday's morning peak), below it a two-segment ribbon: the first block is the remainder of the current period and the second is the next opposite period (spanning days when needed — e.g. Friday evening draws straight to Monday's morning peak), widths proportional to actual durations with the left edge marking "now"; blocks are tagged inline as Peak / Off-peak, advancing every 30 seconds, and a caption stating the official rule "off-peak price is half the peak price"; the ring panel shows a compact version without the caption. Credentials resolve via the `DEEPSEEK_API_KEY` hint from the DSH credential store or environment variables, or can be written directly through the card's credential form. DSH's built-in `@deepseek-ai/dsh-llm-deepseek` official channel (the DeepSeek V4 models in the model picker, route id `deepseek-official`) needs **no extra settings route** — the quota lookup merges that runtime channel automatically and adapts it to the official balance endpoint, so the ring also appears when you switch to one of its models. The auto-detected DeepSeek row is **hidden entirely while `DEEPSEEK_API_KEY` is not configured**; each visit to the Quota lookup tab re-checks the credential once (a failed check stays silent), and the card appears automatically once the key exists. A DeepSeek row adapted manually through the UI is exempt and always shown so its key can be filled in
|
|
57
57
|
- **CLIProxyAPI accounts (cliproxy)**: queries the **official remaining quota** of each OAuth upstream account inside a CLIProxyAPI (CPA) deployment (Codex 5-hour/weekly windows, per-model quotas for GeminiCLI and Antigravity) — not proxy-key usage. Prerequisites: CPA's `remote-management.secret-key` must be set (the whole management API is unavailable without it), remote access requires `allow-remote-management`, and the management secret goes into the DSH credential store as `CPA_MANAGEMENT_KEY` or an environment variable of the same name — it is independent of the proxy API key, and the plugin never sends the proxy key to the management plane. When you save the "CLIProxyAPI accounts" adaptation, the plugin pins the hostname of that provider's settings baseURL into the config file and afterwards only sends requests to that pinned domain (re-save the adaptation after changing baseURL); a single refresh queries at most 8 accounts (disabled accounts and account types without a supported quota endpoint are skipped automatically), and failures on some accounts do not affect the rest. Note that CPA's management plane has built-in key throttling — 5 wrong attempts ban your IP for 30 minutes — so make sure the secret is correct before testing
|
|
58
58
|
- **Xiaomi MiMo Token Plan quota (xiaomi-token-plan-cn)**: queries the "plan usage" of a Xiaomi MiMo Open Platform Token Plan subscription — plan total quota, compensation credits and other buckets, straight from the same source as the console; each row shows used percentage, an absolute figure abbreviation (e.g. `12% · 1.4B / 11B`) and a subscription-validity countdown. Xiaomi has no API-key-based quota endpoint (the inference gateway serves `/v1` inference paths only); the data lives behind console-origin APIs, so the credential is your **web session cookie**: log into platform.xiaomimimo.com in your browser, open DevTools, copy the full `Cookie:` request header from any `/api/v1/tokenPlan/` call, then click **"Set console cookie (web session)"** on the card and paste it (stored in the DSH credential store under `XIAOMI_MIMO_CONSOLE_COOKIE` or an environment variable of the same name; the `Cookie:` prefix is optional). The cookie is sent only to two fixed endpoints, `platform.xiaomimimo.com/api/v1/tokenPlan/detail` and `/usage`; the `tp-…` inference key is never sent to the console plane. When the cookie expires (logout or session expiry) the card shows "Console cookie expired" — copy it again. Providers auto-detect from baseURL (token-plan-cn.xiaomimimo.com); when you reach MiMo through a relay domain, pick "Xiaomi MiMo Token Plan" in "Manual adapt" instead (the queried plane is fixed and unaffected by baseURL). Note that a web cookie grants full console access — it is stored only in your local credential store and sent only to those two fixed endpoints; never hand it to environments you do not trust
|
|
59
59
|
- Credential fill-in form: when an adapted provider shows "credential missing", its card offers an inline form — regular adaptations show **"Set API credential"**, the CLIProxyAPI adaptation shows **"Set management key (web login key)"** (the remote-management secret you use to log into CPA's web management UI — not the proxy API key, which the management plane bans wrong attempts for), and the Xiaomi Token Plan adaptation shows **"Set console cookie (web session)"**; the credential name comes from a host-derived whitelist per adaptation type (CLIProxyAPI only ever offers `CPA_MANAGEMENT_KEY`/`CLIPROXY_MANAGEMENT_KEY` — these are **alias slots for the same secret; storing one is enough**, discovery takes the first configured value in order; Xiaomi likewise offers `XIAOMI_MIMO_CONSOLE_COOKIE`/`MIMO_CONSOLE_COOKIE`; the primary name is marked and the form defaults to a configured slot. Never the proxy key or the tp-… inference key); saving writes the value into the DSH credential provider (the `refs:` section of `$DSH_HOME/.credentials.yaml`, hot-effective without restarts) and then force-refreshes that provider; the form can also clear a stored file-layer credential in one click. If a process environment variable is currently shadowing that name, the host refuses the write (credential-store contract) — change the environment variable itself instead
|
|
@@ -75,7 +75,7 @@ The plugin also appears under **Plugins → Plugin configuration**, with nine ho
|
|
|
75
75
|
- The "Skills" tab lists every local skill under three sections — **auto-loaded / manual-only / fully disabled** — scanning project `.dsh`, project `.agents`, user `~/.dsh/skills`, `$DSH_AGENTS_HOME`, and `$DSH_BUNDLED_SKILL_DIR` roots one level deep, with a name filter and source badges; same-name shadowing (lower rank wins) marks both winner and loser copies, bundled directories are shown read-only, and a physical directory hit by multiple root rules is counted once
|
|
76
76
|
- Two per-entry switches edit the SKILL.md frontmatter directly: `disable-model-invocation` for "visible to model", `user-invocable` for "invocable via /"; switches use a two-click confirmation, changes go live within ~200 ms, active sessions receive a catalog-update notice on their next step, and toggling back and forth leaves no residue in the file
|
|
77
77
|
- Entries carrying legacy camelCase invocation keys (e.g. `disableModelInvocation`) are dropped entirely by the official parser: the panel shows a ⚠ warning and a two-click confirmed fix that converts them to canonical keys semantically
|
|
78
|
-
- "Fill with AI" (✨): pick any configured model (last choice remembered); the Host calls it with a fixed template to draft description and usage **in the DSH UI language** (Simplified Chinese in Chinese environments, English in English ones); the draft is previewed old-vs-new and saved after explicit confirmation as an "AI note". Notes live only in the plugin sidecar index `DSH_HOME/dsh-service-skills-index.json` and **never modify SKILL.md** — they render under their entry on the Skills tab only (with a remove button); a body change marks the note stale for refilling
|
|
78
|
+
- "Fill with AI" (✨): pick any configured model (last choice remembered); the Host calls it with a fixed template to draft description and usage **in the DSH UI language** (Simplified Chinese in Chinese environments, English in English ones); the draft is previewed old-vs-new and saved after explicit confirmation as an "AI note". Notes live only in the plugin sidecar index `DSH_HOME/dsh-service-skills-index.json` and **never modify SKILL.md** — they render under their entry on the Skills tab only, at full entry-card width (with a remove button); a body change marks the note stale for refilling
|
|
79
79
|
- One-click batch fill: automatically collects unannotated skills or ones whose body changed (read-only roots included; invalid entries and shadowed copies are skipped), shows candidate count / estimated payload and an expandable per-entry skip list first, then runs sequentially with live progress; individual failures never block the batch, and cancelling immediately interrupts the in-flight model call. Zero file modifications throughout, and the plugin never issues model calls autonomously
|
|
80
80
|
- Batch runs in the Host background: switching tabs, closing the settings panel, or even refreshing the page never interrupts it; returning to the Skills tab restores progress and the cancel button, the Skills tab title shows a live `⟳done/total` badge while running, and planning a second batch mid-run is explicitly rejected
|
|
81
81
|
|
|
@@ -93,13 +93,13 @@ The plugin also appears under **Plugins → Plugin configuration**, with nine ho
|
|
|
93
93
|
|
|
94
94
|
- Notification settings live in the top-level Notifications tab: browser notification when a session finishes its turn, or when your approval, plan review, or answer is needed
|
|
95
95
|
- Clicking a notification focuses the DSH page and closes the popup
|
|
96
|
-
-
|
|
97
|
-
- Bell icon in the conversation input bar toggles the master switch quickly
|
|
96
|
+
- Four toggle switches: master switch, task completion, approvals & questions, and bell-icon visibility, each controlled independently
|
|
97
|
+
- Bell icon in the conversation input bar toggles the master switch quickly; a "Show composer bell icon" capsule switch on the Notifications tab hides it entirely (notification behavior is unaffected, and the choice persists across reloads)
|
|
98
98
|
- All toggles persist across page reloads
|
|
99
99
|
|
|
100
100
|
### Mobile adaptation
|
|
101
101
|
|
|
102
|
-
-
|
|
102
|
+
- Off by default (enable it under "Plugins → Plugin configuration"); active only on phones or narrow windows (viewport <1024px, matching the official shell's sidebar collapse breakpoint). Desktop widths are completely unaffected
|
|
103
103
|
- Sidebar becomes a drawer: the official sidebar slides into a left overlay drawer — a translucent floating button on the left edge toggles it, and tapping the backdrop or any session row closes it automatically. The preview/file-tree details column becomes a right overlay instead of squeezing the conversation area. Open/close goes through DSH's official `layout` service; no host DOM reparenting
|
|
104
104
|
- Modals become bottom sheets: settings dialogs turn into bottom sheets (rounded top corners, internal scrolling), and the settings left navigation becomes a horizontally scrollable top strip
|
|
105
105
|
- Touch details: adds `viewport-fit=cover` automatically and respects notch safe areas; double-tap no longer zooms the page; text inputs stay ≥16px so iOS no longer auto-zooms on focus
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
设置页「服务控制」面板包含九个顶部标签:**概览、通知、健康诊断、模型统计、额度查询、备份维护、技能、子代理、重启**;重启、额度查询、技能与子代理标签还可在设置页左侧标签列底部开启快捷入口(默认关闭)。
|
|
12
12
|
|
|
13
|
-
插件同时出现在「插件 →
|
|
13
|
+
插件同时出现在「插件 → 插件配置」,提供九个宿主级开关:**健康诊断、模型统计、额度查询、备份维护、任务通知、技能管理、子代理模型、移动端适配、`/healthz` 探活端点**(除移动端适配默认关闭外,其余默认开启)。关闭后不仅隐藏对应界面,也会停止相关轮询/订阅并由宿主拒绝对应能力;概览与重启固定保留。设置写入 DSH settings,全部热生效:关闭或重新开启都无需刷新页面或重启 DSH Web。已在途的统计刷新、额度请求或备份操作允许完成;额度重新开启时保留既有缓存、TTL 与退避状态,因此可立即恢复界面和调用,但不保证立刻重新请求上游。
|
|
14
14
|
|
|
15
15
|
### 版本与更新
|
|
16
16
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
- 近 7 天输入/输出/缓存 token 堆叠柱图,蓝/橙/青图例
|
|
43
43
|
- 按项目筛选,鼠标悬停显示精确数值
|
|
44
|
-
- 模型明细为横向堆叠柱形图(沿用主图图例配色),列表头部右侧提供「今日 / 近 7 天 /
|
|
44
|
+
- 模型明细为横向堆叠柱形图(沿用主图图例配色),列表头部右侧提供「今日 / 近 7 天 / 累计」切换标签(默认今日):今日只聚合当天,近 7 天按主图同窗口排序,累计覆盖索引内全部日期(受会话持久化留存范围限制);每行附「x次 · 缓存命中 x% · 输入 xM token · 输出 xM token」明细
|
|
45
45
|
- 提供方未上报 token 用量的模型步骤不纳入统计
|
|
46
46
|
- 最近 24 小时模型/工具报错统计,默认折叠
|
|
47
47
|
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
- 对话输入框内一枚额度圆环,跟随当前会话所选模型的供应商,显示最紧预算窗口的已用百分比(<80% 绿色、≥80% 黄色);点击弹出面板——头部标明供应商,各窗口带独立进度条与已用百分比、重置时间单独一行;手机等窄屏上面板自动切换为视口居中的浮层(完整可见、超高时内部滚动),旋转或拖宽窗口即时切回圆环上方锚定
|
|
54
54
|
- 可在「额度查询」标签开启「设置页左列显示入口」开关(默认关闭),开启后在设置页左侧标签列底部显示「额度查询」快捷入口(与「重启」入口同模式)
|
|
55
55
|
- 内置适配:**OpenCode Go**(`{baseURL}/usage`)、**智谱 GLM Coding Plan / zai-coding-cn**(官方监控端点 `quota/limit`,含 5 小时滚动 Token、每周 Token、MCP 月度配额三个窗口,5 小时窗口空闲时与官网一致地不显示重置时间)、**OpenRouter**(credits 已用%)、**Kimi/Moonshot** 与 **硅基流动**(人民币余额)、**DeepSeek 开放平台**(官方余额,见下一条)、**小米 MiMo Token Plan**(订阅套餐额度,见下一条);原生报「剩余百分比」的方言会自动把面板头部切换为「剩余」并把预警阈值反向;上游瞬时网络错误自动重试,智谱双域候选链自动切换;供应商与适配类型的对应关系保存在 `DSH_HOME/dsh-service-quota.json`,已知服务商按 baseURL 自动识别适配(如 opencode.ai、bigmodel.cn、api.deepseek.com、token-plan-cn.xiaomimimo.com),无需手选;未适配或已停用的供应商不会被发起任何上游请求,停用可在卡片脚部选「停用查询」或在配置文件写 `"<provider>": null`(两者等价)。智谱的重置卡暂无 API Key 可查的接口,可在「额度查询」标签内点各供应商卡片的「添加重置卡」填写名称与到期时间(可精确到分钟),可连续添加多条、每条独立移除;圆环面板同步显示(数据存入同一配置文件),过期自动标注
|
|
56
|
-
- **DeepSeek 官方余额与峰谷提示(deepseek)**:调用 DeepSeek 开放平台官方 `GET /user/balance`,按币种显示总额余额(CNY→¥、USD→$),未过期赠金大于 0 时追加一行「赠送余额」;卡片内嵌**峰谷时段提示块**——顶部为当前状态徽标(橙=「当前高峰时段 · 标准价」、绿=「当前空闲时段 · 半价计费」)和下一次换挡的北京时间倒计时(如「09:00 转高峰(12 小时 41 分钟后)」,跨周末也能算到周一早高峰),下方是一条**两段式**峰谷色带:第一段是当前时段的剩余部分,第二段是下一个相反时段(可跨天,如周五晚直接画到周一早高峰),宽度按实际时长比例、左缘细标线即当前时刻;段内只标「忙时 / 闲时」,每 30 秒自动前进,底部说明行写明官方规则「空闲时段价格为高峰时段的一半」;圆环面板内同步显示紧凑版(无说明行)。凭据按 `DEEPSEEK_API_KEY` 线索从 DSH 凭据库或环境变量解析,也可直接在卡片上用「填写 API 密钥」表单写入。DSH 内置的 `@deepseek-ai/dsh-llm-deepseek` 官方渠道(模型选择器里的 DeepSeek V4 系列,路由名 `deepseek-official`)**无需在 settings
|
|
56
|
+
- **DeepSeek 官方余额与峰谷提示(deepseek)**:调用 DeepSeek 开放平台官方 `GET /user/balance`,按币种显示总额余额(CNY→¥、USD→$),未过期赠金大于 0 时追加一行「赠送余额」;卡片内嵌**峰谷时段提示块**——顶部为当前状态徽标(橙=「当前高峰时段 · 标准价」、绿=「当前空闲时段 · 半价计费」)和下一次换挡的北京时间倒计时(如「09:00 转高峰(12 小时 41 分钟后)」,跨周末也能算到周一早高峰),下方是一条**两段式**峰谷色带:第一段是当前时段的剩余部分,第二段是下一个相反时段(可跨天,如周五晚直接画到周一早高峰),宽度按实际时长比例、左缘细标线即当前时刻;段内只标「忙时 / 闲时」,每 30 秒自动前进,底部说明行写明官方规则「空闲时段价格为高峰时段的一半」;圆环面板内同步显示紧凑版(无说明行)。凭据按 `DEEPSEEK_API_KEY` 线索从 DSH 凭据库或环境变量解析,也可直接在卡片上用「填写 API 密钥」表单写入。DSH 内置的 `@deepseek-ai/dsh-llm-deepseek` 官方渠道(模型选择器里的 DeepSeek V4 系列,路由名 `deepseek-official`)**无需在 settings 另建路由**——额度查询会自动并入这个运行时渠道并按官方余额适配,切换到它的模型时圆环同样出现。自动识别出的 DeepSeek 行在 `DEEPSEEK_API_KEY` 未配置时**整行不显示**;每次进入「额度查询」标签页都会自动重新识别一次凭据是否已配置(识别失败静默不提示),配置好后卡片自动现身。手动在 UI 里适配过的 DeepSeek 行不受此限,照常显示以便填写密钥。
|
|
57
57
|
- **CLIProxyAPI 账号额度(cliproxy)**:查询 CLIProxyAPI(CPA)部署内各 OAuth 上游账号的**官方剩余额度**(Codex 的 5 小时/本周窗口、GeminiCLI 与 Antigravity 的各模型配额),而不是代理 key 的用量。前置条件:CPA 配置 `remote-management.secret-key` 非空(为空时管理面整体不可用)、远程访问需开启 `allow-remote-management`;管理密钥放入 DSH 凭据库 `CPA_MANAGEMENT_KEY` 或同名环境变量——它与代理用的 API key 相互独立,插件绝不会把代理 key 发往管理面。在「手动适配」中选择「CLIProxyAPI 账号额度」保存时,插件会把该供应商 settings baseURL 的域名钉进配置文件,此后外呼只发往这个域(修改 baseURL 后需重新保存适配);单次刷新最多查询 8 个账号(已禁用或无受支持配额端点的账号自动跳过),部分账号失败不影响其余账号的结果。请注意 CPA 管理面对错误密钥有内建封禁(连续错 5 次封 IP 30 分钟),务必填对再测
|
|
58
58
|
- **小米 MiMo Token Plan 额度(xiaomi-token-plan-cn)**:查询小米 MiMo 开放平台 Token Plan 订阅的「套餐使用情况」——套餐总额度、补偿积分等额度桶,与控制台同源,每行显示已用百分比、绝对数缩写(如 `12% · 1.4B / 11B`)和订阅有效期倒计时。小米没有可用 API Key 查询额度的接口(推理网关只有 `/v1` 推理路径),数据来自控制台同源 API,凭据是**网页登录态 Cookie**:浏览器登录 platform.xiaomimimo.com 后打开开发者工具,从任意 `/api/v1/tokenPlan/` 请求复制完整 `Cookie:` 请求头的值,点卡片上的**「填写控制台 Cookie(网页登录态)」**粘贴保存(写入 DSH 凭据库 `XIAOMI_MIMO_CONSOLE_COOKIE` 或同名环境变量;带不带 `Cookie:` 前缀都行)。Cookie 只发往 `platform.xiaomimimo.com/api/v1/tokenPlan/detail` 与 `/usage` 两个固定端点;`tp-` 开头的推理密钥绝不会发往控制台平面。Cookie 失效(退出登录或过期)时卡片显示「控制台 Cookie 已失效」,重新复制一次即可。供应商按 baseURL 自动识别(token-plan-cn.xiaomimimo.com);经中转域接入推理时在「手动适配」中选择「小米 MiMo Token Plan」即可(查询平面固定、不受 baseURL 影响)。请注意网页 Cookie 等同整个控制台的访问权限——它只保存在本机凭据库、只用于上述两个固定端点,请不要把 Cookie 交给不信任的环境
|
|
59
59
|
- 凭据填写窗口:已适配供应商因缺凭据显示「凭据未配置」时,卡片上会出现内联表单——普通适配显示**「填写 API 密钥」**,CLIProxyAPI 适配显示**「填写管理密钥(网页登录的 key)」**(即登录 CPA 网页管理面所用的 remote-management 密钥,不是代理 API key——管理面对错密钥有封禁,别填错),小米 Token Plan 适配显示**「填写控制台 Cookie(网页登录态)」**;凭据名称由宿主按适配类型派生白名单(CLIProxyAPI 只会出现 `CPA_MANAGEMENT_KEY`/`CLIPROXY_MANAGEMENT_KEY`——两者是同一密钥的**别名存放槽,二选一即可**,查找按顺序取第一个已配置的值;小米只出现 `XIAOMI_MIMO_CONSOLE_COOKIE`/`MIMO_CONSOLE_COOKIE`,同理二选一;主名带「主名」标记且表单默认选中已配置的槽位。绝不混入代理 key 或 tp- 推理密钥),输入密钥值保存即写入 DSH 凭据提供方(`$DSH_HOME/.credentials.yaml` 的 `refs:` 分节,热生效无需重启),随后自动强制重拉该供应商;表单里还能一键清除已存的文件层凭据。进程环境变量正在遮蔽该名字时宿主会拒绝写入(凭据库契约),此时请改环境变量本身
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
- 「技能」标签按**自动加载 / 仅手动调用 / 完全停用**三区展示全部本地技能(项目 `.dsh`、项目 `.agents`、用户 `~/.dsh/skills`、`$DSH_AGENTS_HOME` 与 `$DSH_BUNDLED_SKILL_DIR` 根,一层深度),支持名称过滤与来源徽标;同名遮蔽(低 rank 优先)与被遮蔽副本均有标注,内置目录只读展示;同一物理目录被多条规则命中时只计一次
|
|
76
76
|
- 每条目双开关直接改写 SKILL.md frontmatter:`disable-model-invocation` 控制「对模型可见」,`user-invocable` 控制「可被 / 调用」;开关采用两段式点击确认,改动约 200ms 内热生效,活跃会话下一步自动收到目录更新提示;往返切换不会在文件中累积残留
|
|
77
77
|
- 带 camelCase 旧版调用键(如 `disableModelInvocation`)的条目会被官方解析器整条剔除:面板给出 ⚠ 提示,两段式确认后按语义换算修复为规范键
|
|
78
|
-
- ✨「AI 补全说明」:从已配置模型中任选一个(记住上次选择),宿主用固定模板调用该模型生成描述与用法草稿,**输出语言跟随 DSH 设置的界面语言**(中文环境出简体中文、英文环境出英文);草稿以新旧对照预览,显式确认后保存为「AI 注释」。注释**只存在插件的侧车索引 `DSH_HOME/dsh-service-skills-index.json` 里、绝不改写 SKILL.md
|
|
78
|
+
- ✨「AI 补全说明」:从已配置模型中任选一个(记住上次选择),宿主用固定模板调用该模型生成描述与用法草稿,**输出语言跟随 DSH 设置的界面语言**(中文环境出简体中文、英文环境出英文);草稿以新旧对照预览,显式确认后保存为「AI 注释」。注释**只存在插件的侧车索引 `DSH_HOME/dsh-service-skills-index.json` 里、绝不改写 SKILL.md**,仅在技能标签页该条目下方单独展示并占满条目卡片宽度(带移除按钮);正文变更后注释自动标记过期,重新补全即可刷新
|
|
79
79
|
- 一键批量补全:自动圈出未注释或正文有变的技能(含只读目录,跳过无效与被遮蔽副本),先展示候选数/预计发送量与可展开的逐条跳过清单,确认后顺序执行并显示进度,单条失败不影响批次,可随时取消(取消会立即中断在途模型调用);全程零文件改动,也绝不自动发起任何模型调用
|
|
80
80
|
- 批量任务在宿主后台持续运行:切换标签、关闭设置面板甚至刷新页面都不会中断;回到技能页自动恢复进度与取消按钮,批量进行中「技能」标签标题实时显示 `⟳已完成/总数` 角标,运行中重复生成计划会被明确拒绝
|
|
81
81
|
|
|
@@ -93,13 +93,13 @@
|
|
|
93
93
|
|
|
94
94
|
- 通知设置位于服务控制顶部的「通知」标签:会话完成一轮任务、或需要你授权/审阅计划/选择答案时发送浏览器通知
|
|
95
95
|
- 点击系统通知弹窗会聚焦到 DSH 页面并关闭该通知
|
|
96
|
-
-
|
|
97
|
-
-
|
|
96
|
+
- 四档开关条:总开关、任务结束通知、授权与提问通知、铃铛图标显隐,各自独立控制
|
|
97
|
+
- 对话栏内铃铛图标快速切换总开关;「通知」标签内的「显示输入框旁的铃铛图标」胶囊开关可把它整体隐藏(通知行为不受影响,显隐选择在页面刷新后保持)
|
|
98
98
|
- 各开关在页面刷新后保持
|
|
99
99
|
|
|
100
100
|
### 移动端适配
|
|
101
101
|
|
|
102
|
-
-
|
|
102
|
+
- 默认关闭(可在「插件 → 插件配置」开启),仅在手机竖屏或窄窗口(视口 <1024px,与官方外壳的侧栏折叠断点一致)下生效,桌面宽度完全无感
|
|
103
103
|
- 侧栏变抽屉:官方侧栏整体收进左侧 overlay 抽屉——左缘半透明悬浮按钮开合、点背板或点选任意会话后自动收起;预览/文件树详情列变右侧浮层,不再挤压会话区。开合走 DSH 官方 `layout` 服务,不重挂宿主 DOM
|
|
104
104
|
- 模态弹窗底部化:设置等对话框变为底部 sheet(上圆角、内部滚动),设置页左列导航变为顶部横向滑动条
|
|
105
105
|
- 触屏细节:自动补 `viewport-fit=cover` 并避让刘海安全区;按钮双击不再触发页面缩放;文本输入框保持 ≥16px,iOS 聚焦时不再自动放大
|
package/client.js
CHANGED
|
@@ -400,6 +400,7 @@ window.__ModuleLoader__.load({
|
|
|
400
400
|
'notification.kind.question': '等待选择答案',
|
|
401
401
|
'notification.bellOn': '通知开启',
|
|
402
402
|
'notification.bellOff': '通知关闭',
|
|
403
|
+
'notification.bellShow': '显示输入框旁的铃铛图标',
|
|
403
404
|
'quota.cardTitle': '额度查询',
|
|
404
405
|
'quota.navToggle': '设置页左列显示「额度查询」入口',
|
|
405
406
|
'quota.navToggleHint': '默认关闭;开启后在设置页左侧标签列底部显示「额度查询」快捷入口',
|
|
@@ -900,6 +901,7 @@ window.__ModuleLoader__.load({
|
|
|
900
901
|
'notification.kind.question': 'answer requested',
|
|
901
902
|
'notification.bellOn': 'Notifications on',
|
|
902
903
|
'notification.bellOff': 'Notifications off',
|
|
904
|
+
'notification.bellShow': 'Show composer bell icon',
|
|
903
905
|
'quota.cardTitle': 'Quota lookup',
|
|
904
906
|
'quota.navToggle': 'Show "Quota lookup" entry in settings left nav',
|
|
905
907
|
'quota.navToggleHint': 'Off by default; when enabled, a "Quota lookup" entry appears at the bottom of the settings left navigation',
|
|
@@ -1109,7 +1111,8 @@ window.__ModuleLoader__.load({
|
|
|
1109
1111
|
if (number >= 1e3) return `${Math.round(number / 1e2) / 10}K`
|
|
1110
1112
|
return number.toLocaleString()
|
|
1111
1113
|
}
|
|
1112
|
-
|
|
1114
|
+
// mobileAdaptation 默认关闭(v0.31 用户点名):宿主与客户端默认值必须一致。
|
|
1115
|
+
const DEFAULT_FEATURES = { healthDiagnostics: true, modelUsage: true, quotaLookup: true, backupMaintenance: true, taskNotifications: true, healthz: true, skillManager: true, subagentRoute: true, mobileAdaptation: false }
|
|
1113
1116
|
const featureScope = ctx.settingsScope.bind({ namespace: NS })
|
|
1114
1117
|
const featureSnapshot = () => featureScope.getSnapshot()
|
|
1115
1118
|
const featureValue = () => Object.assign({}, DEFAULT_FEATURES, featureSnapshot().value || {})
|
|
@@ -1139,26 +1142,41 @@ window.__ModuleLoader__.load({
|
|
|
1139
1142
|
let notifyEnabled = false
|
|
1140
1143
|
let notifyDone = true
|
|
1141
1144
|
let notifyInput = true
|
|
1145
|
+
// 输入框铃铛图标的显隐(v0.31 用户点名):默认显示,独立于通知行为开关——
|
|
1146
|
+
// 藏掉铃铛只是收起快捷入口,通知照常按既有三档工作。
|
|
1147
|
+
let notifyBellVisible = true
|
|
1142
1148
|
try { notifyEnabled = localStorage.getItem('dsh-service-notify') === 'true' } catch (_) {}
|
|
1143
1149
|
try { notifyDone = localStorage.getItem('dsh-service-notify-done') !== 'false' } catch (_) {}
|
|
1144
1150
|
try { notifyInput = localStorage.getItem('dsh-service-notify-input') !== 'false' } catch (_) {}
|
|
1151
|
+
try { notifyBellVisible = localStorage.getItem('dsh-service-notify-bell') !== 'false' } catch (_) {}
|
|
1145
1152
|
const notifyListeners = new Set()
|
|
1153
|
+
const bellListeners = new Set()
|
|
1146
1154
|
const persistNotify = (key, value) => { try { localStorage.setItem(key, value ? 'true' : 'false') } catch (_) {} }
|
|
1147
1155
|
const publishNotify = () => { for (const listener of notifyListeners) listener() }
|
|
1156
|
+
const publishBell = () => { for (const listener of bellListeners) listener() }
|
|
1148
1157
|
const setNotifyEnabled = (value) => { notifyEnabled = value; persistNotify('dsh-service-notify', value); publishNotify() }
|
|
1149
1158
|
const setNotifyDone = (value) => { notifyDone = value; persistNotify('dsh-service-notify-done', value); publishNotify() }
|
|
1150
1159
|
const setNotifyInput = (value) => { notifyInput = value; persistNotify('dsh-service-notify-input', value); publishNotify() }
|
|
1160
|
+
const setNotifyBellVisible = (value) => {
|
|
1161
|
+
notifyBellVisible = value === true
|
|
1162
|
+
persistNotify('dsh-service-notify-bell', notifyBellVisible)
|
|
1163
|
+
publishBell()
|
|
1164
|
+
}
|
|
1165
|
+
/** 槽位注入回调用:铃铛显隐变化时重挂 conversation.input.left 条目。 */
|
|
1166
|
+
const subscribeBellVisible = (listener) => { bellListeners.add(listener); return () => bellListeners.delete(listener) }
|
|
1151
1167
|
const useNotifyState = () => {
|
|
1152
1168
|
const [, setTick] = useState(0)
|
|
1153
1169
|
const [enabled, setEnabled] = useState(notifyEnabled)
|
|
1154
1170
|
const [done, setDone] = useState(notifyDone)
|
|
1155
1171
|
const [input, setInput] = useState(notifyInput)
|
|
1172
|
+
const [bell, setBell] = useState(notifyBellVisible)
|
|
1156
1173
|
React.useEffect(() => {
|
|
1157
|
-
const update = () => { setEnabled(notifyEnabled); setDone(notifyDone); setInput(notifyInput); setTick((t) => t + 1) }
|
|
1174
|
+
const update = () => { setEnabled(notifyEnabled); setDone(notifyDone); setInput(notifyInput); setBell(notifyBellVisible); setTick((t) => t + 1) }
|
|
1158
1175
|
notifyListeners.add(update)
|
|
1159
|
-
|
|
1176
|
+
bellListeners.add(update)
|
|
1177
|
+
return () => { notifyListeners.delete(update); bellListeners.delete(update) }
|
|
1160
1178
|
}, [])
|
|
1161
|
-
return { enabled, done, input, setEnabled: (v) => setNotifyEnabled(v), setDone: (v) => setNotifyDone(v), setInput: (v) => setNotifyInput(v) }
|
|
1179
|
+
return { enabled, done, input, bell, setEnabled: (v) => setNotifyEnabled(v), setDone: (v) => setNotifyDone(v), setInput: (v) => setNotifyInput(v), setBell: setNotifyBellVisible }
|
|
1162
1180
|
}
|
|
1163
1181
|
// 设置页左列入口开关的通用实现(重启/额度/技能三个入口共用,不再三套复制):
|
|
1164
1182
|
// localStorage 持久化、默认关;开启才注册 settings.section 条目、关闭即注销——
|
|
@@ -2873,7 +2891,10 @@ window.__ModuleLoader__.load({
|
|
|
2873
2891
|
onClick: opts.onClick,
|
|
2874
2892
|
style: { width: '34px', height: '20px', borderRadius: '10px', padding: 0, flexShrink: 0, position: 'relative', cursor: opts.disabled ? 'default' : 'pointer', lineHeight: 0, border: '1px solid ' + (opts.armed ? 'var(--dsw-alias-state-warn-primary)' : checked ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-border-l2)'), background: opts.armed ? 'transparent' : checked ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-bg-layer-2)', opacity: opts.disabled ? 0.5 : 1 },
|
|
2875
2893
|
}, React.createElement('span', { style: { position: 'absolute', top: '1px', left: checked && !opts.armed ? '15px' : '1px', width: '16px', height: '16px', borderRadius: '50%', background: opts.armed ? 'var(--dsw-alias-state-warn-primary)' : checked ? '#fff' : 'var(--dsw-alias-label-tertiary)' } }))
|
|
2876
|
-
|
|
2894
|
+
// v0.31 用户点名两连修:AI 注释块独占整行占满技能展示区;技能自带的描述/用法行
|
|
2895
|
+
// 回到「文本 | 开关」双栏的左列原宽度,给右侧胶囊开关列留位。头部行 = 名称 +
|
|
2896
|
+
// 自带描述/用法/无效行(左列)+ 右侧开关列,注释块铺满全宽垫底。
|
|
2897
|
+
const entryCard = { border: '1px solid var(--dsh-alias-border-l2)', borderRadius: '10px', padding: '11px 13px', marginBottom: '8px' }
|
|
2877
2898
|
|
|
2878
2899
|
const renderEntry = (entry) => {
|
|
2879
2900
|
const invalidLegacy = typeof entry.invalid === 'string' && entry.invalid.startsWith('legacy-invocation-key:')
|
|
@@ -2908,8 +2929,10 @@ window.__ModuleLoader__.load({
|
|
|
2908
2929
|
pillSwitch(entry.invocation.user, { testid: 'skill-switch-user-' + entry.name, disabled: !entry.writable, armed: confirmingKey === entry.id + ':user', title: confirmingKey === entry.id + ':user' ? translate('skills.switch.confirm') : undefined, onClick: () => void toggleSkill(entry, 'user') })),
|
|
2909
2930
|
data !== null && data.llmAvailable ? React.createElement('button', { type: 'button', 'data-testid': 'skill-describe-' + entry.name, onClick: () => void openDescribe(entry), style: { fontSize: '11px', padding: '3px 10px', borderRadius: '6px', border: '1px solid var(--dsw-alias-interactive-bg-hover)', background: 'transparent', color: 'var(--dsw-alias-label-secondary)', cursor: 'pointer' } }, '✨ ' + translate('skills.describe.button')) : null) : null
|
|
2910
2931
|
return React.createElement('div', { key: entry.id, 'data-testid': 'skill-entry-' + entry.name, style: entryCard },
|
|
2911
|
-
React.createElement('div', { style: {
|
|
2912
|
-
|
|
2932
|
+
React.createElement('div', { style: { display: 'flex', gap: '12px', alignItems: 'flex-start', justifyContent: 'space-between' } },
|
|
2933
|
+
React.createElement('div', { style: { minWidth: 0, flex: 1 } }, nameLine, descLine, usageLine, invalidLine),
|
|
2934
|
+
switches),
|
|
2935
|
+
noteLine)
|
|
2913
2936
|
}
|
|
2914
2937
|
|
|
2915
2938
|
const renderLogBox = (testid, lines, showHeader) => lines.length === 0 ? null : React.createElement('div', { 'data-testid': testid, style: { marginTop: '9px', padding: '7px 10px', borderRadius: '7px', background: 'var(--dsw-alias-bg-layer-3)', border: '1px solid var(--dsw-alias-border-l2)', maxHeight: '130px', overflowY: 'auto' } },
|
|
@@ -3505,8 +3528,8 @@ window.__ModuleLoader__.load({
|
|
|
3505
3528
|
const [modelErrorsOpen, setModelErrorsOpen] = useState(false)
|
|
3506
3529
|
const [toolErrorsOpen, setToolErrorsOpen] = useState(false)
|
|
3507
3530
|
const [modelsOpen, setModelsOpen] = useState(false)
|
|
3508
|
-
// 模型列表口径:today
|
|
3509
|
-
const [modelScope, setModelScope] = useState('
|
|
3531
|
+
// 模型列表口径:today=仅今日(默认,v0.31 用户点名)/ week=近 7 天 / all=宿主索引内全部日期累计。
|
|
3532
|
+
const [modelScope, setModelScope] = useState('today')
|
|
3510
3533
|
const [activeTab, setActiveTab] = useState('overview')
|
|
3511
3534
|
// 版本详情行内展开(不用浮层:弹层会被设置模态盖住)
|
|
3512
3535
|
const [channelOpen, setChannelOpen] = useState(false)
|
|
@@ -4294,7 +4317,7 @@ window.__ModuleLoader__.load({
|
|
|
4294
4317
|
// 重启区块复用共享组件(「重启」标签还承载设置页左列入口的显示开关;左侧入口默认关闭)
|
|
4295
4318
|
const restartBlock = React.createElement(RestartSection, { showNavToggle: true })
|
|
4296
4319
|
|
|
4297
|
-
const { enabled: notifyOn, done: notifyDoneOn, input: notifyInputOn, setEnabled: setNotifyOn, setDone: setNotifyDoneOn, setInput: setNotifyInputOn } = useNotifyState()
|
|
4320
|
+
const { enabled: notifyOn, done: notifyDoneOn, input: notifyInputOn, bell: notifyBellOn, setEnabled: setNotifyOn, setDone: setNotifyDoneOn, setInput: setNotifyInputOn, setBell: setNotifyBellOn } = useNotifyState()
|
|
4298
4321
|
const notifSupported = typeof Notification !== 'undefined'
|
|
4299
4322
|
const notifPermission = notifSupported ? Notification.permission : 'denied'
|
|
4300
4323
|
const notifySwitch = (on, onChange, disabled) => React.createElement('button', {
|
|
@@ -4327,7 +4350,9 @@ window.__ModuleLoader__.load({
|
|
|
4327
4350
|
: React.createElement('div', { style: { marginTop: '8px', display: 'flex', flexDirection: 'column' } },
|
|
4328
4351
|
notifyRow('notify-row-master', translate('notification.master'), null, notifyOn, setNotifyOn, false),
|
|
4329
4352
|
notifyRow('notify-row-done', translate('notification.done'), null, notifyDoneOn, setNotifyDoneOn, !notifyOn),
|
|
4330
|
-
notifyRow('notify-row-input', translate('notification.input'), null, notifyInputOn, setNotifyInputOn, !notifyOn)
|
|
4353
|
+
notifyRow('notify-row-input', translate('notification.input'), null, notifyInputOn, setNotifyInputOn, !notifyOn),
|
|
4354
|
+
// 铃铛显隐独立于通知总开关:藏掉只是收起输入框旁的快捷入口(v0.31 用户点名)。
|
|
4355
|
+
notifyRow('notify-row-bell', translate('notification.bellShow'), null, notifyBellOn, setNotifyBellOn, false))))
|
|
4331
4356
|
const overviewBlock = React.createElement('div', null, versionBlock, containerInfoBlock, overviewErrorsBlock)
|
|
4332
4357
|
// 任务通知独立成顶部标签(v0.14 起不再混在概览里)
|
|
4333
4358
|
const notifyBlock = React.createElement('div', null, notificationBlock)
|
|
@@ -4413,7 +4438,8 @@ window.__ModuleLoader__.load({
|
|
|
4413
4438
|
let dispose = null
|
|
4414
4439
|
const sync = () => {
|
|
4415
4440
|
if (dispose !== null) { dispose(); dispose = null }
|
|
4416
|
-
|
|
4441
|
+
// 铃铛显隐开关关闭时整条注销(v0.31 用户点名),通知行为不受影响。
|
|
4442
|
+
if (!featureEnabled('taskNotifications') || !notifyBellVisible) return
|
|
4417
4443
|
dispose = ctx.slots.register(
|
|
4418
4444
|
{ name: 'conversation.input.left', id: 'dsh-service-notify', order: 90, label: () => t('notification.bellOn') },
|
|
4419
4445
|
() => React.createElement(InlineNotifyBell, null),
|
|
@@ -4421,7 +4447,8 @@ window.__ModuleLoader__.load({
|
|
|
4421
4447
|
}
|
|
4422
4448
|
sync()
|
|
4423
4449
|
const unsubscribe = featureScope.subscribe(sync)
|
|
4424
|
-
|
|
4450
|
+
const unsubscribeBell = subscribeBellVisible(sync)
|
|
4451
|
+
return () => { unsubscribe(); unsubscribeBell(); if (dispose !== null) dispose() }
|
|
4425
4452
|
})
|
|
4426
4453
|
ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register(
|
|
4427
4454
|
{ name: 'sidebar.footer.action', id: 'dsh-service-update', order: 90, label: () => t('update.badge') },
|
package/index.js
CHANGED
|
@@ -28,7 +28,8 @@ const DEFAULT_FEATURE_SETTINGS = Object.freeze({
|
|
|
28
28
|
healthz: true,
|
|
29
29
|
skillManager: true,
|
|
30
30
|
subagentRoute: true,
|
|
31
|
-
|
|
31
|
+
// v0.31 用户点名:移动端适配默认改为关闭(需要时到插件配置卡打开)。
|
|
32
|
+
mobileAdaptation: false,
|
|
32
33
|
})
|
|
33
34
|
const FeatureSettingsSchema = z.object({
|
|
34
35
|
healthDiagnostics: z.boolean().default(true),
|
|
@@ -39,7 +40,7 @@ const FeatureSettingsSchema = z.object({
|
|
|
39
40
|
healthz: z.boolean().default(true),
|
|
40
41
|
skillManager: z.boolean().default(true),
|
|
41
42
|
subagentRoute: z.boolean().default(true),
|
|
42
|
-
mobileAdaptation: z.boolean().default(
|
|
43
|
+
mobileAdaptation: z.boolean().default(false),
|
|
43
44
|
})
|
|
44
45
|
const NPM_REGISTRY = 'https://registry.npmjs.org/'
|
|
45
46
|
const MAX_NPM_RESPONSE_BYTES = 256 * 1024
|
|
@@ -1007,6 +1008,20 @@ async function discoverQuotaCredential(ctx, kind, profile) {
|
|
|
1007
1008
|
return undefined
|
|
1008
1009
|
}
|
|
1009
1010
|
|
|
1011
|
+
/**
|
|
1012
|
+
* 凭据可用性探测(v0.31 用户点名:自动识别的 DeepSeek 行在 API KEY 未配置时整行不下发)。
|
|
1013
|
+
* 走与真实查询完全相同的发现链(settings apiKeyEnv → 凭据库 → 环境变量),发现即视为配置完成;
|
|
1014
|
+
* 任何异常(凭据服务缺席、resolve 抛错等)一律按未配置处理——调用方静默隐藏该行,
|
|
1015
|
+
* 绝不外呼、绝不下发错误提示,满足「进入余额查询标签页自动识别一次、失败不提示」。
|
|
1016
|
+
*/
|
|
1017
|
+
async function quotaCredentialConfigured(ctx, kind, profile) {
|
|
1018
|
+
try {
|
|
1019
|
+
return typeof (await discoverQuotaCredential(ctx, kind, profile)) === 'string'
|
|
1020
|
+
} catch (_) {
|
|
1021
|
+
return false
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1010
1025
|
/**
|
|
1011
1026
|
* zai-coding-cn(智谱 GLM Coding Plan)方言 → 统一窗口形状。
|
|
1012
1027
|
* 端点返回 data.limits[],每项一个额度窗口:
|
|
@@ -4207,6 +4222,11 @@ function apply(ctx) {
|
|
|
4207
4222
|
rows.push({ provider: profile.name, displayName: profile.displayName, adapted: false })
|
|
4208
4223
|
continue
|
|
4209
4224
|
}
|
|
4225
|
+
// v0.31 用户点名:自动识别出的 DeepSeek 行在 API KEY 未配置时整行不下发——
|
|
4226
|
+
// 额度页不出现卡片、不进节流轮询;每次快照(含进入标签页的那次拉取)重新
|
|
4227
|
+
// 走一遍凭据发现链,配置好后自动现身;探测失败一律静默视为未配置。
|
|
4228
|
+
// 显式在 UI 适配过的不受此限(用户主动选择,照常显示填写入口)。
|
|
4229
|
+
if (kindSource === 'auto' && kind === 'deepseek' && !(await quotaCredentialConfigured(ctx, kind, profile))) continue
|
|
4210
4230
|
if (refreshAll || requestedProviders.has(profile.name)) kickQuotaRefresh(profile, kind, config)
|
|
4211
4231
|
const view = quotaThrottle.view(profile.name)
|
|
4212
4232
|
const windows = Array.isArray(view.windows) ? view.windows : []
|
|
@@ -4515,6 +4535,7 @@ export {
|
|
|
4515
4535
|
parseQuotaConfigText,
|
|
4516
4536
|
parseSkillFrontmatterData,
|
|
4517
4537
|
parseSubagentRouteText,
|
|
4538
|
+
quotaCredentialConfigured,
|
|
4518
4539
|
quotaCredentialHintNames,
|
|
4519
4540
|
quotaEndpointFor,
|
|
4520
4541
|
quotaErrorCode,
|
|
@@ -4562,6 +4583,7 @@ export default {
|
|
|
4562
4583
|
parseQuotaConfigText,
|
|
4563
4584
|
parseSkillFrontmatterData,
|
|
4564
4585
|
parseSubagentRouteText,
|
|
4586
|
+
quotaCredentialConfigured,
|
|
4565
4587
|
quotaCredentialHintNames,
|
|
4566
4588
|
quotaEndpointFor,
|
|
4567
4589
|
quotaErrorCode,
|