环境介绍
使用的环境如下:
- macOS 14.7.6
- Bitwarden Server self-host 2025.5.1
- Bitwarden Client 2025.5.0
- Tabby 1.0.223
解决方案
简单来说,是 macOS 自带了一个 ssh-agent,并且无法覆盖,导致设置的 Bitwarden 的 SSH-Agent 无效,Tabby 调用的一直是系统自带的 SSH-Agent。
于是解决方案如下:
- 重启你的 macOS,重启之后,问题应该已经解决。
编辑文件 ~/Library/LaunchAgents/com.bitwarden.ssh-agent.plist 并保存(把 [user] 替换为你的用户名):
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.bitwarden.ssh-agent</string>
<key>ProgramArguments</key>
<array>
<string>/bin/launchctl</string>
<string>setenv</string>
<string>SSH_AUTH_SOCK</string>
<string>/Users/[user]/Library/Containers/com.bitwarden.desktop/Data/.bitwarden-ssh-agent.sock</string>
</array>
<key>RunAtLoad</key>
<true>
<key>StandardOutPath</key>
<string>/tmp/com.bitwarden.ssh-agent.out</string>
<key>StandardErrorPath</key>
<string>/tmp/com.bitwarden.ssh-agent.err</string>
</true></dict>
</plist>
卸载并禁用系统自带 ssh-agent:
launchctl unload -w /System/Library/LaunchAgents/com.openssh.ssh-agent.plist
sudo launchctl disable system/com.openssh.ssh-agent
解决过程
我个人不太喜欢那种把解决过程和结论一起说的那种博客,别人肯定是遇到了问题才找到了你的帖子,肯定是第一时间想看到解决方案,而不是看你怎么找到解决方案的,所以我这里就把解决方案寻找的过程单独写了。
初步探索:查找相关 issue
当使用开源软件遇到问题的时候首先是去 issue 搜索有没有人遇到了一样的问题。于是搜索到了:Eugeny/tabby#10248,里面有评论是这么说的:
But everything works fine if run Tabby via the terminal command “/Applications/Tabby.app/Contents/MacOS/Tabby”
就是说,如果在终端用命令行启动 Tabby,是可以使用设置的环境变量 SSH_AUTH_SOCK 的,我一试还真是。
接下来我问 AI 这种情况可能的原因,得知:
通过 Finder 双击启动的 GUI 应用不会读取 .zshrc 文件,其环境变量主要由 launchd 管理。也就是说,如果希望让通过双击启动的 GUI 应用使用 SSH_AUTH_SOCK,应该使用如下命令设置环境变量:
launchctl setenv SSH_AUTH_SOCK /Users/<user>/Library/Containers/com.bitwarden.desktop/Data/.bitwarden-ssh-agent.sock
理论上运行了这个命令之后,是立即生效,Tabby 应该就能恢复正常了。但遗憾的是,就算是重启之后,问题还是依旧。
进一步探索:从源码调试
于是乎我打算看一看 Tabby 读到的 SSH_AUTH_SOCK 到底是什么,原本想通过查日志的方式,但 Tabby 没有输出这部分日志,那就只能把源码 clone 下来加日志了。
由于上面提到的,在终端里运行是会读取到 .zshrc 里的环境变量的,因此从 WebStorm 启动的 Tabby 是一定能正确使用 SSH-Agent 的,于是考虑加了日志之后打包再启动。
但是,我之前没有开发过 Electron 应用,这跟我以前见过的前端项目有所不同,至少对于 Tabby 这个项目来说,需要先运行 npm run watch 监控文件更改并实时编译,然后运行 npm run start 启动 Electron 的壳子,这样更改了代码,由 watch 任务编译,才能在 Electron 壳子里实现热重载的效果。简单来说,就是需要先把源码编译到壳子里,再打包壳子。
然后对于 Tabby 这个项目,编译需要先执行 node ./scripts/prepackage-plugins.mjs 打包插件,然后再执行 node ./scripts/build-macos.mjs 打包出来的 .app 才能正确运行,否则 Tabby 启动之后会卡在 logo 界面。
经过一番折腾,日志终于加上了,然后打印出类似以下内容:
process.env.SSH_AUTH_SOCK: /private/tmp/com.apple.launchd.p2z8GBlwMr/Listeners
我一开始以为这是 macOS 为保护隐私而做的一个包装,用一个临时路径指向真实路径~~(什么刻板印象)~~,于是我把环境变量注释掉,launchctl 设置的 env 也取消掉,甚至 Bitwarden 也卸载掉,甚至重启之后,打印的还是这玩意。
可我电脑上 Bitwarden 都卸载掉了,还有谁设置 SSH_AUTH_SOCK,于是我把这个路径问 AI,得到答案:
这是 launchd 创建的临时 socket 文件,可以用以下命令确认其所属:
>shel > lsof | grep p2z8GBlwMr >
运行之后得到结果:
madray@HavendeiMac ~ % lsof | grep p2z8GBlwMr
ssh-agent 615 madray 3u unix 0x966184b0673a94c6 0t0 /private/tmp/com.apple.launchd.p2z8GBlwMr/Listeners
一看这还真是个 SSH-Agent,继续追问 AI,得到答案:
macOS 自带一个 SSH-Agent,位于 /System/Library/LaunchAgents/com.openssh.ssh-agent.plist一看还真有:
madray@HavendeiMac ~ % ll /System/Library/LaunchAgents | grep ssh-agent
-rw-r--r-- 1 root wheel 541B May 2 15:31 com.openssh.ssh-agent.plist
总结
到这里问题就很清晰了,是因为 macOS 自带了一个 SSH-Agent,且无法覆盖,那么解决方案就是禁用这个自带的 SSH-Agent,然后自己创建一个 plist 文件设置新的 SSH-Agent 就行了。具体解决方案如开头所示。