Activepieces Piece 开发完全指南:从零构建、测试与发布自定义集成 Activepieces Piece 开发完全指南从零构建、测试与发布自定义集成【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces导读本文基于 Activepieces 仓库中的 Building Pieces 知识文档系统讲解如何构建、测试并发布自定义 Piece。Piece 是 Activepieces 中承载集成能力的核心单元——它是用 TypeScript 编写的 npm 包仓库中约 60% 的 Piece 由社区贡献。读完本文你将掌握用 CLI 脚手架创建 Piece/Action/Trigger 的完整流程、轮询/Webhook 触发器的实现技术、以及十余个来自真实生产环境的坑gotcha帮助你写出健壮、可发布、可持续维护的集成代码。Piece 是什么npm 包形式的集成单元在 Activepieces 的架构中一个 Piece 就是一段封装了特定第三方服务能力的 TypeScript 代码最终以 npm 包的形式分发。它通过统一的声明式接口暴露三类能力Auth认证声明该集成需要何种认证方式Actions动作用户在流程中可调用的一次性操作如创建记录发送消息Triggers触发器驱动流程启动的事件源如新行产生时Webhook 到达时。仓库中所有官方与社区 Piece 都位于packages/pieces/community/name/目录如packages/pieces/community/google-sheets/其元数据displayName、logoUrl、auth、actions、triggers 等由createPiece(...)声明。从 createPiece 的源码 可以看到它接收一个CreatePieceParams参数对象Piece 类内部维护_actions与_triggers两个注册表并把它们连同auth、minimumSupportedRelease、categories、authors等一起序列化为向后兼容的元数据metadata()方法见 piece.ts。开发环境的准备构建一个自定义 Piece 前你需要Fork 仓库或直接使用 GitHub Codespaces / 开发容器dev container获得开箱即用的环境完成本地开发环境搭建确保npm/bun、turbo等工具可用了解热重载机制Piece 的本地改动约 7 秒即可在构建器中热重载显示可以边改边看。第一步用 CLI 脚手架创建一个 Piecepackages/cli提供了交互式脚手架命令。创建新 Piece 的命令是npm run cli pieces create执行后CLI 会依次询问交互逻辑见 create-piece.ts问题说明默认值/约束Piece namePiece 目录名只能包含小写字母、数字与连字符正则^(?![._])[a-z0-9-]{1,214}$Package namenpm 包名activepieces/piece-namePiece type选择community社区或custom自定义community脚手架会在packages/pieces/community/name/下生成完整工程结构package.json、tsconfig.json、tsconfig.lib.json、.eslintrc.json、src/index.ts与src/lib/、src/i18n/目录。生成的src/index.ts骨架见 create-piece.ts 模板import { createPiece, PieceAuth } from activepieces/pieces-framework; export const myPiece createPiece({ displayName: My Piece, description: , auth: PieceAuth.None(), minimumSupportedRelease: 0.36.1, logoUrl: https://cdn.activepieces.com/pieces/name.png, authors: [], actions: [], triggers: [], });生成的package.json已预置好activepieces/pieces-common、activepieces/pieces-framework、activepieces/core-piece-types、activepieces/core-utils等工作区依赖并带有build、bundle、lint三个脚本见 create-piece.ts可以立即开始开发。注意生成的.eslintrc.json中通过no-restricted-imports禁止 Piece 直接导入lodash、activepieces/shared、activepieces/engine等内部包见 create-piece.ts这是为了保持 Piece 的运行时隔离。第二步声明认证AuthcreatePiece的auth字段决定整个 Piece 的认证形态通过框架的PieceAuth提供。最常用的是两种// 密文形式API Key / Token auth: PieceAuth.SecretText({ displayName: API Key, required: true, description: 在服务商后台创建, }), // 无认证 auth: PieceAuth.None(),框架还支持更多认证形式OAuth2PieceAuth.OAuth2(...)、Basic Auth、自定义认证表单、OIDC 等它们定义在 packages/pieces/framework/src/lib/property/authentication/ 下包括oauth2-prop.ts、basic-auth-prop.ts、custom-auth-prop.ts、oidc-prop.ts、secret-text-property.ts。认证值在 Action/Trigger 的run()中通过context.auth获取。关于各认证形态的完整参数与适用场景可参考框架源码目录中的对应实现在具体选型时对照查阅。第三步创建 Action动作Action 是流程中可调用的操作。命令npm run cli actions createCLI 依次询问 Piece 目录名、Action 显示名与描述然后在src/lib/actions/kebab-name.ts生成骨架见 create-action.tsimport { createAction, Property } from activepieces/pieces-framework; export const myAction createAction({ name: myAction, displayName: My Action, description: ..., props: {}, async run() { // Action logic here }, });一个带输入属性和真实逻辑的 Action 通常长这样export const createRecord createAction({ name: createRecord, displayName: Create Record, description: 在表格中创建一条记录, props: { tableId: Property.Dropdown({ displayName: 表格, required: true, refreshers: [auth], options: async ({ auth }) { // 动态加载选项 return { disabled: false, options: [{ label: ..., value: ... }] }; }, }), fields: Property.Object({ displayName: 字段值, required: true, }), }, async run(context) { const { auth, propsValue } context; // 调用第三方 API 并返回结构化结果 return { success: true, id: ... }; }, });createAction的参数类型与Action类的实现定义在 packages/pieces/framework/src/lib/action/action.ts。第四步创建 Trigger触发器触发器负责驱动流程启动。命令npm run cli triggers createCLI 会让你在polling轮询与webhookWebhook两种技术之间选择见 create-trigger.ts。触发器通过createTrigger({ ..., type: TriggerStrategy.WEBHOOK | POLLING | APP_WEBHOOK, onEnable, onDisable, ... })构建三种技术的对比如下技术原理适用场景Polling轮询周期性地向第三方 API 拉取新数据基于时间或最后一条记录去重无 Webhook 能力、数据量可控的服务Webhook在onEnable中向第三方注册一个回调 URLrun()在收到回调时执行服务支持自定义 WebhookApp Webhook基于 OAuth 订阅的 Webhook当前不受支持not supported不要使用轮询触发器模板CLI 生成的轮询触发器使用pollingHelper见 create-trigger.ts 模板import { createTrigger, TriggerStrategy, AppConnectionValueForAuthProperty } from activepieces/pieces-framework; import { DedupeStrategy, Polling, pollingHelper } from activepieces/pieces-common; import dayjs from dayjs; const polling: PollingAppConnectionValueForAuthPropertyundefined, Recordstring, never { strategy: DedupeStrategy.TIMEBASED, items: async ({ propsValue, lastFetchEpochMS }) { const items [{ id: 1, created_date: 2021-01-01T00:00:00Z }, ...]; return items.map((item) ({ epochMilliSeconds: dayjs(item.created_date).valueOf(), data: item, })); }, }; export const myTrigger createTrigger({ name: myTrigger, displayName: My Trigger, description: ..., props: {}, sampleData: {}, type: TriggerStrategy.POLLING, async test(context) { return await pollingHelper.test(polling, context); }, async onEnable(context) { await pollingHelper.onEnable(polling, context); }, async onDisable(context) { await pollingHelper.onDisable(polling, context); }, async run(context) { return await pollingHelper.poll(polling, context); }, });Webhook 触发器模板import { createTrigger, TriggerStrategy } from activepieces/pieces-framework; export const myTrigger createTrigger({ name: myTrigger, displayName: My Trigger, description: ..., props: {}, sampleData: {}, type: TriggerStrategy.WEBHOOK, async onEnable(context) { // 实现 webhook 创建逻辑把回调 URL 注册到第三方 }, async onDisable(context) { // 实现 webhook 删除逻辑 }, async run(context) { return [context.payload.body]; }, });轮询去重的底层原理pollingHelper是轮询触发器的核心实现在 packages/pieces/common/src/lib/polling/index.ts。它支持两种去重策略DedupeStrategy见 index.tsTIMEBASED从 store 读取lastPoll时间戳只返回epochMilliSeconds大于该值的条目并把新的最大时间戳写回见 index.tsLAST_ITEM从 store 读取lastItem用items.findIndex((f) f.id lastItemId)在刚取回的当前页中定位检查点findIndex返回-1时视为无检查点并重放整页见 index.ts。理解 LAST_ITEM 的实现方式至关重要——它解释了为什么恢复分支的查询绝不能加 LIMIT一旦当前页被截断检查点行可能掉出页面findIndex → -1会导致整页重复发射。发布 Piece版本号与校验发布 Piece 的核心是每次改动都必须升版本号。CI 中运行的validate-publishable-packages脚本入口 validate-publishable-packages.ts检查逻辑在 package-pre-publish-checks.ts会对每个 Piece 目录执行packagePrePublishChecks当 Piece 的package.json版本已是 npm 上的latest且相对origin/main存在代码差异时抛错package version not incremented。关于版本号有几个关键规则改动即升版即使是行为变更比如新增 OAuth scope也按惯例打 patch 版本号diff 基准是origin/main不是 PR basestacked PR 会继承 base 分支改过的所有 Piece也必须一并升版CI 只报第一个错检查以Promise.all每批 10 个并行执行第一个抛错就终止进程——日志可能只点名一个 Piece如azure-ad但实际可能同时有 27 个违规。正确做法是用git diff --name-only origin/main...HEAD | grep pieces/枚举全部受影响 Piece一次性全部升版豁免清单packages/pieces/framework与packages/pieces/common被显式排除notPublished列表因为它们的内容在构建期被内联进各 Piecepackages/pieces/之外的一切也豁免本地复现git fetch origin main之后运行npx ts-node -r tsconfig-paths/register -P packages/server/engine/tsconfig.lib.json tools/scripts/validate-publishable-packages.ts整个脚本本地跑约 3 分钟。合并 main 会吞掉版本号一个常见陷阱是把main合并进 Piece 分支后版本号悄然回退到已发布版本。原因是main上已经发布了与你刚升到的一样的版本号合并时 Git 把两侧解析成同一个数字分支版本就落回已发布版本。此时需要再次升版并同步更新bun.lock中镜像的 workspaceversion字段。排查时不要相信 PR 描述直接用git show origin/main:pkg/package.json逐个 diff 变更包。深度排坑来自生产环境的 Gotchas 实战手册building-pieces.md文档的核心价值在于积累了十余个真实排坑经验。以下按主题归纳每个都能在仓库源码中找到对应证据。1. 引擎侧改枚举值必须先重建 core-executionLoopBatchMode等枚举定义在activepieces/core-execution并通过activepieces/shared再导出。engine 的 vitest 配置把activepieces/shared别名到源码但源码会从 core-execution 的dist拉取——所以新增枚举值而不重建连 PR 自己的测试都会报Cannot read properties of undefined (reading ITEMS_PER_BATCH)。修复先运行npx turbo run build --filteractivepieces/core-executionCI 的 turbo 依赖图会自动处理这个问题本地临时跑测试不会。2. 测试加载真实 Piece 前必须先构建piece-loader 执行await import(abs/pieces/core/x/dist/src/index.js)所以没有dist/的 Piece 会报ERR_MODULE_NOT_FOUND看起来像测试坏了。实际上只要先构建npx turbo run build --filterpiece两个已验证的例子packages/server/engine/test/handler/flow-with-delay.test.ts5/5 通过test/integration/ce/flows/flow-run/execute-flow-e2e.test.ts8/8 通过含三个父→子callFlowsubflow 用例。运行时的两个坑server API 的 turbo workspace 名叫api而不是activepieces/server-api用后者做--filter会报 No package found集成测试需要环境变量需这样调用cd packages/server/api export $(cat .env.tests | xargs) AP_EDITIONce npx vitest run path编辑 Piece 后务必重新构建——测试执行的是dist/而非源码陈旧的 dist 会让旧代码静默通过。3. 流式文件输入Property.File({ streaming: true })把文件流式传入 Piece 的写法是Property.File({ streaming: true })pieces-framework ≥ 0.35.0设计决策见 000014-streaming-file-inputs-resolve-to-a-lazy-apstreamingfile.md。它解析为一个ApStreamingFilebody是Readable接受 URL、base64 data URL、构建器文件选择器或上一步输出的文件fetch 由引擎负责。参考实现amazon-s3/upload-file.ts 与 subflows/stream-csv-to-flow.ts。三个必须知道的行为引擎的fileProcessor会吞掉 fetch 失败并返回null对required: true的属性表现为令人困惑的Expected file url or base64 with mimeType校验错误而非 fetch 错误所以run()里不需要isNil守卫——action 根本不会启动引擎的 fetch没有超时一个连接后挂起的源会烧掉FLOW_TIMEOUT_SECONDS.pipe()不会转发error事件仍需file.body.on(error, ...)否则传输中段会变成沙箱里的未捕获异常。4. OIDC 校验validate()必须显式 re-throw 引擎错误在 OIDC Piece 中认证服务端上下文会把mintOidcToken({ audience })回调交给validate()其引擎侧实现会调用内部/v1/worker/oidc-token端点当该端点 5xx 时引擎抛出PieceServerContextError。朴素写法try { ... } catch (e) { return { valid: false, error: format(e) } }会把平台故障误报成INVALID_APP_CONNECTION——用户看到连接坏了oncall 却收不到告警。必须遵循的契约if (isPieceServerContextError(error)) throw error;在 catch 的 return 之前加上这行。executeValidateAuthpiece-helper.ts会把它重新包装为EngineGenericError再由tryCatchAndThrowOnEngineError路由为ExecutionErrorType.ENGINE并触发告警。参考实现是四个 AWS OIDC Pieceamazon-bedrock、aws-bedrock、amazon-s3、amazon-secrets-manager形状完全一致。注意运行期路径执行步骤、刷新 token故意不走这个类——运行期的 OIDC 失败只让单步失败这符合预期因为持续的平台问题会跨流程显现并被运维捕获。5. 安全性AWS region 字符串必须校验后再拼接aws-sdk/client-sts通过插值sts.region.amazonaws.com构建 STS 端点 URL且不校验格式。构造一个us-east-1.evil.com这样的 region就能把 STS 调用重定向到攻击者控制的域名——这是真实的 SSRF 形状问题Piece 运行在具备出网能力的引擎沙箱中且即将发送的是平台签名的合法 OIDC JWT。必须在任何触碰 region 的代码之前mint OIDC token 之前、构建 STS client 之前、构造缓存键之前校验/^[a-z]{2}(-[a-z])-\d$/四个 AWS OIDC Piece 都在getTemporaryCredentials中强制该校验任何用用户可控 region 构建 AWS SDK client 的场景都适用同样规则。6. 流式上传先查目标服务的单请求上限流式只移除我们这一侧的内存天花板不代表目标 API 会接受大文件。Dropbox 的/2/files/upload在 150 MB 以上返回409 {tag: payload_too_large}Graph 的简单PUT .../content在 250 MB 以上拒绝。识别是服务方拒绝的关键是错误形状——端点特定的 409 文档化 error tag。修复是分块上传会话而不是更大的缓冲区。参考实现dropbox/upload-file.ts 与 microsoft-onedrive/upload-file.ts两者都复用activepieces/pieces-common的streamUtils.readChunks({ readable, chunkSize })。实践规则未知大小的源也要走会话size是尽力而为的分块或压缩源上不存在无法证明其适配退回先缓冲再量大小正是流式改造要消除的 OOM块大小取服务偏好单位的倍数Dropbox 用 4 MiBOneDrive 用 320 KiB分块 body 是Buffer因此不同于一次性流 body能继续享受httpClient的重试能否对未知大小源分块取决于会话如何寻址分片Dropbox 基于偏移cursor.offset从不声明总长可直通流式而 Graph 要求每个分片的Content-Range里带文件总长所以microsoft-sharepoint和microsoft-onedrive必须先readableToBuffer一次获知长度再用Readable.from重新包装。那个缓冲正是流式改造要消除的 OOM属于最后手段——只要 API 提供基于偏移的会话就优先用。7. 轮询恢复分支的 LIMIT 陷阱Postgres new-row 模板把 postgres 的new-row.ts移植到其他 SQL Piece 时constructQuerynew-row.ts的两个分支是不对称的——无检查点分支用ORDER BY %I DESC LIMIT 5做冷启动种子而恢复分支刻意无界WHERE %I %L ORDER BY %I DESC不加 LIMIT。原因是DedupeStrategy.LAST_ITEM通过扫描刚取回的当前页恢复检查点polling/index.ts并发射它前面的所有条目。给恢复页加 LIMIT 会让检查点行掉出页面findIndex → -1被解读为无检查点整页重复发射。所以字面意义的LIMIT 5→TOP (5)是行为变更而非方言翻译。若确实要限页就脱离pollingHelper改用带游标位置的键集游标microsoft-sql-server/src/lib/common/cursor.ts是完整范例每页TOP (limit)、版本化游标、显式 tiebreaker 键。复制该模板还有两个锋利的边角item id 是orderValue | md5(JSON.stringify(row))对检查点行的任何编辑都会改变其 id 并使检查点失效且lastItem.split(|)[0]会截断任何含字面|的 order 值——时间戳和自增 id 没问题文本列排序则错误。8. CSV 解析bom: true是必选项且不要指望trim兜底csv-parse不会移除 UTF-8 BOM其trim选项只去空格和 tab不去 UFEFF。配合columns: true时Excel 和 Google Sheets Download as CSV 产出的 BOM 前缀文件会让第一列表头变成id带 BOM行键同样如此——于是流程里{{ ...rows[0].id }}解析为空而每一步都显示成功无报错、行数正确、运行全绿。已对仓库锁定的csv-parse5.6.0验证headers[0]的 charCode 是[65279, 105, 100]rows[0].id是undefined。parse(csv, { bom: true, // 必开剥离 UTF-8 BOM columns: true, relax_column_count: true, // 容错参差行避免 CSV_RECORD_INCONSISTENT_COLUMNS 中断整个文件 })relax_column_count: true的代价是超列的行会静默丢列。knowledge-base.service.ts与subflows/csv.ts都这样组合使用。注意piece-csv和google-sheets目前仍未开启bom: true。9.columns:还有两个静默全绿失败模式均已对锁定的csv-parse5.6.0验证重复表头折叠后者胜出a,a1,2得到{a:2}一列静默消失而从columns回调捕获的表头数组仍报[a,a]不再描述实际行。真实导出中重复列很常见如两个 Notes。修复是一个选项group_columns_by_name: true——重复列以{a:[1,2]}到达非重复列不受影响代价是重复列的值是string[]而其他列是string行类型要写成string | string[]。行比表头短时缺失键被整体省略表头a,b,c 行1,2得到{a:1,b:2}没有c键不是c:。所以参差行上{{ row.c }}解析为空运行仍然全绿。没有解析器选项能覆盖需要形状稳定的行就在on_record钩子里回填。subflows/csv.ts是两者的参考实现由subflows/test/csv.test.ts锁定。10.pollingHelper必须传完整context不能解构仓库里 403 个onEnable调用点中有 306 个使用解构形式{ store, auth, propsValue }它能通过类型检查读起来也很地道——但 helper 的参数类型比这三个字段更宽TypeScript 只拒绝多余属性、从不拒绝缺失的可选属性。于是每次给轮询上下文新增字段都会在每一个解构触发器里静默缺席。第一个改变行为的字段是context.isRepublishpollingHelper.onEnable用它保留现有lastPoll/lastItem而非重置为当前时间所以解构触发器在重新发布时会丢掉最后一次轮询与重发布之间的所有事件。脚手架npm run cli triggers create、docs/build-pieces/与 piece-builder 技能都传完整context新触发器没问题——真正的坑是从相邻 Piece 复制代码因为错误形状在存量代码中是多数。存量站点按触碰即修策略处理而不是一次性 codemod。11. 动态属性值可能以 JSON 字符串到达run()构建器的 fx / 动态值开关会把输入渲染成文本框getValueForInputOnDynamicToggleChangeauto-form-field-wrapper.tsx会对已有值做JSON.stringify——[year,month]、true、下拉的对象选项值都可能变成字符串。它能保存并发布成功是因为buildSchemaproperty/util.ts故意在这些类型上并集了一个z.string()分支而该 schema 同时被表单和服务端步骤校验器使用。唯一能治愈它的地方是引擎的variables/processors/映射——props-processor.ts是 actions、triggers 以及 agent/MCP 工具路径的唯一咽喉点。先例objectProcessor#5636随后是 multi-select checkbox#14389。要点强制转换只在属性值类型无歧义时才安全——DROPDOWN/STATIC_DROPDOWN被刻意排除因为合法的字符串选项值如[1,2]会被破坏成数组这个缺口仍然开放每个新 processor 都要配一个validatePropertycase否则无法解析的字符串会以零错误到达 Piece在run()深处不明不白地失败空动态输入是而非 nil——processor 把它映射为undefined交给validateProperty裁决所以jsonProcessor及其后继从不读property.required可选则通过必填则报错切回手动模式是镜像陷阱旧分支会丢弃值并返回getDefaultPropertyValue字段静默重置为 Piece 的defaultValue。现在切回路径走formUtils.parseDynamicValueform-utils.tsx只在值的形状对属性无歧义时才恢复值单选下拉仍会重置这是有意为之。需要猜测的强制转换属于引擎 processor绝不能放进这个共享 helper。该设计还让一件事变得不必要无需 flow 迁移。动态 JSON 属性一直存在字符串化问题却从未迁移——引擎在运行期解析文本框就是动态模式的样子。为 #14389 写过的DYNAMIC→MANUAL迁移被放弃了切回路径已按需完成同样的事无需 schema 升级、备份或破坏性变更说明。12.validate()后门FriendlyPieceError.message 可能是 JSON 信封HttpErrorhttp-error.ts把message构造成JSON.stringify({ response: { status, body }, request: { body } })——包含 request body对多数 Piece 来说就是认证载荷。formatPieceErrorfriendly-piece-error.ts只有在extractApiMessage匹配到响应体里约 14 个硬编码键message、error、detail、errors等时才转义该 blob否则pickPlainMessage回退到stripStack(message)即整个 JSON blob。已验证空响应体最常见的 401、完全无 body、{msg}、{Message}大写 M 不是候选键都会原样返回{response:{status:401,body:},request:{body:{api_key:…}}}。所以apiMessage ?? message并不是安全网——只要apiMessage存在message apiMessage。正确做法是能否解析为 JSON作为门槛解析失败就什么都不渲染而不是渲染回退值。注意fetch-http-client.ts在抛出前console.error整个HttpError把 request body 写进worker 日志——这是两者中更大的泄露且与任何 UI 无关。修复应放在pickPlainMessage一处拒绝能解析为 JSON 的 cleaned 值回退到Request failed with status n一次关闭所有消费者。13. 引擎错误处理formatPieceError自身绝不能再抛错formatPieceError运行在引擎错误处理器的内部它抛出的任何东西都会用一个引擎崩溃替换真实的用户错误。两个曾真实触发过的输入均已验证循环引用的 error body 让collectMessage的递归爆栈RangeError: Maximum call stack size exceeded以及循环的responseBody/requestBody在JSON.stringify(formatPieceError(...))处炸出TypeError: Converting circular structure to JSON。落点很糟operations/index.ts是最外层处理器在那里抛错意味着无响应worker 只看到 RPC 超时。现在两者都已加边界——collectMessageFrom和rebuildSerializable各带深度上限加 visitedWeakSet。正确做法是用JSON.stringify探测而不是手写遍历每个 bodytoSerializable先尝试JSON.stringify成功就原样返回原始引用不复制只有真正抛错的值才走有界重建循环变成[Circular]重建本身又被tryCatchSync包裹回退到[Unserializable]。经验法则这个文件可以返回更差的 message但绝不能抛错。14. Windows 开发新增 action/trigger 名称需要杀掉 dev server 进程clearPieceModuleCache——唯一能击穿支撑 dev piece 元数据的 CommonJSrequire()缓存的机制——只在 chokidar watcher 的重建处理器里被调用dev-piece-watcher.ts而该 watcher 在 Windows 上对工具生成的编辑触发并不可靠。普通重启会复用同一 PID用netstat/Get-Process确认 dev 端口绑定服务继续提供陈旧元数据。找到绑定 dev API 端口的 PID 后Stop-Process -Id pid -Force再全新启动。其他改动prop 文案、run()内部逻辑都能热重载只有新 action/trigger 名称或输出形状变化会踩中此坑。15. 属性类型细节Property.Array的properties子 schema 不会进入解析后的propsValue类型——propsValue.someArrayProp的类型是无条件unknown[]见 property/index.ts在使用点强制转换到声明的行类型是唯一选项框架没有类型安全的路径请用注释说明该转换Property.Dropdown不能放进Property.Array——它被排除在ArraySubProps之外见 array-property.ts。行项目数组需要按 id 引用另一资源时无法每行放一个可搜索下拉改为在run()中按精确名称服务端解析基于行的纯文本字段的查找 helper未分组的 props 渲染在所有propertyGroups分区之后而不是按声明顺序内联。做模式选择器这种门控多个分区的 prop 时必须把选择器自身的 section 声明为propertyGroups的第一个否则它渲染在最后——排在它本该门控的字段之后。这是靠视觉审查发现的build/lint 都查不出来HttpRequest.queryParams是Recordstring, string一个键一个值不支持数组见 query-params.ts。第三方 API 要重复参数typeatypeb而不是逗号合并时把重复参数预编码进resourceUri的查询串resourceUri: /x?typeatypeb——getUrl()会先解析并保留 URL 上已有的查询串再在其上合并queryParams两者能正确共存。分享与分发Sharing misc分享可以向社区贡献 Piececontribute to community、发布社区 Piece或保持私有杂项构建/打包/发布 Piece 的完整流程、Pieces 的 CI/CD、nx→turbo 迁移、迁移 Pieces 到 bundles、私有 fork、Piece 测试、dev container、Codespaces、创建新的 AI provider。其中构建→打包→发布的主干路径为npm run cli pieces create # 1. 创建 # ...开发 actions / triggers... npm run build # 2. 构建tsc → dist/ npm run cli pieces bundle # 3. 打包脚本由脚手架生成 npm run cli pieces publish # 4. 发布发布前的核心门槛就是上文第三节的validate-publishable-packages校验版本号未递增会直接阻断。结语构建健壮 Piece 的四条底线版本纪律每次改动升 patch 版本提交前用validate-publishable-packages本地校验合并 main 后重新核对版本号与bun.lock测试纪律运行加载真实 Piece 的测试前先npx turbo run build --filterpiece编辑后重建否则测试跑的是陈旧的 dist健壮性纪律CSV 解析必开bom: true轮询恢复分支不加 LIMITpollingHelper传完整contextOIDCvalidate()显式 re-throw 引擎错误流式上传先核对目标服务的分块会话约束安全纪律任何进入 AWS SDK 的 region 字符串都要先用/^[a-z]{2}(-[a-z])-\d$/校验防止 SSRF 形状的端点劫持。掌握这些实践后你可以参照 pieces-engine 知识索引 继续深入 Piece 目录元数据piece_metadata表、pieceMetadataService、pieceInstallService的安装链路以及 触发器平台侧机制完整理解从一段 TypeScript 代码到用户流程里可用的集成的全链路。【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考