dsh-web-search-ddg
aooyoo
Zero-token DuckDuckGo search provider for the DeepSeek Harness (DSH) web seam — local headless browser, no API key, no m…
PROJECT TOPICS
PROJECT README
DeepSeek Harness 的 Windows 桌面端。把 dsh web 从「cmd 敲命令 + 浏览器开标签页」变成双击即用的桌面应用:
dsh web,解析就绪 URL 后直接内嵌加载 GUI——没有 cmd 窗口,不用手动开浏览器。package.json / pnpm-lock.yaml。插件安装/移除完成后,窗口顶部出现「插件已变更 · N 秒后自动重启」倒计时(可取消),倒计时结束自动重启后端——装完插件不用再回 cmd 敲重启。--port 0(系统随机端口)。npm install # 安装依赖(electron 等)
npm start # 开发模式启动
或直接使用打包产物:dist/DSH-Desktop-0.1.0-portable.exe(免安装,双击即用)。
只需本机装有 Node.js。首次启动若未找到 dsh,应用会通过
npx -y @deepseek-ai/dsh自动下载安装(需联网,稍等片刻),之后复用本地缓存,无需再次下载。
重新打包:
npm run icon # (可选)重新生成图标 assets/icon.png
npm run dist # 打包 Windows portable 单文件 exe
| 操作 | 方式 |
|---|---|
| 重启后端 | 悬浮条「重启」按钮 / 托盘菜单 / 菜单「应用 → 重启后端」/ Ctrl+Shift+R |
| 在系统浏览器打开 | 悬浮条「↗」/ 托盘 / 菜单 / Ctrl+Shift+O |
| 查看日志 | 托盘或菜单「打开日志目录」 |
| 开发者工具 / 缩放 | 菜单「视图」 |
悬浮条(窗口顶部居中的小胶囊)显示后端状态并可拖动:
托盘图标常驻系统托盘:显示主窗口、重启后端、在浏览器打开、打开日志目录、退出。
dsh plugin --profile web add <包名>(或任何会改 profile 依赖的操作);~/.dsh/profiles/web/package.json 或 pnpm-lock.yaml 变化;不想自动重启?在设置里把
autoRestartAfterPluginChange改为false,变更后只提示、由你手动重启。
问题:agent(dsh web 会话)在会话内直接跑 dsh plugin add 时,安装完成后后端会自动重启,把 agent 正在进行的回合掐断——用户看到"话说到一半就没了"。
方案:agent 把安装请求交给 Electron 主进程执行(主进程不随后端重启),安装完整结束后由现有插件变更流程弹倒计时重启;结果写回控制目录,agent 重启恢复会话后读取并汇报。
协议(agent 侧):
cmd-<id>.json:{
"cmd": "install-plugin",
"spec": "dsh-open-in-vscode 或 https://.../archive/refs/tags/v0.1.5.tar.gz",
"id": "inst-20260814-1800-abc",
"profile": "web",
"timeoutMs": 300000
}
result-<id>.json 直到 state ∈ {done, failed, timeout};或直接结束回合,后端重启、会话恢复后读该文件汇报。控制目录默认 %APPDATA%\DSH Desktop\control(可用环境变量 DSH_DESKTOP_CONTROL_DIR 覆盖,测试/多实例隔离用)。
推荐工作流:agent 发起安装后立刻结束回合(提示"安装进行中,后端将自动重启")→ 重启后会话恢复,agent 核对 result-<id>.json 与 node_modules 后汇报结果。
配置文件:%APPDATA%\DSH Desktop\settings.json(首次运行自动生成):
{
"port": 3080, // dsh web 监听端口;0 = 随机端口
"workspace": "C:\\Users\\HWX", // dsh 后端工作目录(agent 文件操作根目录);null = 用户主目录
"closeToTray": false, // 关闭窗口时最小化到托盘(true)还是退出(false)
"autoRestartAfterPluginChange": true, // 插件变更后自动重启后端
"restartCountdownSec": 6, // 自动重启前的可取消倒计时(秒)
"profile": "web", // 启动的 dsh profile
"nodeBin": null, // 手动指定 node.exe 路径(null = 自动探测)
"dshBin": null // 手动指定 dsh 入口(bin.js 或 dsh.cmd,null = 自动探测)
}
┌──────────────────────────────────────────────┐
│ DSH Desktop (Electron 主进程) │
│ ┌────────────────────────────────────────┐ │
│ │ BrowserWindow ← 加载 dsh web GUI │ │
│ │ + 悬浮控制条(preload 注入) │ │
│ └────────────────────────────────────────┘ │
│ 后端管理器 (lib/backend.js) │
│ ├─ spawn node <dsh bin.js> web --port N │
│ ├─ 解析 stdout "dsh web: http://127.0.0.1" │
│ ├─ 端口探测 → 接管外部实例 / 回退随机端口 │
│ ├─ fs.watch profile 依赖文件 → 自动重启 │
│ └─ 意外退出 → 退避重启 │
│ 托盘 / 菜单 / 系统通知 │
└──────────────────────────────────────────────┘
关键点:
settings.dshBin → npx 缓存(%LOCALAPPDATA%\npm-cache\_npx\*\node_modules\@deepseek-ai\dsh\lib\bin.js,取最新)→ PATH 上的 dsh;都找不到时自动回退 npx -y @deepseek-ai/dsh web --port N(自动下载安装,首次需联网)。dsh web: http://127.0.0.1:<port>(--port 0 时也是从这行拿到真实端口)。GET /,响应含 __DSH_BOOT__ 即判定为 dsh web。%APPDATA%\DSH Desktop\logs\main.log(超 5MB 自动轮转)。| 现象 | 处理 |
|---|---|
| 首次启动提示正在自动下载 dsh | 正常:npx -y @deepseek-ai/dsh 自动安装(需联网,之后复用缓存) |
| 自动下载失败(无网络) | 联网后重试,或先在任何终端跑一次 npx -y @deepseek-ai/dsh web,或在 settings.json 指定 dshBin |
| 端口被占且不是 dsh | 应用自动用随机端口;也可在 settings.json 改 port |
| 自动重启没发生 | 确认 autoRestartAfterPluginChange 为 true;看日志是否「pnpm 仍在运行」卡住 |
| 想恢复 cmd 方式 | 关掉应用后 npx dsh web 照常可用,互不影响 |
| 悬浮条碍事 | 按住拖动到别处,或点「–」折叠成小圆点 |
main.js Electron 主进程(窗口/菜单/托盘/IPC/倒计时编排)
preload.js 悬浮控制条注入 + 状态页桥接
lib/
backend.js dsh 后端进程管理器(核心:拉起/接管/监控/重启)
dsh-resolve.js node.exe 与 dsh 入口自动探测
settings.js 设置读写
logger.js 文件日志
status-page.html 后端未就绪时的本地状态页
mock-backend.js Mock dsh 后端(自动化自测用,DSH_DESKTOP_MOCK=1)
scripts/make-icon.js 图标生成(纯 Node PNG 编码)
assets/icon.png 应用图标
内置自动化自测(Mock 模式 + 控制通道):
$env:DSH_DESKTOP_MOCK='1'
$env:DSH_DESKTOP_PROFILE='<临时 profile 目录>'
$env:DSH_DESKTOP_TEST_CONTROL='<控制目录>'
npm start
# 向控制目录写入 JSON 命令:{"cmd":"screenshot","path":"..."} / {"cmd":"status","path":"..."} / {"cmd":"restart"} / {"cmd":"cancel"} / {"cmd":"quit"}
已覆盖:启动与 URL 解析、插件变更自动重启(含倒计时截图)、取消、手动重启、退出清理、外部实例接管(真实 3080 实例)。
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。