CLI 命令参考
anet 管理 Hub、账号、Network、节点和外部 Channel。本页只保留当前命令与容易误操作的行为;专题配置请进入对应指南。
安装与帮助
# 稳定版
npm install -g @sleep2agi/agent-network@latest
# 预览版
npm install -g @sleep2agi/agent-network@preview
anet --help
anet <command> --help
anet <command> <subcommand> --help # 例如 anet node delete --help
anet -v需要 Node.js 22.13+ 和 Bun 1.2+。--help(或 -h)只显示帮助,不会创建 token、启动服务或执行其他业务操作。每个命令和子命令都有自己的用法说明,退出码 0。
最短启动路径
# 终端 1
anet hub start
# 终端 2
anet login --hub http://127.0.0.1:9200 --username admin
# 登录后
anet node create my-agent
anet node start my-agent初始密码随发布频道不同:stable(@latest)使用固定默认值,可在 anet hub start --help 的 --password 说明中查看;preview(@preview)首次启动会打印一次性随机密码。登录后立即运行 anet passwd。
完整安装与首次配置见 快速开始。
Hub
| 命令 | 作用 |
|---|---|
anet hub start | 启动 Hub;默认监听 127.0.0.1:9200 |
anet hub stop [--port <p>] | 停止指定端口上的本机 Hub |
anet hub status [--port <p>] | 显示监听状态、PID 和服务版本 |
anet hub dashboard | 启动 Dashboard,默认端口 3000 |
anet hub config | 查看或修改本机 Hub 启动配置 |
anet hub admin reset-user --username <user> | 在 Hub 主机上重置用户密码和用户 token |
常用启动参数:
| 参数 | 说明 |
|---|---|
--port <port> | Hub 端口,默认 9200 |
--host <host> / --ip <host> | 绑定地址,默认仅本机的 127.0.0.1 |
--username <user> | 首次启动时指定管理员用户名 |
--password <pass> | 首次启动时显式指定管理员密码 |
--dev-open | 关闭鉴权,仅限隔离开发环境 |
--version <v> | 指定要启动的 commhub-server 版本(精确版本号,或 latest / preview) |
--channel <c> | latest 或 preview,覆盖默认按 anet 自身通道选版本的结果 |
启动的 Hub 版本:不带 --version 时,anet hub start 先按已装 anet 的版本对照 @sleep2agi/agent-network 的 dist-tags 判断通道(等于或落后于 latest 判为 latest,比 latest 新的预发布版判为 preview;不能只看 -preview.N 后缀,latest 本身也带这个后缀),再取该通道对应的 @sleep2agi/commhub-server dist-tag,交给 bunx 精确版本号启动;该 tag 若比当前 anet 支持的最低版本还旧,则改用最低版本。启动时会打印一行 Hub version: @sleep2agi/commhub-server@<版本> [source: registry | cache | explicit | pinned minimum],说明版本和来源。查不到 npm registry 时,使用本机 bun 缓存里不低于最低版本的最新一版并告警;缓存里只有更旧的版本时不会静默启动它,而是打印两个版本号的告警,并提示用 --version <缓存版本> 显式启动。
不要把 --dev-open 或直接暴露的 0.0.0.0:9200 用于生产。公网部署见 生产部署。
停止 Hub(anet hub stop):只停监听在该端口上、且命令行确认是 commhub-server 的进程,不按进程名匹配。找监听进程不依赖 lsof:Linux 依次用 /proc/net/tcp(6) 的 socket inode 对 /proc/<pid>/fd、ss -ltnp、lsof、netstat,macOS 用 lsof、netstat -anv,Windows 用 netstat -ano,取第一条能运行的;都不能运行时,用 anet hub start 写的 ~/.anet/server/hub-<port>.pid.json(PID 已退出或已被别的程序复用时忽略)。输出会列出每条探测的结果、找到的 PID 和命令行,以及实际停掉了哪个 PID。先 SIGTERM,3 秒后仍在则在再次确认命令行后 SIGKILL。
| 情况 | 动作 | 退出码 |
|---|---|---|
找到 commhub-server 并已停止 | 停止 | 0 |
该端口没有监听、/health 也不应答 | 不动 | 0 |
| 端口被别的程序占用,或读不到它的命令行 / 属主 | 拒绝,打印 PID 和命令行 | 1 |
/health 有应答,但本机找不到它的 PID(没有任何探测可用,也没有 pid 文件) | 不动,提示用启动它的进程管理器(pm2、systemd 等)停 | 1 |
SIGKILL 后进程仍在,或 /health 仍有应答 | — | 1 |
--port 不是合法端口 | — | 2 |
账号、Network 与 Token
账号
| 命令 | 作用 |
|---|---|
anet register | 创建账号 |
anet login | 使用用户名和密码登录 |
anet login --token <token> | 使用已有 API token 登录 |
anet logout | 先在 Hub 上撤销本次登录会话,再删除本机保存的登录 token(见下方 退出登录) |
anet whoami | 显示当前用户和可访问的 Network |
anet passwd | 修改密码并轮换当前登录 token |
退出登录(anet logout):用保存的 token 调 GET /api/auth/sessions 取得 current_token_id,再 DELETE /api/auth/sessions/<token_id> 撤销这一条登录会话(与 app「设置 → 账号 → 登录设备 → 退出」同一对端点,见 登录设备 API),然后删除 ~/.anet/config.json 里的 token / user / network_id / network_name(保留 hub)。输出只显示 token id,不显示 token。
| 情况 | 本地 | 服务端 | 退出码 |
|---|---|---|---|
| 撤销成功 / token 在 Hub 上本来就已失效(401) | 删除 | 已失效 | 0 |
Hub 连不上、Hub 早于 v0.9.0-preview.70(/api/auth/sessions 404) | 删除 | 仍有效,打印 ⚠ 警告与撤销方法 | 0 |
保存的是 anet login --token 用的显式 API token 或节点 token | 删除 | 不撤销(它可能还在别处用),打印 ⚠ 警告;用 anet token revoke <token-id> 撤 | 0 |
| 本地配置文件写不了 | 未删除 | — | 1 |
服务端仍有效时的撤销办法:app「设置 → 账号 → 登录设备」里退出那台设备;或重新 anet login 后 anet passwd(改密码会撤销其他所有登录会话)。anet init project 写进各项目 .anet/.env 的 COMMHUB_TOKEN 是同一个 token 的副本,服务端撤销后一起失效。
Network
| 命令 | 作用 |
|---|---|
anet network ls | 列出当前用户加入的 Network |
anet network create <name> | 创建 Network |
anet network use <name> | 切换当前 Network |
anet network info | 查看当前 Network |
anet network rename <old> <new> | 重命名 Network |
anet network delete <name> --force | 删除 Network |
anet network invite [options] | 创建邀请码 |
anet network join <code> | 使用邀请码加入 Network |
anet network members | 列出当前 Network 成员 |
邀请码可使用 --role admin|member|viewer、--uses <n> 和 --expires <days>。
Token
| 命令 | 作用 |
|---|---|
anet token / anet token ls | 列出当前用户的 API token |
anet token create <name> | 创建 API token;明文只显示一次 |
anet token revoke <token-id> | 撤销 token |
Token 类型、作用域与兼容规则见 Token 体系。
节点
交互菜单:anet node
不想记命令:在节点目录里直接敲 anet node(不带参数)。它列出当前目录的所有节点 —— 节点名、runtime(claude-agent-sdk / claude-code-cli / codex-* / grok-* / opencode-cli)、状态(running / stopped)、模型,codex 节点另有登录列 —— 选一个节点再选操作:启动、停止、重启、进入 TUI(attach)、看最近日志、换模型、删除。
- 每个操作执行前都会打印等价的
anet …命令,回答y才执行;删除要输入节点名确认。执行的就是打印出来的那条命令,菜单本身不做任何生命周期动作。 - codex 节点选中后交给 codex 专属操作(登录、接着聊、复制等),与
anet node codex相同,见 Codex 节点速查。 - 不是终端(管道、脚本、agent 调用)时只打印节点表、一份速查和原来的用法行,退出码 0;输出不含任何 token。
anet node --help/anet node help与以前一样只打印帮助。
codex 节点不想记命令:在节点目录里敲 anet node codex,选节点、选操作,执行前会打印等价命令并确认。速查表见 Codex 节点速查:我想……就敲……。
| 命令 | 作用 |
|---|---|
anet node create <name> | 创建节点;未指定 runtime 时进入向导 |
anet node start <name> | 在当前终端启动节点 |
anet node start <name> --tmux | 在 tmux 中启动或连接节点 |
anet node stop <name> | 停止节点及其同名 tmux session |
anet node restart <name> | 停止后重新启动单个节点 |
anet node resume <name> [--session <id>] | 请求恢复保存或指定的会话;能否恢复取决于 runtime,下方说明 V1 共存限制 |
anet node delete <name> --force | 删除本地节点配置,并删掉它在 Hub 上的那一行 |
anet node rename <ref> <new> | 重命名已在 Hub 注册的节点 |
anet node clone <src> <new> | 复制节点设置,生成一个新身份的节点(见下文「复制节点」) |
anet node edit <ref> [--runtime <id>] [--model <id>] | 改已存在节点的 runtime / 模型;要重启才生效 |
anet node ls | 列出本地节点及网络状态 |
anet node ls --all [--network <id|name>] [--json] | 列出 Hub 上当前 Network 里你能看到的全部节点,按机器(hostname)分组(只读,见下文) |
anet node <start|stop|restart> <alias> --remote | 启动 / 停止 / 重启另一台机器上的节点:start / stop 由那台机器的 daemon 去做,restart 直接发给节点自己(见下文「远程管理节点」) |
anet node edit <alias> --model <id> --remote | 给另一台机器上的节点换模型(不需要 daemon) |
anet info <name> | 显示节点配置、进程和近期任务 |
anet logs <name> [--follow] | 查看或追踪节点日志 |
anet node migrate-token-to-envref <name> | 将配置中的明文 secret 改为 envRef,并先生成备份 |
OpenCode V1 共存恢复需要该节点的有效历史快照。 旧 CLI 2.3.0-preview.171 / runtime 2.5.0-preview.137 不恢复旧 session。新配对候选 CLI 2.3.0-preview.172 / runtime 2.5.0-preview.138 支持在新的运行数据目录 导入节点私有快照并核对原 session/history;导入失败不静默新建。没有快照的 旧节点仍会新建会话,不能直接重启试探恢复:首次升级前需要按手册在原桥 运行且任务/TUI空闲的窗口导出。崩溃恢复只保证最近成功保存的快照;外部 文件引用暂不支持迁移。候选通过 main 正式发布门前不是可下载版本。 完整边界见仓库运行手册 V1 重启与软件升级。
anet node ls --all 不看当前目录,而是用你 anet login 的身份读 Hub(/api/status + /api/host-supervisors), 把当前 Network(或 --network <id|name> 指定的那个,按 id、名字或唯一 id 前缀匹配)里的节点按机器分组列出: 别名、runtime、状态与最后心跳、模型,以及那台机器上有没有在线的 daemon(anet daemon,有在线 daemon 的机器以后才能被远程管理)。
Network: team (net_0123456) — 3 node(s) on 2 machine(s)
host-alpha daemon: alpha-daemon online — remote-manageable
ALIAS RUNTIME STATUS LAST SEEN MODEL
a-coder claude-agent-sdk idle 12s ago claude-sonnet-4-5
a-writer codex-app-server working 1m ago gpt-5
host-beta daemon: none visible
ALIAS RUNTIME STATUS LAST SEEN MODEL
b-runner grok-build-cli offline 3d ago -- 权限完全按 Hub 返回的来:被限制了 Agent 访问的成员只看到授权给他的节点;Hub 对这类成员不返回 daemon, 所以显示
daemon: none visible(「没看到」,不等于「没有」)。 - 只用登录令牌(
~/.anet/config.json的token),不用COMMHUB_TOKEN或任何节点令牌。 --json输出{ network, daemons_readable, machines: [{ hostname, daemon, daemons, nodes }] }。- 不加
--all时anet node ls行为不变。
远程管理节点(--remote)
在任何一台登录了 anet login 的机器上,管理住在另一台机器上的节点(#562):
anet node stop coder-b --remote # 先显示节点在哪台机器、daemon 是否在线,再问 y/N
anet node start coder-b --remote --yes # 脚本里用 --yes 跳过确认
anet node restart coder-b --remote --wait 90 # 等结果最多 90 秒(默认 60,--wait 0 不等)
anet node edit coder-b --model m2 --remote # 换模型(节点会重启一次来生效)Node coder-b — on machine-b, daemon daemon-b online; hub state: active / idle
Will run 将执行:
anet node stop coder-b --remote --network net_271eb4d9e48c --yes
(ask the hub to have daemon daemon-b on machine-b stop "coder-b" (its config is kept; start it again with --remote))
Run it? 执行? [y/N]: y
Dispatched (stop_node sr_c6dd58d6e565).
Waiting up to 60s for the hub to show the result …
✓ "coder-b" is stopped on machine-b- 走的是 Hub 上已有的生命周期工具(和 app 一样):start / stop 由那台机器的 daemon(
anet daemon)执行; restart / 换模型由 Hub 直接发给节点自己,节点处理完当前回合后退出(退出码 75),由它自己的anet node start外壳重新拉起—— 不管这个外壳是 daemon 起的还是人手工起的。 - start / stop 的前提:节点所在机器上有在线的 daemon,且节点是由这个 daemon 创建的(app「新建节点」选那台机器)。 否则在派发之前就拒绝并说明原因,例如
this machine can't be managed remotely: no daemon online on machine-c; 手工anet node start起的节点要在它自己的机器上 start / stop。 - restart / 换模型(#570)不需要 daemon,手工起的节点也可以。前提是节点正在运行,并且上报了
config_update_capable(即它跑在anet node start外壳下,退出 75 会被重新拉起)。不满足就在派发前拒绝:"<alias>" can't be restarted remotely: it doesn't support config updates (started without `anet node start`?); restart it on its machine——例如用 pm2 / systemd 直接跑的agent-node,退出后不会有人拉起它。确认时会写明做法,例如ask "hand-b" on machine-b itself, through the hub, to restart (it's not managed by a daemon)。 - 重启 / 换模型的结果以 Hub 为准:节点重新上报(配置版本前进、心跳更新、不是离线)才算成功;超时时如实说明最后看到的状态 (已离线一直没回来 → 去那台机器上看;仍在 working → 当前回合结束后才会重启)。
- 权限完全由 Hub 判:viewer 会看到 Hub 的
permission_denied;被限制 Agent 访问的成员看不到 daemon、也读不到节点配置, start / stop / restart / 换模型都在派发前就被拒绝并说明原因,节点不受影响。 - 只用登录令牌(
~/.anet/config.json的token),不用COMMHUB_TOKEN;保存的若是节点令牌则拒绝。 - 不在终端里(脚本 / 管道)且没带
--yes:什么都不做,退出码1。超时会如实说「请求已被 Hub 接受但还没看到结果」,用anet node ls --all查看。 --network <id|name>选别的 Network;本地专用的参数(--tmux、--copresence等)和--remote一起用会被拒绝(退出码2)。stop --force用于节点还有进行中的任务时强停(Hub 记审计)。远程删除不在这一步的范围内。
anet node delete <name> --force 先做一遍和 anet node stop 相同的停止(共存节点的 tmux 会话也一起停: codex 共存按标记 + CODEX_HOME 认,其余只认完整会话名、不按前缀),再删本地(.anet/nodes/<id>/),最后删 Hub 上这个节点的那一行, 这样它不会在 app / dashboard 里一直显示「离线」。停不干净就报错退出,什么都不删。
节点不在当前目录(clone / codex fork 用了
--workdir)时,anet 按源目录.anet/child-workdirs.json和 codex 登录索引找到它, 不替你删,而是打印cd <目录> && anet node delete <name>,退出码1。同名的节点有好几个时拒绝删除,列出每一个按
node_id删的命令。Hub 上的行只按本地配置里的
node_id匹配,从不按名字。别的机器上同名的节点(名字复用) 不会被删;anet 会打印Left untouched: … belong to other node_id(s)。Hub 连不上或拒绝时,本地照样删掉,anet 打印警告和原样可用的重试命令,退出码
1:bashanet node delete <node_id> --hub-only # 只删 Hub 上那一行 anet node delete <node_id> --hub-only --hub <url> # 节点用的不是当前登录的 Hub 时没配置 Hub 时只删本地,退出码
0。很早的节点配置里没有node_id,那时 Hub 上的行无法安全匹配, anet 不碰它并提示到 app / dashboard 里删。
node delete 不会自动撤销该节点已签发的 ntok_;需要彻底失效时另行执行 anet token revoke <token-id>。
anet node stop 只发 SIGTERM,最多等 8 秒,不会升级到 SIGKILL(它没有 --force)。 节点在 8 秒内没退出时,anet 打印 pid <n> survived SIGTERM、保留 pidfile、退出码 1 —— 它宁可报错也不谎称已停,因为一个还活着的旧进程会继续心跳并把 rename 顶回去。 同样的原因,node restart 和 node delete 这时也会拒绝执行。 唯一会主动发 SIGKILL 的是 anet node rename --force。
COMMHUB_TOKEN 不是 CLI 参数,也不存在 anet node start --token。节点鉴权按节点配置、全局配置、legacy COMMHUB_TOKEN 环境变量的顺序取值;anet login --token 登录的是 CLI 用户,不是向 node start 临时注入节点 token。
创建节点时常用参数:
| 参数 | 说明 |
|---|---|
--runtime <runtime> | 指定 runtime;可用值以当前频道的创建向导和 Runtime 对比 为准 |
--model <id> | 覆盖 runtime 默认模型 |
--resume <id> | claude-code-cli:绑定指定 Claude Code session |
--resume-latest | claude-code-cli:绑定当前项目最近的 session |
--tools <list> | 为支持该选项的 runtime 配置工具集 |
anet session ls 可列出当前目录下的 Claude Code sessions。不同 runtime 的会话语义不同,不要把 Claude session ID 当作 Codex thread ID 使用。
复制节点
完整说明(clone 与 fork 的对照、复制项清单、codex 登录、怎么删干净)见 复制节点(clone / fork)与清理。
想要「再来一个和它一样的节点」,用:
anet node clone <源节点> <新名字>
# 等价写法
anet node create <新名字> --from <源节点>新节点会在 Hub 上重新注册(和 anet node create 走同一个接口),拿到自己的 node_id 和 ntok_; 不加 --start 不会启动。完成后会打印一张表,逐项列出 copied / regenerated / skipped。
| 类别 | 内容 |
|---|---|
| 复制 | runtime、模型、工具、权限 flags、system prompt、非机密 env、commhub channel;--workdir 时还有规则文件(CLAUDE.md / AGENTS.md)、skills 目录、.mcp.json(其中 env / header 的值清空);codex 节点的 codex-home/config.toml、AGENTS.md、skills/ |
| 重新生成 | node_id、别名、ntok_、claude-code-cli 的 session id、grok 共存 socket、codexProjectDir |
| 不复制 | token、所有 session / thread id、日志、pid / 锁、goals、inbox、channel 的 bot 凭据、codex auth.json、共存身份文件、机密 env 的值(只保留键名,改成指向新节点的 envRef,启动前自己填) |
| 参数 | 说明 |
|---|---|
--workdir <dir> | 放到另一个项目目录(不存在会创建);路径必须是英文 / ASCII(节点名可以是中文,目录不行)。不加时和源节点在同一个项目目录,规则文件和 skills 共用 |
--model <id> | 换一个模型 |
--start | 建完立即启动(有待填的机密时不启动,并提示先填) |
会拒绝:目标已存在、新名字和源节点同名、目标落在源节点自己的目录里、非 ASCII 的 --workdir、 opencode-cli 节点(运行时绑定和登录在节点配置之外,请用 anet node create --runtime opencode-cli 新建)、 host daemon(role=host_supervisor)。
codex 节点的登录不会被复制。一个登录只给一个节点(refresh token 一次性,共用会互相顶掉): 启动前先给新节点自己登录 CODEX_HOME=<新节点目录>/codex-home codex login --device-auth。 没登录就启动时,anet 会把本机 ~/.codex 的登录放进去——但如果本机已有别的节点在用这份登录, 就拒绝启动(exit 1,--allow-shared-codex-login 可强行共享,不安全;见 一个登录只给一个节点)。要用登记过的账号,用 anet node codex account install <新名字> --source codex-login:<profile-id>。 要连 codex 会话历史一起带走,用 anet node codex fork(见 Codex 共存)。 在 anet 之外直接用 codex 开过的对话,用 anet node codex adopt <新名字> --thread <id> 收编成节点(见 复制节点)。
不要 cp -r 节点目录
.anet/nodes/<name>/config.json 里存着节点的 node_id 和 ntok_。原样拷贝出来的「新节点」 和源节点是同一个 Hub 身份:两个进程都订阅同一个收件箱,同一个任务会被执行两次、回两次复, 而且 Dashboard 分不清是谁在回。日志、session、codex 登录也一起被共用。复制节点请一律用 anet node clone。
项目批量管理
这些命令扫描当前目录的 .anet/nodes/:
| 命令 | 作用 |
|---|---|
anet project up | 启动所有未运行节点 |
anet project restart | 重启所有节点 |
anet project down | 停止所有节点并上报离线 |
共享参数:
--stagger <seconds>:节点间错峰,默认 3 秒,0表示关闭。--only a,b:只处理列出的 alias 或 node ID。--exclude x,y:跳过列出的 alias 或 node ID。
批量创建和清理见 批量 Agent。
Channel
| 命令 | 作用 |
|---|---|
anet channel add telegram <node> --bot-token <token> --allow <uid> | 添加 Telegram |
anet channel add feishu <node> ... | 添加飞书;当前为 preview 能力 |
anet channel allow feishu <node> ... | 修改飞书私聊或群聊白名单 |
anet channel ls [node] | 列出 Channel |
anet channel status [node] | 显示 Telegram 实际配置路径和白名单 |
Channel 配置不会热加载,修改后需要重启节点。anet channel add wechat 尚未发布。完整命令见 Channel 接入。
Goal
| 命令 | 作用 |
|---|---|
anet goal list [node] | 列出本地 goal |
anet goal show <node> <id> | 查看详情与进度记录 |
anet goal wake-log <node> <id> [--tail N] [--json] | 导出完整 wake 历史 |
anet goal edit <node> <id> ... | 修改 interval、文本或状态 |
anet goal cancel <node> <id> | 标记为 cancelled |
anet node loop <node> "<task>" [--every 5m] | 向在线节点创建周期任务,并等待最多 15 秒确认 |
node loop 通过 Hub 投递 /aloop;goal edit/cancel 则直接修改 .anet/nodes/<node>/goals.json。运行中的节点不会自动重载外部文件修改,使用 edit/cancel 后请重启节点。Dashboard 原生 /goal、/loop 与 ANet /aloop、/agoal 的语义、状态和自管理工具见 Goal 与 Loop。
诊断与维护
| 命令 | 作用 |
|---|---|
anet status | 显示当前 Network 的节点和任务概览 |
anet tasks [status] [--limit <n>] | 查询任务 |
anet doctor | 检查配置、Hub、依赖、secret 与 Channel |
anet doctor --fix | 执行兼容迁移并修复可自动恢复的 token 问题;会修改配置 |
| `anet upgrade [--channel latest | preview] [--dry-run]` |
anet config / anet config path / anet config json | 查看全局配置摘要、路径或 JSON(token 打码) |
anet init | 配置 Hub URL;换到另一个 Hub 时,先在旧 Hub 上撤销保存的登录会话(尽力而为,失败只警告),再从配置里删掉旧 Hub 的 token 与登录信息——旧 token 不会发给新 Hub。之后重新 anet login。anet login --hub <另一个 Hub> 同样处理 |
anet init project | 在当前目录创建 CommHub MCP 项目配置 |
anet setup | 安装所选 runtime 的依赖 |
升级细节见 升级指南。
anet status 的四个数字怎么读
CommHub: http://127.0.0.1:9200
Agents: 127 idle, 0 working, 1 needs attention, 143 offline
└─ 18 掉线 1-3 天, 27 掉线超过 3 天
SSE: 12 connected
Tasks: 10 recent| 数字 | 含义 |
|---|---|
idle | 空闲,等派活 |
working | 正在推进一个回合(含 waiting_input —— 回合还在,只是在等人) |
needs attention | 需要人看一眼:blocked / error,以及任何本版本不认识的状态 |
offline | 心跳过期,或已正常停止 |
四个数相加等于节点总数。needs attention 为 0 时那一格不显示。
offline 下面那一行:它掉了多久
有 offline 节点时会多印一行分档。**「刚停的」和「掉了三天没人发现的」原先是 同一个数字。**实测过一次(84 台节点):45 台 offline 里 27 台超过 3 天、 18 台 1-3 天、近 6 小时内 0 台 —— 「当前没有活故障」和「有 45 台掉了」 是完全不同的两个结论,而屏幕上原本只有后者。
拿不到时间戳的节点单独归入「无时间戳(不知道掉了多久)」,不并进最新那一档 —— 「我不知道它掉了多久」和「它刚掉」是两件事。
🔴 blocked 没有对应的时长,而且不该有。 名册里没有「何时变成 blocked」 这个字段;updated_at 被心跳一直刷,拿它当已 blocked 时长,会给一个卡了很久的 节点印出「4 分钟前」—— 比不显示更糟。详见 这个节点还活着吗。
🔴 blocked / error 不算在 working 里。 它们的含义是「卡住了」而不是 「在干活」—— 一个卡住的节点如果被算进 working,运维看到「N working」会以为 一切正常。needs attention 大于 0 时,下面会列出是哪几个节点、它们自报的状态、 以及当时的任务。
⚠️ 这些状态是 agent 自报的,不含活性成分。 一个进程已经死掉但还没被心跳 清扫的节点,在这里仍可能显示成 idle。要确认它还在不在,发一条任务试试 —— anet status 回答的是「它上次说自己怎么样」,不是「它现在还在不在」。
手工节点收编(预览源码,board #626)
Linux daemon 配置 adopt_roots 为允许收编的工作目录绝对路径列表,默认空列表拒绝收编。 配置修改后由操作员按既有 daemon 启动流程加载;不要把根目录或整个 HOME 当作节点工作目录。 使用自己的 anet login 人类账号,在手工节点的工作目录运行:
anet daemon adopt demo --daemon <daemon-node-id>
anet daemon adopt demo --daemon <daemon-node-id> --yes
anet daemon adopt --all --daemon <daemon-node-id> --yes
anet daemon unadopt demo --yes不带 --yes 只展示计划。--all 仅枚举当前目录 .anet/nodes,不扫描机器其它目录。 请求成功只代表 pending,不代表收编成功;daemon 独立检查 UID、路径、配置身份、 /proc、HOME 和可重建环境,再原子登记并确认。收编及撤销都不重启、不发进程信号。 仅支持 bare / 默认 tmux socket;co-presence 或无法重建的环境会拒绝,错误只列环境键名。 从 daemon 工作目录运行 anet daemon adopted,列出同时具有本机登记和 Hub 有效绑定的节点。
adopt_path_writable_by_others:工作目录、.anet、.anet/nodes、节点目录或 config.json 对组 / 其他用户可写。修法是对报出的路径 chmod go-w。共享团队目录(如 2775)不想去掉组写时, 仅工作目录本身可改为加粘滞位:目录属 daemon 用户且 chmod +t <workdir>(如 drwxrwsr-t)也被接受, 因为粘滞位下其他用户无法改名或删除其中属 daemon 的 .anet;.anet 及以下仍必须 go-w。
此增量只完成收编协议,收编节点的远程停启需后续 board #627;当前会明确拒绝, 不会落入旧 daemon 子进程清理路径。登记文件是 daemon 工作目录下 .anet/child-workdirs.json,节点配置及密钥仍在原目录。空机恢复需恢复这些私有文件和 Hub 数据备份;只 clone 仓库不会恢复绑定、账号或密钥。撤销绑定不删除工作目录。
Preview 专属能力
开发中、尚未发布(#1053):Daemon/节点环境密钥管理不再只依赖演示表单。 客户端经 daemon_provider_snapshot 的 secret_* 管理动作发起 owner/admin 请求,所属 Daemon 用 anet daemon secret <daemon> 低层适配器读取私有 JSON stdin,回读只有名称/来源。 该适配器不接受命令行密钥值,也不启动或重启节点。Daemon 默认值由节点继承, 节点同名值优先;已有显式进程/config/Provider 配置保持优先。 值在节点下次完整启动时加载,单独重启 Bridge 不等于原生 TUI/server 已更新。 完整契约与恢复边界见仓库 docs/scoped-secrets.md;0.2.248 演示入口不能计为已支持。
以下能力存在于当前 preview,不应写成 stable 已支持:
| 命令 | 作用 |
|---|---|
anet daemon up [name] | 创建并启动 host_supervisor(版本要求见 哪些版本有 anet daemon) |
anet daemon init <name> / start <name> / restart <name> / list | 管理本机 daemon(版本要求同上;restart 另见 daemon 页) |
anet node start <name> --copresence | 启动 Codex app-server、桥和共享 TUI |
anet opencode ... | 管理 OpenCode preview 集成 |
OpenCode V2 共享 Server:创建时的复用与逃生门
创建 opencode-cli V2 节点时,除代际与 unsafe 授权外,还有三个与「主机共享 Server」相关的源码选项;对应版本须实际发布后才可使用。细节、凭据与迁移边界见运行手册:
| 开关 | 作用 |
|---|---|
--opencode-shared-server-url <字面量 http://127.0.0.1:<port>> | 显式指定复用哪个 loopback Server(与下面那项必须成对) |
--opencode-shared-server-password-file <绝对路径> | 该 Server 的私有密码文件路径;文件须为私有普通文件,密码值不得写进命令行/URL/日志 |
--opencode-new-server | 显式让新节点自建 Server,不用共享;可与 clientBinDir 并用,但不会继承共享 URL/密码文件 |
主机用户也可以在私有 ~/.anet/opencode-shared-server.json 里登记 { "url": ..., "passwordFile": ..., "clientBinDir": ... },此后新建 V2 节点默认复用它。
源码状态(daemon 创建准入,父 #1302 / #1326):登记是操作者维护的候选登记——它只提供连接候选,不提供进程身份或代际证明。daemon 创建 V2 节点时按「观察 → 代际绑定 → 准入通过」三步判定,三步都过才复用:进程观察(同 uid、真实 pid/startTicks、监听绑定复核)→ 与既有包/文件 attestation 绑定代际 → 准入放行;任何一步拒绝(登记陈旧、进程已死、他人 uid、无法完整观察、代际不可证)都回退为节点自有 Server,不写共享 URL/密码文件。--opencode-new-server 的显式逃生门仍然有效。上述均为源码实现:不代表 npm 已发布或现役 daemon 已采用(完整原生启动验收已于 PR #2790 通过:exact-head 收据 = Actions run 38809788212 / job 116679815373,产物 source=506902c4…,合并提交 079daa31);CLI 直接创建/显式连接须单独核对,不能用 daemon 接线代替其验收;命令可用性以本机 anet -v、anet node create --help 与对应发布说明为准。
开发中、尚未发布(#906):anet daemon provider list <daemon> 回读该 daemon 的脱敏 Provider → Runtime → 认证方式 → 模型配置;anet daemon provider put <daemon> --revision N 从 stdin 保存私有 JSON,key 不接受命令行参数。配置成功不等于登录、模型请求或节点切换成功。 源码已接入受权限保护的配置保存/回读;anet daemon provider scan <daemon> 从 stdin 读取 Hub 归属清单(node_id/alias),只读扫描已有 Codex 配置与脱敏认证信息。客户端在同一管理页展示, 扫描不复制登录文件、不改节点、不触发模型调用;账号/节点实际切换仍在接入,不代表现有安装包已有此能力。
🔴 停 / 删 / 看没有 daemon 版。 daemon 就是一个 role=host_supervisor 的 agent-node, 所以用 node 级命令:anet node stop <name> 停、anet node delete <name> 删、 anet node ls 看它在不在跑(anet daemon list 只列本机配置过的 daemon,不含活性)。 anet daemon restart 内部调的正是 anet node stop 用的那个 stop。
daemon 的两个反直觉之处
① 对已存在的 daemon,anet daemon init <name> 什么都不改。 它打印 ✓ "<name>" already a host_supervisor daemon 后直接返回 —— 一个绿色的对勾, 配置一个字节没动。要改配置必须带 --force(保留 node_id,但会重新签发 token,且改完要重启该 daemon 才生效)。
② 配置里的 runtime 清单是写入那一刻的快照,不会自愈。 之后新增的 runtime 不会补进已有 daemon 的配置,表现为客户端「选服务器」里那台机器可选的 runtime 比别人少。anet daemon list 发现这种情况时会直接打出缺哪几个和回填命令。
Codex 的 --copresence 用于 runtime=codex-app-server。其默认沙箱为只读;开启完整文件系统和网络访问需要 --dangerously-allow-full-access。TTY 会要求输入 yes,非 TTY 还必须同时提供 --yes-danger-full-access。
恢复共存节点时仍应使用 anet node start <name> --copresence,不能改用普通 node start。
opencode-cli 的 V2 TUI 共存与共享 Server 是上文说明的独立预览路径,要求显式 unsafe 授权;不能套用 Codex 的只读沙箱说明,发布状态须按实际版本核对。grok-build-cli 共享 Grok TUI 也未进入当前 preview 包;当前可用的 grok-build-acp 不支持 attach。
其他命令
| 命令 | 作用 |
|---|---|
anet import [alias] | 从 Hub 导入可恢复的本地节点配置 |
anet run --alias <name> | 启动不调用 LLM 的最小 SSE echo agent |
anet demo [name] | 运行实验性演示;不作为生产编排方案 |
anet batch <verb> | 管理 anet create --batch 创建的批次 |
anet license / anet activate <key> | legacy 许可证兼容命令;Apache-2.0 用户通常无需使用 |
旧别名 anet create、anet start 等仍为兼容保留,新文档统一使用 anet node ...。
退出码
| 退出码 | 含义 |
|---|---|
0 | 成功;或只打印帮助(例如不带子命令的 anet node、anet network,或任何命令 / 子命令加 --help、-h) |
1 | 失败:未初始化 / 未登录 / 登录已过期、Hub 连不上或返回错误、找不到对象、本地写入失败、拒绝执行 |
2 | 用法错误:缺少必需参数、未知子命令、非法取值 |
脚本和 CI 可以直接用 anet … || exit 1 判断。下列情况在 #515 之前打印了错误却退出 0,现已改为非零:
| 命令 | 情况 | 之前 → 现在 |
|---|---|---|
anet whoami | 未登录 / 登录已过期 / Hub 连不上 | 0 → 1 |
anet network <ls|use|info|create|delete|rename|invite|join|members> | 未 init / 未登录、Hub 返回错误、找不到 Network、连不上 | 0 → 1 |
anet network create|use|delete|rename|join(缺参数)、anet network <未知子命令> | 用法错误 | 0 → 2 |
anet token ls|create|revoke | 未登录、Hub 返回错误、连不上 | 0 → 1 |
anet token revoke(缺 token id) | 用法错误 | 0 → 2 |
anet passwd | 未登录、两次密码不一致、Hub 拒绝、连不上 | 0 → 1 |
anet activate | 未 init、激活失败、连不上 / 缺 license key | 0 → 1 / 0 → 2 |
anet status、anet tasks | 没有配置 Hub;anet tasks 连不上 | 0 → 1 |
anet hub start | Hub 15 秒内没起来(含缺 Bun) | 0 → 1 |
anet hub stop | 见上方 停止 Hub | 0 → 1 / 2 |
anet hub admin reset-user | 缺 --username / 找不到 DB、重置失败 | 0 → 2 / 0 → 1 |
anet hub <未知子命令>、anet node <未知子命令>、anet session <未知子命令>、anet batch <未知动词> | 用法错误 | 0 → 2 |
anet node resume(缺节点名)、anet logs(缺节点名)、anet batch <动词>(缺前缀) | 用法错误 | 0 → 2 |
anet project down(有节点停失败)、anet node delete(节点进程拒绝退出) | 已经设置了退出码 1,但被 CLI 收尾的 process.exit(0) 覆盖 | 0 → 1 |
anet upgrade | 有包升级失败或查不到 registry | 0 → 1 |
anet node delete --force | 本地已删,但 Hub 上那一行没删掉(连不上 / 被拒);会打印 --hub-only 重试命令 | 新行为:1 |
anet create --batch、anet batch cleanup | 缺 Hub、预设/参数非法、自动登录失败、一个节点都没建成 | 0 → 1 / 2 |
anet demo … | 缺 Hub / token / key、建 Network 或节点失败 | 0 → 1 |
一些较早的用法错误路径仍退出 1(同样是非零,脚本照样能判断),没有为了统一成 2 而改动。anet hub status 是状态查询,Hub 没运行时仍退出 0。
报错与密钥
- 出错时 anet 打印一句说明和下一步要敲的命令,不打印堆栈。要看堆栈:
ANET_DEBUG=1 anet …。 - 以错误码为消息的致命错误仍是
[anet] FATAL: Error: <CODE>这一行,方便脚本从日志里取码。 - anet 不在输出里打印完整密钥:
anet config、anet config json、anet node start、anet doctor显示的 token 都是utok_…ab12这种「前缀…末 4 位」;报错里出现的 token、Bearer头和 URL 里的token=参数同样打码。 anet node create --env KEY=…和anet node migrate-token-to-envref把密钥写进.anet/nodes/<node>/.env(权限 600、已加入 gitignore),只打印打码后的值和一条从该文件读取的export命令。- 例外:
anet token create和anet hub admin reset-user的用途就是发出新凭据,只显示这一次。
配置位置与环境变量
| 路径 | 内容 |
|---|---|
~/.anet/config.json | 当前 Hub、用户 token 和 Network |
.anet/nodes/<node>/config.json | 节点配置 |
~/.commhub/commhub.db | 默认 Hub SQLite 数据库 |
~/.anet/server/admin-utok.json | Hub 主机的本地管理员恢复 token |
常见环境变量:
| 变量 | 作用 |
|---|---|
COMMHUB_URL | Hub URL |
COMMHUB_ALIAS | 节点 alias |
COMMHUB_TOKEN | 认证 token;节点配置中的 token 优先级更高 |
COMMHUB_AUTH_TOKEN | legacy Hub master-token 兼容入口;新部署使用用户和节点 token |
ANET_CODEX_RESUME_TIMEOUT_MS | anet node start --copresence 恢复 Codex 会话的超时(整数毫秒,最大 900000);未设置时从 300 秒起,按 rollout 每开始 1 GiB 增加 120 秒,最高 15 分钟;超时会报告实际等待时间和 rollout 大小 |
Codex 共存启动会在旧进程停止后先创建私有恢复点;备份失败时默认拒绝启动,并清掉半成品。只有操作员明确接受无法安全回滚时,才可使用 --skip-recovery-backup 跳过;命令会打印醒目的风险警告,且不会把旧恢复点冒充成这次启动的新备份。 | ANTHROPIC_BASE_URL | Anthropic 兼容模型端点 | | ANTHROPIC_AUTH_TOKEN | 第三方 Anthropic 兼容端点凭据 | | ANTHROPIC_API_KEY | Anthropic 官方端点凭据 |
Secret 建议使用 envRef,不要把 token 或模型密钥直接提交到配置仓库。详见 安全设计。