安装部署
约 2669 字大约 9 分钟
2026-09-15
环境要求
| 项 | 要求 |
|---|---|
| Node.js | ≥ 20(推荐 22 或更高,Promise.withResolvers 等 API 需要较新版本) |
| 包管理器 | pnpm(9 或更高;用 npm 也能装但 workspace 行为不同,见下) |
| 系统 | Windows / Linux / macOS 均可;渲染需要本机有 Chrome 或 Edge |
| Redis | 可选。本机地址连不上时会降级为内存实现 |
pnpm 版本会影响什么
sqlite3 的原生模块覆盖写在 pnpm-workspace.yaml 的 overrides 里 —— pnpm 9/10/12 都读这个文件; package.json 的 pnpm.overrides 里也放了一份同样的内容,是给 pnpm 8 及更早 用的 (pnpm 10+ 会提示 "The pnpm field in package.json is no longer read",忽略即可)。 两条路都留着,所以哪个版本都不会漏;升级到 pnpm 9+ 后行为一致。
安装
同一份代码有两个分支,按「要不要自己编译内核」二选一:
| 方式 | 分支 | 需要构建 | 适合谁 |
|---|---|---|---|
| 已构建版(推荐) | release | 不需要,内核已编译好 | 只想装上就用 |
| 源码版 | master | 需要(node jiuli s 会自动编译) | 想自己改内核 / 跟最新提交 |
两个分支的更新是同步的:每次发版会先打 tag、建 Release,再把编译好的产物推到 release。 master 里的 lib/core/ 是 tsc 产物、不入库,所以源码版首次启动会多一步编译(CLI 自动做,不用手动 pnpm build)。
方式一:已构建版(不需要自己构建)
git clone -b release --depth 1 https://github.com/HL-z7/JiuLi.git
cd JiuLi
pnpm install # 装依赖
node jiuli s # pm2 后台启动(只生成配置,不编译)方式二:源码版(自己构建)
git clone https://github.com/HL-z7/JiuLi.git
cd JiuLi
pnpm install # 装依赖
node jiuli s # 自动检查依赖、编译内核、生成配置已经适配新版框架的历史版本在 v2 分支:git clone -b v2 https://github.com/HL-z7/JiuLi.git。
网络慢?用代理前缀
GitHub 直连慢的话,在地址前加 https://gh-proxy.org/ 即可(clone 友好,分支与 tag 都正常):
git clone -b release --depth 1 https://gh-proxy.org/https://github.com/HL-z7/JiuLi.git也可以整体换用 Gitee 镜像(分支名一致,但没有 release 分支,需要自己编译): https://gitee.com/fox-glaze/jiuli.git。
等价写法:node jiuli start、pnpm jiuli start、pnpm start。
pnpm install && pnpm start 一条命令也能跑完 —— CLI 会自己补齐「编译内核」和「生成配置」这两步。
首次启动后框架会自己补装缺的系统组件(日志里是 [ Redis ] / [ Render ] / [ Deps ] 开头):
| 缺什么 | 影响 | 自动处理 |
|---|---|---|
| Redis | 数据无法持久化 | 自动装 redis-server;装不上可开内存降级 |
| Chromium | 图片渲染停用 | 优先用系统已装浏览器,否则装 chromium + 中文字体 + emoji 字体 |
| ffmpeg | 插件转视频 / 语音失败 | 自动装 ffmpeg |
自动安装只在 Linux + root 下做;其它情况会打印手动安装命令。整体关掉用环境变量 JIULI_NO_AUTO_INSTALL=1。
从 TRSS-Yunzai 迁移
已经有 Yunzai 在跑,可以一键把数据搬过来。只复制不剪切,源目录不会被改动 —— 在 Yunzai 根目录执行:
# Linux / macOS
curl -fsSL https://gitee.com/fox-glaze/h-library/raw/master/migrate-from-yunzai.mjs -o migrate.mjs && node migrate.mjs# Windows PowerShell
iwr https://gitee.com/fox-glaze/h-library/raw/master/migrate-from-yunzai.mjs -OutFile migrate.mjs; node migrate.mjs脚本会逐项问你:JiuLi 装到哪个路径、是否迁移 config / data / plugins、是否连插件的 .git 一起复制、 目标同名文件是否覆盖、完成后是否立即装依赖并编译内核 —— 确认后才开始复制,复制过程有进度。
| 内容 | 处理方式 |
|---|---|
config/config/ | 用户配置;default_config/ 不复制,避免覆盖框架默认值 |
data/ | 运行数据;迁移前请停掉机器人(里面有数据库) |
plugins/ | 插件目录与裸的单文件插件;node_modules 等依赖目录一律跳过,框架自带的 system / other / adapter 跳过 |
| 依赖 | 迁移后自动 pnpm install → pnpm build(内核是编译产物)→ node jiuli deps 补插件缺失依赖 |
目标已有同名文件时默认不覆盖,所以中途中断重跑是安全的(会接着补齐缺的文件); --dry 可以只统计体量不落盘,--yes 全部用默认值不提问,其余参数见脚本头部注释。
启动模式
| 命令 | 进程模型 | #重启 的行为 |
|---|---|---|
node . | 守护进程 + 主进程,占用当前终端 | 主进程以退出码 255 退出,守护进程在同一终端重新拉起(不开新窗口) |
node jiuli s(pm2) | pm2 托管,后台常驻 | 交给 pm2,日志用 node jiuli l 看 |
node . start | 单进程 | 退出码 255,交给外部管理器(systemd / supervisor)拉起 |
node . dev | 单进程 | 开发用 |
调试推荐 node .:终端里能直接输入指令,日志也直接打在脸上。
首次启动会做什么
- 建运行目录:
data/、logs/、temp/、resources/ - 从
config/default_config/*.yaml复制一份到config/config/*.yaml(用户配置,之后只改这里) - 启动 HTTP 服务(默认
http://localhost:1721)与渲染后端 - 加载
plugins/下全部插件 - 加载适配器。默认带两个:标准输入(终端交互)和 Mock(把机器人发出的消息打到日志)
启动完成后日志长这样:
[ JiuLi ] ──────── 启动中 ────────
[ JiuLi ] v0.1.0 · 插件可见的框架标识 JiuLi v3.1.3
[ Redis ] 已连接 redis://127.0.0.1:6379/0
[ Server ] 启动 HTTP 服务器 http://[::]:1721
[ Render ] 渲染后端 puppeteer 已就绪 · 类型 image · 支持 script 是
[ Plugin ] 插件 110 个 · 规则 377 条 · 事件监听 28 个 · 定时任务 4 个 · 用时 14秒345
[ Adapter ] 适配器 3 个 · Mock(QQ) · 标准输入(stdin) · QQBot(QQBot)
[ JiuLi ] ──────── 启动完成 · 总耗时 15秒021 ────────左侧 [ xxx ] 是日志 id 列,宽度由 config/config/bot.yaml → log_align 决定(默认 " JiuLi "); 没有指定 id 的框架级日志会显示 [ JiuLi ]。
接入协议端
框架自身不连接 QQ,需要装适配器插件:
常见的两个(都在 TRSS 官方仓库):
| 适配器 | 适用 | 地址 |
|---|---|---|
| ICQQ | 个人 QQ(协议端登录) | https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin |
| QQBot | QQ 官方机器人 | https://gitee.com/TimeRainStarSky/Yunzai-QQBot-Plugin |
# 个人 QQ:克隆到 plugins/Yunzai-ICQQ-Plugin,并自动装依赖
node jiuli a https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin
# 重命名目录:加 --name(注意 URL 要写在前面)
node jiuli a https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin --name ICQQ-Plugin默认目录名取仓库地址最后一段(去掉 .git),所以不加 --name 就是 Yunzai-ICQQ-Plugin。 想改名用 --name 新名字(也支持 --name=新名字)。
改名有风险
云崽生态的插件包(有 index.js 的那种)经常把目录名写死在代码里,例如 fs.readdirSync('./plugins/yenai-plugin/apps')。改目录名会让这些写死的路径失效, 需要进插件里一起改。平铺的单文件插件(plugins/example/xxx.js)改名不受影响。
适配器统一走云崽的 Bot.adapter.push 契约,OneBot11 / ICQQ / QQBot 之类的插件都能直接用。 装完 #重启(或等热重载),#诊断 能看到适配器与在线账号状态。
服务器上常驻运行(SSH)
node . 是「守护进程 + 主进程」占用当前终端的模式,而 SSH 断开时终端会发出 SIGHUP, 进程会被结束(守护进程会先结束子进程再退出,不会留下占用端口的孤儿进程)。
想同时保住「实时日志」和「终端输入指令(#重启 等)」,直接用 CLI 进 tmux 会话最省事:
node jiuli m # 进入 tmux 会话 jiuli:不存在则新建,已存在则接管
# 按 Ctrl+B 再按 D # 脱离会话(进程继续跑,SSH 断开也不受影响)
# 重新连上服务器后
node jiuli attach # 回到原始终端:实时日志 + 可以继续敲 #重启查看历史输出:Ctrl+B 再按 [,用方向键 / PgUp 翻阅,q 退出。 会话名默认 jiuli,可用环境变量 JIULI_SESSION 覆盖;没装 tmux 会自动尝试 screen。
等价的手工做法(不想用 CLI 时):
tmux new -s jiuli # 或 tmux new-session -A -s jiuli
node .其它方式与取舍:
| 方式 | 常驻 | 终端输入指令 | 看日志 |
|---|---|---|---|
node jiuli m / tmux | ✅ | ✅ | 直接看,或 node jiuli attach |
nohup node . >/dev/null 2>&1 & | ✅ | ❌ | node jiuli log -f / tail -f logs/jiuli.*.log |
node jiuli s(pm2) | ✅ | ❌ | node jiuli log -f |
| systemd 服务 | ✅ | ❌ | journalctl -u jiuli -f |
没有终端交互时,重启、停止这类操作直接在 QQ 里发 #重启 #停止 即可,效果一样。
更新
命令行:
git pull && pnpm install && node jiuli r源码版(master)在拉取后还需要重新编译内核,用 node jiuli r 重启时会自动补上; 也可以先手动 node jiuli u(或 pnpm build)再重启。已构建版(release)拉下来的就是编译好的产物,不用额外编译。
聊天里的内置指令(与云崽一致):
| 指令 | 说明 |
|---|---|
#更新 | 更新框架本体 |
#更新ICQQ | 只更新指定插件,插件名可简写(Yunzai-ICQQ-Plugin 写 ICQQ 即可) |
#静更新 / #安静更新 | 静默:只有真的有更新或出错时才回复,适合定时任务 |
#强制更新[插件名] | 丢弃本地改动,对齐远程分支(git reset --hard origin/<分支>)后再拉取 |
#全部更新 / #全部静更新 / #全部强制更新 | 框架 + plugins/ 下所有 git 仓库 |
#更新日志 [插件名] | 查看最近提交(以转发消息发出) |
行为说明:
- 更新成功后会自动重启(与云崽一致);守护进程在同一终端重新拉起,不会开新窗口
- 只有
package.json/pnpm-lock.yaml/pnpm-workspace.yaml有变动时才执行pnpm install - 同一时间只允许一个更新流程,重复触发会提示「正在更新,请稍候再试」
- 拉取失败会说明原因与下一步(网络 / 鉴权 / 本地改动 / 分支分叉);本地改动不会被静默丢弃, 需要你自己确认后再发
#强制更新 - 非 git 目录(
plugins/system、plugins/demo、自建目录)在#全部更新时自动跳过
自动更新(config/config/bot.yaml):
update_time: 1440 # 启动后每隔 N 分钟自动更新一次,0 关闭(默认 1440 = 24 小时)
update_cron: # 或按 cron 定时更新,可写多条目录结构
jiuli/
├─ app.js 启动入口(按参数切换启动模式)
├─ jiuli.mjs CLI
├─ src/ 内核 TypeScript 源码(编译到 lib/core/)
├─ lib/ 运行时目录:编译产物 + 手写兼容层 shim
├─ renderers/ 渲染后端(puppeteer)
├─ plugins/ 插件目录
│ ├─ example/ 你自己的插件
│ ├─ system/ adapter/ 框架内置插件与适配器
│ ├─ demo/ 示例插件
│ └─ <第三方插件>/ 不入库
├─ config/
│ ├─ default_config/ 默认配置(随仓库)
│ └─ config/ 用户配置(首次启动生成,不入库)
├─ templates/ 插件骨架模板
└─ test/ 测试脚本运行状态目录
data/、logs/、temp/、config/config/ 是运行状态,升级和排障时不要删:
data/玩家数据、数据库、缓存logs/日志(错误另存logs/error/)config/config/主人、群配置、账号凭据
要重置配置就删单个 config/config/*.yaml,下次启动会重新生成默认值。
