在服务器上运行
Pine 可以不开窗口运行在 macOS 或 Linux 服务器上,只提供浏览器界面。它与桌面端是同一套应用核心,区别只在宿主能力:没有窗口、系统通知、快速输入框、自动更新和 Computer Use。
构建与运行
- 需要 Node.js 22.13 或更高版本(会话索引使用
node:sqlite)。 - 需要按从源码运行准备好源码和依赖。
- 在 Linux 上,Agent 工具的沙箱还需要 bubblewrap、socat 和 ripgrep:
sudo apt install bubblewrap socat ripgrep # Debian / Ubuntu
bun install --frozen-lockfile
bun run build:server
PINE_PASSWORD='至少 8 个字符' node apps/desktop/.vite/server/server.mjs
构建产物在 apps/desktop/.vite/server/,包括:
server.mjs- Agent worker(
agent.mjs及其分块) - 浏览器界面(
renderer/)
Pi 等运行时依赖从仓库的 node_modules 加载,所以要在仓库目录内运行,或把仓库连同依赖一起部署。
node apps/desktop/.vite/server/server.mjs --help 列出全部选项:
| 选项 | 环境变量 | 默认值 |
|---|---|---|
--data-dir |
PINE_DATA_DIR |
$XDG_DATA_HOME/pine-server(~/.local/share/pine-server) |
--port |
PINE_PORT |
9415 |
--lan |
PINE_LAN=1 |
只监听 127.0.0.1 |
--public-origin |
PINE_PUBLIC_ORIGIN |
无 |
--remember-for |
PINE_REMEMBER_FOR |
7d |
--password-file |
PINE_PASSWORD_FILE / PINE_PASSWORD |
无,必须提供 |
--no-password |
PINE_REQUIRE_PASSWORD=0 |
需要密码 |
--remote |
PINE_REMOTE=1(0 关闭) |
保持上次的状态,默认关闭 |
--pair |
PINE_PAIR=1 |
不配对 |
- 访问密码至少 8 个字符,只从环境变量或文件读取,不接受命令行参数,避免出现在进程列表里。启动时密码未变则保留已登录的浏览器;换了密码会让所有浏览器重新登录。
--no-password只用于前面已有登录网关(如 Cloudflare Access)且只监听本机的情况,不能与--lan同时使用。- 收到
SIGTERM/SIGINT时先停止 Agent 和它的工具再退出,再次收到信号则立即退出。
部署建议
推荐只监听本机(默认就是 127.0.0.1:9415),前面放一个终止 TLS 的反向代理,并把 --public-origin 设成代理的公网地址。公网访问请务必使用 HTTPS。systemd 示例:
# /etc/systemd/system/pine.service
[Unit]
Description=Pine
After=network-online.target
[Service]
User=pine
WorkingDirectory=/opt/pine
Environment=PINE_PUBLIC_ORIGIN=https://pine.example.com
Environment=PINE_PASSWORD_FILE=/etc/pine/password
ExecStart=/usr/bin/node apps/desktop/.vite/server/server.mjs
Restart=on-failure
[Install]
WantedBy=multi-user.target
经 Aldine 远程控制
无头服务端可以像桌面端一样经 Aldine 远程控制:
- 手机或另一台电脑的浏览器打开
rc.aldine.cn,配对后即可使用服务器上的 Pine。 - 服务器无需公网地址或端口转发。
- 服务端的网页界面和密码照常工作(配对的设备相当于一个已登录的浏览器)。
PINE_PASSWORD=... node apps/desktop/.vite/server/server.mjs --pair
-
--remote开启并记住远程控制;PINE_REMOTE=0关闭。状态保存在数据目录的agent/remote-control.json(权限0600,含服务器的私钥),重启后保持。 -
--pair在终端打印配对链接和二维码,并开启远程控制。用设备的相机扫描,或在设备浏览器里打开链接。链接几分钟内有效,只能配对一台设备,且是秘密,不要转发。 -
没有图形界面可以点“允许”:能操作服务器终端的人才能拿到链接,所以持有链接的设备会被直接允许,并在日志里打印设备指纹。
-
链接过期或需要再加一台设备时,不必重启:给进程发
SIGUSR2(kill -USR2 <pid>;systemd 下用systemctl kill -s USR2 pine),日志里会出现新的链接。 -
查看和移除已配对的设备要先停止服务端,因为运行中的服务端在内存里保存着设备列表:
node apps/desktop/.vite/server/server.mjs remote list node apps/desktop/.vite/server/server.mjs remote remove <id> # id 可以只写开头几位,要唯一移除后设备再也无法连接。
--data-dir/PINE_DATA_DIR与运行时一致。 -
设备不能再配对其他设备:配对只能由终端发起。
与桌面端的差异
- 数据目录与桌面端分开,默认不共享项目和会话;可以用
--data-dir指向任意目录,但不要让桌面端和服务端同时使用同一个目录。 - 数据目录以
0700创建。服务器没有系统钥匙串,TinyFish 密钥和模型服务密钥一样以仅本用户可读的文件保存。 - 项目文件夹是服务器上的路径。在浏览器里移到废纸篓的文件会移入数据目录下的
trash/,不会被删除。 - 近似位置按服务器的公网 IP 估算,在云主机上通常是机房所在地;需要时可在设置里关闭。
- 支持 macOS 和 Linux(已在 Debian 12 容器中实测)。Windows 不支持,因为它的沙箱需要桌面端的一次性提权配置。
- 服务端是单用户的:所有登录的浏览器共享同一组项目、会话和设置。
Linux 沙箱
Linux 上普通 bash 和文件工具在 bubblewrap 里运行:
- 只能读系统运行时目录(
/usr、/lib*、证书和少数/etc文件)和项目授权的文件夹。 - 只能写可写文件夹,没有网络。
- 命令使用
bash --noprofile --norc,不读取用户的启动文件。
和 macOS 的区别:
-
写到
/tmp等授权以外位置的文件落在每次命令独立的临时文件系统里,命令结束即消失,不会写到主机上;macOS 上这类写入直接失败。项目内的临时文件请用$TMPDIR。 -
bubblewrap 需要非特权用户命名空间。部分主机和容器默认禁止,此时服务端启动时会打印提示,Agent 只能通过需要审批的
privileged_bash执行命令。在 Docker 中运行时加上:docker run --security-opt seccomp=unconfined \ --security-opt apparmor=unconfined \ --security-opt systempaths=unconfined ...