Mac 远程控制指南:为 Claude Code 配置专用机器
详细教程展示如何将闲置 Mac 配置为 Claude Code 的受控设备,适合需要 AI 辅助复杂开发任务的工程师。
详细教程展示如何将闲置 Mac 配置为 Claude Code 的受控设备,适合需要 AI 辅助复杂开发任务的工程师。
下面是一份完整的分步指南,介绍如何将你的闲置 Mac 攜造成一台始终在线、可由 Claude Code 完全控制且已启用计算机操作功能的机器。你可以通过手机上的 Claude 应用与它对话,也可以从主力 Mac 通过 SSH 连接它。
如果你是在 GitHub Pages 上阅读本文,这里还有仓库版本。
我想创建一个可以由 Claude Code 自主控制的独立环境,这样就能把一些我不太想在自己机器上运行的任务交给它,例如某些类型的研究任务和开发任务。
在主力机器上运行 Claude Code 本身就存在风险,尤其是在开启 --dangerously-skip-permissions 标志的情况下。你可以在闲置 Mac 上创建一个独立环境,并只授予它所需的访问权限,从而消除或降低这些风险。
这样做还有一个额外好处:无论何时何地,你都能通过手机与 Claude Code 对话。我个人发现这非常实用,因为在移动应用上,我经常更愿意与 Claude Code 而不是普通的 Claude 对话——Claude Code 往往能力更强。
以下指南假设你有一台主力 Mac,以及一台可以用于这套配置的闲置 Mac。不过,你也可以参考这里的思路,将其应用到任意两台机器的组合上。
首先,我们快速解答几个你可能会有的问题。
我非常支持在容器中运行它——甚至还专门构建了一套环境,让这件事变得更方便。不过,我发现容器存在一些限制。首先,它依然运行在你的主力机器上,因此并没有完全隔离。例如,它发出的网络请求仍然会经过你的主力机器。
其次,容器的能力也存在限制。例如,我希望我的智能体能够运行 Unity 进行游戏开发,但没有简单的方法能在容器中做到这一点。其他任何仅支持 Mac 的应用也是如此——你将无法访问它们。如果你希望 Claude Code 通过计算机操作来控制这些应用,例如点击、拖动等,这一点尤其重要。
我个人希望能使用 Claude Code 完整且最新的功能。我也喜欢通过 Claude 应用控制它——我发现这种方式非常方便。此外,如果你恰好订阅了 Claude,还可以使用订阅中包含的用量,这也是一个额外好处。
归根结底,在一台没有什么可损失的机器上运行拥有广泛权限的智能体会更加安全——同时,与使用容器相比,你还能获得一台完整 Mac 的能力。这里采用的方法是:
使用一台旧的或闲置的 Mac,而不是你的主力机器。
创建一个不含个人数据、未登录 Apple ID 的全新本地账户,让智能体接触不到任何敏感信息。
在本地网络中,从主力 Mac 通过 SSH 操作它,并通过手机控制它。
你需要:
一台闲置 Mac(目标机器)。
你的日常主力 Mac(源机器),并且两台机器连接到同一个 Wi-Fi。
你将授予智能体对这台机器的完全访问权限,因此它能够访问机器上存储的任何内容。如果其中已有你不希望它访问的数据,请先抹掉这台机器:
支持此功能的 Mac:系统设置 -> 通用 -> 传输或还原 -> 抹掉所有内容和设置。
较旧的 Intel Mac:重新启动并进入恢复模式(启动时按住 Cmd-R),使用“磁盘工具”抹掉内部驱动器,然后重新安装 macOS。
之后可以选择更新到最新版本的 macOS(系统设置 -> 通用 -> 软件更新)。
创建一个新的本地用户账户(系统设置 -> 用户与群组)。
我建议不要登录 Apple ID。在设置过程中跳过这一步。
该账户需要拥有管理员权限,否则 sudo 会拒绝运行。
系统设置 -> 用户与群组 -> 将该账户设置为“允许此用户管理这台电脑”。
如果以后需要从另一个管理员账户修复它,请运行:sudo dseditgroup -o edit -a <user> -t user admin
在目标机器上开启 SSH,以便源 Mac 可以连接:
sudo systemsetup -setremotelogin on
如果命令失败,并显示 Turning Remote Login on or off requires Full Disk Access privileges,请先授予终端应用“完全磁盘访问权限”:
系统设置 -> 隐私与安全性 -> 完全磁盘访问权限。
点击 +,然后在文件选择器中前往“应用程序 -> 实用工具 -> 终端”,并将其添加进去。
退出并重新打开终端,然后再次运行该命令。
这样,智能体和你的 SSH 命令就能执行管理员任务,而不必每次都输入密码。在目标机器上运行一次以下命令。此时它会要求输入一次登录密码:
echo "<user> ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/<user>-nopasswd >/dev/null
sudo chmod 440 /etc/sudoers.d/<user>-nopasswd
sudo visudo -cf /etc/sudoers.d/<user>-nopasswd # validate - must print 'parsed OK'
这会创建一个小型规则文件,告诉 Mac:<user> 可以运行 sudo,而不显示密码提示:
第 1 行将规则写入 /etc/sudoers.d/。
第 2 行将文件设为只读——否则 sudo 会忽略该文件。
第 3 行验证语法;sudoers 文件中的拼写错误可能会让你彻底无法使用 sudo,因此它必须输出 parsed OK。
完成后,sudo 将不会再显示密码提示。可以使用 sudo -n true 进行测试;如果免密码 sudo 工作正常,该命令会静默成功。
你可以通过主机名或 IP 地址连接目标机器。我建议使用主机名:主机名保持不变,而 IP 地址可能会变化。
主机名(推荐)。在目标机器上运行:
scutil --get LocalHostName # prints the hostname, e.g. MacBook-Pro
在后面加上 .local 即可组成地址:<target-host>.local。你也可以在“系统设置 -> 通用 -> 共享”中找到它,显示为“本地主机名”。
为目标机器设置一个唯一名称。网络中的每台 Mac 都需要拥有唯一的 .local 名称。如果两台机器使用相同名称,该地址可能会指向错误的 Mac。请确保目标机器的名称是唯一的;如果需要,可以重命名:
sudo scutil --set LocalHostName newmacbook # -> newmacbook.local
IP 地址(不推荐)。在目标机器上运行:
ipconfig getifaddr en0 # e.g. 192.168.1.80
请注意,IP 地址可能会在重启后或经过一段时间后发生变化。
在本指南的后续内容中,请将 <user> 替换为目标账户名,将 <target-host> 替换为上面获取的主机名,这样完整地址就是 <user>@<target-host>.local。你也可以用 IP 地址替代 <target-host>.local。
在源 Mac 上创建 SSH 密钥(如果已经有密钥,可以跳过):
ssh-keygen -t ed25519
将你的公钥安装到目标机器上。此命令会要求输入一次目标账户的登录密码:
ssh-copy-id <user>@<target-host>.local
进行测试——以下命令应当直接输出目标机器上的用户名,而不会显示密码提示:
ssh <user>@<target-host>.local whoami
默认情况下,即使已接通电源,macOS 也会在闲置约 10 分钟后进入睡眠状态,导致机器断开网络。要让它永不睡眠,请在目标机器上运行以下命令,也可以从源机器通过 SSH 运行:
sudo pmset -c sleep 0 # never system-sleep while plugged in (-c = on charger)
sudo pmset -c disablesleep 1 # also prevents sleep with the lid closed (clamshell)
sudo pmset -c displaysleep 0 # keep the display on too
pmset -g | grep -iE 'sleep'
如果输出中包含 sleep 0、SleepDisabled 1 和 displaysleep 0,就说明配置已生效。
如果这台机器有时会使用电池供电,请使用 -a 替代 -c,将设置应用到所有电源模式,但代价是会消耗电池电量。
屏幕保护程序启动时,屏幕仍然可能被锁定。阻止屏幕保护程序启动,以免机器自行锁定:
defaults -currentHost write com.apple.screensaver idleTime 0
macOS 自带 pbcopy(写入剪贴板)和 pbpaste(读取剪贴板)。通过 SSH 管道传输,它们可以在两台机器之间移动剪贴板内容——全程加密、点对点传输,不需要 Apple ID 或第三方服务。
clip.sh 将这一功能封装成一个包含两个子命令的命令,并在 pbcopy/pbpaste 仅支持文本的基础上增加了图片支持。将它作为脚本安装到源 Mac 的 PATH 中,并通过 IC_BOX(其中“ic”代表“isolated claude”,即“隔离的 Claude”)将其指向目标主机:
curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/clip.sh -o ~/.local/bin/clip
chmod +x ~/.local/bin/clip
export IC_BOX="<user>@<target-host>.local" # add to ~/.zshrc
clip send——将这台 Mac 的剪贴板发送到目标机器(文本或图片)。对于图片,你可以在目标机器上的 Claude Code 会话中直接按 Ctrl-V 粘贴。
clip get——将目标机器的剪贴板发送到这台 Mac(文本或图片)。
将安装命令发送过去并运行。你可以在源 Mac 上直接将命令推送到目标 Mac 的剪贴板,也可以远程执行。以下命令会安装指定版本,不过你也可以根据需要安装 latest 或 stable:
ssh <user>@<target-host>.local 'curl -fsSL https://claude.ai/install.sh | bash -s -- 2.1.219'
原生安装程序可能会警告 ~/.local/bin 不在 PATH 中。请在目标 Mac 上将其添加到 ~/.zshenv(而不是 ~/.zshrc)来解决这个问题——每个 zsh 都会读取 .zshenv,包括非交互式 shell:
ssh <user>@<target-host>.local 'echo '\''export PATH="$HOME/.local/bin:$PATH"'\'' >> ~/.zshenv'
这一步是可选的,它会通过 setup-claude-env.sh 应用一组带有明确偏好的默认配置,包括 shell 别名、DX 插件、settings.json 调整、GitHub CLI,以及需要主动选择启用的 Playwright MCP 和 yt-dlp。每一项都可以单独开关;完整列表请参阅 claude-env-components.md。
在目标 Mac 上以交互方式运行——它会显示包含所有项目的检查清单(核心项目默认勾选,可选项目默认不勾选),你可以选择任意组合。将脚本下载到目标 Mac 并运行:
ssh -t <user>@<target-host>.local \
'curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/setup-claude-env.sh -o setup-claude-env.sh && bash setup-claude-env.sh'
以非交互方式运行——如果脚本检测到非交互式环境,或者你至少提供了一个参数,它就会进入这种模式。此模式不会显示提示;默认只安装核心组件,你也可以使用参数选择所需组件(--yt-dlp、--playwright、--all、--core):
ssh <user>@<target-host>.local \
'curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/setup-claude-env.sh -o setup-claude-env.sh && bash setup-claude-env.sh --all'
该脚本是幂等的(可以安全地重复运行)。
这两个登录过程都需要交互操作,因此请先通过 SSH 登录:
ssh <user>@<target-host>.local
然后在目标 Mac 上运行 claude——它会进入 Anthropic 账户登录流程。按照提示操作即可(你可以在主 Mac 的浏览器中完成浏览器/设备代码登录流程)。
GitHub——可选,但强烈推荐,这样 AI 智能体才能操作代码仓库:
gh auth login
如果你没有在上一步安装 GitHub CLI,请务必先完成安装。
我个人建议使用一个单独的 GitHub 账户,而不是你的主账户,以免它把主账户弄乱。
这使目标 Mac 上的交互式 claude 会话能够查看(通过截图)并控制(鼠标/键盘)它自己的桌面,而这些操作是通过 SSH 驱动的。
这个功能无法开箱即用——SSH 和 macOS 的权限模型会造成阻碍,因此下面的配置用于绕过这些限制。
为什么需要变通方案:macOS 将屏幕捕获和输入操作置于“屏幕录制”和“辅助功能”权限的保护之下,而这些权限与 GUI 登录会话绑定,因此 SSH 进程无法访问显示界面。解决办法是:使用 LaunchAgent,在 GUI 会话内通过固定套接字维持一个持续运行的 tmux 服务器;在其中创建的每个 claude 会话都会进入该服务器并继承 GUI 会话,因此能够访问显示界面。你则通过 SSH 连接到它。
在目标 Mac 上运行 setup-computer-use.sh:
ssh -t <user>@<target-host>.local \
'curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/setup-computer-use.sh -o setup-computer-use.sh && bash setup-computer-use.sh'
该脚本会安装 LaunchAgent(带有锚点会话 cc 的持久化 tmux 服务器),并在 ~/.claude.json 中启用内置的 computer-use 工具。它需要 tmux(brew install tmux)以及 Claude Pro 或 Max 套餐。脚本可以重复运行;使用 --uninstall 可将其移除。
在源 Mac 上安装 ic.sh(ic 表示“isolated claude”,即“隔离的 Claude”):
curl -fsSL https://raw.githubusercontent.com/ykdojo/claude-controls-mac/main/ic.sh -o ~/.local/bin/ic
chmod +x ~/.local/bin/ic
echo 'export IC_BOX="<user>@<target-host>.local"' >> ~/.zshrc # or edit the default in the script
每次运行 ic 都会在目标机器上创建一个独立的 claude 会话(可以同时运行多个)并连接到它。其参数与 claude 一致,并额外增加了几个子命令:
ic # new claude session
ic -c # continue the most recent conversation (forwards to: claude -c)
ic -r # resume picker (forwards to: claude -r)
ic --chrome # with Claude in Chrome (forwards to: claude --chrome)
ic sh # a plain shell on the box, no claude (alias: ic shell)
ic vnc # open Screen Sharing (VNC) to the box (see step 15)
ic rc # Remote Control: drive the box from your phone (claude remote-control)
ic history # stored conversations: count, location, recent w/ previews (alias: hist)
ic ls # list live sessions (state, age, what's running, conversation)
ic attach <id> # attach a running session (alias: ic a)
ic kill <id> # kill a session (alias: ic k)
ic kill-all # kill all sessions
ic kill-except <id> <id> ... # kill all sessions except the listed ones
ic -h # help
所有 ic 会话都会使用 --dangerously-skip-permissions 运行(而 ic rc 使用 --permission-mode bypassPermissions)。
复制文本:会话运行在 tmux 中,而 Terminal.app 无法接收 claude 发出的剪贴板转义序列(OSC52),因此用鼠标选中一段文本并不能可靠地将其复制到你的 Mac 剪贴板。快速的变通方法是:按 Cmd-A,再按 Cmd-C,复制当前可见屏幕上的全部内容。
由于每个 ic 会话都位于目标机器通过固定套接字运行的 tmux 服务器上,因此源 Mac 上的 Claude Code 会话可以通过 SSH 直接向其中一个会话发送提示词——这对于将工作委派给目标机器并检查其进度非常有用,实现 AI 智能体之间的协作:
# find the session (ic ls prints the ids)
ic ls
# type a prompt into it, then confirm with a second Enter - with longer prompts,
# an Enter sent in the same burst as the text doesn't register as submit and the
# prompt sits in the input box (short prompts usually submit fine)
ssh <user>@<target-host>.local "tmux -S /tmp/cc-tmux.sock send-keys -t <session> 'Switch to main and pull; PR #4 is merged.' Enter"
sleep 2
ssh <user>@<target-host>.local "tmux -S /tmp/cc-tmux.sock send-keys -t <session> Enter"
# read the reply (re-run / poll until the spinner is gone)
ssh <user>@<target-host>.local "tmux -S /tmp/cc-tmux.sock capture-pane -t <session> -p" | tail -30
“屏幕录制”和“辅助功能”权限只能在 GUI 中授予,并且必须由人在机器前操作(亲自操作或通过“屏幕共享”)——macOS 会阻止在这些提示框上执行模拟点击。首次进行屏幕捕获时,你还需要在“绕过窗口选择器”的提示中点击“允许”(该提示大约每月会再次出现)。
这些权限应授予 tmux,而不是 claude。macOS 会将捕获和控制行为归因于调用链中负责执行的进程,在这里就是 tmux 服务器(claude 作为其子进程运行)。因此:
在“屏幕录制”和“辅助功能”中都为 tmux(/usr/local/bin/tmux;Apple Silicon 上则为 /opt/homebrew/bin/tmux)授予权限——“屏幕录制”用于截图,“辅助功能”用于控制鼠标和键盘。
授权后重启 tmux 服务器——正在运行的进程会在启动时缓存其权限状态,因此新授予的权限只有在服务器重启后才会生效:tmux -S /tmp/cc-tmux.sock kill-server(LaunchAgent 会在几秒内重新创建锚点会话)。重启后启动的 claude 会话会获得新权限。
要让相应条目首先出现在“系统设置”中,请触发一次 computer-use 操作(运行 ic,然后让 Claude“take a screenshot”)——macOS 会将 tmux 添加到列表中(初始为关闭状态),之后你便可以将其开启。
“完全磁盘访问权限”可以阻止反复出现“访问其他 App 的数据”提示。在较新版本的 macOS 上,每当 tmux 内部的任何程序接触某个 App 的数据时,tmux 都会针对该 App 触发一个单独的“tmux would like to access data from other apps”提示。向 tmux 授予“完全磁盘访问权限”(同一个“隐私与安全性”面板)可以彻底禁止这些提示:使用 + 按钮添加 tmux 二进制文件(按 Cmd-Shift-G 可直接输入路径)。之后按照上面的说明重启 tmux 服务器。
我个人喜欢在目标机器上运行 VPN,让它的流量通过与本地 IP 不同的出口发出。我使用 Proton VPN——它提供免费选项,而且我已经用了很长时间——当然,你也可以选择其他方案。
如果你已经完成上一步,可以直接让目标机器上的 Claude 来安装:运行 ic 进入会话,然后说“install Proton VPN”。它会下载并安装该应用。以下部分无法由它独立完成:
凭据。即使使用免费套餐也必须登录,而登录信息需要由你亲自输入。请使用第 7 步中的 clip send 安全地发送密码:在你的 Mac 上复制密码,运行 clip send,然后将其粘贴到目标机器的登录字段中。
macOS 权限提示。首次连接时,系统会弹出提示,要求允许添加 VPN/网络配置(还可能要求输入 Mac 密码)——请在目标机器上批准该请求。
该应用是纯 GUI 的,所以驱动它依赖于第 11 步的设置。登录后,智能体可以自己连接和切换服务器。
这个通用流程不仅适用于 VPN 应用 - 它应该适用于几乎任何其他应用。
Remote Control 让你可以通过 Claude 应用从手机控制 Claude Code。有两种使用方式:
在现有会话中运行 /remote-control(或简写 /rc)并按照说明操作 - 你可以从手机控制同一个会话,在两者之间来回切换。
启动一个服务器 claude remote-control,这也让你可以从手机启动全新的会话,而不仅仅是附加到已经打开的会话。
如果你完成了第 11 步,ic rc 会以让从手机生成的会话可以访问计算机使用功能的方式为你启动该服务器。
第 11 步的计算机使用功能故意不会控制浏览器 - 浏览器获得受限层级,Claude 可以看到屏幕上的内容但无法在其中点击或输入。Claude in Chrome 扩展填补了这个空白,提供了适当的浏览器控制:导航、点击、填充表单、读取控制台日志和网络请求。与 Playwright MCP 不同,它控制你的常规 Chrome 配置文件,所以智能体可以使用你在该机器上设置的任何已登录状态。
它需要目标机器上的 Chrome 和直接的 Anthropic 计划(Pro、Max、Team 或 Enterprise)。
你可以要求该机器上的 Claude 安装 Chrome 并打开扩展的 Chrome Web Store 页面。以下是它无法单独完成的部分(在机器上进行这些操作,亲自或通过屏幕共享):
单击"Add to Chrome"。(可选)你可以通过访问 chrome://extensions 将扩展固定到工具栏。
在扩展中登录你的 Claude 账户。你可以从第 7 步使用 clip send 发送登录信息。
然后在目标上的 Claude Code 中启用它:运行 /chrome 会话并选择"Enabled by default"在每个会话中都有浏览器工具,或跳过并按会话使用 claude --chrome(从你的源 Mac 使用 ic --chrome)。如果连接不起作用,尝试重启 Chrome 一次。
为了使浏览器使用高效(使用来自辅助功能树的元素引用而非坐标,避免无请求的截图),第 9 步的环境设置在目标上的 ~/.claude/CLAUDE.md 中添加了"Claude for Chrome"指导部分。如果你跳过了该步骤,重新运行脚本 - 它是幂等的。
如果你的源 Mac 的 Chrome 也有登录到同一账户的扩展,Claude Code 可以连接到任一浏览器。当两者都连接时它会提示你选择,但其本地机器检测可能出错 (#74667)。为了避免这种情况,你可以暂时从源 Mac 的 Chrome 卸载扩展 - 或者在该机器工作时只是关闭 Chrome。
请注意,通过第 13 步从手机生成的会话目前不会获得浏览器工具 (#74671) - 解决方法是在终端中启动会话并使用 /rc 附加。
这让你可以从源 Mac 实时查看目标屏幕 - 并接管其鼠标和键盘 - 使用 macOS 内置的屏幕共享。
这必须在机器的 GUI 中启用 - 自 macOS 12.1 以来无法从命令行启用。
在目标上:System Settings -> General -> Sharing -> Screen Sharing on。如果启用了远程管理,屏幕共享切换可能被隐藏 - 先关闭它。
然后从源 Mac 连接:
open vnc://<user>@<target-host>.local
使用目标账户的登录密码登录并勾选"Remember this password in my keychain"。
.local 名称只在你的局域网上解析,所以到目前为止一切都是仅限局域网的(除了第 13 步的手机控制,通过 Anthropic 中继)。Tailscale 解决了这个问题:它用点对点的、端到端加密的 WireGuard 隧道连接你的机器,所以 SSH、ic、clip 和屏幕共享在任何网络上都能工作,无需向公网暴露任何内容。在你的家庭网络上它走直接的局域网路径,所以本地使用仍然和以前一样快。加入网络只给设备可达性 - SSH 和屏幕共享仍然需要你的密钥或密码。
在目标上安装(Homebrew 公式在 SSH 上无头工作;在最后一个命令打印的 URL 登录 - 带有社交登录的免费账户就可以了):
ssh <user>@<target-host>.local 'brew install tailscale'
ssh <user>@<target-host>.local 'sudo brew services start tailscale'
ssh <user>@<target-host>.local 'sudo tailscale up --operator=<user>'
在源上安装,然后打开 Tailscale 应用并使用同一账户登录 - 设备只在同一账户的网络内看到彼此:
brew install --cask tailscale-app
使用 MagicDNS(默认启用),目标可以从任何地方通过裸主机名访问 - 相同的地址,去掉 .local。在 ~/.zshrc 中切换 IC_BOX,保留原始名称供参考:
export IC_BOX="<user>@<target-host>" # Tailscale - 也适用于远程
# export IC_BOX="<user>@<target-host>.local" # 原始 - 仅限本地网络
屏幕共享的工作方式相同:打开 vnc://<user>@<target-host>。
推荐在管理员控制台中:设备批准(Settings -> Device management),这样受损的 Tailscale 登录不能单独向你的网络添加设备,并在目标上禁用密钥过期(Machines -> …),这样该机器在约 180 天后密钥过期时不会从网络断开。
要确认远程访问确实有效,将源 Mac 放在不同的网络上(例如手机热点)并运行 ic ls。
如果你想了解更多关于 Claude Code 和智能体编程的建议,欢迎订阅我的新闻通讯《有纪律和技能的智能体编程》。