Prerequisites
From the root README.md and docs/development.md:
| Requirement | Value / note |
|---|---|
| Node.js | ^22.19.0 || >=24.0.0 (CI also covers 26) |
| pnpm | pinned 11.7.0; run corepack enable if pnpm --version doesn't resolve through Corepack |
| Git | 2.26+ |
| DeepSeek API key | needed for the Web/headless/ACP demos and real-API e2e tests (DEEPSEEK_API_KEY or a .env) |
You do not need TypeScript or a build tool to run dsh from npm — the published @deepseek-ai/dsh package ships built lib/*.js. You only need the build toolchain when running from source.
Run from npm
Install Node.js, then:
npx @deepseek-ai/dsh webThe launcher boots the web profile and serves the Web UI at http://127.0.0.1:3080 by default (the port is decided by the web app's own flags, defaulting to 3080). The process uses your invoking directory as the default filesystem root, then the Web UI asks you to Choose workspace and to add a model in Settings → Models before a session becomes fully usable. See the [user guidedocs/user/guide/index.md.
This same launcher handles every mode — apps/cli/src/bin.ts dynamically imports per mode, so --help/--version/a parse error print and exit, and only a valid mode reaches the real dispatch.
Run from source
Clone and build:
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh webpnpm installalso wires worktree-local Lefthook hooks and the translation-pairing Git merge driver (node scripts/install-lefthook.mjsif a cache-restored install skippedpostinstall).pnpm run buildbuilds the Host lib phase, the Client lib phase, and the Web frontend. Thedshroot script is defined asnode --import tsx/esm apps/cli/src/bin.ts, sopnpm dsh <args...>runs the TypeScript entry directly and forwards every argument.- Production runs from npm need no source checkout; the source path is for contributors.
Profiles: web vs headless
dsh is not a single program with modes baked in; it boots a profile, a named composition stored in the Harness home. Two are shipped as templates and auto-initialize on first use:
| Profile | What it brings | Typical invocation |
|---|---|---|
web | dsh-base + dsh-web-app: server, browser UI | dsh web or dsh --profile web |
headless | dsh-base + dsh-headless: one-shot runner, no server | dsh --profile headless "task" |
Any other profile name must be created first through the plugin subcommand:
dsh plugin --profile myname add <package>Useful commands
The launcher's command grammar lives in apps/cli/src/args.ts. It parses only its own flags and hands everything after them to the booted profile — so app flags (--port, --help) belong to that app, not the launcher:
| Command | Effect |
|---|---|
dsh web | boot the web profile (alias of --profile web) |
dsh --profile web | same, spelled out |
dsh --profile headless "task" | run one session on the task, print the answer, exit |
dsh --profile web --help | the web app's help text, not the launcher's |
dsh --help | the launcher's own help |
dsh --profile <name> --patch extra.yml | boot with one extra patch overlay (repeatable) |
dsh --profile web --dump-config | print the composed web tree and exit (with user layer + --patch) |
dsh --profile web --dump-default-config | print the bundle layers only (no user layer) |
dsh plugin --profile <name> add <pkg> | install a plugin into a profile (forwards to pnpm) |
Launcher flags are --profile, --patch, --dump-config, --dump-default-config, and --version. The config dumps are boot-free: renderConfigDump in app-boot composes the tree with the include's own applyEntryPatches, so the printed tree is exactly what would mount — a good way to see how plugins pile up before you write your own patch.
Where data lives
All user data lives under the Harness home, resolved by @deepseek-ai/dsh-home-paths:
resolveDshHome()→ an explicit configured path, else$DSH_HOME, else~/.dsh.- Profiles live at
$DSH_HOME/profiles/<name>(each a package.json manifest +cordis.patch.yml). - The home-level overlay is
$DSH_HOME/cordis.patch.yml; layered environment lives in$DSH_HOME/.env. - Managed credentials live separately in
$DSH_HOME/.credentials.yaml.
The invoking directory is the default workspace for a session; the Harness home is where profiles, credentials, and user patches are stored. Keeping the two distinct is important: home is machine-local state, workspace is what the agent works on.
A quick tour of the examples
After a source build, examples/ holds runnable demos (see examples/README.md):
| Example | What it demonstrates |
|---|---|
headless-agent | a non-interactive one-shot agent with a selectable output format |
jsonrpc-agent | an unattended coding agent driven through the Python SDK / JSON-RPC |
web-cordis | a self-referential agent that inspects and edits its own live plugin tree |
web-schedule | an opt-in Web overlay for durable reminders (dsh web --patch examples/web-schedule/cordis.yml) |
acp-agent | an Agent Client Protocol automation server |
mcp-memory | third-party memory servers via the generic MCP client |
For the credentials-gated demos, set DEEPSEEK_API_KEY (optionally DEEPSEEK_BASE_URL) in the environment or a repo-root .env; without a key, the real-API suites self-skip.
Further reading
- What Is DeepSeek Harness? — the product you just launched.
- Anatomy of the Monorepo — which group owns the pieces you touch.
- Boot & CLI — profiles, bundles, and flags in depth.
docs/development.md— the contributor setup tutorial and daily commands.packages/boot/cmdline/README.md— how app flags reach the booted tree.