dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness plugin for system monitoring & junk cleanup · 系统监控、垃圾清理与应用卸载的 DeepSeek Harness 插件
A PC Manager plugin for DeepSeek Harness (dsh), shipped in the official bundle format. It provides: system status monitoring (model tools + a right-sidebar dashboard), system junk cleanup (a zero-LLM direct-execution window), and app uninstall (M3) — 5 model-visible tools in total.
Platform support: monitoring (pc_status + dashboard) and junk cleanup
(pc_junk_scan/pc_junk_clean + the cleanup window) work on macOS, Linux and Windows; other
platforms return unsupported_platform. On Windows, metrics come from node builtins plus a single
batched PowerShell probe (see Windows data sources), the junk registry is
JUNK_TARGETS_WIN32, and recycling goes to the real Recycle Bin.
It is positioned as a security-first agent system tool: scans are always dry-run; the two
destructive tools default to disabled_by_config and only act after the host explicitly opts in;
trash/uninstall defaults to the Trash (recoverable); if any link of the id validation chain fails,
the whole call is rejected with zero deletion.
This repository is an official bundle plugin (dsh.bundle + dsh.client in the root
package.json); the build artifacts in lib/ are committed, so a git install requires no build
step:
dsh plugin --profile web add "github:maoqizhen/dsh-pc-manager#main"
After installing, restart dsh web (the bundle layer is synthesized at startup). To update:
dsh plugin --profile web update dsh-pc-manager; to uninstall:
dsh plugin --profile web remove dsh-pc-manager — both require a restart to take effect.
pnpm required:
dsh pluginis a pnpm forwarder and fails outright if pnpm is not on the PATH (install withnpm i -g pnpm; the pnpm major version must match the profile's existing store). If dsh runs under a non-login shell on a server, make surenode/pnpmare visible on that process's PATH (e.g. in/usr/local/bin).
Out of the box the install is safe-by-default: read-only tools work immediately, while the two
destructive tools (pc_junk_clean / pc_app_uninstall) refuse to execute
(disabled_by_config). To enable junk cleanup, write to a back-layer patch (the profile's
cordis.patch.yml or ~/.dsh/cordis.patch.yml; the back layer replaces the whole config line,
so all keys must be restated):
- insert:
- id: pc-manager
name: dsh-pc-manager
config:
enableJunkClean: true # the per-call host approval gate still applies
moveToTrash: true # default to Trash (recoverable)
No dsh plugin add needed, but a dual entry is mandatory (the package-name entry lets the
client half be discovered by the clientModules scan; the file-path entry lets the host half's
apply run; the official bundle install has no such issue):
# 1. Let the profile resolve the package by name (the client half is discovered
# via require.resolve('<pkg>/package.json'))
ln -s "$PWD" ~/.dsh/profiles/web/node_modules/dsh-pc-manager
# 2. Append the dual entry to ~/.dsh/cordis.patch.yml (host: absolute path; client: package name)
# - insert:
# - id: pc-manager
# name: /abs/path/to/dsh-pc-manager/lib/index.js
# - id: pc-manager-client
# name: dsh-pc-manager
# 3. Restart dsh web
| Tool | Purpose | Status |
|---|---|---|
pc_status | Read-only system snapshot: CPU/GPU utilization, load, temperature (capability-gated), memory breakdown (incl. swap), disk I/O and per-volume usage, battery/power, network counters, multi-key sortable process table (macOS + Linux + Windows; Windows has no load average, and the per-process network and disk columns are unobtainable — see Windows data sources) | ✅ Shipped |
pc_junk_scan | Enumerates reclaimable junk (macOS: Trash, user caches/logs, system temp, Xcode artifacts, simulator leftovers, package-manager caches, iOS backups; Linux: XDG Trash, ~/.cache, /tmp and /var/tmp, package-manager caches; Windows: the per-account Recycle Bin, %TEMP% and %SystemRoot%\Temp, WER reports and crash dumps, WinINet/D3D/NVIDIA shader/RDP caches, package-manager caches) with per-item size / safety flag / protected-exclusion records; params kinds / minItemBytes; always dry-run | ✅ Shipped |
pc_junk_clean | Trashes items by the exact ids returned by scan; if any link of the structural validation chain (format/root containment/realpath/blocked/safeToClean) fails, the whole call is rejected with zero deletion; the trash kind is emptied in place, everything else defaults to the three-tier trash (macOS: /usr/bin/trash → rename into ~/.Trash → cross-volume cp+rm; Linux: trash-put/gio trash → freedesktop ~/.local/share/Trash/{files,info} with .trashinfo restore records → cross-volume cp+rm; Windows: the Microsoft.VisualBasic recycle API into the real Recycle Bin → fallback %LOCALAPPDATA%\pc-manager\trash → cross-volume cp+rm); refuses to delete when sizes can't be verified; tier-1 results are re-verified (success reported but source still present counts as failure and degrades); requires explicit config opt-in | ✅ Shipped |
pc_apps_list | Installed-app inventory (app bundles + Homebrew; Linux distro packages and Windows registered programs await M3), with size / last-used | Stub, M3 |
pc_app_uninstall | Uninstalls by exact id; defaults to Trash, residual items are reported rather than silently deleted | Stub, M3 |
The "System Monitor" entry (order 30) in the web profile's right sidebar opens a narrow-column card dashboard: header (host/OS + a muted second line "local … · public IP · city" (geo shows city only; the segment collapses when lookup is off or misses) / uptime) → CPU (bar + load + temperature row* + sparkline) → GPU (whole card hidden when unavailable) → memory (swap + breakdown) → disk (usage bars for valid volumes + I/O rate) → battery (hidden when absent) → network (main-interface rate + sparkline) → process table (switchable by CPU/memory/network, top 10 by default; hovering the CPU column header explains its scale). Hand-written SVG, no chart library.
nvidia-smi), the process table's "network" column (always present on macOS
via nettop; Linux only with root-attributed ss -tinp; Windows needs ETW and stays hidden) —
missing capability means hidden, no wall of "—", see
Capability-gated probes. On Windows the disk card lists every
volume by drive letter (C:\, Z:\, …); on POSIX it is still / plus data volumes under
/Volumes, /media, /mnt. Process CPU% is per-core (multi-core processes can exceed
100%) and a process-lifetime average (macOS/Linux from ps; Windows computed from
Get-Process's cumulative CPU seconds over process uptime); the column header tooltip
explains this.GET /pc-manager/stream (text/event-stream) is the shared foundation — one host-side timer
(interval dashboardPollMs, adjustable, min 500) collects on a single path and diffs
server-side (network rates available from the first frame); the dashboard, floating window and
every other consumer share one connection and one frame stream; with no consumers, the
connection and collection stop automatically (reference-counted; hidden tab / closed
floating window releases it). GET /pc-manager/status (?processSort=&processLimit=) remains
as a cache fallback: requests with the pump's sort order (default CPU) read the fresh cache
directly, other sort orders collect on demand. The LLM tool pc_status does not go through
the pump; its contract is unchanged.AMD Radeon(TM) Vega 8 Graphics; the narrow column truncates
with an ellipsis, full name in the tooltip); platforms without a name source keep the generic
GPU.A dedicated "Junk Cleanup" window in the right sidebar (guide entry peer-level with System
Monitor, order 31), zero LLM end to end: click "Scan" → host GET /pc-manager/junk/scan
(read-only; the server filters out small items below a 1 MiB threshold) → presented by
category (the checkbox unit is the category, not single items; an inline size preview with the
top 2–3 item names answers "which apps does this affect"; each category's "details (N)" folds out
into a scrollable list for surgical add/remove; recommended categories are pre-checked —
non-regenerable Xcode archives/iOS backups and safeToClean:false pnpm store/simulator leftovers
are unchecked by default with a stated reason) → "Clean selected" → an in-place two-step
confirmation bar (N items · size → Trash (recoverable) / permanent delete) →
POST /pc-manager/junk/clean executes directly → an in-place result briefing. The host side
shares the same config gate and id validation chain as the tool path (all-or-nothing rejection,
zero deletion) and emits an audit log line; the confirmation bar cannot be skipped and stands in
for the tool path's approval panel. The floating window's "Scan" button invokes this window and
starts scanning automatically (openTab + navigation revision; re-invoking re-scans); the five
tools and all security defenses are unchanged.
Note: the conversational approval panel only appears when the session Access mode's approval
policy is ask; under the danger-full-access deployment default (approval: never), in-session
ask is silently auto-rejected — switch Access mode in the session or adjust the deployment
preset. The UI direct-execution window is not subject to this.
disabled_by_config and only act after the host flips the switches in a back-layer patch.moveToTrash: false. The trash landing has three tiers: on macOS /usr/bin/trash (invoked by
absolute path, guarding against PATH hijacking) → ownership-checked rename into ~/.Trash
(name conflicts get 2/ 3 suffixes) → cross-volume EXDEV cp+rm; on Linux
trash-put/gio trash (probed absolute paths) → freedesktop
~/.local/share/Trash/{files,info} rename + .trashinfo restore records (name conflicts
suffix both sides in sync) → cross-volume cp+rm; on Windows the
Microsoft.VisualBasic.FileIO.FileSystem SendToRecycleBin (the same shell recycle as
Explorer, landing in the real Recycle Bin with the original location recorded, restorable
from Explorer; paths are embedded into the script as PowerShell literals rather than
appended to argv — -Command re-joins arguments with spaces, so a path containing one silently
splits, and a multi-statement script never sees $args at all) → fallback to
%LOCALAPPDATA%\pc-manager\trash (a private holding area inside the profile, still recoverable,
just not in the Recycle Bin UI) → cross-volume cp+rm. After tier-1 reports success, the source
is checked to have actually disappeared, otherwise the tier counts as failed and the next
one runs — a silent no-op (the shell API blocked by policy, arguments never bound) must never be
reported as "reclaimed N bytes" (the Windows e2e caught exactly one such fake success). The
trash kind itself is emptied in place (macOS empties ~/.Trash, Linux empties the contents of
files/+info/, Windows empties this account's %SystemDrive%\$Recycle.Bin\<SID> — exactly
what "Empty Recycle Bin" means for that volume). Moving the Trash back into the Trash would be a
pointless matryoshka.ask_user_question) + the framework's tools/pre-execute
approval gate (askBeforeJunkClean defaults to true, asked every time, fail-closed when no
answerer) + the id structural validation chain (strict child paths for children kinds /
exact root for whole kinds, dual-sided realpath against symlink redirection, blocked list,
safeToClean; any failure rejects the whole call with zero deletion).%VAR% roots), including a sensitive-cache protection
list (the "caches" of password managers/IDEs/input methods/VPNs/sync drives/AI apps are actually
non-regenerable state; hits are recorded as skipped and produce no item) and EDR/active-service
prefix protection (deleting enterprise security agent caches triggers tamper alarms; Linux
additionally protects systemd-private-* and snap-private-tmp; Windows protects AV/EDR
directories and installer scaffolding); safeToClean:false entries are reported but never
cleaned, with suggested commands in the rationale (xcrun simctl delete unavailable /
pnpm store prune).C:\Users\t\AppData\Local\Temp\x and /home/t/.cache/x.src/monitor.ts, junk.ts, apps.ts, win32.ts) do not depend on cordis;
pure logic, independently unit-testable.All probes dispatch per platform and degrade to null/empty + console.warn on failure — a
probe failure never takes down the snapshot. Linux base metrics are all read from /proc and
/sys, zero dependencies and zero privilege escalation; capability-detection enhancements
(per-process network attribution, temperature, per-process GPU) follow "enable only when the
capability is detected; missing capability means silent null, no warn spam" — see the table and
Capability-gated probes.
| Group | Metric | macOS source | Linux source | Parser |
|---|---|---|---|---|
| CPU | Utilization % | os.cpus() two samples, 250ms delta | same (sampling window ≥1s, see disk I/O) | cpuUsagePercent |
| CPU | Model/cores/load 1/5/15 | node:os | node:os | — |
| CPU | Package temperature °C | null (powermetrics needs sudo, not run on the user's behalf) | /sys/class/hwmon (name + temp*_input millidegrees) falling back to /sys/class/thermal (type + temp); max across CPU-family chips; cloud hosts often lack sensors → null | pickCpuTempCelsius / isCpuTempSource |
| GPU | Utilization % (best effort) | Device Utilization % from ioreg -r -d 1 -c IOAccelerator, max across GPUs | nvidia-smi --query-gpu=utilization.gpu (presence probed once and cached; null without NVIDIA hardware, whole card hidden in the UI) | parseIoregGpu / parseNvidiaSmiGpu |
| Memory | macOS: used = active+wired+compressed (fallback total−free); Linux: used = MemTotal−MemAvailable (fallback total−free−buffers−cached) | vm_stat (page size parsed from the header) | /proc/meminfo (app≈AnonPages, wired≈SUnreclaim, cached=Buffers+Cached+SReclaimable) | parseVmStat / parseMeminfo |
| Memory | Swap total/used | sysctl -n vm.swapusage | SwapTotal/SwapFree from /proc/meminfo | parseSwapUsage / parseMeminfo |
| Disk | Per-volume usage | df -k | df -k (parser tolerates both column layouts; filters pseudo-filesystems like tmpfs/udev/overlay/squashfs and /dev /proc /sys /run /snap mount points) | parseDf |
| Disk | I/O throughput (read+write combined) | iostat -d -c 2, last sample summed | /proc/diskstats two-sample delta (whole physical disks sd/nvme/vd/hd/mmcblk, excluding partitions and loop/dm; sampling window stretched to 1s) | parseIostat / parseDiskstats + diskstatRate |
| Battery | Charge/charging/time remaining/cycle count/health | pmset -g batt + ioreg -rn AppleSmartBattery | /sys/class/power_supply/BAT*/uevent (AC from A*/online; card hidden when no battery) | parsePmsetBatt / parseIoregBattery / parseBatteryUevent |
| Network | Per-interface cumulative rx/tx (rates derived from call-to-call deltas) | netstat -ib (excludes lo*, dedupes <Link#> rows) | /proc/net/dev (excludes lo) | parseNetstatIb / parseProcNetDev |
| Network | Local IPv4 | node:os networkInterfaces() (non-internal, excluding 169.254.* link-local; identical across all three platforms, works on win32 too) | same | pickLocalAddresses |
| Network | Public IP and geo (optional) | ipwho.is HTTPS lookup (enableIpGeoLookup on by default; successes cached for ipGeoRefreshMinutes (default 30min, min 5), failures negatively cached 60s; the endpoint can be swapped for a compatible mirror via ipGeoEndpoint) | same | parseIpWhoIs / createIpGeoLookup |
| Process network | Dashboard/SSE frames: live rates; the pc_status tool: cumulative values — both scales stand on their own | nettop cumulative + pump delta | as root ss -tinp socket attribution (TCP only: sum of bytes_received/bytes_sent over currently open sockets; without root the whole column is null and hidden) + pump delta | parseNettop / parseSsTinp / diffProcessRates |
| Process GPU | SM utilization % | null (no source) | nvidia-smi pmon -c 1 attributed by pid (max across GPUs; column hidden without the binary or without occupying processes) | parseNvidiaSmiPmon / mergeGpuPercent |
| System | OS version | sw_vers -productVersion | /etc/os-release PRETTY_NAME (e.g. Debian GNU/Linux 12 (bookworm)) | parseSwVers / parseOsRelease |
| Processes | CPU%/memory%(+rss)/network cumulative/GPU%; disk column reserved, always null | ps -Ao pid,pcpu,pmem,rss,comm + nettop (CSV/JSON both supported) | ps -Ao pid,pcpu,pmem,rss,args (Linux comm truncates at 15 chars, hence args) + ss -tinp (root) + pmon | parsePs / parseNettop / parseSsTinp / mergeProcesses / mergeGpuPercent / sortProcesses |
Windows has neither /proc nor df/ps, so the strategy is node builtins carry the hot
fields + one batched PowerShell probe for everything else: a single
powershell.exe -NoProfile -NonInteractive -NoLogo -Command process fetches everything in one
round (volumes, memory split, per-NIC counters, disk throughput, GPU, battery, ACPI thermal
zones, the process table). One interpreter per round, because startup dominates (~2.3 s cold
start on a host with endpoint security; each extra WMI query adds only 30–700 ms) — and a second
concurrent interpreter would show up as a row in the very process table it is collecting: the
script reports its own $PID and the parser drops that row.
| Group | Metric | Windows source | Parser |
|---|---|---|---|
| CPU | Utilization/model/cores | node:os (os.cpus() delta) | cpuUsagePercent |
| CPU | Load 1/5/15 | null (Windows has no load average; os.loadavg() always returns 0, which is "no such number", not "idle" — the dashboard hides the line) | — |
| CPU | Package temperature °C | root\WMI MSAcpi_ThermalZoneTemperature (0.1 K → °C, max across repeated readings; usually needs elevation and desktops often lack the class → null, capability-gated) | parseWindowsBundle |
| GPU | Utilization % + adapter name | Win32_PerfFormattedData_GPUPerformanceCounters_GPUEngine — vendor-agnostic engine counters (AMD/Intel/NVIDIA alike, unlike nvidia-smi which serves one vendor); the whole-card scale takes the busiest engine (its per-process sum, clamped to 100, the Task Manager scale). The adapter name comes separately from Win32_VideoController.Name (e.g. AMD Radeon(TM) Vega 8 Graphics, the dashboard card title). When the counter class is missing, falls back to nvidia-smi --query-gpu (presence probed once and cached) | parseWindowsGpuEngines |
| GPU | Per-process utilization % | The same engine counters attributed by pid_<pid>_…_engtype_<type> (that process's engine instances summed, clamped to 100 = the Task Manager "Processes" column); falls back to nvidia-smi pmon when the class is missing | parseWindowsGpuEngines / mergeGpuPercent |
| Memory | total/used | node builtins os.totalmem()/os.freemem() (the latter is GlobalMemoryStatusEx's "available", including standby — equivalent to MemAvailable) | — |
| Memory | app/wired/cached/swap breakdown | Win32_PerfFormattedData_PerfOS_Memory (AvailableBytes/StandbyCacheNormalPriorityBytes/PoolNonpagedBytes/CommittedBytes) + Win32_PageFileUsage (the page file as swap); no counters exist for compressed/purgeable → null | parseWindowsBundle |
| Disk | Per-volume usage | Win32_LogicalDisk (DriveType 2/3/4, Size/FreeSpace/FileSystem; mount is the drive root C:\) | parseWindowsVolumes |
| Disk | I/O throughput | Win32_PerfFormattedData_PerfDisk_PhysicalDisk's _Total DiskBytesPersec (no waiting — cheaper than Get-Counter, which would cost an extra 1 s sample) | parseWindowsBundle |
| Network | Per-interface cumulative rx/tx | Win32_PerfRawData_Tcpip_NetworkInterface (the …Persec suffix is a perf-counter naming convention; the values are cumulative since boot; excludes Loopback/isatap/Teredo/Pseudo; duplicate instances keep their _2 suffix) | parseWindowsNetwork |
| Battery | Charge/charging/power source/time remaining | Win32_Battery (BatteryStatus 2/6/7/8/9/11 = on AC, 6–9/11 = charging; powerSource reuses the macOS AC Power/Battery Power literals; the EstimatedRunTime sentinel 71582788 → null); cycle count/health need vendor WMI or powercfg /batteryreport → null | parseWindowsBattery |
| System | OS version | os.version() + os.release() (e.g. `Windows 11 Pro for Workstations 10.0.26300 |