Paste macOS screenshots and clipboard images into Claude Code over SSH.
SSH Image Paste 把 macOS 本地截图、复制的图片或图片文件粘到远程 SSH 会话里。它先把图片上传到远程主机,再把远程图片路径粘进终端,让远程 Claude Code 直接读取图片。
Claude Code 在 SSH 里运行时,读的是远程机器。你在 macOS 截图后,图片还在本地剪贴板;远程机器通常没有 GUI 剪贴板,也看不到这张图片。所以直接 Cmd+V 常会得到:
No image found in clipboard
这个工具解决的就是这条断层:本地有图,远程要读图,中间缺一个自动上传和粘贴路径的桥。
它适合这些场景:
- 在 Warp、Wave、iTerm2、Terminal 或 Ghostty 里 SSH 到远程主机,然后使用 Claude Code。
- 远程主机无 GUI、无
DISPLAY、无wl-copy或xclip。 - 想把截图、设计稿、报错图直接交给远程 AI 编程工具,不想手动
scp。
目标体验:
- 本地截图或复制图片。
- 在 Warp、Wave、iTerm2、Terminal、Ghostty 里的远程 SSH 会话按
Cmd+V。 - 远程 Claude Code 收到
/tmp/ssh-image-paste-*.png路径,并把它当图片读取。
它不依赖远程 GUI 剪贴板。无 GUI 的云主机也能用。
远程 Claude Code 需要读到远程机器上的图片文件。本工具做这几步:
macOS 图片剪贴板
-> 写成本地临时图片
-> 扫描当前活跃 SSH 会话
-> scp 上传到远程 /tmp/ssh-image-paste-*.png
-> 缓存远程路径,保留本机图片剪贴板
-> 终端 Cmd+V 时临时粘贴路径,然后恢复原剪贴板
daemon 会先在后台预上传图片,降低真正粘贴时的等待。--paste-intercept 模式会拦截终端前台的 Cmd+V:如果图片已经预上传,就直接临时把剪贴板切成远程路径并重放 Cmd+V;如果还没上传完,则先补传,再粘贴路径。粘贴完成后会恢复原来的本机图片剪贴板。
需要 macOS 和 SwiftPM。
git clone https://github.com/ripley-xl/ssh-image-paste.git
cd ssh-image-paste
swift test
swift build -c release生成两个可执行文件:
.build/release/ssh-image-paste
.build/release/ssh-image-paste-daemon
ssh-image-paste 是手动命令。
ssh-image-paste-daemon 是后台服务,日常用它。
推荐安装为 LaunchAgent:
cd ssh-image-paste
Scripts/install-launch-agent.sh '~/.local/bin/ssh-clipboard-image-remote.py' --verbose安装脚本默认使用适合无 GUI SSH 主机的模式:--paste-intercept --no-remote-clipboard --interval 0.2。默认不会把本机剪贴板长期改成远程路径;只有终端粘贴瞬间会临时切换并自动恢复。
这条命令会:
- 编译 release binary。
- 写入
~/Library/LaunchAgents/io.github.ripley-xl.ssh-image-paste-daemon.plist。 - 启动
ssh-image-paste-daemon。
当前 daemon 路径可用下面的命令查看。安装脚本会把 LaunchAgent 写成真实路径,并给 release binary 写入稳定的本地签名标识,避免 macOS 权限页把 .build/release 符号链接、重编译后的 CDHash 和真实 binary 混淆:
realpath .build/release/ssh-image-paste-daemon查看运行状态:
launchctl print gui/$(id -u)/io.github.ripley-xl.ssh-image-paste-daemon看日志:
tail -f /tmp/io.github.ripley-xl.ssh-image-paste-daemon.err.log如果启用 --paste-intercept,macOS 需要两个权限:
- 输入监控:监听
Cmd+V。 - 辅助功能:重放
Cmd+V。
先打开真实 binary 所在位置和两个权限页:
REAL_BIN="$(realpath .build/release/ssh-image-paste-daemon)"
open -R "$REAL_BIN"
open 'x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent'
open 'x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility'在“输入监控”和“辅助功能”两个页面都执行同一套操作:
- 如果已经有旧的
ssh-image-paste-daemon,先选中它,用-删除。 - 用
+重新添加 Finder 里高亮的真实 binary,或直接把这个文件拖进列表。 - 打开
ssh-image-paste-daemon右侧开关。
如果添加窗口看不到 .build,按 Cmd+Shift+G,粘贴 realpath 输出的完整路径,再回车。首次切换到稳定签名版本时,建议删除旧的同名条目后重新添加;后续用安装脚本更新时不应再因为 CDHash 变化反复要求授权。
授权后重启 daemon:
launchctl kickstart -k gui/$(id -u)/io.github.ripley-xl.ssh-image-paste-daemon日志里看到这行,说明拦截模式可用:
paste intercept enabled for terminal apps
如果看到下面这行,说明权限还没给对:
paste intercept unavailable; grant Accessibility/Input Monitoring if macOS prompts
没有这两个权限时,daemon 仍可预上传图片,但不能拦截并重放 Cmd+V。如果你想使用旧的轮询路径模式,可以安装时额外加 --local-path-clipboard,让本机剪贴板长期变成 /tmp/ssh-image-paste-*.png。
无 GUI 远程不需要 helper。Claude Code 只要能读 /tmp/ssh-image-paste-*.png 就够了。
如果远程有桌面环境,想顺手写远程系统剪贴板,可以部署 helper:
for h in myserver staging box; do
ssh "$h" 'mkdir -p ~/.local/bin ~/.ssh-image-paste && chmod 700 ~/.ssh-image-paste'
scp Remote/ssh-clipboard-image-remote.py "$h:~/.local/bin/ssh-clipboard-image-remote.py"
ssh "$h" 'chmod +x ~/.local/bin/ssh-clipboard-image-remote.py; python3 -m py_compile ~/.local/bin/ssh-clipboard-image-remote.py'
done在有 user systemd 的远程上,可以把 helper 跑成用户服务。无 GUI 主机即使服务在,通常也没有 wl-copy、xclip、DISPLAY 或 WAYLAND_DISPLAY,所以远程剪贴板写入会跳过。这不影响 Claude Code 读图片路径。
- 本地截图,或复制一张图片。
- 确认本地有活跃 SSH 会话,比如 Warp 里
ssh myserver。 - 在远程 Claude Code 输入框按
Cmd+V。
成功时会粘贴类似路径:
/tmp/ssh-image-paste-06aa3f8739d084598bbc0c13.png
Claude Code 会读取这个远程文件,并把它当作图片。
如果不想跑 daemon,可以手动上传当前剪贴板图片:
.build/release/ssh-image-paste --tty /dev/ttys001 --copy-path --raw也可以显式指定远程:
.build/release/ssh-image-paste --dest myserver --copy-path --raw命令会上传图片,并把远程路径复制回本地剪贴板。
复制一张图片后,检查本地剪贴板:
pbpaste应输出:
/tmp/ssh-image-paste-*.png
检查远程文件:
ssh myserver 'ls -lt /tmp/ssh-image-paste-* 2>/dev/null | head'检查本地 daemon 日志:
tail -n 80 /tmp/io.github.ripley-xl.ssh-image-paste-daemon.err.log常见成功日志:
uploaded 1 file(s) to myserver via ttys001
temporarily copied remote path(s) for terminal paste
restored original local clipboard after terminal paste
这说明 Claude Code 还在读远程 GUI 剪贴板,没有收到远程路径。
检查活跃 SSH:
ps -axo pid=,pgid=,tpgid=,tty=,ucomm=,command= | rg '[s]sh'检查日志:
tail -n 120 /tmp/io.github.ripley-xl.ssh-image-paste-daemon.err.log正常情况下,pbpaste 不会长期显示 /tmp/ssh-image-paste-*.png。只有启用 --local-path-clipboard 时,本机剪贴板才会保留远程路径。
先看日志:
tail -n 80 /tmp/io.github.ripley-xl.ssh-image-paste-daemon.err.log如果看到:
paste intercept unavailable
确认添加的是真实路径,而不是 .build/release 符号链接下的旧条目:
realpath .build/release/ssh-image-paste-daemon去系统设置给这个真实 binary 重新添加并打开“输入监控”和“辅助功能”权限,然后重启 daemon。建议先删除旧的同名 ssh-image-paste-daemon 条目,再重新添加,避免 macOS UI 显示同名但实际授权对象不一致。
无 GUI 远程会失败,这是预期结果。推荐安装时加:
--no-remote-clipboardClaude Code 路径模式不需要远程 GUI 剪贴板。
--tty-inject 会尝试往 /dev/ttysXXX 注入 bracketed paste。macOS 通常返回:
Operation not permitted
这是系统限制。cmux 能直接喂 Ghostty surface,独立后台进程不能。日常不要依赖 --tty-inject。
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/io.github.ripley-xl.ssh-image-paste-daemon.plist
rm ~/Library/LaunchAgents/io.github.ripley-xl.ssh-image-paste-daemon.plist远程 helper 可删:
for h in myserver staging box; do
ssh "$h" 'rm -f ~/.local/bin/ssh-clipboard-image-remote.py'
done