dawsondx
dsh-web-open
DeepSeek Harness (dsh) bundle: when `dsh web` is ready, print the full GUI URL and open it in your default browser. Cross-platform, zero runtime deps, fail-safe.
- Stars
- 2
- Language
- JavaScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
中文
一句话:
dsh web启动完成 → 完整链接自动打印 → 浏览器自动打开。 装一次,任何装了 dsh 的机器都能用;零依赖、跨平台、故障安全。
以前 vs 现在
| 以前(每次都要) | 现在(一次装好) |
|---|---|
记一长串 npx @deepseek-ai/dsh web | 输 dsh web 即可(想更省事可自建快捷方式/别名) |
启动后自己拼 http://127.0.0.1:3080/ 复制进浏览器 | 链接自动打印,浏览器自动打开 |
| 每台机器手动配 PATH、建快捷方式 | dsh plugin --profile web add dsh-web-open 一行安装 |
效果长这样
$ dsh web
[web-open] Web GUI ready: http://127.0.0.1:3080/
# ↑ 完整链接自动打印,浏览器同时自动打开 —— 全程零手动操作
本仓库包含什么(安装前先看清楚)
| 部分 | 内容 | 安装方式 | 平台 |
|---|---|---|---|
| 插件本体(核心) | index.js / opener.js / cordis.patch.yml:dsh web 就绪后打印链接并自动打开浏览器 | dsh plugin --profile web add dsh-web-open(或 GitHub 直装) | Windows / macOS / Linux |
| 可选:Windows 启动器 | contrib/windows/dsh-web.cmd:一键启动 + 自动检查官方新版 | 自行复制到 PATH 目录,或为它建桌面快捷方式 | 仅 Windows |
| 可选:Windows 更新脚本 | contrib/windows/dsh-update.cmd:一键更新 harness | 同上 | 仅 Windows |
| 可选:Unix shell 函数 | contrib/unix/dsh-fn.sh:dsh = dsh web | 加入 ~/.bashrc / ~/.zshrc | macOS / Linux |
插件本体不包含更新功能,也不包含桌面快捷方式——桌面快捷方式、PATH 配置属于每台机器自己的事,仓库不替你创建;可选的 Windows 脚本复制到 PATH 后即可使用。
为什么需要它
官方 dsh (0.1.0-rc.6) 目前只有 printUrl(把链接打印到日志里),没有"启动后自动打开浏览器"的能力;CLI 也没有 --open 参数。社区现有的 dsh-plugin-browser / @anweat/dsh-browser 是给模型用的无头浏览器工具(Playwright 抓网页),与本插件解决的问题不同——本插件打开的是你自己的浏览器来看 GUI。
这个插件解决一个问题:dsh web 启动完成后,打印 URL 并自动打开浏览器。跨平台、零运行时依赖、故障安全设计。
前提条件:已安装 dsh、已初始化 profile(跑过一次 dsh web 即可),且 PATH 里有 pnpm。
从 GitHub 安装(推荐)
dsh plugin --profile web add github:dawsondx/dsh-web-open
或从 npm 安装
dsh plugin --profile web add dsh-web-open
dsh plugin 会自动把包装进 profile 的 node_modules,并注册到 dsh.profile.bundles。无需手动配置。重启生效:
dsh web
# → [web-open] Web GUI ready: http://127.0.0.1:3080/
# → 浏览器自动打开
验证是否生效:
dsh web --dump-config # 输出里应能看到 "id: web-open"
插件等待 Loader 就绪,轮询 webServer 获取绑定端口(最多重试 40×250ms),然后打印 URL 并启动默认浏览器。即使使用 dsh web --port 0(系统分配端口)也能正确打开。
默认行为:打印 URL + 打开浏览器。可在 profile 的 cordis.patch.yml 里覆盖:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: web-open
config:
open: false # 只打印链接,不打开浏览器
printUrl: true
# url: http://x:8080/ # 覆盖默认 http://127.0.0.1:<port>/
# retryAttempts: 40 # 等待服务绑定的重试次数(默认 40 × 250ms)
# retryIntervalMs: 250
或用环境变量临时关闭:
DSH_OPEN_BROWSER=0 dsh web # 只打印,不打开浏览器
兼容性与更新策略
这个插件故意只依赖稳定层:
| 依赖 | 稳定性 | 失效时会怎样 |
|---|---|---|
@deepseek-ai/cordis(peer,>=4.0.0) | 稳定的插件核心,独立版本号 | 插件无法加载 → dsh 有明确报错,移除插件即恢复 |
dsh 内部服务 loader / webServer | 0.1.0-rc 阶段可能改名 | 降级为 no-op:只打一行 warning,dsh web 照常启动,只是不自动打开 |
防失效设计
- apply() 永不抛错。因为 cordis-plugin-loader 对"插件 apply 抛错"的处理是整个 boot 失败,所以本插件把所有步骤包进 try/catch——最坏情况 = 少一个功能,绝不是起不来。
- 缺服务就静默降级。
webServer拿不到就不打开;API 改名只影响本插件,不影响别的。 - profile 锁版本。插件装进
~/.dsh/profiles/web后由 pnpm lockfile 固定,dsh 官方发新版不会自动升级你 profile 里的插件。 - 零 rc 依赖。没有依赖任何
0.1.0-rc.x的内部包,只依赖稳定核心,降低被 rc 版本牵连的概率。
已知的真实风险(写清楚比藏着好)
- 若 dsh 官方把
loader/webServer服务改名(rc 阶段完全可能),本插件需要一次小更新(改两个服务名);期间表现为"不自动打开",不影响使用。 - 若官方未来原生支持
--open,本插件可以退役(卸载即可);为了不撞车,本插件故意不用 CLI 参数,只用 profile 配置控制。 - 恢复/卸载:
dsh plugin --profile web remove dsh-web-open,或直接编辑~/.dsh/profiles/web/package.json的 bundles 列表。
跨平台兼容性(第一性原理审查)
结论:可以跨平台安装并正确运行(Windows / macOS / Linux,含 WSL)。以下逐层对照 dsh 0.1.0-rc.6 实际源码验证过,不是想当然:
| 层 | 实际机制(源码依据) | 结论 |
|---|---|---|
| 安装 | dsh plugin --profile web add <包> → pnpm 装入 profile → 命令内 reconcile 把声明了 dsh.bundle 的包写进 dsh.profile.bundles(plugin-*.js) | 需要 pnpm(dsh 官方要求);boot 不会自动 reconcile,所以必须走 dsh plugin add,手动 pnpm add 不生效 |
| 装载 | row 的 name 由 Node 内部加载器从 profile 目录解析(mountRootInclude / cordis-plugin-loader),与官方 @deepseek-ai/dsh-web-app 完全同路径 | 任意 OS 一致;纯 Cordis 插件,无需 TYPERT 清单(无 ./typert 导出的包会被 typert 注册表静默跳过,不报错) |
| 契约 | 导出 name / apply / config;Cordis unwrapExports 兼容具名导出;无 Config 导出也可(只在有 Config 时才校验) | 与官方插件同款写法 |
| 错误语义 | cordis-plugin-loader:任一插件 apply 抛错 = 整个 boot 失败(loader entries failed to apply) | 本插件 apply() 全 try/catch、永不抛错 → 任何情况下都不会拖垮 dsh web |
| 服务读取 | ctx.get("loader").await() + ctx.get("webServer").port(官方 web-app 的 printUrl 同款) | 任意 OS 一致;拿不到就降级 no-op + 警告 |
| 打开浏览器 | win32 cmd start(走 SystemRoot\System32\cmd.exe 绝对路径,GUI 启动 PATH 被裁剪也不怕);darwin /usr/bin/open;linux xdg-open → sensible-browser → google-chrome → chromium → firefox 逐级回退 | v0.1.1 修复:spawn 挂 error 监听 + 启动前 PATH 探测,缺 xdg-open 的 Linux 也不会崩(只警告并打印链接) |
| 编码 | 插件文件全部 UTF-8(JS/YAML),Node 与 js-yaml 原生支持 | 中/英/日文系统均无乱码问题 |
已知边界(不是 bug,是环境事实):
- Linux 无图形环境(服务器 / 容器 / WSL 无 DISPLAY):浏览器打不开,但
dsh web照常运行,链接照常打印——行为可预期; - headless / CI:用
DSH_OPEN_BROWSER=0关掉自动打开; - 需要
pnpm:dsh plugin的管理命令本身要求 pnpm(没装会提示安装); - dsh 版本:在 0.1.0-rc.6 上验证。rc 系列内用的都是官方 web-app 同款机制,大概率兼容;若未来 rc 改动了
loader/webServer服务名,本插件按设计自动降级为 no-op(不报错、不影响启动),等一次小更新即可。
如何更新(官方更新后)
先分清边界:插件本体(dsh-web-open)只负责"启动后打印链接 + 自动打开浏览器",不包含更新 harness 的功能——插件运行在 dsh 进程内部,无法更新正在运行的宿主。更新 harness 是终端/脚本层面的事:
| 场景 | 命令 / 操作 | 说明 |
|---|---|---|
| 更新 harness(任何系统) | npm install -g @deepseek-ai/dsh@latest | 更新 launcher = 更新整个 harness:官方 bundle(dsh-base / dsh-web-app)按"安装优先"从 launcher 解析,重启即生效 |
| 更新 profile 里的插件 | dsh plugin --profile web update | 本插件发布到 npm 后用此命令升级;GitHub 直装版需重新 dsh plugin add |
| Windows 可选一键脚本 | 把 contrib/windows/dsh-update.cmd 复制到 PATH 目录后执行 dsh-update,或自己为它建桌面快捷方式 | 自动对比 registry → 执行上面两条 |
| Windows 可选启动器自动检查 | 用 contrib/windows/dsh-web.cmd 启动器时,每次打开会静默检查新版并提示;设 DSH_AUTO_UPDATE=1 自动更新后再启动,DSH_SKIP_UPDATE_CHECK=1 关闭 | 同上,需先把脚本放入 PATH |
注意:官方
npx @deepseek-ai/dsh web不会自动更新——npx 复用本地缓存,只有显式@latest才会重新拉取。
可选的 shell 便利层
插件解决的是"启动后自动打开",跨平台一致。但"dsh 三个字母 = 启动 Web"属于 shell 层,插件做不到,需要每个用户自己的终端配置:
Windows:把 dsh-web.cmd 启动器放进 PATH(见 contrib/windows/),或自己为它创建桌面快捷方式(右键 → 发送到 → 桌面快捷方式)。
macOS / Linux:在 ~/.bashrc / ~/.zshrc 加:
dsh() {
if [ $# -eq 0 ]; then command dsh web; else command dsh "$@"; fi
}
装了插件之后,这些便利层里"等待端口 + 打开浏览器"的逻辑都可以删掉(插件会做),只保留启动本身。
开发与测试
npm test # 13 项单元测试:平台命令形状、URL 构建、apply 永不抛错、打开/抑制路径
真实的端到端验证(dsh web 启动后浏览器弹出)请在没有占用 3080 端口的环境里跑一次。
License
MIT
English
One line: when
dsh webis ready → full URL printed → browser opens automatically. Install once, works on any machine with dsh. Zero dependencies, cross-platform, fail-safe.
Before vs After
| Before (every single time) | After (one-time setup) |
|---|---|
Memorize npx @deepseek-ai/dsh web | Run dsh web (or configure an alias / shortcut) |
Manually assemble http://127.0.0.1:3080/ and paste it into a browser | URL printed automatically, browser opens itself |
| Configure PATH / shortcuts on every machine | dsh plugin --profile web add dsh-web-open — one line |
What it looks like
$ dsh web
[web-open] Web GUI ready: http://127.0.0.1:3080/
# ↑ full URL printed, browser opens automatically — zero manual steps
What's in this repo (read before installing)
| Part | Contents | Install | Platform |
|---|---|---|---|
| Plugin core | index.js / opener.js / cordis.patch.yml: prints the URL and opens your browser when dsh web is ready | dsh plugin --profile web add dsh-web-open (or GitHub) | Windows / macOS / Linux |
| Optional: Windows launcher | contrib/windows/dsh-web.cmd: one-click start + checks for official updates | Copy it into a PATH dir yourself, or create a desktop shortcut | Windows only |
| Optional: Windows updater | contrib/windows/dsh-update.cmd: one-click harness update | Same as above | Windows only |
| Optional: Unix shell fn | contrib/unix/dsh-fn.sh: dsh = dsh web | Add to ~/.bashrc / ~/.zshrc | macOS / Linux |
The plugin core has no update feature and ships no desktop shortcuts — PATH placement and shortcuts are per-machine, user-owned. The optional Windows scripts work once you copy them into a PATH dir.
Why you need this
The official dsh (0.1.0-rc.6) only has printUrl to log the URL — it doesn't auto-open your browser when ready. There's no --open CLI flag either. Community plugins like dsh-plugin-browser / @anweat/dsh-browser are headless browser tools for the model (Playwright for web scraping), not for opening your own browser to view the GUI.
This plugin solves one problem: when dsh web is ready, print the URL and open it automatically. Cross-platform, zero runtime dependencies, fail-safe by design.
Requirements: dsh installed, profile initialized (run dsh web once), and pnpm in PATH.
Install from GitHub (recommended)
dsh plugin --profile web add github:dawsondx/dsh-web-open
Or from npm
dsh plugin --profile web add dsh-web-open
dsh plugin automatically adds the bundle to your profile's node_modules and registers it in dsh.profile.bundles. No manual configuration needed. Restart to activate:
dsh web
# → [web-open] Web GUI ready: http://127.0.0.1:3080/
# → Browser opens automatically
Verify it's active:
dsh web --dump-config # Should show "id: web-open"
The plugin waits for the Loader to settle, polls webServer for the bound port (retries up to 40×250ms), then prints the URL and spawns your default browser. Works even with dsh web --port 0 (OS-assigned port).
Default behavior: print URL + open browser. Override in your profile's cordis.patch.yml:
# ~/.dsh/profiles/web/cordis.patch.yml
- id: web-open
config:
open: false # Only print URL, don't open browser
printUrl: true
# url: http://x:8080/ # Override default http://127.0.0.1:<port>/
# retryAttempts: 40 # Wait attempts (default 40 × 250ms)
# retryIntervalMs: 250
Or temporarily disable with environment variable:
DSH_OPEN_BROWSER=0 dsh web # Print only, no browser
Compatibility & update strategy
This plugin deliberately depends only on stable layers:
| Dependency | Stability | If it breaks |
|---|---|---|
@deepseek-ai/cordis (peer, >=4.0.0) | Stable plugin core, independent versioning | Plugin won't load → dsh shows clear error, remove to recover |
dsh internal services loader / webServer | May change during 0.1.0-rc | Degrades gracefully: prints warning, dsh web starts normally, just won't auto-open |
Fail-safe design
- apply() never throws. Since cordis-plugin-loader fails the entire boot on plugin errors, every step is wrapped in try/catch — worst case = one feature missing, never a startup failure.
- Missing services degrade silently. If
webServeris unavailable, it warns and skips opening; other plugins unaffected. - Profile locks versions. Plugins installed in
~/.dsh/profiles/webare pinned by pnpm lockfile; official dsh updates won't auto-upgrade your plugins. - Zero rc dependencies. No internal
0.1.0-rc.xpackages, only stable Cordis core — reduces breaking change risk.
Known risks (transparency)
- If dsh renames
loader/webServerservices (possible during rc), this plugin needs a small update (change two service names); during that time it'll silently skip auto-open without breaking dsh. - If official dsh adds native
--open, this plugin can be retired (just uninstall). To avoid conflicts, this plugin deliberately avoids CLI params and uses profile config only. - Uninstall:
dsh plugin --profile web remove dsh-web-openor edit~/.dsh/profiles/web/package.jsonbundles list.
Cross-platform compatibility (first-principles review)
Verified layer-by-layer against dsh 0.1.0-rc.6 source, not by assumption:
| Layer | Actual mechanism (source basis) | Result |
|---|---|---|
| Install | dsh plugin --profile web add <pkg> → pnpm installs into profile → reconciles within the command, writing bundles that declare dsh.bundle into dsh.profile.bundles | Requires pnpm (official requirement); boot does NOT reconcile, so dsh plugin add is mandatory — manual pnpm add alone won't activate it |
| Load | Row name resolves via Node's internal loader from the profile dir (mountRootInclude / cordis-plugin-loader) — the exact path official @deepseek-ai/dsh-web-app uses | Same on every OS; plain Cordis plugin, no TYPERT manifest needed (packages without a ./typert export are silently skipped by the typert registry) |
| Contract | Exports name / apply / config; Cordis unwrapExports handles named exports; Config export is optional (validated only when present) | Same writing style as official plugins |
| Error semantics | cordis-plugin-loader: one plugin throwing in apply fails the whole boot | This plugin's apply() is fully wrapped in try/catch and never throws → can never take down dsh web |
| Service reads | ctx.get("loader").await() + ctx.get("webServer").port (same pattern as the official web-app printUrl) | Same on every OS; degrades to no-op + warning if unavailable |
| Opening the browser | win32 cmd start (absolute SystemRoot\System32\cmd.exe, safe even when PATH is stripped on GUI launch); darwin /usr/bin/open; linux xdg-open → sensible-browser → google-chrome → chromium → firefox | Fixed in v0.1.1: spawn error listener + PATH probing before launch — a Linux without xdg-open warns and prints the link instead of crashing dsh |
| Encoding | Plugin files are UTF-8 (JS/YAML); Node & js-yaml native | No mojibake on any locale |
Known environment boundaries (not bugs):
- Linux without a graphical session (server / container / WSL without DISPLAY): the browser won't open, but
dsh webruns normally and the link is still printed — predictable behavior. - Headless / CI: disable auto-open with
DSH_OPEN_BROWSER=0. - pnpm is required:
dsh pluginitself requires pnpm (it prompts to install if missing). - dsh version: verified on 0.1.0-rc.6. Within the rc line this uses the same mechanism as the official web-app bundle, so compatibility is very likely; if a future rc renames
loader/webServer, the plugin degrades to no-op by design (no error, no startup impact) until a small update.
How to update (after an official release)
Boundary first: the plugin core (dsh-web-open) only prints the URL and opens the browser — it has no harness-update feature, because a plugin running inside dsh cannot update its own host. Updating the harness is a terminal / script concern:
| Scenario | Command / action | Notes |
|---|---|---|
| Update the harness (any OS) | npm install -g @deepseek-ai/dsh@latest | Updating the launcher = updating the whole harness: the official bundles (dsh-base / dsh-web-app) resolve installation-first from the launcher — effective on next start |
| Update profile plugins | dsh plugin --profile web update | Bumps this plugin once published to npm; GitHub-installed copies need a fresh dsh plugin add |
| Optional Windows one-click | Copy contrib/windows/dsh-update.cmd into a PATH dir, then run dsh-update (or create a shortcut yourself) | Compares against the registry → runs the two commands above |
| Optional Windows launcher check | Using contrib/windows/dsh-web.cmd, each launch silently checks for a newer version and prints a notice; DSH_AUTO_UPDATE=1 updates first, DSH_SKIP_UPDATE_CHECK=1 disables | Requires the script in PATH first |
Note: the official
npx @deepseek-ai/dsh webdoes NOT auto-update — npx reuses its local cache; only an explicit@latestre-fetches.
Optional shell convenience layer
The plugin solves "auto-open after boot" cross-platform. But "type dsh = start web" is a shell-layer concern the plugin can't handle — each user configures their own terminal:
Windows: put dsh-web.cmd from contrib/windows/ in PATH, or create a desktop shortcut for it yourself (right-click → Send to → Desktop).
macOS / Linux: add to ~/.bashrc / ~/.zshrc:
dsh() {
if [ $# -eq 0 ]; then command dsh web; else command dsh "$@"; fi
}
Once the plugin is installed, these shell shortcuts can drop their "wait for port + open browser" logic (the plugin handles it) and just invoke dsh.
Development & testing
npm test # 13 unit tests: platform commands, URL construction, apply never throws, open/suppress paths
For real end-to-end verification (browser actually pops up after dsh web), test in an environment where port 3080 is free.
License
MIT