diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 947041be..0a5f3a4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -363,7 +363,7 @@ jobs: name: ChatLab-cli-build path: | apps/cli/dist - apps/cli/dist-web + apps/cli/dist-cli-web if-no-files-found: error retention-days: 1 diff --git a/.gitignore b/.gitignore index 40d720a6..650fcec9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ error-log-*.txt # Project dist/ -dist-web/ +dist-cli-web/ dist-web-wasm/ docs/.vitepress/cache/ docs/.vitepress/dist/ diff --git a/AGENTS.md b/AGENTS.md index bfc97497..7a4aac68 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,6 +50,7 @@ ## 代码规范 +- 平台术语:内部交流、开发菜单和平台级代码统一使用 `CLI Web`(Node 后端 + Web UI)与 `Web WASM`(纯浏览器运行)。只说 `Web` 且上下文无法区分时,默认指 `Web WASM`。浏览器 Worker、OPFS 和浏览器 adapter 等技术能力使用 `Browser Runtime` 命名,它不是独立平台。详细边界见 `.docs/rules/platform-naming.md`。 - 多语言:代码中的日志、注释、AI 工具描述、错误消息等非 UI 文本默认使用英文。当有运行时 locale 可用时(如工具返回结果、AI 看到的文本),应通过 `isChineseLocale(locale)` 等机制支持中英双语。数据清洗中与聊天平台格式匹配的标签(如 `[分享]`、`[图片]`)保持原始语言不变。UI 文案的国际化遵循 `.docs/rules/i18n.md` - i18n 复用性:新增 UI 文案 key 前,先判断是否是通用动作、状态、提示或组件文案;能复用的优先放在 `common.*` 等共享命名空间,避免在具体业务模块(如 `members.*`、`records.*`)重复定义同义 key。只有明确绑定业务语境、无法自然复用的文案才放到模块命名空间。 diff --git a/README.md b/README.md index 064f58f7..8c1b6e7d 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,8 @@ Or launch a specific target directly: ```bash pnpm dev:desktop # Electron desktop app -pnpm dev:web # Web frontend + local server +pnpm dev:cli-web # CLI Web frontend + local server +pnpm dev:web-wasm # Browser-only Web WASM pnpm dev:serve # CLI server only pnpm docs:dev # Docs site ``` diff --git a/README.zh-CN.md b/README.zh-CN.md index 711b081b..7f6ec0aa 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -113,7 +113,8 @@ pnpm dev ```bash pnpm dev:desktop # Electron 桌面端 -pnpm dev:web # 前端 + 本地服务 +pnpm dev:cli-web # CLI Web 前端 + 本地服务 +pnpm dev:web-wasm # 纯浏览器 Web WASM pnpm dev:serve # 仅 CLI 服务端 pnpm docs:dev # 文档站 ``` diff --git a/apps/cli/package.json b/apps/cli/package.json index b0fe605b..612427f3 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -11,7 +11,7 @@ "files": [ "bin/", "dist/", - "dist-web/", + "dist-cli-web/", "LICENSE" ], "engines": { @@ -36,8 +36,8 @@ "scripts": { "cli": "node scripts/ensure-native.mjs && tsx src/cli.ts", "build": "tsup", - "build:full": "pnpm -w run build:web && pnpm run build && pnpm run prepare:web", - "prepare:web": "rm -rf dist-web && cp -r ../../dist-web dist-web", + "build:full": "pnpm -w run build:cli-web && pnpm run build && pnpm run prepare:cli-web", + "prepare:cli-web": "rm -rf dist-cli-web && cp -r ../../dist-cli-web dist-cli-web", "ensure-native": "node scripts/ensure-native.mjs", "rebuild-native": "bash scripts/rebuild-native.sh", "rebuild-sqlite": "npm rebuild better-sqlite3", diff --git a/apps/cli/src/cli.ts b/apps/cli/src/cli.ts index c4d7c740..b6c7e02f 100644 --- a/apps/cli/src/cli.ts +++ b/apps/cli/src/cli.ts @@ -140,11 +140,11 @@ program let webRoot: string | undefined if (!options.headless) { - const webDir = resolveCliPath('dist-web') + const webDir = resolveCliPath('dist-cli-web') if (fs.existsSync(webDir)) { webRoot = webDir } else { - console.warn('Warning: dist-web/ not found, starting in API-only mode') + console.warn('Warning: dist-cli-web/ not found, starting in API-only mode') } } diff --git a/apps/cli/src/http/index.ts b/apps/cli/src/http/index.ts index 48d879d5..d8863e9c 100644 --- a/apps/cli/src/http/index.ts +++ b/apps/cli/src/http/index.ts @@ -46,7 +46,7 @@ export interface HttpServerOptions { port?: number host?: string token?: string - /** dist-web/ 目录路径,启用后托管 Web SPA 静态资源 */ + /** dist-cli-web/ 目录路径,启用后托管 CLI Web SPA 静态资源 */ webRoot?: string /** When true, /_web/* also requires Bearer token (for server/headless deployments) */ requireAuth?: boolean @@ -202,7 +202,7 @@ export async function startHttpServer(options?: HttpServerOptions): Promise<{ if (options?.webRoot && fs.existsSync(options.webRoot)) { // 注册反向代理:将 /_proxy/chatlab.fun/* 转发至 https://chatlab.fun, - // 行为与 vite dev proxy 一致,解决浏览器 CORS 问题(见 vite.web.config.mts:138-144)。 + // 行为与 vite dev proxy 一致,解决浏览器 CORS 问题(见 vite.cli-web.config.mts)。 // 必须在 @fastify/static 之前注册,确保显式路由优先于静态文件/SPA fallback。 registerProxyRoutes(server) const fastifyStatic = await import('@fastify/static') diff --git a/apps/cli/src/http/routes/proxy.ts b/apps/cli/src/http/routes/proxy.ts index a6eb8de6..f17bd371 100644 --- a/apps/cli/src/http/routes/proxy.ts +++ b/apps/cli/src/http/routes/proxy.ts @@ -2,7 +2,7 @@ * 反向代理路由 — /_proxy/chatlab.fun/* * * 在 Web 生产模式(chatlab start)下,将 /_proxy/chatlab.fun/* 代理到 - * https://chatlab.fun,行为与 vite.web.config.mts 中的 dev proxy 保持一致, + * https://chatlab.fun,行为与 vite.cli-web.config.mts 中的 dev proxy 保持一致, * 以解决浏览器 CORS 限制。 * * 目标 host 硬编码为 https://chatlab.fun,不允许外部指定,避免开放代理风险。 @@ -15,12 +15,8 @@ const PROXY_TARGET = 'https://chatlab.fun' export function registerProxyRoutes(server: FastifyInstance): void { server.get<{ Params: { '*': string } }>('/_proxy/chatlab.fun/*', async (request, reply) => { const subPath = request.params['*'] ?? '' - const queryString = new URLSearchParams( - request.query as Record, - ).toString() - const targetUrl = queryString - ? `${PROXY_TARGET}/${subPath}?${queryString}` - : `${PROXY_TARGET}/${subPath}` + const queryString = new URLSearchParams(request.query as Record).toString() + const targetUrl = queryString ? `${PROXY_TARGET}/${subPath}?${queryString}` : `${PROXY_TARGET}/${subPath}` try { const upstream = await fetch(targetUrl, { diff --git a/docs/cn/contributing/development.md b/docs/cn/contributing/development.md index e209dd7b..9c1f5edd 100644 --- a/docs/cn/contributing/development.md +++ b/docs/cn/contributing/development.md @@ -28,12 +28,14 @@ pnpm install | 命令 | 用途 | | --- | --- | -| `pnpm dev` | 交互式选择开发目标 | +| `pnpm dev` | 交互式选择 Desktop、CLI Web、CLI Web 的 API Server、Web WASM 或文档站开发目标 | | `pnpm dev:desktop` | 启动 Electron 桌面端开发模式 | -| `pnpm dev:web` | 启动 CLI Web UI 开发模式 | +| `pnpm dev:cli-web` | 启动 CLI Web(Node 后端 + Web UI)开发模式,默认访问 `http://127.0.0.1:3100/` | +| `pnpm dev:web-wasm` | 启动 Web WASM(纯浏览器运行),默认访问 `http://127.0.0.1:3130/` | | `pnpm docs:dev` | 启动公开文档站开发模式 | | `pnpm build:desktop` | 构建桌面端 | -| `pnpm build:web` | 构建 Web UI | +| `pnpm build:cli-web` | 构建 CLI Web UI | +| `pnpm build:web-wasm` | 构建 Web WASM | | `pnpm docs:build` | 构建公开文档站 | | `pnpm run type-check:all` | 运行前端和 Node 侧类型检查 | | `pnpm lint` | 运行 ESLint 并自动修复 | @@ -41,6 +43,14 @@ pnpm install 小范围改动优先对修改文件或相关子项目做定向检查;跨模块、发布或架构类改动再跑全量检查。 +## 平台术语 + +- **CLI Web**:由 `chatlab start` 运行,包含 Node.js 后端和 Web UI。 +- **Web WASM**:无 Node.js 后端,解析、存储和查询均在浏览器内运行。 +- “Web”是两者的上位概念。上下文无法区分时,默认指 **Web WASM**。 +- “后端”或“API Server”只指 Node.js 进程,不等于完整的 CLI Web。 +- **Browser Runtime** 只表示 Worker、OPFS、SQLite WASM 和浏览器 adapter 等技术能力,不是另一个平台名称。 + ## 目录职责 | 路径 | 职责 | @@ -58,7 +68,7 @@ pnpm install ## 架构边界 -ChatLab 同时维护 Electron 桌面端和 CLI Web 端。涉及共享业务逻辑时,优先把逻辑放到 `packages/node-runtime/src/services/` 或 `packages/core/`,入口层只做薄适配。 +ChatLab 同时维护 Electron 桌面端、CLI Web 和 Web WASM。涉及共享业务逻辑时,优先把逻辑放到 `packages/node-runtime/src/services/` 或 `packages/core/`,入口层只做薄适配。 - 不要在 Electron IPC handler 或 CLI HTTP route 中重复实现复杂业务流程。 - 不要在入口层绕过 `packages/core/` 直接写成员合并、删除、别名更新等核心 SQL 写操作。 diff --git a/docs/en/contributing/development.md b/docs/en/contributing/development.md index e705bef2..b01bd7c3 100644 --- a/docs/en/contributing/development.md +++ b/docs/en/contributing/development.md @@ -28,12 +28,14 @@ pnpm install | Command | Purpose | | --- | --- | -| `pnpm dev` | Select a development target interactively | +| `pnpm dev` | Select Desktop, CLI Web, Web WASM, API Server, or docs interactively | | `pnpm dev:desktop` | Start the Electron desktop app in development mode | -| `pnpm dev:web` | Start the CLI Web UI in development mode | +| `pnpm dev:cli-web` | Start CLI Web (Node backend + Web UI) in development mode at `http://127.0.0.1:3100/` by default | +| `pnpm dev:web-wasm` | Start Web WASM (browser-only runtime) at `http://127.0.0.1:3130/` by default | | `pnpm docs:dev` | Start the public docs site locally | | `pnpm build:desktop` | Build the desktop app | -| `pnpm build:web` | Build the Web UI | +| `pnpm build:cli-web` | Build the CLI Web UI | +| `pnpm build:web-wasm` | Build Web WASM | | `pnpm docs:build` | Build the public docs site | | `pnpm run type-check:all` | Run both web and Node type checks | | `pnpm lint` | Run ESLint with auto-fix | @@ -41,6 +43,14 @@ pnpm install For small changes, prefer targeted checks for the files or package you changed. For cross-module, release, or architecture changes, run the broader checks. +## Platform Terminology + +- **CLI Web** runs through `chatlab start` and includes a Node.js backend plus the Web UI. +- **Web WASM** has no Node.js backend; parsing, storage, and queries run in the browser. +- "Web" is the umbrella term. When context cannot distinguish the platform, it defaults to **Web WASM**. +- "Backend" and "API Server" refer only to the Node.js process, not the complete CLI Web platform. +- **Browser Runtime** refers only to technical capabilities such as Workers, OPFS, SQLite WASM, and browser adapters; it is not another platform name. + ## Repository Structure | Path | Responsibility | @@ -58,7 +68,7 @@ For small changes, prefer targeted checks for the files or package you changed. ## Architecture Boundaries -ChatLab maintains both an Electron desktop app and a CLI Web app. When changing shared business behavior, put the logic in `packages/node-runtime/src/services/` or `packages/core/` first, and keep entry points thin. +ChatLab maintains the Electron desktop app, CLI Web, and Web WASM. When changing shared business behavior, put the logic in `packages/node-runtime/src/services/` or `packages/core/` first, and keep entry points thin. - Do not duplicate complex business flows inside Electron IPC handlers or CLI HTTP routes. - Do not bypass `packages/core/` in entry points to write core SQL operations such as member merge, delete, or alias updates. diff --git a/package.json b/package.json index fce1e3fa..7b9ab382 100644 --- a/package.json +++ b/package.json @@ -34,10 +34,10 @@ "build:win": "pnpm --filter @openchatlab/desktop build:win", "build:skill-validator": "node scripts/build-chatlab-skill-validator.mjs", "dev:serve": "bash scripts/dev-serve.sh", - "dev:web": "pnpm run ensure:server-native && CHATLAB_AUTO_SERVE=1 vite --config vite.web.config.mts", + "dev:cli-web": "pnpm run ensure:server-native && CHATLAB_AUTO_SERVE=1 vite --config vite.cli-web.config.mts", "dev:web-wasm": "vite --config vite.web-wasm.config.mts", "ensure:server-native": "pnpm --filter chatlab-cli run ensure-native", - "build:web": "vite build --config vite.web.config.mts", + "build:cli-web": "vite build --config vite.cli-web.config.mts", "build:web-wasm": "vite build --config vite.web-wasm.config.mts", "docs:dev": "pnpm --filter @openchatlab/docs dev", "docs:build": "pnpm --filter @openchatlab/docs build", diff --git a/packages/http-routes/src/routes/web/merge.ts b/packages/http-routes/src/routes/web/merge.ts index 6a27233d..fa106706 100644 --- a/packages/http-routes/src/routes/web/merge.ts +++ b/packages/http-routes/src/routes/web/merge.ts @@ -1,5 +1,5 @@ /** - * Merge routes — shared across CLI, Electron, and web-serve. + * Merge routes — shared across CLI, Electron, and CLI Web. * * Parse supports dual-mode: * - multipart/form-data → file upload (web/CLI) diff --git a/scripts/check-workspace-dependencies.mjs b/scripts/check-workspace-dependencies.mjs index 821d5b51..8dd59548 100644 --- a/scripts/check-workspace-dependencies.mjs +++ b/scripts/check-workspace-dependencies.mjs @@ -8,7 +8,17 @@ import ts from 'typescript' const repoRoot = path.resolve(import.meta.dirname, '..') const sourceExtensions = new Set(['.cjs', '.cts', '.js', '.jsx', '.mjs', '.mts', '.ts', '.tsx', '.vue']) -const ignoredDirectories = new Set(['.docs', '.git', 'coverage', 'dist', 'dist-web', 'node_modules', 'out', 'target']) +const ignoredDirectories = new Set([ + '.docs', + '.git', + 'coverage', + 'dist', + 'dist-cli-web', + 'dist-web-wasm', + 'node_modules', + 'out', + 'target', +]) const ignoredPaths = new Set([ path.join(repoRoot, 'apps', 'desktop', 'native'), path.join(repoRoot, 'docs', '.vitepress', 'cache'), diff --git a/scripts/dev-select.mjs b/scripts/dev-select.mjs index ddc0d0b2..2f886c20 100644 --- a/scripts/dev-select.mjs +++ b/scripts/dev-select.mjs @@ -19,8 +19,9 @@ const cacheFile = join(rootDir, 'node_modules', '.cache', 'dev-mode') const options = [ { key: 'desktop', label: 'Desktop (Electron)', command: 'pnpm run dev:desktop' }, - { key: 'web', label: 'Web (CLI start + frontend)', command: 'pnpm run dev:web' }, - { key: 'server', label: 'Server Only (API backend)', command: 'pnpm run dev:serve' }, + { key: 'cli-web', label: 'CLI Web (Node + frontend)', command: 'pnpm run dev:cli-web' }, + { key: 'server', label: 'API Server (CLI Web backend only)', command: 'pnpm run dev:serve' }, + { key: 'web-wasm', label: 'Web WASM (Browser only)', command: 'pnpm run dev:web-wasm' }, { key: 'docs', label: 'Docs (VitePress)', command: 'pnpm run docs:dev' }, ] diff --git a/scripts/run-tests.mjs b/scripts/run-tests.mjs index 401c3a8d..23614025 100644 --- a/scripts/run-tests.mjs +++ b/scripts/run-tests.mjs @@ -11,7 +11,8 @@ const SKIP_DIRS = new Set([ '.docs', 'node_modules', 'dist', - 'dist-web', + 'dist-cli-web', + 'dist-web-wasm', 'out', 'build', 'coverage', diff --git a/vite.web.config.mts b/vite.cli-web.config.mts similarity index 92% rename from vite.web.config.mts rename to vite.cli-web.config.mts index 37a405d3..b3cf7fa5 100644 --- a/vite.web.config.mts +++ b/vite.cli-web.config.mts @@ -1,13 +1,13 @@ /** - * Web 版 Vite 构建配置 + * CLI Web Vite 构建配置 * * 用于构建 CLI Web 的 SPA 前端(不含 Electron 依赖)。 - * 输出到 dist-web/,由 chatlab start 托管。 + * 输出到 dist-cli-web/,由 chatlab start 托管。 * * 与 Electron renderer 构建的关键区别: * - __IS_ELECTRON__ = false(使用 FetchAdapter 而非 window.chatApi) * - 不包含 apps/desktop/preload 和 apps/desktop/main - * - 输出目录独立(dist-web/ vs out/renderer/) + * - 输出目录独立(dist-cli-web/ vs out/renderer/) */ import { resolve } from 'path' @@ -54,7 +54,7 @@ async function isChatlabBackendResponsive(port: number, timeoutMs = 800): Promis /** * 自动启动 chatlab start 后端的插件 - * 仅在 CHATLAB_AUTO_SERVE=1 时生效(由 dev:web 脚本设置) + * 仅在 CHATLAB_AUTO_SERVE=1 时生效(由 dev:cli-web 脚本设置) */ function chatlabServePlugin(): Plugin { let serverProcess: ChildProcess | null = null @@ -106,7 +106,7 @@ function chatlabServePlugin(): Plugin { return } throw new Error( - `[chatlab start] Port ${BACKEND_PORT} is in use, but ChatLab API did not respond. Stop the stale process and restart dev:web.` + `[chatlab start] Port ${BACKEND_PORT} is in use, but ChatLab API did not respond. Stop the stale process and restart dev:cli-web.` ) } @@ -155,7 +155,7 @@ export default defineConfig({ }, define: { __IS_ELECTRON__: JSON.stringify(false), - __IS_BROWSER_STANDALONE__: JSON.stringify(false), + __IS_WEB_WASM__: JSON.stringify(false), __APP_VERSION__: JSON.stringify(JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8')).version), }, plugins: [ @@ -172,7 +172,7 @@ export default defineConfig({ chatlabServePlugin(), ], build: { - outDir: resolve(__dirname, 'dist-web'), + outDir: resolve(__dirname, 'dist-cli-web'), emptyOutDir: true, sourcemap: false, rollupOptions: { @@ -191,10 +191,11 @@ export default defineConfig({ }, }, server: { + host: '127.0.0.1', port: 3100, proxy: { - '/_web': `http://localhost:${BACKEND_PORT}`, - '/api': `http://localhost:${BACKEND_PORT}`, + '/_web': `http://127.0.0.1:${BACKEND_PORT}`, + '/api': `http://127.0.0.1:${BACKEND_PORT}`, '/_proxy/chatlab.fun': { target: 'https://chatlab.fun', changeOrigin: true,