文件系统
文件系统
工作区是一个目录,其 src/ 中存放 + 文件。每个 + 文件都是一个编译器角色:它的默认导出是传给 @norbital-ai/bolt 中该角色声明函数的一个字面量。名称来自路径,从不来自文件内容。不带 + 的文件是普通模块,可以放在任何位置。
目录树
<workspace>/
├── package.json
├── tsconfig.json extends ./.norbital/tsconfig.json
├── norbital.template.json catalogue metadata (templates only)
├── assets/ thumbnail.svg + runtime assets shipped in the artifact
├── seed/ <collection>.json[.gz] rows, assets.json + assets/**, optional seed.ts
├── tests/ bolt test (@effect/vitest + @norbital-ai/bolt/test)
├── .norbital/ generated, never edited
└── src/
├── +workspace.ts tz, locale, currency, app order, AI model classes, connections
├── +secrets.ts secret names: OAuth clients, MCP settings; values set at runtime
├── data/
│ ├── +relationship.ts every relationship
│ ├── model/+<model>.model.ts storage: fields, kinds, constraints, state
│ ├── custom_field/<field>/ +definition.ts, +renderer.svelte
│ └── collection/<model>/ the exposed portion of model <model>
│ ├── +collection.ts reads, writes, the transform, queries, actions, notifications
│ ├── +representation.svelte optional record view (RecordShell otherwise)
│ └── +pipeline.ts optional import / export
├── access_control/
│ ├── team/+team.ts team names → the policies each holds
│ └── policy/+<name>.policy.ts grants, embedded approvals, capabilities, limits
├── integration/<name>/
│ ├── +integration.ts a driver, configuration, credential, feeds, outbound
│ └── +integration.svelte optional setup screen
├── agent/
│ ├── +agent.md in-app agent brief (staff)
│ ├── +agent.external.md brief for external members
│ ├── envoy/+<id>.envoy.ts an agent role a conversation instance deploys
│ ├── skill/+<name>.skill.md an agent skill
│ └── mcp/+<name>.mcp.ts an MCP server the agent may use
├── automation/+<name>.automation.ts cron / event / webhook work and its I/O
├── app/
│ ├── <app>/+app.ts title, icon, audience, pages in nav order
│ ├── <app>/+<page>.page.svelte one page per file
│ └── <group>/+group.ts a nav group; its apps live in its folder
├── i18n/
│ ├── +messages.ts base locale keys
│ └── +<locale>.messages.ts other locales (keys ⊆ the base)
└── lib/ ordinary modules (no roles) 命名规则:
- 如果文件夹已经说明了是什么,文件就是
+<role>.<ext>:data/collection/orders/+collection.ts。 - 否则文件是
+<name>.<role>.<ext>:access_control/policy/+sales_rep.policy.ts。 - 文件夹名使用单数,名称须匹配
^[a-z][a-z0-9_]*$。 - 未知的
+文件、放错文件夹的角色,或没有对应data/model/+<m>.model.ts的data/collection/<m>/文件夹,都是构建错误,其帮助信息会打印角色表。
角色
每个角色有一个声明函数。collection、app、group、integration 与 pipeline 先接收自己的名称;其余只接收字面量。代码体在声明之后以独立语句附加(c.transform(…)、c.query(…)、c.action(…)、a.run(…)),从不写在字面量内部。
| 角色 | src/ 下的路径 | 默认导出 | 指南 |
|---|---|---|---|
| 工作区设置 | +workspace.ts | workspace({…}) | 快速入门 |
| 密钥 | +secrets.ts | secrets({…}) | 集成 |
| 模型 | data/model/+<m>.model.ts | model({…}) | 集合 |
| 关系 | data/+relationship.ts | relationship({…}) | 集合 |
| 自定义字段 | data/custom_field/<f>/+definition.ts | customField({…}) | 集合 |
| 自定义字段渲染器 | data/custom_field/<f>/+renderer.svelte | { view: CustomFieldView } | UI 组件 |
| 集合 | data/collection/<m>/+collection.ts | collection('<m>', {…}) | 写入契约 |
| 记录视图 | data/collection/<m>/+representation.svelte | { view: RecordView<'<m>'> } | UI 组件 |
| 集成 | integration/<n>/+integration.ts | integration('<n>', {…}) | 集成 |
| 集成设置 | integration/<n>/+integration.svelte | Svelte 组件 | 集成 |
| 流水线 | data/collection/<m>/+pipeline.ts | pipeline('<m>', {…}) | 流水线 |
| 团队 | access_control/team/+team.ts | team({…}) | 策略 |
| 策略 | access_control/policy/+<p>.policy.ts | policy({…}) | 策略 |
| Agent 简报 | agent/+agent.md, agent/+agent.external.md | Markdown | Norbius |
| Agent 技能 | agent/skill/+<s>.skill.md | 带 description: frontmatter 的 Markdown | Norbius |
| MCP 服务器 | agent/mcp/+<n>.mcp.ts | mcp({…}) | Norbius |
| Envoy | agent/envoy/+<e>.envoy.ts | envoy({…}) | Envoy |
| 自动化 | automation/+<a>.automation.ts | automation({…}) | 自动化 |
| 应用 | app/<a>/+app.ts | app('<a>', {…}) | 应用 |
| 页面 | app/<a>/+<p>.page.svelte | Svelte 组件 | 应用 |
| 导航分组 | app/<g>/+group.ts | group('<g>', {…}) | 应用 |
| 消息 | i18n/+messages.ts, i18n/+<locale>.messages.ts | messages({…}) | 国际化 |
生成的文件
.norbital/
├── names.ts, names.client.ts # the names index: every path-derived name → typeof its declaration
├── tsconfig.json # the authored tsconfig.json only extends this
├── representations.check.ts
├── artifact/ # bolt build output
├── seed/ # the base (and sample) seed packs
└── cache/** 编译器写出 .norbital/;其中的内容既不编辑也不提交。名称索引把每个由路径得出的名称映射到其声明的 typeof,因此其他每个文件(包括每个页面)都由声明获得类型。只有在添加、重命名或删除角色文件时才会重写它。
命令
bolt check # every error of every stage in one run
bolt build # check + .norbital/artifact/ + seed packs
bolt test # @effect/vitest with TestWorkspace.layer
bolt dev # the author loop: PGlite, live reload, a dev mail sink
bolt start # the self-host server (bolt-server) 五个命令,一切正常时以 0 退出,作者错误以 1 退出,工具故障以 2 退出。bolt build 是宿主运行的唯一关卡。
清单即数据库结构
没有迁移文件。宿主将已应用的数据库结构与清单比较,并在一个事务中应用一个计划。计划无法区分重命名与“删除再添加”,因此重命名字段或模型会丢失数据:先添加新字段并回填,再在之后的变更中删除旧字段。
哪些不是角色
编写接口是封闭的:上表之外的一切都不存在,以下路径也不会被读取——src/collections/、src/datatypes/、src/functions/、src/apps/、src/+agents.md、src/access/policies/、src/capabilities/、+anonymous_limits.ts、vite.config.ts 与 .norbital/migrations/。服务器函数就是集合的查询与操作,密钥名在 +secrets.ts 中声明,Agent 工具就是集合的读取、查询与操作。