Skip to content

Prerequisites

From the root README.md and docs/development.md:

RequirementValue / note
Node.js^22.19.0 || >=24.0.0 (CI also covers 26)
pnpmpinned 11.7.0; run corepack enable if pnpm --version doesn't resolve through Corepack
Git2.26+
DeepSeek API keyneeded 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:

sh
npx @deepseek-ai/dsh web

The 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:

sh
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web
  • pnpm install also wires worktree-local Lefthook hooks and the translation-pairing Git merge driver (node scripts/install-lefthook.mjs if a cache-restored install skipped postinstall).
  • pnpm run build builds the Host lib phase, the Client lib phase, and the Web frontend. The dsh root script is defined as node --import tsx/esm apps/cli/src/bin.ts, so pnpm 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:

ProfileWhat it bringsTypical invocation
webdsh-base + dsh-web-app: server, browser UIdsh web or dsh --profile web
headlessdsh-base + dsh-headless: one-shot runner, no serverdsh --profile headless "task"

Any other profile name must be created first through the plugin subcommand:

sh
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:

CommandEffect
dsh webboot the web profile (alias of --profile web)
dsh --profile websame, spelled out
dsh --profile headless "task"run one session on the task, print the answer, exit
dsh --profile web --helpthe web app's help text, not the launcher's
dsh --helpthe launcher's own help
dsh --profile <name> --patch extra.ymlboot with one extra patch overlay (repeatable)
dsh --profile web --dump-configprint the composed web tree and exit (with user layer + --patch)
dsh --profile web --dump-default-configprint 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):

ExampleWhat it demonstrates
headless-agenta non-interactive one-shot agent with a selectable output format
jsonrpc-agentan unattended coding agent driven through the Python SDK / JSON-RPC
web-cordisa self-referential agent that inspects and edits its own live plugin tree
web-schedulean opt-in Web overlay for durable reminders (dsh web --patch examples/web-schedule/cordis.yml)
acp-agentan Agent Client Protocol automation server
mcp-memorythird-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.