fix: validate provider bootstrap SSH bridge
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / unidesk-host- Success

This commit is contained in:
李昂
2026-07-10 14:51:57 +08:00
parent bfa2ac8fac
commit aa454c6fe1
3 changed files with 72 additions and 14 deletions
+14 -10
View File
@@ -18,9 +18,9 @@ Provider Gateway 是计算节点侧容器。它只主动连出到主 server 暴
## Deployment Method
当前主 server 公网 IP 是 `74.48.78.17``config.json` 中的 `network.publicHost` 必须保持为该地址;公网 frontend 入口是 `http://74.48.78.17:18081/`provider gateway 对外接入入口是 `ws://74.48.78.17:18082/ws/provider`provider ingress 健康检查是 `http://74.48.78.17:18082/health`。主 server 本机 provider 由根目录 `docker-compose.yml``provider-gateway` 服务启动,容器内使用 Docker 内网地址 `ws://backend-core:8081/ws/provider` 自接入;外部计算节点部署 provider-gateway 时必须改用公网 provider ingress URL。
当前主 server 公网 IP 是 `152.53.229.148``config.json` 中的 `network.publicHost` 必须保持为该地址;公网 frontend 入口是 `http://152.53.229.148:18081/`provider gateway 对外接入入口是 `ws://152.53.229.148:18082/ws/provider`provider ingress 健康检查是 `http://152.53.229.148:18082/health`。主 server 本机 provider 由根目录 `docker-compose.yml``provider-gateway` 服务启动,容器内使用 Docker 内网地址 `ws://backend-core:8081/ws/provider` 自接入;外部计算节点部署 provider-gateway 时必须改用公网 provider ingress URL。
新增计算节点推荐使用两项配置的简化挂载流程:在目标节点的 UniDesk 仓库根目录运行 `bun scripts/cli.ts provider attach <PROVIDER_ID> --master-server http://74.48.78.17/ --up`;如果主 server 仍是默认地址,`--master-server` 可省略。该命令生成 `.state/provider-<ID>.env``provider-<ID>.yml`env 文件默认只保留 `UNIDESK_MASTER_SERVER``PROVIDER_ID` 两项Compose 固定 `restart: always``pid: "host"`、Docker socket、只读 `/workspace` 仓库挂载、日志目录、`/run/host-ssh` 维护私钥目录和 `127.0.0.1:18789->18789/tcp` loopback egress proxy 映射。provider-gateway 会从 `UNIDESK_MASTER_SERVER=http://74.48.78.17/` 自动派生 `ws://74.48.78.17:18082/ws/provider`,并自动补齐 `PROVIDER_NAME`默认 labels、心跳/重连参数、`DOCKER_SOCKET_PATH``MONITOR_DISK_PATH``PROVIDER_UPGRADE_*`、runner image 和日志路径;远程升级所需的宿主仓库路径会优先通过 Docker inspect 反查当前容器 `/workspace` 挂载源,避免手写 `/home/ubuntu/unidesk`、Compose project、env file 或 runner image 时出错。显式环境变量仍可覆盖这些默认值;如果主 server 的 provider token 已改成非默认值,挂载时只额外传一次 `--provider-token <token>` 或在 env 文件中加入 `PROVIDER_TOKEN`
新增计算节点推荐使用两项输入的简化挂载流程:在目标节点的 UniDesk 仓库根目录运行 `bun scripts/cli.ts provider attach <PROVIDER_ID> --master-server http://152.53.229.148/ --up`;如果主 server 仍是默认地址,`--master-server` 可省略。该命令生成 `.state/provider-<ID>.env``provider-<ID>.yml`env 文件记录 master 地址、Provider 身份、升级坐标和 Host SSH 目标Compose 固定 `restart: always``pid: "host"`、Docker socket、只读 `/workspace` 仓库挂载、日志目录、`/run/host-ssh` 维护私钥目录和 `127.0.0.1:18789->18789/tcp` loopback egress proxy 映射。provider-gateway 会从 `UNIDESK_MASTER_SERVER=http://152.53.229.148/` 自动派生 `ws://152.53.229.148:18082/ws/provider`,并自动补齐默认 labels、心跳/重连参数、`DOCKER_SOCKET_PATH``MONITOR_DISK_PATH` 和日志路径;远程升级所需的宿主仓库路径会优先通过 Docker inspect 反查当前容器 `/workspace` 挂载源,避免手写 `/home/ubuntu/unidesk`、Compose project、env file 或 runner image 时出错。显式环境变量仍可覆盖这些默认值;如果主 server 的 provider token 已改成非默认值,挂载时只额外传一次 `--provider-token <token>` 或在 env 文件中加入 `PROVIDER_TOKEN`
手写 Compose 仍必须满足同一部署约束:挂载 `/var/run/docker.sock:/var/run/docker.sock` 作为 Docker 状态采集、任务执行和远程升级的唯一自动化通道,并让 provider-gateway 容器运行在宿主 PID namespaceCompose 写法是 `pid: "host"``docker run` 写法是 `--pid host`。缺少该配置时只能看到 provider 容器命名空间内的进程,不能视为完整节点资源监控。provider-gateway 容器必须使用 Docker restart policy `always``unless-stopped`、空 restart policy、手动 `docker stop` 后期待 Docker daemon 重启自动拉起,都不符合长期接入要求。长期接入节点必须保留只读 `/workspace` 仓库挂载,使 `provider.upgrade mode=schedule` 能构建候选 gateway 且只重建 `provider-gateway` service,不影响 database、backend-core、frontend 或业务用户服务。provider-gateway 部署必须同时交付 Host SSH / WSL SSH 透传维护桥;WSL 节点默认把私钥目录挂载到 `/run/host-ssh` 后,gateway 会在发现 `/run/host-ssh/id_ed25519` 时自动使用 `host.docker.internal:22`、从仓库路径推断 WSL 用户与默认工作目录,必要时仍可用 `HOST_SSH_HOST``HOST_SSH_PORT``HOST_SSH_USER``HOST_SSH_KEY``HOST_REMOTE_CWD` 显式覆盖。
@@ -28,6 +28,8 @@ Provider Gateway 是计算节点侧容器。它只主动连出到主 server 暴
新增 provider-gateway 或 legacy bootstrap 后,Docker socket、远程升级和 SSH 透传必须作为同一个部署包验收,不能只让 provider 在线就认为完成。节点侧应先启动目标宿主或 WSL 的 sshd,把维护公钥写入目标用户 `authorized_keys`,再用只读目录挂载私钥到 provider-gateway 容器内 `/run/host-ssh`provider-gateway 环境变量必须包含 `HOST_SSH_HOST``HOST_SSH_PORT``HOST_SSH_USER``HOST_SSH_KEY``HOST_REMOTE_CWD`。注册成功后,主 server 看到的 labels 必须同时满足 `hostSshConfigured=true``hostSshKeyPresent=true``hostSshTarget` 指向目标 sshd,且 `unideskCapabilities` 包含 `host.ssh`
`provider attach --up` 必须把容器内私钥 presence 和真实 SSH probe 纳入 `containerValidation.ok`;只有 `hostSshKeyPresent=true``hostSshProbeOk=true` 才能接受部署。Docker Desktop 使用 WSL 私钥目录时,必须从拥有该目录的 WSL distro 发起 Compose;从 Windows Docker CLI 直接挂载 `/home/<user>/...` 可能落到 Docker Desktop VM 的空目录,容器仍会 running,但维护桥实际不可用。
使用初始密码把新增外部节点引导成密钥登录时,本机缺少 `sshpass` 应先安装 `sshpass`,再用受控脚本写入维护公钥;不要长期保留交互式密码登录流程,也不要把密码写入命令输出、文档、Compose env 或仓库文件。
计算节点镜像必须包含 `ssh` 客户端,否则 provider 虽然可以在线并执行 Docker 任务,但 `host.ssh` 调度会在节点侧失败。标准 `src/components/provider-gateway/Dockerfile` 已安装 `openssh-client`;如果节点使用本地兜底镜像或复制 Bun/Docker CLI 的自定义镜像,也必须同时安装或复制可用的 OpenSSH client,并在容器内通过 `ssh -V``test -r /run/host-ssh/id_ed25519``ssh -i /run/host-ssh/id_ed25519 ... <target> hostname` 做本地烟测后再接入主 server。
@@ -54,7 +56,7 @@ TCP pool 的长期方向是把 `trans`/`tran` 变成真正并发的短连接工
## WSL Compute Node Deployment
WSL 计算节点和普通外部计算节点使用同一套 provider ingress 协议:节点不暴露入站端口,只需要从 WSL 主动连出到 `ws://74.48.78.17:18082/ws/provider`。已验证的 WSL 节点命名口径是短设备编号,例如 `PROVIDER_ID=D601``PROVIDER_NAME=D601` 保持一致;新增 WSL 节点必须替换为唯一 ID,避免把旧节点状态覆盖到同一条 `unidesk_nodes.provider_id` 记录。
WSL 计算节点和普通外部计算节点使用同一套 provider ingress 协议:节点不暴露入站端口,只需要从 WSL 主动连出到 `ws://152.53.229.148:18082/ws/provider`。已验证的 WSL 节点命名口径是短设备编号,例如 `PROVIDER_ID=D601``PROVIDER_NAME=D601` 保持一致;新增 WSL 节点必须替换为唯一 ID,避免把旧节点状态覆盖到同一条 `unidesk_nodes.provider_id` 记录。
WSL 节点应优先使用 WSL 内部原生 Docker Engine 和 `/var/run/docker.sock`,让资源采样、Docker 状态和任务执行都反映 WSL 计算环境本身,而不是 Windows 或 Docker Desktop 的代理上下文。如果当前 Docker CLI 实际连接 Docker Desktop daemon,也可以先作为可用计算节点接入,但必须在 `PROVIDER_LABELS_JSON` 中显式标注 `dockerContext=docker-desktop` 或等价标签,避免在 frontend 中把 Docker Desktop 资源误判为 WSL 原生资源。UniDesk 仓库建议放在 WSL 原生文件系统,例如 `/home/ubuntu/unidesk`,再按需从 Windows 工作区同步源码;长期运行和升级挂载应使用 WSL 原生路径只读挂到容器内 `/workspace`,减少 `/mnt/c` 权限、性能和路径转换问题。
@@ -74,19 +76,21 @@ WSL 节点出网异常时,先把代理设置固化到目标 WSL 用户的 `~/.
Windows 代理程序常见默认只监听 `127.0.0.1:7890`,此时 WSL 访问 `<windows-host-ip>:7890` 会失败。可选修复是让代理程序监听 WSL 可达网卡,或在 Windows 用户态启动一个幂等 TCP relay,把 `<windows-host-ip>:7890` 转发到 `127.0.0.1:7890``netsh interface portproxy` 通常需要管理员权限,不应作为普通节点 bootstrap 的唯一方案。`.bashrc` 可以在发现 `<windows-host-ip>:7890` 不可达时调用 Windows 侧脚本启动该 relay,但 relay 脚本、临时 apt source、日志和节点私钥都属于本地运行态,必须留在 `.state` 或用户目录,不能提交到仓库。
apt 或镜像构建遇到 `archive.ubuntu.com` 超时、代理 `503` 或 registry metadata 拉取失败时,优先使用节点侧临时镜像源或临时 apt sources 完成 bootstrap,例如把 Ubuntu 源临时替换到可达镜像后安装 `openssh-server`。这类网络兜底只解决节点初始化,不改变 `PROVIDER_SERVER_URL`、provider token、Docker socket、Host SSH 透传和 remote CLI 验收标准。
apt 或镜像构建遇到 `archive.ubuntu.com` 超时、代理 `503` 或 registry metadata 拉取失败时,优先使用节点侧临时镜像源或临时 apt sources 完成 bootstrap,例如把 Ubuntu 源临时替换到可达镜像后安装 `openssh-server`。这类网络兜底只解决节点初始化,不改变 `UNIDESK_MASTER_SERVER`、provider token、Docker socket、Host SSH 透传和 remote CLI 验收标准。
Docker build 的 `HTTP_PROXY` / `HTTPS_PROXY` build args 只覆盖 Dockerfile 中的 `RUN` 步骤,不能保证 BuildKit 拉取 `FROM` metadata 时使用代理。若 `auth.docker.io` metadata 超时,应先让 Docker daemon 使用受控代理,或通过 `host.docker.internal:7890` 和可信 image transport 预热精确 base image,再执行标准 Dockerfile 构建;不得把旧 provider 最终镜像冒充 base image。预热完成后仍要用 `--no-cache` 验证 `apk``bun install` 经代理成功,并由最终 Compose heartbeat 和 Host SSH 原入口验收收口。
## WSL Image Build Fallback
标准镜像构建路径使用 `src/components/provider-gateway/Dockerfile`,基础镜像为 `oven/bun:1-alpine`,并在镜像内安装 Bun、Docker CLI、Compose plugin、`df` 和 provider-gateway 源码。WSL 或 Docker Desktop 环境中的 registry mirror、DNS 或代理配置可能导致 `oven/bun` 元数据拉取失败;此时不要修改 provider ingress 协议或服务端配置,应改用节点侧镜像交付兜底。
标准镜像构建路径使用 `src/components/provider-gateway/Dockerfile` 及其中声明的精确 `oven/bun` 基础镜像,并在镜像内安装 Docker CLI、Compose plugin、OpenSSH client 和 provider-gateway 源码。WSL 或 Docker Desktop 环境中的 registry mirror、DNS 或代理配置可能导致 `oven/bun` 元数据拉取失败;此时不要修改 provider ingress 协议或服务端配置,应改用节点侧镜像交付兜底。
可复用的兜底方式是使用本机已存在的 Debian/Node 基础镜像,复制 WSL 本地 `bun``docker``docker-compose` plugin 到镜像内,再复制 `src/components/provider-gateway/src``src/components/shared/src`。该镜像必须能在容器内执行 `bun --version``docker version``docker compose version``ssh -V`,并通过挂载的 `/var/run/docker.sock` 执行 `docker info``docker ps`;只有这些命令通过后才能作为 `unidesk_provider-gateway:<provider-id>` 运行。该兜底镜像只改变节点侧交付方式,不改变 `PROVIDER_SERVER_URL`、token、heartbeat、labels、Docker socket、SSH 私钥只读挂载和服务端验收标准。
可复用的兜底方式是使用本机已存在的 Debian/Node 基础镜像,复制 WSL 本地 `bun``docker``docker-compose` plugin 到镜像内,再复制 `src/components/provider-gateway/src``src/components/shared/src`。该镜像必须能在容器内执行 `bun --version``docker version``docker compose version``ssh -V`,并通过挂载的 `/var/run/docker.sock` 执行 `docker info``docker ps`;只有这些命令通过后才能作为 `unidesk_provider-gateway:<provider-id>` 运行。该兜底镜像只改变节点侧交付方式,不改变 `UNIDESK_MASTER_SERVER`、token、heartbeat、labels、Docker socket、SSH 私钥只读挂载和服务端验收标准。
如果兜底镜像还要承载 WSL SSH 透传,必须同时复制 `/usr/bin/ssh``/etc/ssh/ssh_config``/etc/ssh/ssh_config.d` 以及 `ldd /usr/bin/ssh` 输出的全部动态库,尤其不能漏掉 `/lib64/ld-linux-x86-64.so.2` 这类 ELF loader。只在 rootfs 中看到 `/usr/bin/ssh` 文件不代表可执行;验收必须运行 `docker run --rm <image> /bin/sh -lc '/usr/bin/ssh -V && /usr/bin/docker compose version'`,再以实际 provider 容器执行 `ssh -T -i /run/host-ssh/id_ed25519 -p 22 ubuntu@host.docker.internal hostname`。若 Compose 使用 Docker bridge 网络,WSL 节点必须在 compose 文件中加入 `extra_hosts: ["host.docker.internal:host-gateway"]`,否则容器内可能无法把 `host.docker.internal` 解析到 WSL 宿主。
## Deployment Verification
provider-gateway 部署是否成功必须以 UniDesk frontend 中可见的 Provider 信息为准,不能只看节点容器 `running`。验证时访问 `http://74.48.78.17:18081/`,使用配置中的账号密码登录,进入 `资源节点 / 节点清单`,确认目标 `PROVIDER_ID``PROVIDER_NAME``online` 状态、`lastHeartbeat` 和 labels 可见;点击该节点的 `查看原始JSON`,确认 raw payload 中的 `providerId``name``status``labels` 与部署环境变量一致。随后进入 `资源节点 / 资源监控`,确认该 Provider 有 CPU、实际内存和硬盘采样曲线;进入 `资源节点 / Docker 状态`,确认 Docker daemon、containers、images、volumes、networks 已渲染出来,且 `dockerSocketPresent` 或 Docker ready 状态与预期一致。只有这些前端信息都能通过 UniDesk 正常读取,才说明 provider-gateway 已经真正挂载到主 server。
provider-gateway 部署是否成功必须以 UniDesk frontend 中可见的 Provider 信息为准,不能只看节点容器 `running`。验证时访问 `http://152.53.229.148:18081/`,使用配置中的账号密码登录,进入 `资源节点 / 节点清单`,确认目标 `PROVIDER_ID``PROVIDER_NAME``online` 状态、`lastHeartbeat` 和 labels 可见;点击该节点的 `查看原始JSON`,确认 raw payload 中的 `providerId``name``status``labels` 与部署环境变量一致。随后进入 `资源节点 / 资源监控`,确认该 Provider 有 CPU、实际内存和硬盘采样曲线;进入 `资源节点 / Docker 状态`,确认 Docker daemon、containers、images、volumes、networks 已渲染出来,且 `dockerSocketPresent` 或 Docker ready 状态与预期一致。只有这些前端信息都能通过 UniDesk 正常读取,才说明 provider-gateway 已经真正挂载到主 server。
WSL 节点还应补充一次真实调度验证:向该 `PROVIDER_ID` 下发 `docker.ps`,任务必须从 `dispatched` 进入 `succeeded`,并在结果中看到 WSL Docker daemon 返回的容器列表;对于容器化运行的 provider-gateway,列表中通常应包含 `unidesk-provider-gateway-<PROVIDER_ID>`。这一步可以同时证明 provider WebSocket、服务端任务路由、节点侧 Docker socket 和结果回传链路都已贯通。
@@ -170,7 +174,7 @@ D601 这类长期 WSL provider 不得因为单一路径失败被直接写成全
## Manual Upgrade Maintenance
手动升级只用于把旧节点 bootstrap 到支持 always-enabled 远程升级的版本;bootstrap 完成后,常规重建/升级必须回到 `provider.upgrade mode=schedule`,不得再用 SSH 透传同步重建 `provider-gateway`。节点侧维护步骤是:进入节点本地 UniDesk 仓库,确认 GitHub 访问走本机 provider-gateway WS egress proxy,例如 `git config --local http.proxy http://127.0.0.1:18789``git config --local https.proxy http://127.0.0.1:18789` 后再执行 `git pull --ff-only` 获取主 server 已推送版本;不得让 provider 侧 Git 拉取退回直连公网、SSH SOCKS 或公开 master proxy。随后确认 `.state/provider-<ID>.env` 中存在 `PROVIDER_SERVER_URL=ws://74.48.78.17:18082/ws/provider``PROVIDER_ID=<ID>``PROVIDER_NAME=<ID>``PROVIDER_TOKEN``PROVIDER_LABELS_JSON``PROVIDER_UPGRADE_HOST_PROJECT_ROOT=/home/ubuntu/unidesk``PROVIDER_UPGRADE_WORKSPACE_PATH=/workspace``PROVIDER_UPGRADE_COMPOSE_FILE``PROVIDER_UPGRADE_ENV_FILE``PROVIDER_UPGRADE_COMPOSE_PROJECT``PROVIDER_UPGRADE_SERVICE=provider-gateway``PROVIDER_UPGRADE_RUNNER_IMAGE=unidesk_provider-gateway:<id>``DOCKER_SOCKET_PATH=/var/run/docker.sock``MONITOR_DISK_PATH=/`、心跳和重连参数。旧 env 文件中如果还残留 `PROVIDER_UPGRADE_ENABLED`,新版 provider-gateway 会忽略它;长期文档和新部署不得再依赖这个键。
手动升级只用于把旧节点 bootstrap 到支持 always-enabled 远程升级的版本;bootstrap 完成后,常规重建/升级必须回到 `provider.upgrade mode=schedule`,不得再用 SSH 透传同步重建 `provider-gateway`。节点侧维护步骤是:进入节点本地 UniDesk 仓库,确认 GitHub 访问走本机 provider-gateway WS egress proxy,例如 `git config --local http.proxy http://127.0.0.1:18789``git config --local https.proxy http://127.0.0.1:18789` 后再执行 `git pull --ff-only` 获取主 server 已推送版本;不得让 provider 侧 Git 拉取退回直连公网、SSH SOCKS 或公开 master proxy。随后确认 `.state/provider-<ID>.env` 中存在 `UNIDESK_MASTER_SERVER=http://152.53.229.148/``PROVIDER_ID=<ID>``PROVIDER_NAME=<ID>``PROVIDER_TOKEN``PROVIDER_LABELS_JSON``PROVIDER_UPGRADE_HOST_PROJECT_ROOT=/home/ubuntu/unidesk``PROVIDER_UPGRADE_WORKSPACE_PATH=/workspace``PROVIDER_UPGRADE_COMPOSE_FILE``PROVIDER_UPGRADE_ENV_FILE``PROVIDER_UPGRADE_COMPOSE_PROJECT``PROVIDER_UPGRADE_SERVICE=provider-gateway``PROVIDER_UPGRADE_RUNNER_IMAGE=unidesk_provider-gateway:<id>``DOCKER_SOCKET_PATH=/var/run/docker.sock``MONITOR_DISK_PATH=/`、心跳和重连参数。旧 env 文件中如果还残留 `PROVIDER_UPGRADE_ENABLED`,新版 provider-gateway 会忽略它;长期文档和新部署不得再依赖这个键。
如果节点固定 UniDesk 仓库存在并行未提交修改或落后太多,不能为了 provider-gateway 恢复而 stash、reset 或把脏工作区作为构建真相。应在同一节点的固定仓库下从最新 remote 创建独立 clean runtime worktree,例如 `/home/ubuntu/unidesk/.worktree/provider-gateway-<ID>-runtime`,把节点本地 `.state/provider-<ID>.env``provider-<ID>.yml` 和必要的本地 Dockerfile/Compose 运行态指向该 worktree,并把 `PROVIDER_UPGRADE_HOST_PROJECT_ROOT` 改为该 clean worktree。bootstrap 成功后仍必须立刻执行标准 `provider.upgrade mode=schedule` 和 Host SSH/`trans` 原入口验收;clean runtime worktree 是恢复固定 repo 隔离性的手段,不是新的长期 source truth。
@@ -180,7 +184,7 @@ D601 这类长期 WSL provider 不得因为单一路径失败被直接写成全
如果节点已有专用 Compose,优先用节点本地 Compose 手动重建一次:`docker compose --env-file .state/provider-<ID>.env -f <compose-file> -p <compose-project> up -d --no-deps --build --force-recreate provider-gateway`。这条命令必须在节点本地终端、节点自有 Web terminal、系统计划任务或 detached shell 中执行;不得通过正在被重建的 UniDesk provider-gateway 自己提供的 SSH 透传同步执行,否则旧 provider 容器停止时会切断 SSH client,可能导致重建中断在旧容器已停、新容器未起的状态。若只能通过 UniDesk 触达该节点,必须使用 `provider.upgrade mode=schedule` 的 detached updater,或先用节点本地 `nohup`/systemd 启动一个不依赖当前 provider 容器生命周期的重建脚本。老版 `docker-compose` 可能在重建已存在容器时因为 `ContainerConfig` 兼容问题失败;此时只能移除目标 provider-gateway 容器后重新 `up -d --no-deps provider-gateway`,不得执行 `down -v``docker volume rm` 或任何会影响 database 命名卷的命令。如果节点当前只有 `docker run` 部署,则先构建镜像 `docker build -f src/components/provider-gateway/Dockerfile -t unidesk_provider-gateway:<id> .`,再以固定容器名重建:使用 `--restart always --pid host`,挂载 `/var/run/docker.sock:/var/run/docker.sock``/home/ubuntu/unidesk:/workspace:ro`、节点日志目录到 `/var/log/unidesk`,如需 WSL SSH 维护桥还要把只读私钥目录挂载到 `/run/host-ssh`,并使用同一个 `.state/provider-<ID>.env` 启动。无论 Compose 还是 `docker run`,容器名和镜像 tag 都必须带 Provider ID,便于 Docker 状态页、进程资源表、任务历史和节点本地排障互相对应。
手动升级完成后的判定标准固定为主 server 可观测结果,而不是节点容器 `running`:访问公网 frontend `http://74.48.78.17:18081/`,确认该 Provider 在线;随后在任意装有本仓库且 `config.json` 含正确 frontend 登录凭据的计算节点上执行 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug dispatch <PROVIDER_ID> provider.upgrade --mode schedule --wait-ms 300000`,确认任务 `succeeded` 且 result 包含最终 Compose 容器 `containerId`、目标 gateway `version``restartPolicy=always``pidMode=host` 和新的 `heartbeatTimestamp`;最后再次查看 frontend 或执行 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug health`,确认节点重连、指标恢复、labels 中 `host.ssh` 能力存在。每个 provider-gateway 手动升级后都必须用 remote CLI 再执行 `bun scripts/cli.ts --main-server-ip 74.48.78.17 debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000``bun scripts/cli.ts --main-server-ip 74.48.78.17 ssh <PROVIDER_ID> hostname`,验证维护桥没有在升级后丢失;该 remote CLI 默认走公网 frontend,不需要指定 `--main-server-key`
手动升级完成后的判定标准固定为主 server 可观测结果,而不是节点容器 `running`:访问公网 frontend `http://152.53.229.148:18081/`,确认该 Provider 在线;随后在任意装有本仓库且 `config.json` 含正确 frontend 登录凭据的计算节点上执行 `bun scripts/cli.ts --main-server-ip 152.53.229.148 debug dispatch <PROVIDER_ID> provider.upgrade --mode schedule --wait-ms 300000`,确认任务 `succeeded` 且 result 包含最终 Compose 容器 `containerId`、目标 gateway `version``restartPolicy=always``pidMode=host` 和新的 `heartbeatTimestamp`;最后再次查看 frontend 或执行 `bun scripts/cli.ts --main-server-ip 152.53.229.148 debug health`,确认节点重连、指标恢复、labels 中 `host.ssh` 能力存在。每个 provider-gateway 手动升级后都必须用 remote CLI 再执行 `bun scripts/cli.ts --main-server-ip 152.53.229.148 debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000``bun scripts/cli.ts --main-server-ip 152.53.229.148 ssh <PROVIDER_ID> hostname`,验证维护桥没有在升级后丢失;该 remote CLI 默认走公网 frontend,不需要指定 `--main-server-key`
## Host SSH Maintenance Bridge
@@ -196,4 +200,4 @@ Host SSH 的 cwd 语义必须区分默认目录和显式 workspace。未传 cwd
面向人的终端入口是 `trans <PROVIDER_ID> [ssh-like args...]`。无后续参数时打开远端登录 shell,有后续参数时执行远端命令并返回远端 exit code;该入口的 client 侧仍连接 backend-core 内网 `/ws/ssh` brokercore 只用 provider WebSocket 下发 open/dispatch 控制消息,终端 stdin/stdout/stderr 数据面必须走 provider 主动连接 main server 的 `host.ssh.tcp-pool` TCP warm pool,不新增计算节点入站要求,也不保留旧 WebSocket 数据 fallback。传统 ssh 传输参数由 provider-gateway 环境变量统一控制,CLI 只负责把 Provider ID 后的远端命令和终端 stdin/stdout/stderr 透传过去。非交互单进程远端命令优先使用 argv 入口:`trans D601 argv true`;需要 shell 特性时在 operation 位置显式写 `sh``bash`,例如 `trans D601 sh -- '<command>'``trans D601 bash -- '<bash command>'`。WSL 节点需要同时看清 Linux/WSL 与 Windows 两套 skill 时,使用 `trans <PROVIDER_ID> skills`,该命令只通过已建立的维护桥读取 `SKILL.md` 元数据,不要求 provider-gateway 新增业务 API。
验证 WSL SSH 桥时,先在目标 WSL 中启动 sshd 并确保维护公钥写入目标用户的 `authorized_keys`,再确认目标 provider 注册 labels 中 `unideskCapabilities` 包含 `host.ssh`。运行 `bun scripts/cli.ts debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000` 后,结果应在 `debug task latest` 或前端任务历史中显示 `status: succeeded``probeLine``UNIDESK_SSH_TEST``exitCode: 0`,并且目标节点 labels 中 `hostSshKeyPresent` 为 true;随后运行 `trans <PROVIDER_ID> argv true` 验证非交互 argv 维护命令,再运行 `trans <PROVIDER_ID> hostname` 验证近似原生 ssh 的远端命令体验。在计算节点本机自测时,使用 remote CLI 透传同一组命令:`bun scripts/cli.ts --main-server-ip 74.48.78.17 debug health``bun scripts/cli.ts --main-server-ip 74.48.78.17 debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000``bun scripts/cli.ts --main-server-ip 74.48.78.17 ssh <PROVIDER_ID> argv true``bun scripts/cli.ts --main-server-ip 74.48.78.17 ssh <PROVIDER_ID> hostname`;默认 remote CLI 走公网 frontend 登录态,不需要主 server SSH key。健康检查必须能看到该 Provider 在线、`hostSshConfigured=true``hostSshKeyPresent=true``hostSshTarget` 正确、`unideskCapabilities` 包含 `host.ssh`probe 必须返回 `UNIDESK_SSH_TEST``ssh <PROVIDER_ID> argv true``ssh <PROVIDER_ID> hostname` 必须 exit code 为 0。如果 D518 这类 WSL 节点没有公网 SSH 入口,也必须通过这个 provider-gateway 自连维护桥完成验证,而不是要求主 server 直接连节点公网 22 端口;旧版 provider 未声明 `host.ssh` 时必须先升级 provider-gateway,否则 core 会拒绝 SSH 透传。
验证 WSL SSH 桥时,先在目标 WSL 中启动 sshd 并确保维护公钥写入目标用户的 `authorized_keys`,再确认目标 provider 注册 labels 中 `unideskCapabilities` 包含 `host.ssh`。运行 `bun scripts/cli.ts debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000` 后,结果应在 `debug task latest` 或前端任务历史中显示 `status: succeeded``probeLine``UNIDESK_SSH_TEST``exitCode: 0`,并且目标节点 labels 中 `hostSshKeyPresent` 为 true;随后运行 `trans <PROVIDER_ID> argv true` 验证非交互 argv 维护命令,再运行 `trans <PROVIDER_ID> hostname` 验证近似原生 ssh 的远端命令体验。在计算节点本机自测时,使用 remote CLI 透传同一组命令:`bun scripts/cli.ts --main-server-ip 152.53.229.148 debug health``bun scripts/cli.ts --main-server-ip 152.53.229.148 debug dispatch <PROVIDER_ID> host.ssh --wait-ms 15000``bun scripts/cli.ts --main-server-ip 152.53.229.148 ssh <PROVIDER_ID> argv true``bun scripts/cli.ts --main-server-ip 152.53.229.148 ssh <PROVIDER_ID> hostname`;默认 remote CLI 走公网 frontend 登录态,不需要主 server SSH key。健康检查必须能看到该 Provider 在线、`hostSshConfigured=true``hostSshKeyPresent=true``hostSshTarget` 正确、`unideskCapabilities` 包含 `host.ssh`probe 必须返回 `UNIDESK_SSH_TEST``ssh <PROVIDER_ID> argv true``ssh <PROVIDER_ID> hostname` 必须 exit code 为 0。如果 D518 这类 WSL 节点没有公网 SSH 入口,也必须通过这个 provider-gateway 自连维护桥完成验证,而不是要求主 server 直接连节点公网 22 端口;旧版 provider 未声明 `host.ssh` 时必须先升级 provider-gateway,否则 core 会拒绝 SSH 透传。