DeepSeek Harness desktop external CLI: dsh web and RPC
The desktop app really is running an authenticated API, and the port is live — but you cannot get the key to enter it. The desktop host starts with --no-open --host 127.0.0.1 --port 0, and the token-bearing URL is handed only to the Electron shell over Node IPC, so external clients have no supported pairing flow (#10956). Identify the host first, then choose an architecture.
DeepSeek Harness: Step 1: identify which host you are attaching to
The desktop app and dsh web share the same session data but authorize outside clients in completely different ways; misidentify the host and every command after that is wasted.
- Look at processes: the desktop app is an Electron shell plus an embedded host;
dsh webis a Node process starting the host directly. - Look at launch arguments: the desktop uses
--no-open --host 127.0.0.1 --port 0with a random port. - Look at terminal output:
dsh webprints a line likedsh web: http://127.0.0.1:3080/?token=…. - Look at CLI capability: the current CLI has only profile launch,
--dump-config*, anddsh plugin, with no attach/session/send. - A quick check:
dsh session --helpon rc.3 reportsprofile "session" does not exist, because it treats session as a profile name.
DSH plugin: Step 2: why the desktop cannot be entered
Three hard boundaries stack up: the token only travels over IPC, there is no pairing flow, and the session lock forbids a second writer.
- Token boundary: the token-bearing URL is sent only to the Electron shell (
desktop-hostL30, L103-L104), so external clients have no way to obtain it. - Pairing boundary: remote access offers only a policy bit for "non-owner principals", but no flow registers such principals, so the only caller type is owner, admitted by the process's launch token.
- Lock boundary: an active session holds a cross-process flock on
session.lock, so a second host may list sessions but getssession/writer-heldon submit. - One more at the source level: the desktop host does print the
dsh web: …?token=line, but Electron wires it into the app's own stdout (host-process.ts), so it is visible only when launching the app binary from a terminal. - Port and token change on every start, and this path carries no documentation promise for the desktop app.
DeepSeek Harness: Step 3: the supported alternative architecture
Use a dsh web host, exchange the launch URL for a cookie, then operate sessions over RPC.
- Start it:
dsh web --host 127.0.0.1 --port 3080, and note the token-bearing URL printed in the terminal. - Exchange for a cookie:
B=http://127.0.0.1:3080
curl -s -c jar.txt -o /dev/null \
"$B/connect?token=<launch-token>&interactions=none"
# expect 302 and an API cookie
- Define an RPC call function (argument keys are the TypeScript parameter names):
rpc() { curl -s -b jar.txt -H 'content-type: application/json' \
-d "{\"type\":\"client-request\",\"rpcId\":\"$RANDOM\",\"method\":\"$1\",\"payload\":{\"args\":$2}}" \
"$B/api/$1"; }
rpc session/list '{"_request":{}}'
rpc session/prompt '{"request":{"requestId":"req-1","sessionId":"session-…","mode":"queue","content":[{"type":"text","text":"follow-up"}]}}'
- Read history with
session/pageandsession/follow; manage the queue withsession/updateQueueandsession/cancel. - If the client should answer approvals on the user's behalf, change
interactionstoanswer. - Without a cookie,
session/listreturns 401 — the fastest signal that the chain is or is not connected.
Community measurement: /connect returned 302 pointing at ./api/remote.mux, after which session/create and session/prompt returned {"accepted":true}; starting another dsh web on the same home could list the session, but prompt submission returned session/writer-held.
DSH Plugin: Migrating existing desktop sessions
Both share session files under $DSH_HOME, so after quitting the desktop app you can open the same data with dsh web and continue — the cost is exactly the restart you wanted to avoid.
- Quit every desktop process.
- Start
dsh webwith the sameDSH_HOME. - Find the original session in the list and confirm the history is intact.
- Then submit follow-up prompts using the RPC sequence above.
- The whole process needs no session file copying and no workspace rebuilding.
DeepSeek Harness: Known limitations
This RPC is an internal wire format, not a documented public API.
argskeys are TypeScript parameter names, and the list interface literally takes_request, which looks odd but is correct.- Argument shapes may change across rc versions, so regression-test before upgrading.
- The remote access policy currently covers only non-owner principals; do not expect it to grant external clients access.
- There is no attach or pairing entry for the desktop app today, and the rc.4 and 0.2.2-alpha.2 release notes add none.
- To get that capability, file an Ideas request and wait for upstream to decide.
DSH plugin: Cautions
Use the supported host instead of prying a token out of Electron.
- Do not try to extract the token from the Electron process. It is unsupported and out of scope here.
- Do not use a second host to force-write the same session.
session/writer-heldis the lock working correctly. - Do not hardcode the RPC as a stable API. After upgrading, run
session/listonce to verify. - Mind the cookie scope. Changing port or hostname requires a fresh
/connect. - Record the launch arguments. With a random port, only the URL in the terminal output tells you the address.
If your goal is only "run a task once from a script", headless is simpler than attaching to the desktop app; only when you need to operate an existing session is the dsh web route worth it.
A one-line selection rule: to operate an existing session cross-process, use dsh web RPC; for one-shot execution, use headless; if you need neither, do not open external access at all.
If you are already on the desktop app and cannot restart, external access is unsolvable in the current version — waiting for upstream to provide a pairing entry is the only direction.See dsh web authentication required: how to fix the prompt for the related case.
FAQ
No, DeepSeek Harness 0.2.1-rc.3 does not support it, and the rc.4 and 0.2.2-alpha.2 release notes add nothing. The desktop host serves the same API set, but the token is handed only to the Electron shell over Node IPC, so external clients have no supported authorization entry.
No, you did not install it wrong: the DeepSeek Harness CLI currently has only profile launch, --dump-config*, and dsh plugin families, with no attach/session/send, so dsh session is treated as a profile name and reports that the profile does not exist.
Starting a second host against the same session fails in DeepSeek Harness because an active session holds a cross-process flock on session.lock. A second host can list the session, but submitting a prompt returns session/writer-held.
The supported approach in DeepSeek Harness is to start a separate dsh web host: it prints a launch URL containing a token, exchange that with /connect for an API cookie, then call RPC methods such as session/list and session/prompt.
Yes, existing desktop sessions can be moved to dsh web in DeepSeek Harness, but you must quit the desktop app. Both share sessions under $DSH_HOME, so after closing the desktop app you can open the same data with dsh web and continue — at the cost of exactly the restart you wanted to avoid.
Related Terms
- Desktop Host
- Desktop Host is the host process embedded in the desktop app, started with --no-open --host 127.0.0.1 --port 0, serving the same authenticated API as dsh web, but its launch token is given only to the Electron shell.— https://github.com/deepseek-ai/deepseek-harness/discussions/10956
- /connect
- /connect is the dsh web authorization entry that exchanges a token-bearing URL for an API cookie. It returns 302, after which the cookie is required to call session RPCs.— https://github.com/deepseek-ai/deepseek-harness/discussions/10956
- session/writer-held
- session/writer-held is the error returned when submitting a prompt indicates another process holds write access to that session, guaranteed by the cross-process flock on session.lock.— https://github.com/deepseek-ai/deepseek-harness/discussions/10956
- RPC method
- RPC methods are the session operation interfaces exposed by dsh web, such as session/list, session/prompt, and session/page. Argument keys are passed through by TypeScript parameter name and are an internal format that may change across versions.— https://github.com/deepseek-ai/deepseek-harness/discussions/10956
Sources
- #10956 — Native CLI/API attachment to an existing Desktop session· deepseek-ai (GitHub Discussions)
- deepseek-ai/deepseek-harness (source repository)· GitHub