关于 caffeine-app
caffeine-app 是一项指导 Agent 从零搭建 Caffeine 全栈应用的技能,涵盖项目结构、清单文件和 CLI 构建流程。它面向使用 Motoko 后端与 React/Vite 前端的 Caffeine 项目;Motoko 语言细节则交由 `writing-motoko` 技能处理。
主要功能
- 01搭建 Caffeine 项目目录结构
- 02配置工作区与 canister 清单
- 03说明 Motoko 后端和前端绑定流程
- 04指导 caffeine CLI 构建循环
- 05涵盖稳定状态检查与迁移配置
适用场景
- 01从零创建 Caffeine 应用
- 02修复 `caffeine.toml` 或 `mops.toml`
- 03运行 `caffeine` 检查与预览构建
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dfinity/ icskills --skill caffeine-app安装后在 Claude Code 中输入/caffeine-app确认已加载。
附带文件references/frontend-template.md
详细介绍
Caffeine App(从零开始构建)
Caffeine(caffeine.ai)在 Internet Computer 上构建并托管全栈应用:一个单独的 Motoko 后端 canister,以及一个作为 assets canister 提供服务的 React/Vite 前端。本技能将介绍准确的项目结构和 CLI 构建流程,帮助你从零构建一个可编译的 Caffeine App。
本技能负责项目结构和构建工作流,不负责 Motoko 语言。 编写或修改任何后端代码前,请获取并遵循 writing-motoko 技能:https://skills.internetcomputer.org/skills/writing-motoko/SKILL.md。该技能是有关 actor 语法、稳定状态、mo:core 和编译器错误的权威指南。本技能只展示最精简的空 actor 以及它所在的位置。
何时使用本技能
- 从零创建或搭建新的 Caffeine App 或 caffeine.ai 项目。
- 编写或修复
caffeine.toml(工作区或 canister 清单)或mops.toml。 - 运行 caffeine CLI 流程:
auth login、doctor、install、check、preview。
不要将本技能用于 Motoko 语言细节(请使用 writing-motoko),也不要用于原始 IC/dfx/icp 部署(请使用 icp-cli)。Caffeine App 通过 caffeine CLI 和 caffeine.ai Web 应用构建和部署,而不是通过 dfx。
重要事实
- 没有
caffeine init/caffeine new。“从零开始”是指你手动创建本技能中所述的文件,然后运行构建流程。(caffeine projects clone <id>只会下载现有云项目,并不是从零搭建项目的脚手架。) - 后端 canister 必须且只能有一个,并且必须命名为
backend。mops.toml声明一个[canisters.backend]。 - 前端与后端的绑定文件由工具生成,绝不能手写。
src/frontend/src/backend.ts和src/frontend/src/declarations/由caffeine-bindgen在构建过程中生成。编辑这些文件毫无意义,因为它们会被覆盖。 - 将应用部署到线上 URL 需要在 caffeine.ai Web 应用中进行,而不是通过 CLI。 CLI 最多会帮你生成一个已构建的草稿(
caffeine preview --build,需要云端的project.id,详见构建流程)。没有caffeine deploy/publish命令。
先决条件(运行一次)
# Install the CLI (public npm package). The binary is `caffeine`.
npm install -g @caffeineai/cli
# Authenticate against caffeine.ai (opens a browser; use --device on headless hosts).
caffeine auth login
caffeine auth status
# Check the environment: Node, pnpm, mops, caffeine-bindgen, that you are logged in,
# and that the Caffeine API is reachable (installs/repairs what it can).
caffeine doctor --fix
如果不希望全局安装,或者全局安装失败,请在每个 caffeine 命令前加上 npx -y @caffeineai/cli@latest 前缀运行,例如:npx -y @caffeineai/cli@latest auth status。
caffeine doctor --fix 会检查 Node.js、pnpm、mops 和 caffeine-bindgen,确认你已登录,并验证 Caffeine API 是否可访问;它会安装或修复能够处理的项目。它不会安装 Motoko 编译器(moc)或代码检查器(lintoko):mops 会在首次运行 mops install / 构建时,根据 mops.toml 中的 [toolchain] 固定版本自动获取这些工具。Caffeine 构建不需要 dfx。
项目结构
Caffeine App 是一个包含根清单和两个 canister 的 pnpm 工作区。请创建以下准确的目录树(文件内容见下文):
my-app/
├── .old/src/backend/dist/
│ └── backend.most # stable-signature baseline — seed with an empty actor (see below)
├── caffeine.toml # root workspace manifest — MUST contain [workspace]
├── mops.toml # Motoko build config — ONE per project, at the root
├── package.json # pnpm workspace root + the `bindgen` script
├── pnpm-workspace.yaml # declares src/**/* as workspace packages
├── tsconfig.json # root TS config
└── src/
├── backend/ # the single Motoko canister, named "backend"
│ ├── caffeine.toml # canister manifest: type = "motoko"
│ ├── main.mo # actor entry point (write with the `writing-motoko` skill)
│ └── migrations/ # enhanced-migration chain (see the `mops.toml` notes)
│ └── 00000000.mo # the no-op first migration
└── frontend/ # the assets canister (React + Vite)
├── caffeine.toml # canister manifest: type = "assets"
├── package.json # frontend deps + scripts (see references/frontend-template.md)
├── vite.config.js # (see references/frontend-template.md)
├── tsconfig.json # (see references/frontend-template.md)
├── biome.json # (see references/frontend-template.md)
├── tailwind.config.js # (see references/frontend-template.md)
├── postcss.config.js # (see references/frontend-template.md)
├── index.html
├── env.json # runtime config — copied into dist/ on build
└── src/
├── main.tsx # React entry (providers)
├── App.tsx
├── index.css
├── backend.ts # GENERATED by caffeine-bindgen — do NOT edit
└── declarations/ # GENERATED by caffeine-bindgen — do NOT edit
下文仅以内联形式展示根清单、两个 canister 清单、mops.toml、main.mo 和少量根配置文件。完整的前端样板代码(package.json、vite.config.js、Tailwind/PostCSS/Biome 配置、index.html、env.json、main.tsx、入门版 App.tsx)位于 references/frontend-template.md 中——创建前端时请阅读该文件。
清单
根目录中的 caffeine.toml — 工作区
根清单必须包含 [workspace] 部分。该部分用于标记工作区根目录;若缺少该部分,CLI 会将此文件视为普通 canister 清单,导致无法发现工作区。
manifest_version = "0.1.0"
[project]
name = "My App"
# id is assigned by the cloud after your first `caffeine preview`/create — omit it for
# a brand-new local project; the CLI fills it in.
[workspace]
include = [ "src/**" ]
# Build the backend before the frontend, because the frontend imports generated
# bindings produced from the backend's Candid interface.
[canisters.frontend]
depends_on = [ "backend" ]
[workspace].include 是一个 glob 列表,CLI 会据此扫描嵌套的 canister 清单。src/** 会找到 src/backend/caffeine.toml 和 src/frontend/caffeine.toml。
src/backend/caffeine.toml — Motoko 容器
manifest_version = "0.1.0"
[project]
name = "backend"
type = "motoko"
main = "main.mo"
[build]
commands = ["mops build", "pnpm bindgen"]
out = "dist"
[check]
commands = ["mops check"]
[check.fix]
commands = ["mops check --fix"]
[build].commands 按顺序运行:mops build 将 main.mo 编译为 Wasm + Candid,然后
pnpm bindgen(在根目录的 package.json 中定义)将 Candid 转换为前端的
TypeScript 客户端。caffeine check --fix 会运行 [check.fix].commands。
src/frontend/caffeine.toml — 资产容器
manifest_version = "0.1.0"
[project]
name = "frontend"
type = "assets"
[build]
commands = ["pnpm build"]
out = "dist"
[check]
commands = ["pnpm typecheck", "pnpm check"]
[check.fix]
commands = ["pnpm typecheck", "pnpm fix"]
mops.toml — Motoko 构建配置(每个项目在根目录下有一个)
只能有一个 [canisters.backend]。caffeine preview 只检查结构性部分,这些部分保持不变:单个 [canisters.backend]、
带有所示路径且没有 skipIfMissing 的 [canisters.backend.check-stable],以及
[build] outputDir = "src/backend/dist"。其他所有内容——工具链版本、[moc]
标志和 -E/-W/-A 诊断代码,以及依赖项列表——都是当前快照,Caffeine 模板会随时间更新;参见下文“版本和依赖项变更”。
[package]
name = "backend"
version = "0.1.0"
[build]
outputDir = "src/backend/dist"
args = ["--release"]
check-wasm = true
[canisters.backend]
main = "src/backend/main.mo"
[canisters.backend.check-stable]
path = ".old/src/backend/dist/backend.most"
[toolchain]
moc = "2.0.0"
lintoko = "0.12.0"
[lint]
extends = true
[moc]
args = [
"--implicit-package=core",
"-no-check-ir",
"-E=M0254",
"-W=M0235,M0223,M0236,M0237",
"-A=M0241"
]
[dependencies]
core = "2.6.1"
ic = "4.2.0"
caffeineai-lints = "0.1.0"
caffeineai-oql = "0.8.0"
[canisters.backend.migrations]
chain = "src/backend/migrations"
check-limit = 1
说明:
-
新项目使用 moc 2。 在 moc 2 中,裸
actor是持久化的,而persistent关键字只会触发警告 M0217。旧指南会在[moc] args中添加--default-persistent-actors或--generate-view-queries:这些是 moc 1.x 标志,已在 moc 2.0 中移除,传入后每次编译都会因unknown option而失败。从切换之前克隆的项目可能仍固定使用 moc 1.x 并保留--default-persistent-actors;此类项目应继续使用其固定版本。 -
check-stable指向.old/src/backend/dist/backend.most,这是上次部署的后端稳定签名快照。caffeine check会将你的稳定状态与之比较,而caffeine projects clone会从云端恢复它。不要删除.old/。从零开始创建的项目必须用空 actor 为其初始化,就像 Caffeine 模板所做的那样,否则caffeine check会因以下错误而失败:Deployed file not found: .old/src/backend/dist/backend.most:// Version: 1.0.0 actor {};不要改为设置
skipIfMissing = true:mops 已弃用此选项,而且caffeine preview会拒绝它,因为跳过检查只会在之后部署时才失败。 -
[canisters.backend.migrations]会启用增强迁移:src/backend/migrations/中的每个.mo文件都按名称排序,作为一个步骤,将上一个稳定状态映射到下一个状态。新项目从无操作的00000000.mo开始,它的排序位置早于writing-motoko技能所创建的YYYYMMDD_HHMMSS.mo文件:module { public type OldActor = {}; public type NewActor = {}; public func migration(_ : OldActor) : NewActor { {}; }; };部署迁移后,绝不要编辑或删除它。
-
core是mo:core标准库。请使用mo:core,切勿使用已弃用的mo:base——参见writing-motoko技能。caffeineai-oql提供起始main.mo所包含的Expose混入(见下文)。 -
ic提供 IC 管理容器的类型和调用(mo:ic/Types、mo:ic/Call)。请用它替代import ... "ic:aaaaa-aa",后者只有在存在.did文件的--actor-idl目录时才能解析,而 Caffeine 项目已不再包含该目录。
版本和依赖项变更。
[toolchain]版本、[moc]中的-E/-W/-A代码以及前端依赖项集合由模板管理,并会随时间变化(例如,从@dfinity/*迁移到@icp-sdk/*、moc/core 版本升级)。请将此处的具体值视为快照——如需获取当前值,请使用caffeine projects clone克隆一个近期项目,并复制其mops.toml和src/frontend/package.json。 不随版本变化的内容:文件布局、单个backend容器、带初始化基线的check-stable、[build] outputDir、mo:core,以及useActor(createActor)。
package.json(根目录)— pnpm 工作区 + bindgen 脚本
bindgen 脚本负责将后端 Candid 转换为前端 TS 客户端。后端容器的 [build] 步骤会调用它(pnpm bindgen)。
{
"name": "@caffeine/template-app",
"type": "module",
"engines": {
"node": ">=18.17.0",
"pnpm": ">=7.0.0",
"npm": "please use pnpm"
},
"scripts": {
"build": "pnpm -r --if-present run build",
"typecheck": "pnpm -r --if-present run typecheck",
"check": "pnpm -r --if-present run check",
"fix": "pnpm -r --if-present run fix",
"bindgen": "caffeine-bindgen --did-file ./src/backend/dist/backend.did --out-dir ./src/frontend/src --actor-interface-file --force"
},
"devDependencies": {
"sharp": "^0.34.4"
}
}
pnpm-workspace.yaml
packages:
- src/**/*
onlyBuiltDependencies:
- esbuild
tsconfig.json(根目录)
{
"compilerOptions": {
"strict": true,
"target": "ES2020",
"experimentalDecorators": true,
"strictPropertyInitialization": false,
"moduleResolution": "node",
"allowJs": true,
"outDir": "HACK_BECAUSE_OF_ALLOW_JS"
}
}
src/backend/main.mo — 起始后端
规范模板的起始 main.mo 包含来自
caffeineai-oql 的 Expose 混入,用于发布后端的查询接口。Actor 目前没有稳定
状态,因此首次迁移会将 {} 映射为 {}。请按照 writing-motoko 技能在 Actor 内添加自己的函数。
import Expose "mo:caffeineai-oql/Expose";
actor {
include Expose({ entities = [] });
};
在加载 writing-motoko 技能之前,不要编写更多 Motoko 代码——该技能涵盖持久化 Actor、稳定类型、mo:core,以及 Agent 否则可能凭空编造的编译器错误陷阱。
前端
获取此技能的配套参考文件 —
references/frontend-template.md,它与此 SKILL.md 位于同一目录(即 …/skills/caffeine-app/references/frontend-template.md)——并根据该文件创建前端文件;其中包含经过验证的 package.json、Vite/Tailwind/Biome 配置、main.tsx,以及 useActor(createActor) 后端调用示例。要点如下:
- 入口文件
src/frontend/src/main.tsx使用InternetIdentityProvider(来自@caffeineai/core-infrastructure)和 TanStackQueryClientProvider包装应用。 - 前端通过生成的模块
src/frontend/src/backend.ts(以及src/frontend/src/declarations/)与后端通信;该模块导出一个createActor(...)工厂,其类型根据后端的 Candid 定义生成。这些文件无需手动编写——首次构建后会自动生成。将该createActor传给@caffeineai/core-infrastructure中的useActor(createActor),即可调用后端(同一软件包还提供main.tsx中使用的身份验证提供程序);你无需手动编写 Actor/配置/存储胶合代码。请参阅references/frontend-template.md中的示例。 env.json存放运行时配置占位符(backend_host、backend_canister_id、project_id、ii_derivation_origin、storage_gateway_url),本地值均为"undefined";前端的build脚本会将其复制到dist/(cp env.json dist/)。Caffeine 提供应用时会注入真实值。请保留env.json和copy:env步骤。
构建循环
从项目根目录运行。顺序很重要。
# 1. Install dependencies. On a brand-new project, generate the pnpm lockfile FIRST
# (first run only — see Common Pitfalls), then install the rest (pnpm + mops).
pnpm install
caffeine install
# 2. Write your backend in src/backend/main.mo using the `writing-motoko` skill, and your
# frontend in src/frontend/src/.
# 3. Build: compiles the backend, then `pnpm bindgen` regenerates the frontend client
# (src/frontend/src/backend.ts + declarations/), then the frontend `pnpm build` runs.
# Run this BEFORE check, so the frontend has the generated ./backend to typecheck
# against. Artifacts land in src/backend/dist + src/frontend/dist (no cloud / no id).
caffeine build
# 4. Lint + typecheck + auto-fix (runs each canister's [check.fix] commands:
# backend `mops check --fix`, frontend `pnpm typecheck` + `pnpm fix`).
caffeine check --fix
# 5. Upload a built draft (needs a cloud project id). For a brand-new app, create the
# cloud project once to obtain an id, then preview:
caffeine projects create # reports a new project id
# put that id in caffeine.toml [project] id = "...", OR pass --project-id:
caffeine preview --build --project-id <id>
设置 CAFFEINE_VERBOSE=1 可流式输出 caffeine install、caffeine build 和 caffeine check 的完整子进程输出。这相当于对每条命令传入 --verbose。--json 仍会禁止流式输出。
生成顺序。 只有在构建过程中运行 pnpm bindgen 后,src/frontend/src/backend.ts 才会生成。因此,如果前端从 ./backend 导入内容,就必须至少运行一次 caffeine build,之后 caffeine check 才能对前端进行类型检查。全新应用的 App.tsx 尚未导入 ./backend 时,即使在首次构建前也能通过类型检查。
上线。 caffeine preview --build(需要 project.id)会生成一个草稿并返回其 URL。在 caffeine.ai 网页应用中将草稿提升为正式 URL(通过 caffeine chat open 打开,或访问 caffeine.ai)。CLI 没有部署命令。使用 caffeine projects show <id> 查看草稿和正式 URL;如果已分配自定义域名,其中也会显示可用的自定义域名。
替代方案:让 Caffeine 自带的 AI 来构建
如果无法在本地运行 CLI(没有 shell 或工具链),可以改用 Caffeine 托管的 AI:通过 caffeine chat send "<prompt>"(或 caffeine.ai 网页聊天)用自然语言描述应用。随后,Caffeine 托管的 AI 会在服务器端搭建、构建并部署应用——chat send 只会提交提示词,因此这与 CLI 没有部署命令并不矛盾。本技能适用于在本地手动编写的流程。
常见陷阱
- 根目录的
caffeine.toml中缺少[workspace]。 如果没有该配置,根目录文件会被解析为 canister 清单,CLI 也就无法找到你的 canister。根清单由是否存在[workspace]定义,而不是由它是否位于顶层决定。 - 重命名后端 canister。 它必须命名为
backend——mops.toml声明了[canisters.backend],根目录的bindgen脚本会读取./src/backend/dist/backend.did。重命名会导致构建失败,并破坏生成的客户端。 - 手动编写或编辑
src/frontend/src/backend.ts或declarations/。 这些文件由caffeine-bindgen生成,并会在每次构建时被覆盖。请在main.mo中添加后端方法,然后重新构建,客户端就会重新生成。生成的文件头写着“请勿对本文件做任何更改”;此外,该文件也被 Biome 排除在外。 - 首次构建前导入
./backend。 绑定模块由构建中的pnpm bindgen步骤创建。在依赖import { createActor } from "./backend"之前,先运行一次caffeine preview --build(或caffeine build)。 - 尝试运行
dfx deploy或期待有caffeine deploy。 Caffeine 应用通过caffeineCLI 构建,并通过 Web 应用上线。工作流中没有dfx,也没有 CLI 部署/发布命令。 - 删除必需的
mops.toml设置。 请保留单个[canisters.backend]、[canisters.backend.check-stable]、[build] outputDir,以及固定版本的[toolchain] moc(没有该设置,mops 就无法编译)。 - 删除
.old/或env.json,或跳过copy:env。.old/保存check-stable用于比较的稳定签名快照;必须将env.json复制到dist/,以便提供服务的前端读取其运行时配置。 - 不使用
writing-motoko技能就编写 Motoko。 现代 Caffeine Motoko 使用mo:core、持久化 Actor 和特定模式。靠猜会导致编译器报错。请先加载 https://skills.internetcomputer.org/skills/writing-motoko/SKILL.md。 - 使用
npm/yarn而非pnpm。 该工作区基于 pnpm(pnpm-workspace.yaml、pnpm -r脚本)。caffeine install使用 pnpm + mops。 - 全新项目中,
caffeine install/build因ERR_PNPM_NO_LOCKFILE失败。 在非交互式 shell(Agent、CI 或任何没有 TTY 的运行环境)中,这些命令会运行pnpm install --frozen-lockfile,而这要求存在全新项目尚未创建的pnpm-lock.yaml。先运行一次pnpm install生成锁文件,然后整个流程就能正常运行。(在交互式终端中,系统会自动为你创建锁文件。) - 首次运行
caffeine check时,因Deployed file not found: …/backend.most失败。 稳定签名检查需要上次部署的快照,而从未部署过的项目没有该快照。请用一个空 Actor 创建.old/src/backend/dist/backend.most(见上文的mops.toml)。caffeine build不会创建该文件。 caffeine preview --build因No project.id in caffeine.toml失败。 上传草稿需要云端项目。运行caffeine projects create获取 ID,在[project] id中设置(或传入--project-id <id>),然后再预览。单独运行caffeine build无需 ID,足以验证应用能否编译。
相关技能
writing-motoko— 必须搭配使用的技能。所有后端 Motoko 代码的权威参考: https://skills.internetcomputer.org/skills/writing-motoko/SKILL.md。编辑src/backend/main.mo前,务必先加载。icp-cli— 用于原生 Internet Computer /dfx/icp项目。Caffeine 不会用到它;只有在用户没有构建 Caffeine 应用时才使用。
补充参考资料
references/frontend-template.md— 完整且未经修改的前端样板代码(package.json、vite.config.js、Tailwind/PostCSS/Biome 配置、tsconfig.json、index.html、env.json、main.tsx、起始版App.tsx以及后端 Actor 接线模式)。创建src/frontend/时请阅读。