Sidebar system monitor for the DSH Web UI: live host overview (CPU/memory/network/disk), process list, and Docker container status, all read-only.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:pc439527/dsh-side-monitor
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
A read-only system monitor for DeepSeek Harness (DSH) Web: a “System Monitor” entry in the left sidebar footer opens a right-side monitor drawer that shows live host (the machine DSH runs on) overview, process list, and Docker container status.
Read-only by design: no docker restart/stop, no process kill, no exec, no shell. Built for quick resource checks, troubleshooting, and container observation.
v0.6.2 — macOS (Darwin) support
macOS has no /proc, so every Linux reader used to come back empty there (CPU "sampling…", no disk, 0 processes, no interfaces). This release adds a native Darwin provider (lib/darwin.js) that returns the same shapes from Apple's own tooling:
| Metric | Source |
|---|---|
| CPU % / per-core | node:os per-core tick times (same delta maths as /proc/stat) |
| CPU model / cores / clock | sysctl machdep.cpu.brand_string, hw.physicalcpu, hw.logicalcpu |
| Memory | vm_stat page counts + sysctl hw.memsize (available = free + inactive + speculative + purgeable) |
| Swap | sysctl vm.swapusage |
| Load / uptime | getloadavg(3) / node:os |
| Disks | df -k, APFS-aware: the /System/Volumes/Data volume becomes the single / row (system volume and /System/Volumes/* noise are dropped) |
| Network | netstat -ibn byte counters + os.networkInterfaces() addresses; primary = default-route NIC, never a VPN utun |
| Processes | ps -A -o pid,ppid,user,%cpu,%mem,rss,etime,state,command |
| Threads / runnable | sysctl kern.num_threads + R states from ps |
| Docker | Docker Desktop sockets (~/.docker/run/docker.sock, /var/run/docker.sock, DOCKER_HOST) |
Not available without root on macOS and reported honestly as unknown: per-disk read/write counters (iostat has no split) and thermal sensors (powermetrics needs privileges). The About dialog's capabilities reflect this.
An explicit procRoot still forces the Linux readers, so the /proc fixture tests run on any host.
v0.6.1 — DSH 0.2.2-rc.1 compat pass
Compatibility-only release against the installed DeepSeek Harness 0.2.2-rc.1 (@deepseek-ai/cordis 4.1.0). The full runtime surface was re-verified against the shipped packages and no runtime code change was needed — only the manifest's version declarations were retargeted:
engines.dsh: ^0.2.2-rc.1added.app-boot'sevaluatePluginCompatibilitychecksengines.dsh(when present) instead of peers, usingsemver.satisfies(..., { includePrerelease: true }); this pins the plugin to the 0.2.2 line it was verified on.@deepseek-ai/cordis:~4.0.4→~4.1.0— the installed runtime ships cordis 4.1.0 (anddsh-client-connection@0.2.2-rc.1itself peers on~4.1.0).@deepseek-ai/dsh-client-connection/-ui-renderer:^0.2.0-rc.2→^0.2.2-rc.1— align the declared DSH client line with the installed 0.2.2-rc.1 packages.- Re-verified:
connection.rpc.call(channel, endpoint, payload)still emits the sameclient-request/server-responseenvelopes (dsh-client-connection@0.2.2-rc.1); the keyedmain+sidebar.panellistseats still resolve throughui-renderer/ui-sidebar/ui-layout; the HostwebServer.register({ kind: 'prefix' })+connection.requestRejectionroute fence still matchesdsh-host-webserver. - Fixed
tools/sync-generated.mjs: it usednew URL(...).pathname, which leaves spaces percent-encoded (%20) and crashed in workspaces whose path contains a space. Now usesfileURLToPath. - Fixed a pre-existing timing flake in
lib/collectors.jsnetwork rates: two polls within the same wall-clock millisecond produceddt === 0and blankedrxBytesPerSectonull. The rate baseline now uses a monotonic millisecond stamp; the test suite is stable on fast disks. lib/client.jsCLIENT_VERSIONsynced to0.6.1;test/manifest.test.jsexpectations updated and anengines.dshassertion added.
v0.6.0 — Section rail, richer host metrics, history
- Left section rail inside the main panel (概览 / 性能 / 进程 / Docker / 硬件与系统). Narrow screens (<768px) turn the rail into a horizontal strip.
- Performance view: 10-minute server-side history (2 s samples, in memory only) with 1 / 5 / 10 minute windows; CPU, memory, swap, load, network and disk IO trends; per-core CPU bars.
- Overview adds swap, threads / runnable tasks, disk IO rates and a memory breakdown (free / buffers / cached / available).
- Hardware & system adds CPU clock and thermal sensors (shows "no sensor" when none are readable).
- Docker adds a daemon summary card (
/infoand/version, read-only). - RPC protocol v4 (additive): new endpoints
historyanddockerInfo;meta.capabilitiesgainsswap,perCpu,diskIo,temperature,history,dockerInfo. Still read-only.
v0.5.0 — System Monitor as a main panel
- Entry moved to the navigation group beside Plugins and Automation tasks, using the same
sidebar.panellistseat as the context-insight plugin. The old footer trigger is gone. - The monitor is a main-panel page (keyed
mainslot, keydsh-side-monitor) instead of a right-side drawer. Page header, tabs (Overview / Processes / Docker), scrollable body and responsive cards follow the context-insight layout. The width drag handle and drawer animation were removed. - The row label is a thunk (
t("monitorTitle")), so it follows the panel language without re-registration. - Fixed a blank page: the page component was passed as a string tag, which React rendered as an unknown element.
v0.4.0 — DSH 0.2.0-rc.2 compat pass
- Verified against DeepSeek Harness 0.2.0-rc.2: the
sidebar.footer.actionslot (ctx.slots.inject+ctx.slots.register), thectx.connection.rpc.callbrowser contract and the Host route-mount registration all match the 0.2 runtime (the /side-monitor endpoints were driven end-to-end against a live boot: token exchange, session cookie, meta / overview responses). - The drawer is now a
shell.overlayslot entry instead of a second React root appended todocument.body. The 0.2 UI guide forbids a plugin-owned app root; the shell's frame-wide floating layer gives the drawer the host theme, light/dark switching and stacking for free, and it is click-through until the drawer's own box (which opts back into pointer events) is hit — the rest of the app stays usable while the monitor is open. - Host channel registration follows the shipped route-mount pattern: the bundled
dsh-host-open-in-appis the live proof of how 0.2 plugins serve browser routes. Reaching the carrier throughconnection.rpc.handle()(the 0.1.x way) does not work: the facade mounts its route throughowner.webServer, and cordis 4 refuses that read on the shadow-bound context the facade ends up with (even when the plugin declares it), so the channel silently never mounted and the drawer reported 'transport failure ... HTTP 405'. v0.4.0 instead declaresinject = ['connection', 'webServer']and registers its ownPOST /side-monitor/<endpoint>prefix route, fenced byconnection.requestRejectionbefore anything is reachable, speaking the exact 0.2 client wire (client-request/server-responseenvelopes). - Manifest fixed:
dsh.client.injectandpeerDependenciesdropped@deepseek-ai/dsh-client-runtime(it stopped publishing at 0.1.1-rc.2).injectnow names the modules whose seats and services the panel uses —@deepseek-ai/dsh-client-connection,@deepseek-ai/dsh-client-ui-layout(declaresshell.overlay),@deepseek-ai/dsh-client-ui-renderer(provides theslotsservice) and@deepseek-ai/dsh-client-ui-sidebar(declaressidebar.footer.action);peerDependenciespin@deepseek-ai/dsh-client-connectionand@deepseek-ai/dsh-client-ui-rendererto the^0.2.0-rc.2line and@deepseek-ai/cordisto~4.0.4. - Theme tokens refreshed: the removed
--dsw-specific-surface-fillis replaced by--dsw-specific-menu(dropdown, port menu, modal) and the dedicated--dsw-alias-toast-bg/--dsw-alias-toast-labelpair; the drawer shadow uses--dsw-elevation-panelinstead of a literal. - Regression tests now guard the manifest, the wire contract on the mounted route (trust fence, media type, envelope validation, rpcId echo), the
shell.overlayregistration and the absence of adocument.bodyReact root.
The panel still ships its own zh-CN / en-US dictionary and language switcher (
dsh-side-monitor:language) rather than the 0.2 Client locale service. Migrating toctx.locale— so the panel follows Settings → General and registers in the language catalog — is planned for a later release.
v0.3.1 — DSH 0.1.1-rc.2 compat pass
- Verified against DeepSeek Harness 0.1.1-rc.2: the
sidebar.footer.actionslot (ctx.slots.inject+ctx.slots.register), thectx.connection.rpc.callbrowser contract and the Hostconnection.rpc.handlechannel registration all match the 0.1.1-rc.2 runtime (live-tested:/side-monitoroverview / processes / containers / meta return current data). - Dropped the stale
@deepseek-ai/dsh-client-ui-slotsreference: the package no longer ships in DSH 0.1.1-rc.2 (its slot runtime merged into@deepseek-ai/dsh-client-runtime). Removed it fromdsh.client.injectand frompeerDependenciesso freshdsh plugininstalls resolve cleanly instead of pulling the rc.6-era package.
v0.3.0 — stopped/issue split, perf, capabilities & drift checks
- Docker stopped / issue semantic split: a cleanly stopped container (exit code 0) is now just "Stopped" — it no longer inflates the issue count. Issues are reserved for real problems: unhealthy, health-starting, crash-looping (restarting), dead, or non-zero exits. The Overview + Docker tabs show separate Total / Running / Stopped / Issues chips and filters; crashed containers display their exit code (Crashed (137)).
- Host network probe performance: the
--net=hostnetns probe now runs at most every 15s (was 5s) and never blocks the overview poll — stale snapshots are reused while the refresh happens in the background, with a baseline guard so the container-netns fallback never diffs against host-netns counters. - Client version auto-sync:
lib/client.js'sCLIENT_VERSIONis regenerated frompackage.jsonbytools/sync-generated.mjs(replacestools/convert-i18n.mjs), which also regenerates the inline i18n dictionary and lints for the no-concatenation rule;npm run check:genverifies there is no drift. - i18n no-string-concatenation: every user-visible sentence is a placeholder message — no
t("a") + t("b")splicing anywhere in the client, enforced by the generator's lint. - Label & header polish:
Received/Sent->RX/TX;DSH Container->DSH running in container(DSH on host->DSH running on host); the status line under the header no longer repeats the data source (it was duplicated with the header badge) and the view label is properly spaced (Host view, neverHostview). - meta status细分 + capabilities: the
metaendpoint reports fine-grained status (mode, per-source state, network probe, consistency) and Host capabilities (Host Mount mode, Docker socket, host-netns probe, process aggregation, container stats) — shown in new Status / Capabilities sections of the About dialog. - Process aggregate details: grouped cards expand to show the command, distinct users and total RSS.
- Memory chart transparency: the memory sparkline fill opacity is tuned for legibility (0.2 vs 0.12 for CPU); Docker cards are compressed further.
v0.2.3 — i18n & host networking
- Full bilingual UI (zh-CN / en-US): default Simplified Chinese (never follows the browser), switch instantly from the drawer menu (⋯ → Language) with persistence via
dsh-side-monitor:language. Every header / tab / card / process / docker / toast / error / tooltip / diagnostics string is translated (dictionary inlib/i18n.js, regenerated into the client bundle bytools/sync-generated.mjs). - Header now shows the data source badge (
Host Data/宿主数据) with the runtime note in the subtitle (PC9527-fnOS · DSH Container); About reports Browser / Host / RPC / Runtime / System / Process / Docker sources. - Real host network in Host Mount Mode:
/proc/netis net-namespace scoped, so a/host/procbind still shows the container's interfaces — v0.2.3 reads the host netns through a short-lived--net=hostread-only probe (cached, falls back gracefully) and curates veth/br/docker noise. Disk dedup now usesst_devand surfaces host data volumes (/vol1on fnOS). - Docker port chips follow the visual rules: web = blue
🌐 host → container, plain TCP = neutral📋, loopback = yellow🔒 + Host only, unpublished = gray🔒 + Container only; IPv4+IPv6 dual-stack renders once with anIPv4 + IPv6tag.
Screenshots
Features
Overview
- CPU / memory metric cards: large percentage, sub-info, and an area-filled sparkline (fixed 0–100 axis).
- Network primary-interface throughput / root-partition disk lightweight KPIs.
- Sections below: system load, system info, disk partitions (multiple mount points), network interfaces (default-route + virtual-interface markers, RX/TX rates), Docker summary (total / running / stopped / issues).
Processes
- Source label (host / current container).
- Search / sort / pagination run entirely in the Host RPC (scans all processes, then filters) — stays smooth with large process tables.
- Sort chips for CPU / memory / PID / name; cards show PID · PPID · user, click to expand RSS / uptime / command.
- List and aggregate views: group by name+command, expand to see the PID list, command, distinct users and total RSS.
Docker (Containers)
- Container name / image / state / health (healthy/unhealthy/starting) / CPU% / memory / ports. Clean stops (exit 0) are shown as Stopped and never counted as issues; crashed containers show Crashed (137)-style badges.
- Actionable ports: published Web ports (with hostPort) open in a new tab on click; non-Web ports copy
host:port; right-click menu offers HTTP/HTTPS open / copy address. - Correct handling of
127.0.0.1/0.0.0.0/ explicithostIp(IPv6 auto-bracketed); unpublished ports show 🔒 and cannot be opened; containers with failed stats show a ⚠ tooltip.
UX & Reliability
- Sidebar entry: registered on the
sidebar.panellistnav group (beside Plugins / Automation tasks) — shows text when expanded, icon only when collapsed, highlighted while open. - Responsive: the monitor is a keyed main-panel page (v0.5.0+) with a left section rail (Overview / Performance / Processes / Docker / Hardware & system); below 768px the rail becomes a horizontal strip and the layout adapts via Container Query; mobile uses
100dvh+ safe-area insets. - Source identification: auto-detects the environment (Host / Container); top badge + status line (per-module sources for overview / processes / Docker) + a “View data sources” dialog listing the real source paths with a consistency self-check.
- Independent module state: each module has its own error / updated-at; on failure the last good data is kept with a stale banner.
- Protocol handshake: RPC responses carry
protocolVersion(v4) +pluginVersion; a mismatch shows a “version mismatch” banner and an About panel (Browser / Host / RPC versions, fine-grained status and capabilities) instead of undefined fields. - Manual refresh (spinner animation) and a “copy diagnostics” action that generates a one-click diagnostic text.
- Polling stops/pauses when the panel is closed or the tab is hidden; each poll awaits the previous request (no re-entrancy).
Installation
# install from a local directory
dsh plugin --profile web add /path/to/dsh-side-monitor
After installing, refresh the page — a “System Monitor” entry appears in the left sidebar's navigation group (beside Plugins / Automation tasks).
Host Mount Mode
When DSH runs inside a container, the collectors read the container's own /proc (container view). To monitor the real host, add read-only mounts that expose the host's proc / sys / root filesystem at fixed paths:
services:
deepseek-harness:
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/host/root:ro
- /var/run/docker.sock:/var/run/docker.sock
The collector auto-detects these paths (host view wins when present, container view otherwise); you can also set them explicitly via plugin config:
procRoot: /host/proc
sysRoot: /host/sys
fsRoot: /host/root
Once mounted, overview / processes read host resources and the source label switches to “host view”. Note: /proc, /sys, and / must be mounted read-only.
Refresh Intervals
| Data | Interval |
|---|---|
| CPU / memory / network / load / uptime | 2s |
| Disk | 10s (Host-side cache) |
| Process list | 3s (Host-side snapshot cache) |
| Docker list + stats | 5s (stats 3s cache) |
Architecture
Client UI (Sidebar Nav Row + Monitor Main-Panel Page + 5 Sections)
│ RPC: connection.rpc.call('/side-monitor', ...)
▼
Host Service (lib/collectors.js + lib/rpc.js)
├─ Environment (mode / systemSource / processSource / dockerSource / hostname)
├─ Overview Collector (procRoot/stat|meminfo|loadavg|uptime|cpuinfo|sys/kernel/osrelease + fsRoot/etc/os-release + net/dev|net/route + mounts/statfs)
├─ Process Collector (procRoot/<pid>/stat|status|cmdline, host-side search/sort/pagination, PPID included)
├─ Network Collector (procRoot/net/dev sampled diff + procRoot/net/route default route + fib_trie/if_inet6 interface IPs)
├─ Disk Collector (procRoot/mounts + statfs multi-mount, mountinfo major:minor dedup, 10s cache)
└─ Docker Collector (/var/run/docker.sock read-only Engine API, health + structured ports)
Security
- The browser never touches the host filesystem or the Docker socket directly — all collection goes through the Host-side whitelisted RPC.
- The Host mounts a single prefix route under
/side-monitorwith three read-only endpoints:overview/processes/containers. Every request is fenced byconnection.requestRejection— DSH Connection's Host/Origin trust fence (loopback plus configured--trusted-hostauthorities) and its browser-session authentication — before anything is reachable. - No arbitrary command execution, no generic Docker API proxy, no control operations.
- In Host Mount Mode,
/host/proc,/host/sys, and/host/rootmust be mounted read-only.
Development
npm run check # syntax check
npm test # node:test unit tests (test/fixtures/proc are real /proc snapshots)
Known Limitations
- macOS without root: per-disk read/write counters (
iostathas no split) and thermal sensors (powermetricsneeds privileges) are reported as unknown. - Enabling
pid: hoston the DSH container gives the full host PID view, but the consistency self-check then reports that the PID namespace is not isolated. - A settings page and native DSH Side Card integration are planned for later releases. (Historical trends shipped in v0.6.0; the panel keeps its own zh-CN / en-US dictionary rather than the Client locale service.)
Changelog
- v0.6.2 — macOS support: native Darwin provider (sysctl / vm_stat / df / ps / netstat), APFS-aware disks, per-core CPU, swap, processes, Docker Desktop sockets.
- v0.6.1 — DSH 0.2.2-rc.1 compat pass:
engines.dsh+ cordis 4.1.0 / DSH client 0.2.2-rc.1 peer line; generator path fix; runtime surface re-verified (no code change needed). - v0.6.0 — Section rail, 10-minute history, richer host metrics, RPC protocol v4.
- v0.5.0 — System Monitor became a keyed main panel with a
sidebar.panellistnav row. - v0.4.0 — DSH 0.2.0-rc.2 compat pass: drawer moved to the
shell.overlayslot (nodocument.bodyapp root), Hosthandle(channel, handler)arity, manifest peer deps /dsh.client.injectretargeted to the 0.2 line, current theme tokens. - v0.3.x — Stopped/issue split, host netns probe caching, client version auto-sync, i18n lint, 0.1.1-rc.2 compat.
- v0.2.2 — Reliability: network uses
/proc/net/devas source of truth (interfaces/traffic kept even when IP resolution fails); CPU distinguishes physical cores / logical CPUs; Docker port refinements (loopback locking, hostIp dedup, localized uptime); RPC version handshake; process aggregate view; mobile100dvh+ safe areas; fixture unit tests and CI. - v0.2.1 — Host metric accuracy: load / uptime / CPU model / kernel / OS read from real host sources; process uptime uses host uptime; network IPs from
/proc/net/fib_trieandif_inet6; disk dedup via mountinfomajor:minor; source self-check. - v0.2.0 — Source identification, redesigned CPU/memory cards, actionable Docker ports, Host Mount Mode.
- v0.1.0 — Initial release: responsive monitor panel.
License
MIT
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 10406
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 10406
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
ccch1mneyyy/dsh-TUI★ 7675
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 7201
A fixed-corner whale widget for the DSH web GUI — balance, today's usage and per-turn cost with peak/off-peak pricing, editable balance-alert and daily-budget bubbles, a module-based custom bubble queue with A/B weighted choices and random lines or images, 30+ vendor templates (OpenAI, OpenRouter, Kimi, SiliconFlow, Ark, Zhipu, MiniMax and more) with per-model balance and subscription quota, plus task-end sound, imported audio, custom roles and a resource manager. Local-only, no telemetry.
omdsh-dev/DSH-better-sidebar★ 5142
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1816
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.