扣子罗盘(coze-loop)前端开发指南:基于 Rush.js Monorepo 的环境搭建、开发调试与构建实践
扣子罗盘(coze-loop)前端开发指南:基于 Rush.js Monorepo 的环境搭建、开发调试与构建实践
本文面向希望在本地搭建 CozeLoop(扣子罗盘)前端开发环境的开发者,完整讲解基于 Rush.js 管理的 Monorepo 前端仓库从环境准备、依赖安装、开发调试到生产构建的全流程,并结合仓库内 frontend/apps/cozeloop 的源码与配置,深入说明 workspace 依赖机制、Rsbuild 构建管线、路由组织以及 API 类型生成等关键实现。读完本文,你将能够独立完成 CozeLoop 前端的本地启动、开发与构建,并理解其"源码即依赖"的 monorepo 协作方式。
一、前端仓库整体认知
CozeLoop 是一个下一代 AI Agent 优化平台,前端部分位于仓库根目录下的 frontend 目录,采用 Monorepo 架构,由 Rush.js 统一管理。整个前端被拆分为三类角色:
- 应用入口:
frontend/apps/cozeloop,即"扣子罗盘"主应用,是一个 React 应用; - 业务模块包:
@cozeloop/*系列,如账号(account)、提示词(prompt-pages)、评测(evaluate-pages)、可观测性(observation-pages)、标签(tag-pages)等; - 架构基础包:
@coze-arch/*系列,如设计系统(coze-design)、ESLint 配置、TypeScript 配置、Vitest 配置、构建配置等公共设施。
从 frontend/apps/cozeloop/README.md 可以确认应用的核心技术栈:React 18.2 + TypeScript 5.8、Rsbuild 构建、React Router 6.22 路由、Zustand 状态管理、Tailwind CSS 3.3 与 Less 样式、@coze-arch/coze-design 组件库,并内置 i18n 国际化能力。
二、环境要求与准备工作
原文档明确列出了三件套版本要求,这是保证构建产物一致性的关键,务必严格对齐:
| 依赖 | 版本要求 | 说明 |
|---|---|---|
| Node.js | 24+(推荐 lts/krypton) |
运行时环境,必须满足版本下限 |
| pnpm | 10.27.0 | 包管理器,由 Rush 驱动 |
| Rush | 5.172.1(即 @microsoft/rush) |
Monorepo 任务编排器 |
2.1 安装 Node.js
推荐使用 nvm 安装并锁定 lts/krypton 版本:
nvm install lts/krypton
nvm alias default lts/krypton # 设置默认 Node 版本
nvm use lts/krypton
2.2 检出仓库并切换目录
# 克隆仓库
git clone git@github.com:coze-dev/coze-loop.git
# 切换目录
cd frontend
2.3 安装全局依赖
npm i -g pnpm@10.27.0 @microsoft/rush@5.172.1
之所以要全局安装指定版本的 pnpm 与 Rush,是因为 Rush 需要借助固定版本的 pnpm 来生成统一的依赖树与 lockfile,跨版本使用可能导致 pnpm-lock.yaml 不一致。
三、安装/更新项目依赖:rush update
进入 frontend 目录后,直接执行:
rush update
rush update 会读取 rush.json 中声明的所有项目(apps、packages),解析各包 package.json 中的 workspace:* 依赖关系,并在 common/config/subspaces/default/ 下生成统一的 pnpm-lock.yaml。这与直接在各子包中执行 pnpm install 的关键区别在于:整个 monorepo 只维护一份 lockfile 和一个 node_modules 拓扑,从而保证所有 workspace 包使用同一套依赖版本,避免"依赖地狱"。
四、启动开发服务器
扣子罗盘项目位于 apps/cozeloop 目录,文档特别强调:在 Rush 管理的项目中应使用 rushx 而不是 pnpm run 或 npm run,因为 rushx 由 Rush 注入正确的环境变量并在项目上下文内执行脚本。
cd apps/cozeloop
rushx dev
启动后在浏览器中打开 http://localhost:8090/](http://localhost:8090/) 即可查看页面。端口 8090 并非随意设定,而是定义在 [frontend/apps/cozeloop/rsbuild.config.ts 中:
const port = 8090;
export default createRsbuildConfig({
server: {
port,
cors: { origin: '*' },
},
dev: {
lazyCompilation: false,
assetPrefix: `http://localhost:${port}`,
client: { port: `${port}`, host: 'localhost', protocol: 'ws' },
},
html: {
title: 'Coze Loop',
template: './src/assets/template.html',
favicon: './src/assets/images/coze.svg',
crossorigin: 'anonymous',
},
});
从配置可以看到:开发服务默认关闭 lazyCompilation、开启 CORS,并通过 assetPrefix 与 client 配置建立了基于 WebSocket 的 HMR 通道;HTML 模板与 favicon 分别指向 src/assets/template.html 与 src/assets/images/coze.svg。
4.1 不同环境/区域的启动变体
查看 frontend/apps/cozeloop/package.json 的 scripts 可以发现,rushx dev 实际是 npm run dev:cn-boe 的别名,完整的环境矩阵如下:
| 脚本 | 环境变量 | 用途 |
|---|---|---|
dev |
REGION=cn CUSTOM_VERSION=inhouse BUILD_TYPE=offline |
默认开发(中国区 BOE 内网环境) |
dev:cn |
REGION=cn |
中国区开发 |
dev:cn-release |
REGION=cn CUSTOM_VERSION=release BUILD_TYPE=online |
中国区生产联调 |
dev:base |
无 | 纯 Rsbuild dev server |
这些环境变量(REGION、CUSTOM_VERSION、BUILD_TYPE)贯穿构建与运行期,用于区分部署区域(cn/us/eu)、自定义版本(inhouse/release)以及构建类型(online/offline)。
五、生产构建:Rsbuild 管线
扣子罗盘项目由 Rsbuild 构建,构建命令为:
cd apps/cozeloop
rushx build
执行链路为 build → build:prod(cross-env REGION=cn rsbuild build)。此外还提供以下配套脚本:
build:ts:tsc -b tsconfig.build.json,TypeScript 类型检查与增量编译;preview:rsbuild preview,本地预览构建产物;analyze:cross-env RSDOCTOR=true rsbuild build,借助 Rsdoctor 插件分析构建包体积(对应 devDependencies 中的@rsdoctor/rspack-plugin)。
六、workspace 依赖:源码即依赖
打开 frontend/apps/cozeloop/package.json,可以看到大量依赖以 workspace:* 版本声明:
"dependencies": {
"@cozeloop/account": "workspace:*",
"@cozeloop/api-schema": "workspace:*",
"@cozeloop/auth-pages": "workspace:*",
"@cozeloop/components": "workspace:*",
"@cozeloop/evaluate-pages": "workspace:*",
"@cozeloop/observation-pages": "workspace:*",
"@cozeloop/prompt-pages": "workspace:*",
"@cozeloop/tag-pages": "workspace:*",
"react": "~18.2.0",
"zustand": "^4.4.7"
}
这表示这些包全部在仓库内维护。扣子罗盘项目依赖的是这些 workspace 包的源码而非构建产物——直接修改 workspace 依赖的源代码,变更会立刻生效(有时候需要重新运行开发服务器)。这一机制让跨包联调无需先 build 再引用产物,极大提升了开发效率。
从 frontend/packages/loop-base 与 frontend/packages/loop-pages 的目录结构可以确认:account、api-schema、components、guard、i18n、stores、toolkit 等基础能力在 loop-base 下维护,而 auth-pages、prompt-pages、evaluate-pages、observation-pages、tag-pages 等页面模块在 loop-pages 下维护。
七、更新前端 API 类型:从 IDL 到 TypeScript
当后端接口变更时,前端需要重新生成 API 类型。原文档给出的命令是:
rush update-api
7.1 产物位置
文档中提及 IDL 相关文件将生成在 packages/cozeloop/api-schema,需要说明的是:在当前仓库中,@cozeloop/api-schema 包的实际路径为 frontend/packages/loop-base/api-schema(原文档路径已随仓库演进调整,请以仓库实际结构为准)。该包通过 package.json 的 exports 按领域暴露入口,例如:
"exports": {
".": "./src/index.ts",
"./observation": "./src/api/observability/index.ts",
"./evaluation": "./src/api/evaluation/index.ts",
"./data": "./src/api/data/index.ts",
"./llm-manage": "./src/api/llm-manage/index.ts",
"./foundation": "./src/api/foundation/index.ts",
"./prompt": "./src/api/prompt/index.ts"
}
7.2 IDL 生成管线
查看 frontend/packages/loop-base/api-schema/package.json 可以发现,API Schema 更新实际由三段脚本串联:
"prethrift": "bash ./scripts/download-thrift.sh --branch=main",
"thrift": "idl2ts gen ./",
"update": "npm run thrift"
即:先下载 IDL(thrift 文件),再由 @coze-arch/idl2ts-cli 生成 TypeScript 代码。
下载脚本 frontend/packages/loop-base/api-schema/scripts/download-thrift.sh 的实现非常巧妙:
- 若检测到当前所在仓库就是
coze-dev/coze-loop,则直接软链接本地idl/thrift目录(位于仓库根目录 idl/thrift/coze/loop),保证本地开发时 IDL 与源码同步; - 否则通过 git sparse-checkout 从远程仓库浅克隆
idl/thrift目录到node_modules/.cache/coze-loop-idl/; - 默认分支为
main,可通过--branch参数指定其他分支。
随后 frontend/packages/loop-base/api-schema/api.config.js 定义 idl2ts 的生成规则,将 prompt、observability、evaluation、data、llm、foundation 等各领域的 thrift 文件分别映射为独立的 TS 模块,产物输出到 ./src/api/idl。例如:
entries: {
promptDebug: './prompt/coze.loop.prompt.debug.thrift',
promptManage: './prompt/coze.loop.prompt.manage.thrift',
observabilityTrace: './observability/coze.loop.observability.trace.thrift',
evaluationEvalSet: './evaluation/coze.loop.evaluation.eval_set.thrift',
dataDataset: './data/coze.loop.data.dataset.thrift',
foundationSpace: './foundation/coze.loop.foundation.space.thrift',
// ...
}
从后端源码也可以印证这一链路:IDL 定义位于 idl/thrift/coze/loop,对应的 Go 服务端类型与处理逻辑由 backend/kitex_gen/coze/loop 和 backend/api/handler/coze/loop 承载,前后端以 thrift IDL 为唯一契约。
八、应用源码结构纵深
8.1 应用入口与启动流程
入口文件 frontend/apps/cozeloop/src/index.tsx 展示了应用启动前的异步初始化:
await Promise.all([
initIntl({ fallbackLng: ['zh-CN', 'en-US'] }),
pullFeatureFlags({ timeout: 1000 * 4, fetchFeatureGating: ... }),
dynamicImportMdBoxStyle(),
]);
即并行完成国际化初始化(兜底语言为 zh-CN 与 en-US)、特性开关拉取(4 秒超时)以及 Markdown 盒子样式动态导入,随后挂载到 #cozeloop-root 节点。
8.2 应用组件树
frontend/apps/cozeloop/src/app.tsx 组装了应用骨架:
const router = createBrowserRouter(routeConfig);
export function App() {
useSetupI18n();
return (
<CozeLoopProvider i18n={I18n} sendEvent={sendEvent}>
<Suspense fallback={<PageLoading />}>
<LocaleProvider>
<RouterProvider router={router} />
</LocaleProvider>
</Suspense>
</CozeLoopProvider>
);
}
CozeLoopProvider 注入国际化与埋点(sendEvent 来自 @cozeloop/tea-adapter),配合 Suspense 支持业务模块的按需懒加载。
8.3 路由组织
frontend/apps/cozeloop/src/routes/index.tsx 定义了完整的路由树,清晰映射了平台的四大业务域:
| 路径 | 模块 | 说明 |
|---|---|---|
/auth/* |
@cozeloop/auth-pages |
登录鉴权 |
/console/enterprise/:enterpriseID/space/:spaceID/pe/* |
@cozeloop/prompt-pages |
提示工程(Prompt Engineering) |
/console/.../space/:spaceID/evaluation/* |
@cozeloop/evaluate-pages |
评测系统 |
/console/.../space/:spaceID/observation/* |
@cozeloop/observation-pages |
可观测性 |
/console/.../space/:spaceID/tag/* |
@cozeloop/tag-pages |
标签管理 |
所有业务页面均通过 lazy(() => import(...)) 懒加载,路由结构采用 enterprise → space → 业务域 的三级组织模型。
8.4 鉴权与空间初始化
frontend/apps/cozeloop/src/routes/base-route.tsx 负责登录态守卫:settling 时展示加载页、not_login 时重定向到 /auth/login、logined 时进入 GuardProvider 包裹的业务路由。
frontend/apps/cozeloop/src/hooks/use-setup-space.ts 则处理 Space(空间)的选择逻辑:拉取用户空间列表后,优先匹配 URL 中的 spaceID,否则回退到 Personal 类型空间或列表首个空间,并自动重定向到正确的 /console/enterprise/:id/space/:spaceId 路径;找不到空间时返回 NOT_FOUND,请求失败时返回 FETCH_ERROR,两者分别由 frontend/apps/cozeloop/src/components/basic-layout/index.tsx 渲染对应的错误页。
8.5 样式与 UI 设施
- Tailwind 配置:frontend/apps/cozeloop/tailwind.config.ts 基于
@cozeloop/tailwind-config工厂函数生成,content扫描范围覆盖coze-design、components及各业务包,确保跨包类名都能被正确提取; - 基础布局:frontend/apps/cozeloop/src/components 下提供
basic-layout(基础布局)、navbar(导航栏)、breadcrumb(面包屑)、user-info-section(用户信息区)与locale-provider(语言环境提供者)等通用组件; - API 错误提示:frontend/apps/cozeloop/src/hooks/use-api-error-toast.tsx 监听
@cozeloop/api-schema暴露的$notification的apiError事件,统一以 Toast 形式呈现接口异常。
九、测试与质量保障
- 单元测试:frontend/apps/cozeloop/vitest.config.ts 基于
@coze-arch/vitest-config的web预设定义,执行rushx test(即vitest --run --passWithNoTests)即可运行测试; - 代码检查:
rushx lint使用 frontend/apps/cozeloop/eslint.config.cjs 中@coze-arch/eslint-config的web预设; - TypeScript 检查:
rushx build:ts通过tsc -b执行项目引用(Project References)增量编译,frontend/apps/cozeloop/tsconfig.json 引用了tsconfig.build.json与tsconfig.misc.json两个子配置。
此外,frontend/apps/cozeloop/config/rush-project.json 向 Rush 声明了 build 与 ts-check 操作的输出目录(dist、lib-ts、src/locales),使 Rush 能够精确跟踪构建产物并支持增量构建。
十、常见问题与排查建议
rushx提示找不到命令:请确认已按本文第二节全局安装@microsoft/rush@5.172.1,且在当前项目目录(apps/cozeloop)下执行;- 依赖不一致 / lockfile 冲突:重新执行
rush update以对齐common/config/subspaces/default/pnpm-lock.yaml; - 端口被占用:8090 由
rsbuild.config.ts固定,如需调整请修改 frontend/apps/cozeloop/rsbuild.config.ts 中的port常量; - API 类型与后端不同步:修改 workspace 依赖源码后需重跑开发服务器,若涉及 API 类型变更,则在 frontend/packages/loop-base/api-schema 目录执行
rushx update(或仓库级rush update-api)重新生成。
十一、许可证
前端代码与整个仓库一致,采用 Apache License, Version 2.0 开源协议。