dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Alibaba Cloud Workbench CLI plugin — lets DeepSeek Harness's Agent directly control remote ECS instances.
v0.9.0 · MIT License
English | 中文
A DeepSeek Harness (Cordis) plugin that lets the Agent control remote Alibaba Cloud ECS instances through the local Workbench CLI.
It drives the official Alibaba Cloud Workbench CLI locally, and ships 11 agent-native tools — ecs_find / ecs_list / ecs_exec / ecs_log / ecs_upload / ecs_download / ecs_diagnose / ecs_deploy / ecs_runbook / ecs_snapshot / ecs_session — covering the full find → list → diagnose → execute → upload → restart → verify → snapshot check loop, plus a visual settings panel (CLI status, instance browser, guarded deploy wizard, sessions, operation timeline). Instances are reached through the Workbench backend channel, so no public IP is needed; destructive commands go through the Harness approval guard and are rejected unless explicitly allowed (fail closed).
dsh web, DSH 0.1.x) and the DeepSeek Harness desktop app (dsh-desktop-host, DSH 0.2.x) — one package, one lib/client.js, both hosts. The desktop host is the same web surface, so the settings tab and all 11 tools appear there too; the peerDependencies range covers both runtimes so neither host skips the bundle, and test/compat.mjs locks that inecs_snapshot, v0.7.0+ adds ecs_find): ecs_find / ecs_list / ecs_exec / ecs_log / ecs_upload / ecs_download / ecs_diagnose / ecs_deploy / ecs_runbook / ecs_snapshot / ecs_session, integrated with the Harness tool pipelineecs_snapshot (v0.8.0+): makes "the rollback point before you touch anything + the difference check afterwards" a first-class concern instead of a hand-written docker tag + docker cp + docker images/ps assembly script per agent — create runs one read-only collection (default collectors: host info / docker images --digests / docker ps / ss -tln, plus paths file-and-directory fingerprints and commands custom collectors) and writes the manifest into the workspace at .dsh/workbench-ecs/snapshots/<name>.json (diffable, committable, greppable); list makes no remote call at all; diff re-collects with the collectors recorded in the manifest and compares item by item (files added/removed/changed/metadata-only, collector output reported down to the first differing line), and against compares two snapshots with each otherparams grows from "just a default" into a parameter descriptor { required, pattern, enum, default, description, hint } (the scalar form still means a default, fully backward compatible) and it covers implicit parameters too (including instance-anchor fields) — a missing required parameter, a pattern mismatch or an enum mismatch is caught by validate / plan before any command is sent. The settings panel's Validate button and the pre-execution checks share one implementation, so "the panel said OK but execution blew up" cannot happenfrom_step (v0.8.0+): ecs_runbook validate/plan, ecs_deploy dry_run and failure results all carry step-by-step idempotency analysis in a "replay advice" block (safe_prefix plus one directly actionable conclusion, e.g. "the first 2 steps can be repeated; step 3 onwards may have side effects — either re-run the whole runbook, or use from_step: 2 to continue from that step"). ecs_deploy { from_step: N } skips the steps with [i] < N, and both the preview and the real result explicitly list skipped_prefix while warning that "the side effects of these steps will not be recreated"ecs_find (v0.7.0+): answers "which region is my instance actually in" without guessing a region first — keyword substring-matches instance name / instance ID / private IP / public IP / instance type / tag values (case-insensitive), and an omitted region (or "all") concurrently searches the built-in list of 22 public regions, or pass one region or several comma-separated ones. There is no early stop on first hit (listing too few instances is far more dangerous than listing too many): results are grouped per region and the call reports regions_tried / regions_ok / regions_failed (a failing region never hides the others) / scanned, and it also lists the workspace instance anchors. ecs_list stays "region required, single region"; when that region returns 0 instances the result carries an empty_hint pointing at ecs_find.dsh/workbench-ecs/instances.json (the plugin defines the format, the project fills in the content), and afterwards every tool that accepts instance_id / instance_ids (ecs_exec / ecs_upload / ecs_download / ecs_diagnose / ecs_deploy / ecs_log / ecs_runbook, …) accepts an anchor name directly (e.g. "prod") — the plugin resolves it to the real instance ID at the tool-registration boundary and fills in the anchor's region when none was given, so every tool stays unchanged. Fields other than instance_id/region (such as repo) become implicit runbook parameters, so a runbook can write ${repo} without passing it every time. Without the file, behaviour is exactly as beforeecs_upload gains retries (default 2, i.e. up to 3 attempts) and retry_delay (default 1s, exponential backoff), and only transient network failures are retried while semantic failures (remote file already exists, permission denied, bad argument) fail immediately. Failure messages state the owner of the problem: the "local machine → OSS" hop, not the target ECS instance, and suggest simply re-running the same command later (uploads are idempotent). verify_sha256 now defaults to true in all three places: ecs_upload / ecs_deploy (classic phases) / steps[].uploadexit_code: 0 with timed_out: true (the process exit code is really 124) — so the previous "JSON is authoritative" rule rendered a killed command as "success with no output". From v0.7.0 on, everything settles to exit_code: 124 + timed_out: true with duration and the timeout reason, and a timed-out step in an orchestration fails loudly. The read-only guard now lists every matched rule with its matched text and position plus concrete "read-only equivalent" advice; and a rejected destructive command explains four distinct cases (no approval service mounted / session policy never / user rejected / request cancelled or unattended) instead of collapsing into one "not approved", stating explicitly that "this is not a syntax error, re-running the same command will not change the outcome"ecs_exec { detach: true } starts the job with remote nohup, writes a log file, and immediately returns job_id/log_path/exit_path; the plugin polls increments on an interval, so it never holds the instance for the whole run (other calls on the same instance interleave normally) and the remote log file stays the single source of truth — a slow reader can no longer lose or duplicate segments. ecs_log reads any remote file by byte cursor, so release-log polling no longer needs repeated full tailsecs_exec { session_id: "deploy" } keeps the working directory and environment across calls (cd/export carry over), different session ids stay isolated, and an idle session resets after 30 minutes with an explicit noticeworkbench command and reach instances through the Alibaba Cloud Workbench backend (works for instances without public IPs){code, message}) become readable messagesrm -rf, shutdown, reboot, mkfs, dd, iptables -F/-X, …) request confirmation through the Harness approval service; anything not allowed-once is rejected (fail closed)ecs_exec's script parameter base64-delivers the body to a remote file and executes it, so the content never passes through a shell quoting layer — docker exec ... node -e "...", Chinese text, $, backticks, heredocs and multi-line scripts all work with zero escaping; bodies over 16KB are delivered in chunks and verified by byte countread_only on ecs_exec / ecs_diagnose rejects write operations before they reach a shell (redirects, rm/mv/cp/chmod, docker/systemctl mutations, nohup, …); on by default for ecs_diagnose, with zero false positives on the built-in diagnostic scriptecs_upload.verify_sha256 compares local/remote digests after upload (on by default since v0.7.0, unified with ecs_deploy) and aborts before restart on a mismatch. Local hashing uses sha256sum/shasum/certutil, so no extra runtime is requiredecs_exec supports run_in_background — long commands are registered with the jobs service, read incrementally with job_output, terminated with job_kill; with a batch, each instance gets its own job and the call returns job_ids (v0.5.1+)<workspace>/.dsh/workbench-ecs/runbooks/*.json and run it with ecs_deploy { runbook: "release", runbook_params: { sha } } in one call; the plugin only supplies the mechanism (load / validate / ${param} substitution / expansion) while the content and script bodies stay in the project repository — reviewable, versioned, and free of plugin-side project logic. The settings panel can also list / validate / preview / execute the same runbook (shared engine, byte-identical preview); the ecs_runbook tool gives read-only static checks (typos, missing params, weak assertions, guard conflicts) and shell variables escape as $${NAME}; five generic runbook templates ship with the package (v0.6.7+: host-check / compose-redeploy / disk-cleanup / tls-cert-check / log-dig) — copy one into any project and it worksecs_deploy { steps: [...] } expresses "upload → run → assert → read log" as one call; assert evaluates expect checks and reports exactly which assertion failed, what was expected, and what actually happened; dry_run previews the commands without executing. A release drops from a dozen calls to oneecs_exec supports an instance_ids array (per-instance failures do not stop others); concurrency controls parallelism (default 4 when read_only, otherwise serial) while the same instance still serializes behind its lock — cluster triage no longer queues one host at a time (v0.5.1+)ecs_upload { local_dir: "dist" } does "local tar → upload → sha256 verify → remote extract" in one call; a checksum mismatch aborts the extract, so a corrupt archive never rewrites the remote directoryoutput_json: true returns stable JSON text for downstream automation (ecs_exec / ecs_list)strip_ansi: false opts out)exit_code from the CLI's JSON is authoritative (rather than the local process status), and request_id/session_id come back for after-the-fact isolation checks; a remote timeout settles as 124 + timed_out: true first (v0.7.0+ — the CLI's JSON lies with exit_code: 0 on timeout; see "New remote-timeout semantics" below)workbench via PATH and falls back to common install locations (e.g. C:\Program Files\workbench\workbench.exe), handling stale host-process PATHTwo hosts, two profiles (v0.9.0+) — DSH now ships both a browser surface and a desktop app. They are separate host processes loading separate profiles, so the plugin must be installed into each one:
Which one you use Host process Profile directory Install command Browser ( dsh web)dsh web(npm-global CLI)%DSH_HOME%\profiles\webdsh plugin --profile web add dsh-workbench-ecsDesktop app dsh-desktop-host(the runtime bundled in Electron)%DSH_HOME%\profiles\desktopdsh plugin --profile desktop add dsh-workbench-ecsRun both commands to cover both. The desktop side is still the web surface (it bundles
dsh-web-app, and the client module system only acceptsdsh.client.platform === "web"), so one package and onelib/client.jsserve both — but the profiles are not shared: if you only installed into the web profile, the desktop app will not load the plugin at all (no tools, no settings tab).
dsh web, or the DeepSeek Harness desktop app);When the host loads a bundle it applies a version gate: every @deepseek-ai/dsh-* entry in the package's peerDependencies is compared against the running DSH version, and a mismatch makes the host silently skip the whole bundle (the plugin never loads and nothing is reported). The peer range therefore has to cover both runtimes:
| Host | Measured DSH runtime | This package's peer declaration |
|---|---|---|
Browser dsh web | 0.1.1-rc.2 | @deepseek-ai/dsh-tools: ^0.1.1-rc.2 || ^0.2.0-rc.2 |
| Desktop app | 0.2.0-rc.2 | same (plus @deepseek-ai/cordis: >=4.0.1 <5.0.0) |
v0.8.0 only knew
^0.1.1-rc.2, which under semver means>=0.1.1-rc.2 <0.2.0-0— 0.2.0-rc.2 does not satisfy it, so the desktop host skipped the entire bundle. v0.9.0 fixes that and turns the gate into a regression test (test/compat.mjs): if either runtime stops being covered,npm testfails.
# browser surface
dsh plugin --profile web add dsh-workbench-ecs
# desktop app (a separate command)
dsh plugin --profile desktop add dsh-workbench-ecs
Afterwards the 11 tools are visible to the Agent and a "Workbench ECS" tab appears in the harness settings (gear icon).
dsh web: restart dsh web when hot reload is unavailable;For local development from a checkout, link the repo instead:
dsh plugin --profile <web|desktop> add link:<absolute-path-to-repo>— subsequentlib/client.jsedits apply after a plain page refresh (no server restart).
# the desktop host uses 19387, `dsh web` uses 3080 — use whichever you run
curl -s http://127.0.0.1:19387/dsh-workbench-ecs/health
# => {"ok":true,"plugin":"dsh-workbench-ecs","version":"0.9.0"}
On the desktop host (and any host with authentication on) a bare
curlcan return401 unauthorized: the host protects the whole site with a launch token plus a session cookie. That does not mean the plugin is missing — open the same URL from the already signed-in page to see thehealthJSON.
Then ask the Agent:
ecs_list { region: "cn-shanghai" }
ecs_exec { instance_id: "i-uf66ct2o35p7fjcd0sru", command: "df -h" }
ecs_diagnose { instance_id: "i-uf66ct2o35p7fjcd0sru" }
| Platform | Command |
|---|---|
| Windows (PowerShell) | irm https://workbench-cli.oss-cn-hangzhou.aliyuncs.com/install.ps1 | iex |
| Linux / macOS | curl -fsSL https://workbench-cli.oss-cn-hangzhou.aliyuncs.com/install.sh | bash |
Verify after install:
workbench version # should print version / commit / build date
⚠️ Windows note: if you installed the CLI after the Harness process started, the host process's inherited
PATHis stale andworkbenchwill not resolve on its own. The plugin includes a fallback scan of common install locations, so it usually works without a restart; if it still fails, restart the Harness session or add the install directory (e.g.C:\Program Files\workbench) toPATH.
The Workbench CLI stores credentials in ~/.workbench/config.json (must be mode 0600). Five authentication modes are supported; edit the file directly (avoid interactive workbench config):
AK mode (development / long-lived credentials, default):
{
"current": "default",
"profiles": {
"default": {
"mode": "AK",
"access_key_id": "LTAIxxxxxxxxxxxxxxxx",
"access_key_secret": "xxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
StsToken mode (temporary security credentials):
{
"current": "default",
"profiles": {
"default": {
"mode": "StsToken",
"access_key_id": "LTAIxxxxxxxxxxxxxxxx",
"access_key_secret": "xxxxxxxxxxxxxxxxxxxxxxxx",
"security_token": "xxxxxxxxxxxxxxxxxxxxxxxx"
}
}
}
RamRoleArn mode (production / cross-account / least privilege, auto-refreshed STS tokens):
{
"current": "default",
"profiles": {
"default": {
"mode": "RamRoleArn",
"access_key_id": "LTAIxxxxxxxxxxxxxxxx",
"access_key_secret": "xxxxxxxxxxxxxxxxxxxxxxxx",
"ram_role_arn": "acs:ram::123456789:role/WorkbenchRole",
"role_session_name": "workbench-session"
}
}
}
CredentialsCmd mode (zero-trust / Vault integration — external command prints credential JSON):
{
"current": "default",
"profiles": {
"default": {
"mode": "CredentialsCmd",
"credentials_cmd": "vault read -format=json secret/aliyun-ecs"
}
}
}
CredentialsURI mode (metadata service / sidecar):
{
"current": "default",
"profiles": {
"default": {
"mode": "CredentialsURI",
"credentials_uri": "http://localhost:8080/credentials"
}
}
}
Set file permissions (Linux/macOS; on Windows make sure the file is not readable by other users):
chmod 600 ~/.workbench/config.json
One-shot setup script (Windows): the repo ships scripts/workbench-setup.ps1 supporting all 5 modes and non-interactive multi-profile setup:
# AK mode
./scripts/workbench-setup.ps1 -AccessKeyId LTAIxxx -AccessKeySecret xxx
# RamRoleArn mode (recommended for production) + profile switching
./scripts/workbench-setup.ps1 -Mode RamRoleArn -Profile prod -AccessKeyId LTAIxxx -AccessKeySecret xxx -RamRoleArn acs:ram::123456789:role/WorkbenchRole -AutoSwitch
Multi-profile management (non-interactive):
workbench config list # list all profiles (* marks the active one)
workbench config switch --profile prod # switch the active profile
workbench config get # show the current profile details (JSON)
workbench config delete --profile old # delete a profile (cannot delete the active one)
Attach the following minimum policy to the RAM user/role that runs the CLI:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": ["ecs-workbench:LoginECSInstance", "ecs-workbench:ChatMessages"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["ecs:DescribeInstances", "ecs:DescribeCloudAssistantStatus", "ecs:StartTerminalSession"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "ram:CreateServiceLinkedRole",
"Resource": "*",
"Condition": {
"StringEquals": { "ram:ServiceName": "workbench.ecs.aliyuncs.com" }
}
}
]
}
To restrict to specific instances, replace "Resource": "*" with:
ecs-workbench:LoginECSInstance: acs:ecs:<region>:<account-id>:ecs/<instance-id>ecs actions: acs:ecs:<region>:<account-id>:instance/<instance-id>Add the plugin row to your Cordis composition instead (cordis.yml / cordis.patch.yml):
- id: dsh-workbench-ecs
name: dsh-workbench-ecs
Note: the settings panel is only wired up by the dsh command (which uses the package's dsh.bundle layer and dsh.client declarations). Add the row to each profile you want it in — %DSH_HOME%\profiles\web for the browser, %DSH_HOME%\profiles\desktop for the desktop app.
Use scripts/install-local.ps1 to link the repo into %DSH_HOME% with a junction and write the plugin row for you. It is idempotent and reversible:
# every profile that already exists (web + desktop)
powershell -ExecutionPolicy Bypass -File .\scripts\install-local.ps1
# just one of them
powershell -ExecutionPolicy Bypass -File .\scripts\install-local.ps1 -Profile desktop
# inspect / undo
powershell -ExecutionPolicy Bypass -File .\scripts\install-local.ps1 status
powershell -ExecutionPolicy Bypass -File .\scripts\install-local.ps1 uninstall
A profile directory only exists after its host has started once (dsh web for web, the desktop app for desktop). Edits apply on the next patch reload, a page refresh, or the relevant host restart — the desktop app reads its profile once at process start, so it needs a restart.
| Area | Capabilities |
|---|---|
| CLI status | CLI availability / version / credential profile / daemon; 20s host cache + instant local render (panel opens immediately, refreshes silently in the background; [Refresh] forces a re-check) |
| ECS instances | region / status filters, name-or-ID search, status distribution strip, checkboxes for batch actions, 30s auto-refresh |
| Row actions | [Run] pick target / [Diagnose] one-shot health check (disk · memory gauges) / [Deploy] guarded publish wizard / [Details] metadata + recent logs |
| Remote command | command history (datalist), two-click confirmation for destructive patterns (host still rejects); batch execution with per-instance result table |
| Guarded deploy | upload local file (OSS relay, ≤1GB) + restart/apply comm |