mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
chore(web): add Web WASM development workflow
This commit is contained in:
1 parent
8ca8b4f623
commit
73c6de364e
17 files changed
+74
-42
No files matched your search
@@ -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
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ error-log-*.txt
|
||||
|
||||
# Project
|
||||
dist/
|
||||
dist-web/
|
||||
dist-cli-web/
|
||||
dist-web-wasm/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
|
||||
@@ -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。只有明确绑定业务语境、无法自然复用的文案才放到模块命名空间。
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
+2
-1
@@ -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 # 文档站
|
||||
```
|
||||
|
||||
@@ -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",
|
||||
|
||||
+2
-2
@@ -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')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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<string, string>,
|
||||
).toString()
|
||||
const targetUrl = queryString
|
||||
? `${PROXY_TARGET}/${subPath}?${queryString}`
|
||||
: `${PROXY_TARGET}/${subPath}`
|
||||
const queryString = new URLSearchParams(request.query as Record<string, string>).toString()
|
||||
const targetUrl = queryString ? `${PROXY_TARGET}/${subPath}?${queryString}` : `${PROXY_TARGET}/${subPath}`
|
||||
|
||||
try {
|
||||
const upstream = await fetch(targetUrl, {
|
||||
|
||||
@@ -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 写操作。
|
||||
|
||||
@@ -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.
|
||||
|
||||
+2
-2
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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' },
|
||||
]
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ const SKIP_DIRS = new Set([
|
||||
'.docs',
|
||||
'node_modules',
|
||||
'dist',
|
||||
'dist-web',
|
||||
'dist-cli-web',
|
||||
'dist-web-wasm',
|
||||
'out',
|
||||
'build',
|
||||
'coverage',
|
||||
|
||||
@@ -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,
|
||||
Reference in new issue
Block a user