{"slug": "streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies", "title": "Streaming a desktop agent to your phone with zero npm dependencies", "summary": "A developer built DeepSeek Phone Harness, a mobile remote for DeepSeek Harness, using zero npm dependencies and a single Node process. The tool streams desktop agent events to a phone via WebSocket and REST, and the developer highlighted key architectural decisions, including a unified event stream and a fix for a deadlock caused by separate channels for approvals and questions.", "body_md": "One WebSocket, one Node process, one HTML file. Here's the architecture behind DeepSeek Phone Harness, and the three decisions that made it work.\n\nLast weekend I built [DeepSeek Phone Harness](https://github.com/2903077918-lgtm/DeepSeek-phone-harness) — a mobile remote for DeepSeek Harness. The constraint I set myself: **zero npm dependencies**. Not because I'm a purist, but because a tool like this should be \"clone and run,\" not \"install 47 packages and hope.\"\n\nHere's the architecture that made it work.\n\n```\nPhone browser (relay.html)\n   │  HTTPS / Tailscale\n   ▼\nAgent (Node, :8788)  ── http server + static page + WS relay\n   │ 127.0.0.1:3080\n   ▼\nDeepSeek Harness gateway (dsh web)\n```\n\nThe Agent is a translator, not a replacement: it speaks REST to the phone and RPC to DSH.\n\nDSH exposes `events.mux`\n\n— a WebSocket stream carrying approvals, questions, and session events in one pipe. Instead of opening one connection per concern, I keep **one persistent connection** and route frames locally:\n\n| Frame | Goes to |\n|---|---|\n`approval/requested` |\npending table → phone approval cards |\n`question/requested` |\npending table → phone answer cards |\n`session/event` |\nring buffer (200/session) → phone stream polling |\n\nThe phone polls `/api/events?afterSeq=N`\n\n— each poll returns only new bytes, which keeps 4G streaming smooth.\n\nThe single most important bug I fixed: DSH agents call `ask_user_question`\n\nthrough a **separate channel** from approvals. If you only listen for `approval/requested`\n\n, the moment the agent asks something the task deadlocks *forever* — it's waiting for a human answer that the phone never shows.\n\nThe answer protocol had to match the Web GUI exactly:\n\n```\n{ \"ok\": true, \"value\": { \"sessionId\": \"...\", \"answer\": { \"answers\": [{ \"id\": \"q1\", \"selected\": [\"...\"] }] } } }\n```\n\nEvery `tool/call`\n\nevent creates a tool card (name, icon, status, **file path extracted from arguments**). Every `tool/result`\n\npairs back by `callId`\n\nand flips the card to done/failed. Tapping expands the full arguments and result.\n\nOne trap: DSH's `tool/result`\n\ncontent is **double-nested** (`content[].content[].text`\n\n) — my first version read one level too shallow and results came back empty. Lesson: capture a real payload before trusting any schema.\n\n`relay.html`\n\n)\"Phone remote for a desktop agent\" sounds like it needs a platform team. It doesn't. The barrier to entry for this whole category just collapsed — **anyone with Node 22+ can build or extend one in a weekend.**\n\nThe code is open (MIT):\n\nQuestions about the architecture? Happy to go deeper in the comments.", "url": "https://wpnews.pro/news/streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies", "canonical_source": "https://dev.to/joey020907/streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies-3728", "published_at": "2026-08-16 07:05:08+00:00", "updated_at": "2026-08-16 07:41:23.670228+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-infrastructure"], "entities": ["DeepSeek Phone Harness", "DeepSeek Harness", "Node", "Tailscale", "GitHub"], "alternates": {"html": "https://wpnews.pro/news/streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies", "markdown": "https://wpnews.pro/news/streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies.md", "text": "https://wpnews.pro/news/streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies.txt", "jsonld": "https://wpnews.pro/news/streaming-a-desktop-agent-to-your-phone-with-zero-npm-dependencies.jsonld"}}