@gehennawu/dsh-service 2.0.6 → 2.0.8

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 CHANGED
@@ -9,9 +9,9 @@
9
9
  <em>DeepSeek Harness (DSH) Web 服务控制与运维插件。</em>
10
10
  </p>
11
11
 
12
- [![Version](https://img.shields.io/badge/version-2.0.6-3b82f6.svg?style=flat-square)](package.json)
12
+ [![Version](https://img.shields.io/badge/version-2.0.8-3b82f6.svg?style=flat-square)](package.json)
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-10b981.svg?style=flat-square)](LICENSE)
14
- [![DSH Compatibility](https://img.shields.io/badge/DSH-%E2%89%A50.1.1--rc.2%20%C2%B7%20compatible%20with%200.2.1--alpha.1-6366f1.svg?style=flat-square)](https://github.com/deepseek-ai/deepseek-harness)
14
+ [![DSH Compatibility](https://img.shields.io/badge/DSH-%E2%89%A50.1.1--rc.2%20%C2%B7%20compatible%20with%200.2.1--alpha.2-6366f1.svg?style=flat-square)](https://github.com/deepseek-ai/deepseek-harness)
15
15
  [![Cordis](https://img.shields.io/badge/Cordis-v4.x-f59e0b.svg?style=flat-square)](https://cordis.moe/)
16
16
  [![Platform](https://img.shields.io/badge/platform-DSH%20Web-ec4899.svg?style=flat-square)](https://github.com/gehennawu/dsh-service)
17
17
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/gehennawu/dsh-service/issues)
@@ -41,7 +41,7 @@ A service-control and operations plugin for DSH Web: safe restart, version manag
41
41
 
42
42
  - [🚀 Features](#-features)
43
43
  - [Version and updates](#version-and-updates) · [Safe restart](#safe-restart) · [Health diagnostics](#health-diagnostics) · [Model statistics](#model-statistics)
44
- - [Quota lookup](#quota-lookup) · [Backup management](#backup-management) · [Skills management](#skills-management) · [Subagent model](#subagent-model)
44
+ - [Quota lookup](#quota-lookup) · [Model configuration](#model-configuration) · [Backup management](#backup-management) · [Skills management](#skills-management) · [Subagent model](#subagent-model)
45
45
  - [Task notifications](#task-notifications) · [Session manager](#session-manager) · [Mobile adaptation](#mobile-adaptation) · [Model provider icons](#model-provider-icons) · [Right-Sidebar file editing](#right-sidebar-file-editing) · [External liveness probe](#external-liveness-probe)
46
46
  - [🏗️ Architecture](#-architecture)
47
47
  - [⚡ Installation](#-installation) · [🔄 Automatic restart](#-automatic-restart) · [🖥️ Platform support](#-platform-support)
@@ -49,9 +49,9 @@ A service-control and operations plugin for DSH Web: safe restart, version manag
49
49
 
50
50
  ## 🚀 Features
51
51
 
52
- The Settings "Service Control" panel has a six-page navigation: **Overview · Model stats · Quota lookup · Health · Maintenance · Configuration**; "Maintenance" aggregates five subpages — Sessions · Skills · Subagents · Backups · Restart — and "Configuration" aggregates Features · Task notifications · Settings Nav. Restart, Quota lookup, and Sessions can each enable a **quick entry in the settings left navigation** (off by default; the Skills and Subagents sidebar entries were removed).
52
+ The Settings "Service Control" panel has a seven-page navigation: **Overview · Model stats · Quota lookup · Model configuration · Health · Maintenance · Configuration**; "Model configuration" contains Providers & models / Subagent routing, "Maintenance" aggregates four subpages — Sessions · Skills · Backups · Restart — and "Configuration" aggregates Features · Task notifications · Settings Nav. Restart, Quota lookup, and Sessions can each enable a **quick entry in the settings left navigation** (off by default; the Skills and Subagents sidebar entries were removed).
53
53
 
54
- Under **Plugins → Plugin configuration**, twelve host-level switches: **Health diagnostics, Model statistics, Quota lookup, Backup maintenance, Task notifications, Skill manager, Subagent model, Session manager, Mobile adaptation, Model provider icons, Right-Sidebar file editing, `/healthz` liveness endpoint** (all on by default except Mobile adaptation). All are live settings: disabling hides the UI, stops polling/subscriptions, and makes the host reject that capability; Overview and Restart stay available.
54
+ Under **Plugins → Plugin configuration**, thirteen host-level switches: **Health diagnostics, Model statistics, Quota lookup, Model configuration, Backup maintenance, Task notifications, Skill manager, Subagent model, Session manager, Mobile adaptation, Model provider icons, Right-Sidebar file editing, `/healthz` liveness endpoint** (all on by default except Mobile adaptation). All are live settings: disabling hides the UI, stops polling/subscriptions, and makes the host reject that capability; Overview and Restart stay available.
55
55
 
56
56
  ![Plugin configuration](./screenshots/plugin-config_en.png)
57
57
 
@@ -66,7 +66,7 @@ Under **Plugins → Plugin configuration**, twelve host-level switches: **Health
66
66
 
67
67
  ![Configuration page](./screenshots/configuration_en.png)
68
68
 
69
- - Maintenance groups Sessions, Skills, Subagents, Backups, and Restart; it remembers the most recent subpage and falls back to an available item when a feature is disabled
69
+ - Maintenance groups Sessions, Skills, Backups, and Restart; subagents have a separate subpage under Model configuration, with no duplicate entry; it remembers the most recent subpage and falls back to an available item when a feature is disabled
70
70
  - Configuration groups feature switches, task notifications, and settings nav tabs; switches are grouped and apply live, Notifications stays visible but disabled when that feature is off, and Settings Nav supports manual reordering (drag / arrows) and visibility management of all tabs in the settings dialog sidebar, persisted to the server-side unified config file `$DSH_HOME/dsh-service-config.json` (cross-device sync, auto-fetched on startup with a local-cache fallback), applying live (the Service Control tab is permanently locked visible to prevent lockout); quota card order and visibility use the same config (the `quotaCards` section, fetched when the quota page opens)
71
71
  - Unified plugin config file: lightweight per-feature preferences converge into a single `$DSH_HOME/dsh-service-config.json` (atomic write, `0600`), partitioned by feature section — updating or clearing one section never touches others; large caches (usage index etc.) and encrypted credentials stay out of this file
72
72
 
@@ -111,6 +111,7 @@ Under **Plugins → Plugin configuration**, twelve host-level switches: **Health
111
111
  - **Indexed usage is kept permanently**: deleting a session or a project folder never subtracts history, so totals and the heatmap are always the complete history since the index was built; an index version bump preserves old summaries until readable sessions are fully rebuilt successfully; failures retain history and are retried
112
112
  - When a **project folder no longer exists**, that project disappears from the project filter, yet its usage still counts toward the "All projects" totals, day buckets, and model breakdown
113
113
  - Last-48-hour model/tool errors (collapsed by default, rendered only when present)
114
+ - **Opening the panel syncs automatically — no manual refresh needed**: the mount sync always runs once (it renders the persisted cache first, then folds the delta in the background); after that it only syncs on **real edges** — crossing local midnight (the "Today" card lands on the new day), returning from a background tab, and each time you enter the usage page. Idling on the page arms no polling at all, and other pages pay none of that cost. The refresh button reads "Refreshing…" during a sync (about 0.3 s when idle, but tens of seconds when historical-format sessions exist and the corpus is changing)
114
115
  - Steps whose provider reports no token usage are excluded
115
116
  - A session that cannot be read, migrated, or parsed no longer blocks other sessions. An all-projects warning shows successful/skipped counts and expandable session IDs, error categories, and safe summaries. Previously indexed data is retained and marked stale; first-time failures contribute nothing and are retried on the next refresh. Only global errors, such as an unavailable service, failed session listing, or failed index write, fail the entire refresh. The warning can be dismissed (remembered locally), and silencing it mutes only that same failure set: a newly skipped session or a changed error category brings it back, so dismissing once never hides a new problem for good
116
117
 
@@ -137,9 +138,11 @@ Under **Plugins → Plugin configuration**, twelve host-level switches: **Health
137
138
  | CLIProxyAPI deployment | Official remaining quota of each OAuth upstream account |
138
139
 
139
140
  - Credentials go into the DSH credential store (`$DSH_HOME/.credentials.yaml`, hot-effective): an API key, the CPA management key, the Xiaomi console cookie, or the StepFun Step Plan console token (Oasis-Token; the `Oasis-Webid` is derived from the token automatically — no manual entry); the Command Code quota plane reuses the inference key, so no extra credential is needed
140
- - Anti-rate-limit pacing: 60 s result cache, exponential backoff (30 s doubling, capped at 15 min); querying is fully manual — a snapshot is fetched when you open the quota page, expand the ring, or hit refresh, and the client never arms a periodic poll (upstream throttling still lives entirely in the host)
141
+ - **Credentials can be changed at any time**: every adapted card keeps a credential entry in its **Config** area — unconfigured rows show the per-channel wording, configured rows show "Edit credential (configured)", where the stored value can be overwritten (key rotation, another account, a typo) or cleared; the value is only ever sent with the save request and is never echoed back
142
+ - Anti-rate-limit pacing: 60 s result cache, exponential backoff for transient network errors (30 s doubling, capped at 15 min); **deterministic failures switch to manual** — credential errors, 4xx, expired plans, management API disabled, unexpected response shapes and anything else only a config change can fix stop the automatic retries (the error line then reads "Auto-retry paused — retry manually"), and only a successful manual reconnect via the refresh button (5 s cooldown) returns the channel to automatic mode; querying is fully manual — a snapshot is fetched when you open the quota page, expand the ring, or hit refresh, and the client never arms a periodic poll (upstream throttling still lives entirely in the host)
143
+ - **CLIProxyAPI management-plane bans get their own notice**: five consecutive auth failures from one IP ban that IP for 30 minutes on the management plane; while banned the card reads "Temporarily banned by CLIProxyAPI" plus the upstream message (including the remaining time). That state offers no credential form (the key is fine) — wait for the ban to expire, or change the host's egress IP, then hit "Retry connection". The ban covers the management plane only; model inference through that CPA is unaffected
141
144
  - CLIProxyAPI: when an account's live query fails, its last cached snapshot windows are shown with a "cached" badge; snapshot windows whose reset time has already passed (the window they described has ended) are dropped, avoiding the illusion of quota stuck on yesterday
142
- - Failures state their real reason: cards and the ring show "error copy (HTTP status · failing endpoint · failing account · upstream message) · next automatic retry" — a wrong key, an unpaid balance, rate limiting, and a moved endpoint each read differently instead of one generic notice; an upstream 401/403 is classified as "credential rejected by upstream" and the card keeps its credential form available, and business error codes inside an HTTP 200 envelope are classified the same way (auth failure → credential rejected, expired plan → no active subscription, vendor-side failure → upstream service error) instead of being reported as "unexpected response format"
145
+ - Failures state their real reason: cards and the ring show "error copy (HTTP status · failing endpoint · failing account · upstream message) · next automatic retry" — a wrong key, an unpaid balance, rate limiting, and a moved endpoint each read differently instead of one generic notice; an upstream 401/403 is classified as "credential rejected by upstream" and the card keeps its credential form available, and business error codes inside an HTTP 200 envelope are classified the same way (auth failure → credential rejected, expired plan → no active subscription, vendor-side failure → upstream service error) instead of being reported as "unexpected response format"; rows switched to manual mode end with "Auto-retry paused — retry manually" instead of a retry time
143
146
  - API keys are resolved only inside the host process; the browser receives normalized window data only; unadapted providers are never requested
144
147
 
145
148
  ### Backup management
@@ -153,7 +156,9 @@ Under **Plugins → Plugin configuration**, twelve host-level switches: **Health
153
156
  - Export download / import upload / delete (two-step confirmation); unlimited, never auto-pruned; imported archives must pass the same integrity inspection used by restore
154
157
  - **Integrity inspection** validates gzip/tar structure, paths, and entry types. Only `sessions`, the three allowlisted config files, `profiles/<name>/package.json`, and `meta/backup.json` are accepted; traversal, links, special files, unknown content, corrupt archives, and invalid profile manifests are rejected
155
158
  - **Restore preflight** produces a single-use five-minute plan showing the full sessions replacement, config replacements/removals, and profile-manifest updates. Final commit rechecks the archive SHA-256 and current-target fingerprint; any drift rejects the restore
156
- - Restore uses a transaction journal and rollback directory: sessions are replaced in full, config is made exact to the snapshot, and profiles update package.json only while keeping node_modules, credentials, and attachments. Managed runtimes restart automatically; manual launches receive hand-restart instructions
159
+ - **Cross-device / cross-platform restore**: every path inside an archive is relative and restore lands under the local `$DSH_HOME`, so a different DSH_HOME, username, or mount point makes no difference to import; `meta/backup.json` also records the source platform and architecture, and the preflight reports when the source system differs from the current one. The preflight additionally lists read-only notices (they never block a restore): paths past the classic Windows limit, paths differing only by case collapsing on this volume, and a sessions directory that is currently unwritable (in use or lacking permissions)
160
+ - **Target profile selection**: you choose which local profile the archive's `profiles/<name>` content restores into — candidates come from the profiles that actually exist here (the active one is marked), or you can keep the source name. A cross-name mapping does **not** overwrite the local profile's plugin manifest (`package.json`) by default; tick the box to overwrite it explicitly. The Profile configuration layer (`cordis.patch.yml`) is restored whenever the archive carries it
161
+ - Restore uses a transaction journal and rollback directory: sessions are replaced in full, config is made exact to the snapshot, and profiles update according to that mapping while keeping node_modules, credentials, and attachments. Managed runtimes restart automatically; manual launches receive hand-restart instructions. On Windows, a target left in use by another process fails with explicit guidance (quit the desktop app, pause sync clients, disable real-time antivirus scanning, then retry)
157
162
 
158
163
  ### Skills management
159
164
 
@@ -165,6 +170,45 @@ Under **Plugins → Plugin configuration**, twelve host-level switches: **Health
165
170
  - Entries with legacy camelCase keys are dropped by the official parser: ⚠ warning + one-click canonical fix
166
171
  - ✨ Fill with AI: pick a model to draft a description (follows the UI language), saved to a plugin sidecar index — **SKILL.md is never modified**; one-click batch fill runs in the host background and can be cancelled. Already-annotated skills are listed separately in the plan and are only overwritten after a "Confirm forced refill" second confirmation (annotating no longer blocks future batch fills forever); completion-log timestamps use your local timezone
167
172
 
173
+ ### Model configuration
174
+
175
+ > Model-configuration writes require a host with trackable configuration revisions. Older hosts without this capability remain view-only and writes are explicitly refused. Legacy workbench source and tests are retained as compatibility regression assets; the current UI and host register only the models3 flow. Sensitive authentication headers and historical manual-recovery states must be handled through native DSH configuration/recovery rather than automatically cleared here.
176
+
177
+ - A top-level **Model configuration** page: the left sub-tab **Channels & models** (default) plus the right **Subagent routing** (reusing the "Subagent model" block; the sub-tab bar disappears entirely when only one sub-page applies). There is **one interface only** — the old hidden localStorage Simple/Legacy split is retired
178
+ - **Vertical accordion**: providers are laid out as cards in two groups (built-in / custom); clicking a title row expands that provider's detail, one card is open at a time, and clicking again collapses it. On narrow screens (≤640px) only the inner padding shrinks — it **never becomes a side-by-side layout**. Switching providers with unsaved edits asks first
179
+ - **Channel-first loop**: provider list → "Add custom provider" (id / display name / protocol / address / API key / manual or picked model IDs) → provider detail → model list · independent model detail · separate credential replacement · deletion and latest restore. Creating does not automatically discover models; an entered API key is stored under an independent credential reference after creation is confirmed
180
+ - **This page edits only a few provider fields**: display name, address, protocol (editable for custom providers; built-in providers show the currently resolved protocol read-only) and credentials. Provider-level advanced keys — provider defaults, headers, thinkingBudgets, transport/cache, timeouts, image budgets and retryPolicy — have no editor here, and **saving never clears them** (writes merge as field patches; keys that are not shown survive untouched). No second JSON editing mode either
181
+ - **Anthropic model discovery**: `anthropic-messages` uses `/v1/models?limit=1000`, accepts base URLs with or without trailing `/v1`, sends `x-api-key` and `anthropic-version`, and reads `display_name`. Only one page is fetched; pagination is not followed automatically. Existing public-address and response guardrails remain in force rather than using the official unguarded fetch path
182
+ - **Discovery and manual entry are peers**: the network is contacted only when you click "Fetch online"; results distinguish already-added from addable models and support search plus multi-select batch adds. A failed discovery, an unsupported protocol or a refused probe only reports the reason — **entering ids manually always keeps working**. Listing models does not prove they can be called
183
+ - **The per-model panel**: the model ID is **editable** (including rows added from an online discovery list); the value is bounded in three ways — it must match the model-ID format, it may not duplicate another ID on the same provider, and a rename is likewise refused when it collides with an existing ID, and disabled models must be explicitly enabled before renaming. A rename is submitted as "delete the old, add the new", so it still passes the deletion-reference guard. The same panel carries display name, context window / max output tokens (**accepts K/M suffixes such as `256K` or `1M`**; blank means inherit the catalog default), input modalities (the text box is checked and disabled, only image can be toggled; saved as an explicit host-schema declaration), output modalities (display only, text — the pi-ai model profile has no such field) and reasoning levels. The save button sits at the **bottom** of the panel next to the "unsaved" status and is disabled until something changes; submitting writes only the changed fields
184
+ - **The advanced model-parameter area has no collapsible header**: input modalities and reasoning levels are always expanded inside model detail, not hidden behind a disclosure title
185
+ - **Deleting a model or a provider both check references**: the confirmation first lists the **exact references** — main-chat default model, subagent primary model, subagent fallback model — each as `provider / model`, and any reference blocks deletion. A custom provider cannot lose its final model, and inherited-provider reset is not presented as deletion
186
+ - **Enabling and disabling apply immediately, with no second confirmation**: the provider switch in the header and each model-row switch commit as soon as you click them (the host fetches a plan and submits it within the same click; no confirmation dialog follows). The provider switch's hover text states that disabling makes the provider unselectable and blocks new main-chat and subagent calls while keeping configuration and credentials. Disabled state is Profile-scoped, and disabled providers leave composer and subagent catalogs and reject new exact-model calls, including saved routes, explicit overrides and prepared calls not yet dispatched. Already-running calls are not forcibly cancelled. This is plugin policy, not an official `enabled` configuration field; unloading the plugin removes enforcement
187
+ - **A basic call test is not a capability audit**: the model-row test action directly tests the saved entry without a second fee confirmation. The host sends fixed short text with a 30,000-token / 120-second ceiling and may incur charges. Elapsed time is shown while running; failures expose sanitized codes, HTTP status, finish reason and details. It verifies only a basic text call, not images, capacity or every reasoning level. A failure never rolls back saved configuration
188
+ - **Built-in inheritance is not a gap**: built-in channels inherit capability fields from the catalog (runtime falls back to the catalog base), so "not written in settings" does not mean "missing capability" — you will not be nagged to fill in reasoning levels
189
+ - **Manual editor**: id / name / context / max output always visible; the capabilities area is always expanded (two vision switches, six equal-width horizontal reasoning checkboxes (off is not shown); metadata wire mappings are retained, but wire-edit inputs are currently not visible). Clearing a field removes the explicit override to inherit the default, and `reasoningEfforts: false` explicitly declares "not supported" (distinct from omitting it)
190
+ - **Deletion keeps the credential and is restorable**: deleting a channel removes only the route from settings and keeps the credential reference (other channels sharing it are unaffected). The full pre-deletion configuration goes into a snapshot first, and the **restore entry stays at the bottom of the list** instead of disappearing with the row. Restore writes the reference back but **never the old key value**
191
+ - **Configuration is not a call-time selection**: effort checkboxes declare selectable levels, not the level selected for every call. Unconfigured levels inherit existing values or leave behavior to the API default; they do not automatically disable reasoning. Smart fill retains mappings explicitly declared by metadata. Manually checking a non-off level currently uses its English name as the wire value, whose suitability must be checked. Saving requires at least one non-off mapping
192
+ - **Drafts stay on the page**: a failed save or a concurrent conflict never clears the draft, and a conflict asks you to re-confirm against the latest values; leaving with unsaved edits warns first; drafts are never persisted across refreshes (so keys never reach browser storage)
193
+ - **Built-in and custom catalogs are handled separately**: built-in providers show the resolved protocol and allow adding out-of-catalog models or editing current Profile model entries. Catalog defaults are displayed as inherited values rather than copied wholesale when unchanged. Unknown overrides and advanced fields are retained; the entire dynamic catalog is not frozen
194
+ - **Zero external network on the overview**: reads the current Profile, pi-ai catalog, runtime providers and sidecar, with saved / loaded / basic-call status shown separately. Opening the provider list does not query external metadata
195
+ - **Per-model smart fill**: OFF whenever the detail opens. ON queries the local pi-ai index and fixed modelparams.dev / OpenRouter sources, **never provider discovery, inference or automatic save**. **This round is model-ID-scoped filling: it binds no current-provider or protocol source**, and the UI has no source picker or binding action (candidates are evidence only and are never written into configuration)
196
+ - **Suggestions shown per parameter**: display name, context capacity, maximum output, input modalities and reasoning levels are shown separately. Modalities are compared as sets, so ordering is not a difference. Suggestions use a stable source order; reasoning prefers explicitly declared mappings without filtering by the current provider, and missing mappings never fabricate levels. modelparams request ranges are not model output limits. Metadata is reference information, not a real API capability test
197
+ - **One confirmation to fill**: "Fill all" first lists which draft fields it will replace; confirming **only updates the draft**, writes no configuration and still requires Save. There is no source binding or per-field ✓ / ✕. Hovering the switch explains the sources and operation boundaries
198
+ - **The two reasoning notices are distinct**: when the model metadata declares reasoning support but no concrete level suggestions were found, the panel says reasoning is supported yet the level values are unavailable and the API default is unknown; when there is no reasoning metadata at all it says no reasoning level suggestions were found and to configure them manually. **Absent metadata is not "unsupported"**; only `reasoningEfforts: false` declares that explicitly
199
+ - **Effort badges use English level names**: current and suggested levels render as `minimal / low / medium / high / xhigh / max` badges regardless of the UI language (the `models.smart.effort.*` Chinese dictionary is retained but not used here); the "Current levels / Suggested levels" labels themselves still follow the UI language
200
+ - **Snapshot before mutation**: ordinary save is one click; delete / credential replacement / restore preview consequences before confirmation. Snapshot failure prevents configuration writes. Creation, edits and restore capture owner / full fingerprint / revision and reject concurrent overwrite; subsequent external edits block undo. Ordinary snapshots exclude sensitive headers; deletion is refused if sensitive values cannot be restored in full safely
201
+ - **Online verification**: looks a model up in the OpenRouter model directory by id (fixed domain, redirects not followed); results are shown for reference only and never change your config
202
+
203
+ #### URL and credential safety
204
+
205
+ - **No input concatenation**: address edits are schema-validated configuration data; entering or saving an address never fetches it. Discovery uses only the **baseURL read from the current host Profile**. Model IDs / names never enter shell commands, filesystem paths or command arguments; metadata requests use fixed host-owned source domains
206
+ - **Online discovery guardrails**: HTTPS-only, no userinfo, port empty or 443, IP literals refused, **every** DNS resolution result must be outside private/reserved ranges, redirects refused, the resolved address pinned for a direct connection with the correct Host header (anti DNS-rebinding), 10s timeout, ≤4MB transferred and ≤8MB decompressed. Non-public / plain-HTTP / non-443 endpoints can still be saved and written by hand — they are simply barred from online discovery and labelled "not verified by the plugin"
207
+ - **Separate credential management**: replacement values pass over loopback into the system store, never returned, logged or snapshotted. Defaults to a new independent reference, not overwriting shared credentials. Explicit shared replacement previews users; configuration failure reports stored credentials honestly and never deletes them as compensation. Discovery resolves `apiKeyEnv` and sends it only to the current provider address, never metadata sites
208
+ - **Short-lived host handles**: editing baselines, deletion, restore and credential consequences bind to `sessionId` / `planId`; submission re-checks the complete baseline and revision. Unknown, expired or replayed handles are rejected. Manual parameters pass a closed schema vocabulary
209
+ - **Structural write protection**: writes use `settings.mutate(entryId, ops, expectedRevision)` with the current pi-ai owner's dynamically resolved entry ID; only legacy hosts use `llm-pi-ai`. Complete official validation, atomic persistence and live reconciliation are retained; no direct writes to real configuration YAML
210
+ - **Loopback only**: every endpoint is exposed through the single-segment `/dsh-service` channel with `loopback` authority
211
+
168
212
  ### Subagent model
169
213
 
170
214
  ![Subagent model](./screenshots/subagent-model_en.png)
@@ -177,7 +221,7 @@ Under **Plugins → Plugin configuration**, twelve host-level switches: **Health
177
221
  - **Mode switching keeps the config** — the saved custom model (including its reasoning effort) and the fallback list survive switching between the three modes; a mode only decides whether the route applies, so switching back to "Custom" needs no re-selection (provider/model missing from the runtime catalog fall back to the first catalog entry)
178
222
  - **No shift on entry** — the page seeds its first frame from the most recent successful read, so it lands directly on the real mode instead of showing "Default" and then jumping to "Custom"; on a fresh install or a different browser (no cache) it briefly shows "Reading configuration…"
179
223
  - **Fallback models (in order)** — both Follow and Custom modes accept an ordered fallback list: when the primary route is unavailable (channel unloaded, or quota state marks it unserviceable), the next model is tried in order; if none works, delegations fall back to native inheritance instead of failing. Fallback entries pass the same allow-list check as the primary route, with an optional reasoning effort per entry
180
- - **Conversation-page visibility** — a session-level line sits under the composer listing the models your subagents actually ran on, e.g. `Subagents: cpa/gpt-5.6-luna (xhigh) · opencode-go/deepseek-v4-flash (max)` — covering fallback hits, explicit routes, and inherited sources, so you can verify the custom route at a glance. The line is mounted on the official `conversation.composer.dock` slot, occupying its own line right below the official stats instead of sharing their row; 20s refresh, independent of turn data (it keeps showing even when compaction folds the delegation tool calls), covered from the host dispatch records and visible in any view; switch it off independently from Maintenance → Subagent. Records live in host memory (survive page reloads, cleared on process restart). Works regardless of the official "assign subagent models" switch
224
+ - **Conversation-page visibility** — a session-level line sits under the composer listing the models your subagents actually ran on, e.g. `Subagents: cpa/gpt-5.6-luna (xhigh) · opencode-go/deepseek-v4-flash (max)` — covering fallback hits, explicit routes, and inherited sources, so you can verify the custom route at a glance. The line is mounted on the official `conversation.composer.dock` slot, occupying its own line right below the official stats instead of sharing their row; 20s refresh, independent of turn data (it keeps showing even when compaction folds the delegation tool calls), covered from the host dispatch records and visible in any view; switch it off independently from Model configuration → Subagent routing. Records live in host memory (survive page reloads, cleared on process restart). Works regardless of the official "assign subagent models" switch
181
225
  - Config stored in `$DSH_HOME/dsh-service-subagent-route.json` (atomic writes, `0600`); one-click reset
182
226
 
183
227
  ### Task notifications
@@ -261,7 +305,7 @@ The plugin is a Cordis two-half structure: the **Host half (`index.js`)** owns a
261
305
  ```mermaid
262
306
  flowchart TB
263
307
  subgraph Client["🌐 Client browser half (client.js)"]
264
- UI["Settings : Service Control panel (six-page navigation + quick entries)<br/>quota ring · notification bell · mobile adaptation"]
308
+ UI["Settings : Service Control panel (seven-page navigation + quick entries)<br/>quota ring · notification bell · mobile adaptation"]
265
309
  end
266
310
 
267
311
  subgraph Host["⚙️ Host half (index.js)"]
@@ -305,7 +349,7 @@ Key contracts:
305
349
  1. Open the **Plugins** page in the left sidebar.
306
350
  2. Click the **+ Add plugin** button in the top-right corner.
307
351
  3. In the dialog, fill in **one of the three** source forms (the same field accepts all three, and the hint below it follows what you type):
308
- - **npm package name (recommended)**: `@gehennawu/dsh-service`;
352
+ - **npm package name (recommended)**: `@gehennawu/dsh-service@2.0.8` — **include the exact version** (written as `package@version`);
309
353
  - **GitHub repository URL**: `https://github.com/gehennawu/dsh-service` (public repository, no token needed; the shorthand `github:gehennawu/dsh-service` works too);
310
354
  - **Local plugin directory**: an absolute path on this machine, e.g. `/path/to/dsh-service` (for your own or pre-downloaded plugins).
311
355
  4. Pick the **install source** for your network: the default **official npm registry**, or the **China mainland mirror** if fetching from npm is restricted there (it affects package-name installs only; GitHub URLs and local directories are unaffected).
@@ -316,15 +360,17 @@ Key contracts:
316
360
 
317
361
  > Tip: all three forms go through the official installation path — no manual profile edits; entering a GitHub URL in the dialog is equivalent to the `github:` prefix on the command line.
318
362
 
363
+ > **Why the npm form wants a version**: pnpm 11 enables a supply-chain release cooldown by default (`minimumReleaseAge`, 1440 minutes). **Without a version** (including `@latest`), a release published less than the cooldown period ago is not installed automatically, so the package manager falls back to the newest version that has cleared it — which is why the plugin page may report that it installed x.y.z instead of the latest a.b.c. Writing an **exact version** (e.g. `@gehennawu/dsh-service@2.0.8`) installs exactly the release you name. The cooldown is a supply-chain safety policy, not a sign that the new release is broken; if you are not in a hurry, installing without a version after the 24 hours have passed also reaches the new release.
364
+
319
365
  ### Option 2: Command line
320
366
 
321
367
  | Method | Command |
322
368
  | --- | --- |
323
- | npm (recommended) | `dsh plugin --profile web add @gehennawu/dsh-service` |
369
+ | npm (recommended) | `dsh plugin --profile web add @gehennawu/dsh-service@2.0.8` |
324
370
  | GitHub | `dsh plugin --profile web add github:gehennawu/dsh-service` |
325
371
  | Local development | `dsh plugin --profile web add link:/path/to/dsh-service` |
326
372
 
327
- The command line uses the default npm / pnpm registry; if you need a mirror, point npm / pnpm's registry at one the usual way first, then run the command above.
373
+ The command line uses the default npm / pnpm registry; if you need a mirror, point npm / pnpm's registry at one the usual way first, then run the command above. The npm method is subject to the same pnpm release cooldown: without a version it installs the newest release that has cleared the cooldown, while an exact version (`@gehennawu/dsh-service@2.0.8`) installs that release (see the note under Option 1).
328
374
 
329
375
  Restart DSH Web after installing or updating:
330
376
 
@@ -376,7 +422,9 @@ pm2 start "dsh web --host 127.0.0.1" --name dsh-web
376
422
 
377
423
  Requirements: Node.js `>=22`, and a DSH Web installation capable of loading both Host and Client plugin halves. Update checks require access to `registry.npmjs.org`; network failures do not affect other features.
378
424
 
379
- **DSH compatibility statement**: adapted to DSH `0.2.1-alpha.1` — session format V4 (V3 logs are migrated by the official layer into a `session.v4.jsonl.zstd` generation file on first read, with the old `session.v3.jsonl.zstd` retained per the format catalog's policy; the detail view automatically archives system events), the `SettingsForms` configuration surface (plugin config now lives in the Profile's `cordis.patch.yml` with hot updates over `loader/volatile-update`, while the legacy `settings.register` remains authoritative on 0.1.5/0.1.6 so the two never cross-contaminate on a shared host), and all existing V3 adaptation (the `system/message` history, the handle-based sessionPersistence, the official right sidebar, the object-shaped turn-process), plus dual-hash compatibility for mobile bottom-row triggers, subagent turn-tail list-slot adaptive compatibility, `plugins.bundle.config` slot injection, and session-detail open fallback through `uiWorkspace`. Older DSH releases (`>=0.1.1-rc.2`) remain supported. The declared range now extends through `0.2.1-alpha.1` (`engines.dsh` upper bound `<0.2.1-alpha.2`); source-level, npm-artifact-level (per-source-module sha256 via rolldown `//#region` slices) and real-host mounting reviews of 0.2.1-alpha.1 (266 commits) confirmed that 17 of the 20 existing seams are region-identical and the 3 changed ones do not touch this plugin's contracts, that the 9 slot keys, 25 theme tokens and the 35 official `data-*` hooks this plugin uses show zero removals, and that host RPC, `webServer`, the settings surface, persistence and session-format artifacts are byte-identical; the removed runtime invariant mechanism (each package's `./invariant` export 38->0, `dsh-invariants` retired) is unreferenced here, and the upstream fix for toggling a plugin removing other plugins' styles benefits this plugin. **The single display-level break is adapted in dual mode**: upstream split the composer stats row into `activity`/`usage` entries and dropped the row root class `.root`, so the mobile rules keep the legacy `[class*="bOPqQW_root"]` anchor for older hosts and add the official `[data-composer-stat]` hook plus the <=375px font step for the new host; the 0.2.0-rc.2 MenuGroup group-heading dual selector remains effective and the Client Inspect hang defect stays fixed as of 0.2.0-rc.2. The plugin-version gate introduced in rc.1 reads only `peerDependencies` (this plugin declares none, so the gate does not apply); real-host mounting on DSH `0.2.1-alpha.1` is operational. The settings surface and the persistence/layout seams all run in dual shapes detected from runtime capabilities, and adaptation items that target newer structures are naturally inert on older hosts (cosmetic only, no functional loss). Note: sessions written after upgrading cannot be read by older DSH releases — **backups do not restore across a version downgrade**. The plugin marketplace judges compatibility from the `engines.dsh` range in `package.json`, which is the single declaration of the supported range.
425
+ **DSH compatibility statement (alpha.1 baseline)**: adapted to DSH `0.2.1-alpha.1` — session format V4 (V3 logs are migrated by the official layer into a `session.v4.jsonl.zstd` generation file on first read, with the old `session.v3.jsonl.zstd` retained per the format catalog's policy; the detail view automatically archives system events), the `SettingsForms` configuration surface (plugin config now lives in the Profile's `cordis.patch.yml` with hot updates over `loader/volatile-update`, while the legacy `settings.register` remains authoritative on 0.1.5/0.1.6 so the two never cross-contaminate on a shared host), and all existing V3 adaptation (the `system/message` history, the handle-based sessionPersistence, the official right sidebar, the object-shaped turn-process), plus dual-hash compatibility for mobile bottom-row triggers, subagent turn-tail list-slot adaptive compatibility, `plugins.bundle.config` slot injection, and session-detail open fallback through `uiWorkspace`. Older DSH releases (`>=0.1.1-rc.2`) remain supported. The alpha.1 review established the previous declared range through `0.2.1-alpha.1` (then bounded by `<0.2.1-alpha.2`); source-level, npm-artifact-level (per-source-module sha256 via rolldown `//#region` slices) and real-host mounting reviews of 0.2.1-alpha.1 (266 commits) confirmed that 17 of the 20 existing seams are region-identical and the 3 changed ones do not touch this plugin's contracts, that the 9 slot keys, 25 theme tokens and the 35 official `data-*` hooks this plugin uses show zero removals, and that host RPC, `webServer`, the settings surface, persistence and session-format artifacts are byte-identical; the removed runtime invariant mechanism (each package's `./invariant` export 38->0, `dsh-invariants` retired) is unreferenced here, and the upstream fix for toggling a plugin removing other plugins' styles benefits this plugin. **The single display-level break is adapted in dual mode**: upstream split the composer stats row into `activity`/`usage` entries and dropped the row root class `.root`, so the mobile rules keep the legacy `[class*="bOPqQW_root"]` anchor for older hosts and add the official `[data-composer-stat]` hook plus the <=375px font step for the new host; the 0.2.0-rc.2 MenuGroup group-heading dual selector remains effective and the Client Inspect hang defect stays fixed as of 0.2.0-rc.2. The plugin-version gate introduced in rc.1 reads only `peerDependencies` (this plugin declares none, so the gate does not apply); real-host mounting on DSH `0.2.1-alpha.1` is operational. The settings surface and the persistence/layout seams all run in dual shapes detected from runtime capabilities, and adaptation items that target newer structures are naturally inert on older hosts (cosmetic only, no functional loss). Note: sessions written after upgrading cannot be read by older DSH releases — **backups do not restore across a version downgrade**. The plugin marketplace judges compatibility from the `engines.dsh` range in `package.json`, which is the single declaration of the supported range.
426
+
427
+ **DSH 0.2.1-alpha.2 compatibility update**: the supported range now includes `0.2.1-alpha.2` (`engines.dsh`: `>=0.1.1-rc.2 <0.2.1-alpha.3`). Subagent startup supports both the new `startActivation(spec)` and older `start` / `startContinuable` methods. Mobile task-chip CSS handles the removal of `.QsffPG_root` and the switch to a body-portaled task menu. On the current alpha.2 Web host, the plugin RPC, `available:true` subagent seam, real `subagent` and `subagent_fork` executions and their routed dispatch records were verified; legacy and new API tests pass. With an actual background task on alpha.2, the mobile chip geometry was checked at 320/360/390/560px, its in-flow fallback at 561px, and the portaled menu was opened twice at 390px without crossing the viewport. At 320px the task-count text is fully elided, but the icons and complete `aria-label` remain. **A real older-host UI comparison and other tab counts remain unverified.**
380
428
 
381
429
  **DSH Desktop (Electron) statement**: the desktop app reuses the same Web frontend and the same client plugin graph (`platform: web`), so the plugin loads as usual and almost everything works unchanged; only "restart" and "one-click upgrade" differ in meaning. The host is owned by the desktop app, so **restart never calls `process.exit(42)`** there (the shell treats any non-zero exit as a host crash, shows a native failure dialog and does not relaunch it; there is no official programmatic restart RPC) — the Restart button, the `/restart` command and the post-restore restart all guide the user to quit and reopen the desktop app instead; **one-click upgrade is greyed out on the desktop** and points at the app's own plugin manager (the bundled CLI cannot boot or mutate `profiles/desktop`, so the upgrade endpoint short-circuits with `desktop-managed-upgrade` and spawns nothing); the Health diagnostics "Runtime environment" row reads "Managed by DSH Desktop (Electron)". The signal is `process.versions.electron` inside the host process (measured on the real Windows desktop build: `44.0.0`, with `ELECTRON_RUN_AS_NODE=1` and non-TTY stdio); non-Electron desktop wrappers can declare `DSH_SERVICE_RUNTIME_ENV=desktop`. Task notifications **do work on the desktop app** (the permission is granted and a real system notification has been observed); the only boundary is that **clicking a notification cannot bring the window back** (closing the window hides it and `dshDesktop` exposes no window API — use the tray). macOS / Linux desktop builds are not verified.
382
430
 
@@ -414,7 +462,13 @@ Check two things first: (1) the **master notification switch** under "Configurat
414
462
  <details>
415
463
  <summary><strong>A quota card shows "credential missing"?</strong></summary>
416
464
 
417
- Use the inline form on the card: an API key for regular adaptations, the management key for CLIProxyAPI (not the proxy key), and the console cookie for Xiaomi Token Plan. The value goes into the DSH credential store and the provider refreshes automatically; if a process environment variable shadows the name, the host refuses the write — change the variable itself.
465
+ Use the inline form on the card: an API key for regular adaptations, the management key for CLIProxyAPI (not the proxy key), and the console cookie for Xiaomi Token Plan. The value goes into the DSH credential store and the provider refreshes automatically; if a process environment variable shadows the name, the host refuses the write — change the variable itself. To fix a typo or rotate a key, a configured card keeps an "Edit credential (configured)" entry in its **Config** area where the stored value can be overwritten or cleared.
466
+ </details>
467
+
468
+ <details>
469
+ <summary><strong>A CLIProxyAPI card shows "Temporarily banned by CLIProxyAPI"?</strong></summary>
470
+
471
+ Repeated wrong management keys tripped the CPA fail2ban: five failures from one IP ban that IP for 30 minutes, and while banned **even the correct key is rejected**. That state deliberately offers no credential form (the key itself is fine). Wait for the ban to expire (the upstream message carries the remaining time), or change the DSH host's egress IP, then hit "Retry connection". The ban applies to the `/v0/management` plane only — model inference through that CPA keeps working.
418
472
  </details>
419
473
 
420
474
  <details>
@@ -444,7 +498,7 @@ The token expired (the official `oasis-token is embezzled` error means the token
444
498
  <details>
445
499
  <summary><strong>What does restoring a backup do?</strong></summary>
446
500
 
447
- The host first runs an integrity inspection and presents a restore-preflight plan for final confirmation. Immediately before commit it rechecks the backup SHA-256, current-target fingerprint, and running work; any change aborts without a partial overwrite or restart. A successful commit replaces sessions in full, makes the allowlisted config files exact to the snapshot, and updates only each profile's package.json (node_modules, credentials, and attachments stay untouched). Docker/systemd/pm2-style managed runtimes restart automatically; likely terminal-launched instances show manual-restart instructions. Deleting a backup also requires two-step confirmation.
501
+ The host first runs an integrity inspection and presents a restore-preflight plan for final confirmation. Immediately before commit it rechecks the backup SHA-256, current-target fingerprint, and running work; any change aborts without a partial overwrite or restart. A successful commit replaces sessions in full, makes the allowlisted config files exact to the snapshot, and updates profiles exactly as the preflight mapping says (a cross-name mapping restores only the Profile configuration layer unless you tick the manifest box; node_modules, credentials, and attachments stay untouched). Docker/systemd/pm2-style managed runtimes restart automatically; likely terminal-launched instances show manual-restart instructions. Deleting a backup also requires two-step confirmation.
448
502
  </details>
449
503
 
450
504
  <details>
@@ -459,6 +513,12 @@ That skill lives in a read-only bundled directory. Only `project-*` and `user-*`
459
513
  Concurrency protection kicked in: SKILL.md was just modified by an external editor (version mismatch). Refresh to get the latest state and retry.
460
514
  </details>
461
515
 
516
+ <details>
517
+ <summary><strong>The plugin page installed the plugin, but the version is an older one?</strong></summary>
518
+
519
+ pnpm 11 enables a supply-chain release cooldown by default (`minimumReleaseAge`, 1440 minutes): **installing without a version** (including `@latest`) only selects the newest release that has already cleared the cooldown, so a freshly published release is skipped and the official plugin page reports that it installed x.y.z instead of the latest a.b.c. To get the new version right away: uninstall it in the plugin page and reinstall with `package@version` (e.g. `@gehennawu/dsh-service@2.0.8`), or use this plugin's **Service Control → Upgrade** — it installs an exact version and is not subject to the cooldown. The cooldown is a supply-chain policy (a fresh release may contain defects that are not known yet); after the 24 hours have passed, installing without a version also reaches the new release.
520
+ </details>
521
+
462
522
  <details>
463
523
  <summary><strong>Does a failed update check affect other features?</strong></summary>
464
524
 
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  <em>A service-control &amp; operations plugin for DeepSeek Harness (DSH) Web.</em>
10
10
  </p>
11
11
 
12
- [![Version](https://img.shields.io/badge/version-2.0.6-3b82f6.svg?style=flat-square)](package.json)
12
+ [![Version](https://img.shields.io/badge/version-2.0.8-3b82f6.svg?style=flat-square)](package.json)
13
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-10b981.svg?style=flat-square)](LICENSE)
14
- [![DSH Compatibility](https://img.shields.io/badge/DSH-%E2%89%A50.1.1--rc.2%20%C2%B7%20%E5%B7%B2%E9%80%82%E9%85%8D%200.2.1--alpha.1-6366f1.svg?style=flat-square)](https://github.com/deepseek-ai/deepseek-harness)
14
+ [![DSH Compatibility](https://img.shields.io/badge/DSH-%E2%89%A50.1.1--rc.2%20%C2%B7%20%E5%B7%B2%E9%80%82%E9%85%8D%200.2.1--alpha.2-6366f1.svg?style=flat-square)](https://github.com/deepseek-ai/deepseek-harness)
15
15
  [![Cordis](https://img.shields.io/badge/Cordis-v4.x-f59e0b.svg?style=flat-square)](https://cordis.moe/)
16
16
  [![Platform](https://img.shields.io/badge/platform-DSH%20Web-ec4899.svg?style=flat-square)](https://github.com/gehennawu/dsh-service)
17
17
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/gehennawu/dsh-service/issues)
@@ -41,7 +41,7 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
41
41
 
42
42
  - [🚀 功能](#-功能)
43
43
  - [版本与更新](#版本与更新) · [安全重启](#安全重启) · [健康诊断](#健康诊断) · [模型统计](#模型统计)
44
- - [额度查询](#额度查询) · [备份管理](#备份管理) · [技能管理](#技能管理) · [子代理模型](#子代理模型)
44
+ - [额度查询](#额度查询) · [模型配置](#模型配置) · [备份管理](#备份管理) · [技能管理](#技能管理) · [子代理模型](#子代理模型)
45
45
  - [任务通知](#任务通知) · [会话管理](#会话管理) · [移动端适配](#移动端适配) · [模型厂家图标](#模型厂家图标) · [右栏文件编辑](#右栏文件编辑) · [外部探活](#外部探活)
46
46
  - [🏗️ 架构](#-架构)
47
47
  - [⚡ 安装](#-安装) · [🔄 自动重启配置](#-自动重启配置) · [🖥️ 平台支持](#-平台支持)
@@ -49,9 +49,9 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
49
49
 
50
50
  ## 🚀 功能
51
51
 
52
- 设置页「服务控制」面板六页导航:**概览 · 模型统计 · 额度查询 · 健康诊断 · 维护 · 配置**;其中「维护」聚合 会话管理 · 技能 · 子代理 · 备份维护 · 重启 五个子页,「配置」聚合 功能开关 · 任务通知 · 设置栏标签 三个子页。重启、额度查询、会话管理可另行开启**设置页左列快捷入口**(默认关闭;技能与子代理的左列入口已撤销)。
52
+ 设置页「服务控制」面板七页导航:**概览 · 模型统计 · 额度查询 · 模型配置 · 健康诊断 · 维护 · 配置**;其中「模型配置」聚合渠道与模型 / 子代理路由,「维护」聚合 会话管理 · 技能 · 备份维护 · 重启 四个子页,「配置」聚合 功能开关 · 任务通知 · 设置栏标签 三个子页。重启、额度查询、会话管理可另行开启**设置页左列快捷入口**(默认关闭;技能与子代理的左列入口已撤销)。
53
53
 
54
- 「插件 → 插件配置」提供十二个宿主级开关:**健康诊断、模型统计、额度查询、备份维护、任务通知、技能管理、子代理模型、会话管理、移动端适配、模型厂家图标、右栏文件编辑、`/healthz` 探活**(除移动端适配外默认开启)。全部热生效:关闭即隐藏界面、停止轮询并让宿主拒绝对应能力;概览与重启固定保留。
54
+ 「插件 → 插件配置」提供十三个宿主级开关:**健康诊断、模型统计、额度查询、模型配置、备份维护、任务通知、技能管理、子代理模型、会话管理、移动端适配、模型厂家图标、右栏文件编辑、`/healthz` 探活**(除移动端适配外默认开启)。全部热生效:关闭即隐藏界面、停止轮询并让宿主拒绝对应能力;概览与重启固定保留。
55
55
 
56
56
  ![插件配置](./screenshots/plugin-config.png)
57
57
 
@@ -66,7 +66,7 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
66
66
 
67
67
  ![配置页](./screenshots/configuration.png)
68
68
 
69
- - 「维护」集中会话管理、技能、子代理、备份维护与重启;记住最近使用的子页,关闭对应功能后自动回退到仍可用的项目
69
+ - 「维护」集中会话管理、技能、备份维护与重启;子代理在「模型配置」独立子页,不重复入口;记住最近使用的子页,关闭对应功能后自动回退到仍可用的项目
70
70
  - 「配置」集中功能开关、任务通知与设置栏标签;开关按功能组展示并热生效,任务通知关闭时保留入口但显示置灰状态,设置栏标签支持对设置弹窗左侧全部导航标签进行手动排序(拖拽/上下箭头换位)与显隐管理,配置持久化到服务端统一配置文件 `$DSH_HOME/dsh-service-config.json`(多设备同步、启动自动拉取,本地缓存兜底),即时生效(服务控制面板永久锁定显示防锁死);额度卡的排序与显隐走同一份配置(`quotaCards` 区块,进入额度页时拉取)
71
71
  - 插件统一配置文件:各功能的轻量偏好收敛在 `$DSH_HOME/dsh-service-config.json` 单一文件(原子写入、`0600`),按功能分区块隔离——修改或清除某一区块绝不影响其他区块;大缓存(使用统计索引等)与加密凭据不在此文件内
72
72
 
@@ -111,6 +111,7 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
111
111
  - **已统计的用量永久保留**:删除会话或删除项目文件夹都不会扣减历史,总量与热力图始终是「索引建立以来的完整历史」;索引版本升级先保留旧摘要,可读取的会话仅在完整重建成功后替换,失败时保留历史并重试
112
112
  - **项目文件夹已删除**时,该项目不再出现在项目筛选入口里,但它贡献的用量仍计入「全部」总量、日期桶与模型明细
113
113
  - 最近 48 小时模型 / 工具报错统计(默认折叠、仅非空渲染)
114
+ - **打开面板即自动补拉,不靠手动刷新**:挂载时无条件同步一次(先渲染持久缓存,再在后台折叠增量);此后只在**真实边沿**上补拉——跨本地零点(「今日」卡落到新的一天)、从后台切回前台、以及每次进入统计页。敞着不动不做任何周期轮询,停在别的页面也不花这份成本。同步期间刷新钮显示「刷新中…」(实测空闲折叠约 0.3 秒,存在历史格式会话且语料在变动时可达数十秒)
114
115
  - 提供方未上报 token 用量的步骤不纳入统计
115
116
  - 单个会话无法读取、迁移或解析时继续统计其他会话,显示全项目成功/跳过数量与可展开的会话 ID、错误类别及安全摘要;失败会话有旧缓存时保留并标明过期,首次失败不计入,下次刷新重试。只有服务不可用、列表读取或索引写入失败等全局错误才让整次刷新失败。该提示可关闭(关闭后记入本地存储),且**只静音同一批失败**——出现新的跳过会话或错误类别变化时会再次提示,不会一次关闭就永久盖住新问题
116
117
 
@@ -139,9 +140,11 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
139
140
  | CLIProxyAPI 部署 | 各 OAuth 上游账号官方剩余额度 |
140
141
 
141
142
  - 凭据写入 DSH 凭据库(`$DSH_HOME/.credentials.yaml`,热生效):普通适配填 API key,CLIProxyAPI 填管理密钥,小米填控制台 Cookie,StepFun Step Plan 填控制台令牌(Oasis-Token,`Oasis-Webid` 由令牌自动派生无需手填);Command Code 额度面与推理面同一把 key,无需另配
142
- - 防风控:结果缓存 60 秒、失败指数退避(30 秒 ×2、封顶 15 分钟);查询为纯手动——打开额度页/展开圆环/点刷新才拉取,客户端不排任何周期轮询(上游节流仍全部在宿主侧)
143
+ - **凭据可随时修改**:每张已适配卡片的「配置」区常驻凭据入口——未配置时显示各渠道的填写文案,已配置后显示「修改凭据(已配置)」,可直接覆盖(密钥轮换、换账号、填错想改)或清除已存值;凭据值只随保存请求发出,绝不回显
144
+ - 防风控:结果缓存 60 秒、瞬时网络错误指数退避(30 秒 ×2、封顶 15 分钟);**确定性失败转手动**——凭据类、4xx、套餐失效、管理面未启用、响应格式异常等「换配置才可能恢复」的错误,宿主停止自动重试(错误行改报「自动重试已暂停,请手动重试」),只有点刷新手动重连(带 5 秒冷却)且成功后,才回到自动模式;查询为纯手动——打开额度页/展开圆环/点刷新才拉取,客户端不排任何周期轮询(上游节流仍全部在宿主侧)
145
+ - **CLIProxyAPI 管理面封禁有专属提示**:管理面对同一 IP 连续认证失败 5 次封 30 分钟,被封时卡片显示「已被 CLIProxyAPI 暂时封禁」+ 上游原话(含剩余时间);该状态不提供重填密钥的入口(密钥没错),等封禁解除或更换出口 IP 后点「重试连接」即可;封禁只作用于管理面,走该 CPA 的模型推理不受影响
143
146
  - CLIProxyAPI 某账号实时查询失败时,回退显示其上次缓存的快照窗口并标注「缓存」徽标;重置时间已过的快照窗口(快照描述的窗口已结束)直接丢弃,避免「额度停在昨天」的错觉
144
- - 失败原因如实呈现:卡片与圆环显示「错误文案(HTTP 状态 · 失败端点 · 失败账号 · 上游原话)· 下次自动重试时刻」——错 key、欠费、限流、路径变更各有各的上游原话与状态码,不再只有一个笼统提示;上游 401/403 判为「凭据被上游拒绝」,卡片同时保留凭据填写入口;HTTP 200 业务信封里的错误码同样定族——鉴权失败判凭据被拒(保留填写入口)、套餐到期判无生效订阅、上游自身故障判「上游服务故障」,不再一律报「响应格式异常」
147
+ - 失败原因如实呈现:卡片与圆环显示「错误文案(HTTP 状态 · 失败端点 · 失败账号 · 上游原话)· 下次自动重试时刻」——错 key、欠费、限流、路径变更各有各的上游原话与状态码,不再只有一个笼统提示;上游 401/403 判为「凭据被上游拒绝」,卡片同时保留凭据填写入口;HTTP 200 业务信封里的错误码同样定族——鉴权失败判凭据被拒(保留填写入口)、套餐到期判无生效订阅、上游自身故障判「上游服务故障」,不再一律报「响应格式异常」;转手动模式的错误行末尾改报「自动重试已暂停,请手动重试」
145
148
  - API key 只在宿主进程内解析,浏览器仅收到归一化窗口数据;未适配的供应商绝不发起请求
146
149
 
147
150
  ### 备份管理
@@ -155,7 +158,9 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
155
158
  - 导出下载 / 导入上传 / 删除(两段式确认);不限份数、不自动清理;导入归档必须先通过与恢复相同的完整性检查
156
159
  - **完整性检查**:恢复前校验 gzip/tar、路径与条目类型,只接受 `sessions`、三份允许配置、`profiles/<name>/package.json` 与 `meta/backup.json`;拒绝越界路径、链接、特殊文件、未知内容、损坏归档与非法 profile 清单
157
160
  - **恢复预检**:先生成 5 分钟有效的一次性计划,展示会话整体替换、配置覆盖/移除和 profile manifest 覆盖清单;最终确认前再次校验归档 SHA-256 与当前目标指纹,发生漂移则拒绝执行
158
- - 恢复提交使用事务日志和回滚目录:会话整体替换,配置按快照精确替换,profile 只更新 package.json 并保留 node_modules/凭据/附件;成功后托管环境自动重启,手动启动环境提示用户手动重启
161
+ - **跨设备 / 跨平台恢复**:归档内的路径全部是相对的,恢复按**本机** `$DSH_HOME` 落地,所以 DSH_HOME、用户名、挂载点不同都不影响导入;`meta/backup.json` 另记录备份时的来源平台与架构,预检会提示来源与当前系统不同。预检同时列出只读提示(不阻断恢复):路径超过 Windows 经典上限、仅大小写不同的路径在当前卷上会折叠、会话目录当前不可写(占用或权限不足)
162
+ - **目标 profile 选择**:归档里的 `profiles/<名>` 内容恢复进**本机哪个 profile** 由你选——候选来自本机实际存在的 profile(含当前活动 profile 标记),或保持与源同名;跨名映射默认**不覆盖**本机 profile 的插件清单 `package.json`,可显式勾选覆盖;Profile 配置层 `cordis.patch.yml` 归档有则照常恢复
163
+ - 恢复提交使用事务日志和回滚目录:会话整体替换,配置按快照精确替换,profile 按上述映射更新并保留 node_modules/凭据/附件;成功后托管环境自动重启,手动启动环境提示用户手动重启;Windows 上目标文件被占用导致失败时给出明确指引(退出桌面应用、暂停同步盘、关闭杀毒实时扫描后重试)
159
164
 
160
165
  ### 技能管理
161
166
 
@@ -167,6 +172,45 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
167
172
  - 带 camelCase 旧版键的条目会被官方解析器剔除:⚠ 提示 + 一键修复
168
173
  - ✨ AI 补全说明:选模型生成描述草稿(跟随界面语言),确认后存入插件侧车索引——**绝不改写 SKILL.md**;支持一键批量补全(宿主后台运行、可取消)。已注释技能会在计划中单列,经「确认强制补全」二次确认后才会被覆盖(不再是一旦注释就永远无法再次补全);补全日志时间按本机时区显示
169
174
 
175
+ ### 模型配置
176
+
177
+ > 模型配置写入要求宿主提供可追踪的配置 revision;缺少该能力的旧宿主仅可查看,写操作会明确拒绝。旧工作台源码与测试作为兼容回归资产保留,当前界面和宿主仅注册 models3 流程。敏感认证 headers 和历史人工恢复状态请通过 DSH 原生配置/恢复流程处理,本页不自动清理。
178
+
179
+ - 顶级「模型配置」页:左子标签**渠道与模型**(默认)+ 右**子代理路由**(复用「子代理模型」区块,只剩一个可用子页时子标签栏整条隐藏)。**只有一套界面**(旧版按 localStorage 隐形切换的 Simple/Legacy 双模式已退役)
180
+ - **纵向手风琴**:渠道按内置 / 自定义两组平铺成卡片,点标题行展开该渠道详情,一次只开一个,再点收起;窄屏(≤640px)只收窄内边距,**不改成左右并排**。有未保存草稿时切换渠道先确认
181
+ - **渠道优先闭环**:渠道列表 →「添加自定义渠道」(标识 / 显示名 / 协议 / 地址 / API Key / 手填或勾选模型 ID)→ 渠道详情 → 模型列表 · 独立模型详情 · 单独换密钥 · 删除与最近恢复;创建默认不探测;填写 API Key 时在确认创建后保存为独立凭据引用
182
+ - **本页只编辑少数渠道字段**:显示名、地址、协议(自定义渠道可改,内置渠道只读展示当前解析出的协议)与凭据。渠道默认、headers、thinkingBudgets、transport/cache、超时、图像预算与 retryPolicy 等渠道级高级键本页不提供编辑器,**保存不会把它们清除**(写入按字段补丁合并,未展示的键原样保留);本页也不做双 JSON 编辑
183
+ - **Anthropic 在线模型列表**:支持 `anthropic-messages` 的 `/v1/models?limit=1000`,兼容地址末尾有无 `/v1`,使用 `x-api-key` 与 `anthropic-version`,解析 `display_name`。只取单页、不自动追随分页;沿用公网与响应护栏,不调用官方无 SSRF 限制的抓取路径。
184
+ - **发现与手填并重**:只有显式点「在线获取」才出网;结果区分已添加与可添加,支持搜索与勾选批量添加;发现失败、协议不支持、安全策略拒绝探测都只提示,**手动输入 ID 的路径照常可用**。拿到列表 ≠ 调用成功
185
+ - **单模型面板**:模型 ID **可编辑**(含从「在线获取」列表添加的条目),值受宿主三处限制:必须符合模型 ID 格式、同渠道内不得重名、重命名同样拒绝撞已有 ID,已停用模型需先明确启用才能改名;重命名按「删旧增新」提交,所以仍会经过删除引用守卫。同面板还有显示名、上下文窗口 / 最大输出 token(**可写 `256K`、`1M` 这类 K/M 后缀**,留空 = 继承目录默认值)、输入模态(文本一项勾选且禁用,只有图片可切换;保存时按宿主 schema 明确声明)、输出模态(只读展示文本——pi-ai 的模型 profile 没有该字段)、推理档位。保存按钮在面板**底部**,与「尚未保存」状态同排,无改动时禁用;提交只写被改动的字段
186
+ - **高级模型参数区没有折叠头**:输入模态与推理档位在模型详情里始终展开可见,不藏在折叠标题后面
187
+ - **删模型与删渠道都要核对引用**:删除确认先列出**确切引用位置**——主对话默认模型、子代理主模型、子代理回退模型,逐条给出 `provider / model`,存在引用就阻止删除;自定义渠道不能删到零模型,继承渠道不能用复位伪装删除
188
+ - **启停即时生效、无二次确认**:渠道右上开关与每个模型行的开关点下去立即提交(宿主先取计划,同一次点击内直接提交,不再弹出二次确认);渠道开关的悬停提示写明「关闭后该渠道不再可选,主对话和子代理均不能发起新调用;配置和密钥保留」。停用状态按 Profile 保存,停用渠道从输入框及子代理模型目录排除,并拒绝新的精确模型调用(包含已保存路由、显式指定和已准备但尚未发送的调用);已有正在执行的请求不会被强行取消。此为插件可用性策略,不是官方 `enabled` 配置字段;卸载插件会撤去策略
189
+ - **基础调用测试不等于能力验证**:模型列表测试按钮直接测试已保存的条目,不弹费用二次确认;宿主固定发送短文本,上限 30,000 token / 120 秒,可能计费。测试期间显示耗时,失败显示脱敏的错误码、HTTP 状态、结束原因与信息;只验证基础文本调用,不验证图片、容量或全部思考档位。失败不回滚已保存配置
190
+ - **内置继承不误报**:内置渠道的能力字段由目录继承(运行时回落目录 base),「配置没写」不等于「能力缺失」——不会反复提示补全思考等级
191
+ - **手动编辑器**:ID / 名称 / 上下文 / 最大输出常显,能力区始终展开(识图两枚开关、六档等宽横排推理复选框(不展示 off);资料中的 wire 映射保留,wire 编辑输入目前不在可见界面展示);清空字段 = 删除显式覆盖改为继承默认,`reasoningEfforts: false` = 明确声明不支持(与省略区分)
192
+ - **删除保留凭据并可恢复**:删除渠道只移除 settings 中的路由,凭据引用保留(不影响共享同一凭据的其它渠道);删除前整份渠道配置进快照,**恢复入口常驻列表底部**,不随渠道行消失。恢复写回引用,但**不恢复旧密钥值**
193
+ - **思考配置与调用选择不同**:档位复选框声明可选等级,不代表每次调用选中该等级;全部未配置时继承已有值或由接口默认行为决定,不等于关闭思考。智能填充保留资料中明确声明的 wire 映射;手动勾选非 off 档位暂以英文档位名作为参数值,适用性需自行核对。至少一个非 off 映射才能保存
194
+ - **草稿留在当前页**:保存失败或并发冲突不清空草稿,冲突后对照最新值重新确认;有未保存编辑时离开先提醒;不跨刷新持久化草稿(避免密钥进浏览器存储)
195
+ - **内置与自定义目录分开处理**:内置渠道显示解析后的协议,可追加目录外模型并编辑当前 Profile 的模型条目;目录提供的默认值作为继承展示,未改动字段不整份复制写死。已有未知覆盖和高级字段保留,不冻结整张动态目录
196
+ - **首页零外网**:总览只读当前 Profile、pi-ai 目录、运行时提供方和侧车,分别显示保存 / 加载 / 基础调用状态;进入列表不会查询外部资料
197
+ - **单模型智能填充**:每次进详情默认 OFF;ON 自动查询本机 pi-ai 索引和固定 modelparams.dev / OpenRouter 资料,**不探测渠道、不发推理、不自动保存**。**本轮是「模型 ID 维度」的填充:不绑定当前渠道或协议来源**,界面没有来源选择与绑定交互(候选只作依据,不写进配置)
198
+ - **建议按参数分开展示**:显示名、上下文容量、最大输出、输入模态与思考等级分别展示;模态按集合比较,顺序不同不算差异。多来源候选按稳定顺序选取建议,思考等级优先采用有明确映射的资料,不以当前渠道筛选;没有映射不猜测具体档位。modelparams 的请求范围上限不当模型最大输出;资料只是参考,不代表接口能力实测
199
+ - **统一确认填入**:「一键填入」弹层先列出将覆盖哪些字段的草稿值,确认后**只更新草稿**,不写配置,仍需点保存;不再提供来源绑定或逐项 ✓ / ✕。开关悬停说明资料来源与操作边界
200
+ - **思考等级两条提示分开**:模型资料声明「支持思考」但拿不到具体等级建议时提示「支持思考但未获取到等级建议值,默认行为要看该接口」;完全没有思考资料时提示「未获取到思考等级建议值,请手填」。**资料缺席 ≠ 不支持**,`reasoningEfforts: false` 才是明确声明不支持
201
+ - **档位徽标用英文等级名**:当前与建议档位以 `minimal / low / medium / high / xhigh / max` 徽标展示(不随界面语言翻译;`models.smart.effort.*` 的中文词典另有保留但未用于这里),「当前等级 / 建议等级」的标签本身仍跟随界面语言
202
+ - **先快照再写入**:普通保存一次点击,删除 / 换密钥 / 恢复先展示后果再确认;写前快照落盘失败则不写配置。新建、编辑与恢复均捕获 owner / 完整指纹 / revision,拒绝并发覆盖,后续外部修改阻止撤销。普通快照不含敏感 headers;含敏感值且无法完整恢复的删除直接拒绝
203
+ - **联网查证**:按模型 ID 查 OpenRouter 模型档案(固定域名、不跟随重定向),结果只作参考展示,不改配置
204
+
205
+ #### URL 与密钥安全机制
206
+
207
+ - **零输入拼接**:地址编辑仅作为配置数据,经 schema 校验,不因输入地址而出网;普通保存不抓该地址。在线发现只请求**宿主当前 Profile 读出的 baseURL**;模型 ID / 名称绝不进 shell、文件路径或命令参数,固定资料请求只使用宿主白名单域名
208
+ - **在线发现护栏**:HTTPS-only、无 userinfo、端口空或 443、拒 IP 字面量、**DNS 解析全部结果**须为非私网/保留段、拒重定向、钉住解析地址直连并带 Host 头(防 DNS rebinding)、10s 超时、传输 ≤4MB 且解压后 ≤8MB。非公网 / HTTP / 非 443 地址仍可保存与手填,只是禁在线发现并标注「未由插件验证」
209
+ - **密钥独立管理**:更换密钥只经 loopback 提交到系统凭据库,不回显、不落日志或快照;默认新建独立引用,不覆盖已有共享值。显式共享替换先列使用者;配置失败如实报告已写凭据,不以补偿为名误删。发现解析 `apiKeyEnv`,只发往该渠道当前地址,绝不发资料站
210
+ - **短命宿主句柄**:编辑基线、删除、恢复与凭据后果由 `sessionId` / `planId` 绑定,提交复核完整基线与 revision;未知、过期或重放句柄拒绝。手动参数通过闭集 schema 验证
211
+ - **结构性写保护**:写入走 `settings.mutate(entryId, ops, expectedRevision)`,新版 entryId 由当前 pi-ai owner 解析,旧版才用 `llm-pi-ai`;官方完整校验、原子落盘与热生效,绝不手写真实配置 YAML
212
+ - **仅 loopback**:全部端点经 `/dsh-service` 单层 channel 暴露,authority 一律 `loopback`
213
+
170
214
  ### 子代理模型
171
215
 
172
216
  ![子代理模型](./screenshots/subagent-model.png)
@@ -179,7 +223,7 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
179
223
  - **模式切换保留配置**:已保存的自定义模型(含思考等级)与回退列表在三种模式间切换时不会被清除——模式只决定是否生效,切回「自定义」无需重新选择(供应商/模型已不在运行时清单时自动回落到清单首项)
180
224
  - **进入无位移**:本页以最近一次成功读取的配置作为首帧缓存,进入即直接落在真实模式上,不会先显示「初始」再跳到「自定义」;首次安装或换浏览器(无缓存)时先显示一行「读取配置…」
181
225
  - **回退模型(按顺序)**:跟随与自定义模式都可配置回退列表——第一路由不可用时(渠道已卸载、额度查询判定其不可服务)依次尝试后续模型;全部不可用则回落原生继承,不让派生失败。回退条目与主路由同一道白名单校验,思考等级逐条可选
182
- - **对话页可见性**:输入框下方常驻一行本会话子代理实际使用的模型,如 `子代理:cpa/gpt-5.6-luna (xhigh) · opencode-go/deepseek-v4-flash (max)`——含回退命中、显式路由与继承来源,可直接核对自定义路由是否生效。行挂官方 `conversation.composer.dock` 槽位,独占官方统计行的下一行、不与统计胶囊/上下文圆环同行挤占;20s 刷新,不依赖回合数据(会话发生 compaction 折叠工具调用也照常显示),由宿主派发记录兜底,任何视图都能看到;可在 维护 → 子代理 页用独立开关关闭。记录存宿主内存(页面刷新不丢、进程重启即清),官方「指派子代理模型」开关关闭与否都不影响本功能
226
+ - **对话页可见性**:输入框下方常驻一行本会话子代理实际使用的模型,如 `子代理:cpa/gpt-5.6-luna (xhigh) · opencode-go/deepseek-v4-flash (max)`——含回退命中、显式路由与继承来源,可直接核对自定义路由是否生效。行挂官方 `conversation.composer.dock` 槽位,独占官方统计行的下一行、不与统计胶囊/上下文圆环同行挤占;20s 刷新,不依赖回合数据(会话发生 compaction 折叠工具调用也照常显示),由宿主派发记录兜底,任何视图都能看到;可在 模型配置 → 子代理路由 页用独立开关关闭。记录存宿主内存(页面刷新不丢、进程重启即清),官方「指派子代理模型」开关关闭与否都不影响本功能
183
227
  - 配置存 `$DSH_HOME/dsh-service-subagent-route.json`(原子写入、`0600`),可一键重置
184
228
 
185
229
  ### 任务通知
@@ -263,7 +307,7 @@ DSH Web 服务控制与运维插件:安全重启、版本管理与一键升级
263
307
  ```mermaid
264
308
  flowchart TB
265
309
  subgraph Client["🌐 Client 浏览器端 (client.js)"]
266
- UI["设置页「服务控制」面板(六页导航 + 快捷入口)<br/>额度圆环 · 通知铃铛 · 移动端适配"]
310
+ UI["设置页「服务控制」面板(七页导航 + 快捷入口)<br/>额度圆环 · 通知铃铛 · 移动端适配"]
267
311
  end
268
312
 
269
313
  subgraph Host["⚙️ Host 服务端 (index.js)"]
@@ -307,7 +351,7 @@ flowchart TB
307
351
  1. 打开左侧边栏的「**插件**」页。
308
352
  2. 点右上角的「**+ 添加插件**」按钮。
309
353
  3. 在弹窗里按来源**三选一**填写(同一个输入框接受三种形式,底部提示会随填写内容变化):
310
- - **npm 包名(推荐)**:`@gehennawu/dsh-service`;
354
+ - **npm 包名(推荐)**:`@gehennawu/dsh-service@2.0.8`——**带上精确版本号**(写法 `包名@版本号`);
311
355
  - **GitHub 仓库地址**:`https://github.com/gehennawu/dsh-service`(公开仓库,无需令牌;`github:gehennawu/dsh-service` 简写同样可用);
312
356
  - **本地插件目录**:本机上的绝对路径,例如 `/path/to/dsh-service`(自研或已下载的插件)。
313
357
  4. 「**安装源**」按网络情况选择:默认「**npm 官方源**」;国内网络拉取受限时可选「**中国大陆镜像源**」(影响走包名安装时的拉取源,GitHub 地址与本地目录不受其影响)。
@@ -318,15 +362,17 @@ flowchart TB
318
362
 
319
363
  > 提示:三种形式都走官方安装链路,无需手动改 profile 文件;图形界面填 GitHub 地址与命令行 `github:` 前缀等价。
320
364
 
365
+ > **npm 方式为什么建议带版本号**:pnpm 11 默认启用供应链发布观察期(`minimumReleaseAge`,1440 分钟)。**不带版本号**(含 `@latest`)时,发布时间未满观察期的版本不会被自动安装,包管理器会退到「最近的、已过观察期的那一版」——插件页因此可能提示「本次安装的是 x.y.z,而非最新版本 a.b.c」;**写精确版本号**(如 `@gehennawu/dsh-service@2.0.8`)才会安装你点名的那一版。观察期属供应链安全策略,不代表新版有问题;不着急时等满 24 小时,不带版本号安装同样会装到新版。
366
+
321
367
  ### 方式二:命令行
322
368
 
323
369
  | 方式 | 命令 |
324
370
  | --- | --- |
325
- | npm(推荐) | `dsh plugin --profile web add @gehennawu/dsh-service` |
371
+ | npm(推荐) | `dsh plugin --profile web add @gehennawu/dsh-service@2.0.8` |
326
372
  | GitHub | `dsh plugin --profile web add github:gehennawu/dsh-service` |
327
373
  | 本地开发 | `dsh plugin --profile web add link:/path/to/dsh-service` |
328
374
 
329
- 命令行走 npm / pnpm 的默认源;需要镜像时,请先按 npm / pnpm 的常规做法把 registry 指到镜像站,再执行上面的命令。
375
+ 命令行走 npm / pnpm 的默认源;需要镜像时,请先按 npm / pnpm 的常规做法把 registry 指到镜像站,再执行上面的命令。npm 方式同样受 pnpm 发布观察期影响:不带版本号会装到最近的、已过观察期的版本,写精确版本号(`@gehennawu/dsh-service@2.0.8`)才会装该版(原因见「方式一」的提示)。
330
376
 
331
377
  安装或更新后重启 DSH Web:
332
378
 
@@ -378,7 +424,9 @@ pm2 start "dsh web --host 127.0.0.1" --name dsh-web
378
424
 
379
425
  运行要求:Node.js `>=22`,DSH Web 能加载 Host 与 Client 两半插件。更新检查需访问 `registry.npmjs.org`;网络失败不影响其他功能。
380
426
 
381
- **DSH 适配口径**:已适配 DSH `0.2.1-alpha.1`——会话格式 V4(V3 日志首读时由官方迁移为 `session.v4.jsonl.zstd` 代际文件,旧 `session.v3.jsonl.zstd` 按格式目录策略保留;详情视图自动归档系统事件)、`SettingsForms` 配置面(插件配置改由 Profile 的 `cordis.patch.yml` 承载,热更新走 `loader/volatile-update`;旧的 `settings.register` 在 0.1.5/0.1.6 上仍是权威来源,装在同一宿主上两者互不串台)、会话格式 V3 既有适配全部保留(`system/message` 入史、sessionPersistence handle 化、官方右栏、turn-process 对象化)、移动端底行触发钮双哈希兼容、子代理回合尾模型行 list 槽位自适应兼容、`plugins.bundle.config` 槽位注入、会话详情打开接入 `uiWorkspace` 降级链路。旧版 DSH(`>=0.1.1-rc.2`)保持兼容。支持区间扩展到 `0.2.1-alpha.1`(`engines.dsh` 上界 `<0.2.1-alpha.2`);0.2.1-alpha.1 源码级、npm 产物级(按 rolldown `//#region` 切片做逐源模块 sha256)与真机挂载审查(266 commits)确认:20 条既有接缝 17 条区域级逐字节相同、3 条均不触及本插件契约,9 个 slot key、25 个主题令牌、插件使用的 35 项官方 `data-*` 零丢失,宿主 RPC / `webServer` / 设置面 / persistence / session-format 产物逐字节全等;官方本版移除运行时 invariant 机制(各包 `./invariant` 导出 38→0、`dsh-invariants` 包退役)本插件零引用,并修复了「启停插件误删其它插件样式」。**唯一展示面破坏已双模式适配**:官方把 composer 统计行拆成 `activity`/`usage` 两个 dock 条目并删除行根类 `.root`,移动端规则除保留旧锚 `[class*="bOPqQW_root"]`(老宿主命中)外,新增官方新钩子 `[data-composer-stat]` 的同形约束与 ≤375px 字号阶梯(新宿主命中);0.2.0-rc.2 轮的模型弹窗分组标题 `MenuGroup` 双选适配继续有效,Client Inspect 挂起缺陷自 0.2.0-rc.2 起保持修复。rc.1 起新增的插件版本兼容门禁只读取 `peerDependencies`(本插件无该声明,不受门禁约束),并已在 DSH `0.2.1-alpha.1` 真机挂载运行。设置面、persistence/布局 seam 均按运行时能力探测走双形态,旧宿主上针对新结构的适配项天然不生效(纯展示,无功能损失)。注意:升级后写入的会话日志无法被旧版 DSH 读取,**备份不可跨版本降级恢复**。插件市场按 `package.json` 的 `engines.dsh` 区间判定兼容性(该字段是唯一的支持口径声明)。
427
+ **DSH 适配口径(alpha.1 基线)**:已适配 DSH `0.2.1-alpha.1`——会话格式 V4(V3 日志首读时由官方迁移为 `session.v4.jsonl.zstd` 代际文件,旧 `session.v3.jsonl.zstd` 按格式目录策略保留;详情视图自动归档系统事件)、`SettingsForms` 配置面(插件配置改由 Profile 的 `cordis.patch.yml` 承载,热更新走 `loader/volatile-update`;旧的 `settings.register` 在 0.1.5/0.1.6 上仍是权威来源,装在同一宿主上两者互不串台)、会话格式 V3 既有适配全部保留(`system/message` 入史、sessionPersistence handle 化、官方右栏、turn-process 对象化)、移动端底行触发钮双哈希兼容、子代理回合尾模型行 list 槽位自适应兼容、`plugins.bundle.config` 槽位注入、会话详情打开接入 `uiWorkspace` 降级链路。旧版 DSH(`>=0.1.1-rc.2`)保持兼容。alpha.1 轮曾将声明支持区间扩展到 `0.2.1-alpha.1`(当时 `engines.dsh` 上界 `<0.2.1-alpha.2`);0.2.1-alpha.1 源码级、npm 产物级(按 rolldown `//#region` 切片做逐源模块 sha256)与真机挂载审查(266 commits)确认:20 条既有接缝 17 条区域级逐字节相同、3 条均不触及本插件契约,9 个 slot key、25 个主题令牌、插件使用的 35 项官方 `data-*` 零丢失,宿主 RPC / `webServer` / 设置面 / persistence / session-format 产物逐字节全等;官方本版移除运行时 invariant 机制(各包 `./invariant` 导出 38→0、`dsh-invariants` 包退役)本插件零引用,并修复了「启停插件误删其它插件样式」。**唯一展示面破坏已双模式适配**:官方把 composer 统计行拆成 `activity`/`usage` 两个 dock 条目并删除行根类 `.root`,移动端规则除保留旧锚 `[class*="bOPqQW_root"]`(老宿主命中)外,新增官方新钩子 `[data-composer-stat]` 的同形约束与 ≤375px 字号阶梯(新宿主命中);0.2.0-rc.2 轮的模型弹窗分组标题 `MenuGroup` 双选适配继续有效,Client Inspect 挂起缺陷自 0.2.0-rc.2 起保持修复。rc.1 起新增的插件版本兼容门禁只读取 `peerDependencies`(本插件无该声明,不受门禁约束),并已在 DSH `0.2.1-alpha.1` 真机挂载运行。设置面、persistence/布局 seam 均按运行时能力探测走双形态,旧宿主上针对新结构的适配项天然不生效(纯展示,无功能损失)。注意:升级后写入的会话日志无法被旧版 DSH 读取,**备份不可跨版本降级恢复**。插件市场按 `package.json` 的 `engines.dsh` 区间判定兼容性(当前区间见下方 alpha.2 补充口径)。
428
+
429
+ **DSH 0.2.1-alpha.2 补充适配**:支持范围扩展至 `0.2.1-alpha.2`(`engines.dsh`:`>=0.1.1-rc.2 <0.2.1-alpha.3`)。子代理启动同时兼容新版 `startActivation(spec)` 与老版 `start` / `startContinuable`;移动端任务芯片兼容官方移除 `.QsffPG_root`、任务菜单改用 body portal 定位。已在 alpha.2 当前 Web 宿主验证插件 RPC、子代理路由 `available:true`、真实 `subagent` 和 `subagent_fork` 派生及各自的路由派发记录,旧版和新版接口单测均通过。移动端在 alpha.2 真实有后台任务的会话上验证 320/360/390/560px 泊位几何、561px 在流回退及 390px portaled 菜单两次开合无越屏;320px 下任务计数文字完全省略但图标/箭头及完整 `aria-label` 保留。**老宿主真机 UI 对照与不同标签数尚未实测**。
382
430
 
383
431
  **DSH 桌面端(DSH Desktop / Electron)口径**:桌面端复用同一份 Web 前端与同一张客户端插件图(`platform: web`),插件照常加载,绝大多数功能原样可用;语义不同的只有「重启」与「一键升级」。宿主由桌面应用托管,**重启一律不再 `process.exit(42)`**(桌面壳把任何非 0 退出判为 Host 崩溃,会弹原生「启动失败」对话框且不会自动重拉,官方也没有可编程重启 RPC)——「重启」按钮、对话里的 `/restart` 与备份恢复后的重启统一改为提示「请在桌面端退出应用后重新打开」;**一键升级在桌面端置灰**并指向应用自带的插件管理页(桌面端 CLI 不能启动/改写 `profiles/desktop`,升级端点直接短路返回 `desktop-managed-upgrade`,不产生任何 subprocess);健康诊断的「运行环境」行显示「由桌面端托管(Electron)」。判据是宿主进程内的 `process.versions.electron`(Windows 桌面版真机实测 `44.0.0`,同进程 `ELECTRON_RUN_AS_NODE=1`、stdin/stdout 均非 TTY);非 Electron 的桌面封装可用 `DSH_SERVICE_RUNTIME_ENV=desktop` 显式声明。任务通知在桌面端**可用**(权限 granted,真机已收到系统通知);唯一的边界是**点击通知唤不回窗口**(桌面端关窗=隐藏窗口,`dshDesktop` 无窗口 API,请用托盘唤回)。macOS / Linux 桌面端未验证。
384
432
 
@@ -416,7 +464,13 @@ pm2 start "dsh web --host 127.0.0.1" --name dsh-web
416
464
  <details>
417
465
  <summary><strong>额度卡片显示「凭据未配置」?</strong></summary>
418
466
 
419
- 点击卡片上的内联表单写入凭据:普通适配填 API key,CLIProxyAPI 填管理密钥(不是代理 key),小米 Token Plan 填控制台 Cookie。写入 DSH 凭据库后自动强制刷新;被进程环境变量遮蔽时宿主会拒绝写入,需改环境变量本身。
467
+ 点击卡片上的内联表单写入凭据:普通适配填 API key,CLIProxyAPI 填管理密钥(不是代理 key),小米 Token Plan 填控制台 Cookie。写入 DSH 凭据库后自动强制刷新;被进程环境变量遮蔽时宿主会拒绝写入,需改环境变量本身。凭据填错或需要轮换时,已配置的卡片在「配置」区同样有「修改凭据(已配置)」入口,可直接覆盖或清除已存值。
468
+ </details>
469
+
470
+ <details>
471
+ <summary><strong>CLIProxyAPI 卡片显示「已被 CLIProxyAPI 暂时封禁」?</strong></summary>
472
+
473
+ 管理密钥连续填错触发了 CPA 的 fail2ban:同一 IP 失败满 5 次封 30 分钟,封禁期间**正确密钥也会被拒**,且该状态不提供重填入口(密钥本身没问题)。等封禁到期(上游原话里带剩余时间),或更换 DSH 宿主机的出口 IP 后,点卡片「重试连接」即可恢复。封禁只作用于 `/v0/management` 管理面,走这台 CPA 的模型推理不受影响。
420
474
  </details>
421
475
 
422
476
  <details>
@@ -446,7 +500,7 @@ Step Plan 订阅没有 API-key 形态的查询接口,需要网页登录态令
446
500
  <details>
447
501
  <summary><strong>恢复备份会怎样?</strong></summary>
448
502
 
449
- 先做完整性检查并展示恢复预检计划,再由用户最终确认。提交前宿主会复检备份 SHA-256、当前目标指纹与运行中工作;任何变化都会中止,不会部分覆盖或重启。提交成功后会话目录整体替换,允许的配置文件按快照精确替换,profile 只覆盖 package.json(node_modules、凭据、附件不动)。受 Docker/systemd/pm2 等托管时自动重启;疑似终端手动启动时显示手动重启指引。删除备份同样需要两段式确认。
503
+ 先做完整性检查并展示恢复预检计划,再由用户最终确认。提交前宿主会复检备份 SHA-256、当前目标指纹与运行中工作;任何变化都会中止,不会部分覆盖或重启。提交成功后会话目录整体替换,允许的配置文件按快照精确替换,profile 按预检里的目标选择恢复(跨名映射默认只恢复 Profile 配置层、不覆盖本机插件清单,勾选后才覆盖;node_modules、凭据、附件不动)。受 Docker/systemd/pm2 等托管时自动重启;疑似终端手动启动时显示手动重启指引。删除备份同样需要两段式确认。
450
504
  </details>
451
505
 
452
506
  <details>
@@ -461,6 +515,12 @@ Step Plan 订阅没有 API-key 形态的查询接口,需要网页登录态令
461
515
  并发保护生效:SKILL.md 刚被外部编辑器改动(版本比对失败)。点击「刷新」获取最新状态后重试即可。
462
516
  </details>
463
517
 
518
+ <details>
519
+ <summary><strong>插件管理页装了插件,版本却停在旧版?</strong></summary>
520
+
521
+ pnpm 11 默认启用供应链发布观察期(`minimumReleaseAge`,1440 分钟):安装时**不带版本号**(含 `@latest`)只会选「发布时间已满观察期的最新版」,刚发布的新版会被跳过,官方插件页会提示「本次安装的是 x.y.z,而非最新版本 a.b.c」。要立刻装上新版:在插件管理页卸载后按 `包名@版本号` 重装(例如 `@gehennawu/dsh-service@2.0.8`),或直接用本插件的「**服务控制 → 一键升级**」——它按精确版本安装,不受观察期影响。观察期属供应链安全策略(新发布版本可能含尚未被发现的缺陷),等满 24 小时后不带版本号安装也会自然装到新版。
522
+ </details>
523
+
464
524
  <details>
465
525
  <summary><strong>更新检查失败会影响其他功能吗?</strong></summary>
466
526