dsh-client-ui-task-board
Verified@linxin666/dsh-client-ui-task-board · v0.3.5 · Apache-2.0 · Web UI
Host-authoritative task board for the DSH Web GUI with real session execution, Host cron scheduling, and optional cross-platform idle-sleep protection; mounted without DSH source changes.
Install
dsh plugin add @linxin666/dsh-client-ui-task-board Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Readme
dsh-task-board — DSH web GUI task board plugin
English | 中文
A hot-pluggable DeepSeek Harness (DSH) Web GUI plugin with a Host-authoritative task ledger, real DSH session execution, Host cron scheduling, and optional cross-platform idle-sleep protection. It is mounted through cordis.patch.yml and the profile mechanism and does not modify DSH source code.
- The browser is an asynchronous view; closing the page does not stop Host scheduling or execution settlement.
- Every run creates a separate DSH session and applies pinned workspace, agent preset, and permission before sending the task prompt.
- The display may turn off while optional power protection keeps the computer from entering idle system sleep.
Features
- Task board UI: a sidebar entry below New Session shows icon and text in the wide sidebar and an icon in the collapsed rail; the board provides five kanban columns, search, task details, archive/restore, execution history, and links to execution transcripts. Archived tasks are read-only except for restore, delete, and transcript viewing, and cannot run manually or on schedule until restored.
- Host-authoritative ledger: tasks, schedules, and execution records live in
$DSH_HOME/task-board/ledger-v2.json; browser actions become confirmed Host transactions. - Bounded execution history: each task keeps the most recent 20 execution records; the oldest runs are trimmed when a new run starts, so ledger size and write cost stay bounded regardless of how often a task has run.
- Real execution: manual and scheduled runs use the same Host runner, create a fresh session, rename it, apply the agent preset and
/permission <id>, then queue the task prompt. - Fail-closed pins: a missing workspace, missing or broken preset, or rejected permission command fails before the task prompt is sent.
- Host scheduler: 5-field cron supports
*,*/n, ranges, comma lists, Sunday0/7, and standard day-of-month/day-of-week OR semantics in the Host local time zone. - Deterministic recovery: a running execution with a recorded session is observed after restart; an interrupted start without a session id is cancelled and is not resent.
- Live synchronization: mutations return a full revisioned snapshot; SSE announces revision, scheduler, and power changes, while reconnect and page visibility recovery fetch a full snapshot.
- Optional idle-sleep protection: off by default; when enabled it covers every running DSH session, enabled non-archived task-board schedules, and unknown session state.
- System-prompt injection: the Host registers a
plugin:task-boardsection (order 200) throughSystemPrompt.section, and the task-board settings can disable the announcement without disabling the board. The guidance also reminds agents to close any visibletodo_writeplan before the final answer.
Architecture and protocol
src/index.tsmounts the Host service through the official@deepseek-ai/dsh-host-apiproxyand@deepseek-ai/dsh-host-webserverSDKs.src/host-ledger.tsserializes actions and persists{ schemaVersion: 2, revision, tasks, scheduler, recentRequests }through a temporary file plus atomic rename.src/host-service.tsowns cron ticks, missed-trigger skipping, runner launch, restart reconciliation, and power reasons.src/client/host-api.tsimports legacy browser data once, submits idempotent actions, and treats Host snapshots as the only confirmed UI state.- Same-origin endpoints are
GET /api/task-board/state,GET /api/task-board/events, andPOST /api/task-board/action. - Every endpoint requires a browser same-origin marker. Direct access is restricted to the DSH loopback origin; an authenticated same-host reverse proxy must use an explicit Host allowlist and a server-injected token. POST requests additionally require JSON. Ordinary actions are limited to 64 KiB and import to 2 MiB. The action union has no command, executable path, shell text, or arbitrary argument field.
Install
Install the aggregate package or this package alone, then restart dsh web:
dsh plugin --profile web add @linxin666/dsh-client-ui-task-board@latest
For local development:
git clone https://github.com/zhu1090093659/dsh-web.git
cd dsh-web
pnpm install
pnpm build
dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board
Configuration
| Key | Default | Behavior |
|---|---|---|
enabled |
true |
Enables the Host service and browser board. |
announceToAgent |
false |
Opt-in: when true, adds the task-board guidance section to agent system prompts. |
preventIdleSleep |
false |
Holds one system idle-sleep assertion while any DSH session runs, any schedule is enabled, or session state is unknown. |
trustedProxyHosts |
[] |
Canonical host[:port] authorities accepted only through the authenticated loopback reverse-proxy path. |
proxyTokenEnv |
DSH_TASK_BOARD_PROXY_TOKEN |
Environment variable containing the reverse-proxy token; the token itself is never stored in plugin config. |
Direct browser access remains limited to the DSH loopback origin. For a same-host authenticated reverse proxy, bind DSH Web to loopback, set trustedProxyHosts, place a high-entropy token in the environment variable selected by proxyTokenEnv, and configure the proxy to replace (not forward from the client) X-Dsh-Task-Board-Proxy-Token after it authenticates the request. The proxy Host must be allowlisted, and the browser Origin must have that same authority. Restart the Host after changing these composition-level proxy settings.
On macOS the backend starts /usr/bin/caffeinate -i -w <host-pid> and never requests -d. On Windows it starts the absolute Windows PowerShell under SystemRoot with a fixed helper that requests only ES_CONTINUOUS | ES_SYSTEM_REQUIRED; it never requests ES_DISPLAY_REQUIRED, changes a power plan, or requires administrator privileges. On Linux it starts a systemd-logind idle block inhibitor only from /usr/bin/systemd-inhibit or /bin/systemd-inhibit; it does not request sleep, handle-lid-switch, or a display/screensaver inhibitor. A Linux host without systemd-logind reports unsupported or a visible error and does not start a desktop-specific fallback. Other platforms report unsupported.
Data storage and migration
- The v2 ledger is
$DSH_HOME/task-board/ledger-v2.json. New POSIX files use mode0600; Windows inherits the user directory ACL. - A corrupt v2 file is moved to a collision-resistant
ledger-v2.json.corrupt-*name and the Host starts with an empty ledger plus a visible scheduler error. The corrupt bytes are not overwritten. - On the first upgraded page load for an origin,
dsh.taskBoard.v1is imported by stable source and request ids. Tasks merge by id, strictly newer browser top-level fields win, equal timestamps keep Host fields, and execution records merge by execution id. - The most recent 256 request ids and SHA-256 action fingerprints are stored with the ledger, so a retried mutation remains idempotent after a Host restart without duplicating full action payloads.
- The import marker
dsh.taskBoard.v2.hostImportedstores the confirmed Host ledger generation only after import succeeds. A new or recovered ledger generation is offered the retained v1 data again. The v1 localStorage value remains untouched as a read-only rollback copy. - One Host process owns a task-board ledger directory at a time through
$DSH_HOME/task-board/ledger-v2.lock; a second Host using the same DSH home fails closed instead of concurrently writing the ledger.
Security model
- The plugin stays inside the existing DSH Web deployment and network boundary and emits no permissive CORS headers. State, action, and SSE routes share the same access fence; bare local command-line requests are not accepted as browser requests.
- All mutation payloads use a strict, versioned discriminated union; schedule-owned timestamps and execution outcomes cannot be written by the browser.
- Workspace, preset, permission, cron, task status, and imported records are validated again on the Host.
- A task prompt is data sent to a DSH agent session. The protocol does not accept shell commands, PowerShell bodies, executable paths, or configurable helper arguments.
- Power helpers use fixed executable paths, fixed arguments,
shell: false, and bounded retry delays of 1, 2, 5, 10, then 30 seconds. The Linux helper follows the Host stdin lifetime so the systemd inhibitor is released automatically after an abnormal Host exit.
Build and test
Node 20 or newer and the official NPM SDK packages are required; no DSH source checkout is used.
pnpm --filter @linxin666/dsh-client-ui-task-board typecheck
pnpm --filter @linxin666/dsh-client-ui-task-board test
pnpm --filter @linxin666/dsh-client-ui-task-board build
Set DSH_POWER_SMOKE=1 to opt into the native helper smoke test on Windows, macOS, or Linux. It starts the fixed helper, waits for readiness, releases it in cleanup, and confirms process exit without changing the system power plan. Linux first probes systemd-logind with a bounded timeout; without a usable system bus the native portion is skipped while pure logic tests remain available.
Manual verification
- Mount the package, restart
dsh web, open the task board, and confirm the Host time zone and power status are visible. - Create and edit a task; refresh or open a second same-origin tab and confirm both show the same Host revision.
- Run a task with pinned workspace, preset, and permission; confirm a new session appears and the task settles from its
turn/endhistory. - Enable a near-future cron, close all browser pages, and confirm the Host still creates and settles exactly one execution.
- Stop the Host past a cron occurrence, restart it, and confirm the missed occurrence is skipped and
nextRunAtrolls forward from current Host time. - Enable
preventIdleSleep, run a long session, and let the display turn off; after restoring the display, confirm the session continued and the execution settled. - Disable the setting and all schedules, stop DSH, and confirm the helper exits; on macOS,
pmset -g assertionsshould show no display-sleep assertion from this plugin. - On Linux, use
systemd-inhibit --listto confirm that only anidle/blockentry exists; the display should still follow desktop settings, while manual sleep and lid close remain under system policy.
Known limitations
- Missed occurrences during Host downtime, system sleep, or a long pause are skipped and never queued for catch-up.
- A task that is already running skips its due occurrence and rolls to the next cron match; task runs never overlap or queue.
- DST follows the Host local wall clock: a nonexistent spring-forward minute is skipped, and a repeated fall-back minute is not replayed a second time.
- Power protection prevents only idle system sleep. It deliberately allows display sleep and lock.
- Lid close, manual sleep, hibernation, shutdown, low-battery forced sleep, and enterprise power policy are outside the guarantee.
- The plugin does not schedule wake timers and cannot wake a computer that is already asleep.
- Linux requires systemd-logind and policy permission for the current user to acquire an idle block lock. Containers, WSL, hosts without a system bus, and non-systemd systems may report
unsupportedorerror. Whether a desktop also associates a logind idle lock with display idleness is desktop policy; the plugin does not request a screensaver or display inhibitor. - Keeping enabled schedules armed may increase battery consumption because protection starts before their future trigger time.
- Host execution consumes the same API quota as an ordinary DSH agent session.
Telemetry
The browser half sends one anonymous install heartbeat per UTC day to dsh-market.com: a random localStorage id plus this package's name, nothing else. The server stores only a salted hash of that id, never IP addresses, and exposes aggregate counts only. See docs/telemetry.md for the full contract.