deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:CJYLZS/dsh-remote-development
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
English | 中文
This plugin adds lightweight remote development to DeepSeek Harness: you register an SSH machine, pick a remote directory as the session's workspace, and the agent then works on that remote workspace with the SAME tools it uses locally — file tools, shell, and search. The plugin does not add any model-facing tool and no third-party UI plugin: it replaces the filesystem, subprocess, and bash providers with routing versions that translate local tool calls into remote execution over SSH, and it contributes one settings section plus one workspace directory-flow dialog in the Web GUI.
From GitHub (recommended) — the built lib/ is committed, so it is one command with no build step:
dsh plugin add --profile web github:CJYLZS/dsh-remote-development
For development, link a local checkout instead:
cd dsh-remote-development
pnpm install # self-contained workspace; store lives in .pnpm-store/
pnpm run build # emits lib/index.js (host) and lib/client.js (browser)
dsh plugin add --profile web link:/absolute/path/to/dsh-remote-development
A link: install points the profile at the checkout directory, so later pnpm run build runs apply on the next harness restart without re-adding.
Restart the harness after installing.
Three steps and nothing else:
Under the hood, setting a remote workspace creates an anchor under $DSH_HOME/remote-workspaces/<host>-<user>-<port>/<basename> — a real local directory whose metadata records the remote coordinates. There is no "current machine" and no default target: the anchor alone decides where its session's tools execute. If that machine is later deleted, operations on the workspace fail with an explicit "machine is no longer configured" error instead of silently running elsewhere — re-add the machine to resume, or delete the workspace directory.
Three routing providers replace the base row of the same service, so every local tool keeps working and only the transport changes:
RoutingFileSystem (replaces the sandbox filesystem) — file reads, writes, edits, and listings whose path resolves to a remote root go through SFTP; everything else delegates to the local base via super().RoutingSubprocessRuntime (replaces the local subprocess runtime) — spawns with an anchor cwd execute on the remote host over an SSH exec channel; the packaged ripgrep used by search tools is rewritten to the remote rg binary. Routing keys on the workdir alone, so behavior is identical on every host platform.RoutingBashExecutor / RoutingPwshExecutor (replace the sandbox bash/pwsh executor) — commands with a workdir under an anchor run through bash -c on the remote host, and background processes get a real remote PID via a process-group kill protocol. The host platform picks which executor mounts — only the LOCAL fallback is platform-bound (local bash on POSIX, local pwsh on Windows); the remote host's POSIX shell always decides the remote dialect.bash tool (never pwsh), and a local session on a Windows host sees pwsh (never the plugin-added bash). POSIX local sessions keep bash for both worlds, matching the base composition.Machine field, editable in settings), and the client turns that list into attribute selectors over the tree's data-files-* hooks, injected as a stylesheet. One color per machine; leaving it empty falls back to the theme accent. The sidebar's workspace rows expose no data hooks, so their rules reach the row through its aria-labels (:has()), matching the workspace title an anchor workspace adopts from its directory basename./status join the marker color uses) and rewrites those display texts to the remote path through a mutation observer, touching only existing text nodes so React's reconciler keeps every element it owns. The underlying data-files-* coordinates stay local: the tree still navigates by them.The model never sees the anchor handle. The harness's system prompt reports the session's working directory; for a remote session the plugin overrides that variable per agent with the remote path, so the model-visible cwd is the directory its commands actually run in. As a safety net, anchor-directory spellings (absolute, ~, $HOME, ${HOME}) in command text are rewritten to their remote paths before execution — same-machine anchors only, stdin left verbatim. Local sessions pass through unchanged.
Remote mutation policy mirrors the local sandbox: read-only mode rejects remote writes; workspace-write allows writes only under the remote workspace root and the remote /tmp. Remote writes are serialized per file, publish atomically (temp file + rename), and refuse stale versions and ambiguous edits with the same error codes the local filesystem produces.
Host keys use TOFU (accept-new by default): the first-seen key is recorded, a changed key is rejected with a MITM reason, and verify/off modes are available per machine.
Machines are managed in the settings section; the plugin itself takes config defaults through cordis.yml (all optional):
| Field | Default | Meaning |
|---|---|---|
commandTimeoutMs |
20000 | Per-command timeout; the channel is closed after SIGTERM grace. |
connectTimeoutMs |
15000 | SSH connection establishment timeout. |
maxOutputChars |
200000 | Command output kept head-plus-tail beyond this size. |
maxFileBytes |
52428800 | Largest file read or written through SFTP. |
hostKeyMode |
accept-new |
accept-new, verify, or off. |
remoteRipgrep |
rg |
Remote binary the packaged ripgrep is rewritten to. |
anchorRoot |
$DSH_HOME/remote-workspaces |
Root directory for anchor directories. |
auditLog |
off | Append-only JSONL audit of remote executions. |
uname detection rejects Windows targets with a clear error. Adaptation is reserved for a later phase.bash tool for remote sessions and the pwsh tool for local ones.@ file references are not supported in remote sessions. Typing @ in a remote session yields a single explicit "not supported yet" candidate rather than a silent failure; the reference-source interface is reserved for a later phase.maxFileBytes.hooks.** The recoloring targets the file tree's data attributes, which are not a declared public contract; a dsh rename silently drops the coloring (purely presentational — nothing else breaks). Workspace-row markers additionally match by workspace title: renaming a workspace, or a dsh change to the sidebar'saria-label` copy, drops the row marker while the file-tree marker keeps working.rg binary must exist on the remote machine (configurable via remoteRipgrep); otherwise search tools fail on remote paths.ssh2 never loads its optional native accelerators, so throughput on large SFTP transfers is lower than a natively-built ssh2 would give.browse backend (only the list/createDirectory primitives — no OS chooser). The local tab branches on that resolution — native opens the OS chooser, browse drives the host's in-app web browser instead; before this, the tab hard-coded pick, which fails with directory-picker/unavailable on such boots.The plugin directory is a self-contained pnpm workspace (packages: [- .], storeDir: .pnpm-store) so pnpm cannot reach the harness repository's workspace. dsh framework packages are declared as peerDependencies (^0.1.2-rc.1, supplied by the host profile) and pinned exactly in devDependencies for local types and builds; no relative link: dependencies exist inside the dependency graph, so the directory builds standalone in any location.
Commands: pnpm run build (tsdown, both halves), pnpm run typecheck, pnpm run test (node:test via tsx; no SSH server needed — the pool accepts an injected client factory and the SFTP surface is faked).
ssh2 is a devDependency because it is build input, not a runtime dependency: pnpm run build bundles it into lib/index.js. Commit the rebuilt lib/ with any source change, or the GitHub install serves stale code.
lib/index.js contains bundled copies of ssh2 (MIT), asn1 (MIT), safer-buffer (MIT), tweetnacl (Unlicense), and bcrypt-pbkdf (BSD-3-Clause). Their license texts are reproduced in THIRD-PARTY-NOTICES.md.
Bundling moves security updates onto this repository: an ssh2 advisory no longer reaches users through their own pnpm update. Patching it means pnpm update ssh2 && pnpm run build, then committing the result here.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。