F[Pi 输入框]整个链路不复杂,但配置时最容易踩的坑是:以为在 ~/.ssh/config 里写了 RemoteForward 就完事了,实际当前已有的 VS Code SSH 连接或 ControlMaster 并不会动态补上新转发。\n">Pi 远程 SSH 图片剪贴板插件配置与踩坑F[Pi 输入框]整个链路不复杂,但配置时最容易踩的坑是:以为在 ~/.ssh/config 里写了 RemoteForward 就完事了,实际当前已有的 VS Code SSH 连接或 ControlMaster 并不会动态补上新转发。\n"> F[Pi 输入框]整个链路不复杂,但配置时最容易踩的坑是:以为在 ~/.ssh/config 里写了 RemoteForward 就完事了,实际当前已有的 VS Code SSH 连接或 ControlMaster 并不会动态补上新转发。\n">

Pi 远程 SSH 图片剪贴板插件配置与踩坑

背景与原理

在 Mac 上通过 VS Code Remote SSH 连到远端无图形界面的 Fedora 开发机使用 Pi 时,按 Ctrl+V 默认读不到 Mac 本地的剪贴板图片。

解决思路很简单:

  1. Mac 端:通过 pngpaste 导出剪贴板图片,用 launchd 监听端口(inetd 模式按需调用);
  2. 传输层:通过 SSH 反向隧道(RemoteForward)把 Mac 本地的服务暴露给 Fedora 的 127.0.0.1:7779
  3. Fedora 端:安装 pi-ssh-image-clipboard 插件,拦截 Ctrl+V,从 127.0.0.1:7779 读取图片写入 /tmp/,并将文件路径填入 Pi 输入框。

整个链路不复杂,但配置时最容易踩的坑是:以为在 ~/.ssh/config 里写了 RemoteForward 就完事了,实际当前已有的 VS Code SSH 连接或 ControlMaster 并不会动态补上新转发

1. Mac 端配置:提供图片服务

安装 pngpaste

1
2
3
4
brew install pngpaste
which pngpaste
# Apple Silicon: /opt/homebrew/bin/pngpaste
# Intel Mac:     /usr/local/bin/pngpaste

配置 clipserve (LaunchAgent)

新建 ~/Library/LaunchAgents/org.pasky.clipserve.plist

注意<string> 中的 pngpaste 路径务必替换为 which pngpaste 的实际输出。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.pasky.clipserve</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/pngpaste</string>
        <string>-</string>
    </array>
    <key>Sockets</key>
    <dict>
        <key>Listeners</key>
        <dict>
            <key>SockNodeName</key>
            <string>127.0.0.1</string>
            <key>SockServiceName</key>
            <string>7779</string>
        </dict>
    </dict>
    <key>inetdCompatibility</key>
    <dict>
        <key>Wait</key>
        <false/>
    </dict>
    <key>StandardErrorPath</key>
    <string>/tmp/pngpaste-clipserve.err</string>
</dict>
</plist>

使用 inetd 模式的好处是:平时不占用后台进程,只有端口收到连接时才调起 pngpaste - 吐出二进制流。

加载服务并验证:

1
2
3
4
5
6
# 启动服务
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/org.pasky.clipserve.plist

# 截图到剪贴板后测试本地能否读取
nc 127.0.0.1 7779 | file -
# 正常输出:/dev/stdin: PNG image data, ...

2. 传输层:常驻 SSH 反向隧道

踩坑:为什么不直接靠 VS Code SSH 转发?

VS Code Remote SSH 复用的是初始建立的 SSH 通道,在 ~/.ssh/config 新增 RemoteForward 后,不断开重连是不会生效的。而且只要 VS Code 窗口关掉或连接重置,转发就会中断。

更稳妥的方式是用单独的 LaunchAgent 维护一条专职转发的 SSH 隧道。

配置 SSH Tunnel (LaunchAgent)

新建 ~/Library/LaunchAgents/org.pasky.pi-clip-tunnel.plist

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.pasky.pi-clip-tunnel</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/ssh</string>
        <string>-S</string>
        <string>none</string>
        <string>-NT</string>
        <string>-o</string>
        <string>BatchMode=yes</string>
        <string>-o</string>
        <string>ExitOnForwardFailure=yes</string>
        <string>-o</string>
        <string>ServerAliveInterval=30</string>
        <string>-o</string>
        <string>ServerAliveCountMax=3</string>
        <string>-o</string>
        <string>ConnectTimeout=10</string>
        <string>-R</string>
        <string>127.0.0.1:7779:127.0.0.1:7779</string>
        <string>fedora</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>10</integer>
    <key>StandardErrorPath</key>
    <string>/tmp/pi-clip-tunnel.err</string>
</dict>
</plist>

关键参数:

  • -S none:不复用已有的 SSH ControlMaster,避免被其他会话状态干扰;
  • -o ExitOnForwardFailure=yes:端口冲突或转发失败时立即退出,交给 launchdKeepAlive 重试;
  • -o BatchMode=yes:非交互模式,依赖配置好的 SSH Key(需已配好公钥免密登录或 1Password / ssh-agent)。

加载隧道:

1
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/org.pasky.pi-clip-tunnel.plist

在 Fedora 上验证隧道是否通畅:

1
2
3
4
5
# 检查端口监听
ss -ltn '( sport = :7779 )'

# 测试读取 Mac 剪贴板
nc 127.0.0.1 7779 | file -

3. Fedora 端配置:安装与配置 Pi 插件

安装插件(锁定 Commit)

Pi Extension 和 Pi 运行在同一权限下,建议锁定经过审计的 commit 安装:

1
pi install 'git:github.com/pasky/pi-ssh-image-clipboard@55db30a67dc039aec243c0737f5fec179add057c'

解绑内置冲突快捷键

Pi 原生自带的 app.clipboard.pasteImage 同样绑定了 Ctrl+V,在无 GUI 的 Linux 下无法工作还会截断事件。

在 Fedora 的 ~/.pi/agent/keybindings.json 中将其置空:

1
2
3
{
  "app.clipboard.pasteImage": []
}

修改后在 Pi 终端里执行 /reload 重新加载配置。

排障速查

  • Fedora 报 Connection refused

    • 原因:远端 7779 端口未建立监听(转发隧道未通)。
    • 排查:Fedora 上运行 ss -ltn '( sport = :7779 )' 检查端口;Mac 上测试 ssh -S none -o BatchMode=yes fedora true 确认 SSH 密钥是否支持非交互免密登录。
  • Remote forwarding failed

    • 原因:远端 7779 端口被旧残余进程占用,或 sshd 禁止了端口转发。
    • 排查:Fedora 上运行 ss -ltnp '( sport = :7779 )' 查看占用进程;检查远端 /etc/ssh/sshd_config 是否开启 AllowTcpForwarding yes
  • 提示 No image on client clipboard

    • 原因:Mac 当前剪贴板中没有可识别的图片数据。
    • 排查:在 Mac 本地执行 pngpaste - | file - 验证剪贴板是否有有效图像流。
  • Ctrl+V 没反应或报快捷键冲突

    • 原因:Pi 内置图片粘贴动作未解绑,或插件未生效。
    • 排查:检查 ~/.pi/agent/keybindings.json 配置,并在 Pi 终端中执行 /reload
  • 发送后模型报错无法识别图片

    • 原因:当前激活的模型不支持 Vision(图片)输入。
    • 排查:运行 pi --list-models,确认当前模型在 images 一列是否标记为 yes

安全与多用户注意事项

  1. 回环端口暴露127.0.0.1:7779 在单人开发机上最省事,但在多人共享的服务器上,其他本地用户也能连该端口偷读你的 Mac 剪贴板。多用户环境建议改用插件支持的 Unix Socket 方案:~/.pi-clip/<client>.sock(配合 chmod 700)。

  2. 临时文件权限: 插件保存的 /tmp/pi-clipboard-*.png 默认权限是 0644。如果涉及敏感业务截图,可配置别名让 Pi 在严格 umask 下运行:

    1
    
    alias pi='(umask 077 && command pi "$@")'
    

    并定期清理历史缓存:

    1
    
    find /tmp -maxdepth 1 -user "$USER" -name 'pi-clipboard-*' -mtime +1 -delete
    
使用 Hugo 构建
主题 StackJimmy 设计