zhin runtime start
zhin runtime start 是启动 Bot 的唯一命令。它直接运行项目里的 TypeScript 源码,不需要先编译;按配置装配 HTTP、数据库、Console、Agent 等服务;开发模式下保存文件即热重载,不用重启进程。
常见用法
# 日常开发:HMR + Console
pnpm dev
# 生产:关闭 watch,日志走 stdout(交给 systemd / 容器收集)
zhin runtime start --mode production --no-watch
# 裸机常驻:daemon + 开机自启
zhin runtime start --daemon
zhin service install
# CI 冒烟:起一次确认配置与装配无误
zhin runtime start --once --mode test| 场景 | 用哪种 |
|---|---|
| 本地开发 | pnpm dev:监听文件、热重载 |
| 服务器上由 systemd / Docker 管理 | --mode production --no-watch:不监听文件,日志输出到 stdout |
| 服务器上没有进程管理器 | --daemon:自带守护进程,崩溃自动拉起 |
| CI 中检查配置和装配 | --once:启动一次就退出,输出 JSON |
选项
| 选项 | 说明 |
|---|---|
--mode <mode> | 运行模式:development(默认)/ test / production |
--environment <name> | 环境名,默认 development;决定加载 .env.<name>,须匹配 /^[a-z0-9][a-z0-9-]*$/ |
--no-watch | 关闭文件监听与热重载 |
--once | 完成一次启动后输出 JSON 快照并退出 |
-d, --daemon | 守护进程模式:写 pid 文件、日志重定向、崩溃自动重启 |
--log-file <path> | daemon 日志文件,默认 <项目根>/.zhin/runtime.log |
未知选项会在启动前直接报错(fail fast),不会进入重启循环。
原生 TypeScript(strip-types)
运行时不经过项目编译步骤,插件源码由 Node 直接执行。支持版本与仓库 engines 一致:>=22.19.0,不满足时直接报错。
前台模式会按 Node 的 TypeScript 支持能力决定是否需要 wrapper。daemon 模式始终使用 supervisor → 子进程两层结构,见下文进程管理。
因为是「源码即运行时」,import 本地 TS 文件必须带 .js 扩展名(由运行时做 specifier 重映射),这也是仓库代码约定之一。
.tsx 入口是怎么加载的
服务端约定入口也支持 .tsx。CLI 会优先交给进程现有 loader;Node 不识别 .tsx 时,再为该入口启用隔离的 TSX loader,因此开发 watch、--no-watch、supervisor 与 daemon 使用同一条启动命令。项目的 tsconfig.json 需设置 "jsx": "react-jsx" 和合适的 "jsxImportSource";新脚手架默认使用 zhin.js,单文件可用 /** @jsxImportSource zhin.js */ 覆盖。
启动流程
注意 Agent 栈是按需加载的:只有配置了 ai / assistant 任一段,才会解析 @zhin.js/agent;纯 IM 项目不会载入 AI 依赖。启动成功后的输出也因环境而异——TTY 下是一行摘要(插件数、HTTP 地址、在线/离线适配器),非 TTY 或 --once 则输出结构化 JSON({ started: true, ... }),便于脚本消费。配置校验失败会报 Invalid Plugin config in zhin.config.yml 并列出全部问题。
HMR 语义
开发模式默认开启文件监听(--once / --no-watch 关闭)。变更按影响面分两级处理:
| 变更内容 | 处理方式 |
|---|---|
| 插件 / 应用源码 | 进程内世代(generation)重载:只重建受影响的 capability slot 与插件子树,Console 经 SSE 收到 hmr:reload |
依赖清单类文件(pnpm-lock.yaml、pnpm-workspace.yaml、package-lock.json、yarn.lock) | 进程级重启:进程以退出码 75 退出,由外层 supervisor 重新拉起 |
世代重载是事务式的:新世代先完成装配与 commit,再异步 dispose 旧世代;HTTP 端口在世代切换时先释放再 listen,不会出现端口占用导致的 Console 丢失。同一批文件事件会合并为一次世代事务,避免连续抖动。
进程管理
daemon 模式
zhin runtime start --daemon # 日志进 .zhin/runtime.log
zhin runtime start -d --log-file ./bot.log
zhin stop # 停止(读取 .zhin.pid)supervisor 常驻并把自身 pid 写入 <项目根>/.zhin.pid,bot 进程的 stdout/stderr 追加到日志文件。崩溃(信号或非零退出)与退出码 75 都会触发重新拉起;zhin stop 或 kill -TERM <pid> 正常结束。另有风暴保护:最多每分钟重启 10 次、每次间隔 3 秒,超出预算 supervisor 放弃并退出,避免崩溃循环刷爆平台连接。
退出码
| 退出码 | 含义 |
|---|---|
0 | 正常退出 |
51 | Console 请求重启(daemon 下会被自动拉起) |
75 | restartRequired(依赖状态变化等),supervisor 按风暴保护重启 |
孤儿看门狗
bot 进程每 2 秒检查一次 supervisor(ZHIN_SUPERVISOR_PID)与父进程存活状态。wrapper 被 kill -9、终端关闭或崩溃时,bot 会主动关闭——不会留下仍握着平台连接和文件监听的僵尸进程。SIGINT / SIGTERM / SIGHUP 也会被转发给子进程。