Metadata-Version: 2.4
Name: comate-relay
Version: 0.1.0
Summary: Relay for remote sandbox agents: accounts, tokens and forwarding over comate_rpc
Author-email: Andy <andy.dev@aliyun.com>
Requires-Python: <3.14,>=3.11
Requires-Dist: comate-agent-sdk<0.13,>=0.12.9
Requires-Dist: comate-rpc<0.2,>=0.1.2
Requires-Dist: starlette>=0.52
Requires-Dist: uvicorn>=0.53
Requires-Dist: websockets>=15
Description-Content-Type: text/markdown

# comate-relay

远程 sandbox Agent 的中转服务。Agent（`comate --rpc-relay`）主动拨出到这里，桌面和手机也连这里，中转按连接原样转发。
中转不懂协议、不存业务状态：只存账号、Agent 记录和登录令牌（SQLite），重启只丢在线状态。

## 快速上手

```bash
comate-relay user add alice                     # 交互式输入两次密码；脚本里用 --password-stdin
comate-relay agent add --user alice --name box  # 打印 agent_id 和令牌，令牌只显示这一次
comate-relay serve                              # 默认只听 127.0.0.1:8765
```

启动后标准输出只有一行就绪信息：`{"type": "ready", "url": "http://127.0.0.1:8765/", "insecure": false}`。
浏览器打开这个地址就是 demo 页：登录 → 看自己名下的 Agent（在线状态、版本、主机名、工作目录）→ 点一个进会话。

局域网里用手机验收（Agent 跑在 docker 容器里）：见 [acceptance/README.md](acceptance/README.md)。

## 子命令

| 命令 | 作用 |
|---|---|
| `serve` | 跑中转 |
| `user add NAME [--password-stdin]` / `user remove NAME` / `user list` | 管账号；删账号连同名下 Agent 记录和登录令牌 |
| `user revoke NAME` | 删掉该账号全部登录令牌（手机丢了用它）。新连接立刻被拒；已连着的最多 30 秒内被断开（1008） |
| `agent add --user NAME --name NAME` / `agent remove ID` / `agent list [--user NAME]` | 管 Agent 记录；`list` 永远不显示令牌；删掉在线 Agent 的记录，它最多 30 秒内被断开（1008，不再重连） |

## 接口

| 路径 | 用途 |
|---|---|
| `POST /api/auth/login` `{"account", "password"}` | → `{"token", "expires_at", "device"}` |
| `POST /api/auth/logout`（`Authorization: Bearer 令牌`） | 删当前登录令牌，204；用它连着的连接当场断开（1008） |
| `WS /ws/control` | 终端的控制连接（登录令牌），跑 `agents` 服务（见下） |
| `WS /ws/agents/{agent_id}` | 终端连某个 Agent（登录令牌），里面就是 comate_rpc 协议 v4 |
| `WS /ws/agent-link` | Agent 拨入（Agent 令牌） |
| `/` | demo 页（TS 包 `npm run build:demo` 的产物，提交在 `comate_relay/static/`） |

WebSocket 的令牌一律放子协议：`["comate-rpc", 令牌]`。

控制连接上的 `agents` 服务（用 comate_rpc 框架写，TS 类型在 `packages/comate_rpc/ts/src/generated/relay/`）：

| 方法 / 事件流 | 内容 |
|---|---|
| `agents.list` | 调用者账号名下的全部 Agent：`agent_id`、`name`、`online`、`last_seen`、`meta`（上线时自报的版本、主机名、工作目录；中转重启后、它再上线之前为空） |
| `agents.events` | 上下线：`{kind: "online" \| "offline" \| "superseded", agent: 同上}`。被顶替 = `superseded` 紧跟 `online`。只推订阅之后的：先订阅再 `list` |

| 关闭码 | 含义 |
|---|---|
| 1008 | 令牌错误 / 过期 / 被撤销：重新登录（Agent：停止重连）。已连着的连接令牌失效也收到它 |
| 4003 | 这个 Agent 不属于你 |
| 4004 | Agent 离线：退避重连 |
| 4009 | 被同令牌的新连接顶替（发给旧 Agent 连接）：停止重连 |

## 传输安全

- 默认只听 `127.0.0.1`。
- 监听非本机地址：给 `--certfile` / `--keyfile` 就跑 HTTPS / WSS；都不给、也没加 `--allow-insecure-http` 就拒绝启动。
- 加了 `--allow-insecure-http`：启动，日志打 WARNING，就绪信息 `insecure: true`。只在可信的局域网里用。
- 放在反向代理后面：让中转只听本机或内网地址，由代理做 HTTPS。

## 配置与初值

没有配置文件，全部是命令行参数；除 `--allow-insecure-http` 外都有对应的环境变量，命令行优先。

| 参数 | 环境变量 | 初值 |
|---|---|---|
| `--data-dir` | `COMATE_RELAY_DATA_DIR` | `~/.comate-relay/` |
| `--host` / `--port` | `COMATE_RELAY_HOST` / `COMATE_RELAY_PORT` | `127.0.0.1` / `8765` |
| `--certfile` / `--keyfile` | `COMATE_RELAY_CERTFILE` / `COMATE_RELAY_KEYFILE` | 无 |
| `--allow-insecure-http` | 刻意没有 | 关 |
| `--login-ttl-hours` | `COMATE_RELAY_LOGIN_TTL_HOURS` | 24 |
| `--login-delay-cap` | `COMATE_RELAY_LOGIN_DELAY_CAP` | 30 秒（连续失败后等 1、2、4……秒，成功清零；只在内存，不锁账号） |
| `--token-recheck-interval` | `COMATE_RELAY_TOKEN_RECHECK_INTERVAL` | 30 秒（多久复查一次在线连接的令牌；登录令牌被撤销或过期、Agent 记录被删，最多这么久后断开） |
| `--ping-interval` | `COMATE_RELAY_PING_INTERVAL` | 30 秒（WebSocket ping） |
| `--terminal-queue` | `COMATE_RELAY_TERMINAL_QUEUE` | 500 条 |
| `--agent-queue` | `COMATE_RELAY_AGENT_QUEUE` | 5000 条 |

登录失败计数按账号名记在内存里，最多 10 万个名字（实测约 10 MB），满了忘掉最久没被试过的；账号名最长 64 个字符。
