ToolJet CLI 深度解析:用 @tooljet/cli 在命令行完成插件全生命周期管理
本篇技术指南基于 ToolJet 仓库中的 cli/README.md 展开,系统讲解官方 CLI 工具 @tooljet/cli 的安装方式与全部四个命令(info、plugin create、plugin delete、plugin install),并结合 cli/src/commands/ 下的命令实现源码,剖析每条命令背后的目录校验、交互式提问、hygen 模板生成与 plugins.json 注册机制。读完本文,你可以直接在 ToolJet 仓库根目录用命令行创建、删除并构建 marketplace 插件,以及为核心插件安装 npm 依赖。
一、CLI 是什么:基于 oclif 的插件管理工具
@tooljet/cli 是 ToolJet 官方提供的命令行工具,采用 oclif 框架(@oclif/core)构建,用于管理 ToolJet 的插件体系。从 cli/package.json 可以确认以下关键事实:
| 配置项 | 值 | 含义 |
|---|---|---|
name |
@tooljet/cli |
npm 包名 |
version |
0.0.14 |
当前仓库中的包版本 |
bin.tooljet |
./bin/run |
安装后暴露的全局命令为 tooljet |
oclif.commands |
./dist/commands |
命令入口目录(编译产物) |
oclif.topics.plugin |
"manage plugins options: create, delete and install" | plugin 子命令主题 |
engines.node |
>=12.0.0 |
Node.js 版本要求 |
从源码结构看,cli/src/commands/ 目录与 package.json 声明的命令路由一一对应:
- cli/src/commands/info.ts — 环境信息命令
- cli/src/commands/plugin/create.ts — 创建插件
- cli/src/commands/plugin/delete.ts — 删除插件
- cli/src/commands/plugin/install.ts — 为插件安装 npm 模块
值得一提的是,cli/README.md 中的 <!-- usage -->、<!-- commands --> 标记区域是由 oclif 自动生成的:package.json 中定义了 "prepack": "npm run build && oclif manifest && oclif readme" 与 "version": "oclif readme && git add README.md" 脚本,即每次打包或升版时,tooljet --help 输出的 USAGE/FLAGS/EXAMPLES 内容会自动同步回 README。因此该文档的命令行说明与源码中的 static flags / static examples 定义严格一致。
二、安装与基础用法
按 cli/README.md 给出的安装方式:
$ npm install -g @tooljet/cli
$ tooljet COMMAND
running command...
$ tooljet (--version)
@tooljet/cli/0.0.13 darwin-x64 node-v14.17.3
$ tooljet --help [COMMAND]
USAGE
$ tooljet COMMAND
...
--version:输出格式为<包名>/<版本> <平台> node-<node 版本>,例如@tooljet/cli/0.0.13 darwin-x64 node-v14.17.3;--help [COMMAND]:可带子命令查看该命令的完整 USAGE 帮助。
所有命令都必须在 ToolJet 仓库根目录下执行(README 明确说明 "Command should be executed inside Tooljet directory"),因为命令内部通过相对路径操作 marketplace/、plugins/、docs/ 等仓库目录。
三、tooljet info:运行环境自检
USAGE
$ tooljet info
DESCRIPTION
This command returns the information about where tooljet is being run
该命令打印当前 CLI 的运行环境,实现见 cli/src/commands/info.ts。输出内容分三组:
Operating System:
platform: ... # os.platform()
arch: ... # os.arch()
version: ... # os.version()
Binaries:
node: ... # process.versions.node
npm: ... # execSync('npm --version')
Relevant packages:
tooljet: ... # require('./package.json').version
从源码可以看到几个健壮性细节:getBinaryVersion() 与 getPackageVersion() 都用 try/catch 包裹,npm 不存在或 package.json 解析失败时会优雅降级输出 N/A,而不是直接抛错退出。在排查"CLI 报错了但环境本身有问题"的场景时,这条命令是最快的定位手段。
四、tooljet plugin create:模板化生成 marketplace 插件
USAGE
$ tooljet plugin create [PLUGIN_NAME] [--type database|api|cloud-storage] [-b]
ARGUMENTS
PLUGIN_NAME Name of the plugin
FLAGS
-b, --build
--type=<option> <options: database|api|cloud-storage>
DESCRIPTION
Create a new tooljet plugin
EXAMPLES
$ tooljet plugin create <name> --type=<database | api | cloud-storage> [--build]
该命令基于 hygen 模板引擎 + marketplace/_templates/plugin/new/ 下的 EJS 模板,一键生成一个 marketplace 插件骨架。以 cli/src/commands/plugin/create.ts 源码为准,完整执行流程如下:
4.1 参数校验与交互式补全
- 插件名不能是纯数字:
Number(args.plugin_name)为真时直接报错退出(create.ts#L24-L27); - 交互式输入 display name:即使传了位置参数,命令仍会 prompt 一次
Enter plugin display name,作为插件在界面上的展示名,同样禁止纯数字(create.ts#L31-L36); --type缺省时进入选择器:未指定类型时,用 inquirer 弹出database / api / cloud-storage三项单选列表(create.ts#L38-L48)。
4.2 目录前置检查
命令会校验三个路径必须同时存在,否则提示 "make sure that you are running this command in Tooljet directory" 并退出(create.ts#L50-L60):
| 路径 | 用途 |
|---|---|
marketplace/ |
插件输出根目录 |
docs/ |
文档目录 |
marketplace/_templates/ |
hygen 模板目录 |
4.3 插件 ID 唯一性检查
命令会读取 server/src/assets/marketplace/plugins.json(marketplace 插件注册表,当前已登记 Plivo、GitHub 等条目),若已有插件的 id 与新插件小写名相同,则报 "Plugin id already exists" 并退出(create.ts#L75-L82)。这保证了注册表中 id 不冲突。
4.4 hygen 模板渲染
校验通过后,命令构造 hygen 参数 ['plugin', 'new', '--name', <name>, '--type', <type>, '--display_name', <name>, '--plugins_path', 'marketplace'] 并调用 runner(),模板目录指向 marketplace/_templates。模板目录中包含以下 EJS 模板(文件名后缀 .ejs.t 是 hygen 模板约定):
- manifest.ejs.t — 生成
lib/manifest.json - packagejson.ejs.t — 生成插件
package.json index.ejs.t、operations.ejs.t、types.ejs.t、readme.ejs.t、tests.ejs.t、icon.ejs.t、tsconfig.ejs.t、gitignore.ejs.t
从模板内容可以确认生成骨架的关键结构:
- manifest.json:包含
$schema指向plugins/schemas/manifest.schema.json,以及title、type(即--type的值)、source(name、kind、exposedVariables、options)、defaults、properties、required字段,是后续在 ToolJet 界面配置数据源选项的 schema 基础; - package.json:插件包名为
@tooljet-marketplace/<name>,构建脚本为ncc build lib/index.ts -o dist(即使用@vercel/ncc打成单文件 bundle,这是 ToolJet 插件在服务端运行时可独立加载的前提),并默认依赖@tooljet-marketplace/common公共库(对应 marketplace/plugins/common/)。
4.5 注册到 plugins.json 与可选构建
模板渲染完成后,命令把新插件写入注册表(create.ts#L98-L110),条目结构为:
{
"name": "插件名(原样)",
"description": "<type> plugin from <插件名>",
"version": "1.0.0",
"id": "插件名(小写)",
"author": "Tooljet",
"timestamp": "UTC 时间字符串"
}
若带了 --build(或 -b)标志,命令会在 marketplace/ 目录内执行 npm run build --workspaces(create.ts#L116-L120),把整个 workspace(包括 marketplace/plugins/common)统一构建一遍。
4.6 实际示例
# 在 ToolJet 仓库根目录执行,交互式输入 display name
tooljet plugin create mydb --type database --build
执行成功后终端会以 oclif tree 形式展示生成的目录结构:marketplace/ → plugins/ → mydb/。
五、tooljet plugin delete:区分 marketplace 与核心插件的删除
USAGE
$ tooljet plugin delete [PLUGIN_NAME] [-b]
ARGUMENTS
PLUGIN_NAME Name of the plugin
FLAGS
-b, --build
DESCRIPTION
Delete a tooljet plugin
EXAMPLES
$ tooljet plugin delete <name> [--build]
该命令的实现(cli/src/commands/plugin/delete.ts)比 README 展示的更完整:源码中还有一个 -m, --marketplace 标志(delete.ts#L11-L14,源码注释标注了 "TODO: remove this flag, and make it default"),用于指定删除目标属于哪类插件。
5.1 交互式确认市场类型
未带 -m 时,命令会先询问 Is this a marketplace plugin?(默认 no),据此决定插件根路径(delete.ts#L23-L35):
| 类型 | 插件目录 | 附加要求 |
|---|---|---|
marketplace(-m) |
marketplace/plugins/<name> |
无 |
| 核心插件(默认) | plugins/packages/<name> |
同时要求 docs/docs/data-sources/<name>.md 存在 |
任一前置路径不存在即报 "Plugin not found, make sure that you are running this command in Tooljet directory"。核心插件必须附带数据源文档页,是因为 ToolJet 官方数据源的文档(如 docs/docs/data-sources/ 下的条目)与插件包成对维护,删除插件时文档一并清理。
5.2 确认删除与执行
再次弹出 Do you want to proceed with deleting the plugin [...] 确认框(默认 yes),确认后按类型执行不同清理逻辑(delete.ts#L64-L95):
- marketplace 插件:
rimraf删除插件目录;随后读取server/src/assets/marketplace/plugins.json,按name找到条目并splice移除、写回注册表;--build时在marketplace/内执行npm run build --workspaces重新构建。 - 核心插件:
rimraf删除插件目录与docs/docs/data-sources/<name>.md文档;在plugins/目录执行npx lerna link convert清理 lerna workspace 链接;--build时在仓库根目录执行npm run build:plugins。
用户回答 no 则输出 Aborted by user,不产生任何删除。
六、tooljet plugin install:为核心插件安装 npm 依赖
USAGE
$ tooljet plugin install [NPM_MODULE] --plugin <value>
ARGUMENTS
NPM_MODULE Name of the npm module
FLAGS
--plugin=<value> (required)
DESCRIPTION
Installs a new npm module inside a tooljet plugin
EXAMPLES
$ tooljet plugin install <npm_module> --plugin <plugin_name>
实现见 cli/src/commands/plugin/install.ts,逻辑非常精简:
- 定位插件目录为
plugins/packages/<plugin>(注意:该命令只作用于核心插件,不作用于 marketplace 插件); - 目录不存在则报 "Plugin not found" 并退出;
- 在该目录下执行
npm i <npm_module>(install.ts#L29-L31)。
例如为核心插件 mysql 安装一个 SDK:
tooljet plugin install mysql2 --plugin mysql
由于核心插件的依赖最终会通过 workspace 构建被 ncc 打入 bundle,这条命令的意义在于把"进入插件目录手动 npm i"这一重复操作收敛为一条命令。
七、典型开发工作流串联
结合上述命令,一个完整的 marketplace 插件开发闭环如下(均在 ToolJet 仓库根目录执行):
# 1. 环境自检
tooljet info
# 2. 生成插件骨架(自动写入 plugins.json 注册表)
tooljet plugin create weatherapi --type api --build
# → 生成 marketplace/plugins/weatherapi/
# 其中 lib/manifest.json 由 manifest.ejs.t 渲染,
# package.json 使用 ncc 构建脚本
# 3. 编辑插件代码(manifest.json 定义选项 schema,operations 定义查询逻辑)
# 4. 需要第三方 SDK 时(核心插件场景)
tooljet plugin install axios --plugin <core_plugin>
# 5. 不再需要时删除
tooljet plugin delete weatherapi -m --build
配套的背景资料可参考 docs/docs/contributing-guide/marketplace/creating-a-plugin.md 中关于创建 marketplace 插件的贡献指南,以及 plugins/README.md 与 marketplace/README.md 中两个插件体系的说明。
八、适用前提与注意事项
- 必须在仓库根目录运行:三条
plugin子命令都依赖marketplace/、plugins/、docs/等相对路径,脱离仓库根目录会直接报错退出; - Node 版本:
package.json要求node >= 12.0.0;README 示例环境为 node-v14.17.3; - README 与源码的轻微差异:cli/README.md 由 oclif 在 v0.0.13 时生成,
create命令 USAGE 中的[-m]标志在当前源码 create.ts 中并不存在(源码只有--type与--build),而delete命令的-m标志在 README 中未展示、源码中实际存在。使用时以tooljet --help <command>的实时输出为准最可靠; - 构建标志的作用域:
create/delete的--build分别触发marketplace/下的 workspace 构建或npm run build:plugins,构建耗时取决于插件数量,CI 或频繁调试时可省略、手动统一构建。
小结
@tooljet/cli 虽只有四个命令,但覆盖了 ToolJet 插件开发的核心环节:info 提供环境自检,plugin create 通过 hygen 模板 + plugins.json 注册表生成标准化的 marketplace 插件骨架,plugin delete 按 marketplace/核心两类路径安全清理并维护注册表一致性,plugin install 简化核心插件的依赖安装。理解 cli/src/commands/ 的源码实现后,你可以将这套命令嵌入本地开发流或脚本化流程,而不必手改模板文件与注册表。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00