命令行
约 1702 字大约 6 分钟
2026-09-15
CLI 入口是 jiuli.mjs,三种调用方式等价:
node jiuli <命令> # 推荐,带简写
node jiuli.mjs <命令>
pnpm jiuli <命令>命令表
| 命令 | 简写 | 说明 |
|---|---|---|
init | i | 安装依赖 + 编译内核 + 生成配置 |
start | s | pm2 后台启动(自动检查依赖 / 构建 / 配置) |
start --fg | f | 前台启动,Ctrl+C 退出 |
stop | x | 停止 |
restart | r / rs | 重启,完成后通知原会话 |
log [条数] | l | 查看日志;-f 持续输出 |
flush | 清空 pm2 日志文件 | |
status | st | 环境与运行状态 |
list | ls | 列出 plugins/ 下的插件目录 |
create <名称> | c | 生成插件骨架(--dsl 函数式 / --dir 独立目录) |
add <git地址> | a | 安装第三方插件(克隆 + 自动装依赖),--name 目录名 指定目录名 |
session | m | 在 tmux 会话里启动:可脱离、随时接回(服务器推荐) |
attach | at | 接回 tmux / screen 会话,实时日志与终端输入都还在 |
build | b | 编译内核(tsc) |
deps | d | 扫描 plugins/ 下 JS 插件的 import,一键装齐缺失依赖(--dry 只看不装) |
tune | 一键内存优化:堆上限 512MB、浏览器空闲 10 分钟关闭、50 张重启(--show 查看 / --revert 还原) | |
help | h / ? | 命令说明 |
npm scripts 等价物:pnpm start / pnpm stop / pnpm rs / pnpm log / pnpm setup / pnpm build / pnpm app(= node .)。
安装第三方插件
# 目录名默认取仓库地址最后一段(去掉 .git)
node jiuli a https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin
# 指定目录名:--name 写在 URL 后面
node jiuli a https://gitee.com/TimeRainStarSky/Yunzai-ICQQ-Plugin --name ICQQ-PluginURL 必须写在 --name 前面(CLI 取第一个非 - 开头的参数作为地址)。 目标目录已存在时不会覆盖,需要更新就进那个目录执行 git pull。
云崽插件包常把目录名写死在代码里(
./plugins/<目录名>/apps),改名要连代码一起改, 详见安装部署的说明。
安装插件依赖
散在 plugins/example/*.js 这类单文件插件里的 import 没有任何地方声明依赖, 只能一个个手装 —— 用 deps 扫一遍就都装齐了:
node jiuli deps --dry # 只看缺什么,不安装(推荐先跑)
node jiuli deps # 扫描并安装[JiuLi] 扫描松散 JS 插件目录:plugins/example (另有 9 个插件包的依赖由 pnpm i 负责)
[JiuLi] plugins/example 缺失 2 个依赖:
uuid ← plugins/example/fakeFile.js
highlight.js ← plugins/example/图片日志.js
[JiuLi] 安装中:pnpm --dir plugins/example add uuid highlight.js
[JiuLi] 已安装 2 个依赖 → plugins/example
[JiuLi] 发 #重载 或 #重启 后生效规则:
- 只扫「没有
index.js的平铺插件目录」(example/这类)。插件包(miao-plugin、genshin、TRSS-Plugin… 有index.js的)依赖由各自package.json负责,跑pnpm i即可, 扫它们只会报出一堆假缺失 - 只扫插件代码:跳过
resources/、web/、static/、*.min.js这些前端产物与压缩库 - 内置模块(
node:fs)、相对路径、已经能解析的包都不算缺失 - 插件内部自己的文件写法(
models/xx.js且本地存在)不算依赖 - 子路径归到包名:
require('pkg/a/b.js')→ 装pkg;@scope/name保持完整 - 该目录声明过版本就用它(
pkg@^1.2.3),否则装最新 - 装到该插件目录(有
package.json就用pnpm --dir),没有则用pnpm -w装到项目根 —— 直接pnpm add到 workspace 根会被 pnpm 拒绝(ERR_PNPM_ADDING_TO_ROOT) - 装完发
#重载(或#重启)生效
内存优化(tune)
一条命令把三处与内存相关的配置调好,并支持一键还原:
node jiuli tune # 应用推荐值
node jiuli tune --show # 查看当前值 + 是否有备份
node jiuli tune --revert # 还原到优化前
node jiuli tune --heap 640 --idle 15 --restart-num 60 # 自定义| 配置 | 推荐值 | 作用 |
|---|---|---|
config/config/bot.yaml → max_old_space_size | 512 | V8 老生代上限(MB)。Node 默认按物理内存自动定(2GB 机器约 1GB),所以 RSS 能一路涨到 1GB+;调小让 GC 更早介入,不减少真实数据,只减少回收不及时造成的虚高 |
renderers/puppeteer/config.yaml → idleTime | 10 | 空闲多少分钟关掉 Chromium(0 = 不关);下次截图自动拉起 |
renderers/puppeteer/config.yaml → restartNum | 50 | 截图多少张后重启 Chromium(浏览器越跑越胖的解法) |
config/pm2.yaml → max_memory_restart / node_args | 1024M / --max-old-space-size=512 | 仅 pm2 模式:上限必须高于堆上限,否则 RSS 一过线就反复重启 |
改动是逐行替换,原有注释都保留。第一次改动前会把原文件备份成 *.tune-bak, --revert 按备份逐字节还原(连注释和你原来的自定义值一起回来); 万一没有备份(老版本或手动改过),则退回写框架默认值 0 / 30 / 100 / 512M,输出里会说明是"写默认值"。
堆上限什么时候生效
Node 无法在运行中改老生代上限,但守护进程每轮拉起子进程时都会重新读 max_old_space_size, 所以 tune 之后 #重启 即生效(不用重开会话)。 pm2 模式用 node jiuli stop && node jiuli start(pm2 restart 不重读 pm2.yaml)。
内存都花在哪
一台 311 个插件、2013 个群 / 3075 个好友、2GB 内存的机器实测:
| 组成 | 占用 | 说明 |
|---|---|---|
| 框架 + 内置插件(20 个) | ~10MB | 框架本身很轻 |
面板类插件(如 hl-icqq-web) | ~0 | 数据落 sqlite,内存里只留少量 |
| 插件代码(170 个) | ~10MB | 只加载代码几乎不占;占内存的是插件自己的缓存 |
| 运行时数据 | 200MB+ | 按群/好友规模增长:群名单、插件 per-group 缓存 |
| Chromium | 150~600MB | 独立进程,随截图变胖;idleTime / restartNum 控制 |
| V8 未回收的垃圾 | 0~500MB | 上限越大越懒;这一段 RSS 高≠真实占用 |
所以「用户不多但占用高」通常是 群多 + 浏览器 + V8 回收不及时,不是框架或面板。 disable: 只禁用指令、不省内存(插件代码照样加载),要省得把插件目录移走。
启动模式
| 命令 | 进程模型 | 重启行为 |
|---|---|---|
node . | 守护进程 + 主进程,占用当前终端 | 主进程以 255 退出,守护进程在同一终端重新拉起 |
node jiuli s | pm2 托管 | 交给 pm2 |
node . start | 单进程 | 以 255 退出,由外部管理器拉起 |
node . stop | — | 让运行中的实例退出 |
守护进程只在收到退出码 255 时重新拉起(这是 #重启 的约定),正常退出或崩溃都不会无限重启。
环境变量
| 变量 | 作用 |
|---|---|
JIULI_CONFIG_DIR | 指定用户配置目录,默认 config/config/。多实例、测试隔离靠它 |
JIULI_PLUGINS_DIR | 指定插件目录,默认 plugins/ |
JIULI_MOCK_STDIN | 为 1 时,stdin 每行一个 JSON 事件,用 Mock 适配器模拟收发 |
JIULI_CHILD | 内部用:标记当前进程是守护进程拉起的子进程 |
多实例示例:
JIULI_CONFIG_DIR=config/instance-b/ JIULI_PLUGINS_DIR=plugins-b/ node .退出码约定
| 码 | 含义 |
|---|---|
0 | 正常停止(#停止) |
255 | 请求重启,守护进程据此重新拉起 |
