关于 objectstack-platform
objectstack-platform 是介绍 ObjectStack 项目配置、运行时设置和插件开发的技能,指导开发者使用 `defineStack()`、验证元数据、管理构建产物、选择驱动及了解内核和 HTTP 边界。
主要功能
- 01使用 defineStack() 配置堆栈
- 02验证元数据及交叉引用
- 03构建并组织项目脚手架
- 04选择和连接存储驱动
- 05开发插件及内核集成
- 06通过 Hono 集成 ObjectStack HTTP 路由
适用场景
- 01初始化 ObjectStack 项目
- 02运行前检查堆栈元数据
- 03为开发或生产环境选择驱动
- 04将 ObjectStack 路由嵌入 Hono 应用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ objectstack-ai/ objectstack --skill objectstack-platform安装后在 Claude Code 中输入/objectstack-platform确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/config-plugins-ops.jsonreferences/_index.mdbootstrap.mdplugin-hooks.mdplugin-development.mdoperations.mdrules/plugin-lifecycle.mdservice-registry.md详细介绍
Platform — ObjectStack 引导与插件系统
围绕 defineStack() / 内核表面的两个关注点:项目设置
(objectstack.config.ts、驱动程序、启动序列)和插件
开发(插件、服务、内核钩子 / 事件处理器、
ObjectKernel 与 LiteKernel)。
defineStack() — 核心配置
objectstack.config.ts 是每个项目的唯一入口。
它调用 defineStack() 来声明所有元数据。
→ 已原样移至 references/bootstrap.md § 最小示例。
完整配置参考
defineStack() 接受一个 ObjectStackDefinitionInput,其顶层键
包括 manifest、packages、objects、objectExtensions、picklists、
picklistExtensions、views、apps、pages、dashboards、reports、
datasets、actions、flows、jobs、
emailTemplates、docs、books、positions、permissions、
capabilities、sharingRules、apis、webhooks、api、server、
agents、tools、skills、hooks、functions、mappings、
analyticsCubes、connectors、data(种子数据)、datasources、
datasourceMapping、translations、i18n、plugins、devPlugins、
devHint、devLogins、requires、tiers、onEnable、appName、email、sms。
这里有意不提供顶层 workflows 或 approvals 集合:
审批是通过包含 Approval 节点的流程来编写的(ADR-0019),记录
状态机则是每个对象上的 state_machine 验证规则
(ADR-0020)。roles: 或 policies: 这样的虚构键不会被静默忽略——顶层会拒绝它,导致堆栈加载失败:
defineStack validation failed (1 issue):
✗ (root): Unrecognized key(s) on this stack definition: `policies`. …
未声明的键会根据所在表面进行处理,应牢记以下两种处理方式:
- 拒绝——
defineStack()的顶层、每个objects[]条目 (ObjectSchema)以及每个字段(FieldSchema)。解析会抛出错误,指出 表面和有问题的键。TypeScript 会更早拒绝该字面量, 并报出TS2353: Object literal may only specify known properties。 - 发出警告后丢弃——形状尚未封闭的编写表面(
connectors就是其中之一)。defineStack()会在解析前打印警告,该值也不会通过解析:defineStack: connectors.stripe.bogusKey: 'bogusKey' is not a declared connector key, so its value is dropped at load.应将这些情况视为即将成为错误的问题——封闭其余形状是已安排的迁移,因此今天只会触发警告的键,预计之后会被拒绝。
确切的 Zod 形状——包括哪些键是可选的,以及集合项采用哪些类型——请参阅
node_modules/@objectstack/spec/src/stack.zod.ts
(ObjectStackDefinitionSchema)。每个集合的项形状都位于其各自的域文件夹中(data/object.zod.ts、ui/view.zod.ts,等等)。
→ 已原样移至 references/bootstrap.md § 映射格式(键 → 名称)· § Barrel 导入模式。
严格验证
defineStack() 默认会进行验证(strict: true):
- Zod 模式——字段名称、类型、枚举
- 交叉引用——视图 / 操作 / 流程引用已定义的对象
- 种子数据——数据集对象存在于定义中
要禁用(高级用法——例如由其他插件提供对象):
export default defineStack(config, { strict: false });
编译产物与运行时元数据边界
ObjectStack 运行时元数据必须来自本地开发期间的源文件,或来自编译产物。不要将环境运行时配置为通过业务数据库读取或写入元数据。
# the CLI ships an `os` binary; `objectstack` is an alias for it
objectstack compile
# -> dist/objectstack.json
OS_ARTIFACT_PATH=./dist/objectstack.json objectstack dev
运行时经验法则:
| 场景 | 元数据来源 | 数据库用途 |
|---|---|---|
| 本地开发 | TS 文件或 dist/objectstack.json | 仅存储业务行 |
| 生产运行时 | Artifact API 响应 | 仅存储业务行 |
| 控制平面 | 元数据存储中的已发布 JSON | 环境版本、历史记录、覆盖项 |
生成 objectstack.config.ts 时,对象名称要简短并使用 snake_case;绝不要设置 tableName,也不要将 sys_metadata 对象添加到环境运行时清单中。
清单参考
每个栈都需要一个 manifest 来标识自己在生态系统中的身份:
manifest: {
id: 'com.example.crm', // Reverse domain unique ID
version: '1.0.0', // Semver
type: 'app', // app | plugin | driver | module | ...
name: 'Acme CRM', // Human-readable display name
description: 'CRM system', // Optional description
engines: { protocol: '^17' }, // Metadata-protocol major this app targets
}
manifest.engines.protocol: 应用所依据的元数据协议主版本。create-objectstack 会将其写入生成的每个项目中(所有三个示例应用也都包含此项)。运行时会在加载任何内容之前检查它,因此,超出版本范围的运行时会在边界处拒绝该应用,并给出准确的迁移命令,而不会等到之后才崩溃。只有在有意迁移到新的协议主版本时才更改它,绝不要为了消除版本不匹配而更改。
对象命名: 对象的 name 是规范标识符,且与物理表名相同。任何域前缀都要直接嵌入名称中(例如 name: 'crm_account');对象级别的 namespace 字段已废弃(ADR-0129 D3),加载时会被拒绝。
manifest.namespace(ADR-0048): 可选,但设置后会强制执行。当包声明 manifest.namespace: 'crm' 时,每个 object.name 都必须以 crm_ 开头,否则 defineStack 会报错(@objectstack/spec 中的 validateNamespacePrefix);旧版 <ns>__<short> 双下划线格式会被拒绝,而以 sys_ 为前缀的名称属于平台保留名称,不受此限制。命名空间也是包所有权标识键——如果安装的两个包都声明 crm,就会失败并返回 NamespaceConflictError(使用 OS_METADATA_COLLISION=warn 可将其降级为警告)。设置命名空间后,os lint 还会对名称未带命名空间前缀的 UI/自动化项(app、page、dashboard、flow、action、report、dataset)发出非致命的 naming/namespace-prefix 警告。
应用 / 平台边界
ObjectStack 应用是业务功能的简化实现:依据平台规范编写元数据,在这些技能的指导下完成,并使用 os 命令检查(验证你的工作)。绝不要重建平台负责的内容。
- **业务功能属于应用;能力属于平台。**缺少默认值、诊断错误、规范拒绝的结构——应在上游修复。在那里提出问题;不要在此处补偿。
- 能否由只掌握元数据、不了解这家公司的事物来编写? 不能——它编码了这家公司的特定判断(折扣上限、案件分配对象、赢单/输单的登记方式)⇒ 属于应用。能——它只检查元数据是否自洽(引用完整性、翻译覆盖率、视图成员列表、共享规则覆盖率、CRUD 往返测试、针对每个已声明职位的 RLS 探测)⇒ 属于平台。
- 平台缺陷意味着等待平台修复。不要编写防御性代码,不要容忍不符合规范的结构,不要手写谓词重新实现平台规则,也绝不要“先合入能做的一半”——这会放弃契约优先的选择,并导致决策只执行了一半。将阻塞情况记录在平台问题中,使其可供机器识别;恢复工作前,确认你锁定的版本包含该修复(上游已合并 ≠ 你锁定的版本已包含),并重新运行该缺陷对应的复现步骤。
- 不合适的平台默认值应该修正默认值,而不是在每个调用点绕开它。
模板
blank 是 create-objectstack 提供的唯一模板,也是默认模板:
- 随
create-objectstack一起打包,无需从网络获取 - 包含一个示例对象;
requires: ['automation', 'triggers'],以及plugins:中的三个通用连接器执行器;还有八个生成器聚合文件(objects、views、actions、flows、dashboards、apps、skills、picklists),每个src/*/index.ts都通过exportsOf交给与其名称对应的堆栈键。脚手架生成的AGENTS.md说明了如何向其中添加内容。文件中不包含 memory 驱动和 Hono 服务 — CLI 会在启动时自动注册两者 - 提供干净的起点,便于根据本技能所述扩展元数据
五个已弃用的远程模板(todo、compliance、content、contracts、procurement)会按名称拒绝。
→ 原文已移至 references/bootstrap.md § 脚手架命令。
项目结构约定
每个 ObjectStack 项目都遵循以下目录结构:
my-app/
├── objectstack.config.ts # ← THE entry point — defineStack()
├── package.json
├── tsconfig.json
└── src/
├── objects/ # Business object definitions
│ ├── task.object.ts # → exports a single object
│ └── index.ts # → barrel: export * from './task.object'
├── views/ # Optional: UI view definitions
│ ├── task.view.ts
│ └── index.ts
├── apps/ # Optional: app definitions (nav, pages)
│ ├── main.app.ts
│ └── index.ts
├── flows/ # Optional: automation flows
│ ├── task.flow.ts
│ └── index.ts
├── actions/ # Optional: action definitions
│ ├── task.action.ts
│ └── index.ts
├── dashboards/ # Optional: dashboards
├── reports/ # Optional: reports
├── datasets/ # Optional: analytics datasets
├── i18n/ # Optional: translation bundles
└── handlers/ # Optional: runtime hook handlers
命名约定
| 概念 | 约定 | 示例 |
|---|---|---|
| 文件名 | {name}.{type}.ts | task.object.ts、main.app.ts |
| 机器名称 | snake_case | project_task、first_name |
| 配置键 | camelCase | maxLength、defaultValue |
| 聚合导出 | Object.values(imported) | objects: Object.values(objects) |
驱动选择指南
驱动是存储层。请根据环境选择:
| 驱动 | 包 | 适用场景 | 备注 |
|---|---|---|---|
| Memory | @objectstack/driver-memory | 开发、测试、原型设计 | InMemoryDriver — 重启后数据会丢失 |
| SQL | @objectstack/driver-sql | 生产环境(PostgreSQL、MySQL、SQLite) | SqlDriver — 底层使用 Knex.js(pg / mysql / better-sqlite3 客户端) |
| MongoDB | @objectstack/driver-mongodb | 生产环境(文档存储) | MongoDBDriver |
| SQLite WASM | @objectstack/driver-sqlite-wasm | 浏览器 / WebContainer | SqliteWasmDriver — 进程内运行,无需服务端 |
| Turso | @objectstack/driver-turso | 边缘端、无服务器、多租户 | 仅限 Cloud / EE — 随 ObjectStack 云端 / 企业版发行版提供,不属于开源框架。开源核心 CLI 能识别 libsql:// URL,但会明确报错(UnsupportedDriverError) |
驱动接线 — 通常无需手动操作
在 os dev / os serve / os start 下,CLI 会根据数据库 URL 自行解析驱动,并为你注册 DriverPlugin(开发环境使用 memory,生产环境使用 SQL)。不要将驱动放入配置的 plugins: 数组:没有示例应用这样做,而且 plugins: 键用于 CLI 无法推断的插件(连接器执行器、自定义插件)。通过设置数据库 URL 来选择驱动,而不是编写代码。
只有在你负责运行时(通过 Runtime / ObjectKernel 嵌入,或直接启动内核的测试)时,才自行构造 DriverPlugin:
import { DriverPlugin } from '@objectstack/runtime';
import { SqlDriver } from '@objectstack/driver-sql';
new DriverPlugin(new SqlDriver({ client: 'pg', connection: process.env.DATABASE_URL }));
HTTP 层(Hono)
有两个包:
| 包 | 导出项 | 使用场景 |
|---|---|---|
@objectstack/hono | createHonoApp({ kernel, prefix }) | 由你负责服务端:将 ObjectStack 路由嵌入自己的 Hono 应用 / 部署目标。 |
@objectstack/plugin-hono-server | HonoServerPlugin | 由 ObjectStack 负责服务端:这是一个用于托管 Hono 应用并打开监听套接字的内核插件(os dev / os serve 注册的就是它)。 |
不存在 @objectstack/adapter-* 包(没有 adapter-express / -fastify / -nextjs / -nuxt / -nestjs / -sveltekit)。要集成其他框架,请挂载 Hono 应用(符合 Web 标准的 fetch 处理程序),或自行调用分发器。
用法模式(Hono)
import { createHonoApp } from '@objectstack/hono';
// prefix defaults to '/api'.
export default createHonoApp({ kernel });
⚠️ prefix 不会改变 auth 的挂载位置。 /auth/* 挂载路径遵循 auth
服务的 basePath(AuthPlugin 默认值为 /api/v1/auth),而不是 prefix —
createHonoApp({ kernel }) 会将请求路由到 better-auth 的 /api/v1/auth/*。如果 prefix
不在该 basePath 下,启动时会拒绝,并指出这两个值。
架构
createHonoApp 会创建一个 HttpDispatcher,为 auth 和发现功能挂载显式
路由,并将其他所有请求委派给它 — 因此,添加到 HttpDispatcher 的新
路由会自动生效。
运行时启动顺序
了解 ObjectStack 的启动方式有助于调试和自定义:
objectstack.config.ts
└── defineStack({ manifest, objects, views, ... })
│
▼
CLI: `os serve` / `os dev`
1. Load .env files (NODE_ENV-based)
2. Dynamic import of config file
3. Create Runtime + ObjectKernel
4. Auto-detect and register plugins (in this order):
├── ObjectQLPlugin (if objects defined)
├── DriverPlugin (memory in dev, SQL in prod)
├── AppPlugin (loads the defineStack bundle)
├── I18nServicePlugin (if translations/i18n defined)
├── HonoServerPlugin (registered BEFORE AuthPlugin — the server must
│ exist for plugins that mount routes during init/start)
├── AuthPlugin
├── Split platform-app plugins (ADR-0048, optional/best-effort, after AuthPlugin):
│ @objectstack/setup → createSetupAppPlugin (first-run wizard)
│ @objectstack/account → createAccountAppPlugin
│ (@objectstack/studio is intentionally NOT default-loaded — the
│ Console, mounted at /_console/ by `--ui`, ships its own Studio
│ surface at /_console/studio/…)
├── RESTPlugin (auto-generated API)
├── DispatcherPlugin
└── AIServicePlugin (cloud / EE only — reverse-mounted by a cloud host; absent in the open framework per cloud ADR-0025)
5. Runtime.start() → init + start all plugins
6. Server listens on the resolved port (see "Ports & networking" in Part 3)
端口解析(os dev 和 os start 都会转到 os serve):
--port 标志 › $OS_PORT › $PORT › 3000。发生冲突时的行为
取决于运行模式 — 开发模式会尝试下一个空闲端口,生产模式则会明确报错。请参阅
端口与网络。
requires: — 哪些服务插件会启动
第 4 步的列表是固定核心。其他所有服务插件都需显式启用,而
堆栈根节点中的 requires: [...] 就是启用它们的方式。CLI 会通过
packages/cli/src/commands/serve.ts 中的 CAPABILITY_PROVIDERS 注册表展开每个
标记 — 其中有 19 个可选条目:
| 标记 | 提供方包 |
|---|---|
automation | @objectstack/service-automation — 工作流,以及任何声明式 connectors: 条目 |
analytics cache storage queue job messaging realtime settings sms | @objectstack/service- + 标记 |
marketplace | @objectstack/service-package |
audit email sharing approvals webhooks | @objectstack/plugin- + 标记 |
pinyin-search | @objectstack/plugin-pinyin-search |
mcp | @objectstack/mcp |
triggers | @objectstack/trigger-record-change,以及 trigger-schedule 和 trigger-api |
PLATFORM_CAPABILITY_TOKENS 中未列入该映射的标记,无法通过该映射
解析:
- 受层级限制 —
ai、ai-studio、i18n、ui、auth没有提供方条目;serve.tsrun()中的专用代码块会改为启用相应层级 (ai/ai-studio通过由意图驱动的 AI 代码块启用,其他三个通过 各自的层级代码块启用)。 - 企业版 / 云端 —
hierarchy-security没有开源版本的提供方, 随@objectstack/security-enterprise一同发布,并通过plugins[]加载;ai-seat和governance仅由云端的 objectos-runtime 解析。
权威列表是 PLATFORM_CAPABILITY_TOKENS
(@objectstack/spec,kernel/platform-capabilities.ts)— 未知标记会在编写时被
defineStack 拒绝,而不是在启动时拒绝。
以下五条规则会影响你的配置:
- 优先级:
requires›tiers›--preset› 内置默认值。plugins:中的 显式实例始终会覆盖能力解析。 - 声明即表示需要。 如果你声明的能力所对应的提供方包不存在,就会导致启动错误;平台自动注入的能力则会尽力启用(发出警告并继续)。
auth会隐含email。 Auth 回调(密码重置、邮箱验证、魔法链接、邀请)需要邮件服务,因此 CLI 会在需要auth时自动追加email。- 只要
plugins:中列有连接器,就保留automation— 连接器执行器会向其注册提供方工厂;没有它就无处注册,启动会失败。 - 将
triggers与job配对使用。 单独启用triggers只会启动记录变更触发器;计划触发器和相对时间触发器由 job 服务运行,因此没有job时,自动启动的计划工作流不会执行。
onEnable — 应用绑定运行时代码的位置
objectstack.config.ts 可以在默认堆栈旁导出 onEnable。
AppPlugin 会在启动期间调用它,并传入应用的实时运行句柄:这是声明式元数据进入命令式代码的唯一接口(注册操作处理程序、向作业提供数据句柄、预配夹具数据源)。三个示例应用都使用了它。
export const onEnable = async (ctx: { ql: { registerAction: (...a: unknown[]) => void } }) => {
registerTaskActionHandlers(ctx.ql);
};
→ 原样移至 references/bootstrap.md § 插件加载顺序很重要 · § 编程式引导(不使用 CLI)。
→ 原样移至 references/bootstrap.md § 多应用组合。
种子数据
堆栈的 data: 集合使用 defineSeed() 编写,该函数由 objectstack-data 提供——有关 externalId 匹配、env: 作用域,以及哪些键是派生键,请参阅该项目。尤其要注意,object 是从对象定义派生而来:切勿手动填写,也不要手写原始的 data: [{ object: … }] 字面量。
mode 决定种子运行如何处理已存在的行:
| 模式 | 行为 |
|---|---|
upsert(默认) | 根据 externalId 匹配结果插入或更新 |
insert | 始终插入(遇到重复项时失败) |
update | 仅更新找到的记录;忽略新记录 |
ignore | 不存在时插入,否则跳过 |
replace | ⚠️ 数据丢失 — 删除并重新插入所有记录 |
CLI 命令
日常命令见 第 3 部分 — 运维。 引导循环速查表:
npx create-objectstack my-app
cd my-app && npm install
os dev --ui # dev server + Console at /_console/ (auto-hops port if taken)
os validate # metadata cross-reference checks
os compile # produce dist/ artifact
os migrate plan # preview metadata↔DB schema drift (additive sync never alters existing columns)
os migrate apply # reconcile DB to metadata (loosening only; --allow-destructive for drops/tightenings)
PORT=8080 os start # production — pin the port explicitly (see Ports & networking)
→ 原样移至 references/bootstrap.md § 完整工作示例。
→ 第 2 部分 — 插件开发与内核扩展已原样移至 references/plugin-development.md(快速参考 — 详细规则 · ObjectKernel 与 LiteKernel · 插件接口 — 快速概览 · PluginContext API · 完整插件示例 · 使用插件 · 测试插件 · 知名插件名称与服务 · MetadataPlugin 运行时边界 · 健康监控 · 功能标志);其详细规则见 rules/plugin-lifecycle.md · rules/service-registry.md · references/plugin-hooks.md。
→ 第 3 部分 — 运维:CLI、测试、部署已原样移至 references/operations.md(日常循环命令 · 构建与运行时 · 验证工作 · 端口与网络 · 数据与迁移 · 环境与部署 · 发布插件 · 测试模式 · 部署目标 · 健康与可观测性 · 常见运维陷阱)。
参考资料
完整的 Zod 架构列表(含单行说明)见 references/_index.md——其中包含指向 node_modules/@objectstack/spec/src/ 的索引。请始终 Read 源码以确认准确的字段结构;不要依赖对属性名称的记忆。