packages/dsh-task-board
zhu1090093659/dsh-web侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
在 DSH Web GUI 聊天输入框左侧添加可自定义的 switch 快捷按钮,一键插入斜杠命令。
DSH plugin that adds customizable switch buttons to the left of the chat composer in the DSH Web GUI. Clicking a button inserts a slash command into the input box; you then fill in any arguments and send manually.
Buttons have a single behavior (mode: "insert", one-shot insertion): each click inserts /command before the existing content of the input box (at the beginning when it is empty); click again to insert again. Nothing is executed on the host, there is no persistent state, and buttons are rendered as plain blue text labels (no icon).
dsh-switch entry config of the active Host profile (cordis.patch.yml), survives browser cache clears; localStorage is only a first-paint cachetoggle (switch command) and persistent (sticky insertion) button types were removed; existing buttons are downgraded to one-shot insertion on read (the command is kept, commandOff/projection are dropped), so they never disappearsettings.yaml (DSH Desktop 1.x / harness layouts), the old dsh-switch: section is imported into the entry config once (guarded by a volatile legacyMigrated flag), so your buttons survive the host upgrade⚠️ Do not run
dsh plugin addon a profile that already contains git dependencies (e.g.aegis): the CLI forwards to pnpm, whose git resolution can hang silently forever in that situation. Use path ② below — it needs no pnpm and no network.
When dsh-switch is available on the npm registry, install/update it from the DSH Desktop plugin marketplace or dsh plugin --profile <profile> add dsh-switch — this uses the host's generation mechanism and keeps automatic updates working.
git clone https://github.com/weimingyu9312/DSHSwitch.git
cd DSHSwitch
node install.mjs # auto-detects DSH_HOME and the web profile
node install.mjs --profile desktop # DSH Desktop 2.x uses the "desktop" profile
node install.mjs --profile web --home <DSH_HOME> --dry-run # preview only
The script locates DSH_HOME (env var wins; else ~/.dsh / %APPDATA%\dsh-desktop\harness / the other standard locations), copies the package body (incl. vendored deps) into <profile>/node_modules/dsh-switch/, and registers it in both required places of the profile manifest (dependencies["dsh-switch"] and dsh.profile.bundles) as UTF-8 without BOM. On a desktop profile it also removes dsh-switch from dsh.desktop.desktopDeselectedBundles so the plugin is actually enabled. It is idempotent and never touches dsh.desktop.generationProjection (that layer belongs to the marketplace staging). Restart DSH Desktop afterwards for the host half to load.
Copy the repository (with its node_modules/ vendor tree) to <DSH_HOME>/profiles/<profile>/node_modules/dsh-switch/, then edit <DSH_HOME>/profiles/<profile>/package.json: add "dsh-switch": "file:./node_modules/dsh-switch" under dependencies, append "dsh-switch" to the dsh.profile.bundles array, and remove it from dsh.desktop.desktopDeselectedBundles if present. Save as UTF-8 without BOM (PowerShell's Set-Content -Encoding UTF8 adds a BOM and breaks the host parser — use [IO.File]::WriteAllText or an editor without BOM). Both registrations are required; missing either makes the host reconcile skip the bundle. Restart DSH Desktop.
The Host runtime dependency @deepseek-ai/schemastery (plus its transitive deps @deepseek-ai/cosmokit and the type-only @standard-schema/spec) is both vendored in the git repository under node_modules/ (a fresh clone works out of the box) and declared as regular npm dependencies (exact-pinned, so a registry npm install dsh-switch carries them automatically). The pinned versions must match the target host release (currently schemastery 3.18.4 / cosmokit 1.8.5 = DSH Desktop 2.0.17 / dsh 0.2.0-rc.2); when bumping them, refresh all three packages together. For junction/git installs the plugin resolves dependencies through the vendored tree — keep it, and don't replace it with symlinks, which create a reparse chain that breaks startup (full reasoning in the header comment of lib/index.js).
Registry installs are now self-sufficient: npm forces every
node_modules/path out of published tarballs, so the registry copy carries no vendored tree — but since v1.7.0 the three packages are declared as dependencies, sonpm install dsh-switchfetches exactly the pinned versions into the profile'snode_modulesand Node lookup resolves them. The one-shot installer detects this layout and skips vendoring.
| Contract | Verified against |
|---|---|
Host half registers the dsh-switch Config (volatile buttons, settings.configure({auto:false})), client half reads/writes it via the configForms service; slots conversation.input.left + settings.section; composer face inputActions.setDraft + standard hook useInput | DSH Desktop 2.0.17 (dsh 0.2.0-rc.2, desktop profile) |
Degraded mode: slot delivers no inputActions / no useInput (older or unusual host) | falls back to the execCommand('insertText') DOM channel and the legacy input prop — reduced reliability, still functional |
Minimum host: any dsh-web-app build that provides the inputActions face (0.8.x line) and — for v1.8.0's config service — the configForms client service (dsh 0.1.7-rc.1+). Older builds receive no config (localStorage cache only); the composer buttons still render.
/plan off){
"buttons": [
{ "id": "plan", "label": "Plan", "command": "/plan", "mode": "insert", "enabled": true },
{ "id": "plan-off", "label": "Plan Off", "command": "/plan off", "mode": "insert", "enabled": true }
]
}
Defaults apply only while the Host config has no buttons key yet; once you change anything, the stored value wins. To restore defaults, remove the - id: dsh-switch entry's config from the profile's cordis.patch.yml, clear the browser's localStorage, and restart.
| Field | Type | Description |
|---|---|---|
id | string | Unique button identifier |
label | string | Button display text |
command | string | Slash command to insert |
mode | string | Fixed insert; legacy values (toggle/persistent/missing) are migrated automatically on read |
enabled | boolean | Whether the button is enabled |
/plan appears at the start; with existing content (e.g. research pricing) → it becomes /plan research pricing; Enter sends normallyAsk the plugin itself what it sees, from the browser console (F12):
__dshSwitch.state() // scopeBound / buttons / faces / draft / editorFound / editorPhase
__dshSwitch.log() // last 40 insertions: kind(setDraft|dom|no-editor|skip:stale-snapshot|…)/text/before/after
__dshSwitch.insert("/plan") // run one insertion manually, check whether the draft gets the command
Common symptoms: faces: [] = the host slot did not deliver inputActions (older host version); insertion falls back to the DOM channel with reduced reliability. editorFound: false = the composer DOM changed and neither channel can write. Enable verbose logging with localStorage.setItem("dsh-switch-debug","1").
| File | Responsibility |
|---|---|
lib/index.js | Host half: exports the dsh-switch Config schema (volatile buttons), opts out of the auto-generated settings form, and imports the legacy settings.yaml dsh-switch: section once |
lib/client.js | Client half: button bar (injected into the conversation.input.left slot), settings panel (injected into the settings.section slot), config read/write via the configForms service and migration |
The sole insertion behavior is a draft write (never command execution): the preferred path is the host composer face's inputActions.setDraft(command + existing draft) — committed through the editor's own state, so it cannot be wiped by Lexical reconciliation. When the draft contains citation chips, the commit machine is locked, or the session has no face, it falls back to execCommand('insertText'). Config changes render optimistically, mirror to localStorage, and persist via the entry config (configForms.set("buttons", …) / Host settings.mutate).
This repository is linked into the profile via a junction, so editing workspace files takes effect directly. The plugin ships as two halves with asymmetric hot-reload — know which one you touched before judging a change "not applied":
| Changed file(s) | Action required |
|---|---|
lib/client.js (button bar, settings panel, insertion logic) | refresh the browser page only |
lib/index.js, package.json, cordis.patch.yml (host half) | fully restart the host process (dsh web: Ctrl+C and rerun / quit DSH Desktop completely) |
| Button configuration (via the settings panel) | nothing — saved to the profile entry config and applied live |
node test/client.test.cjs # or npm test — 33 jsdom behavior tests (migration rules, face routing, chip/phase gates, retired-symbol regressions)
node test/preview.cjs # offline-render the settings panel to .preview/preview-{dark,light}.html for layout review
node install.mjs --dry-run # verify the installer against a fake DSH_HOME before shipping
See CHANGELOG.md for release history.
MIT