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

远程访问 Dynesshely/dsh-plugin-remote

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

一个简单的 dsh-plugin,给每个 dsh 应用加上远程访问能力。

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

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Dynesshely/dsh-plugin-remote
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · ui
装得上吗
本站尚未做安装检查
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 8 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 60 处,展示前 51 条)
  • 读取本地文件
    index.js:171
    const parsed = JSON.parse(readFileSync(file, 'utf8'))
  • 写入本地文件
    index.js:180
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    index.js:181
    writeFileSync(file, `${JSON.stringify({ token, createdAt: new Date().toISOString() }, null, 2)}\n`, { mode: 0o600 })
  • 读取本地文件
    index.js:435
    if (!existsSync(DIST_ROOT)) return undefined
  • 读取本地文件
    index.js:441
    const info = statSync(target)
  • 读取本地文件
    index.js:444
    body: readFileSync(target),
  • 读取本地文件
    index.js:517
    return readFileSync(DIST_INDEX)
  • 读取本地文件
    index.js:530
    if (!existsSync(DIST_ROOT)) {
  • 发起网络请求
    index.js:1309
    const upstream = await fetch(parsed, {
  • 读取本地文件
    host/files.js:89
    info = statSync(target)
  • 读取本地文件
    host/files.js:99
    names = readdirSync(target, { withFileTypes: true })
  • 读取本地文件
    host/files.js:112
    stat = statSync(child)
  • 读取本地文件
    host/files.js:149
    info = statSync(target)
  • 读取本地文件
    host/files.js:160
    const bytes = readFileSync(target)
  • 读取本地文件
    host/files.js:190
    info = statSync(target)
  • 写入本地文件
    host/files.js:246
    mkdirSync(source, { recursive: false })
  • 写入本地文件
    host/files.js:253
    writeFileSync(source, '', { flag: 'wx' })
  • 写入本地文件
    host/files.js:264
    renameSync(source, target)
  • 删除本地文件
    host/files.js:271
    rmSync(source, { recursive: body?.recursive === true, force: false })
  • 写入本地文件
    host/files.js:281
    writeFileSync(source, bytes)
  • 写入本地文件
    host/files.js:288
    writeFileSync(source, body.text, 'utf8')
  • 写入本地文件
    host/files.js:367
    writeFileSync(target, bytes)
  • 读取本地文件
    host/hot.js:30
    const info = statSync(fileURLToPath(url))
  • 读取本地文件
    host/hot.js:62
    const info = statSync(fileURLToPath(url))
  • 写入本地文件
    host/hot.js:193
    mkdirSync(dirname(path), { recursive: true })
  • 写入本地文件
    host/hot.js:194
    appendFileSync(path, line, 'utf8')
  • 执行外部命令来自配置/变量
    host/hot.js:211
    const child = spawn(command, args, {
  • 读取本地文件
    host/hot.js:238
    if (existsSync(readyPath)) return Date.now() - startedAt
  • 读取本地文件
    host/hot.js:284
    if (!existsSync(script)) return { skipped: true, note: `skipped: helper script missing (${script})` }
  • 写入本地文件
    host/hot.js:289
    mkdirSync(readyDir, { recursive: true })
  • 读取本地文件
    host/hot.js:300
    ...(existsSync(vbsLauncher)
  • 删除本地文件
    host/hot.js:315
    rmSync(readyPath, { force: true })
  • 删除本地文件
    host/hot.js:352
    rmSync(readyPath, { force: true })
  • 读取本地文件
    host/hot.js:395
    for (const name of readdirSync(root)) {
  • 执行外部命令来自配置/变量
    host/hot.js:462
    const child = spawn(runtime, args, { detached: true, stdio: 'ignore', windowsHide: true, cwd: process.cwd(), env })
  • 读取本地文件
    host/model-meta.js:27
    const config = JSON.parse(readFileSync(join(homedir(), '.dsh', 'remote-control', 'usage.json'), 'utf8'))
  • 读取本地文件
    host/model-meta.js:34
    const text = readFileSync(join(homedir(), '.dsh', '.credentials.yaml'), 'utf8')
  • 读取本地文件
    host/model-meta.js:45
    const config = JSON.parse(readFileSync(join(homedir(), '.dsh', 'remote-control', 'usage.json'), 'utf8'))
  • 发起网络请求
    host/model-meta.js:60
    const response = await fetch(`${gatewayBase()}/models`, {
  • 写入本地文件
    host/relaunch.mjs:57
    mkdirSync(dirname(path), { recursive: true })
  • 写入本地文件
    host/relaunch.mjs:58
    appendFileSync(path, `[${new Date().toISOString()}] ${message}\n`)
  • 读取本地文件
    host/relaunch.mjs:145
    if (plan.exe === '' || !existsSync(plan.exe)) {
  • 执行外部命令来自配置/变量
    host/relaunch.mjs:163
    const child = spawn(plan.exe, args, {
  • 读取本地文件
    host/usage.js:63
    const parsed = JSON.parse(readFileSync(configFile(), 'utf8'))
  • 写入本地文件
    host/usage.js:94
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    host/usage.js:95
    writeFileSync(file, `${JSON.stringify({ gateway: config.gateway, providers: config.providers }, null, 2)}\n`, { mode: 0o600 })
  • 读取本地文件
    host/usage.js:107
    const text = readFileSync(credentialFile(), 'utf8')
  • 发起网络请求
    host/usage.js:337
    const response = await fetch(gateway.url, {
  • 发起网络请求
    host/usage.js:379
    const response = await fetch(provider.url, {
  • 发起网络请求
    ui/src/lib/api.js:69
    response = await fetch(path, init)
  • 发起网络请求
    ui/src/lib/api.js:94
    const response = await fetch('/api/login', {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

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

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

README

由 DeepSeek 最新模型翻译生成
DeepSeek Remote DeepSeek Remote 是 [DeepSeek Harness][dsh] 的插件。它把桌面应用的对话发布成网页,让你在手机、平板或同一网络下的另一台机器上查看回答、发送提示词、管理会话。它直接和所在 Host 通信——不需要单独的守护进程,不经过云端中转——每个请求都要用你自己的 token 验证。 功能 - 对话实时同步。 对话记录、流式回答、推理链、工具调用和分组工具运行都通过 Server-Sent Events 送达。 - 随处发送提示词。 Markdown 编辑器带实时预览(CodeMirror 6),支持图片附件。 - 会话管理。 新建、重命名、置顶、分叉、归档——改动双向同步,手机上置顶的会话会出现在桌面端。 - 模型控制。 选择器带真实品牌图标,另有目录浏览器,展示上下文窗口和可用的推理强度。 - 余额与用量。 一张卡片,数据来自聚合网关端点或各厂商 API key。 - 从页面重启或退出 DSH:调用桌面外壳自带的恢复对话框,在 Windows 上自动确认「重启」按钮(设 RC_AUTO_CONFIRM_RESTART=0 可关闭)。 - 图片真的能加载。 同源图片代理同时解析真实 URL 和工作区路径,支持点击放大。 - 为手机而生。
展开完整文档(README 剩余 1 节)
DeepSeek Remote

DeepSeek Remote 是 [DeepSeek Harness][dsh] 的插件。它把桌面应用的对话发布成网页,让你在手机、平板或同一网络下的另一台机器上查看回答、发送提示词、管理会话。它直接和所在 Host 通信——不需要单独的守护进程,不经过云端中转——每个请求都要用你自己的 token 验证。

功能

- 对话实时同步。 对话记录、流式回答、推理链、工具调用和分组工具运行都通过 Server-Sent Events 送达。
- 随处发送提示词。 Markdown 编辑器带实时预览(CodeMirror 6),支持图片附件。
- 会话管理。 新建、重命名、置顶、分叉、归档——改动双向同步,手机上置顶的会话会出现在桌面端。
- 模型控制。 选择器带真实品牌图标,另有目录浏览器,展示上下文窗口和可用的推理强度。
- 余额与用量。 一张卡片,数据来自聚合网关端点或各厂商 API key。
- 从页面重启或退出 DSH:调用桌面外壳自带的恢复对话框,在 Windows 上自动确认「重启」按钮(设 RC_AUTO_CONFIRM_RESTART=0 可关闭)。
- 图片真的能加载。 同源图片代理同时解析真实 URL 和工作区路径,支持点击放大。
- 为手机而生。 玻璃质感 UI、提示词栏、对话记录懒渲染,还有滚动补偿——在视口上方渲染的内容不会把读者甩出位置。
- token 验证,单进程。 一个 Host 端,无需构建步骤;一个静态 SPA;一个共享密钥。

环境要求

- DeepSeek Harness 桌面应用——插件在它内部运行,驱动它的 Remotes。
- Node.js 18+——仅用于重新构建 SPA 或运行测试套件。
- 手机、平板,或同一网络下的另一台电脑,也可以用 WireGuard、Tailscale 这类隧道。

安装

1. 克隆这个仓库,把它作为 bundle 加进你的 DSH 配置档。cordis.patch.yml 里已经写好现成的配置档行(bind、port、token)。
2. 启动 DSH。宿主日志会打印一个可以直接打开的 URL:

http://127.0.0.1:19400/?k=<token>

3. 在设备上打开它。?k= 只能用一次 —— 页面会拿 token 换一个 HttpOnly; SameSite=Strict cookie,然后清掉地址栏。
4. token 在首次启动时生成,存到 ~/.dsh/remote-control/auth.json,权限 0600。想固定成自己的 token,就在配置里设置 token:。

配置

- id: remote-control
name: '@local/dsh-remote-control'
config:
bind: '0.0.0.0' # 127.0.0.1 keeps it local; 0.0.0.0 exposes it to the network
port: 19400
token: '' # empty = generated once, then persisted

对外暴露

- 隧道(推荐)。 保持 bind: 127.0.0.1,通过 WireGuard、Tailscale 或 SSH 转发访问。什么都不暴露,页面照样有安全上下文。
- 直接走局域网。 设置 bind: 0.0.0.0,打开 http://<lan-ip>:19400/?k=<token>。在非回环地址上用纯 HTTP,浏览器会把页面当成不安全上下文:装成应用、通知、部分剪贴板 API 都用不了。需要这些功能,就在反向代理上终止 TLS。

Windows 防火墙

直接走局域网还需要一条入站规则。仓库里带了一个脚本(需要管理员权限;会弹 UAC 提示):

allow inbound TCP 19400 for the Domain/Private profiles, local subnet only
pwsh -File plugins\remote-control\tools\firewall-remote-control.ps1

remove the rule again
pwsh -File plugins\remote-control\tools\firewall-remote-control.ps1 -Remove

规则名是 DSH Remote Control (TCP <port>)。它只负责开防火墙 —— 监听仍然跟着 bind 走,所以 bind 是 127.0.0.1 时这条规则什么也不会暴露。

开发指南

构建 UI

宿主托管的是 ui/dist,所以用之前得先把 SPA 构建出来:

pnpm --dir plugins/remote-control/ui install
pnpm --dir plugins/remote-control/ui run build # emits ui/dist

运行形态(生产 / 开发)

生产:宿主是纯后端。 index.js 只做两件事——托管 ui/dist 的静态资源、提供 /api/(外加 /healthz)。它不内置任何页面:ui/dist 缺失(或没有 index.html)时只回一个内联样式的 503 说明页。/api/ 与 /healthz 永不回退成 HTML;带扩展名的路径(/assets/x.js)缺失时回 404 ——否则浏览器会把 HTML 当 JS 解析,控制台里冒出一条与真问题无关的语法错误;只有无扩展名路径(/、/files …)回退到 index.html,history 路由才能直接打开。

开发:Vite 管前端,宿主只做反代。 先用 pnpm --dir plugins/remote-control/ui run dev 起 Vite dev server(默认 http://127.0.0.1:5173),再把它的地址告诉宿主:

Windows PowerShell(在启动 DSH 之前设置) $env:RC_DEV_ORIGIN = 'http://127.0.0.1:5173'

macOS / Linux
export RC_DEV_ORIGIN=http://127.0.0.1:5173

设置后,宿主对所有非 /api/、非 /healthz 的 GET 请求做流式反向代理(method 与 headers 原样转发,dev server 的响应原样回传):HMR 与模块转换由 Vite 提供,数据和鉴权仍由宿主提供,/api/ 永远不转发。该变量在插件加载时读取,改完要重启 DSH;不设置时行为与生产完全一致,dev server 没起或连不上时宿主回 502 而不是崩溃。反代出来的静态路径与普通静态资源一样不校验 token,它只是开发开关,不要在生产上设置。用手机访问时 HMR 的 WebSocket 由浏览器直连 dev server,所以 Vite 也要监听一个设备能访问到的地址(server.host)。

重启与退出(自动确认)

POST /api/dsh/restart 在桌面端只做一件事:发一条 fatal,请 Electron 外壳弹出官方恢复对话框(「DeepSeek Harness 无法使用 → 退出 / 重启 / 禁用第三方插件并重启」)。宿主不自己退出,所以窗口不会掉进「重新连接中…」。

默认会自动点下「重启」:Host 在发 fatal 之前以 detached 方式启动 host/confirm-restart.ps1(仅 Windows),它先等 1.5 秒,再用 UI Automation 轮询 60 秒 —— 窗口标题必须同时包含 DeepSeek Harness 与 无法使用,按钮文案必须精确等于「重启」,并且先把窗口恢复置顶(ShowWindow + SetForegroundWindow + AttachThreadInput 绕行 + SetWindowPos(HWND_TOPMOST))才 InvokePattern.Invoke()。找不到窗口、找不到按钮、Invoke 抛错或 PowerShell 不可用都只写日志后安静退出:不模拟键盘,也绝不点「禁用第三方插件」(那会禁用本插件)。

- 关闭自动确认:RC_AUTO_CONFIRM_RESTART=0(不设置或 1 为开启)。关闭后只弹框,等人在电脑前点。
- POST /api/dsh/exit 不自动点任何按钮:用户说「退出」就只弹框自己选,避免语义冲突。
- 助手只往 ~/.dsh/remote-control/relaunch.log 追加带时间戳的日志。
- 没有 Electron 外壳的部署(dsh web)走另一条路:官方 appExit 优雅退出,重启时由 host/relaunch.mjs 等旧监听器释放后再拉起新实例。

运行测试

每个测试套件都是独立脚本,都不需要 DSH 处于运行状态。集成套件在进程内启动插件,用真实 HTTP 和 SSE 驱动它。

node plugins/remote-control/tools/test-remote-control.mjs # Host: auth, routes, SSE, models, permissions, attachments, paging
node plugins/remote-control/tools/test-remote-ui-store.mjs # transcript folding: tool groups, context notes, images
node plugins/remote-control/tools/test-remote-ui-questions.mjs # questions: argument/answer parsing, recommended flags
node plugins/remote-control/tools/test-remote-ui-rail.mjs # prompt rail: ticks, active detection, geometry
node plugins/remote-control/tools/test-remote-ui-layout.mjs # mobile detection: viewport vs overflow
node plugins/remote-control/tools/test-remote-ui-highlight.mjs # highlighting: language set, injection, Markdown pipeline
node plugins/remote-control/tools/test-remote-ui-loading.mjs # loading phases: slow notice, failure, retry and cancel

read-only probe against a running instance
node plugins/remote-control/tools/smoke-remote-control.mjs http://127.0.0.1:19400 <sessionId>

热重载

- 浏览器端。 SPA 像普通 Vite 应用一样重载;布局、样式和组件都不用重启。
- host/hot.js 及其同级文件导入时带一个由 mtime 派生的查询参数,所以改完在下一个 HTTP 请求就生效 —— POST /api/__reload 可以强制触发。
- index.js 属于 Loader 的模块代际:改它需要重启 DSH。切换 bundle 只会重新应用缓存的模块。

项目结构

plugins/remote-control/
├── index.js Host half: HTTP server, auth, routes, SSE, ui/dist hosting (+ dev proxy)
├── host/
│ ├── hot.js Hot-reloadable routes: session management, DSH control, presets
│ ├── usage.js 余额与用量聚合,优先走网关 │ └── model-meta.js 上下文窗口,从网关的 /v1/models 合并 ├── ui/ Vue 3 + Vite SPA │ ├── src/components/ 对话记录、输入框、侧边栏、用量卡片、模型浏览器 …
│ ├── src/lib/ 状态存储、Markdown 处理管线、懒渲染、API 客户端 │ └── public/ 应用图标 ├── locale/ UI 文案(en、zh) ├── tools/ 测试套件、冒烟探测、防火墙与 DSH 控制脚本 ├── docs/poster.png README 主图 ├── page.html 历史遗留文件:宿主已不再读取(只托管 ui/dist) └── package.json, cordis.patch.yml 打包清单与配置档行

HTTP API

除 / 和 /healthz 外,所有路由都要求会话 cookie 或 Authorization: Bearer <token>。

| 路由 | 用途 |
| GET /healthz | 免认证的存活检查 |
| POST /api/login、/api/logout | 用 token 换 cookie,以及反向操作 |
| GET /api/state | 会话、工作区、待处理交互、绑定信息 |
| GET /api/stream?sessionId= | SSE:hello、pending、sessions、frame、stream-error |
| GET /api/models、POST /api/select-model | 模型目录与切换 |
| GET /api/older?sessionId=&beforeSeq= | 一页更早的历史记录({ records, hasMore }) |
| GET /api/attachment?sessionId=&attachmentId= | 从会话日志取图片字节 |
| GET /api/image?url=|path= | 供对话记录使用的同源图片代理 |
| GET /api/usage、POST /api/usage/config | 余额与用量,以及它的配置 |
| GET /api/model-meta | 每个模型 id 的上下文窗口 |
| GET /api/dsh/info、POST /api/dsh/restart、/api/dsh/exit | 查看、重启或退出 DSH |
| POST /api/send、/api/cancel、/api/answer | 发送提示词、停止一轮、回答问题 |
| GET /api/permissions、POST /api/permission | 权限预设与切换 |

安全

- 32 字节的 token 把守每条 /api/* 路由;比较使用常量时间,会话 cookie 和 Authorization: Bearer 头都接受。
- 登录失败按对端限流(每分钟 6 次);请求体有大小上限;拒绝静态路径穿越;页面运行在 default-src 'none' 下,并带 frame-ancestors 'none'。
- 模型输出先经 DOMPurify 清洗,再进入 DOM —— 页面持有智能体控制凭据,所以任何模型文本都不可信。
- token 等同于在运行智能体的机器上执行代码。 不要分享它;任何非回环绑定都要在前面加 TLS 或隧道。

设计上的限制

- 工具审批无法远程确认。 审批是单槽瀑布流,归桌面端应答器所有;劫持它会让桌面面板一直等待一个永远不来的答案。页面只显示在等什么,并请你在桌面端确认。
- 切换权限预设需要活跃会话 —— 冷会话会先恢复。
- 这不是桌面 UI 的镜像:没有文件树、终端、追踪视图,也不能编辑队列。

Star History

License

以 GNU Affero General Public License v3.0 发布。简单说:随便用、随便改、随便分享——但如果你把改过的版本当作网络服务跑起来,就得把源码提供给用这个服务的人。

[dsh]: https://github.com/Dynesshely
预览分享卡片长什么样 ↓Dynesshely/dsh-plugin-remote
分享 · DeepSeek Harness Hub
Dynesshely/dsh-plugin-remote

一个简单的 dsh-plugin,给每个 dsh 应用加上远程访问能力。

这是你的仓库?认领条目

回到顶部