🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

手机远程控制台 2903077918-lgtm/DeepSeek-phone-harness

DeepSeek Harness
2903077918-lgtm76 天前有提交已提供中文文档扫描:含敏感能力

手机远程控制 DeepSeek Harness:对话/模型切换/工具审批/终端/文件,4G/5G 任意网络可用

37/100
综合分
14
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装。以下命令仍可尝试,但可能失败。
无法用 dsh 插件命令安装(仓库内无 dsh 插件清单)
git clone https://github.com/2903077918-lgtm/DeepSeek-phone-harness.git
按该仓库 README 的原生方式安装;若作者另行发布了 DSH 插件包,请以其文档为准

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · ui
装得上吗
作者未把包发布到 npm,只能用源码安装
插件本身没问题,是分发方式不同
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 76 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

仓库根目录及浅层子目录均无 dsh 插件清单,无法用 dsh 插件安装命令安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包DeepSeek-phone-harness(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 11:42:10

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 124 处,展示前 113 条)
  • 读取本地文件
    build-en-new.mjs:25
    const body = readFileSync(post.file, 'utf8');
  • 写入本地文件
    build-en-new.mjs:34
    writeFileSync(`${process.env.TEMP}/en-new-${i}.json`, JSON.stringify({ query }));
  • 执行外部命令固定命令
    check-comments.mjs:3
    const TOKEN = execSync('gh auth token', { encoding: 'utf8' }).trim();
  • 请求外部主机api.github.com
    check-comments.mjs:9
    const r = await fetch('https://api.github.com/graphql', {
  • 发起网络请求
    check-comments.mjs:9
    const r = await fetch('https://api.github.com/graphql', {
  • 执行外部命令固定命令
    check-engagement.mjs:4
    const TOKEN = execSync('gh auth token', { encoding: 'utf8' }).trim();
  • 请求外部主机api.github.com
    check-engagement.mjs:8
    const r = await fetch('https://api.github.com/graphql', {
  • 发起网络请求
    check-engagement.mjs:8
    const r = await fetch('https://api.github.com/graphql', {
  • 请求外部主机dev.to
    check-engagement.mjs:39
    const r = await fetch('https://dev.to/api/articles/me/published?per_page=100', { headers: { 'api-key': DEVTO } });
  • 发起网络请求
    check-engagement.mjs:39
    const r = await fetch('https://dev.to/api/articles/me/published?per_page=100', { headers: { 'api-key': DEVTO } });
  • 发起网络请求
    cloud-relay/api/index.ts:71
    const response = await cloudRelay.fetch(request, env);
  • 发起网络请求
    cloud-relay/src/index.ts:29
    async fetch(request: Request, env: Env): Promise<Response> {
  • 发起网络请求
    cloud-relay/src/index.ts:281
    const assets = (env as unknown as { ASSETS?: { fetch(r: Request): Promise<Response> } }).ASSETS;
  • 发起网络请求
    cloud-relay/src/index.ts:285
    const res = await assets.fetch(req);
  • 发起网络请求
    cloud-relay/src/relay.ts:48
    async fetch(request: Request): Promise<Response> {
  • 发起网络请求
    cloud-relay/src/supabase.ts:34
    const r = await fetch(`${this.base}/${table}?${qs.toString()}`, { headers: this.headers() });
  • 发起网络请求
    cloud-relay/src/supabase.ts:46
    const r = await fetch(`${this.base}/${table}?${qs.toString()}`, { headers: this.headers() });
  • 发起网络请求
    cloud-relay/src/supabase.ts:52
    const r = await fetch(`${this.base}/${table}`, {
  • 发起网络请求
    cloud-relay/src/supabase.ts:69
    const r = await fetch(`${this.base}/${table}?${idColumn}=eq.${encodeURIComponent(id)}`, {
  • 发起网络请求
    cloud-relay/test-cloud-relay-service.mjs:18
    return handler.fetch(r, { SUPABASE_URL: 'https://db.test', SUPABASE_SERVICE_ROLE_KEY: 'sd_ro' });
  • 发起网络请求
    cloud-relay/test-cloud-relay-service.mjs:36
    async fetch(url, init = {}) {
  • 发起网络请求
    cloud-relay/test-cloud-relay-service.mjs:83
    if (url.pathname.includes('/rest/v1/')) return mem.fetch(url, init || {});
  • 发起网络请求
    cloud-relay/test-cloud-relay-service.mjs:97
    const preflight = await cloudRelay.fetch(
  • 发起网络请求
    cloud-relay/test-cloud-relay-service.mjs:103
    const noDb = await cloudRelay.fetch(new Request('https://relay.test/v1/poll?deviceId=d'), {});
  • 发起网络请求
    cloud-relay/test-cloud-relay-service.mjs:192
    const noDbStatus = await cloudRelay.fetch(new Request('https://relay.test/v1/status'), {});
  • 发起网络请求
    design/check-ui.js:128
    r = await fetch(path, {
  • 读取本地文件
    design/integrate-pwa-icons.js:4
    let html = fs.readFileSync(path, 'utf8');
  • 读取本地文件
    design/integrate-pwa-icons.js:7
    const svg = fs.readFileSync('C:/Users/Joey/Documents/phone-harness/design/icon-source-blackgold.svg', 'utf8');
  • 读取本地文件
    design/integrate-pwa-icons.js:12
    const png = fs.readFileSync('C:/Users/Joey/Documents/phone-harness/design/icon-512.png');
  • 写入本地文件
    design/integrate-pwa-icons.js:52
    fs.writeFileSync(path, html, 'utf8');
  • 读取本地文件
    design/integrate-pwa-icons.mjs:4
    let html = fs.readFileSync(path, 'utf8');
  • 读取本地文件
    design/integrate-pwa-icons.mjs:7
    const svg = fs.readFileSync('C:/Users/Joey/Documents/phone-harness/design/icon-source-blackgold.svg', 'utf8');
  • 读取本地文件
    design/integrate-pwa-icons.mjs:12
    const png = fs.readFileSync('C:/Users/Joey/Documents/phone-harness/design/icon-512.png');
  • 写入本地文件
    design/integrate-pwa-icons.mjs:52
    fs.writeFileSync(path, html, 'utf8');
  • 读取密钥环境变量
    publish-devto.mjs:5
    const TOKEN = process.env.DEVTO_TOKEN;
  • 读取本地文件
    publish-devto.mjs:64
    const body = readFileSync(`docs/promotion/${f}`, 'utf8');
  • 发起网络请求
    publish-devto.mjs:77
    const r = await fetch(API, {
  • 读取本地文件
    scan-emoji.cjs:2
    const c = fs.readFileSync('web/index.html', 'utf8');
  • 发起网络请求
    src/approval-relay.js:180
    resp = await fetch(baseUrl + '/api/respond', {
  • 写入本地文件
    src/audit.js:23
    mkdirSync(path.dirname(file), { recursive: true });
  • 写入本地文件
    src/audit.js:52
    appendFileSync(file, line, encoding);
  • 写入本地文件
    src/cloud-register.mjs:36
    writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8');
  • 发起网络请求
    src/cloud-register.mjs:67
    const resp = await fetch(host + '/v1/devices/register', {
  • 读取本地文件
    src/cloud-register.mjs:78
    try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return {}; }
  • 读取密钥环境变量
    src/config.js:14
    if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
  • 执行外部命令固定命令
    src/config.js:16
    const regOut = execFileSync('reg', ['query', 'HKCU\\Environment', '/v', 'DEEPSEEK_API_KEY'], { encoding: 'utf8', windowsHide: true });
  • 读取本地文件
    src/config.js:22
    if (existsSync(envFile)) {
  • 读取本地文件
    src/config.js:23
    const line = readFileSync(envFile, 'utf8').split('\n').find(l => l.startsWith('DEEPSEEK_API_KEY='));
  • 读取本地文件
    src/config.js:32
    if (existsSync(CONFIG_PATH)) {
  • 读取本地文件
    src/config.js:33
    return JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
  • 写入本地文件
    src/config.js:36
    writeFileSync(CONFIG_PATH, JSON.stringify(cfg, null, 2));
  • 发起网络请求
    src/dsh-utils.js:19
    const resp = await fetch(WEB_API_BASE + '/api/' + method, {
  • 执行外部命令来自配置/变量
    src/executor.js:28
    const child = spawn(DSH_CMD, ['--profile', 'headless', task], { env, shell: true });
  • 执行外部命令固定命令
    src/executor.js:40
    execFileSync('taskkill', ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true });
  • 读取本地文件
    src/executor.js:149
    if (!existsSync(SESSIONS_PATH)) return;
  • 读取本地文件
    src/executor.js:150
    const arr = JSON.parse(readFileSync(SESSIONS_PATH, 'utf8'));
  • 写入本地文件
    src/executor.js:168
    writeFileSync(SESSIONS_PATH, JSON.stringify([...registry.values()], null, 2));
  • 发起网络请求
    src/executor.js:449
    const resp = await fetch(WEB_API_BASE + '/api/commands/execute', {
  • 发起网络请求
    src/executor.js:585
    const resp = await fetch(WEB_API_BASE + '/api/session.export?sessionId=' + encodeURIComponent(sid));
  • 执行外部命令固定命令
    src/executor.js:796
    ? spawn('node', [bin, ...args], { cwd: dir, windowsHide: true })
  • 执行外部命令来自配置/变量
    src/executor.js:797
    : spawn(bin, args, { cwd: dir, windowsHide: true });
  • 执行外部命令固定命令
    src/executor.js:861
    const where = execFileSync('where', ['codex'], { encoding: 'utf8' });
  • 读取本地文件
    src/executor.js:864
    const content = readFileSync(cmdPath, 'utf8');
  • 读取密钥环境变量
    src/executor.js:877
    const env = String(process.env.GEMINI_API_KEY || '').trim();
  • 读取本地文件
    src/executor.js:880
    const cfg = JSON.parse(readFileSync(path.join(ROOT_DIR, 'config.json'), 'utf8'));
  • 发起网络请求
    src/executor.js:894
    const resp = await fetch(url, {
  • 执行外部命令来自配置/变量
    src/executor.js:915
    const out = execFileSync(cmd, Array.isArray(args) ? args : ['--version'], { encoding: 'utf8', timeout: 8000, windowsHide: true });
  • 读取本地文件
    src/history.js:8
    if (!existsSync(HISTORY_PATH)) return [];
  • 读取本地文件
    src/history.js:9
    try { return JSON.parse(readFileSync(HISTORY_PATH, 'utf8')); } catch { return []; }
  • 写入本地文件
    src/history.js:14
    writeFileSync(HISTORY_PATH, JSON.stringify(h.slice(0, 200), null, 2));
  • 执行外部命令固定命令
    src/transport-lan.js:24
    const child = spawn('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', scriptPath, imagePath], {
  • 执行外部命令固定命令
    src/transport-lan.js:82
    const child = spawn('cmd.exe', ['/d', '/s', '/c', 'chcp 65001 >nul & ' + cmd], {
  • 执行外部命令固定命令
    src/transport-lan.js:104
    try { spawn('taskkill', ['/PID', String(job.child.pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true }); } catch { }
  • 读取本地文件
    src/transport-lan.js:162
    if (existsSync(relayFile)) {
  • 读取本地文件
    src/transport-lan.js:163
    res.end(readFileSync(relayFile, 'utf8'));
  • 读取本地文件
    src/transport-lan.js:173
    if (existsSync(consoleFile)) {
  • 读取本地文件
    src/transport-lan.js:174
    res.end(readFileSync(consoleFile, 'utf8'));
  • 读取本地文件
    src/transport-lan.js:184
    if (existsSync(relayFile)) {
  • 读取本地文件
    src/transport-lan.js:185
    res.end(readFileSync(relayFile, 'utf8'));
  • 写入本地文件
    src/web-push.js:22
    try { writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf8'); } catch { }
  • 发起网络请求
    src/web-push.js:88
    const resp = await fetch(endpoint, {
  • 读取本地文件
    src/web-push.js:106
    if (!existsSync(filePath)) return;
  • 读取本地文件
    src/web-push.js:107
    const arr = JSON.parse(readFileSync(filePath, 'utf8'));
  • 写入本地文件
    src/web-push.js:112
    try { writeFileSync(filePath, JSON.stringify([...subs.values()], null, 2), 'utf8'); } catch { }
  • 读取密钥环境变量
    t-e2e-question.mjs:3
    const TOK = process.env.PH_TOKEN || '请设置环境变量 PH_TOKEN';
  • 发起网络请求
    t-e2e-question.mjs:11
    const r = await fetch(BASE + path, { headers: h, ...opts });
  • 发起网络请求
    t-e2e-question.mjs:25
    const r = await fetch(BASE + '/api/dsh-continue', {
  • 读取本地文件
    test-approvals.mjs:6
    const token = JSON.parse(readFileSync('C:/Users/Joey/Documents/phone-harness/config.json', 'utf8')).token;
  • 发起网络请求
    test-approvals.mjs:20
    const a1 = await (await fetch(base + '/api/approvals', { headers: h })).json();
  • 发起网络请求
    test-approvals.mjs:24
    const bad = await (await fetch(base + '/api/approvals', {
  • 发起网络请求
    test-approvals.mjs:30
    const noauth = await (await fetch(base + '/api/approvals', { headers: { 'content-type': 'application/json' } })).json();
  • 读取本地文件
    test-core-modules.mjs:73
    const lines = readFileSync(tmpFile, 'utf8').trim().split('\n').map((l) => JSON.parse(l));
  • 删除本地文件
    test-core-modules.mjs:76
    if (existsSync(tmpFile)) unlinkSync(tmpFile);
  • 读取本地文件
    test-core-modules.mjs:76
    if (existsSync(tmpFile)) unlinkSync(tmpFile);
  • 读取本地文件
    test-e2e.mjs:6
    const token = JSON.parse(readFileSync('C:/Users/Joey/Documents/phone-harness/config.json', 'utf8')).token;
  • 发起网络请求
    test-e2e.mjs:11
    const status = await (await fetch(base + '/api/status', { headers })).json();
  • 发起网络请求
    test-e2e.mjs:17
    const execResp = await (await fetch(base + '/api/exec', {
  • 发起网络请求
    test-e2e.mjs:27
    const hist = await (await fetch(base + '/api/history', { headers })).json();
  • 发起网络请求
    test-goal-api.mjs:147
    const resp = await fetch(`http://127.0.0.1:${port}/api/session.create`, {
  • 读取本地文件
    test-integration.mjs:6
    const token = process.argv[2] || JSON.parse(readFileSync('C:/Users/Joey/Documents/phone-harness/config.json', 'utf8')).token;
  • 发起网络请求
    test-integration.mjs:14
    const r = await fetch(base + path, { ...opts, headers: h });
  • 发起网络请求
    test-integration.mjs:25
    const page = await (await fetch(base + '/', { headers: { authorization: 'Bearer ' + token } })).text();
  • 读取密钥环境变量
    test-utils/agent-ready.mjs:12
    if (process.env.RELAY_TOKEN) return process.env.RELAY_TOKEN;
  • 读取本地文件
    test-utils/agent-ready.mjs:14
    const cfg = JSON.parse(readFileSync(pathToFileURL(process.cwd() + '/config.json')));
  • 发起网络请求
    test-utils/agent-ready.mjs:28
    const r = await fetch(AGENT_BASE + '/api/status', { headers, signal: ctrl.signal });
  • 读取本地文件
    verify-v2.mjs:4
    const token = JSON.parse(readFileSync('C:/Users/Joey/Documents/phone-harness/config.json', 'utf8')).token;
  • 发起网络请求
    verify-v2.mjs:8
    const page = await (await fetch('http://127.0.0.1:8788/', { headers: { authorization: 'Bearer ' + token } })).text();
  • 发起网络请求
    verify-v2.mjs:11
    const s = await (await fetch('http://127.0.0.1:8788/api/dsh-sessions?withCount=true', { headers: h })).json();
  • 发起网络请求
    verify-v2.mjs:15
    const ev = await (await fetch('http://127.0.0.1:8788/api/events?sessionId=' + (s.items?.[0]?.sessionId || '') + '&afterSeq=0', { headers: h })).json()
  • 发起网络请求
    verify-v2.mjs:20
    const c = await (await fetch('http://127.0.0.1:8788/api/cancel', { method: 'POST', headers: h, body: JSON.stringify({ sessionId: 'x' }) })).json();
  • 写入本地文件
    video/capture.mjs:10
    mkdirSync(OUT, { recursive: true });
  • 读取密钥环境变量
    video/capture.mjs:12
    const TOKEN = process.env.PH_TOKEN || 'eaa8e4ef031fa3acb5c2b60e097408a7edd07d671f6457a04c19294514708b59';
  • 写入本地文件
    video/capture.mjs:83
    writeFileSync(OUT + 'layout.json', JSON.stringify(layout, null, 2));
扫描于 2026/11/2(1 天前) · 范围 全包扫描 · 来源 GitHub 仓库
涉及外部主机:api.github.com、dev.to

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 远程控制对话
  • 切换模型
  • 工具审批

README

由 DeepSeek 最新模型翻译生成
🐋 DeepSeek Phone Harness 把 DeepSeek Harness 装进口袋 —— 手机远程控制你的电脑智能体 DeepSeek Harness on your phone. Remote-control your desktop agent from anywhere — 4G/5G, no WiFi needed. Node.js · 零依赖 · Cloudflare Workers · WebCrypto E2EE ▶ 点击上方图片观看 31 秒宣传片 · Phone in hand. Computer at your command. 手机在手,电脑随行 ⭐ 觉得有用?点个 Star,就是对这个开源项目最大的支持 三分钟跑起来 git clone https://github.com/2903077918-lgtm/DeepSeek-phone-harness cd DeepSeek-phone-harness && cp config.example.json config.json node agent.mjs --mode=both 手机 Tailscale 连上电脑,打开 http://<电脑IP>:8788/,开工。任意网络(4G/5G/WiFi)都能用,不需要公网 IP。 这是什么?
展开完整文档(README 剩余 1 节)
🐋 DeepSeek Phone Harness

把 DeepSeek Harness 装进口袋 —— 手机远程控制你的电脑智能体

DeepSeek Harness on your phone. Remote-control your desktop agent from anywhere — 4G/5G, no WiFi needed.

Node.js · 零依赖 · Cloudflare Workers · WebCrypto E2EE

▶ 点击上方图片观看 31 秒宣传片 · Phone in hand. Computer at your command. 手机在手,电脑随行

⭐ 觉得有用?点个 Star,就是对这个开源项目最大的支持

三分钟跑起来
git clone https://github.com/2903077918-lgtm/DeepSeek-phone-harness
cd DeepSeek-phone-harness && cp config.example.json config.json
node agent.mjs --mode=both

手机 Tailscale 连上电脑,打开 http://<电脑IP>:8788/,开工。任意网络(4G/5G/WiFi)都能用,不需要公网 IP。

这是什么?

DeepSeek Phone Harness 是一个手机端远程控制系统,让你在任何网络(4G/5G / WiFi / 公网)下,用手机操作电脑上的 DeepSeek Harness —— 发任务、看流式输出、批准工具调用、回答智能体提问、跑终端命令、翻文件、查任务记录,全都在手掌心里完成。

界面参考开源项目 codex-relay 的设计语言,对话体验对标 DeepSeek Harness Web GUI:模型选择、图片附件、思考过程折叠、工具调用卡片(带路径/状态/详情)、审批与提问卡,一样不少。

✨ 核心特性

| 能力 | 说明 |
| 💬 对话 | 多轮会话、Markdown 渲染、分步 step 卡片、会话历史 |
| ⚡ 实时跟随 | SSE 长连接推送(对标 DSH events.mux),打开会话即实时收事件——消息/工具/统计自动刷新,非轮询 |
| 🎛 模型/模式切换 | 顶栏胶囊 → 底部面板,多服务商 / 多模型即时切换(如 deepseek-v4-flash)|
| 🖼 图片 OCR | 手机发图 → 电脑端 Windows 自带 OCR 转文本(免费、本地、不耗 token)拼进任务 |
| 🧠 思考过程 | 智能体推理内容折叠展示,流式时自动展开 |
| 🔧 工具卡片 | 每一步操作一张卡:图标 + 状态(执行中/完成/失败)+ 文件路径参数 + 点击展开完整参数与结果 |
| 🛡 审批与提问 | 危险操作审批卡(允许/拒绝)、ask_user_question 提问卡(选项/自定义回答/跳过),不回答任务不卡死 |
| 🪜 轨迹面板 | 对话/轨迹切换:ASSISTANT/TOOL 时间线 + 搜索 + Duration 列 |
| ⚙️ 设置同步 | 繁忙时 Enter(排队/插话)、语言(中/英)、新会话默认权限、模型提供方增删改——真写入电脑端 DSH 设置 |
| ⌨️ 终端 | 手机远程执行电脑命令,流式输出、可停止、支持 UTF-8 中文 |
| 📋 任务列表 | 历史任务回看、展开 stdout/stderr、一键重跑、复制输出 |
| 📁 文件与代码 | 浏览电脑目录、查看文件内容(代码界面)、打开 DSH 配置文件 |
| 🗂 工作区/会话 | 多项目工作区切换、会话新建 / 重命名 / 删除 / 分支(长按或 ⋮ 操作面板)|
| 🎨 视觉 | 对标 DSH 桌面端:中性灰胶囊 + 单一强调蓝、任务清单可收起、默认深色 |
| 📡 任意网络 | Tailscale 4G/5G 直连电脑,或经 Cloudflare Worker 云端入口 |
| 🔒 安全 | Bearer Token 鉴权、审批/提问需手机确认、E2EE 可选(云端通道) |

🏗 架构

┌──────────────┐ HTTPS / Tailscale ┌─────────────────────────────┐
│ 手机浏览器 │ ──────────────────────────▶ │ Agent(电脑端, Node, 8788) │ │ relay.html │ │ ┌─────────────────────────┐ │ │ codex-relay │ │ │ transport-lan(HTTP+鉴权)│ │ │ 风格 UI │ ◀── SSE 推送 /api/events.stream ─│ │ executor(DSH RPC 封装) │ │ └──────────────┘ │ │ approval-relay(WS 中继) │ │ │ │ └───────────┬─────────────┘ │ │ 可选:Cloudflare Worker 静态托管 │ │ 127.0.0.1:3080 │ ▼ │ ▼ │ ┌──────────────┐ │ ┌─────────────────────────┐ │ │ relay.axyntara│ ◀── 手机直接访问 Worker ───── │ │ DeepSeek Harness 网关 │ │ │ .cn (Worker) │ │ │ (dsh web, 会话/工具) │ │ └──────────────┘ │ └─────────────────────────┘ │ └─────────────────────────────┘

- Agent:纯 Node.js 实现,零第三方依赖。HTTP 传输层(8788)+ DSH Web API RPC 封装(127.0.0.1:3080)+ 审批/事件中继(events.mux WebSocket)。
- 前端:单文件 relay.html,无框架,深色 codex-relay 设计系统(#191919 / 金色鲸鱼标识)。
- 云端:cloud-relay/ 为 Cloudflare Worker(可选),托管前端静态资源并支持 E2EE 任务通道(Supabase 可选)。

🚀 快速开始

前置条件

- 电脑已安装并运行 DeepSeek Harness Web(dsh web,监听 127.0.0.1:3080)
- Node.js 22+(Agent 使用全局 fetch / WebSocket)

1. 配置并启动 Agent

1) 复制配置模板并填写访问令牌
cp config.example.json config.json

2) 启动(LAN 8788 + 云端轮询)
node agent.mjs --mode=both

启动后终端会打印本机地址与 Token:

deepseekharness-relay Agent v0.4.0
控制台: http://<本机IP>:8788 Token: <你的访问令牌>

2. 手机连接

1. 手机与电脑同一 Tailscale 网络(4G/5G 也可直连):打开 http://<电脑Tailscale IP>:8788/
2. 或在任意网络访问云端入口:https://relay.axyntara.cn/
3. 输入服务器地址 + 访问令牌 → 连接 → 选择工作区 → 开始对话

3. 云端部署(可选)

cd cloud-relay
wrangler deploy # 需要 Cloudflare 账号;静态资源来自 ../web

🗂 目录结构

phone-harness/
├── agent.mjs # Agent 入口(--mode=lan|cloud|both) ├── src/ │ ├── transport-lan.js # 局域网 HTTP 传输层(8788,REST + 静态页) │ ├── executor.js # DSH RPC 封装:会话/历史/审批/模型/终端等 │ ├── approval-relay.js# events.mux WebSocket 中继(审批+提问+事件流) │ ├── dsh-utils.js # RPC/事件归一化工具 │ └── config.js # 端口 / 版本 / API Key 解析 ├── web/ │ ├── relay.html # ★ 手机端主界面(单文件,codex-relay 风格) │ ├── index.html # 与 relay.html 同步(云端根路径) │ └── console.html # 旧版局域网控制台(归档) ├── cloud-relay/ # Cloudflare Worker(静态托管 + 可选 E2EE 通道) └── t-e2e-question.mjs # 端到端测试脚本

🔐 安全说明

- 所有 API 均要求 Authorization: Bearer <token>,token 由 config.json 管理且 不入库(见 .gitignore)。
- 工具调用采用 审批制:危险操作会先在手机端弹出「需要授权」卡片,由你决定允许/拒绝。
- Agent 反问用户时必须手机回答(提问卡),否则任务会等待,不会擅自继续。
- 建议仅在 Tailscale 等私有网络暴露 8788;公网请经 Cloudflare Worker 并开启 E2EE。

🗺 Roadmap

- [x] 对话 / 流式 / 模型选择 / 图片附件
- [x] 工具卡片(路径·状态·详情)/ 思考过程 / 审批 / 提问
- [x] 终端 / 任务列表 / 文件与代码 / 会话管理(含分支)
- [x] SSE 实时跟随(长连接推送,替代轮询)
- [x] 轨迹面板(ASSISTANT/TOOL + Duration + 搜索)
- [x] 设置同步(Enter 行为 / 语言 / 默认权限 / 提供方 → DSH)
- [x] 图片 OCR 转文本(免费本地不耗 token)
- [x] 视觉对标 DSH 桌面端(中性灰 + 单一强调蓝)
- [ ] 移动端 PWA(离线壳 + 推送)
- [ ] 会话「继续在电脑端打开」
- [ ] DSH WebSocket-Remote 客户端(解锁权限切换 / 消息反馈 / commands)
- [ ] 多设备 / 多账号

📄 License

MIT © 2026 项目作者

DeepSeek Harness 手机远程控制 —— 让 Agent 随时随地在手边工作。
预览分享卡片长什么样 ↓2903077918-lgtm/DeepSeek-phone-harness
分享 · DeepSeek Harness Hub
2903077918-lgtm/DeepSeek-phone-harness

手机远程控制 DeepSeek Harness:对话/模型切换/工具审批/终端/文件,4G/5G 任意网络可用

这是你的仓库?认领条目

回到顶部