← 返回列表
需源码安装
macOS 菜单栏启动器:一键在后台运行 dsh-tavernDeepSeek Harness 文字游戏 Agent服务
暂不能直接安装(需源码编译或环境不满足):仓库缺少 package.json,无法用 dsh 插件安装命令安装。 · 最近上游提交 2026/9/18 · 已提供中文文档
macOS 菜单栏启动器:一键在后台运行 dsh-tavern(DeepSeek Harness 文字游戏 Agent)服务
综合分
30.4
GitHub 分
30.4
用户评分
—
★ Stars
1
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add JshGao/dsh-tavern-server仓库缺少 package.json,无法用 dsh 插件安装命令安装,改用 GitHub 源安装
信任档位:已验证本站已于 1 天前真实安装成功
- 是什么
- dsh 原生插件 · chat
- 装得上吗
- 本站已真实安装成功(非静态推断)
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 8 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
🟢实装验证通过· 2026/9/24
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/21(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查需源码安装
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-tavern-server(未发布到 npm,仅可源码安装)
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明
仓库缺少 package.json,无法用 dsh 插件安装命令安装
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/21 09:02:29
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成DSH Tavern(macOS 菜单栏应用)
一个只有状态栏图标、不占程序坞的轻量包装器,用来在后台运行 dsh-tavern
(基于 DeepSeek Harness 的文字游戏 Agent),省掉每次手敲 dsh-tavern start。
实现规格见 VIBE_CODING.md,源码是单文件 main.swift。
环境要求
- macOS 11+(已在 macOS 26.6 上验证)
- 构建:swiftc(Xcode 或 xcode-select --install)
- 运行:已安装 dsh-tavern(独立 runtime 在 ~/.dsh-tavern,或 DSH Desktop),以及可用的 DSH 凭据
安装
不需要自己编译。 从 Releases 下载已构建好的文件:
| 文件 | 说明 |
|---|---|
| DSH-Tavern-.dmg | 推荐:打开后把 DSH Tavern 拖到 Applications |
| DSH-Tavern-.zip | 解压后把 DSH Tavern.app 拖到 Applications |
然后从"应用程序"双击 DSH Tavern,右上角状态栏出现酒馆招牌图标即成功。
首次打开会被 Gatekeeper 拦下。 本 App 是 ad-hoc 签名、未做 Apple 公证,首次双击会提示
"无法打开,因为 Apple 无法检查其是否包含恶意软件"。任选一种解决办法:
- 在"应用程序"里右键(或 Control-点击)图标 → 打开 → 再点"打开",之后就能正常双击;
- 或终端执行 xattr -dr com.apple.quarantine "/Applications/DSH Tavern.app"。
想彻底消除这个提示需要 Apple Developer ID 签名 + 公证,目前没有做。
运行前提:本机已经装好酒馆。尚未安装时,菜单会显示"未安装",并有一项
复制 Tavern 安装命令,把它粘到终端执行即可:
curl -fsSL https://cdn.jsdelivr.net/gh/flizzywine/dsh-tavern@main/install.sh | DSH_TAVERN_HOST=cli sh
本 App 只是启动器,不包含酒馆本身,也不会改动它的源码。
发布流程:推一个 v 标签(例如 v1.0.0)→ GitHub Actions 在 macOS runner 上构建、验收、
打包并把 .dmg / .zip 挂到 Release 上(见 .github/workflows/build.yml)。
从源码构建
./build.sh # 生成 build/DSH Tavern.app
./scripts/package.sh 1.0.0 # 可选:打包成 .dmg / .zip
open "build/DSH Tavern.app" # 启动
构建不依赖 macOS 的 SVG 渲染能力——图标产物已提交在 assets/ 下;改了
assets/tavern-sign.svg 或 assets/tavern-status.svg 之后再执行 TAVERN_REGEN_ICONS=1 ./build.sh
重新渲染。
启动后右上角状态栏会出现酒馆招牌图标(一块挂在横梁上的盾牌,盾牌里是冒泡的啤酒杯;
浅色菜单栏显示黑色,深色菜单栏自动变白),程序坞和 Cmd-Tab 里都不会出现它。
双击图标后的菜单:
| 菜单项 | 说明 |
|---|---|
| DSH Tavern:运行中(端口 3081) | 状态行:启动中 / 运行中 / 已停止 / 未安装 / 异常原因 |
| 独立 runtime:~/.dsh-tavern | 当前服务用的是哪套 DSH |
| 在 Safari 中打开 | 打开带登录 token 的地址,首次也能直接进界面 |
| 复制服务地址 | 把同一个地址复制到剪贴板 |
| 打开日志 | 打开 ~/Library/Logs/dsh-tavern.log |
| 打开酒馆数据目录 | 打开 ~/.dsh-tavern |
| 重启 DSH Tavern | 停掉当前服务再重新拉起 |
| 退出并停止 DSH Tavern | 退出 App,同时终止服务及其所有子进程 |
默认地址:
不要手动把 http://127.0.0.1:3081/ 存成书签直接用。
酒馆界面受进程级 token 保护,裸地址在没有有效 cookie 时返回 401 空白页。
菜单里的"在 Safari 中打开"用的是每次启动从日志中解析出的带 token 地址,永远可用。
图标
状态栏图标和 App 图标都是同一个"酒馆招牌"造型,矢量源在 assets/ 下:
| 源文件 | 内容 | 产物 |
|---|---|---|
| assets/tavern-sign.svg | 完整招牌:木质横梁 + 黄铜挂钩 + 盾牌 + 金啤杯与泡沫 | assets/AppIcon.icns(Finder 图标)、assets/icon-preview.png |
| assets/tavern-status.svg | 同一造型的单色剪影,啤酒杯用 fill-rule="evenodd" 从盾牌中镂空 | assets/tavern-status.png(状态栏,19pt,isTemplate = true) |
build.sh 会用 scripts/make-icons.swift 把两个 SVG 直接光栅化到每个尺寸
(走 macOS 的 _NSSVGImageRep),不做位图缩放。换图标只需替换 SVG 后重新构建。
想常驻使用
1. 把 build/DSH Tavern.app 拖到 /Applications(或 ~/Applications)。
2. 系统设置 → 通用 → 登录项,把它加入“打开时启动”。
配置
配置文件(可选,不存在就用默认值):
~/Library/Application Support/DSHTavern/config.json
优先级:环境变量 > config.json > 默认值。
| 键 | 环境变量 | 默认值 | 说明 |
|---|---|---|---|
| host | DSH_TAVERN_HOST / DSH_TAVERN_RUNTIME_HOST | auto | auto / cli / desktop;决定服务用哪套 DSH |
| cliHome | DSH_TAVERN_CLI_HOME | ~/.dsh-tavern | cli 模式的独立 runtime 与数据目录 |
| desktopHome | DSH_HOME | ~/.dsh | desktop 模式的数据目录 |
| dshPath | DSH_TAVERN_DSH_PATH | 自动探测 | dsh 可执行文件;cli 模式默认 /runtime/bin/dsh |
| profile | DSH_TAVERN_PROFILE | tavern | DSH profile 名 |
| workingDirectory | DSH_TAVERN_WORKDIR | 酒馆源码目录 | 服务工作目录,也是 DSH 的工作区 |
| webPort | DSH_TAVERN_PORT | 3081 | 监听端口 |
| extraArguments | — | [] | 追加到服务命令之后的参数 |
| environment | — | {} | 追加给服务的环境变量 |
| envFile | DSH_TAVERN_ENV_FILE | /env | KEY=VALUE 行,# 注释,不存在则忽略 |
| logPath | DSH_TAVERN_LOG_PATH | ~/Library/Logs/dsh-tavern.log | 服务输出,追加写入,权限 0600 |
| logMaxBytes | — | 5242880 | 超过则轮转为 .log.1 |
示例:
{
"webPort": 3081,
"host": "cli",
"workingDirectory": "/Users/me/Projects",
"environment": { "DEEPSEEK_API_KEY": "sk-..." }
}
改完配置后从菜单点“重启 DSH Tavern”即可生效,不用重新构建。
关于环境变量:从 Finder 双击启动的 App 不读 ~/.zshrc,所以放在那里的 PATH、
DEEPSEEK_API_KEY 等不会自动生效。请写进 config.json 的 environment 或 envFile。
工作原理(简述)
- 服务命令就是 dsh-tavern start 内部用的那一条:
/runtime/bin/dsh --profile tavern --host 127.0.0.1 --port 3081 --no-open,
环境变量也照 bin/launcher-environment.mjs 一一对应(DSH_HOME、DSH_TAVERN_RUNTIME_HOST、
DSH_TAVERN_CLI_HOME、DSH_TAVERN_LEGACY_DSH_HOME、pnpm_config_update_notifier=false)。
- 用 posix_spawn 的 POSIX_SPAWN_SETPGROUP 把 zsh → node 整棵树放进一个独立进程组,
退出时用 killpg 一次性清理,SIGTERM 4 秒不退就升级 SIGKILL。
- 服务启动后会向日志打印 dsh web: http://127.0.0.1:3081/?token=...,App 解析这一行作为
“就绪”信号,并把该地址用于“打开 / 复制”。
- 组内有一个看门狗子 shell:App 崩溃或被 kill -9 后,它会在 1 秒内杀掉整个进程组。
- 注销 / 关机 / kill -TERM 会转到正常退出路径,做同样的清理。
- App 启动前会先探测端口:如果那里已经是一个可识别的酒馆服务(HTTP 返回 200/303/401),
就直接接管显示(状态行标注“外部实例”),此时退出不会动那个进程;如果是别的程序占用,
则只报错、不碰它。
- 同时只允许一个 App 实例(flock)。
与 dsh-tavern 命令行的兼容性
两边可以互相接管,不会打架:
- App 启动服务后会把 pid 写进 ~/.dsh-tavern/logs/tavern.pid.json(超集格式,多一个 launcher 字段),
并把带 token 的地址镜像进 ~/.dsh-tavern/logs/tavern.log。因此
dsh-tavern status / dsh-tavern open 能拿到正确地址,dsh-tavern stop 也能停掉它。
- 反过来,先用 dsh-tavern start 起了服务再打开 App,App 会识别为“运行中(外部实例)”。
- App 退出时会删掉自己写的那条 pid 记录,不会留下会让 dsh-tavern status 误判的残留。
排错
菜单显示“未安装”:没找到 /runtime/bin/dsh(也没有可用的 dsh)。
点菜单里的“复制 Tavern 安装命令”,在终端执行后重启 App。
端口被占用:菜单会显示“端口 3081 已被占用”并弹窗。
lsof -nP -iTCP:3081 -sTCP:LISTEN # 看占用者
kill -TERM # 确认是残留进程后再杀
Safari 打开是空白 / 401:说明用的不是带 token 的地址。等服务就绪(状态行显示“运行中”)
再用菜单打开;也可以直接从日志里取地址:
grep -o 'http://127.0.0.1:3081/?token=[^ ]' ~/Library/Logs/dsh-tavern.log | tail -1
一直卡在“启动中…”:酒馆首次启动要装依赖、加载人物卡,几十秒是正常的;超过两分钟看日志:
tail -50 ~/Library/Logs/dsh-tavern.log
状态行显示异常:日志尾部会给出原因摘要;常见的是 EADDRINUSE(端口冲突)或
runtime 找不到(cliHome / dshPath 配错)。
日志安全:App 日志里有 ?token=,等同于本机 agent 的控制权,文件权限固定 0600,不要外发。
怀疑泄露就 rm ~/Library/Logs/dsh-tavern.log 后重启 App(token 每个进程都会重新生成)。
项目结构
dsh-tavern-server/
├── main.swift # 菜单栏 App 全部源码(配置、安装探测、状态机、posix_spawn、清理)
├── build.sh # 构建 .app:编译 + 图标 + Info.plist + ad-hoc 签名
├── assets/
│ ├── tavern-sign.svg # App 图标矢量源(改它之后用 TAVERN_REGEN_ICONS=1 重新渲染)
│ ├── tavern-status.svg # 状态栏图标矢量源(单色镂空剪影)
│ ├── AppIcon.icns # 提交的 App 图标产物(构建时的真源)
│ ├── tavern-status.png # 提交的状态栏图标产物
│ └── icon-preview.png # README 预览图
├── scripts/
│ ├── make-icons.swift # SVG -> AppIcon.icns + tavern-status.png
│ ├── package.sh # 打包 .dmg / .zip
│ └── verify.sh # 验收:静态检查 + 运行期 / 退出清理检查
├── .github/workflows/build.yml # CI:构建 + 验收;打标签时发布 Release
├── VIBE_CODING.md # 实现规格(需求、技术方案、验收、DoD)
├── LICENSE # MIT
├── README.md # 中文 README(本文件)
└── README.en.md # 英文 README
build/ 是构建产物目录,已在 .gitignore 中忽略。
已知限制
- 包装器与酒馆的耦合点只有四处:--profile tavern --host --port --no-open 命令行、
DSH_TAVERN_* 环境变量、日志里的 dsh web: 行、以及 logs/tavern.pid.json。
上游若改名需要改 main.swift 并重新构建;酒馆或 DSH 发新版本本身不需要重新构建。
- 酒馆要求 DSH 版本严格匹配(见上游 README)。本 App 不检查版本;版本不对时服务会自己在
日志里报错,状态行会显示异常摘要。
- 状态栏图标大小在 main.swift 里一行调整(当前 19pt)。NSStatusItem.squareLength 是个正方形,
约 22pt 已接近菜单栏高度上限,再往上调不会有变化。
- 服务与其它酒馆实例共享 ~/.dsh-tavern 的会话存储,不建议同时跑多个实例。
- 只支持 macOS。Windows / Linux 请用上游的 dsh-tavern 命令行或 DSH Desktop。
来源与许可
- 本项目是 dsh-tavern 的非官方macOS 启动器,
不修改酒馆源码,只按它公开的启动方式拉起服务。
- 代码结构仿照 DSH-desktop-server(MIT)。
- assets/ 下的招牌图标由本项目绘制,不在上游商标范围内;酒馆与 DeepSeek 的名称、标识
归各自权利人所有,这里仅用于本机桌面图标。
- 本项目的代码以 MIT 许可发布。
重新构建 / 验收 / 卸载
./build.sh # 重新构建(改了 main.swift 之后)
./scripts/verify.sh # 验收:App 运行时查运行期,未运行时查退出清理
rm -rf "build/DSH Tavern.app" # 卸载 App
rm -rf ~/Library/Application\ Support/DSHTavern # 卸载配置与状态
rm -f ~/Library/Logs/dsh-tavern.log # 卸载日志
./scripts/verify.sh 会检查 bundle 结构、LSUIElement、签名、图标资源、日志权限、端口监听、
pid 记录兼容性、token URL 能否真正加载酒馆页面,以及退出后是否还有残留进程或残留记录。