Bundle
dsh-plugin-perf
Out-of-tree dsh web-performance plugin: compression and immutable caching for shell assets and client bundles, plus preload hints — additive, disposable, no dsh source changes required.
- Source
- royenheart
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-plugin-perf
An out-of-tree [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that makes the Web GUI faster over slow or high-latency links (`ssh -L`, nginx, Tailscale, …) without changing a single line of dsh source. It works purely through public Cordis/dsh extension points:
- Registers named routes for `/`, `/favicon.svg`, `/manifest.webmanifest`, and prefix `/assets` that shadow the frontend-static **fallback** (no route collisions) and replicate its observable behavior exactly, while adding negotiated gzip/brotli compression and immutable `Cache-Control` for content-hashed shell assets.
- Registers per-entry prefix routes `/plugins/<entryId>` that shadow the `/plugins` owner for its own entries (longer prefix wins) and serve `client.js` / `client.js.map` with compression and immutable caching; the bundle URL already carries `?rev=<content-hash>`.
- Injects optional `<link rel="preload" as="script" fetchpriority="low">` hints through `ctx.webServer.tapIndex` for stage-one client bundles.
- Never touches `/api`, event streams, sessions, or any other behavior.
## Compatibility statement
- **No dsh source changes required.** The plugin is a profile bundle: its package manifest declares `dsh.bundle.patch: ./cordis.patch.yml`, and `dsh plugin add` reconciles that bundle into `dsh.profile.bundles` automatically.
- **Additive and unloadable.** Every route, index tap, and graph subscription is a Cordis effect; unloading the plugin removes all registrations and clears its caches.
- **Behavior-preserving.** For the shadowed paths it keeps the frontend fallback's 405 (non-GET/HEAD), 403 (traversal outside the dist root), SPA index fallback on miss, MIME map, and unknown-extension octet-stream semantics. For bundle routes it keeps the client-modules owner's 404/405 and source-map behavior for any suffix other than `client.js` / `client.js.map`.
- **Degrades to a no-op** when optional services are absent. In a profile with no `webServer` the plugin stays idle; with no `clientModules` it still serves static routes and preload taps; with no frontend dist it skips static routes and still provides bundle routes and preload taps.
- **Tracks upstream fixes automatically.** At startup the plugin probes the official server once per optimization point (static compression, static immutable cache, bundle compression/cache, bundle preload). When the official deepseek-harness code already implements a point, the plugin leaves that point unregistered instead of double-applying it. As upstream adopts these optimizations, this plugin will keep updating the detection set in lockstep; even if a new official release arrives before this plugin is updated, the startup probe still prevents conflicts by falling back to official behavior for the features it detects.
## Measured effect (simplified report)
### How this was measured
The numbers below come from the checked-in benchmark harness `tests/benches/bench.mjs`, not from ad-hoc local commands: run `pnpm fetch-harness` to clone the pinned upstream deepseek-harness into gitignored `vendor/`, `pnpm build` to build this plugin, then `pnpm bench`. The harness starts `dsh web` twice on the same free port with fresh throwaway `DSH_HOME` directories — once unmodified (baseline) and once with the plugin's bundle mounted through a generated temp patch — so the only difference between the two runs is the plugin overlay. The plugin build used the defaults in `cordis.patch.yml` (`compression: gzip+br`, `immutableCache: true`, `preloadClientBundles: immediate`). The plain `node:http` pass measures `/`, shell assets, and every `/plugins/*/client.js` bundle; the optional Chrome pass (`CHROME_PATH=... pnpm bench`) loads the same pages through Chrome DevTools Protocol with `Network.emulateNetworkConditions` at 50 ms RTT / 2 Mbps down and cache disabled. Raw per-route JSON and server logs are written to gitignored `results/run-*/`, and the tables below are one such run — rerun `pnpm bench` at any time to reproduce or refresh them.
### Chrome CDP, emulated remote link (50 ms RTT / 2 Mbps down)
| Metric | Baseline | With dsh-plugin-perf | Delta |
|---|---:|---:|---:|
| Encoded bytes received | 4,497,240 | 1,025,900 | **−77.2 %** |
| Request count | 60 | 63 | +3 preload-related fetches under disabled cache |
| TTFB of `/` | 58.1 ms | 56.7 ms | ~same (HTML itself is not on the critical savings path) |
| Time until last plugin bundle | 17,696.9 ms | 4,649.7 ms | **−73.7 %** |
| Total page load (`loadEvent`) | 17,855.6 ms | 4,842.9 ms | **−72.9 %** |
### Plain node:http, loopback (no emulated latency)
| Metric | Baseline | With dsh-plugin-perf | Delta |
|---|---:|---:|---:|
| Encoded bytes | 4,412,820 | 898,770 | **−79.6 %** |
| Request count | 43 | 43 | unchanged |
| TTFB of `/` | 29.3 ms | 53.1 ms | first-request brotli warm-up |
| Last plugin bundle | 54.2 ms | 1,552.9 ms | first-request compression cost; subsequent requests hit the in-process compression cache |
| Total page load | 57.4 ms | 1,552.9 ms | see the compression-cache note below |
### Representative routes (Chrome CDP run)
| Route | Baseline TTFB | Plugin TTFB | Encoding added |
|---|---:|---:|---|
| `/assets/index-*.js` (442 KB raw) | 3818.2 ms | 1938.6 ms | brotli |
| `/assets/vendor-*.js` (745 KB raw) | 4981.7 ms | 2031.6 ms | brotli |
| `/plugins/.../dsh-client-connection/client.js` | 4170.0 ms | 1481.7 ms | brotli |
| `/plugins/.../dsh-client-trajectory/client.js` | (part of the 17.7 s tail) | 2204.7 ms | brotli |
### Why this is the right fix and why it is safe
The remote-access trace showed the page spends almost all of its time waiting on HTTP: dozens of uncompressed, uncacheable JS/CSS responses over HTTP/1.1 with a six-connection limit. The plugin removes the two largest costs without touching logic: compressed bodies shrink the dominant payload by roughly three quarters, and immutable caching (safe because shell assets are content-hashed and plugin bundles are addressed by `?rev=<content-hash>`) makes repeat visits revalidate nothing. Preloading the stage-one bundle tier starts plugin fetches while the shell still executes. All of this happens in transport/serving behavior only: route semantics, MIME handling, 404/405 behavior, the boot manifest, and every `/api`/event-stream path are untouched, and every registration is a reversible Cordis effect.
Known caveats: the loopback HTTP run shows the first plugin-served response pays compression CPU before the compressed-buffer cache warms up; under a real slow link this is negligible compared with the saved transfer time. With the Chrome benchmark's cache disabled, preloads can add three requests; with normal browser caching the preloaded response is reused by the later script fetch.
## Install / load
`install.py` is a LOCAL-only installer: it never calls `dsh plugin`, npx, or a registry, and it never edits the profile's own `cordis.patch.yml`. Every run rebuilds this checkout's host/client bundles, symlinks the package into the profile `node_modules`, and updates the profile `package.json` (`link:` dependency + `dsh.profile.bundles`). The profile itself must already exist — run any `dsh plugin --profile web ...` command once to initialize it.
```sh
python3 install.py install --profile web
python3 install.py uninstall --profile web
```
Options are `--profile` (default `$DSH_PROFILE`/`web`) and `--home` (default `$DSH_HOME`/`~/.dsh`). There is no `--skip-build` or remote spec: local iteration is `python3 install.py install` then restart dsh. The self-contained bundle patch inserts this row (override it later with a normal profile patch if needed):
```yaml
- insert:
- id: web-perf
name: dsh-plugin-perf
config:
compression: gzip+br
immutableCache: true
preloadClientBundles: immediate
```
## Restart vs hot reload
A restart of `dsh --profile web` is required after install/uninstall and after rebuilding this plugin's host code. Host-plane changes (webserver routes, index taps, settings registration) load only at process boot: the shipped web profile disables host-side `cordis-plugin-hmr` (`- id: hmr disabled: true` in the web-app bundle patch). The plugin now also ships a client bundle, so while `pnpm run dev:web` runs from the harness checkout the browser can hot-reload client-side changes; host-side iteration remains `python3 install.py install` (which always rebuilds) followed by a dsh restart.
## Settings UI
Installing the bundle also registers a **Performance** navigation entry in `dsh web → 设置`(设置 → 性能). It edits the same durable settings document the host reads at startup and exposes: compression mode, immutable cache, preload tier, auto-detect, probe timeout, compression-cache limits, and the log summary switch. All of these fields are registered with `applies: restart`, so the UI shows a restart note and changes take effect after restarting `dsh --profile web`; the bundle patch in `cordis.patch.yml` remains the composition-level default that user settings override.
## Configuration
| Field | Type | Default | Description |
|---|---|---|---|
| `compression` | `'gzip' \| 'br' \| 'gzip+br' \| 'none'` | `gzip` | Content-Encoding(s) the plugin may emit when the request accepts them. |
| `immutableCache` | `boolean` | `true` | Emit `Cache-Control: public, max-age=31536000, immutable` for content-addressed shell assets and plugin bundles. |
| `preloadClientBundles` | `'none' \| 'immediate' \| 'all'` | `immediate` | Inject `<link rel="preload" as="script">` for stage-one (`immediately:true`) entries, every entry, or no entries. |
| `cacheMaxEntries` | `number` | `128` | Compressed-buffer cache entries. |
| `cacheMaxBytes` | `number` | `67108864` | Total bytes of cached compressed buffers (64 MiB). |
| `logSummary` | `boolean` | `false` | Log one bytes-served summary line on unload. |
| `autoDetect` | `boolean` | `true` | Probe the official server at startup and skip each optimization point it already implements. |
| `probeTimeoutMs` | `number` | `2000` | Timeout for one official-capability probe request; probe failures keep the plugin enabled. |
All fields are optional and conservative; the schema is exported as `Config` (Schemastery) and the plugin also works with no config at all.
## Repository contents
- `src/` — the standalone `dsh-plugin-perf` plugin (this README).
- `tests/benches/` — before/after benchmark harness and report templates; concrete run data stays in gitignored `results/`.
- `scripts/fetch-deepseek-harness.sh` — generic pinned fetch of deepseek-harness into gitignored `vendor/` (`DEEPSEEK_HARNESS_REPO` / `DEEPSEEK_HARNESS_REF` overridable).
- `analysis/` — trace/session case reports and reusable measurement scripts (`trace_analyze.py`, `session_timeline.py`, `cdp_bench.py`, `throttle_proxy.mjs`).
## Development
```sh
pnpm install
pnpm build # tsc --noEmit + tsdown
pnpm test # vitest unit tests with fake webserver/clientModules contexts
```
Unit tests cover route registration/disposal, compression negotiation, cache headers, traversal safety, preload injection, bundle suffix 404/405 semantics, and graph-change resync. No real dsh process is needed.
## Benchmark
```sh
pnpm fetch-harness # clones vendor/deepseek-harness (gitignored), ref override: DEEPSEEK_HARNESS_REF
pnpm bench # before/after runs with fresh throwaway DSH_HOME dirs
```
`pnpm bench` boots `dsh web` twice on a free port (baseline, then with the plugin mounted through a generated temp patch), measures with plain `node:http`, and — when Chrome/Chromium is found or `CHROME_PATH` is set — also runs an optional CDP trace with emulated network conditions (default 50 ms / 2 Mbps, tunable via `BENCH_LATENCY_MS` / `BENCH_DOWNLOAD_KBPS`). Metrics: total encoded bytes, request count, per-route TTFB distribution, TTFB for `/`, time until the last plugin bundle, and total page-load time. Raw data goes to the gitignored `results/` directory; methodology and report templates are in `tests/benches/reports/`.
Install
dsh plugin --profile web add github:royenheart/dsh-plugin-perf
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-perf from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.