首页
/ 在 Twenty 上开发"应用":Hello World 示例工程与 twenty-sdk 开发全流程实战指南

在 Twenty 上开发"应用":Hello World 示例工程与 twenty-sdk 开发全流程实战指南

2026-09-07 23:01:06作者:秋阔奎Evelyn

导读:本文以 packages/twenty-apps/examples/hello-world 为骨架,系统讲解如何在 Twenty(面向 AI 的开源 Salesforce 替代品)之上,通过 twenty-sdk 构建一个可安装、可同步、可卸载的"Twenty 应用"。你将掌握 workspace 认证(remote)、yarn twenty dev 开发模式、用 dev:add 脚手架声明对象/字段/逻辑函数/前端组件/角色/视图等实体,以及用集成测试验证"应用安装成功"的完整闭环。

Twenty 的"应用(Application)"并非运行在 Twenty 内部的插件进程,而是一份用 TypeScript 声明业务实体、并通过 CLI 同步到目标 workspace 的代码包。Hello World 示例正是这样一个最小但五脏俱全的应用:它由 create-twenty-app 脚手架生成,既展示了 twenty-sdk 的实体声明语法,也内置了一套可执行的安装集成测试。本文会先带你完成"认证 → 开发 → 查看结果"的起步流程,再逐个拆解工程结构与底层实现。

一、起步:认证、开发模式与查看结果

1.1 认证你的 workspace

Hello World 工程通过 twenty CLI 与一个正在运行的 Twenty workspace 通信。第一步是把它"绑定"到本地开发环境:

yarn twenty remote:add --api-url http://localhost:2020 --as local

该命令将 http://localhost:2020 这个 API 地址注册为一个名为 local 的 remote(远端),twenty CLI 会把认证凭据写入本机配置目录(默认为 ~/.twenty)。从测试配置可以印证这一结构:setup-test.ts 会向 ~/.twenty/config.test.json 写入形如 { "remotes": { "local": { "apiUrl, apiKey } }, "defaultRemote": "local" } 的配置,说明 CLI 的认证体系以"命名 remote + 默认 remote"为核心。

1.2 启动开发模式

yarn twenty dev

该命令进入开发模式,它会一次性完成四件事:监听文件变更、执行构建、把应用声明同步到 workspace、自动生成类型化客户端(Typed Client,即 twenty-client-sdk)。也就是说,你在 src/ 中每新增或修改一个实体声明,CLI 都会自动把它推送到远端的 Twenty 实例。

1.3 查看结果

打开你的 Twenty 实例,进入 /settings/applications(应用管理)页面,即可看到当前 workspace 中已安装的应用。若 yarn twenty dev 同步成功,Hello World 应用会出现在应用列表中;其附带的视图、导航菜单项、逻辑函数等也会随之出现在对应位置。

二、命令速查:Remotes 与应用生命周期

运行 yarn twenty help 可随时查看全部命令。以下是本示例 README 中梳理的常用命令分类:

# Remotes & Authentication(远端与认证)
yarn twenty remote:add --api-url http://localhost:2020 --as local     # 认证并添加 Twenty 远端
yarn twenty remote:status         # 查看认证状态
yarn twenty remote:use            # 设置默认 remote
yarn twenty remote:list           # 列出所有已配置的 remote
yarn twenty remote:remove <name>  # 移除某个 remote

# Application(应用)
yarn twenty dev            # 启动开发模式(watch、build、sync、自动生成类型化客户端)
yarn twenty dev:add        # 脚手架:生成新实体(object、field、function、front-component、
                           #   role、view、navigation-menu-item)
yarn twenty dev:function:logs    # 流式查看逻辑函数运行日志
yarn twenty dev:function:exec    # 携带 JSON payload 执行某个逻辑函数
yarn twenty app:uninstall  # 从 workspace 卸载应用

几个值得注意的语义:

  • remote 多环境管理:通过 --as local 你可以命名多个 remote(如 stagingprod),再用 remote:use 切换默认目标;remote:list / remote:status 分别用于查看全部远端与当前认证状态。
  • dev:add 面向实体而非文件:它交互式地为你生成某一类实体的骨架文件,覆盖了 Twenty 应用支持的全部实体类型,无需手写样板。
  • app:uninstalldev 相对:负责把应用从目标 workspace 彻底移除,是发布/回滚流程中的关键操作。

上述命令的完整可用列表以 yarn twenty help 的输出为准。工程对运行时环境有明确要求,见 package.jsonnode^24.5.0yarn>=4.0.2(声明 packageManager: yarn@4.13.0),且推荐使用 yarn 而非 npm。

三、工程解剖:Hello World 应用由哪些"零件"组成

Hello World 应用的价值在于它展示了 Twenty 应用支持的全部实体类型。以 src/ 为根,目录即类型:

实体类型 目录 对应文件 作用
应用本身 src/ application-config.ts 声明应用的 universalIdentifier、显示名与默认角色
对象 src/objects example-object.ts 定义一个名为 exampleItem 的自定义对象
字段 src/fields example-field.ts 为对象补充额外字段
逻辑函数 src/logic-functions 见下 可被 HTTP 触发的服务端逻辑
前端组件 src/front-components hello-world.tsx React 组件,可嵌入记录页
角色 src/roles default-role.ts 定义默认权限角色
视图 src/views example-view.ts 面向对象的列表/看板视图
导航菜单项 src/navigation-menu-items example-navigation-menu-item.ts 把视图挂到左侧边栏
页面布局 src/page-layouts example-record-page-layout.ts 自定义记录详情页 tab 与 widget
Agent src/agents example-agent.ts 声明一个 AI Agent
Skill src/skills example-skill.ts 为 Agent 提供能力指令

所有实体统一由 twenty-sdk/define 暴露的 defineXxx 工厂函数声明,并以 ESM 默认导出。这套"声明式 + 统一标识符"的设计,是 CLI 能够把应用同步到 workspace 的基础。下面深入几类核心实体。

3.1 应用入口:universalIdentifier 是全局契约

application-config.ts 是应用的身份证:

import { defineApplication } from 'twenty-sdk/define';
import { DEFAULT_ROLE_UNIVERSAL_IDENTIFIER } from 'src/roles/default-role';

export const APPLICATION_UNIVERSAL_IDENTIFIER =
  'bb1decf6-dee5-43ef-b881-9799f97b02a8';

export default defineApplication({
  universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
  displayName: 'Hello world',
  description: '',
  defaultRoleUniversalIdentifier: DEFAULT_ROLE_UNIVERSAL_IDENTIFIER,
});

关键点:universalIdentifier 是一个 UUID v4(LLMS.md 特别强调所有生成的 UUID 必须是合法 v4,见下文第四节),它在应用的所有安装目标间保持稳定,也是集成测试定位"这个应用是否已安装"的匹配键。defaultRoleUniversalIdentifier 指向应用安装后需要默认存在的角色。

3.2 对象与字段:数据模型的声明

example-object.tsdefineObject 声明对象,并通过 fields 内联声明主字段:

export default defineObject({
  universalIdentifier: '47fd9bd9-392b-4d9f-9091-9a91b1edf519',
  nameSingular: 'exampleItem',
  namePlural: 'exampleItems',
  labelSingular: 'Example item',
  labelPlural: 'Example items',
  description: 'A sample custom object',
  icon: 'IconBox',
  labelIdentifierFieldMetadataUniversalIdentifier: NAME_FIELD_UNIVERSAL_IDENTIFIER,
  fields: [ /* { type: FieldType.TEXT, name: 'name', ... } */ ],
});

example-field.ts 展示了"独立字段"的写法——通过 objectUniversalIdentifier 把新字段(FieldType.NUMBERpriority)挂到已有对象上。这种拆分意味着:对象与字段可以分别声明、分别演进,多个应用甚至可以共享同一个对象的字段增量。

3.3 逻辑函数:可被 HTTP 触发的服务端逻辑

src/logic-functions 下有四类函数,恰好覆盖四种用法:

  • hello-world.ts:无参纯函数,返回 { message: 'Hello, World!' }httpRouteTriggerSettings 将其暴露为 GET /hello-world-logic-function,且 isAuthRequired: false(公开接口)。
  • create-hello-world-company.ts:在 handler 内实例化 CoreApiClient,通过类型化 mutation createCompany 真正向 workspace 写入一条名为 "Hello World" 的公司记录;对应 HTTP 路由为 POST /create-hello-world-companyisAuthRequired: true。这是"逻辑函数 = 内嵌 GraphQL 数据访问"的直接范例。
  • pre-install.ts:用 definePreInstallLogicFunction 声明,在应用安装前执行,用于准备工作(如环境校验),handler 可读取 InstallPayload.previousVersion 获知升级前版本。
  • post-install.ts:用 definePostInstallLogicFunction 声明,在应用安装后执行初始化/置数据。

三者共同定义了 Twenty 应用"安装 → 生效"的生命周期钩子。每个函数都显式声明 timeoutSeconds(普通函数 5、安装类钩子可高达 300),超出将被终止。开发时可借助 yarn twenty dev:function:logsyarn twenty dev:function:exec 分别查看日志与手动触发。

3.4 前端组件与页面布局:把 UI 嵌入 Twenty

hello-world.tsx 是一个普通 React 组件:进入页面时用 CoreApiClient 查询公司表第一行并渲染"公司名 + ID"。它通过 defineFrontComponent 包装成 Twenty 可识别的前端组件,随后 example-record-page-layout.ts 将其作为 FRONT_COMPONENT 类型的 widget,挂到某对象的记录详情页(PageLayoutTabLayoutMode.CANVAS 画布布局)上。

值得注意的约束(同样来自 LLMS.md):前端组件必须自适应 Twenty 给定的固定 widget 宽高,不要自带滚动条,除非它专门用于 Canvas 标签页场景。

3.5 角色、视图、导航菜单与 AI 实体

  • default-role.ts 声明默认角色:可读/可更新/可软删除全部对象记录,但禁止物理删除(canDestroyAllObjectRecords: false),为函数执行提供最小权限模型。
  • example-view.ts 声明视图 All example items,通过 objectUniversalIdentifier 绑定到自定义对象并配置列宽与可见性。
  • example-navigation-menu-item.ts 把该视图挂载为左侧边栏菜单项。注意:如果创建视图却不创建关联的 navigationMenuItem,视图会出现在左侧边栏上(这是 LLMS.md 明确列出、易被忽视的坑)。
  • example-agent.tsexample-skill.ts 展示了如何随应用一起分发 AI Agent 与其配套 Skill——这正是 Twenty "designed for AI" 的应用层体现。

四、随工程分发的"LLM 须知"(LLMS.md)

工程根目录还带有一份 LLMS.md,专门面向使用该工程的开发者或 AI 编程助手,其中浓缩了三条关键约束:

  1. UUID v4 要求:所有生成的 universalIdentifier 必须是合法 UUID v4。
  2. 视图必须与导航菜单项配对:只建 view、不建关联 navigationMenuItem,会导致该视图直接出现在左侧边栏。
  3. 前端组件不要自带滚动:组件应自适应固定 widget 的宽高;Canvas tab 场景除外。

此外它把 fixtures/rich-app 指认为"更丰富的应用示例"——当 Hello World 无法覆盖你的需求时,可进入该 fixture 查看更大规模的真实组织方式。完整的 Twenty 应用开发文档与更复杂示例位于仓库 twenty-apps 目录体系内。

五、集成测试:验证"应用确实装上了"

Hello World 自带一个端到端的安装集成测试,脚本入口是 app-install.integration-test.ts

# 确保一个 Twenty server 正在运行
yarn test

README 提示需先启动 Twenty server;实际端口以 vitest.config.tsTWENTY_API_URL 的默认值为准(默认 http://localhost:2020),可用同名环境变量覆盖。若使用远端 workspace,请把目标地址与该 workspace 的 API Key 一并注入。

5.1 测试做了什么

测试在 beforeAll 中顺序执行三段流水线(均来自 twenty-sdk/cli 编程接口,而非命令行):

  1. appBuild:把 process.cwd()(即应用目录)构建成 tarball(tarball: true),产出可分发的安装包;
  2. appDeploy:把 tarball 上传部署到 workspace;
  3. appInstall:执行安装(此刻 pre-install / post-install 逻辑函数被触发)。

随后主断言通过 MetadataApiClient(来自 twenty-client-sdk/metadata)查询元数据:

const result = await metadataClient.query({
  findManyApplications: {
    id: true,
    name: true,
    universalIdentifier: true,
  },
});
const installedApp = result.findManyApplications.find(
  (application) => application.universalIdentifier === APPLICATION_UNIVERSAL_IDENTIFIER,
);
expect(installedApp).toBeDefined();

其核心逻辑是:APPLICATION_UNIVERSAL_IDENTIFIER 在已安装应用列表中精确匹配,命中即证明应用真实安装成功。afterAll 阶段则调用 appUninstall 做清理,保证测试可重复执行、不留残留。

5.2 测试配置的注入方式

setup-test.ts 是 vitest 的 setup 文件,它负责三件事:

  • 从环境变量读取 TWENTY_API_URL / TWENTY_API_KEY,缺失时直接抛错并提示先启动本地 server 或在 vitest env 中配置;
  • 请求 {apiUrl}/healthz 做可达性探测,server 未启动则给出明确报错;
  • 把凭据写为独立测试配置 ~/.twenty/config.test.json(与开发配置隔离),并把 token 注入 TWENTY_APP_ACCESS_TOKEN

vitest.config.ts 还设置了两个关键参数:testTimeout / hookTimeout 均为 120_000(构建 + 部署 + 安装的完整流程耗时较长,普通超时会误杀);include 只匹配 src/**/*.integration-test.ts,确保单元与集成测试互不干扰;其 env 段内置了一个开发用默认 API Key 作为兜底(生产环境务必用环境变量覆盖)。

六、总结与下一步

通过 Hello World 示例,一条清晰的 Twenty 应用开发路径已经成型:

  1. 认证绑定yarn twenty remote:add --api-url <workspace> --as <name>
  2. 实体声明:用 defineObject / defineLogicFunction / defineFrontComponent / defineView 等描述业务;
  3. 持续同步yarn twenty dev 监听变更并自动推送,配合 dev:add 快速生成新实体骨架;
  4. 验证交付yarn test 跑通"构建 → 部署 → 安装 → 元数据断言 → 卸载"的闭环。

从工程内部看,这套体系的根基在于两点:其一是统一的 universalIdentifier(UUID v4)契约,让 CLI、元数据客户端与集成测试能用同一个键对齐声明与真实 workspace 状态;其二是 twenty-sdk 的声明式 API + twenty-client-sdk 的类型化 GraphQL 客户端,让对象模型与数据访问都获得编译期类型保障。若想继续深入,建议阅读仓库中的 rich-app fixture(更完整的实体组合)、twenty-sdk 源码definecli 的具体实现),以及 twenty-client-sdkmetadata/generate 目录(理解类型化客户端如何由 GraphQL schema 生成)。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391