dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH plugin: talk to your DeepSeek Harness from your phone. Send text and photos, read replies as they stream — across any network via your own Tailscale tailnet. 用手机跟电脑上的 DSH 对话,不同 Wi-Fi 也能用。
v1.1.2
用手机跟电脑上的 DSH 对话:发文字、拍照、传图,回复实时回显。不同 Wi-Fi 也能用。
Official Web and the official desktop app are both supported. Official
dsh webuses profileweb. The official desktop EXE (DeepSeek Harness 0.2.0-rc.2) uses profiledesktop. Install and Apply them separately.No public server, no third-party relay. The phone and the PC meet on a private WireGuard network that you create with your own Tailscale account. Nothing is exposed to the public internet.
Node.js ≥18. Android 7.0 (API 24) or newer for the native app; any modern browser also works.
| File | Goes on | Size |
|---|---|---|
phone-bridge-1.1.2.apk | phone | 249 KB |
phone-bridge-plugin-1.1.2.zip | PC (DSH plugin) | 5 MB |
Three steps:
# 1. Install the plugin on the PC, then restart DSH
dsh plugin --profile desktop add github:chs-1108/dsh-phone-bridge
# 2. Install Tailscale on both sides and sign in
# PC https://tailscale.com/download phone: Play Store
# 3. Install the APK, open it, scan the QR in DSH -> Settings -> Phone Sync
adb install -r phone-bridge-1.1.2.apk
The APK is a generic build with no baked-in address: every user scans their own PC's QR code and connects to their own PC.
phone-bridge is a plugin for the official DeepSeek Harness that puts your DSH session in your pocket. The phone opens a small web app (or a native Android shell), sends text and photos into a real DSH session, and reads the Agent's reply as it streams back.
Phone (WebView / browser)
│ HTTP over Tailscale — encrypted WireGuard, peer to peer
▼
DSH webServer ──▶ /phone-bridge prefix route ──▶ sessionController ──▶ model
It consumes only DSH's public webServer and sessionController services. No core patches, no forked host.
100.x.y.z address, and the URL never changes when you switch between Wi-Fi and cellular./api/prefs, or let it create one lazily and reuse it.sessionController.follow({assistantStream: true}) and the phone polls incremental results, so long answers appear while they are being written.onShowFileChooser into a system chooser with a camera entry, writes the capture through MediaStore, and hands the WebView a readable URI. Text and up to 8 images per message.setup-phone-bridge.ps1 installs Tailscale, patches the listen address, registers the plugin, builds the APK, and pushes both apps to a USB-attached phone. It needs your Tailscale account and nothing else.aapt2 + d8 + zipalign + apksigner in about ten seconds.| DSH | 0.2.0 or newer, desktop EXE or dsh web |
| Node.js | ≥ 18 |
| Host OS | Windows 10/11 (the deploy script is PowerShell) |
| Phone | Android 7.0+ for the APK, or any modern browser |
| Account | Your own free Tailscale account |
Optional, only for building the APK: JDK 17 and Android SDK build-tools 34.0.0. Optional, only for pushing to a phone over USB: adb.
Download the repository, then double-click install.cmd. A window opens; follow it.
Only two things need you:
Everything else is automatic: install Tailscale, change the listen address, register the plugin, create the pairing key, detect the port, build the APK, push and install it, grant the camera permission, run the health check.
Details in 安装说明.md (Chinese). Prefer a terminal?
install-gui.ps1 -Console. Prefer the command line? Use the steps below.
Everything lives on one page: Releases → v1.1.2
| File | Goes on | Size |
|---|---|---|
phone-bridge-1.1.2.apk | the phone | 249 KB |
phone-bridge-plugin-1.1.2.zip | the PC (DSH plugin) | 0.9 MB |
# From GitHub
dsh plugin --profile desktop add github:chs-1108/dsh-phone-bridge
# Or from the downloaded zip
dsh plugin --profile desktop add "D:\path\to\phone-bridge-plugin-1.1.2.zip"
Restart DSH, then open Settings → Phone Sync. A QR code means it worked.
DSH's web server binds 127.0.0.1 by default — it is meant for the local browser
only, so requests from the phone over Tailscale are refused outright.
Open Settings → Phone Sync. If a red block reports the server is listening on loopback only, click Fix it for me:
🔴 DSH listens on loopback only
The web server accepts local connections only, so requests from the phone
never arrive.
[ Fix it for me ] edits the config; DSH must restart after
The plugin locates the current profile's cordis.patch.yml, changes host, and
leaves a timestamped backup beside it. Then fully quit DSH and reopen it.
Edit the profile's cordis.patch.yml:
Windows: %USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml
macOS/Linux: ~/.dsh/profiles/desktop/cordis.patch.yml
Add this (if a webserver entry already exists, change only host):
- id: webserver
name: '@deepseek-ai/dsh-host-webserver'
inject: [webStartup]
config:
host: '0.0.0.0'
port: !!js ctx.webStartup.port ?? 3080
compression: gzip
compressionLevel: 1
compressionThresholdBytes: 1024
nameis@deepseek-ai/dsh-host-webserveror@deepseek-ai/dsh-web-serverdepending on the DSH version. The automatic fix reuses whatever the file already says.
Fully quit DSH and reopen it (closing the window is not enough), then:
netstat -ano | findstr LISTENING | findstr 19387
| You see | Meaning |
|---|---|
0.0.0.0:19387 or [::]:19387 | ✅ the phone can reach it |
127.0.0.1:19387 | ❌ local only, the phone will always fail |
From v1.1.2 the plugin detects this itself and offers the repair inline, so you never have to guess.
curl http://127.0.0.1:19387/phone-bridge/health
# {"ok":true,"name":"phone-bridge","version":1}
Install Tailscale on both sides and sign in (same account, or share the devices with each other). PC builds are at tailscale.com/download; the phone app is on the Play Store.
adb install -r phone-bridge-1.1.2.apk
No cable? Copy the file over and tap it. Opening the app shows the pairing screen.
The QR code is generated on demand: it encodes this PC's Tailscale address plus a random token, recomputed every time the page loads. Nothing to configure.
You can also type the two values by hand: the pairing address (http://100.x.y.z:19387/phone-bridge) and the pairing token shown on the same settings page.
In the app: Settings → Clear pairing (scan again). That returns to the pairing screen; scan the new PC's code.
Type in the box at the bottom (or tap Camera / Gallery to send a photo), then send. The reply streams back. Sessions at the top switches the target session; Settings toggles whether the reasoning stream is included.
Open the pairing address in the phone's browser instead. Same features; photos go through the standard file picker.
dsh pluginDSH ships a plugin subcommand that forwards its arguments to pnpm inside the profile directory. That is the supported install path for every third-party plugin, this one included:
# Desktop app (DeepSeek Harness 0.2.0-rc.2)
dsh plugin --profile desktop add github:chs-1108/dsh-phone-bridge
# Official web build
dsh plugin --profile web add github:chs-1108/dsh-phone-bridge
Add it from a local clone instead, if you already have one:
dsh plugin --profile desktop add /path/to/dsh-phone-bridge
Then append the bundle name to dsh.profile.bundles in the profile's package.json, and restart DSH:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-phone-bridge"
]
}
}
}
Confirm it loaded:
curl http://127.0.0.1:19387/phone-bridge/health
# {"ok":true,"name":"phone-bridge","version":1}
Once the plugin is in, setup-phone-bridge.ps1 does the rest of the environment — Tailscale, the listen address, the APK, and the phone over USB. On the PC, in an elevated PowerShell:
Set-ExecutionPolicy -Scope Process Bypass -Force
.\setup-phone-bridge.ps1
It walks nine steps and stops to ask you only twice:
Connect the phone by USB with USB debugging enabled beforehand if you want the script to install the Tailscale and phone-bridge APKs for you. It also pre-grants the camera permissions.
When it finishes you get a URL like:
http://100.x.y.z:19387/phone-bridge/?token=<48 hex chars>
Open it on the phone, or launch the installed DSH 手机同步 app — it is already paired.
?token=is a query parameter on purpose. A#fragmentnever reaches the server, so#t=...would fail authorization.
.\setup-phone-bridge.ps1 -SkipPhone # PC side only
.\setup-phone-bridge.ps1 -SkipApk # reuse the last APK build
.\setup-phone-bridge.ps1 -SkipTailscale # already installed and signed in
.\setup-phone-bridge.ps1 -SkipRestart # restart DSH yourself later
.\setup-phone-bridge.ps1 -Port 19387 # pin the port
If dsh plugin is not available in your build, wire it up by hand:
1. Put the plugin where DSH can find it
git clone https://github.com/chs-1108/dsh-phone-bridge.git
mkdir -p ~/.dsh/phone-bridge
cp -r dsh-phone-bridge/{index.js,client.js,package.json,cordis.patch.yml,web,vendor} ~/.dsh/phone-bridge/
2. Register it in the profile
Link the package into the profile's node_modules and add a row to cordis.patch.yml:
- id: phone-bridge
name: dsh-phone-bridge
3. Make the webServer reachable
The server listens on 127.0.0.1 by default, so the phone cannot reach it. Override the host in the same cordis.patch.yml (profile patch is the highest-precedence layer):
- id: webserver
name: '@deepseek-ai/dsh-web-server'
config:
host: '0.0.0.0'
port: !!js ctx.webStartup.port ?? 3080
compression: gzip
compressionLevel: 1
compressionThresholdBytes: 1024
inject: [webStartup]
4. Restart DSH, then read the token
cat ~/.dsh/phone-bridge/state.json
Just install phone-bridge-1.1.2.apk from the release - no build step needed.
It opens on the pairing screen; scan the QR or type the two values. The pairing is stored on the phone, so later launches go straight to the chat.
The app runs a foreground service (partial wake lock plus a low-priority notification), so the screen going off or the app going to the background does not drop the connection. Tailscale runs as a background service on both sides too, so closing a window never drops the link and a reboot reconnects on its own.
Needs JDK 17 and the Android SDK (build-tools 34, platform 34):
$env:PHONE_BRIDGE_JAVA_HOME = "C:\path\to\jdk-17"
$env:PHONE_BRIDGE_SDK = "C:\path\to\Android\Sdk"
$env:PHONE_BRIDGE_BUILD_TOOLS = "34.0.0"
pwsh -File build-apk.ps1
# -> apk-out/phone-bridge.apk
With no arguments you get the generic build: each user scans their own PC's code. That is the one to publish.
To skip scanning while debugging, compile the address in:
.\build-apk.ps1 -BaseUrl "http://100.x.y.z:19387/phone-bridge" -Token "<your token>"
It opens straight into the chat, but it only ever connects to that one PC, so do not publish it. Clearing the pairing from inside the app still works and is not undone by the baked-in values.
| Piece | Role |
|---|---|
index.js | Host half. Registers a prefix route on webServer, drives sessionController.prompt() and .follow(), mints the token. |
client.js | Client half. Injects the settings panel into the DSH web UI. |
web/app.html | The single-page app the phone loads. No framework, ~16 KB. |
apk/ | Native Android shell: SetupActivity, MainActivity (WebView + file chooser + camera), ScanActivity (zxing), CaptureProvider. |
setup-phone-bridge.ps1 | Nine-step deployment for both ends. |
build-apk.ps1 | Gradle-free APK build. |
Authorization is either loopback (the PC talking to itself) or a 48-hex-character token compared in constant time. Sessions are resolved lazily: if the stored sessionId still exists it is reused, otherwise a new one is created and persisted.
The capture path deliberately writes into the shared MediaStore rather than a private ContentProvider. Every camera app can already write there, which removes the cross-process URI grant that silently fails on several OEM ROMs.
Full detail, including the nine capture bugs and why each fix was needed, is in IMPLEMENTATION.md. Deployment walkthrough and troubleshooting: SETUP.md.
This plugin ships no server address, token, or account. Values are injected at build time by build-apk.ps1 from -BaseUrl / -Token, and the committed BuildDefaults.java and network_security_config.xml stay blank apart from loopback.
Traffic stays inside your own tailnet, end-to-end encrypted, with nothing proxied through a third party. The plugin opens no additional outbound connections.
Treat the token like a password: anyone holding it can talk to your DSH. Rotate it by deleting token from ~/.dsh/phone-bridge/state.json and restarting.
| Symptom | Cause | Fix |
|---|---|---|
{"ok":false,"error":"unauthorized"} | Token not transmitted | Use ?token=, never #t= |
| Phone cannot connect | Server bound to loopback | Set host: '0.0.0.0' in cordis.patch.yml, restart |
web boot: 1 entry did not activate | Client registration id ≠ package name | Must be dsh-phone-bridge |
| Capture saves but the photo never appears | Provider unreachable by the camera app | Use the MediaStore path in MainActivity |
adb devices shows unauthorized | Debugging prompt not accepted | Unlock the phone, tap Allow |
| APK install fails on signature | Different signing key | adb uninstall com.dsh.phonebridge first |
Check the plugin is alive:
curl http://127.0.0.1:19387/phone-bridge/health
# {"ok":true,"name":"phone-bridge","version":1}
node test/loader-import.mjs # client half registers under the package name
node test/composition.mjs # host half composes
node test/host.smoke.mjs # route and auth smoke tests
Rebuild the distributable:
.\build-zip.ps1
MIT — see LICENSE.