
Twenty Apps 内嵌工作流开发指南安装即交付手动工作流并把逻辑函数接入工作流构建器【免费下载链接】twentyThe open alternative to Salesforce, designed for AI.项目地址: https://gitcode.com/GitHub_Trending/tw/twenty本文针对 Twenty Apps 开发体系整理一份工作流集成专项指南。核心主题是当一个 Twenty App 需要在安装时自动交付一个手动记录触发的工作流或希望某个逻辑函数能从工作流构建器中被调用时应该如何声明、编排与验证。读完本文你将掌握工作流在 Twenty 中的工作区记录心智模型、Workflow WorkflowVersion 的生命周期管理、definePostInstallLogicFunction与 workspace API 的配合方式、幂等与权限的红线以及本地调用与验证的完整流程。本指南的事实基础是 codex 插件开发文档 develop-app/workflows.md并辅以仓库内 SDK 定义源码、示例 App 与 Twenty 服务端工作流实现的交叉验证。workflows.md与相邻的 logic.md、app-structure.md 同属于 develop-app 参考集本文只聚焦工作流这一主题其余能力仅在必要时带过。适用场景何时需要读这篇文档按照 skills/develop-app/SKILL.md 的定义develop-app skill 覆盖对象、布局、逻辑函数与前端组件等实体变更其中工作流子场景的触发语是add a workflow with a manual trigger添加带手动触发的工作流。落到具体业务典型诉求包括App 安装后需要预置一条人工可运行的工作流例如从选中记录生成文档、批量审批流转一条已经写好的逻辑函数需要让它出现在工作流构建器的步骤面板里让最终用户在可视化画布上自由编排需要理解为什么工作流不能像 object、field 那样通过define*原语直接声明以及如何在 post-install hook 里绕过这一限制。参考策略建议动手前先读 how-apps-work.md 建立 App / 实例 / remote / sync 的全局模型写实体前读 app-structure.md写 hook 与逻辑函数前读 logic.md。本文假设你已经具备这些前置知识。心智模型工作流是工作区记录不是 App 实体workflows.md开门见山给出一个容易踩坑的结论Twenty 工作流是 workspace工作区记录不是 App 实体SDK 中不存在define*工作流原语。也就是说你无法像声明defineObject、defineField、defineView那样去声明一个defineWorkflow。这一点在仓库内得到了直接佐证twenty-partners 的 workflow runbook 开头明确写道The SDK has nodefineWorkflow, so this is a documented setup step — not shipped in the app manifest.SDK 没有defineWorkflow因此这是一个文档化的配置步骤而不是随 App manifest 下发的内容。该 App 的日常摘要工作流是在 Twenty 工作区 UI 里手工构建的且每安装一个 workspace 都要重建一次——因为工作流是 workspace 元数据不会随deploy/install迁移。作为替代安装即交付的正规通道是definePostInstallLogicFunction workspace API在安装钩子中通过工作流的标准 GraphQL mutation 去创建、编排并激活工作流记录。SDK 侧对definePostInstallLogicFunction的校验逻辑位于 define-post-install-logic-function.ts它要求提供universalIdentifier且handler必须是一个函数任何缺失都会通过createValidationResult收集成校验错误其配置类型 post-install-logic-function-config.ts 在通用配置之上仅追加了一个可选字段shouldRunSynchronously是否同步执行。把工作流当作数据而非代码来对待是理解后续所有规则的前提一个工作流 一条Workflow记录 至少一条WorkflowVersion版本记录创建一个Workflow会自动产生它的草稿版v1永远不要直接创建WorkflowVersion记录——版本必须由工作流生命周期 API 代为生成与管理。因此安装一条默认工作流这件事在实现上等价于在 post-install hook 里写入若干条 workspace 记录并天然继承 workspace 记录的数据边界它属于当前 workspace不随 App 包体发布。为什么不能简单地发一个版本号工作流不同于普通对象记录的关键点在于它带版本 状态机。从 Twenty 服务端的工作流工具实现 create-complete-workflow.tool.ts 可以看到底层的编排逻辑先插入Workflow初始statuses: [DRAFT]再插入名称为v1、状态为DRAFT的WorkflowVersion最后当activate: true时调用activateWorkflowVersion并把Workflow.statuses更新为[ACTIVE]、记录lastPublishedVersionId。这段代码恰好印证了文档反复强调的两条禁令的底层原因详见后文生命周期与禁止事项。手动记录触发Manual Record Trigger的数据契约工作流支持多种触发类型服务端工具注释列出的合法类型为DATABASE_EVENT、MANUAL、CRON、WEBHOOK本文所述场景使用的是manual record selection手动选中记录触发。workflows.md对这条触发链路给出了三条容易写错的契约触发类型使用手动记录选择manual record selection例如在记录表格/详情页勾选记录后手动触发该工作流载荷形状被选中的记录本身就是触发 payload——没有包裹层record字段。即模板变量直接落在trigger之下字段引用语法引用字段一律写作{{trigger.field}}例如{{trigger.name}}严禁写成{{trigger.record.field}}。这一契约决定了逻辑函数与工作流步骤在读取触发数据时都必须按扁平 trigger处理。结合 logic.md 的批量输入约定records: Array{ id: string; ...fields }当你的逻辑函数需要被选中记录 → 工作流步骤的路径调用时建议让函数支持批量记录输入由前端组件把选中记录一次性交给函数而不是让前端循环调用同一个函数。生命周期create → configure draft → activateworkflows.md把工作流的正确生命周期概括为一句铁律永远执行 create → configure draft → activate绝不直接发布一个 draft。分步拆解如下按稳定名称或 slug 查找 / 创建Workflow。createWorkflow创建 Workflow 的 mutation会自动产出草稿版v1通过updateWorkflowVersion在草稿版本上设置 trigger此处的 trigger 即上文的手动记录选择及字段映射配置通过 workflow-step mutation 添加步骤。注意这里有一个容易误解的细节createWorkflowVersionStep返回的不是 step 本身而是一个stepsDiff步骤差异对象——需要先从 diff 中读出新建 step 的 id再拿着完整 payload调用updateWorkflowVersionStep完成真正的写入通过activateWorkflowVersion激活版本。激活后该版本成为活动版本工作流进入可用状态。服务端实现可以印证第 3 步的diff设计在 workflow-version-step-creation.workspace-service.ts 中createWorkflowVersionStep的返回类型是WorkflowVersionStepChangesDTO方法内部读取草稿版本的existingSteps/existingTrigger通过insertStep计算updatedSteps/updatedTrigger并落库最后调用computeWorkflowVersionStepChanges对比新旧快照产出差异。因此调用方拿到的确实是一份变更摘要而不是{ step }对象——把createWorkflowVersionStep的返回值直接当作步骤来用是常见错误。一个与文档语义一致、可直接作为结构化草稿的编排示意具体请求字段以工作流 mutation 的 schema 为准// 伪代码post-install hook 内的工作流播种逻辑语义与 workflows.md 一致 const workflowId await createWorkflow({ name: Summarize selected record }); // 自动产出 draft v1 await updateWorkflowVersion({ workflowVersionId: draftVersionId(workflowId), // 由 createWorkflow 返回 trigger: { type: MANUAL, settings: { /* 手动记录选择输入为选中记录无 record 包裹层 */ }, }, }); const diff await createWorkflowVersionStep({ workflowVersionId: draftVersionId(workflowId), stepType: CODE, // 引用一个 defineLogicFunction 暴露的函数 defaultSettings: { /* ... */ }, }); const newStepId diff.changes.find((c) c.type ADD).stepId; // 从 stepsDiff 读新 id await updateWorkflowVersionStep({ workflowVersionId: draftVersionId(workflowId), stepId: newStepId, payload: { /* 完整步骤 payload引用 {{trigger.field}} */ }, }); await activateWorkflowVersion({ workflowVersionId: draftVersionId(workflowId) });需要再次强调这里的 mutation 名称直接取自workflows.mdcreateWorkflow、updateWorkflowVersion、createWorkflowVersionStep、updateWorkflowVersionStep、activateWorkflowVersion。真实调用时请以工作流相关的 GraphQL schema、SDK 生成的类型或工作流工具服务端 workflow-tools 下create_complete_workflow、create_workflow_version_step、activate_workflow_version等同名工具见 workflow-tools 目录为准核对入参形状。禁止事项两条后端语义红线workflows.md用醒目的 Forbidden 段落划出两条不要手写的边界不要写Workflow.statuses字段。该字段是由活动版本active version的状态计算得出的属于 Twenty 自行维护的派生数据。手写它会与版本状态机产生不一致。这也解释了 logic.md 中Do not write fields Twenty computes elsewhere的通用原则其中点名了 workflowstatuses。不要直接创建WorkflowVersion行。版本记录必须走工作流生命周期 API直接插入会绕过 draft v1 自动生成、状态机推进与激活逻辑。服务端的updateWorkflowStatus在 create-complete-workflow.tool.ts 内展示的是正确写法激活动作成功后才把statuses更新为[ACTIVE]并回填lastPublishedVersionId。播种代码应当遵循同样的顺序与归责。幂等性先按确定性标识查找再创建Post-install hook 可能被多次执行升级、重装、重建 workspace因此workflows.md明确要求在创建之前先按确定性的 name 或 slug 查找。这套要求与 logic.md 中hooks must be idempotent: find by stable identifier before creating, update if it exists, never duplicate完全一致。两个实现要点确定性查找键使用稳定的工作流名称或 slug也可以配合 App 的 universal identifier 体系参考 how-apps-work.md 中universal identifiers 在重命名、升级与重新同步后保持稳定的说明。先查后建、命中即更新绝不重复创建把未找到当作需要创建文档特别提醒在部分 Twenty 版本中单记录single-record查询在记录不存在时会直接抛错。因此播种代码不能用查询抛错作为失败路径而要把 not-found 显式翻译为needs create分支去走createWorkflow流程。可验证的幂等范例仓库中就有hello-world 示例的 post-install.ts 展示了definePostInstallLogicFunction的最小声明方式universalIdentifiernamedescriptiontimeoutSecondshandler其 handler 接收InstallPayload含previousVersion用一条打印确认安装钩子已执行。真实播种逻辑只需在该骨架里套用上面查找 → 创建 → 配 trigger → 加步骤 → 激活的顺序即可。权限给 App 角色授予 workflow settings 权限因为播种是通过 workspace API 写入记录而不是以普通用户身份操作所以Seeder安装钩子内的播种代码需要在 App 角色上拥有 workflow settings 权限。workflows.md给出的授予途径是defineRole。SDK 中的 define-role.ts 说明了角色声明的校验规则defineRole要求提供universalIdentifier与label并支持objectPermissions、fieldPermissions、rowLevelPermissionPredicates等配置块每项都会校验引用对象的universalIdentifier、字段标识与谓词完整性。播种工作流所需的设置类权限应当在角色的权限配置中一并授予让安装钩子在 App 上下文中拥有写入工作流记录的合法身份。workflows.md还给出了一条重要的排障指引值得单独强调如果一条类型化 mutation在 App 上下文app context中被拒绝请先回头修角色的权限配置——不要退而求其次改用裸 GraphQL。改用裸请求是在掩盖权限范围设计错误属于信号错位signals the wrong scope。也就是说类型化 mutation 被拒通常是权限模型没配全正确的动作是补齐defineRole中的 workflow settings 权限而不是绕过 SDK 的类型化通道。本地调用与验证install hook 不会随 dev sync 执行开发阶段最容易产生的困惑是我yarn twenty apply之后工作流怎么没出现。答案在workflows.md与 logic.md 中表述得很一致开发同步yarn twenty apply会跳过 install hooks。原因不难理解install hook 的本意是安装到某个 workspace 时才执行的一次性播种而 dev sync 只是把实体定义推送到开发 remote二者语义不同。因此本地验证播种逻辑要走专门通道yarn twenty dev:function:exec该命令用于在本地直接执行*.post-install.ts以及其它逻辑函数文档明确要求rebuild重新构建之后再次运行一遍用第二次执行来验证幂等性——如果第二次执行没有产生重复工作流、重复版本说明先查后建的查找逻辑是健壮的。配合完整的实体变更收尾流程来自 app-structure.md 的 Validation Checklist建议在工作流播种代码就绪后按如下顺序验证一次yarn twenty dev:typecheck # 校验生成的 App 类型 yarn lint # 校验本地 lint 规则 yarn twenty apply # 构建并把实体定义同步到 active remote注意跳过 install hooks yarn twenty dev:function:exec # 本地执行 post-install hook验证播种 # rebuild 后再次执行 dev:function:exec验证幂等补充说明执行环境边界如果需要在真实安装路径上看到工作流应在执行yarn twenty install/部署安装的 workspace 中观察 hook 生效纯 dev sync 场景只能通过dev:function:exec间接验证。这些命令面向安装了twenty-sdkCLI 的 App 工程SDK 命令入口见 twenty-sdk CLI dev 命令在 App 根目录含package.json与src/application-config.ts下运行。仓库实例手工工作流作为对照如果需求只是某个内部 App 需要一个固定结构的工作流并且可以接受人工配置仓库内 twenty-partners 的 workflows/README.md 提供了一份完整的工作区手工构建 runbook作为对照方案它描述了一条 cron 触发的每日摘要邮件工作流Find Records→Iterator→ 关联 Person 查询 →If/Else→Send Email并再三强调SDK 没有defineWorkflow该工作流是文档化的 setup 步骤不随 App manifest 交付工作流是 workspace 元数据每个安装它的 workspace本地 bundle、staging、prod都要重建一遍重建后需要确认版本处于ACTIVE对应本文激活步骤。这个实例与workflows.md形成互补需要可编程、随安装自动交付时走 post-install hook 播种 workspace API接受人工在 UI 里建一次时可参考该 runbook 的步骤拆解来核对播种代码应还原的每一步触发、查找步骤、迭代、条件分支、发送动作与最终激活。小结一份工作流播种的核对清单把workflows.md的全部要点收拢为一条可直接对照的 checklist模型工作流是 workspace 记录不是 App 实体用definePostInstallLogicFunction声明安装钩子在其 handler 中通过 workspace API 播种不要寻找不存在的defineWorkflow结构目标 Workflow 至少一个WorkflowVersioncreateWorkflow自动产出草稿v1触发手动记录选择时选中记录即 payload无record包裹层字段引用统一用{{trigger.field}}顺序create → updateWorkflowVersion 配 trigger → createWorkflowVersionStep读stepsDiff拿新 step id→ updateWorkflowVersionStep 写完整 payload → activateWorkflowVersion红线不写Workflow.statuses由活动版本计算不直接创建WorkflowVersion行绝不直接 publish 一个 draft幂等先按稳定 name/slug 查找命中即更新单记录查询的 not-found 一律按needs create处理权限App 角色通过defineRole授予 workflow settings 权限类型化 mutation 被拒先修角色不要退回裸 GraphQL验证yarn twenty apply跳过 install hooks用yarn twenty dev:function:exec本地执行播种rebuild 后复跑以验证幂等。对照以上八条即可在 Twenty App 中实现安装即出现、可被用户手动触发、逻辑函数可被工作流构建器调用的完整工作流能力。【免费下载链接】twentyThe open alternative to Salesforce, designed for AI.项目地址: https://gitcode.com/GitHub_Trending/tw/twenty创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考