# 使用说明

> HTML: https://remote.ivjn.us/docs/zh-CN/usage/

这条路径面向操作员：拿到命令行和设备端，登录，添加一台设备，让设备连上，然后执行一条远程命令并打开 shell。浏览器控制台、手机控制台、Android 设备端、配对和自更新见文末链接，这里不重复那些页面的步骤。

示例里的地址和令牌请换成你自己的。令牌只在创建时出现一次，不要把它写进仓库。

**多身份与账号授权。** 全局 `--profile <name>` 优先于 `REMOTE_CLI_PROFILE`。命名身份把 `config.toml`、密封身份私钥、根信任和 grants 缓存保存在 `<config_base>/remote-cli/profiles/<name>/`；`config_base` 沿用 XDG_CONFIG_HOME、HOME/.config 或 Windows APPDATA。名称限 1–64 个 ASCII 字母、数字、点、连字符或下划线，不能为 `.` 或 `..`。命名 profile 优先于 `REMOTE_CLI_CONFIG`；不指定 profile 时，原有配置路径和 `REMOTE_CLI_CONFIG` 行为不变。每个 profile 单独登录，之后每次调用都选择同一 profile。

`operator set` 接受账号 ID 或唯一名称；设备参数为设备 ID。`--restricted` 仅显示显式授权设备，`--unrestricted` 恢复按角色确定的范围。只有超级管理员可修改范围；角色和设备授权由超级管理员管理，没有超级管理员时管理员可在允许范围内管理，不能修改自身。`operator grant list` 仅返回当前账号可见的授权。数据库设备授权与密封签名授权是两层检查：修改 grant 不会替代 `seal approve`。创建接口目前不支持原子设置范围，请先保存 `operator add` 返回的一次性令牌，再执行 `operator set` / `operator grant set`；这些是独立请求。

所有 operator 动词支持 `-o text|json|xml`。JSON 请求使用 `op=list|add|set|grant_list|grant_set|grant_remove`；`set` 接受 `operator` 与可选 `role` / `restricted`（至少一个），`grant_set` 接受 `operator`、`device` 和必填布尔值 `can_manage`。`grant_list` 可选 `operator`，`grant_remove` 接受 `operator` 和 `device`。`--json` 不与子命令混用。

```bash
remote-cli --profile alice login --server https://exchange.example.com
remote-cli --profile root operator set alice --restricted
remote-cli --profile root operator set alice --role readonly
remote-cli --profile root operator grant list --operator alice -o json
remote-cli --profile root operator grant set alice lab --view
remote-cli --profile root operator grant set alice lab --manage
remote-cli --profile root operator grant remove alice lab
remote-cli --profile root operator set alice --unrestricted
remote-cli --profile root operator --json '{"op":"grant_set","operator":"alice","device":"lab","can_manage":false}' -o json
```


## XML 请求与输出

先用 `remote-cli --help` 检查安装版本是否包含新选项。`--xml` 与 `--xml-file` 为已有 `--json` / `--json-file` 请求入口增加 XML 输入；四个输入选项互斥，仍是完整请求体，不可混用同一命令的选项或子命令，设备位置参数规则不变。输入与输出独立选择，`-o text|json|xml` 默认使用文本。

```powershell
remote-cli device --xml '<request><op>list</op></request>' -o xml
remote-cli exec my-device --xml-file request.xml -o xml
```

无子元素时默认为字符串，有子元素时默认为对象。数字、布尔值、数组、空值分别使用 `type="number"`、`type="boolean"`、`type="array"`、`type="null"`；数组子元素必须为 `<item>`，空对象使用 `type="object"`。任意键可用 `<field name="arbitrary key">value</field>`，空请求使用 `<request type="object"/>`。

```xml
<request>
  <command>echo hello</command>
  <timeout_secs type="number">30</timeout_secs>
  <hold type="boolean">false</hold>
  <env><GREETING>hello &amp; welcome</GREETING></env>
</request>
```

输出根节点为 `<response type="...">`，对象成员为 `<field name="..." type="...">`，数组成员为 `<item type="...">`。类型始终显式保留，文本按 XML 1.0 转义。运行时错误返回 `ok=false` / `error` 且退出码非零；命令行语法错误仍由参数解析器写入 `stderr`。

XML 输出支持 `device list/update`, `operator list/add/set/grant`, `exec`, `session list/kill`, `ui`, `ubus`, `sensors`, `net`, `metrics`, `preset`, `audit`, `browser`，包括 exec 作业操作。其他命令选择 `-o xml` 时明确报错；它们仍可使用 XML 请求和原有输出格式。

流式 `exec` / `sensors --live` / `browser session` 每行输出一个独立 XML 文档，逐行解析。exec 数据事件包含 `stream` / `encoding=base64` / `data`，Base64 保留原始字节，普通 exec 最后返回退出状态。`browser session` 的交互输入仍为 JSON Lines。`ui xml` 不带 `-o xml` 时保持原始 UI 层级 XML。

请求上限 1 MiB，嵌套上限 64 层；拒绝重复字段、混合文本与子元素、DTD 和外部实体。XML 1.0 无法表示的控制字符会导致输出错误，需要保留这类文本时选择 JSON。Agent 配置和网络协议仍为 JSON，XML 只作用于 CLI 输入与输出。

## 拿到 remote-cli 和 remote-agent

在仓库根目录构建：

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

命令行是 `target/release/remote-cli`。同一命令也会编出 `target/release/remote-agent`。给 flash 紧张的设备用体积优先的 agent：

```bash
cargo build -p agent --no-default-features --profile release-small
```

产物是 `target/release-small/remote-agent`。

Windows、Linux、macOS 也可以用网站上的安装脚本一次装好其中一个二进制，说明在 [多平台设备部署](/docs/zh-CN/platform-installation.md#一条命令安装windows--linux--macos--freebsd--netbsd)。下面的命令假定 `remote-cli` 和 `remote-agent` 已经在 `PATH` 里；若刚在本机编出来，改用上面的 `target/...` 路径。

自建中转服务时，先按 [开发说明](/docs/zh-CN/development.md) 用 `DATABASE_URL` 启动 `exchange`，从启动日志拿到管理员令牌。使用公共实例时，向管理员要运维令牌即可。

## 登录

```bash
remote-cli login --server http://127.0.0.1:8080 --token '<运维令牌>'
```

`--server` 填 `http://` 或 `https://`。命令行会按同样的安全级别推导 WebSocket 地址：`https` 对应 `wss`，`http` 对应 `ws`。

登录成功后，配置写在：

- Linux / macOS：`$XDG_CONFIG_HOME/remote-cli/config.toml`，未设置 `XDG_CONFIG_HOME` 时为 `~/.config/remote-cli/config.toml`
- Windows：`%APPDATA%\remote-cli\config.toml`

文件里只有两个键：`server` 和 `token`。要用别的路径时设置 `REMOTE_CLI_CONFIG`。之后的 `device`、`exec`、`shell` 都读这份配置，不必每次再传令牌。

## 添加设备

```bash
remote-cli device add lab-router --name "实验室路由"
```

`<id>` 是设备 ID，只能含字母、数字和 `-` `_` `.`，最长 64 个字符。`--name` 可省略，省略时显示名与 ID 相同。命令输出里的设备令牌只显示这一次。

## 启动 agent

在要被管理的机器上启动设备端，并带上服务端、设备 ID 和刚才的设备令牌：

```bash
remote-agent --server wss://127.0.0.1:8080 --device-id lab-router --token '<设备令牌>'
```

三个参数也可以用环境变量：`REMOTE_SERVER`、`REMOTE_DEVICE_ID`、`REMOTE_TOKEN`。

`--server` 必须以 `ws://` 或 `wss://` 开头。只写主机（例如 `wss://mgmt.example.com`）时，agent 会自动补上 `/ws/device`；已经写了路径则原样使用。本地 exchange 若没有 TLS，用 `ws://127.0.0.1:8080`。

不想把令牌放在进程参数里时，写一份 JSON，键名为 `server`、`device_id`、`token`：

```json
{
  "server": "wss://mgmt.example.com",
  "device_id": "lab-router",
  "token": "<设备令牌>"
}
```

然后 `remote-agent --config /path/to/config.json`。桌面系统的安装与自启动见 [多平台设备部署](/docs/zh-CN/platform-installation.md)。

`remote-cli` 与 `remote-agent` 的 WebSocket 都按 `HTTPS_PROXY` / `https_proxy` → `ALL_PROXY` / `all_proxy` → `HTTP_PROXY` / `http_proxy` 读取代理，并遵守 `NO_PROXY` / `no_proxy`；agent 无界面和 `--ui` 模式使用同一连接路径。代理须支持 HTTP CONNECT（不支持 SOCKS），WSS 的 ALPN 仅声明 `http/1.1`。后台服务请在服务环境中设置变量，详见 [出站代理与连接保活](/docs/zh-CN/platform-installation.md#出站代理与连接保活)。

设备连上之后：

```bash
remote-cli device list
```

列表里能看到这台设备的在线状态。

## 执行远程命令

`--` 后面的内容交给设备，不再由本机命令行解析：

```bash
remote-cli exec lab-router -- uname -a
```

`--` 后面给几个参数，决定了远端怎么理解它们：

- **多个参数**按 argv 原样传过去，每个参数的边界都保留，和在设备上直接敲这几个参数效果一样。需要时 CLI 会自动加引号（Windows 设备按 PowerShell 规则）。
- **只有一个参数**时，整串交给设备的 shell 当命令行解析，可以用管道、`;`、通配符和变量。

```bash
remote-cli exec lab-router -- sh -c 'uname -a; cat /proc/uptime'   # 多个参数，第三个参数完整交给 sh -c
remote-cli exec lab-router -- 'dmesg | tail -20'                    # 一个参数，管道在设备上执行
```

注意：多参数形式下，本地 shell 原样传过来的 `*`、`$VAR`（在 PowerShell 里很常见）会被当作字面量，不会在远端展开。想让设备来展开，就写成单个参数，例如 `-- 'ls /etc/*.conf'`。

exec 默认开启 hold：超时默认 3600 秒（硬上限 86400 秒），运维端断开只分离输出，不杀作业。用 `--timeout` 指定时限，例如 `remote-cli exec lab-router --timeout 120 -- uname -a`；`--hold-timeout` 可覆盖 hold 时限。加 `--no-hold` 恢复运维端断开即取消的行为，默认超时 60 秒。命令正常返回时，其退出码会成为 `remote-cli` 的退出码。

### 查看和终止作业、释放会话槽位

```bash
remote-cli exec-holds lab-router
remote-cli exec-hold-kill lab-router <job_id>
remote-cli exec-hold-tail lab-router <job_id>
remote-cli exec-hold-cat lab-router <job_id>
remote-cli exec-attach lab-router <job_id>
remote-cli sessions lab-router
remote-cli session-kill lab-router <session_id>
```

`exec-holds` 列出 exec 作业和正在进行的下载、上传、目录查询，显示 `job_id`、`kind`、`status`、`age_secs`、当前设备侧 `session_id`（如有）与截断的命令/路径摘要。`exec-hold-kill` 按作业编号终止进程组或中止文件传输；tail、cat、attach 仅用于 exec 作业。

`sessions` 直接读取 Exchange 路由表，显示当前 `count/limit` 和设备侧会话编号。`session-kill` 使用这个**设备侧**编号，向设备发送取消指令并立即释放 Exchange 槽位；即使设备没有回复也能释放。两者不向设备发起额外会话，不占并发槽位，适合处理满额卡住的会话；只读账号不可使用。

exec 超时会终止整个进程组，输出排空及结束消息发送均有时间上限。Exchange 在达到并发上限时先自动回收失去运维端或超过截止时间的路由，再尝试新会话：exec 截止时间为实际执行时限加 15 秒宽限，文件传输为 3600 秒加 15 秒（设备另有 30 秒空闲超时），其它会话为 24 小时。hold exec 在截止时间内不会仅因运维端断开而被杀；断开仍然只是 detach。显式 `session-kill` 或超过截止时间则会取消 hold 进程。

## 打开 shell

```bash
remote-cli shell lab-router
```

这是设备上的一个 PTY 会话，不是 SSH。退出 shell 即结束这次会话。

## 其它入口

- 浏览器控制台：[Web 控制台](/docs/zh-CN/web-console-and-notifications.md)
- 手机控制台：[移动客户端](/docs/zh-CN/platform-installation.md)
- Android 设备端 APK：[Android 独立 APK](/docs/zh-CN/platform-installation.md#android-独立-apk)
- 二维码、六位码和账号：[初始化与配对](/docs/zh-CN/pairing-and-enrollment.md)
- 命令行与 agent 的自更新：[自更新](/docs/zh-CN/self-update.md)

## 端到端加密（密封会话）

`remote-cli exec <设备> -- <命令>`、`remote-cli shell <设备>` 使用端到端密封，无需额外参数，让 Exchange 只转发密文，看不到命令、输出和终端内容；设备只信任工作区根（超级管理员的密钥，超级管理员登录时自动生成）以及根签发的授权；根客户端按账号角色自动给成员签发。详见下文链接中的一次性设置。详见 [端到端密封会话](/docs/zh-CN/sealed-sessions.md)。

所有 CLI 设备会话只使用端到端密封，无需额外参数。缺少设备能力信息或密封握手失败时，不会改用明文。LAN 直连和浏览器操作使用同样的密封要求。

## 持久后台任务

声明 `tasks` 能力的 Agent 在设备上执行单操作或声明式 DAG。任务提交后，控制端断线仍继续运行；重连可查询进度、输出和本地产物。运行时重启将未完成任务标记为 `interrupted`，不会自动重放。

创建要求密封 `manage/owner` 授权。表单可配置依赖、固定条件、祖先结果的 JSON Pointer 引用、失败策略和最多五次显式重试。重试可能重复产生副作用，取消不回滚。Web 与移动控制端支持表单及 JSON 导入导出；桌面、Android、浏览器与 LuCI 本地入口提供历史、输出、取消和删除。

先查询操作目录，平台支持和排除原因以设备返回为准。文件输入必须先暂存。默认保留 30 天、最多 1000 个任务、总预算 64 MiB、每任务文本 8 MiB、并行步骤 4（OpenWrt 为 2）；步骤默认超时 30 分钟，可设至 24 小时。旧 Agent 不显示任务入口。
```sh
remote-cli task DEVICE operations
remote-cli task DEVICE create pipeline.json --wait
remote-cli task DEVICE list
remote-cli task DEVICE get TASK_ID
remote-cli task DEVICE logs TASK_ID --offset 0
remote-cli task DEVICE cancel TASK_ID
remote-cli task DEVICE upload ./input.bin
remote-cli task DEVICE download TASK_ID STEP.artifact ./result.json
remote-cli task DEVICE delete TASK_ID
```

## 服务托管

Task 的 Service 模式可托管原生前台命令，支持持久运行意图、退出重启策略、指数退避、启动频率限制和 Agent 重启恢复。默认保存为停止状态；编辑或删除前先停止并等待退出。授权到期或撤销会停止服务。普通 DAG 重启后仍不重放。Web、移动、本机和 Android 可管理；LuCI 配置使用 Console 或 CLI。浏览器 Agent 不支持系统进程托管。

配置使用 command.command 或 command.argv，可设置 cwd/env、restart（never/on_failure/always）、start_secs、stop_grace_secs、backoff_secs、max_backoff_secs、restart_window_secs 和 max_starts。Windows 优雅退出需提供 stop_command；Unix 默认 SIGTERM，宽限期后强制清理进程树。Agent 开机运行须另行配置。service get 返回 run_id，可用 task get/logs 查看。

```sh
remote-cli task DEVICE service put worker.json
remote-cli task DEVICE service list
remote-cli task DEVICE service start worker
remote-cli task DEVICE service get worker
remote-cli task DEVICE service restart worker
remote-cli task DEVICE service stop worker
remote-cli task DEVICE service delete worker
remote-agent task service put worker.json
```
