从源代码构建
AISCouncil 是一个由 src/ 中 80 个模块化源文件组装而成的单文件 HTML 应用程序。本指南涵盖完整的构建管道,从 TypeScript 编译到最终组装。
前提条件
- Node.js 20+ 和 npm
- Bash(Linux/macOS,或 Windows 上的 WSL)
- Python 3.10+(仅用于注册表验证)
设置
克隆仓库并安装开发依赖:
git clone https://github.com/nicholasgasior/bcz.git
cd bcz
npm install
这会安装:
- esbuild —— TypeScript 类型剥离(无捆绑)
- vitest + jsdom —— 测试框架
- typescript —— 类型检查(
tsc --noEmit)
这些仅是开发依赖。node_modules 中的任何内容都不会发送到浏览器。生产输出是一个零外部依赖的单个 index.html。
构建步骤
1. TypeScript 编译
如果您在 modules/*/index.ts 中有 TypeScript 模块,请先编译:
npm run build:ts
或编译单个模块:
node scripts/compile-ts.js grid
如果安装了 esbuild,build.sh 会自动运行 TypeScript 编译。如果您只想编译而不组装,只需手动运行 npm run build:ts。
2. 组装
将所有 80 个源部分串联成 index.html:
./build.sh
输出:
Compiling TypeScript modules...
grid: modules/grid/index.ts -> src/grid.js
Compiled 1 module.
Built index.html (15432 lines, 982451 bytes) from 80 parts
3. 验证
检查 src/ 是否与当前 index.html 匹配而不覆盖:
./build.sh --check
匹配时输出:
OK: src/ matches index.html (80 parts)
不匹配时输出:
MISMATCH: src/ does not match index.html
TypeScript 管道
TypeScript 管道是一个轻量级类型剥离系统,而不是打包器。
工作原理
对于 modules/*/ 中的每个模块:
- 源代码:
modules/{name}/index.ts—— 带类型的 TypeScript 源码 - 包装器:
modules/{name}/wrapper.txt—— 带有{CODE}和{Name}占位符的 IIFE 模板 - 输出:
src/{name}.js—— 准备好串联的<script>块
scripts/compile-ts.js 脚本:
- 读取 TypeScript 源码
- 调用
esbuild.transformSync()剥离类型(无捆绑、无格式转换) - 删除任何
import/export语句(模块被包装在 IIFE 中) - 缩进代码并将其插入包装器模板
- 如果代码定义了
__exports,追加return __exports;
包装器模板
每个 TypeScript 模块都有一个 wrapper.txt,定义 <script> 外壳:
<script>
// ============================================================================
// MODULE: AIS.{Name} (compiled from TypeScript)
// ============================================================================
if (!AIS.{Name}) AIS.lazy('{Name}', function() {
'use strict';
{CODE}
});
</script>
{Name} 占位符被替换为大写的模块名称(例如 grid 变为 Grid)。{CODE} 占位符被替换为类型剥离、缩进的源码。
编写 TypeScript 模块
为模块的公共 API 使用 var __exports = {...} 模式:
// modules/mymodule/index.ts
interface MyConfig {
name: string;
value: number;
}
function doSomething(config: MyConfig): string {
return config.name + ": " + config.value;
}
var __exports = {
doSomething,
};
不要使用裸 return 语句 —— 它们会导致 tsc --noEmit 错误。使用 var __exports = {...},compile-ts.js 会自动追加 return __exports;。
不要在 esbuild 选项中设置 format: 'esm'。这会导致破坏 IIFE 模式的 CommonJS 包装。编译脚本故意省略 format 选项。
类型检查
在仅检查模式下运行 TypeScript 编译器(无输出):
npm run check
这使用项目 tsconfig.json 运行 tsc --noEmit。类型定义位于:
core-types/index.d.ts—— 完整的AIS命名空间类型core-types/grid.d.ts—— Grid 特定类型
源文件顺序
build.sh 中的文件顺序至关重要。PARTS 数组定义了确切的串联序列:
Shell (HTML/CSS/DOM):
shell-head.html # DOCTYPE、meta 标签、早期重定向
shell-style.html # 无类 CSS 样式
shell-body.html # HTML body、布局、对话框、表单
Core Modules (single <script> block):
core-boot.js # <script> 标签、AIS 命名空间、事件总线、延迟加载器
core-auth-main.js # OAuth、Google 登录
core-auth-local.js # 本地账户、设备密码
core-auth-init.js # 身份验证 UI 绑定、初始化、导出
core-billing.js # 订阅级别、试用
core-ads.js # 静态广告系统
core-codec.js # Base80、VLQ、压缩
core-storage.js # IndexedDB + SQLite
core-providers.js # SSE 工厂、注册 API
core-providers-builtin.js # 内置提供商定义
core-ui.js # DOM 工具、markdown、toast
core-session.js # 机器人会话 CRUD
core-chat.js # 消息、流式传输
core-config.js # 机器人配置面板
core-app-botlist.js # 机器人列表、上下文菜单
core-app-switch.js # switchBot、createBot、超级菜单
core-app-events.js # bindEvents、委员会 UI
core-app-search.js # 搜索、导出、导入、插件
core-app-init.js # 委员会助手、初始化、启动
core-end.js # </script>
WASM-Replaceable (each in own <script>):
registry.js, grid.js, council.js, wizard.js,
vision.js, memory.js, imagegen.js, tools.js,
reminders.js, themes.js, templates-registry.js,
model-picker.js
Infrastructure:
kernel-bootstrap.html, moduleloader.js, plugins.js,
mcp.js, channels-*.js, sandbox.js, publish.js,
perf.js, p2p.js, profiles.js, cron.js
Platform:
settings-main.js, i18n.js, miniprograms.js,
docs.js, billing-ui.js, pwa.js
Tail:
shell-bottom.js # 欢迎屏幕处理程序、结束标签
不要重新排列文件顺序。核心模块共享一个由 core-boot.js 打开并由 core-end.js 关闭的单个 <script> 块。在其中插入包含 </script> 的文件会破坏构建。
运行测试
测试套件使用带有 jsdom 环境的 Vitest。
# 运行所有测试一次
npm test
# 在监视模式下运行测试(文件更改时重新运行)
npm run test:watch
# 仅运行 TypeScript 模块测试
npm run test:modules
# 运行特定测试套件
npm run test:settings
Worker 测试在 worker/ 目录中单独运行:
cd worker
npm test
开发工作流程
标准的编辑-构建-测试循环:
# 1. 编辑源文件
$EDITOR src/core-chat.js
# 2. 组装成 index.html
./build.sh
# 3. 在浏览器中测试
# 打开 index.html 或使用本地服务器
# 4. 运行测试
npm test
# 5. 验证构建完整性
./build.sh --check
对于 TypeScript 模块:
# 1. 编辑 TypeScript 源码
$EDITOR modules/grid/index.ts
# 2. 类型检查
npm run check
# 3. 编译 + 组装(build.sh 两者都做)
./build.sh
# 4. 测试
npm run test:modules
添加新模块
要添加新的延迟加载模块:
- 使用标准模式创建
src/mymodule.js:
<script>
if (!AIS.MyModule)
AIS.lazy("MyModule", function () {
"use strict";
function doWork() {
/* ... */
}
return { doWork };
});
</script>
-
将文件添加到
build.sh中的PARTS数组的适当位置。 -
运行
./build.sh验证它正确组装。
要添加新的 TypeScript 模块:
- 创建
modules/mymodule/index.ts作为源代码。 - 创建
modules/mymodule/wrapper.txt作为 IIFE 模板(从现有模块如modules/grid/wrapper.txt复制)。 - 可选地将类型定义添加到
core-types/index.d.ts。 - 将
src/mymodule.js添加到build.sh中的PARTS数组。 - 运行
./build.sh。
注册表验证
模型和包注册表有验证脚本:
# 验证模型注册表
python3 registry/validate.py
# 验证包注册表
python3 registry/validate.py packages
# 验证单个清单
python3 registry/validate.py manifest examples/hello-world/manifest.json
WASM 内核(可选)
WASM 内核是可选组件。构建它需要 Zig 0.14.0+:
cd kernel
../tools/zig/zig build # 输出 zig-out/bin/kernel.wasm (~5.5 KB)
主应用程序不需要内核即可运行。所有内核功能都有 JavaScript 回退。