dsh-at-file
FSMargoo/dsh-at-fileCodex 风格的 `@file` 文件引用,输入框里直接搜索并引用工作区文件。
English | 中文
A hot-pluggable DeepSeek Harness (DSH) client GUI plugin: it adds a task board entry below "新会话" (New session) in the sidebar; clicking it switches the middle column entirely to a multi-column kanban view. Tasks execute for real through DSH's own session mechanism (session.prompt), and execution status is written back to the card in real time.
dsh-web-ui/packages/skins/skin-center).[data-pane="sidebar"] on older shells, [class*="sidebarCol"] on the DSH 0.1.0-rc.6 AppFrame layout) below the new-session button (wide rail shows icon + text, collapsed rail shows a bare icon, adapting to DSH skin tokens).workspaces.connectWorkspace, reusing a blank session or letting the host create one), names the session after the task title, and drives a real agent via session.prompt([{ type: 'text', text }], 'queue'); it then subscribes to that session's snapshot and, once the round really finishes, sets the card to 已完成/已失败 and records the execution result. The execution session appears in the session list and can be opened to view the real transcript.* / */n / a-b / comma lists) + common presets (daily 09:00, every hour, every 10 minutes, Mondays 09:00); enabling computes and persists the "下次运行时间" (next run time), and the card shows a scheduled marker; at the due time it automatically takes the same real-execution path (as manual run), and the execution session remains linkable.src/index.ts) registers a plugin:task-board section (order 200) via SystemPrompt.section, declaring this plugin's existence, capabilities, and limits to every agent — it is injected when the plugin is in the composition (after mount + DSH restart) and disappears when removed (after unmount + restart), so an agent needs no external docs to know how to work with this board.package.json / tsconfig.json / tsdown.config.ts # standalone repo build
build/tsdown.client.ts + build/web/src/platform.ts # client bundle preset copied from the DSH checkout (kept in sync with the running version)
src/index.ts / src/invariant.ts # host half: only injects SystemPrompt section (no other behavior)
src/client/index.ts # apply(ctx): wires runtime services + mounts DOM
src/client/sidebar-entry.ts # sidebar entry injection (self-healing MutationObserver)
src/client/board-mount.tsx # middle-column board mount + show/hide toggle
src/client/board/*.tsx # React board views (columns/cards/details/new/confirm)
src/client/board.module.css # styles (--dsw-* tokens, adapting to theme/skin)
src/core/tasks.ts # task model + state machine (pure functions)
src/core/schedule.ts # cron parsing + next-run time (pure functions)
src/core/scheduler.ts # browser scheduler (ticks every minute to fire due tasks)
src/core/store.ts # persistence (TaskStore interface + localStorage impl)
src/core/execution.ts # real execution service (session connect/prompt/settlement watch)
src/core/controller.ts # controller (ledger state, view state, navigation awareness)
tests/*.spec.ts # automated tests: storage/state transitions/execution trigger/cron/scheduling
scripts/dsh-task-board.js # one-click mount/unmount/status CLI
sidebar.workspaces / sidebar.settings, both already taken by ui-workspace / ui-settings; an external plugin cannot register a new slot (declaring means claiming, and duplicating throws). So the entry goes through the skin-precedent DOM injection, self-healed with a MutationObserver (when a React re-render touches the node it re-inserts within the same frame, no flicker).conversation slot is single and already taken by ui-conversation. The board view mounts on the center column ([data-pane="conversation"] on older shells, [class*="centerCol"] on the DSH 0.1.0-rc.6 AppFrame layout) as a tail child node (outside React's ownership), toggled via the <html data-dsh-taskboard-active> attribute, keeping the chat subtree below mounted and stateful.cordis.patch.yml); localStorage is also how DSH's own client snapshot store (createSnapshotStore persist) persists.ctx.sessions.list subscribes to session state (running / byId), ctx.workspaces.connectWorkspace() creates/reuses a session, session.prompt() drives a real agent, and ctx.sessions.open() jumps to the transcript.LocalStorageTaskStore.subscribeExternal), so a task deleted in one tab can never keep firing from another tab's stale in-memory copy — nor be written back by that copy's later persistence (schedule roll-forward, execution settlement).Install the family aggregate package @linxin666/dsh-web-ui-all (all plugins and skins in one) or this plugin alone:
### 从 npm 安装(推荐)
dsh plugin --profile web add @linxin666/dsh-client-ui-task-board
### 从仓库安装(开发调试)
git clone https://github.com/zhu1090093659/dsh-web-ui.git
cd dsh-web-ui
pnpm install && pnpm -r build
dsh plugin --profile web add link:$(pwd)/packages/dsh-task-board
After installing, restart dsh web — a "任务看板" (task board) entry appears below "新会话" (New session) in the sidebar; a page refresh is not enough, the process must restart.
Prerequisites: Node ≥ 20 with the official NPM SDK reachable (configure the NPM_TOKEN env var + project .npmrc if still using private-scope auth; see the repo docs/plugins.md). Types and runtime APIs all come from the official NPM SDK (@deepseek-ai/* devDependencies); no DSH source checkout is required.
cd ~/code/dsh-web-ui/packages/dsh-task-board
pnpm install # first time (run pnpm install at the workspace root)
pnpm run build # produces lib/index.js + lib/client.js (tsdown + shared/tsdown.client.ts preset)
pnpm run typecheck # type check (SDK package types from node_modules)
pnpm test # vitest: storage read/write / state transitions / execution trigger
This plugin uses the official profile-bundle shape (package.json declares dsh.bundle.patch + dsh.client, see cordis.patch.yml). Mounting = registering the dependency and bundle rows in the web profile manifest (~/.dsh/profiles/web/package.json) and installing:
# Mount (registers dependencies + dsh.profile.bundles, pnpm install; takes effect after restarting the GUI)
node scripts/dsh-task-board.js mount
# View status
node scripts/dsh-task-board.js status
# Unmount (removes the registered rows; restores the original GUI after restart; task data is kept)
node scripts/dsh-task-board.js unmount
The rows registered in the profile manifest:
{
"dependencies": { "@linxin666/dsh-client-ui-task-board": "link:/Users/zcl/code/dsh-web-ui/packages/dsh-task-board" },
"dsh": { "profile": { "bundles": [ "...", "@linxin666/dsh-client-ui-task-board" ] } }
}
Note: the profile layer (bundle rows,
dsh.clientmetadata) is read when the dsh web process starts, so a restart of the dsh web GUI is required after mount/unmount (a page refresh is not enough).
dsh.taskBoard.v1 (origin http://127.0.0.1:<dsh web port>; same origin persists across refresh/restart).localStorage.removeItem("dsh.taskBoard.v1") in the browser console.TaskStore interface (src/core/store.ts); it can later be swapped for IndexedDB or a host file channel without touching the upper logic.npm run build → node scripts/dsh-task-board.js mount → refresh http://127.0.0.1:3080.*/10 * * * *); a scheduled marker appears on the card; wait for the next whole 10-minute mark, watch the card automatically enter 进行中 (in progress) and eventually complete, with "上次触发" (last trigger) showing a time and a new execution-log row (the session is linkable).