# 开发说明

这份文档说明如何在本仓库里构建、测试并在本地把 exchange 跑起来。部署步骤、协议字段表和各平台安装细节仍以专题文档为准，文末有链接。

## 工具链

工作区使用 Rust 2021，要求 **Rust 1.82 或更新版本**。版本写在仓库根 `Cargo.toml` 的 `rust-version` 字段里。

## 工作区与二进制

Rust 代码在 `crates/` 下。协议类型只定义在 `crates/proto`，`exchange`、`agent`、`cli` 都依赖这一个 crate，不要在某一端再抄一套消息结构。

| crate | 路径 | 产物 |
|---|---|---|
| `proto` | `crates/proto` | 库。控制消息、二进制帧和共享数据结构只在这里 |
| `exchange` | `crates/exchange` | 二进制 `exchange`（包名与二进制名相同） |
| `agent` | `crates/agent` | 二进制 `remote-agent` |
| `cli` | `crates/cli` | 二进制 `remote-cli` |
| `updater` | `crates/updater` | 库，供 `remote-agent` 与 `remote-cli` 做自更新 |
| `telemetry` | `crates/telemetry` | 库，三端共用的异常与日志上报 |

`exchange` 的源文件入口是 `crates/exchange/src/main.rs`，没有单独的 `[[bin]]` 表，所以 `cargo build -p exchange` 得到的就是 `exchange`。`agent` 与 `cli` 在各自的 `Cargo.toml` 里把二进制名写成 `remote-agent` 和 `remote-cli`。

连接表按职责拆开：

- `crates/exchange/src/hub.rs` 管在线设备、重连顶替和 epoch
- `crates/exchange/src/hub/session_table.rs` 管运维侧编号与设备侧编号的翻译
- `crates/exchange/src/hub/outbound.rs` 管单条连接的有界发送队列

## 构建

在仓库根目录执行。

发布档（`opt-level = 2`、thin LTO，保留 unwind）会编出服务端、命令行和 agent：

```bash
cargo build --release
```

二进制在：

- `target/release/exchange`
- `target/release/remote-cli`
- `target/release/remote-agent`

`release` 保留 unwind 是因为 exchange 是长驻多连接进程。单个连接任务 panic 时，unwind 让 tokio 能停掉这一条任务，而不是把同进程里其它设备的会话一起带走。

设备端还要一个体积优先的 profile（`opt-level = "z"`、LTO、`panic = "abort"`、`strip`、`codegen-units = 1`）：

```bash
cargo build -p agent --profile release-small
```

产物是 `target/release-small/remote-agent`。agent 崩溃后由 procd、systemd 或计划任务拉起，所以这里可以用 `panic = "abort"` 换体积。

数据库不参与编译。`crates/exchange/src/db.rs` 使用 sqlx 的运行时查询，不用 `query!` 宏，因此没有数据库也能 `cargo build`。

## 测试

```bash
cargo test --workspace
```

Windows 上不会运行依赖 Unix `/bin/sh`（或 OpenWrt 上的 `/bin/ash`）和 PTY 的测试。`crates/agent/src/pty.rs` 的测试模块由 `#[cfg(all(test, unix))]` 包住，非 Unix 平台不编译这部分。PTY 实现会依次尝试 `/bin/ash` 和 `/bin/sh`。`exec.rs` 里的进程组与 Unix 信号路径同样只在 Unix 编译；Windows 上的命令测试走 PowerShell，不调用 `/bin/sh`。改了 `exec.rs` 或 `pty.rs` 的 Unix 路径时，需要在 Linux 上再跑一遍 `cargo test --workspace`，只看 Windows 的结果不够。

单 crate 可以缩小范围，例如 `cargo test -p proto` 或 `cargo test -p exchange -- hub::`。

## 本地启动 exchange

先准备 PostgreSQL，再把连接串放进 `DATABASE_URL`。这个变量对应 `exchange` 的 `--database-url`（见 `crates/exchange/src/config.rs`）。进程启动时 `db::connect` 会执行编译期嵌入的迁移：

```rust
sqlx::migrate!("../../migrations")
```

迁移文件在仓库根的 `migrations/`，启动时自动跑完，不需要 sqlx-cli。

```bash
cargo run -p exchange -- --listen 127.0.0.1:8080
```

PowerShell 下先设置环境变量再启动：

```powershell
$env:DATABASE_URL = "postgres://luci:luci@127.0.0.1:5432/luci"
cargo run -p exchange -- --listen 127.0.0.1:8080
```

已经编过发布二进制时，把上面的 `cargo run -p exchange --` 换成 `./target/release/exchange` 即可。监听地址的默认值是 `0.0.0.0:8080`，本地调试建议显式绑到 `127.0.0.1`。

库里还没有任何启用中的运维账号时，启动过程会自举一个管理员，并把令牌打在这一次的启动日志里。明文只出现这一次。完整的部署、反向代理和账号说明在仓库的 `docs/deployment.md`。该页包含服务器地址，不在本站公开。

## 相关文档

- [技术说明](technical.md) —— 外连、会话翻译、epoch、有界队列和令牌存储
- [使用说明](usage.md) —— 登录、添加设备、启动 agent、执行命令和打开 shell
- 部署指南在仓库 `docs/deployment.md`（含服务器地址，不在本站公开）
- [线路协议](protocol.md)
- [多平台设备部署](platforms.md)
