DeepSeek Harness dsh commands: web, plugin and --profile, with exit codes

Install & Get StartedPublished 2026-09-03Author: DeepSeek Plugin Market
DeepSeek HarnessDSHdsh webdsh plugincommand list
dsh is the DeepSeek Harness command-line launcher: dsh web starts the Web UI, dsh plugin manages plugins, dsh --profile runs a mode; see flags and exit codes.

dsh is the command-line launcher for DeepSeek Harness. Its commands fall into four families: dsh web boots the Web UI, dsh --profile headless "job" runs a one-shot session, dsh --profile <name> boots any profile, and dsh plugin manages plugins — the launcher parses only its own flags and hands everything after to the booted app. This dsh command list is organized by entry-command cheatsheet, argument ownership, dsh web flags, dsh plugin, config debugging and exit codes. Every command and flag comes from the official dsh CLI README and CLI behavior reference, so you can copy and run them as-is.

dsh commands at a glance: web, plugin and --profile entry modes

dsh does one thing: load the selected profile and hand the remaining arguments to the booted app. In the official docs' words, the command grammar lives in src/args.ts and src/bin.ts loads only the selected runner (source). The Entry modes table defines all four entry commands — the backbone of any dsh command list (source):

CommandPurpose
dsh --profile <name>Boot the named profile under $DSH_HOME/profiles/<name>
dsh --profile headless "job"Run one fresh persisted session, print the final answer, exit
dsh webHardcoded alias of --profile web, boots the Web UI
dsh plugin --profile <name> <pnpm args>Forward to pnpm inside the profile directory to manage plugins

To get started, follow these three steps (source):

  1. Open a terminal and cd into the project directory you want as the workspace;
  2. Run npx @deepseek-ai/dsh web (not globally installed) or dsh web (globally installed) and wait for the printed address;
  3. Open the Web UI in a browser, add an API key, select the workspace and start.

Supporting facts from the same source (source):

  1. The invoking directory is the default workspace root in every mode.
  2. Auto-initialization: the web and headless profiles initialize from shipped templates on first use (web = base + web-app, headless = base + headless); any other missing profile fails loud with a hint to run dsh plugin --profile <name> add <package> (source).
  3. Data directory: profiles live at $DSH_HOME/profiles/<name>, where $DSH_HOME defaults to ~/.dsh.

Argument ownership: launcher flags vs flags handed to web or headless

dsh parses only its own flags; from the first token it does not recognize onward, every argument is handed verbatim to the booted profile. The official README shows the boundary with (source):

bash
dsh --profile web --port 8080           # --port belongs to the web app
dsh --profile headless "run the tests"  # the quoted job is the headless app's positional argument
dsh --profile web --help                # prints the web app's help and boots nothing
dsh --help                              # prints the launcher's own help

Edge cases worth knowing (source):

  1. -V/--version works only before the boundary: dsh --version prints the launcher version; past the boundary it is handed to the app.
  2. A literal -- needs two: the launcher consumes one --, so an app argument that must arrive as a literal -- needs -- --.
  3. The web app's own command line is --host, --port and repeatable --trusted-host (source).
  4. The headless app's own command line is a single positional task text; an invocation without a task is a usage error.

dsh web flags: changing the port, trusted hosts and boot examples

dsh web is the hardcoded alias of --profile web and boots the Web UI; it supports --host, --port and --trusted-host. The web runner serves http://127.0.0.1:3080 by default (source). Common forms:

bash
# Minimal boot (equals dsh --profile web)
dsh web

# Change the port: --port is a web-app flag
dsh web --port 8080

# Boot with an extra local patch layer
dsh web --patch ./extra.cordis.yml

# Print the composed config tree without booting
dsh web --dump-config

# Show the web app's full flags
dsh web --help

What to know when using it (source):

  1. Default 3080: after a successful boot, open http://127.0.0.1:3080 in a browser; on first use add an API key in Settings and select a workspace — see How to use npx @deepseek-ai/dsh web.
  2. --host 0.0.0.0 is not supported yet and exits with a usage error; to constrain source hosts use the repeatable --trusted-host flag to add named authorities.
  3. npx equivalent: without a global install, prefix every command with npx @deepseek-ai/dsh, e.g. npx @deepseek-ai/dsh web --port 8080.

dsh plugin: add, remove, why, update and GitHub source installs

dsh plugin forwards whatever follows --profile <name> to pnpm inside the profile directory, so pnpm verbs such as add, remove, why and update work unchanged. If the profile is missing it first initializes from a template (shipped for web, @deepseek-ai/dsh-base alone for other names) and then runs pnpm (source). Examples:

bash
# Install an npm plugin into the web profile
dsh plugin --profile web add dsh-plugin

# Install from a GitHub repo (source install, active right away)
dsh plugin --profile tui add github:deepseek-harness/turtle-ui

# Remove a plugin
dsh plugin --profile tui remove turtle-ui

# Find out why a dependency is installed
dsh plugin --profile web why dsh-plugin

# Update plugins in a profile
dsh plugin --profile web update

Two things to remember when installing from GitHub source (source):

  1. The first git add can fail: git-hosted plugins that ship sources build through a prepare script during install, which pnpm ≥10 blocks by default; copy the printed allowBuilds key into the profile's pnpm-workspace.yaml and rerun.
  2. Local paths win: relative specs (., ../plugin and their file:/link: forms) anchor to the invoking directory, so running dsh plugin --profile web add . inside a plugin checkout installs that checkout, not the npm package.

After each successful run, dsh.profile.bundles reconciles against the installed state: a dependency that newly declares dsh.bundle.patch joins the layer stack, and a removed dependency leaves it (source). If you would rather click than memorize flags, install DSH Plugin Hub and use Settings > Plugin market in the Web UI for visual installs, uninstalls and updates.

Debugging config: --patch and --dump-config / --dump-default-config

dsh composes its tree by layering bundle patches, the profile's cordis.patch.yml, the home-level $DSH_HOME/cordis.patch.yml and --patch overlays, with later layers winning; use the dump flags to see the result without booting (source). Debug with:

bash
# Print only the bundle layers (no profile/home/patch overlays)
dsh --profile web --dump-default-config

# Print the full composed tree: bundle + profile + home + --patch
dsh --profile web --patch ./extra.yml --dump-config
  1. Plugin installed but not taking effect? Run dsh --profile web --dump-config and check which file supplied each row and which overlay changed it — the comments name the source file.
  2. Want to change a setting temporarily without editing files? Pass --patch ./extra.yml; it is the last layer and wins.
  3. Two limits: a dump never runs app command-line providers, so an invocation that carries app arguments is rejected; it also does not actually boot the Web UI.

Exit codes: how to tell from a script whether dsh succeeded

dsh expresses results through exit codes: a headless job exits 0 on completed and 1 otherwise, help requests exit 0, rejected arguments exit nonzero, SIGINT reports 130 and SIGTERM exits 0. Invalid commands, options from another mode, config errors and boot failures all exit nonzero (source), so check $? in a script instead of parsing text output:

ScenarioExit code
Headless job completed0
Headless job ended not completed1
--help request0
Rejected arguments / invalid command / config error / boot failurenonzero
SIGINT (graceful drain after Ctrl+C)130
SIGTERM (supervisor's ordinary stop)0
Second signalforce exit immediately

A complete example for scripts and automation — run a one-shot job and branch on the result:

bash
dsh --profile headless "run the tests"
if [ $? -eq 0 ]; then
  echo "job completed"
else
  echo "job failed with exit code $?"
fi

Note that web gives the plugin tree up to five seconds to dispose on SIGINT/SIGTERM, so the first Ctrl+C is a graceful drain, not an instant kill (source). When you put dsh into a script or CI, checking the exit code is the most reliable signal — use the cheatsheet above for dsh web, dsh plugin and dsh --profile, and turn to DSH Plugin Hub's UI when you prefer clicking over typing.

Sources: dsh CLI README, dsh CLI behavior reference, official Quickstart

FAQ

How do I use dsh web, dsh plugin and dsh --profile?

dsh is the command-line launcher of DeepSeek Harness, with four official entry commands: dsh web boots the Web UI (an alias of dsh --profile web), dsh --profile headless "job" runs one session and exits, dsh --profile <name> boots any installed profile, and dsh plugin --profile <name> <pnpm args> manages plugins. Not installed globally? Prefix any command with npx @deepseek-ai/dsh, for example npx @deepseek-ai/dsh web.

Why does dsh --profile web --port 8080 change the port?

Because dsh is a launcher and parses only its own flags: everything after the first token it does not recognize goes to the booted DeepSeek Harness app. So --profile belongs to the launcher and --port belongs to the web app, and dsh --profile web --port 8080 boots the Web UI on port 8080. A literal -- inside app arguments needs -- --; -V/--version also works only before that boundary.

Which flags does dsh web support to set the port and host?

dsh web boots the Web UI, which supports --host, --port and a repeatable --trusted-host; dsh web --port 8080 changes the listening port. The web app serves http://127.0.0.1:3080 by default, and the CLI does not support --host 0.0.0.0 yet, exiting with a usage error. To constrain source hosts use --trusted-host, or prefix with npx when dsh is not installed globally.

How do I add a plugin with dsh plugin from npm or GitHub?

Both work. dsh plugin forwards whatever follows --profile <name> to pnpm inside that profile directory, so pnpm verbs like add, remove, why and update are available unchanged: dsh plugin --profile web add dsh-plugin installs an npm package, and dsh plugin --profile tui add github:deepseek-harness/turtle-ui installs from GitHub source. If the first git add is blocked by pnpm, copy the printed allowBuilds key into the profile's pnpm-workspace.yaml and rerun.

How do I read dsh exit codes in a script?

Check the exit code instead of parsing output: a headless job exits 0 on completed and 1 otherwise, help requests exit 0, and rejected flags, invalid commands, config errors and boot failures all exit nonzero. SIGINT from Ctrl+C reports 130 and SIGTERM exits 0. In a script, branch on $? with an if statement to tell success from failure.

Related Terms

dsh
dsh is the command-line launcher (product launcher) of DeepSeek Harness: it loads the selected profile and hands the remaining arguments to it, with official entry commands grouped into dsh web, dsh --profile and dsh plugin.dsh CLI README
profile
A profile is an ordered stack of plugin-bundle patch layers stored under $DSH_HOME/profiles/<name>; profiles such as web and headless auto-initialize from shipped templates on first use.dsh CLI README
dsh plugin
dsh plugin is dsh's plugin-management subcommand; it forwards whatever follows --profile <name> to pnpm in that profile directory, so pnpm verbs such as add, remove, why and update all work.dsh CLI behavior reference
--dump-config
--dump-config is a dsh flag that prints the composed config tree without booting, useful for debugging whether plugin patches took effect; --dump-default-config prints only the bundle layers.dsh CLI behavior reference

Sources