Routr 2.25.1 常用 CLI 命令与工具详解
面向用命令行与脚本管理 Routr 的运维与后端工程师,实测 rctl 对 2.25.1 服务端的全部资源组命令(7 组增删改查 + describe/get/extended/分页)、Node.js SDK 的类/方法/签名矩阵与三大传参坑、2.25 新增 trace 工具族(validate/lint/replay/send/sanitize/compat)对消息录制 JSONL 的完整消费,全部输出为本地实测回显。
Routr 2.25.1 常用 CLI 命令与工具详解
目标:面向用命令行与脚本管理 Routr 的运维与后端工程师,实测 rctl 对 2.25.1 服务端的全部资源组命令(7 组 × create/delete/describe/get/update,含单资源、extended、分页)、Node.js SDK 的类/方法/签名矩阵与三大传参坑、2.25 新增 trace 工具族(validate/lint/replay/send/sanitize/compat)对消息录制 JSONL 的完整消费。全部命令与回显均为本地实测。
一、实验环境与取证说明
| 项目 | 取值 |
|---|---|
| 服务端 | fonoster/routr-one:2.25.1(部署篇同一栈,API 经宿主 8128 → 51908) |
| CLI | rctl @routr/ctl/2.25.0 linux-x64 node-v24.22.1(npm 当前最新) |
| SDK | @routr/sdk(npm 最新,Node.js 24 实测) |
| 版本错位说明 | CLI 2.25.0 ↔ 服务端 2.25.1,小版本错位不影响资源管理(全流程实测通过) |
| 初始资源 | Domain sip.local、Credentials admin、Agent 1001(部署篇所建;鉴权 admin/q1w2e3r4) |
| 取证日期 | 2026-10-26 |
二、rctl 命令地图
2.1 命令结构
rctl <资源组> <动作> [REF] [全局flags]
- 资源组 7 个:
acl/agents/credentials/domains/numbers/peers/trunks - 动作 5 个:
create/delete/describe/get/update - 另有
plugins(oclif 插件管理)与trace(2.25 新增录制工具族,第六节)
2.2 全局 flags(必须放在资源命令之后)
| flag | 说明 |
|---|---|
-e, --endpoint | API 地址,默认 localhost:51908;本实验为 localhost:8128(compose 映射) |
-i, --insecure | 明文连接 API(API 口默认无 TLS) |
-c, --cacert | CA 证书路径;TLS_ON=true 时替代 -i |
部署篇已踩过的坑再强调一次:全局 flags 写在资源命令前面会被 oclif 误解析(
rctl -e localhost:8128 domains get报command -e not found)。正确姿势:rctl domains get -e localhost:8128 -i。
2.3 动作语义(rctl agents get --help 实测)
| 动作 | 参数 | 行为 |
|---|---|---|
get [REF] | -s 页大小(默认 50)、-x 扩展输出 | 有 REF 出单资源详情表,无 REF 出分页表格 |
describe [REF] | — | 键值式详情(含 Credentials/Extended 嵌套) |
create | 无字段参数 | 全交互 prompt,结束打印新资源 ref |
update REF | 无字段参数 | 全交互 prompt,按已有值回填 |
delete [REF] | — | 静默执行,成功仅退出码 0 |
create/update 只走交互意味着脚本化场景必须用 SDK(第五节)。
三、资源组 get:七张实测表
以下为同一时刻七组资源的 rctl <组> get -e localhost:8128 -i 输出(构造过程见第五节校验实录):
$ rctl agents get ...
Name Username Domain Max Contacts Privacy Enabled
Agent 1003 1003 sip.local 1 None Yes
Agent 1002 1002 sip.local None Yes
Agent 1001 1001 sip.local Private Yes
$ rctl credentials get ...
Name Username
Cred 2002 2002
Cred 2001 2001
Agent credentials admin
$ rctl domains get ...
Name URI
Local domain sip.local
$ rctl peers get ...
Name Username AOR Max Contacts Balancing Algorithm Enabled
FreeSWITCH Demo freeswitchpbx sip:freeswitchpbx@sip.local Least Connections Yes
Asterisk Demo asteriskpbx sip:asteriskpbx@sip.local Least Connections Yes
$ rctl trunks get ...
Name Inbound SIP URI Send Register
Trunk Demo sip.provider.example false
$ rctl numbers get ...
Name Telephony URL AOR Link Geo
Number Demo tel:+15551234567 sip:1001@sip.local Hangzhou, China (CN)
$ rctl acl get ...
Name Deny CIDR Networks Allow CIDR Networks
ACL Demo 0.0.0.0/0 10.89.57.0/24
要点:Agent 的 Privacy 支持 Private/None;Peer 表自带负载均衡算法列(默认 Least Connections);Number 的 Geo 来自创建时的 city/country 字段;ACL 必须同时有 allow 与 deny 两列(校验规则见 5.2)。
四、describe、单资源与时间戳缺陷
$ rctl agents describe 8a2e3a2e-f796-479d-a0bd-5c5a583a6fb7 -e localhost:8128 -i
Ref: 8a2e3a2e-f796-479d-a0bd-5c5a583a6fb7
APIVersion: v2
Name: Agent 1002
Username: 1002
Domain: sip.local
Max Contacts:
Privacy: None
Credentials:
Ref: 336cbbb3-d129-4d17-ad98-1ce3f32cd84f
Username: 2001
Enabled: Yes
Extended: null
Created: 1970-01-21T18:03:43.286Z
Updated: 1970-01-21T18:03:43.286Z
Created/Updated显示为 1970 年:实测资源创建于 2026-10-26,rctl 2.25.0 把毫秒时间戳按秒解释了(1.79e12 ms → 1970-01-21)。判断真实创建时间以 SDK 返回值或数据库为准。- 单资源查询:
rctl agents get 7055df5a-5a2e-4be6-b303-2b9ae7005da5 -e localhost:8128 -i,输出与列表同款单行表。 delete静默成功:rctl agents delete <ref> -e ... -i无任何输出、退出码 0,需再get复核。update为交互 prompt(逐字段回填旧值),改完用describe复核。
五、创建与校验报错实录(SDK 构造 + API 校验)
5.1 正确 payload 实测
create 交互式不便脚本化,以下用 SDK 构造(脚本见 5.3),全部创建成功:
const acl = await acls.createAcl({
name: "ACL Demo", allow: ["10.89.57.0/24"], deny: ["0.0.0.0/0"] })
const peer = await peers.createPeer({
name: "Asterisk Demo", username: "asteriskpbx",
aor: "sip:asteriskpbx@sip.local", contactAddr: "10.89.57.20:5060",
enabled: true, maxContacts: -1 })
const trunk = await trunks.createTrunk({
name: "Trunk Demo", sendRegister: false, inboundUri: "sip.provider.example",
uris: [{ user: "trunkuser", host: "sip.provider.example", port: 5060,
transport: "UDP", weight: 1, priority: 1 }] })
const num = await numbers.createNumber({
name: "Number Demo", telUrl: "tel:+15551234567",
aorLink: "sip:1001@sip.local", city: "Hangzhou", country: "China",
countryIsoCode: "CN", trunkRef: trunk.ref })
5.2 校验报错对照(实测原话,缺什么补什么)
| 报错 | 触发 | 处置 |
|---|---|---|
3 INVALID_ARGUMENT: acl rules are required | ACL 只给 allow | allow 与 deny 都必须非空,且每项为合法 IPv4 CIDR/IP(源码 hasACLRules:任一为空即拒);无 deny 需求时填 ["0.0.0.0/0"] |
3 INVALID_ARGUMENT: the username is required | Peer 未给 username | Peer 必填:username/aor/contactAddr/enabled/maxContacts |
3 INVALID_ARGUMENT: the priority must be a valid integer | TrunkURI 缺 priority | uris[] 每项需 user/host/port/transport/weight/priority |
3 INVALID_ARGUMENT: the inboundUri is required | Trunk 缺 inboundUri | Trunk 必填:sendRegister + inboundUri(出向 URI 列表为可选) |
3 INVALID_ARGUMENT: the city is required | Number 缺地理信息 | Number 必填:telUrl/aorLink/city/country/countryIsoCode;trunkRef 可选 |
5.3 分页实测(pageSize/pageToken)
let token
let page = 0
do {
const r = await agents.listAgents({ pageSize: 2, pageToken: token })
page++
console.log(page, r.items.map(i => i.name), r.nextPageToken)
token = r.nextPageToken
} while (token && page < 5)
page1 {"n":2,"names":["Agent temp","Agent 1001"],"next":"7055df5a-5a2e-4be6-b303-2b9ae7005da5"}
page2 {"n":0,"names":[],"next":null}
实测结论:pageSize 生效(2→2 条);结果按创建时间新→旧排序;nextPageToken 为本页末条的 ref 游标、向更旧方向翻页——尾页之后仍会返回一个"空页 + null",遍历循环按 nextPageToken 判停即可(会多一次空查询)。rctl 的 -s 即该页大小参数(默认 50)。
六、SDK 脚本化:签名矩阵与三大传参坑
6.1 类与方法(@routr/sdk 实测导出)
7 个客户端类:Acls / Agents / Domains / Numbers / Peers / Trunks / Credentials;每类 5 个方法,命名规整:
| 类 | 方法 |
|---|---|
| Acls | createAcl / updateAcl / getAcl / deleteAcl / listAcls |
| Agents | createAgent / updateAgent / getAgent / deleteAgent / listAgents |
| Domains | createDomain / updateDomain / getDomain / deleteDomain / listDomains |
| Numbers | createNumber / updateNumber / getNumber / deleteNumber / listNumbers |
| Peers | createPeer / updatePeer / getPeer / deletePeer / listPeers |
| Trunks | createTrunk / updateTrunk / getTrunk / deleteTrunk / listTrunks |
| Credentials | createCredentials / updateCredentials / getCredentials / deleteCredentials / listCredentials |
构造参数 ClientOptions:{ endpoint, insecure?, cacert?, username?, password? }——明文用 insecure: true,TLS 用 cacert(配置篇 6.2 实测)。
6.2 三大传参坑(全部实测踩中)
getX(ref)/deleteX(ref)收位置字符串,不收对象。传{ref: "..."}时:getAgent报5 NOT_FOUND: Resource not found: [object Object](对象被字符串化成字面量[object Object]);deleteAgent更隐蔽——静默返回成功但什么都没删(实测 before/after 列表完全一致)。正确:await agents.deleteAgent(ref)。updateX才收对象,且ref写在对象里:await agents.updateAgent({ ref, name: "...", maxContacts: 1 }),改后getAgent(ref)复核name/maxContacts均已生效(实测输出"Agent temp (updated)", 1)。- 列表参数是
pageSize/pageToken,不是size。传{size: 2}键名不匹配被忽略,一次返回全部 50 条默认页——看起来像"分页失效",实为传参错误。
6.3 端到端示例(创建 → 查 → 改 → 删,实测通过)
const SDK = require("@routr/sdk").default
const O = { endpoint: "localhost:8128", insecure: true }
const credentials = new SDK.Credentials(O)
const agents = new SDK.Agents(O)
async function main() {
const cred = await credentials.createCredentials({
name: "Cred temp", username: "temp2", password: "temppass" })
const agent = await agents.createAgent({
name: "Agent temp", username: "temp2",
domainRef: "7117e0cd-4459-4af4-931a-d406d65d57e6",
credentialsRef: cred.ref, maxContacts: -1, enabled: true })
const got = await agents.getAgent(agent.ref) // 位置字符串!
console.log(got.name, got.username) // Agent temp temp2
await agents.updateAgent({ ref: agent.ref, name: "Agent temp (updated)", maxContacts: 1 })
console.log((await agents.getAgent(agent.ref)).name) // Agent temp (updated)
await agents.deleteAgent(agent.ref) // 位置字符串!
await credentials.deleteCredentials(cred.ref)
}
main().catch(console.error)
七、trace 工具族(2.25 新增):录制消息的消费端
rctl 2.25 新增 trace 六个子命令,消费 dispatcher 的消息录制产物(JSONL,每行一条消息记录)。录制开启方法见《Routr 2.25.1 配置参数详解》第六节(RECORD_MESSAGES 家族)。
| 子命令 | 用途 |
|---|---|
trace validate FILE | 校验 JSONL 可读性:JSON 合法、traceVersion 支持、记录 schema 正确 |
trace lint FILE... | 对每条记录跑 SIP 规则,--sip-connect 追加 SIPconnect 1.1 规则 |
trace replay FILE | 重放整通呼叫:消息阶梯、组件改动、规则结论;`--view summary |
trace send FILE | 把单条录制消息发给运行中的 dispatcher/processor,比对线上应答与录制差异 |
trace sanitize FILE | 生成语料:用语料测试凭据重算 Digest 响应、对号码/主机做字面量改写 |
trace compat | 语料回归:整批语料过 dispatcher,报告每条漂移 |
实测(录制文件为配置篇 6.3 的一次 401→200 REGISTER 全流程):
$ rctl trace validate 20261026T135939229Z-1911051453_10.89.57.3.jsonl
20261026T135939229Z-1911051453_10.89.57.3.jsonl: 2 records, trace version 1, Call-ID 1911051453@10.89.57.3
$ rctl trace lint 20261026T135939229Z-1911051453_10.89.57.3.jsonl
20261026T135939229Z-1911051453_10.89.57.3.jsonl 2 records ok no findings
rules: sip
$ rctl trace replay 20261026T135939229Z-1911051453_10.89.57.3.jsonl --static
routr. trace 20261026T135939229Z-1911051453_10.89.57.3.jsonl 1911051453@10.89.57.3 ANSWERED 200
8e6607060fc0 2.25.1 recorded 2026-10-26 13:59:39 UTC 2 messages
ANSWERED 200 REGISTER c2 2 messages
FACTS
Call 1001@sip.local → 1001
Call-ID 1911051453@10.89.57.3
EdgePort 8e6607060fc0 2.25.1
Dispatcher dispatcher 2.25.1
Recorded 2026-10-26 13:59:39 UTC
CHECKS no INVITE was forwarded
- sip rules (no INVITE was forwarded)
LADDER
1001 8e6607060fc0
| |
? +0 |-REGISTER c1--------------------------------------->
<........................................401 Unauthorized.|
- +112 |-REGISTER c2---------------------------------------|
价值:一次 docker logs 看不出来的"这通呼叫每个组件改了什么、规则怎么说",replay 一条命令给出结论;--fail-on rejected 可直接挂进 CI。录制文件中 401 响应的 nonce/response 摘要凭据字段已被红摘(redact)。
八、排障速查
| 现象 | 根因与处置 |
|---|---|
Cannot connect to service [service address = ..., tlsOn = false] | API 口不通或 TLS 状态不匹配:明文口加 -i;TLS_ON=true 后改 -c <ca.crt>(SDK 用 cacert) |
command -e not found | 全局 flags 写到了资源命令前面 |
| SDK 删了"成功"但资源还在 | deleteX(ref) 传了 {ref} 对象——静默无操作。改传字符串 |
NOT_FOUND: Resource not found: [object Object] | getX(ref) 传了对象。改传字符串 ref |
| "分页不生效,一次返回全部" | 列表参数名写成了 size,应为 pageSize/pageToken |
| describe 时间是 1970 年 | rctl 2.25.0 毫秒/秒换算缺陷,展示层问题 |
3 INVALID_ARGUMENT: ... | 按 5.2 对照表补必填字段 |
| create/update 想自动化 | rctl 无字段级参数,改用 SDK(第六节) |
九、参考
- rctl 官方参考:https://docs.routr.io/reference/rctl
- SDK 官方参考:https://docs.routr.io/reference/sdk
- npm 包:
@routr/ctl、@routr/sdk(https://www.npmjs.com/package/@routr/ctl) - 消息录制与 trace 规则:https://docs.routr.io/reference/components/dispatcher
- 本专栏姊妹篇:《Routr 2.25.1 介绍与选型指南》(认知与选型)、《Routr 2.25.1 Docker Compose 部署指南》(部署与验证)、《Routr 2.25.1 配置参数详解》(配置文件与环境变量全表)