See-Sol-Lab/DeepSeekGUI--packages-client-modules ↗★ 274
@deepseek-ai/dsh-client-modules
Client module system, dual-face: node half composes the __DSH_BOOT__ entry graph (incremental dsh.client scan, bundle route, index tap, webPlugins service); browser half is the lazy-CJS module table the vendored cordis Loader consumes as its internal seam
同名套件的其他儲存庫
- deepseek-ai/deepseek-harness--packages-client-modules
- whitelonng/dshcode--packages-client-modules
- fufankeji/deepseek-harness-studio--packages-client-modules
- op7418/pilot-harness--packages-client-modules
- SuperJJ007/papermachine--packages-client-modules
- peiyuwang54/deepseek-harness-cli--packages-client-modules
安裝
此插件尚未提供可驗證的 bundle,或相容性檢查未通過。請先閱讀倉庫說明。 閱讀完整 README ↗
說明文件
閱讀完整 README ↗description: "Client module system for the web GUI: the host composes the boot graph and serves plugin bundles, and the browser loads them lazily, for users and maintainers composing or debugging client plugins." kind: "package-reference"
@deepseek-ai/dsh-client-modules
English | 中文
Summary
dsh-client-modules turns a plugin package's dsh.client declaration into a loadable browser bundle: the host half scans enabled Loader entries and composes the boot graph, an available Web carrier serves each bundle over /plugins, and a shell-owned carrier dispatches the same exact bundle responses through fetchBundle(). The browser half loads those bundles lazily on demand. Plugin bundles execute lazily — running a bundle only registers a factory, and module side effects run at materialization — so nothing runs until a plugin is first used. Everything here is browser-kernel machinery; the model never sees it.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Use DshClientManifest for the declaration type. Client-modules validates the JSON and owns the normalized boot graph.
Use it when you compose or build a browser client plugin: the package turns a package's dsh.client declaration into a loadable browser bundle with no per-plugin wiring. It activates with the web composition; the shell boots it before any plugin runs.
Declaring a client plugin
A browser plugin package declares dsh.client in its package.json with platform: 'web', exports a ./client bundle, and lists any non-baseline module requests under dsh.client.external. The host half turns each declaration into a served bundle under /plugins, ordered so dynamic providers load before their consumers.
What the browser loads
The application combo scripts carry only each plugin's client.js entry and register those factories once during boot; module bodies remain lazy and run only at first import or materialization. A source import() split by tsdown compiles to require.async("./client..js"); its versioned sibling script arrives only when that expression runs. Rows that share a combo URL share one in-flight script task. A combo whose `` fails to load is requested once more; a combo that loads without registering a row is never re-executed, because the batch script registers its packages in sequence and a replay would stop at the first duplicate registration. In both cases each still-missing row then loads its own one-resource combo URL, so one failed batch costs at most three requests per missing row and leaves the rows it did register untouched. The module system records the last import failure per row (transport, registration, dependency cascade, or factory execution); the Web boot audit reports that text per entry. HMR switches one changed row to its revisioned one-resource combo URL. /client and the bare id resolve to the same exports, because a plugin bundle is its package's client half.
Live plugin composition
An open Web page follows the Host's complete module graph through the HMR transport. Enabling an ordinary plugin adds its Loader entry; disabling it removes the entry and waits for its asynchronous effects before evicting unused modules and styles. Re-enabling loads one instance with its styles. Other Loader contributors and shared modules still needed by active entries remain loaded. Settings → Plugins → Plugin list shows page-local synchronization failures and offers retry without changing Host enablement.
Host release changes
Every graph carries the Host release identity as release: this package's version, which releases share with the CLI, and the source commit that the client build fixes into the built Host artifact. Two releases are equal only when both fields are. A page applies graphs and rebuilt frames only while the connected Host serves the release the page booted from, so every module the page runs belongs to that release; when the Host returns to it, the usual revision comparison replaces exactly the changed modules. While the Host serves another release, the page applies nothing from it, keeps its current plugins running, and publishes ctx.modules.entries.releaseChange with the page's boot release, the Host's previous release, and its current release; only a page reload loads the other release. dismissRelease() withdraws the announcement and keeps the page quiet for that Host release until another one appears; it remembers only the last dismissed release, in page memory. Retrying failed plugins on the Plugins page does not load the other release either: the retry requests the boot release's artifacts, which a Host serving another release no longer has, so it ends in a download failure.
Sharing modules
The shell seeds a frozen module table (PLATFORM_MODULES); every dynamic bundle resolves its externals against exactly that baseline. dsh.client.external adds only exact non-baseline requests, each answered by the dynamic package row it names or an exact static-table key. Type-only imports are erased and create no request. Composition rejects malformed requests, missing suppliers, self-requests, and synchronous request cycles.
Build requirements
The host serves built client bundles, so pnpm run build must have produced each lib/client.js before launch; a missing bundle fails activation loudly with one build instruction and a package/path list. Source launch maps host imports to TypeScript source but still consumes the built client export. The package accepts no plugin config of its own.
Understand the implementation
Implementation internals — click to expand
This section explains how the module system is built; observable behavior is covered in Use this package.
Design concept
The package has two sides: the Node half is the composition and serving side (ctx.clientModules, ClientModuleRegistry), the browser half is the loading side (ctx.modules, ClientModuleSystem). The wire between them is the boot graph — WebBootEntry rows injected as window.__DSH_BOOT__, with .js?rev= request reads that script through the asynchronous file API, so a filesystem that materializes files on first asynchronous read can supply it, and caches it; its map remains uncomputed until the map URL is requested. The chunk reference the browser derives from its owner row is document-relative as well (plugins/ /client..js?rev=), and a chunk script's map trailer is the bare client..js.map?rev= name, which resolves against the script's own directory. Every combo or chunk map is Indexed Source Map v3 and uses an authored section when available or an identity section for the packaged bundle. Initial publication and HMR derive per-plugin revisions from the entry's mtimeMs, ctimeMs, and size, without hashing artifact contents. Status-change time distinguishes rewrites that preserve mtime and size. Unchanged artifacts therefore retain their revisions across Host restarts, so SSE reconnection does not replace their browser plugins. The shared preset stamps client.js after every package output is written, so a chunk-only rebuild changes the owner revision without a Host-side chunk scan. Combo revisions derive from the ordered row revisions. Advertised combo responses and requested chunk responses remain immutable across unrelated graph recomposition, and an unknown resource or revision returns 404.
Boot manifest injection
The bundle route follows the injected webServer lifetime: it registers when the service is ready and is removed and re-registered when that service is replaced. Module composition and fetchBundle() remain available without a Web server.
The host contributes structured index rows that inject, into ``: the window.__ModuleLoader__ queue facade, advisory preloads for every application combo, the parser-blocking bootstrap combo scripts, then the boot graph before the shell reads it. A Web carrier renders those rows into its index response; a shell-owned carrier can render the same rows without a Web server. The facade's create() materializes the modules bundle, delegates construction to its createClientModuleSystem export, and leaves the same facade in live-registration mode. The shell installs that returned system as its Loader's internal; the modules plugin publishes that instance as ctx.modules, so separate Cordis trees never select an instance through module-global state.
Entry ownership
ClientEntries records the entries created during boot and serializes full-graph updates, retries and code reloads over the same Loader. A local generation prevents an older download from mounting after its desired entry or code changes; snapshots of the same targets share the pending load. A graph from another release than the boot release stays outside the queue: retries keep reconciling the last applied graph, and rebuilt frames are ignored while the Host serves that release. New arrivals use single-resource URLs, never startup batches that could register existing factories twice. Factories retain their artifact revision before an entry exists; graph updates discard stale unowned factories, their styles and failed arrival targets before importing consumers. Cleanup retains declared and observed transitive module requests from every remaining Loader entry. Its observable status has no runtime library import because the modules bootstrap materializes before platform seeds are available.
Source map
| File | Role |
|---|---|
src/index.ts | Node half: ClientModuleRegistry, scan, artifact snapshots, optional combo route, structured index rows |
src/client/index.ts | Browser half: bootstrap export, ctx.modules enrollment |
src/client/system.ts | ClientModuleSystem: load/materialize/invalidate machinery |
src/client/entries.ts | Page entry reconciliation, retries and code replacement |
src/client/entry-lifecycle.ts | Loader fiber teardown through the registry and owned-style cleanup |
src/client/manifest.ts | Wire types, boot-manifest parsing, and the dsh.client declaration parser |
Further Exploration
Read these when the module contract is not enough: the subsystem reference, the shell that boots the tree, and the client authoring rules behind the graph.
- Client modules subsystem — the web plugin table,
WebBootGraphwire, and the bundle route. - Web boot kernel — the shell that creates the module system and boots the plugin tree.
- Client HMR driver — the reload chain that drives
invalidate/prefetchon rebuilt bundles. - Client authoring rules — the shared-module baseline and
dsh.client.externalsemantics. - Client group map — the browser half this package belongs to.
Model Experience
None, as the module loader is browser-side kernel machinery that registers nothing model-facing.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known Limitations and Deferred Work
These limits define what the module system does not do. They are current package constraints, not a task backlog.
- Metadata-based revisions — revisions identify filesystem generations, not content equality. Metadata-only changes can reload a plugin; changes invisible in mtime, ctime, and size cannot be distinguished.
- Flat module graph by design — every bundle is one module node whose edges point only at table leaves; the interface (
loadCache/edges/invalidate) already supports a general module graph, so the externalization granularity can change without an interface change. - Host release detection rides the graph stream — a page learns another Host release only from a graph frame. When a restarting Host answers
/plugins/eventswith a non-200 status before registering the route, the browser closes that stream for good, and the page neither updates plugins nor detects a release change until it reloads. - Source launch has no build step for the Host half — a Host started from TypeScript source reads the commit that `pnpm ru