Skip to content

@celljs/cli ​

Not translated yet

This page is only written in Chinese so far; the original is shown below.

Cell 4 的命令行工具 cell:读取 cell.config.ts,按 preset 构建应用,运行和部署构建产物。设计见仓库的 docs/design/architecture.md,从 3.x 迁移见 docs/migration-4.md。

需要 Node.js ^20.19.0 || >=22.12.0。在项目里安装:

bash
npm install -D @celljs/cli
npx cell build

命令 ​

命令作用
cell init [template] [dir]从模板创建项目(backend、fullstack),自带 AGENTS.md、cell 技能和 .mcp.json;不带参数时列出模板
cell dev开发:前后端同一个端口,前端 HMR,后端改文件自动重载,配置变化自动重启
cell build构建到 .cell/output:server/index.mjs(依赖内联的单个文件)、public/(前端)、manifest.json
cell info描述项目:preset 及其来源、mode、目标、组件、DI 模块、hooks、产物状态。只读,不构建、不执行 hooks
cell check一次完成配置、TypeScript 类型、装饰器设置、后端 DI 绑定和模块注册的检查,有错误时退出码 1
cell preview按 manifest.json 运行构建产物,不重新构建
cell deploy用 preset 构建,再交给 preset 的部署器发布;--dry-run 只给出计划
cell config get <key>某个配置项合并后的值,--why 列出设置它的文件和环境变量
cell docs <query>检索已安装组件和模块随包的文档(与项目所用版本一致)
cell mcpstdio MCP 服务器,把 info、check、config_get、docs_search、build、deploy_plan 和模块注册的工具提供给 coding agent
cell agent sync按已安装的版本重写 AGENTS.md 中由 Cell 管理的部分和 .agents/skills/cell/SKILL.md

Preset ​

preset产物运行 / 预览部署器
node(默认)Node.js HTTP 服务:node .cell/output/server/index.mjscell preview 用 Node.js 运行无,产物自行部署
cloudflareCloudflare Workers:server/worker.mjs + wrangler.json,前端作为静态资源cell preview 用 wrangler dev 在本机的 workerd 中运行cloudflare(Wrangler)
cliNode.js 命令行程序:node .cell/output/server/index.mjs 启动应用但不起服务,由组件(如 ApplicationLifecycle)完成工作;不需要 @celljs/express-adapter,也不报缺少入口cell preview 用 Node.js 运行,继承当前终端无
fc阿里云函数计算 3.0 自定义运行时:产物加 bootstrap 启动脚本,前端由服务端从 public/ 返回Node.js 运行fc(函数计算 API)
scf腾讯云 SCF Web 函数:产物加 scf_bootstrapNode.js 运行scf(云函数 API)
lambdaAWS Lambda + 函数 URL:产物加 server/lambda.mjs 处理器Node.js 运行lambda(AWS API)
vercelVercel Build Output API:.vercel/output,前端在 CDN,服务端是一个函数Node.js 运行函数的 bundle,前端取自 static/vercel(Vercel CLI)

每个 preset 的选项写在 cell.config.ts 里与 preset 同名的字段(如 fc: { region: 'cn-shanghai' }),按 preset 的 schema 校验,未知选项报 CELL_CONFIG_INVALID;切换 preset 时其他 preset 的字段保留不报错。

cloudflare ​

bash
cell build --preset cloudflare          # 构建到 .cell/output
cell preview                            # wrangler dev,在 workerd(Cloudflare 的运行时)中运行,默认 8787 端口
cell deploy --preset cloudflare --dry-run   # 构建并执行 wrangler deploy --dry-run,输出计划,不上传
cell deploy --preset cloudflare         # 构建并部署,输出 workers.dev 地址
  • 运行方式:Cell 后端(Express)运行在 Workers 的 node:http 服务端上(需要 nodejs_compat),由 @celljs/express-adapter 的 http 入口提供,与 node preset 相同。前端(public/)由 Cloudflare 作为静态资源直接返回,只有不匹配静态文件的请求进入 Worker。产物只支持 ESM,--format cjs 报 CELL_CONFIG_CONFLICT。
  • 启动时机:Workers 只允许在模块加载阶段生成代码(new Function,Express 的依赖会用到),只允许在处理请求时做 I/O。所以应用的模块在 Worker 启动时加载,Application.start() 和 onStart 在第一个请求时执行(ApplicationHost.beforeStart)。模块加载失败会让 Worker 启动失败,wrangler dev / wrangler deploy 直接报出。
  • Cloudflare 的配置写在项目根目录的 wrangler.jsonc 或 wrangler.json(可选,按 Cloudflare 文档的写法):name、compatibility_date、vars、KV / D1 / R2 等绑定、routes 都在这里。构建时 Cell 以它为基础生成 .cell/output/wrangler.json,并设置:main(项目里写了会被忽略并警告)、有前端时的 assets.directory、compatibility_flags 里的 nodejs_compat。没有配置文件时,name 取自 package.json 的包名(如 @acme/shop → acme-shop),compatibility_date 默认 2026-03-17,这也是允许的最早日期(node:http 服务端与 node:tty 从这天起默认可用)。wrangler.toml 不支持,请改写成 wrangler.jsonc。
  • 单页应用:有前端时默认 assets.not_found_handling: "single-page-application":浏览器导航到不存在的路径时由 Cloudflare 返回 index.html(与 Node.js 上 @celljs/serve-static 的 spa 一致),API 等其他请求仍进入 Worker;在 wrangler.jsonc 里写了 not_found_handling 就以项目为准。Worker 里没有前端文件,@celljs/serve-static 发现没有前端目录时直接放行,未知路径得到正常的 404。
  • 自定义域名:域名的 DNS 托管在 Cloudflare 时,在 wrangler.jsonc 里写 "routes": [{ "pattern": "app.example.com", "custom_domain": true }],部署时 Cloudflare 自动创建 DNS 记录和证书,deployment.url 报告这个域名。国内网络无法访问 *.workers.dev,面向国内访问时要绑定自定义域名。
  • 应用配置:vars 和 secrets(wrangler secret put)进入 process.env,所以 cell.yml 里的 ${env.X} 和 CELL_BACKEND__* 变量照常生效。
  • Wrangler:不是 Cell 的依赖。项目装了 wrangler 就用项目的,否则由 npx --yes wrangler@4 在第一次使用时安装(npm 缓存)。凭据由 Wrangler 管理:设置 CLOUDFLARE_API_TOKEN(必要时加 CLOUDFLARE_ACCOUNT_ID),或执行一次 npx wrangler login。也可以不用 cell deploy,直接执行 npx wrangler deploy --config .cell/output/wrangler.json。
  • Workers Builds(Cloudflare 的 CI,设置 WORKERS_CI=1)里自动选用 cloudflare preset:构建命令 npx cell build,部署命令 npx cell deploy --skip-build。
  • wrangler dev 会在项目根目录创建 .wrangler/,请加入 .gitignore。

fc、scf、lambda ​

三个函数平台的做法相同:Cell 服务端原样运行,产物目录整体打成 zip 作为函数代码;部署器直接调用云 API(请求签名自己实现:阿里云 ACS3、腾讯云 TC3、AWS SigV4),不需要云 SDK 或云厂商 CLI。函数名默认取包名(@acme/shop → shop),可用 name 指定;env 写入函数的环境变量(另带 NODE_ENV=production),cell.yml 里的 ${env.X} 和 CELL_BACKEND__* 照常生效。

preset运行方式选项凭据(环境变量)部署结果
fccustom.debian10 运行时,bootstrap 用镜像自带的 Node.js 20(node: '18' 可选)在 9000 端口(FC_SERVER_PORT)启动服务name、region(默认 ALIBABA_CLOUD_REGION_ID 或 cn-hangzhou)、node、memory(MB,默认 1024)、cpu(默认按内存 2 GB/核)、timeout(默认 60)、concurrency(单实例并发,默认 20)、envALIBABA_CLOUD_ACCESS_KEY_ID、ALIBABA_CLOUD_ACCESS_KEY_SECRET(可选 ALIBABA_CLOUD_SECURITY_TOKEN)创建或更新函数,没有时创建匿名 HTTP 触发器 http(已有的触发器不改),返回 fcapp.run 地址
scfWeb 函数(Type: HTTP),scf_bootstrap 用运行时的 Node.js 18(runtime: 'Nodejs16.13' 可选)在 9000 端口启动服务;只有 /tmp 可写name、region(默认 TENCENTCLOUD_REGION 或 ap-guangzhou)、namespace(默认 default)、runtime、memory(默认 512)、timeout(默认 60)、envTENCENTCLOUD_SECRET_ID、TENCENTCLOUD_SECRET_KEY(可选 TENCENTCLOUD_SESSION_TOKEN)创建或更新函数(先代码后配置,等待状态变为 Active),没有时创建公网、免鉴权的函数 URL
lambdanodejs22.x,初始化阶段在回环端口启动服务,server/lambda.handler 把函数 URL / HTTP API(payload 2.0)事件转成请求,响应以 base64 返回,多个 Cookie 放在 cookiesname、region(默认 AWS_REGION 或 us-east-1)、runtime、architecture(x86_64 / arm64)、memory(默认 1024)、timeout(默认 30)、role、envAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY(可选 AWS_SESSION_TOKEN),或 ~/.aws/credentials 的 AWS_PROFILE / default没有 role 时创建或复用执行角色 cell-lambda-execution(只有日志权限);创建或更新函数;没有时创建 AuthType: NONE 的函数 URL 及其两项公开调用权限
  • 代码包直接随请求上传,上限:FC 100 MB、SCF 20 MB、Lambda 50 MB(zip 后),超出报 CELL_DEPLOY_PACKAGE_TOO_LARGE。体积大多来自 build.external 留在 server/node_modules 的包。
  • 没有凭据时 --dry-run 照样给出计划(动作为 deploy,并警告不知道函数是否已存在);有凭据时查询云端,动作是 create 或 update。实际部署缺凭据报 CELL_DEPLOY_CREDENTIALS_MISSING,云 API 的错误报 CELL_DEPLOY_API_ERROR / CELL_DEPLOY_UNAUTHORIZED,消息里带云厂商的错误码和请求 ID。
  • 函数计算的默认域名 fcapp.run 会让浏览器下载 HTML 页面,网页应用请绑定自定义域名;API 不受影响。
  • 镜像里的 Node.js 较旧(FC 20.10、SCF 18.15):服务端 bundle 自带 import.meta.dirname / filename 的补丁。

vercel ​

bash
cell build --preset vercel     # .vercel/output(Build Output API v3)
cell preview                   # Node.js 运行函数的 bundle,前端取自 .vercel/output/static
cell deploy --preset vercel    # vercel deploy --prebuilt,不在 Vercel 上再构建
  • 前端放在 static/ 由 Vercel CDN 返回(/assets/* 带一年的 immutable 缓存头);其他请求进入函数 functions/index.func(nodejs22.x),函数里的 Cell 服务端处理 API,并用其中的 public/index.html 做单页回退。vercel build 留在 .vercel/output 的文件在构建时清除。
  • 选项:project(默认取包名)、scope(团队)、environment(production 默认 / preview)、runtime、memory、maxDuration、regions(逗号分隔)。输出目录固定 .vercel/output(--out-dir 到别处时部署报 CELL_DEPLOY_OUTPUT_MISMATCH);不接受 --single / --compile。
  • Vercel CLI 不是依赖:项目装了就用项目的,否则 npx --yes vercel@65。凭据:VERCEL_TOKEN,或执行一次 npx vercel login;项目不存在时自动创建(--create-if-missing)。
  • Vercel 的构建环境(VERCEL=1)里自动选用 vercel preset。.vercel/ 请加入 .gitignore。

cell dev ​

选项说明
--preset <name>用哪个 preset 的入口运行后端,默认 node
-m, --mode <mode>额外的 mode,可重复;development 总是自动加上
-p, --port <port>端口,默认取 cell.server.port(考虑 CELL_BACKEND__SERVER__PORT),否则 3000;0 表示随机端口
--host <host>监听地址,默认取 cell.server.host,否则 127.0.0.1
  • 一个端口:Vite 提供前端页面和模块(HMR),/favicon.ico 与组件的 assets 和构建产物里的位置一致,其余请求交给后端。
  • 后端不打包,在 Vite 的 server 环境里由 module runner 加载:Cell 包和项目源码经过 Cell 插件(生成的 DI 容器与配置写到 .cell/generated/),其他依赖由 Node 直接加载。入口通过 ApplicationHost(@celljs/core)把请求处理函数交给 cell dev,不自己监听端口。
  • 后端用到的文件变化时:先停止旧应用(触发 onStop),再从新的模块重新启动,并触发 dev:reload。启动失败时请求返回 500 和 CellError(CELL_DEV_BACKEND_FAILED),保存后自动重试。
  • 项目或组件的 cell*.yml、package.json、cell.config.* 变化时:重新解析工具链并在同一端口重启,再次触发 dev:ready。配置写错时报错并等待下一次修改。
  • 源码里的 require() 和项目内的 CommonJS 文件(如生成的 prisma client)照常可用。
  • POST /_cell/mcp:运行中应用的 MCP 端点(Streamable HTTP,JSON 响应),工具有 get_errors(后端启动错误、失败的重载、5xx 请求、错误日志)、list_routes、list_bindings、get_config(${...} 已求值)、recent_requests、logs。只接受本机来源(无 Origin 或回环地址),其他来源返回 403。
  • --json:就绪时 stdout 输出一行结果(url、port、mode、targets、mcp,后端启动失败时带 backendError),之后应用和 Vite 的输出都走 stderr。收到 SIGINT / SIGTERM 时停止应用并以 0 退出。
  • React Fast Refresh 等框架插件由组件的 toolchain 模块提供(模块宿主完成后);在那之前前端修改会整页刷新,CSS 热替换。

cell build ​

选项说明
--preset <name>产物形态:node(默认)、cloudflare、cli、fc、scf、lambda、vercel
-m, --mode <mode>额外的 mode,读取 cell.<mode>.yml,可重复;production 总是自动加上
--format <esm|cjs>服务端产物格式,默认取 preset(node 为 esm)
--single前端内嵌进 server/index.mjs:一个文件运行整个应用(见下)
--compile [bun|node|deno]编译成一个可执行文件 .cell/output/app,默认 bun(见下)
--compile-target <target>交叉编译,交给 bun(如 bun-linux-x64)或 deno(如 x86_64-unknown-linux-gnu)
--sourcemap生成 source map
--out-dir <dir>输出目录,默认 .cell/output

单文件(--single):先构建前端,再把 public/ 的全部文件(页面、脚本、favicon、组件的 assets)以 base64 写进服务端 bundle,启动时注册到 Symbol.for('@celljs/serve-static/assets'),@celljs/serve-static 从内存返回它们(Content-Type、ETag / 304、Cache-Control 与从磁盘时一致,单页应用回退到 index.html)。产物只有 server/index.mjs 和 manifest.json(single: true,没有 public),把这一个文件拷到任意目录用 node index.mjs 即可运行。项目只有后端时与不加 --single 相同(会提示);build.external 等留在 server/node_modules 的包仍要放在它旁边。也可在 cell.config.ts 写 build: { single: true }。

可执行文件(--compile):在单文件 bundle 的基础上编译成 .cell/output/app(Windows 目标为 app.exe),拷到没有 Node.js 和 node_modules 的机器也能运行;manifest.json 记录 executable,cell preview 直接运行它。

工具做法需要
bun(默认)bun build --compile本机装有 Bun
denodeno compile --allow-all本机装有 Deno
nodeNode.js 单可执行应用(SEA):CommonJS bundle 生成 blob,用 npx postject 注入 node 的副本,macOS 上重新签名产物格式自动为 cjs;第一次使用时 npx 下载 postject;不能交叉编译

留在 bundle 外的包(build.external、组件的 optionalDependencies,通常是原生模块)无法放进可执行文件,报 CELL_COMPILE_EXTERNALS 并列出它们。cloudflare、vercel preset 的前端由平台托管,不接受 --single / --compile。

cell info ​

接受与 cell build 相同的 --preset、-m、--format、--out-dir 等选项,描述同样参数下 cell build 会用到的内容;另有:

选项说明
--app-config同时输出每个目标合并后的应用配置(cell.yml);后端的 ${...} 保持原样,运行时才求值

只读取 package.json、cell*.yml、cell.config.ts 和 manifest.json,不运行项目代码,不执行 hooks。--json 结果的主要字段:

字段内容
name、version、cellVersion、configFile项目、CLI 版本、cell.config 文件(相对根目录)
presetname、source(flag / config / env / ci / default)、entry、format、runtime(workerd 表示 Cloudflare Workers)、deployer、available
mode、targets、targetSources生效的 mode;构建目标及其来由(cell.config.ts、default 或声明它的组件)
build生效的 format、outDir、external、sourcemap、single、compile
components按加载顺序;dir、configFiles 相对根目录,targets / entries / toolchain / optionalDependencies 只在组件声明时出现
frontend、backendentry、提供入口的 entryComponent、staticModules / modules(DI 模块,按加载顺序)、config(仅 --app-config)
hooks、modules、moduleOptionscell.config.ts 里的 hooks、modules 条目、toolchain 模块选项的键
outputdir、built,构建过时还有 manifest 里的 preset、format、targets、server、public
frontend.envOverrides、backend.envOverrides当前环境里的 CELL_FRONTEND__* / CELL_BACKEND__* 变量:applied(变量 → 配置路径)和 problems(找不到键或类型不符)。没有这类变量时不出现,不输出值

warnings 会提前指出不起作用或会让 cell build 失败的设置:没有组件提供 preset 的入口、-m 指定的 mode 没有任何 cell.<mode>.yml(附拼写建议)、使用了尚未提供的 single / compile、不生效的 CELL_FRONTEND__* / CELL_BACKEND__* 变量。

cell check ​

选项说明
--preset <name>、-m, --mode <mode>同 cell build
--skip <check>跳过某项检查,可重复;未知名字报 CELL_ARGUMENT_INVALID 并列出可用的检查
检查内容
config与 cell info 的 warnings 相同:缺少入口(CELL_ENTRY_MISSING)、无效 mode(CELL_MODE_UNKNOWN)、不生效或被遮住的环境变量(CELL_ENV_OVERRIDE_IGNORED / CELL_ENV_OVERRIDE_SHADOWS)、不可用的 single / compile
types用项目的 TypeScript 执行 tsc --noEmit -p tsconfig.json,每条诊断带文件、行列和 TSxxxx 码;没有 tsconfig.json 或 typescript 时跳过
decoratorstsconfig 要开启 experimentalDecorators 和 emitDecoratorMetadata(CELL_TSCONFIG_DECORATORS),与构建时的装饰器语义一致
di用 cell dev 的方式加载后端 DI 容器(不启动应用),逐个解析绑定:CELL_DI_NO_BINDING、CELL_DI_AMBIGUOUS、CELL_DI_CIRCULAR、CELL_DI_MISSING_INJECT 为错误,带修复建议;只能在注入点或请求中创建的绑定记为 info
模块注册的检查toolchain 模块用 addCheck 注册,名字如 typeorm:entities

--json 结果:passed、summary(errors / warnings / info 计数)、issues(每条带 check、level、code、message,可能有 file、line、column、hint)、checks(每项的耗时、问题数、跳过原因)。不加 --json 时按编译器格式输出 src/a.ts:3:5 error TS2322 …,info 不显示。

cell init ​

bash
npx @celljs/cli init backend my-app     # 或 fullstack:React 前端 + 后端,经 RPC 调用
cd my-app && npm install && npx cell agent sync && npx cell dev
选项说明
--name <name>包名,默认取目录名(转成小写、去掉空格)
--force允许写入非空目录(同名文件被覆盖)

从不提问;目录非空时报 CELL_INIT_NOT_EMPTY,模板不存在时报 CELL_TEMPLATE_NOT_FOUND(带拼写建议)。项目依赖 @celljs/* 的 ^<cli 版本>,测试用 bun test + createTestApp()。为 coding agent 生成:AGENTS.md(Cell 指南,与版本匹配)、.agents/skills/cell/SKILL.md、CLAUDE.md(@AGENTS.md)、.mcp.json(npx cell mcp)。

cell agent sync ​

把 @celljs/cli 自带的 Cell 指南和已安装组件、toolchain 模块(addDocs 目录)的 AGENTS.md 片段写进 AGENTS.md 的 <!-- cell:begin --> … <!-- cell:end --> 之间(其余内容保持不变;没有标记时追加到末尾),并重写 .agents/skills/cell/SKILL.md。升级 Cell 或增减组件后执行一次。

cell config get ​

bash
cell config get cell.server.port --why
# cell.server.port (backend) = 8080
#   node_modules/@celljs/web/cell.yml: 3000
#   cell.yml [backend]: 8080
选项说明
-m, --mode <mode>额外的 mode,可重复
--target <target>backend 或 frontend,默认有后端时取 backend
--why列出设置该值的文件(按合并顺序:所有文件的公共部分在前,目标段 backend: / frontend: 在后,后者覆盖前者)和环境变量

键可写 a.b.c、items[0].name。--json 结果有 value、layers(file、component、section、value)、env(设置了该键或其上下级的 CELL_<TARGET>__* 变量)、expressions(后端值含 ${...},服务端启动时求值)。键不存在时报 CELL_CONFIG_KEY_NOT_FOUND,带最接近的键。

cell docs ​

cell docs <query> [--limit n]:在各组件的 README.md、docs/ 和模块 addDocs() 的目录里按标题分段检索,返回最匹配的段落和 file:line。

cell mcp ​

在 stdio 上运行 MCP 服务器(每行一条 JSON-RPC 消息),直到 stdin 关闭。工具都对应同名命令的 --json 结果:

工具对应命令副作用
infocell info只读
checkcell check只读
config_getcell config get只读
docs_searchcell docs只读
buildcell build写输出目录
deploy_plancell deploy --dry-run写输出目录,部署器可能联网;不部署

不提供部署工具:实际部署由人执行 cell deploy。

之后是 toolchain 模块用 addMcpTool 注册的工具(与内置工具重名的会被忽略并警告);cell.config.*、package.json 或模块文件变化后自动重新加载。每个工具标注 readOnlyHint / openWorldHint;参数按 inputSchema 校验,失败时返回 isError 和 CellError。stdout 只有协议消息,模块和 hooks 的输出都走 stderr。

在 agent 中配置(项目根目录下运行):

jsonc
// Claude Code / Cursor / CodeBuddy:.mcp.json 或 .cursor/mcp.json
{ "mcpServers": { "cell": { "command": "npx", "args": ["cell", "mcp"] } } }
toml
# Codex:~/.codex/config.toml
[mcp_servers.cell]
command = "npx"
args = ["cell", "mcp"]

cell dev 运行时,另外把 http://127.0.0.1:<port>/_cell/mcp 配成 HTTP 类型的 MCP 服务器即可查看运行中的应用。

cell preview ​

选项说明
-p, --port <port>监听端口,默认取 cell.server.port(3000);等同于设置 CELL_BACKEND__SERVER__PORT。cloudflare 产物传给 wrangler dev --port,默认 8787
--out-dir <dir>产物目录,默认 .cell/output

产物由哪个 preset 构建,就按它的方式运行:node、cli、fc、scf、lambda 用 Node.js 运行 server/index.mjs(标准输入输出继承当前终端),cloudflare 用 wrangler dev,vercel 用 Node.js 运行函数里的 bundle 并从 static/ 返回前端。

cell deploy ​

选项说明
--preset <name>部署到哪个平台:cloudflare、fc、scf、lambda、vercel;node、cli 没有部署器,报 CELL_DEPLOY_UNSUPPORTED(构建之前就检查)
--dry-run构建并计划,不上传(cloudflare:wrangler deploy --dry-run;fc / scf / lambda:打包并查询函数是否存在;vercel:列出函数和静态文件)
--skip-build部署 --out-dir 里已有的产物;产物的 preset 与 --preset 不一致时报 CELL_DEPLOY_OUTPUT_MISMATCH
-m, --mode <mode>、--sourcemap、--out-dir <dir>同 cell build

先 plan 后 apply:计划列出要部署的资源(resources,每项有 type、name、action、details),--dry-run 到此为止。依次触发 deploy:before(闸门,hooks 里的命令失败会取消部署)、deploy:plan、deploy:done 事件。部署工具(Wrangler、Vercel CLI)的输出在 --json 时走 stderr。--json 结果:preset、deployer、dryRun、plan,实际部署时还有 deployment(url,以及 details.versionId)。

通用选项 ​

选项说明
--root <dir>项目根目录,默认当前目录
--jsonstdout 只输出一行 JSON 结果,其余输出(进度、hook 命令的输出)走 stderr
-h, --help / -v, --version帮助 / 版本

命令从不交互提问。退出码:成功 0,执行失败 1,用法错误(未知命令、选项、取值)2。

--json 输出 ​

json
{"schemaVersion":1,"command":"build","ok":true,"root":"/app","preset":"node","format":"esm","mode":["production"],"targets":["backend"],"outDir":".cell/output","entry":"@celljs/express-adapter/lib/node/http-application-entry","durationMs":480,"warnings":[]}

失败时:

json
{"schemaVersion":1,"command":"build","ok":false,"error":{"code":"CELL_PRESET_NOT_FOUND","message":"Unknown preset 'nod'. Available: node.","hint":"Did you mean 'node'?","docs":"https://cell.cellbang.com/errors/cell-preset-not-found"},"warnings":[]}

schemaVersion 不变时,字段只增不改。

cell.config.ts ​

可选。只放工具链配置;应用配置(@Value 读取的内容)写在 cell.yml。

ts
import { defineConfig } from '@celljs/cli';

export default defineConfig({
    preset: 'node',
    targets: ['frontend', 'backend'],
    modules: ['!@celljs/serve-static', { source: '@x/ui', runtime: false }],
    build: { format: 'esm', external: ['sharp'], sourcemap: false, outDir: '.cell/output' },
    vite: {},
    hooks: { 'build:before': 'prisma generate' },
});
字段说明
preset产物形态。优先级:--preset → 本字段 → CELL_PRESET → CI 环境识别 → node
targets构建目标。默认 backend 加上组件声明的目标(装了 @celljs/react 会经 @celljs/widget 加上 frontend);写了就完全以它为准
modules'!包名' 去掉一个组件(连同只经由它引入的依赖);{ source, runtime: false } 去掉它的 DI 模块、保留配置;{ source, toolchain: false } 去掉它的 toolchain 模块;本地路径或包名表示额外加载的 toolchain 模块(见下文)
build.formatesm 或 cjs
build.external不内联的包,按依赖关系拷到 server/node_modules
build.sourcemap、build.outDir同命令行选项
build.single、build.compile单文件全栈 / 可执行文件,尚未提供(CELL_UNSUPPORTED)
vite合并进每个环境的 Vite 配置
hooks在工具链事件上执行的 shell 命令,见下文

其他顶层字段是 toolchain 模块的选项,值必须是对象。文件支持 .ts/.mts/.cts/.js/.mjs/.cjs,同时存在多个报 CELL_CONFIG_AMBIGUOUS。命令行参数优先于配置文件。

用环境变量覆盖配置 ​

cell.yml 里已有的任意键都可以用环境变量覆盖,不用改文件:

bash
CELL_BACKEND__SERVER__PORT=8080 cell preview      # 服务端启动时应用:cell.server.port
CELL_FRONTEND__API_BASE=/api cell build           # 构建前端时应用:cell.apiBase

__ 分隔层级,第一段先在顶层找、再到 cell 下找(CELL__… 总是指 cell),一个键只能由一个变量设置,不区分大小写并忽略 _、-;值按原值类型转换(JSON 写法的有限数、true / false、对象和数组用 JSON,原值为 null 时是 JSON 就按 JSON),整体是一个 ${...} 的值保留到求值时再定类型(如 CELL_BACKEND__SERVER__PORT='${env.PORT}'),在 ${...} 求值之前生效。只能覆盖已存在的键,找不到键或类型不符的变量不生效并给出警告:服务端在启动时打印,cell build 检查 CELL_FRONTEND__*,cell info 两种都检查。完整规则见迁移指南 5.3。

hooks ​

ts
hooks: {
    'build:before': 'prisma generate',
    'output:done': 'cp extra/* "$CELL_OUT_DIR/server/"',
}
  • 命令经 shell 在项目根目录执行,不读标准输入。PATH 最前面是项目及上级目录的 node_modules/.bin。
  • 环境变量:CELL_EVENT、CELL_PRESET、CELL_MODE(逗号分隔)、CELL_OUT_DIR(输出目录的绝对路径)。
  • cell dev 触发 modules:done → config:resolved → dev:ready,后端重载时 dev:reload,重启时 close 后重新来一遍。
  • cell build 依次触发:modules:done → config:resolved → build:before → 构建 → output:done → close。
  • build:before 是闸门事件:命令非零退出即取消构建(CELL_BUILD_CANCELLED)。其他事件里非零退出报 CELL_HOOK_FAILED。
  • 只接受命令字符串。需要写代码或修改事件对象(build:done、deploy:plan)的扩展写成 toolchain 模块。

toolchain 模块 ​

需要写代码的扩展写成 toolchain 模块:默认导出一个工厂函数,拿到 cell 后注册能力(设计见 docs/design/modules.md)。

ts
// modules/audit.ts,在 cell.config.ts 里写 modules: ['./modules/audit'], audit: { url: '...' }
import type { CellAPI } from '@celljs/kit';

export default function (cell: CellAPI) {
    const { url } = cell.options('audit', { url: 'http://localhost:9000' });
    cell.addVitePlugin({ name: 'audit', transform: code => code }, { env: 'server' });
    cell.on('build:done', event => {
        event.files['audit.json'] = JSON.stringify({ url, targets: event.project.targets });
    });
}
  • 加载顺序固定:依赖里的组件(package.json 的 cell.toolchain,按依赖顺序)→ cell.config.ts 的 modules(本地路径或包名,按书写顺序)。{ source, toolchain: false } 关掉某个组件的模块,'!包名' 连组件一起去掉。
  • 工厂只做注册,不启动进程、端口、监听或定时器:cell info 也会执行工厂。
  • cell.config.ts 里除 preset、targets、modules、build、vite、hooks 之外的顶层字段都是模块选项,必须由某个模块用 options() 或 addConfigSchema() 认领,否则报 CELL_CONFIG_UNKNOWN_OPTION(附拼写建议);addConfigSchema 接受 Standard Schema(zod、valibot 等),不合法报 CELL_CONFIG_INVALID。
  • 工厂抛错报 CELL_MODULE_ERROR(带模块名);默认导出不是函数报 CELL_MODULE_INVALID。cell dev 中通知类事件的处理函数出错只报告不中断,cell build 中任何处理函数出错都失败。
  • 每个模块注册了什么都列在 cell info --json 的 toolchainModules 里。
API作用
command、mode、dev、ci、root、preset、components、logger只读信息;preset 是命令行 / 配置 / 环境变量里写的名字,解析后的 preset 在 config:resolved 事件里
options(key, defaults)、addConfigSchema(key, schema)读并认领 cell.config.ts 的 key 字段(只能在工厂里同步调用)
updateConfig({ build: { external }, vite })追加 external、在项目的 vite 之下合并 Vite 配置(项目优先);应用配置仍写在组件自己的 cell.yml
addVitePlugin(plugin, { env })加 Vite 插件,env 为 client / server 时只作用于前端 / 后端
addRuntimeModule(path, { target, static })、addVirtualModule(id, code)、setEntry(target, path)追加 DI 模块、提供虚拟模块、指定入口
addDevHandler(path, handler)、addWatchFile(path)cell dev 中在后端之前处理某个路径;文件变化时重启
addPreset(preset)、addOutputFile(path, content)注册 preset(参与 --preset 和 CI 识别);构建后写入输出目录的文件(build:done 的 event.files 可增删改)
addCheck(def)、addMcpTool(def)、addDocs(dir)供 cell check、cell mcp、cell docs 使用
on(event, handler)、events订阅工具链事件(组合规则见 modules.md §3.2);模块之间的事件总线
exec(cmd, args, opts)、resolve(spec)不经 shell 执行命令;从项目根目录解析模块

错误码 ​

所有失败都以 CellError { code, message, file?, line?, hint, docs } 输出,code 在 4.x 内保持稳定。

错误码含义
CELL_ARGUMENT_INVALID未知命令、选项或取值(退出码 2)
CELL_PROJECT_NOT_FOUND、CELL_PACKAGE_JSON_INVALID根目录没有 package.json、某个 package.json 无法读取
CELL_CONFIG_LOAD、CELL_CONFIG_AMBIGUOUScell.config.ts 加载或执行失败、存在多个配置文件
CELL_CONFIG_INVALID、CELL_CONFIG_CONFLICT配置字段错误(带字段路径和拼写建议)、选项互相冲突
CELL_CONFIG_PARSE、CELL_CONFIG_NOT_OBJECT、CELL_CONFIG_EXPRESSIONcell.yml 不是合法 YAML、顶层不是映射、${...} 表达式错误
CELL_COMPONENT_NOT_FOUND、CELL_LOCAL_MODULE_NOT_FOUNDmodules 里写的组件或本地模块不存在
CELL_MODULE_NOT_FOUND、CELL_CORE_NOT_FOUND组件声明的 DI 模块无法解析、项目没有依赖 @celljs/core
CELL_CORE_EL_MISSING项目的 @celljs/core 早于 4.1.0,没有构建期求值配置所需的 @celljs/core/el 入口
CELL_PRESET_NOT_FOUND、CELL_PRESET_DUPLICATE、CELL_PRESET_CYCLEpreset 不存在、重名、extends 成环
CELL_BUILD_FAILED打包失败,带出错文件和位置
CELL_BUILD_CANCELLED、CELL_HOOK_FAILEDhook 或模块取消了构建、hook 命令失败
CELL_MODULE_ERROR、CELL_MODULE_INVALIDtoolchain 模块的工厂或处理函数出错、默认导出不是工厂函数
CELL_CHECK_FAILED、CELL_DI_MODULE_FAILED某项检查自身出错、cell check 加载后端 DI 模块失败
CELL_CONFIG_UNKNOWN_OPTIONcell.config.ts 有没有模块认领的选项字段
CELL_DEV_FAILED、CELL_DEV_BACKEND_FAILED、CELL_DEV_START_TIMEOUTcell dev 无法启动(端口等)、后端启动失败、后端入口超时未就绪
CELL_DEV_BACKEND_NOT_RUNNINGdev MCP 工具需要运行中的后端,但后端启动失败或尚未启动
CELL_CONFIG_KEY_NOT_FOUNDcell config get / get_config 的键不存在
CELL_COMPILE_TOOL_MISSING、CELL_COMPILE_FAILED--compile 需要的 bun / deno / node 不存在、编译器报错(带输出的最后几行)
CELL_COMPILE_EXTERNALS、CELL_COMPILE_NO_SERVER有留在 bundle 外的包、项目没有后端可编译
CELL_TEMPLATE_NOT_FOUND、CELL_INIT_NOT_EMPTYcell init 的模板不存在、目标目录非空
CELL_UNSUPPORTED使用了尚未提供的功能
CELL_OUTPUT_NOT_FOUND、CELL_PREVIEW_NO_SERVERcell preview / cell deploy --skip-build 前没有构建、产物里没有服务端
CELL_DEPLOY_UNSUPPORTED、CELL_DEPLOY_OUTPUT_MISMATCH、CELL_DEPLOYER_NOT_FOUNDpreset 没有部署器、已有产物不是该 preset 构建的、部署器不存在
CELL_DEPLOY_PLAN_FAILED、CELL_DEPLOY_FAILED、CELL_DEPLOY_CANCELLED计划失败(如打包失败)、部署失败(凭据问题时 hint 说明如何设置)、hook 或模块取消了部署
CELL_DEPLOY_CREDENTIALS_MISSING、CELL_DEPLOY_UNAUTHORIZED没有云平台凭据、凭据无效或权限不足
CELL_DEPLOY_API_ERROR、CELL_DEPLOY_NETWORK、CELL_DEPLOY_TIMEOUT、CELL_DEPLOY_PACKAGE_TOO_LARGE云 API 报错(带云厂商错误码和请求 ID)、连不上云 API、等待函数就绪超时、代码包超过直传上限
CELL_CLOUDFLARE_CONFIG_INVALID、CELL_CLOUDFLARE_CONFIG_UNSUPPORTED、CELL_CLOUDFLARE_COMPATIBILITY_DATEwrangler.json(c) 不是合法 JSON 或字段类型错误、使用了 wrangler.toml、compatibility_date 早于 2026-03-17
CELL_INTERNAL_ERRORCell 或模块的缺陷;设置 CELL_DEBUG=1 可看到调用栈

测试:createTestApp ​

ts
import { createTestApp } from '@celljs/cli';

const app = await createTestApp({ root, mode: ['extra'] });   // mode 前面总有 test
const res = await app.fetch('/users', { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' });
const { UserService } = await app.import('/src/user-service.ts');   // 后端加载的那份模块
app.get(UserService);                                         // DI 容器里的对象
await app.close();                                            // 停止应用(onStop)

app.get(X) 中测试从项目源码导入的类或 Symbol('X') 与应用里的不是同一个模块实例(应用在 Vite 的 module runner 里),按类名或 symbol 描述匹配;没有或重名时报 CELL_DI_NO_BINDING / CELL_DI_AMBIGUOUS。要拿到应用用的那一份(重名时、或要用模块里的其他导出),用 app.import():项目内的文件写 /src/...,包写包名。

在测试进程里像 cell dev 一样从源码加载后端,不构建、不监听端口;请求经内存中的 req/res 对象走完整的 HTTP 处理链,fetch 返回标准 Response。启动失败抛 CellError(如 CELL_DEV_BACKEND_FAILED,带出错文件);后端没有 HTTP 入口时 fetch 报 CELL_TEST_APP_NO_HTTP。

编程接口 ​

ts
import { CellCli } from '@celljs/cli';

const code = await new CellCli({ stdout, stderr, env }).run(['build', '--json']);

也导出 defineConfig、ConfigResolver、PresetRegistry、DeployerRegistry、EventBus、HookRunner 等,用于测试和编写工具。