ActivePieces Motion Piece 源码解析:构建、API Key 认证与 Motion 任务/项目管理动作实现
【免费下载链接】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/piece-motion为载体,从 README 给出的构建命令出发,逐层剖析该 Piece 的包结构、PieceAuth.SecretText认证校验、动态下拉属性(Workspace/Status/Project/Assignee/Task ID)的远程选项加载与游标分页机制,以及 6 个内置动作 + 1 个轮询触发器的完整 API 调用链。读完后可掌握在 ActivePieces 中开发一个连接第三方 REST API 的社区件所需的完整骨架。
1. Piece 定位与包结构
packages/pieces/community/motion/是 ActivePieces 的社区件(community piece)之一,用于对接 Motion(app.usemotion.com)的 REST API。从 package.json 可以看到该包的基本信息:
- 包名:
@activepieces/piece-motion,版本0.1.7,入口为./dist/src/index.js; - 依赖:
@activepieces/pieces-common、@activepieces/pieces-framework、@activepieces/core-piece-types、@activepieces/core-utils(均为workspace:*工作区依赖),以及用于时间处理的dayjs; - 脚本:
build:tsc -p tsconfig.lib.json && cp package.json dist/,即编译 TS 并把package.json拷入产物目录;bundle:调用 CLI 的 pieces 子命令node ../../../../dist/packages/cli/src/index.js pieces bundle生成可分发的 bundle 包;lint:对src/**/*.ts执行 eslint。
目录结构如下(均来自仓库实际文件):
packages/pieces/community/motion/ ├── src/ │ ├── index.ts # createPiece 入口,聚合 auth/actions/triggers │ ├── lib/ │ │ ├── auth.ts # API Key 认证与校验 │ │ ├── common/props.ts # BASE_URL 与可复用动态下拉属性 │ │ ├── actions/ │ │ │ ├── create-task.ts # 创建任务 │ │ │ ├── update-task.ts # 更新任务 │ │ │ ├── create-project.ts # 创建项目 │ │ │ ├── get-task.ts # 按 ID 获取任务 │ │ │ ├── move-task.ts # 跨 Workspace 移动任务 │ │ │ └── find-task.ts # 按名称搜索任务 │ │ └── triggers/ │ │ └── task-created.ts # 任务创建轮询触发器 │ └── i18n/ # de/es/fr/ja/nl/pt/ru/vi/zh 等翻译 ├── package.json └── tsconfig.json / tsconfig.lib.json2. 构建方式(README 核心内容)
README 给出的唯一构建命令是:
turbo run build --filter=@activepieces/piece-motion即通过 Turborepo 在 monorepo 中按包名过滤,只构建@activepieces/piece-motion这一个依赖树。该命令会触发上面package.json中定义的build脚本:先用tsc -p tsconfig.lib.json按 tsconfig.lib.json 编译出dist/产物,再复制package.json。需要说明的是,--filter只影响 turbo 的包选择,最终执行的仍是目标包自身scripts.build中定义的命令。
3. Piece 入口:createPiece 如何聚合动作与触发器
index.ts 是标准 Piece 入口,用createPiece声明元数据并把所有动作、触发器注册进去:
export const motion = createPiece({ displayName: 'Motion', logoUrl: 'https://cdn.activepieces.com/pieces/motion.png', categories: [PieceCategory.PRODUCTIVITY], auth: motionAuth, authors: ['Sanket6652', 'kishanprmr'], actions: [ createTask, updateTask, createProject, getTask, moveTask, findTask, createCustomApiCallAction({ auth: motionAuth, baseUrl: () => BASE_URL, authMapping: async (auth) => { return { 'X-API-Key': auth.secret_text }; }, }), ], triggers: [taskCreated], });几个值得注意的实现点:
categories: [PieceCategory.PRODUCTIVITY]决定该 Piece 在应用市场中的分类;auth: motionAuth是整包共用的认证定义(见下节),所有动作与触发器都引用它;- 最后一个动作由
createCustomApiCallAction(来自@activepieces/pieces-common)生成,即"自定义 API 调用"动作:它让用户在界面中自由选择 HTTP 方法与路径,baseUrl固定为BASE_URL,authMapping负责把认证值映射为X-API-Key请求头——这是 ActivePieces 社区件提供"逃生舱"式 API 访问的通用手法。
4. 认证:SecretText + /workspaces 探活
auth.ts 使用PieceAuth.SecretText声明 API Key 认证:
export const motionAuth = PieceAuth.SecretText({ displayName: 'API Key', description: `You can obtain API key from [API Settings](https://app.usemotion.com/web/settings/api).`, required: true, validate: async ({ auth }) => { try { await httpClient.sendRequest({ method: HttpMethod.GET, url: `${BASE_URL}/workspaces`, headers: { 'X-API-Key': auth }, }); return { valid: true }; } catch { return { valid: false, error: 'Invalid API key.' }; } }, });从源码结构看,其工作原理是:用户保存认证时,框架会调用validate,向 Motion API 发起一次GET {BASE_URL}/workspaces请求并携带X-API-Key头;请求成功即判定有效,异常则返回Invalid API key.错误。这种"以一次轻量只读请求探活"的模式,是 SecretText 类认证最常用的落地方式。
common/props.ts 定义了全包共用的 API 基地址:
export const BASE_URL = 'https://api.usemotion.com/v1';后续所有动作、触发器、下拉选项的 HTTP 请求都以此为前缀。
5. 动态下拉属性体系:props.ts 深度解读
props.ts 是本件最有复用价值的部分,它把 5 个"远程选项"属性抽象为可复用单元,供各动作/触发器共享:
5.1 workspaceId —— 工厂函数生成的下拉属性
workspaceId(displayName)是一个返回Property.Dropdown的工厂函数(因为同一属性在 Move Task 动作中会被实例化两次,分别标注为 "Current Workspace" 和 "Target Workspace"):
- 未连接账号时返回
disabled: true并提示 "Please connect your account."; - 已连接则
GET {BASE_URL}/workspaces,把响应中workspaces数组的name/id映射为{ label, value }选项。
5.2 statusId / projectId / userId —— 依赖 workspaceId 的级联下拉
三者结构一致:refreshers: ['workspaceId']声明了对workspaceId的级联依赖(工作区切换后自动刷新选项),未选择工作区时同样禁用并提示 "Please connect your account and select workspace."。分别请求:
| 属性 | 端点 | 响应字段 | 选项映射 |
|---|---|---|---|
| statusId(Status) | GET {BASE_URL}/statuses?workspaceId=... | [{ name }] | label/value 均取name |
| projectId(Project) | GET {BASE_URL}/projects?workspaceId=... | projects: [{ id, name }] | label 取 name,value 取 id |
| userId(Assignee) | GET {BASE_URL}/users?workspaceId=... | users: [{ id, name }] | label 取 name,value 取 id |
5.3 taskId —— 带游标分页的全量任务下拉
taskId属性(required)展示了处理"选项可能很多"场景的完整模式:do...while循环携带cursor参数反复请求GET {BASE_URL}/tasks?workspaceId=...&cursor=...,直到响应meta.nextCursor为空:
do { if (nextCursor) { qs['cursor'] = nextCursor; } const response = await httpClient.sendRequest({ method: HttpMethod.GET, url: `${BASE_URL}/tasks`, headers: { 'X-API-Key': auth.secret_text }, queryParams: qs, }); const tasks = response.body.tasks ?? []; for (const { id, name } of tasks) options.push({ label: name, value: id }); nextCursor = response.body.meta.nextCursor; } while (nextCursor);该分页模式在 Find Task 动作与 Task Created 触发器中同样出现,是三处共用的数据获取范式。
5.4 priority —— 静态下拉
Property.StaticDropdown提供 4 个固定选项:ASAP、HIGH、MEDIUM、LOW,required: false,与 Motion API 的优先级枚举一一对应。
6. 内置动作逐一解析
以下 6 个动作均声明了audience: 'both'(界面与 AI Agent 均可用)及aiMetadata(面向 LLM 的工具描述与幂等性标注),这使它们可直接被 ActivePieces 的 AI Agent / MCP 场景调用。
6.1 Create Task(POST /tasks)
create-task.ts 属性:Workspace ID(必填)、Task Name(必填)、Description、Due Date(Property.DateTime)、Duration(分钟数)、Status、Priority、Project、Assignee、Labels(字符串数组)。请求体中有一个关键映射:status: propsValue.statusId——下拉的显示值(status 名称)直接作为请求字段status提交。aiMetadata标注idempotent: false,即每次调用都会新建任务。
6.2 Update Task(PATCH /tasks/:id)
update-task.ts 以taskId下拉(必填)定位任务,其余字段(name、description、dueDate、duration、status、priority、projectId、assigneeId、labels)全部可选,只更新提供的字段;aiMetadata标注idempotent: true。值得留意的一处源码事实:请求体中状态字段被写成了staus: statusId(见 update-task.ts 中run方法),与 Create Task 使用的status键不一致,从源码结构看这更像笔误而非 API 约定,阅读或移植该动作时应对此保持警惕。
6.3 Create Project(POST /projects)
create-project.ts 属性:Workspace ID、Project Name(必填)、Description、Due Date(ISO 8601)、Priority、Labels。与创建任务类似,aiMetadata标注idempotent: false。
6.4 Get Task(GET /tasks/:id)
get-task.ts 是最简单的动作:仅需taskId短文本输入,直接GET {BASE_URL}/tasks/{taskId}并返回整个响应体,aiMetadata标注idempotent: true(只读无副作用)。
6.5 Move Task(PATCH /tasks/:id/move)
move-task.ts 演示了同一动态属性被两次实例化的用法:workspaceId('Current Workspace')与newWorkspaceId: workspaceId('Target Workspace')。请求体只包含目标workspaceId,把任务移动到指定工作区。
6.6 Find Task(GET /tasks 带过滤)
find-task.ts 按名称搜索任务:必填 Workspace ID 与 Task Name,可选includeAllStatuses复选框(序列化为'true'/'false'查询参数)、Status、Assignee、Project。与 5.3 相同的游标分页循环会拉完所有匹配页,最终返回{ found: result.length > 0, result }结构——found布尔值对下游条件分支非常方便。
7. Task Created 触发器:轮询 + 时间去重
task-created.ts 是唯一的触发器,采用TriggerStrategy.POLLING:
- 轮询取数:
polling.items同样以游标分页拉取GET {BASE_URL}/tasks?workspaceId=...全量任务,然后把每条任务的createdTime用dayjs(task.createdTime).valueOf()转成毫秒时间戳,产出{ epochMilliSeconds, data }序列; - 去重策略:
strategy: DedupeStrategy.TIMEBASED——框架以创建时间作为事件时间基准,只推送"新于上次水位"的任务,避免每次轮询重复触发; - 生命周期钩子:
onEnable/onDisable/test/run全部委托给@activepieces/pieces-common的pollingHelper,onEnable会在首次启用时初始化轮询状态(store 中记录水位),这是轮询触发器的标准四件套; - sampleData:触发器内联了一份完整的任务样例(含 id、name、dueDate、priority、labels、assignees、createdTime 等字段),供界面展示和下游动作的字段提示。
触发器只暴露一个属性workspaceId,即"监听某个工作区的任务创建事件"。
8. 请求头与认证映射的一致性
从源码看,本件所有 HTTP 请求(认证探活、5 个动态下拉、6 个动作)都遵循同一约定:认证密钥以X-API-Key请求头传递,且写请求(POST/PATCH)额外携带Content-Type: application/json。createCustomApiCallAction的authMapping也返回'X-API-Key': auth.secret_text,保证"自定义 API 调用"与内置动作的认证行为完全一致。认证值在框架侧以auth.secret_text形式提供,对应PieceAuth.SecretText的存储语义。
9. 国际化与多语言支持
src/i18n/目录包含translation.json及 de、es、fr、ja、nl、pt、ru、vi、zh 共 9 个语言的翻译文件,覆盖动作/触发器的 displayName 与描述。ActivePieces 的社区件惯例是把界面文案纳入 Crowdin 类多语言流程(仓库根目录有 crowdin.yml),该目录即为 Motion 件翻译产物的落盘位置。
10. 小结:一个标准社区件的完整清单
以 Motion 件为样本,一个可运行的 ActivePieces 社区件至少包含:
package.json:workspace 依赖(pieces-framework / pieces-common / core-piece-types / core-utils)+ build/bundle 脚本;src/index.ts:createPiece聚合auth、actions、triggers,建议以createCustomApiCallAction兜底;src/lib/auth.ts:PieceAuth.SecretText+ 一次只读探活请求完成validate;src/lib/common/props.ts:集中BASE_URL与远程选项下拉(注意refreshers级联与游标分页);src/lib/actions/*.ts:每个动作声明name、displayName、audience、aiMetadata(含幂等性标注);src/lib/triggers/*.ts:轮询触发器遵循DedupeStrategy.TIMEBASED+pollingHelper四钩子模式;src/i18n/*.json:多语言文案;- 构建:
turbo run build --filter=@activepieces/piece-motion。
所有结论均可对照仓库中packages/pieces/community/motion/下的源文件直接验证。
【免费下载链接】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),仅供参考