dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
A dsh (DeepSeek Harness) Cordis plugin that keeps llm-pi-ai provider routes' model catalog in sync with the pi.dev gateway — written through the official settings seam, zero patches to dsh internals.
简体中文 | English
· GitHub
A dsh (DeepSeek Harness) Cordis plugin that keeps the model catalog of your llm-pi-ai provider routes in step with the pi.dev gateway's model listing — written through the official settings seam (settings.mutate), with zero patches to dsh internals.
Requires dsh >= 0.2.0-rc.2 — this plugin targets the dsh RC/stable line only (CI and releases resolve the newest of the latest/next dist-tags at runtime). The alpha line is no longer supported. On dsh 0.1.7+ the settings document is the profile patch (settings.yaml is imported once and renamed); the plugin only ever writes through the official API, so it works unchanged on either shape.
https://github.com/user-attachments/assets/c3f9c8b1-ea5e-470c-b8a8-60a81fc5c20a
*A live recording of a dsh session running /model-sync (MP4, 1.5× speed) — drop reasons, per-route sync statuses and the change report in action. More demos in the Demos issue.*
Model lists drift: providers ship new models, retire old ones, and adjust capabilities (contextWindow, input modalities, thinkingFormat, reasoning efforts). Keeping them in sync by hand is error-prone busywork. dsh-model-sync does it for you:
writer.ts, profilesEqual, reason: 'no-change').llm-pi-ai provider profile) simply reflect it.https://pi.dev/api/models/providers/ with ETag/304 revalidation and a persistent per-provider cache under ~/.dsh/models-store.json (remote-catalog.ts). Transient failures and aborts keep the last-good cache; a 404/501 treats the route as absent for the round.managedRoutes is empty, these pi.dev routes are synced: opencode-go, zai-coding-cn, minimax-cn, xiaomi-token-plan-cn (DEFAULT_ROUTES in src/index.ts).writeMode):settings (default) — the zero-patch pipeline: fetch → translate → settings.mutate. Self-contained; never touches the settings document directly, only via the official settings API.overlay (legacy, deprecated) — delegates to the patched dsh-llm-pi-ai adapter's piAiCatalog.refresh() and merges pi.dev entries in memory (requires the optional patch). dsh 0.2.1 removed the piAiCatalog seam outright (pi-ai 1.3.0 ModelsStore rework), so every overlay round now degrades to a notice — use the settings mode.intervalMinutes auto rounds (default 240 / 4h) plus a startupDelaySeconds initial delay (default 5); each round logs the same report a manual refresh produces. 0 disarms the interval (startup-only). The interval re-arms live when the config changes (src/index.ts).diffModelIds), and in settings mode added/removed/changed entries against the current raw settings (diffEntries, diff.ts). Dropped and degraded entries are reported with their reasons.modelSync service (syncNow()) that a UI can call to force one refresh round and read the report./model-sync slash command itself through the shared dsh command registry (@deepseek-ai/dsh-commands), so every interactive UI lists it automatically — no UI-side wiring. Running it forces one sync round on the spot and prints the same report the scheduled rounds log; the sync scope is decided by managedRoutes (arguments are ignored). The registry is an optional peer: hosts without a command registry still get the scheduled rounds and the modelSync service.translate.ts): base-matching vs base-less classification, reasoningEfforts derivation (S2 gate), compat gating to openai-completions (S5 gate), maxTokens handling, and drop logic for mixed-protocol routes. Capacity values get a sanity gate: a contextWindow that is not a positive integer, or a maxTokens that is not a positive integer strictly below the context window (listings sometimes echo the context window into maxTokens), is skipped with a degrade warning instead of written.modelOverrides is your own per-model channel (think levels, narrowed context windows). dsh refuses a models list beside non-empty overrides, so the sync folds your fields into the written models, clears the key in the same write, and re-applies the values from its store (~/.dsh/models-store.json) on every round — they keep winning over the synced values for as long as the route is managed.keepBuiltinOnly: true — keep built-in catalog models that are not (yet) on pi.dev, so adopting the sync doesn't delete models you already use.dropUnserviceable: true — drop unserviceable entries and continue; set to false to abort the whole route instead of writing a partial list.forceMaxReasoningEffort — force models with a non-empty thinkingFormat to max reasoning effort (ensures reasoningEfforts contains max and forces compat.supportsReasoningEffort = true on openai-completions).GET /models listing and unions it in: DeepSeek, Moonshot/Kimi (moonshotai, moonshotai-cn, kimi-coding), Zhipu (zai, zai-coding-cn), and Xiaomi MiMo (xiaomi, xiaomi-token-plan-cn/ams/sgp; api-key header auth). The merge is additions-only — ids pi.dev already carries keep pi.dev's metadata, listing-only ids enter as minimal entries. Unknown capacities are simply not written (contextWindow only when the listing states one, e.g. Moonshot's context_length; maxTokens never), so the route's defaultContextWindow / defaultMaxTokens apply. A failed listing fetch degrades to the pi.dev result and never loses models; providerNativeFetch: false restores the pi.dev-only behavior.SETTINGS_CONFLICT (writer.ts).Requires Node ≥ 22.19 and a dsh profile. Install as a dsh plugin:
npm i @aiwayds/dsh-model-sync
dsh plugin add @aiwayds/dsh-model-syncThe package ships cordis.patch.yml (wired as dsh.bundle.patch), which mounts the plugin into the profile's assembly tree under the stable plugin id dsh-model-sync — on dsh 0.1.7+ that entry id doubles as the settings namespace for the plugin's Config schema.
This plugin ships standalone — install it explicitly with dsh plugin add @aiwayds/dsh-model-sync when you want it.
dsh plugin remove @aiwayds/dsh-model-syncThe host auto-cleans: the bundles entry is spliced out of the profile and the plugin's patch layer drops with the package, so the sync rounds and the /model-sync command simply stop. Three things intentionally stay on disk:
modelOverrides: under the store-first invariant the plugin folds overrides into the written models and unsets the settings key, so for a managed route the store can be the only copy of your override values. Deleting the file deletes them.llm-pi-ai namespace (providers..models) through the official settings seam (~/.dsh/settings.yaml up to dsh 0.1.6, the profile patch on 0.1.7+). They persist after removal and remain valid host config — dsh consumes them exactly as if you had written them by hand. Remove those entries by hand if you don't want them.~/.dsh/models-store.json..tmp file can remain. It is safe to delete.Configure the plugin under the dsh-model-sync settings entry — on dsh 0.1.7+ the entry id (the same stable id the bundle patch always mounted) is the settings namespace; edit it in the settings UI or your profile patch. Every key is optional, and every key is a volatile field, so edits apply without restarting the plugin. Upgrading from a pre-0.1.7 install: a legacy top-level model-sync: section in the old settings.yaml is imported once into the new entry automatically at the next plugin boot (the audit record lands in ~/.dsh/storages/dsh-model-sync/legacy-import.json):
Key · Default · Description writeMode · 'settings' · Zero-patch pipeline; 'overlay' (deprecated since dsh 0.2.1) for the legacy patched-adapter mode intervalMinutes · 240 (4h) · Auto-refresh interval in minutes; 0 = startup-only startupDelaySeconds · 5 · Delay before the first auto refresh, so the llm adapter is ready refreshTimeoutMs · 120000 · Abort budget for one refresh round's network request (min 1000) managedRoutes · [] · Routes to sync; empty = the default pi.dev routes keepBuiltinOnly · true · Keep built-in-only models not present on pi.dev (smooth migration) dropUnserviceable · true · Drop unserviceable entries; false aborts the route instead syncNotify · false · Notify on changes (logger + /model-sync report) forceMaxReasoningEffort · false · Force max reasoning effort on models with a non-empty thinkingFormat providerNativeFetch · true · Union each mapped provider's first-party /models listing into the pi.dev result (additions only) keepDeprecatedBuiltin · false · Opt back into emitting builtin ids the official default model list dropped (dsh 0.1.7 removed deepseek-v4-flash / deepseek-v4-flash-vision-exp; dsh 0.2.0 removed omen-alpha on opencode-go and glm-4.7 / glm-5-turbo / glm-5.1 / glm-5.2 / glm-5.2-highspeed / glm-5v-turbo on zai-coding-cn; the snapshot keeps the data but no longer syncs them by default)
Example (settings section dsh-model-sync; a legacy settings.yaml model-sync: section is not auto-imported by the dsh 0.1.7 host itself — the plugin recovers it once at boot, see above):
dsh-model-sync:
writeMode: settings
intervalMinutes: 30
managedRoutes:
- opencode-go
- zai-coding-cnThe plugin writes to the llm-pi-ai namespace (providers..models) — the same document the adapter consumes — and only for the routes it manages. During migration, keepBuiltinOnly preserves models that exist in your installed built-in catalog but aren't on pi.dev yet.
Synced contextWindow / maxTokens describe what the model accepts at most, as advertised by the gateway listing — not what your deployment is configured for. dsh resolves the settings-written value over the installed catalog, and a written maxTokens becomes the request-level default. Pointing a route at a local or proxied endpoint that serves a smaller context (vLLM / Ollama and friends) while carrying catalog-sized capacities is a known recipe for the "output token limit reached" family of failures.
If you need a model to run under a smaller budget, set it in modelOverrides under the same route — the sync folds the fields into the synced list and re-applies them from its store every round:
providers:
zai-coding-cn:
modelOverrides:
glm-5.3:
contextWindow: 32768Note that listing data itself can be noisy: values are sanity-gated (positive integers; maxTokens strictly below contextWindow), and stripped values show up in the sync report as DEGRADED lines with reasons.
/model-sync commandType /model-sync in any interactive UI to force one sync round on the spot. The plugin registers the command in the shared command registry (@deepseek-ai/dsh-commands), and UIs discover it automatically. It returns the same report the scheduled rounds log. The sync scope is decided by managedRoutes; any arguments typed after the command are ignored. Hosts without a command registry degrade gracefully — the scheduled rounds and the modelSync service keep working.
npm run build # tsc → lib/
npm run check # tsc --noEmit typecheck
npm test # node --test (pretest builds): diff / translate / writer / remote-catalog / serviceability / commandTests use per-route pi.dev fixtures under test/fixtures/ and temp directories for the models store — they never touch the real ~/.dsh.
Utility scripts under scripts/:
generate-builtin-snapshot.mjs — regenerate src/builtin-catalog-snapshot.ts from the installed @deepseek-ai/dsh-llm-pi-ai catalog (--generate for dev, --check for CI). Hand-maintained deprecated: true marks (ids the official default model list dropped) are preserved by id across regenerations.verify-no-patch.mjs — exits non-zero if any installed dsh-llm-pi-ai still carries the overlay patch signatures (withRemoteCatalog / piAiCatalog).backup/backup-patched.mjs — back up a patched dsh-llm-pi-ai/lib/index.js to backups/.backup/restore-official.mjs — restore the official unpatched dsh-llm-pi-ai/lib/index.js from npm, validated against the patch (--dry-run supported).The repo also carries the reference patches that document the legacy overlay behavior: docs-dsh-llm-pi-ai.patch (pi.dev remote-catalog overlay for dsh-llm-pi-ai) and docs-dsh-llm-pi-ai-compat.patch (supportsDeveloperRole compat passthrough).
MIT.