一套以安全隔离、集中开发和会话持续性为目标的个人 AI 编程环境。本文记录我为什么这样搭建、每个组件负责什么、如何安装和验证,以及实践中踩过的坑。
说明:文中
sky是我的远程 Mac mini 的 SSH 主机别名。请替换成自己的主机名、用户名和网络信息。具体软件版本、Pi 的认证方式以及个别配置会随时间变化,执行前应结合本机实际情况确认。
一、为什么要搭建独立的 Vibe Coding 环境
我开始使用 AI Coding Agent 后,首先想到的不是如何提高生成代码的速度,而是两个更基础的问题:安全和集中管理。
1. 不让 Agent 直接操作日常电脑
和代码补全工具不同,Coding Agent 不仅能生成代码,还能读取文件、修改目录、运行 Shell 命令、安装依赖。网上不时能看到 Agent 误删文件、破坏环境的案例。即使只是小概率事件,我也不想让自己的日常 MacBook 承担全部风险。
因此,我选择一台闲置的 2018 Intel Mac mini(6 核 i7、16 GB 内存) 作为专门的开发主机。Agent 直接运行在这台机器的 macOS 用户环境中,不额外使用 Docker 或虚拟机。我主要开发代码,项目通常使用 Git 管理,并且会及时提交。直接运行更省心,也便于调用本机工具链。
必须说明:独立机器不等于沙箱。Agent 仍有可能删除这台 Mac mini 上的文件、未提交的改动和仓库之外的数据;Git 也不是备份系统。重要资料仍需要单独备份,敏感凭据应尽量限制访问,执行危险命令时应保留审批或其他保护措施。
2. 让代码、任务和开发状态集中在一处
我不希望每台电脑都维护一套项目、依赖、Git 状态和 Agent 会话。将所有开发工作放到 Mac mini 后,MacBook 主要负责输入和查看结果。无论我在哪个网络、用哪台客户端连接,都回到同一个环境,避免在多台机器之间同步尚未完成的工作。
由此形成了一个很简单的设计:
MacBook(本地)
└── iTerm2
└── Tailscale 私有网络
└── Mosh(远程连接,网络切换后可恢复)
└── Mac mini / sky(开发服务器)
└── tmux(持久化会话)
└── Pi Coding Agent
├── 项目代码
├── Node.js / pnpm 等开发工具
└── Git 仓库
可选:Debian 云服务器运行 Tailscale Peer Relay,
在无法直接连接时提供自建中继路径。
工具之间并不重复:Tailscale 负责能否到达,Mosh 负责连接体验,tmux 负责会话存续,Pi 负责开发任务。
二、准备 Mac mini 作为开发服务器
1. 基础设置
首先在 Mac mini 上完成 macOS 更新,创建日常开发用户,确认磁盘空间足够。因为它将长期提供远程服务,需要根据实际使用情况配置睡眠和电源策略:避免主机在需要远程连接时进入无法唤醒的状态。这里不建议照抄固定的 pmset 参数,因为台式机、电源和系统版本的设置可能不同。
在 系统设置 → 通用 → 共享 中启用 远程登录(Remote Login),只允许需要的账户通过 SSH 登录。优先使用 SSH 公钥认证,并妥善保护私钥。
从 MacBook 测试:
ssh your-user@your-mac-mini-address
连接成功后可以给它配置 SSH 别名。在 MacBook 的 ~/.ssh/config 中添加:
Host sky
HostName 100.x.y.z
User your-user
IdentityFile ~/.ssh/id_ed25519
这里的 100.x.y.z 只是示意,后面安装 Tailscale 后应换成该设备实际的 Tailscale IP。已有其他密钥时,按实际情况调整 IdentityFile。验证:
ssh sky
2. 开发工具链
我的 Intel Mac mini 上使用过 MacPorts。安装后先确认:
port version
然后根据项目安装 Git、Node.js、pnpm 等工具。Node.js 可以使用版本管理工具(如 nvm 或 mise),避免不同项目的版本需求相互影响。下面是使用 nvm 的一种示例流程,并不代表我的每个项目都采用相同版本:
# 安装并加载 nvm 后
nvm install --lts
nvm use --lts
node --version
npm --version
pnpm 的安装方式应与 Node/Corepack 的实际版本配套。我的机器曾遇到过 pnpm 原生二进制安装被跳过、Corepack shim 和 PATH 混用的问题,所以建议先确认命令实际指向哪里:
command -v node
command -v pnpm
node --version
pnpm --version
这里不强行列出一大串软件安装命令:开发服务器只需要先安装当前项目确实使用的工具,以后再补充。
3. Git 与代码恢复
Git 可以帮助恢复已提交的代码历史,但未提交的文件、未跟踪文件、数据库和仓库之外的内容不能依赖 Git 保护。建议为重要目录另设备份。
三、用 Tailscale 打通远程网络
1. 为什么使用 Tailscale
Mac mini 放在家中,我希望出门后也能访问,不想为此折腾公网 IP、路由器端口映射和动态 DNS。Tailscale 让不同网络中的设备加入同一个私有网络;SSH 和 Mosh 则在这条网络上工作。
在 MacBook 和 Mac mini 上安装 Tailscale,使用同一个 Tailnet 登录。确认两台设备都在线后,在 MacBook 上运行:
tailscale status
tailscale ping sky
ssh sky
tailscale ping 可以帮助判断当前是直连还是经中继。SSH 连通后再继续安装 Mosh,便于把网络问题和终端问题分开排查。
2. 可选:自建 Peer Relay
我还在一台 Debian 12 云服务器上尝试了 Tailscale Peer Relay。它不是每个人都必须部署的组件:当设备能够稳定直连时,通常没有必要增加复杂度。
我的目标是在直连失败或质量不佳时,使用自己可控的中继节点,而不是完全依赖公共 DERP 路径。部署后需要在 Tailnet 的访问控制策略中授权相应的 relay capability,并验证连接是否真的经过 Peer Relay。
tailscale ping sky
我曾观察到连接路径在 DERP、Peer Relay 和 IPv6 直连之间变化。这也提醒我:中继是备用路径,不是固定保证低延迟的加速器。Peer Relay 的安装参数、ACL 和端口要求可能随版本变化,建议以对应版本的 Tailscale 官方文档为准。为了让本文保持可复现的主线,先不把它作为必选步骤。
四、使用 Mosh 获得更可靠的远程连接
1. 为什么 SSH 还不够
普通 SSH 很适合稳定网络,但笔记本合盖、切换 Wi-Fi、移动网络或临时断线后,连接可能断开。我的需求是:重新打开 MacBook 后,尽可能回到原来的终端,而不是重新登录、重新寻找工作状态。
Mosh 的设计适合这种情况:它通过 SSH 完成初始登录和启动远程服务端,之后使用 UDP 维持交互,并支持客户端网络地址变化。
Mosh 不是 SSH 的完整替代品:文件传输、端口转发等任务仍然使用 SSH/scp/rsync 等工具。
2. 安装客户端与服务端
MacBook 上可以通过 Homebrew 安装客户端:
brew install mosh
mosh --version
在 Intel Mac mini 上,我通过 MacPorts 安装:
sudo port install mosh
/opt/local/bin/mosh-server --version
我实际遇到过 mosh-server not found。原因不是没有安装,而是远程非交互 SSH 环境的 PATH 没有包含 /opt/local/bin。
最终使用下面的命令连接成功:
mosh --server=/opt/local/bin/mosh-server sky
如果希望简化命令,可以在本地 shell 中定义一个函数或别名,但最好先确认原始命令能够工作。
3. Mosh 的端口和防火墙
Mosh 默认使用 UDP 端口范围 60000–61000,每个运行中的 mosh-server 会话通常占用其中一个 UDP 端口。
需要确保客户端能通过 Tailscale 网络访问服务端实际选用的 UDP 端口。若启用了 macOS 防火墙或其他过滤规则,需要结合网络拓扑放行必要通信;不应为了省事直接把整个端口范围暴露到公网。
Mosh 服务器进程和 tmux 会话是两回事。客户端退出后,Mosh 会话如何清理取决于进程状态及实现机制;不要把 Mosh 本身当成任务持久化工具。真正需要长期保留的开发任务放进 tmux。
五、使用 tmux 保持开发会话
1. Mosh 和 tmux 解决不同的问题
这是整套方案中最容易混淆的地方:
- Mosh:解决客户端网络变化后如何继续交互。
- tmux:解决终端客户端退出后,如何保留远程会话与其中的进程。
如果 MacBook 关闭了 iTerm2,只要远程主机、tmux 服务和其中的进程还在,重新登录后就可以重新附着到原来的 tmux 会话。这样 Pi 不必随着本地窗口关闭而退出。
2. 安装和最小配置
在 Mac mini 上使用 MacPorts 安装:
sudo port install tmux
tmux -V
建议先从最小配置开始,~/.tmux.conf 可以写成:
set -g mouse on
set -g history-limit 50000
我曾经尝试过更复杂的配置,例如:
set -g extended-keys on
set -g extended-keys-format csi-u
set -s set-clipboard on
set -g allow-passthrough on
以及 OSC 52 的 terminal-overrides。这些设置涉及终端能力、扩展键盘协议和远程剪贴板,不是运行 Pi 的必要条件。它们在不同终端和 tmux 版本中也可能产生兼容问题,因此不建议读者在第一天就全部照搬。
3. 日常命令
# 创建一个名为 coding 的会话
tmux new -s coding
# 列出已有会话
tmux ls
# 重新进入会话
tmux attach -t coding
# 从会话中脱离(在 tmux 内按)
# Ctrl-b,然后按 d
可以将连接流程简化为:
mosh --server=/opt/local/bin/mosh-server sky
# 进入 Mac mini 后
tmux new -A -s coding
new -A 表示如果 coding 会话已存在则附着,否则创建。
4. 在 tmux 中运行 Pi
cd ~/projects/your-project
pi
具体的 Pi 启动命令以当前安装版本为准。随后即使 MacBook 离线,只要 Pi 进程没有因其他原因退出、Mac mini 仍在运行,tmux 中的任务就有机会继续执行。
需要区分:tmux 能保留运行中的进程,但不能保证进程崩溃、主机重启、断电或 Agent 自身报错后自动恢复。这些问题需要另行设计。
六、安装与配置 Pi Coding Agent
1. 为什么选择 Pi
我最终把 Pi 作为这套环境中的主要 AI Coding Agent。相比把文章写成多个 Agent 的横向测评,我更想记录一种稳定的日常使用方式:在同一台远程机器上打开项目,启动 Pi,让它读取代码、修改文件、运行命令,并将结果交给 Git 管理。
2. 安装
Pi 的具体包名、安装命令和模型认证方式可能发生变化,建议先参考项目官方仓库的最新 README:
- Pi Coding Agent:https://pi.dev
以官方文档确认的方式安装后,验证:
command -v pi
pi --version
我之前确实在远程环境启动过 Pi,并看到过模型尚未配置的提示。但没有足够可靠的记录确认当时最终采用了哪一种安装方式、模型提供商和认证方案,因此这里不把猜测写成我的实际配置。
3. 配置模型
第一次启动 Pi 时,需要按当前版本支持的方式登录模型提供商或配置 API Key。我曾遇到过类似下面的提示:
Warning: No models available. Use /login via OAuth or API key
因此,安装成功并不意味着可以立即使用。应先完成模型认证,然后在 Pi 中确认当前可用模型。具体配置文件位置、模型名称和命令选项以当前版本帮助信息为准。
安全方面,我会尽量避免把 API Key 写入仓库,也不会让 Agent 随意读取包含敏感信息的目录。将开发工作放在独立机器上,仍然需要保护凭据。
4. 我的日常开发流程
# MacBook 的 iTerm2 中
mosh --server=/opt/local/bin/mosh-server sky
# Mac mini 中
tmux new -A -s coding
cd ~/projects/your-project
git status
pi
我会让 Pi 在当前项目中完成一个相对明确的任务,过程中检查关键改动,任务结束后再执行测试、查看 diff 和提交:
git status
git diff
# 运行当前项目对应的测试命令
git add -A
git commit -m "Describe completed task"
git push
这套流程的重点是把开发状态集中在一处,并且让每次变更有可追踪的历史。
七、真实踩坑与优化
1. Mosh 会话中 Git 无法使用 SSH Key
我遇到过这样的情况:在 Mac mini 的 GUI Terminal 中 git push 正常,但通过 Mosh/tmux 登录后,Git 提示 SSH 公钥认证失败。
检查发现,Mosh 会话里 ssh-add -l 报错,原因是远程登录环境没有继承 macOS 图形登录会话中的 SSH_AUTH_SOCK。
echo "$SSH_AUTH_SOCK"
ssh-add -l
launchctl print gui/$(id -u) environment | grep SSH_AUTH_SOCK
当时手动将正确的 launchd SSH Agent socket 路径赋给 SSH_AUTH_SOCK 后,ssh-add -l 能够正常列出密钥。
不要直接复制别人机器上的 /var/run/com.apple.launchd.../Listeners 路径:它可能随用户会话而变化。应先确认自己的 Agent socket 和密钥状态,再决定是否将其动态注入远程 shell 环境。
2. 从 MacBook 向远程 Pi 传截图和文件
AI Coding 时经常需要把截图或本地文件交给远程 Agent。我的做法是通过 macOS Shortcuts 和 SSH 文件传输,把截图放到本地与远程一致的目录:
~/Downloads/Screenshots
最简单的手动方案是:
ssh sky 'mkdir -p ~/Downloads/Screenshots'
scp ~/Downloads/Screenshots/example.png sky:~/Downloads/Screenshots/
在此基础上可以再配置快捷指令,实现截图上传和 Finder 文件上传。此前我也探索过将这些功能做成独立的 macOS 菜单栏应用,但这不是核心环境的必需部分。
3. 不要一次性把终端配置调到最复杂
我曾经花不少时间研究 tmux 的 OSC 52 剪贴板、CSI-u 扩展键、Mosh 滚轮和 alternate screen。它们确实影响体验,但都不应该成为开始使用远程 Pi 的前置条件。
现在更倾向于先完成这条最短路径:
SSH 可用 → Tailscale 可达 → Mosh 可连 → tmux 可恢复 → Pi 可开发
确认主流程稳定后,再逐项优化复制粘贴、快捷键和文件上传。
八、最终体验与取舍
现在这套环境的使用方式很简单:我在 MacBook 上打开 iTerm2,通过 Tailscale 网络使用 Mosh 连接 sky,进入已有的 tmux 会话,在项目目录里运行 Pi。代码、依赖和任务状态都留在 Mac mini 上,不需要在不同设备之间来回搬运。
它带来的收益主要有三个:
- 风险边界更清楚:Agent 不直接运行在日常电脑上,降低误操作波及日常环境的可能性。
- 开发状态更集中:项目、Git 和 Agent 会话都在一台机器上,切换客户端不必迁移工作目录。
- 连接更有韧性:Tailscale 解决跨网络访问,Mosh 处理短时断线,tmux 保留远程会话。
代价同样存在:远程机器必须保持可用;网络质量仍会影响交互;macOS 用户环境不是强隔离沙箱;Git 也不能替代备份。对于更高风险的任务,仍然可以进一步加入独立用户、权限限制、沙箱或快照机制。
这套方案的核心是每个工具只解决一个明确的问题。从一台闲置 Mac mini 开始,先建立安全边界和统一开发环境,再逐步改善远程连接和工作连续性,最后把 Pi 放进去执行真正的开发任务。
附录:最短操作清单
在 MacBook 上
# 确认 Tailnet 中能访问 sky
tailscale ping sky
# 首先确认 SSH 可用
ssh sky
# 使用 Mosh 连接
mosh --server=/opt/local/bin/mosh-server sky
在 Mac mini 上
# 进入或创建 tmux 会话
tmux new -A -s coding
# 进入项目并启动 Pi
cd ~/projects/your-project
pi