dsh-go-plugin-adb:让 Agent 在手机上给自己开一个 adb shell

前言

在手机上跑 DSH(DeepSeek Harness)时,Agent 想装个 App、看一段日志、截一张屏,通常是两条路:把它们接到一台插着数据线的电脑上走 adb,或者退而求其次用无障碍服务一点点点屏幕。前者需要额外的机器和局域网,后者只能模拟点击,拿不到真正的 shell。

jminn0/dsh-go-plugin-adb 走的是第三条路:插件的 adb 客户端和 adbd 都在同一台手机里,Agent 连的是回环地址 127.0.0.1:5555。不需要 root,不需要电脑,配对一次之后关掉 WiFi、开飞行模式也不影响。下面介绍它的连接方式、安装步骤、一次性配对流程和六个工具的实际能力。

这是什么

由 JMINN0 维护的 DSH 插件,归类在工作流方向。它能给 Agent 提供一个真实的 adb shell,用来在这台手机自身上执行 pm、am、logcat、dumpsys、screencap 等命令,权限是 uid 2000。

版本 2026.1105.1908(年.月日.时.分格式)。验证环境是 nubia REDMAGIC NX809J / Android 16 (SDK 36) / adb 34.0.4 / DSH 0.2.2-rc.1。README 标注稳定性 10/10、工具定义 6/6。它是纯 ESM 的 JS 插件,没有构建步骤,也没有 npm install。

关键点:端口还是 5555,只是地址换成了回环

这一点值得先说清,因为容易和常规 adb 用法混淆。端口没有变,仍然是 5555;变的是「连哪个地址」——常规做法连局域网 IP,这个插件连 127.0.0.1,流量走回环网卡。

带来的结果:

  • 关 WiFi、开飞行模式都不会断;
  • 不依赖路由器,也不需要手机和别的设备在同一局域网;
  • 同一局域网内的其他机器理论上也能连到这个端口,不过仍需持有本机的 adb 私钥才能进入。

adb tcpip 之后 adbd 监听的是整个网络栈,本机连回环因此可行。但这一步会重启 adbd,把 Shizuku 之类的其它客户端一起踢掉,所以插件自己不执行它(详见下文配对部分)。

前提条件和安装

DSH Go 跑在手机内部的 proot Ubuntu 里,插件相关的命令也都在这里执行,不在电脑上执行。先确认几件事:

  1. 一部开了开发者选项的 Android 手机;
  2. 无线调试可用(用于一次性配对);
  3. 首次配对时手机要连着 WiFi——配对走局域网,之后就不再需要了;
  4. proot 环境里能装上 adb 客户端:
apt-get update && apt-get install -y android-tools-adb

装完验证:

adb version
# Android Debug Bridge version 1.0.41

需要注意:DSH Go 升级运行时镜像时,apt 装的东西会全部丢失,adb 需要重装一次。

包从哪来

这个包目前没有发布到 npm registry,只存在于 GitHub 仓库。dsh plugin add 支持本地目录和包名两种目标,这里要用本地目录:

git clone https://github.com/JMINN0/dsh-go-plugin-adb.git
cd dsh-go-plugin-adb
dsh plugin --profile web add .

add 后面的 . 表示当前目录,必须先进入仓库目录再执行,否则会报找不到包。

--profile web 不能省。DSH 可以有多套 profile,手机本机引擎加载的那套叫 web;装到默认 profile,手机 App 不会加载这个插件。

装完要重启引擎

安装只改配置,不重载代码。在 App 里走:设置 → 引擎 → 重启引擎。

重启后工具列表里会多出 6 个 adb_*。不重启就看不到工具,这是最容易漏的一条。

验证装好了

# ① 配置里有没有这个 bundle
python3 -c "
import json
d=json.load(open('$HOME/.dsh/profiles/web/package.json'))
print([b for b in d['dsh']['profile']['bundles'] if 'adb' in b])"
# 期望输出:['dsh-go-plugin-adb']

# ② 引擎有没有加载失败
grep -i "apply failed" ~/.dsh/dsh-app/diagnostics.md 2>/dev/null | grep adb || echo "没有加载错误 ✅"

这两步只证明配置对了,不证明工具能用。最后一关是在 DSH Go 对话里直接说:

用 adb_devices 看看现在连着哪些设备

期望返回:

127.0.0.1:5555  device

看到这行才算真的装好了。如果 Agent 回复「没有这个工具」,回到重启引擎那一步;如果返回「连不上,已试过端口」,回到下面的配对流程。

一次性配对

这是 adb 的标准流程,不是插件特有的。顺序很重要,跳步会报 no devices/emulators found。

  1. 手机上:开发者选项 → 无线调试 → 使用配对码配对设备。屏幕会显示配对用的 IP:端口(例如 192.168.1.100:37123)和 6 位配对码。注意「无线调试」主页面还有一个「IP 地址和端口」(例如 192.168.1.100:41000),那个端口每次开无线调试都会变,和配对端口不是同一个。

  2. 用配对端口配对:

adb pair 192.168.1.100:37123
# 提示 Enter pairing code: 时输入 6 位码
# 成功显示 Successfully paired to ...
  1. 连上无线调试端口:
adb connect 192.168.1.100:41000
adb -s 192.168.1.100:41000 shell id
# uid=2000(shell) ...
  1. 最后连回环,这是插件默认走的路:
adb connect 127.0.0.1:5555
adb -s 127.0.0.1:5555 shell id

配对成功后密钥存在 ~/.android/adbkey,之后不用再配对,重装 adb 也不会丢。

不要用 adb tcpip 5555 去固定端口

早期文档教过这一步,现在明确不要做。README 里记录的实测结果是:执行 adb tcpip 5555 之前 adbd、shizuku_server、GKD 用户服务都存在,执行 8 秒之后 adbd 的 PID 变了(重启了),Shizuku 和 GKD 的服务进程都没了。原因是 adb tcpip 会重启 adbd,而 adbd 是多个客户端共享的资源——Shizuku 在 adb 模式下本身就是 adbd 的一个客户端,重启等于把别人踢下线。

对照组是 adb kill-server + start-server,这两个操作不会动 adbd。插件自己的 ensure() 里也永远不调 tcpip,只「搭车」:用当前可用的连接,或者扫回环端口找出 adbd 现在挂在哪个端口上。

手机重启后怎么恢复

adb tcpip 的设置不持久,重启就丢,但不需要再执行它:

  1. 手机上确认「无线调试」开着;
  2. 插件会自己扫端口找回连接(扫 127.0.0.1:30000-50000,约 6 秒);
  3. 扫不到的话,读出无线调试页面上「IP 地址和端口」那个号,手动 adb connect <IP>:<端口>。

adb_reconnect 工具也能强制重建连接,手机重启后或 adb_devices 显示 offline 时用它。

六个工具

六个工具都是给 Agent 调用的,正常对话时由 Agent 自己决定调用哪个,也可以直接点名。

adb_devices

列出当前连接的设备,无参数。

adb_devices
→ 127.0.0.1:5555  device

adb_shell

执行任意 shell 命令,最常用的一个。command 支持管道、重定向、&&、$();timeoutMs 可设超时,默认 60000 毫秒,范围 1000–600000。

adb_shell { command: "getprop ro.build.version.release" }   → 16
adb_shell { command: "pm list packages | grep tencent" }
adb_shell { command: "dumpsys battery | grep -E 'level|status'" }

实际边界:

  • 输出上限 32MB,超出返回失败但前 32MB 仍在;
  • 输出按 UTF-8 解码,直接 cat 图片这类二进制内容会损坏,要看二进制得用 od / base64 绕一下;
  • 不支持 stdin,top、vi 这类交互式命令用不了;
  • 命令退出码非 0 不算工具失败,输出照常返回,附带退出码说明;
  • 每次调用多一次连接探测,约 50ms,换来断线自愈。

adb_install

安装 APK。

参数 类型 必填 说明
path string 是 设备上的绝对路径,如 /sdcard/app.apk
reinstall boolean 加 -r 保留数据,默认 true
downgrade boolean 加 -d 允许降级,默认 false
adb_install { path: "/sdcard/Download/app.apk" }
→ { text: "安装成功:<路径>" }

path 用的是设备侧路径,不是 proot 里的路径;proot 里的文件要先 push 到设备上。签名不一致时会失败,要么先卸载旧包(数据会丢),要么用同一密钥重新签名。

adb_logcat

读日志尾部,不会留长驻进程。lines 默认 200,最大 20000;filter 支持正则,走 grep -E。

adb_logcat { filter: "FATAL", lines: 100 }
adb_logcat { filter: "AndroidRuntime|FATAL|ANR", lines: 200 }

匹配不到时返回「(最近 N 行里没有匹配 "X" 的内容)」,这是正常结果不是错误。

adb_screencap

截图并保存。两个参数都可选,adb_screencap {} 也合法。

参数 类型 说明
outPath string 设备侧路径,默认 /sdcard/dsh-screencap.png
localPath string 同时拉回到 proot 的本地路径,如 /tmp/shot.png
adb_screencap {}
adb_screencap { outPath: "/sdcard/a.png", localPath: "/tmp/a.png" }
→ { text: "截图已保存:/sdcard/a.png" }

截的是主屏。要截虚拟屏上的内容,用另一个插件 dsh-go-plugin-screen,不是这个。

adb_reconnect

强制重建连接:杀本地 adb server、重启、把无线调试端口迁移到 5555、验证 uid 2000。无参数。

能做什么,不能做什么

README 里列的已实测能力包括:

pm list packages / pm path / pm install / pm uninstall
am start / am force-stop / am broadcast
logcat / dumpsys / getprop / settings
screencap / input / cmd window / cmd notification

cmd package 的 uninstall / clear 会卸包清数据,其余子命令(如 list packages)只是查询。

不能做的几件事:读不了 /data/data(SELinux 挡着 uid 2000);没有 stdin,交互式命令用不了;二进制输出会按 UTF-8 解码而损坏;超时之后设备侧的命令可能还在跑,取消工具不会杀远端进程。

适用场景与注意事项

适合:已经在手机内部 proot 里跑 DSH Go、希望 Agent 直接操作本机、又不想依赖外部电脑或局域网的场景。社区目录页把分类归在工作流。

用之前要看的几点:

  • 插件以当前 dsh 进程的权限运行,adb_shell 没有拦截、没有白名单、没有二次确认,能卸包、删文件、重启、改设置。安装前应先读一遍源码。
  • 许可证是 MIT。GitHub 仓库星标为 0,仓库创建于 2026-11-04,属于很新的项目。
  • 验证只有单机型:Android 16、adb 34.0.4、nubia REDMAGIC NX809J。其他 Android 版本、其他厂商 adbd、其他 adb 版本都未验证。
  • 目前没有发布到 npm registry,只能从 GitHub 装。
  • 安装只改配置,必须手动重启引擎,否则工具不会出现。

下面是目录页和源码地址:

  • 目录页:https://www.skillhub.cn/plugins/JMINN0/dsh-go-plugin-adb
  • GitHub:https://github.com/JMINN0/dsh-go-plugin-adb
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜