mcpField
F
fcode-factorial-actions

fcode-factorial-actions

github.com/factorialco开发1146 次安装1 天前更新
代码仓库

关于 fcode-factorial-actions

fcode-factorial-actions 是用于将流程配置为 Factorial Actions 的技能,同一实现可由 Factorial 中的多个入口调用。它说明设置契约、调用策略、表单、界面按钮和生命周期行为;Factorial One 工具调用目前尚不可用。

主要功能

  1. 01在 factorial 设置块中配置操作
  2. 02通过界面按钮和表单公开操作
  3. 03在调用前执行 requiredPolicies 检查
  4. 04设置同步或异步结果处理
  5. 05说明 Factorial One 工具契约

适用场景

  1. 01设置可由 Factorial 按钮或表单调用的流程
  2. 02限制只有满足策略的用户才能执行操作
  3. 03配置文件上传和公开表单访问
  4. 04为未来的 Factorial One 支持准备 agentTool 元数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/factorialco/factorial-code-skills --skill fcode-factorial-actions

安装后在 Claude Code 中输入/fcode-factorial-actions确认已加载。

详细介绍

Factorial Code — Factorial Actions

Factorial Action 是客户安装 App 后,能够在 Factorial 内部触达的流程,客户无需知道 Factorial Code 的存在。一个流程就是一个 Action,同一个 Action 可以同时通过多个入口公开——Factorial UI 中的按钮、表单、Factorial One 工具、后端任务。无论入口是什么,Factorial 都会向流程传递参数,并在需要时等待结果;流程不知道是哪个入口启动了它,因此一种实现即可服务所有入口。

Action 所需的一切都位于一个设置块 factorial 中:控制台流程页面中的 Trigger from Factorial 部分,或 processes/<slug>/metadata.json 的 factorial 键(可通过 fcode pull / fcode push 往返同步;字段参考见 fcode-cli)。

当前已上线和即将推出的功能

目前可用即将推出
控制台、metadata.json 中的 factorial 块,以及 API 中的 settings.factorialFactorial One 工具:助手读取 agentTool 契约并调用流程
Factorial 调用的 Actions API:列出某公司的 Actions、读取 Action 的表单架构、调用 Action、签发上传授权,以及在 App 安装前列出该 App 声明的 Actions
每次调用都会强制执行 requiredPolicies,且在任何操作运行之前执行
uiTrigger:按钮根据此契约列出并运行 Action,通过不透明的 action_id 寻址——浏览器不会接触流程 slug
form:从 Factorial 打开的表单通过 Factorial 后端提交;后端会携带用户身份并注入可信上下文
除非 form.public 解除限制,否则表单必须进行 Factorial 身份验证;该开关控制表单端点检查的身份验证模式
文件参数(x-fcode-file)通过授权直接上传到 Factorial Code 存储,提交内容携带 fcode.storage:// 路径
uiTrigger.label 在 ?locale= 列表中通过 fcode.i18n("key") 令牌本地化
从流程的 form.appRole 派生 lifecycleRole;若未声明,则从保留 slug 派生,并在控制台中标记

因此,当前 uiTrigger、form、requiredPolicies 和 awaitResult 都已生效。agentTool 和 backend 已存储且可访问,但目前还没有助手读取工具契约——现在就填写它们,让 Action 在该功能上线时做好准备。

注意事项

  • enabled 是总开关。 当它为 false 时,下面的内容都不会暴露出来,无论各入口自己的 enabled 值是什么。重新启用后,入口会恢复到原来的状态。
  • awaitResult 默认为 true(同步)。 旧版 uiTrigger 默认为触发后即返回。镜像会将 factorial.awaitResult 的值复制到 uiTrigger.awaitResult,因此,将按钮移到 factorial 区块后,会变为同步,除非你写入 "awaitResult": false。同步操作的耗时应控制在约 50 秒内(fcode-ui-triggers)。
  • requiredPolicies 会被强制执行,但键名是自由文本。 Factorial 会在运行任何操作前检查这些键。它不会检查键是否 存在:不认识的键永远不会被授予,因此包含该键的整个 AND 组都会失败——拼写错误只会关闭操作,不会开放操作。请从 Factorial 的策略目录中选取键;与其使用一个很长的组,不如优先使用多个较小的替代组。
  • 没有策略时,操作不受限制。 列表为空或未提供列表时,任何能够访问该入口的人都可以运行该操作——此时只剩下入口自身的限制(例如按钮的位置权限、是否安装应用)。非空列表中的空 组 会被丢弃,而不会被视为“允许所有人”,因此 [["a"], []] 仍要求满足 a。
  • 只有 uiTrigger.label 可以翻译。 这是用户唯一能看到的文本,因此应使用 fcode.i18n("key") 令牌(fcode-i18n)。agentTool 中的文本供模型使用——它们是单一语言的纯文本,绝不会展示给用户。进程自身的 name / description 面向用户和市场卡片;该区块本身没有标题。
  • 未设置 effect 时会被视为 DESTRUCTIVE。 在你将其设为 READ 或 WRITE 之前,每个工具都会默认拒绝操作(要求审批并显示“无法撤销”警告)。
  • 四个生命周期 slug 是保留值。 无论区块如何,install、settings、uninstall、sync 对 Factorial 都有特殊含义。请勿将它们用于普通操作。
  • 省略默认值。 fcode pull 会省略值为 true 的 awaitResult、空的 requiredPolicies、值为 false 的 form.public 和 form.appTool、未设置的 agentTool 文本和空列表,以及任何 enabled 为 false 的子区块。未向 Factorial 暴露的进程完全不会有 factorial 键。部分更新会保留未指定字段的原值。
  • 旧版 form / uiTrigger 键仍可往返保留。 当文件同时包含两者时,fcode push 会发送 factorial,并让镜像更新其余内容。建议优先编写 factorial(见下文)。

factorial 区块,逐字段说明

{
  "factorial": {
    "enabled": true,
    "awaitResult": false,
    "requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
    "uiTrigger": { "enabled": true, "locationId": "calendar.header.admin", "label": "fcode.i18n(\"acme.approve.button\")", "icon": "Bell" },
    "form": { "enabled": true, "public": false, "appTool": true },
    "agentTool": { "enabled": true, "description": "…", "effect": "WRITE" },
    "backend": { "enabled": true }
  }
}
字段类型 / 默认值含义
enabled布尔值,false下方所有入口的总开关
awaitResult布尔值,truetrue:Factorial 等待并接收进程的返回值,且不作更改(fcode-ui-triggers)。false:Factorial 只获知执行已启动;进程自行报告结果(FactorialClient,通知)。导入和批量处理请使用 false
requiredPoliciesstring[][],[]用户必须持有的 Factorial 策略键的与组之或:[["a","b"],["c"]] 表示 (a 和 b) 或 c。为空 = 任何能访问该入口的人。控制台:每行填写一个备选组,组内的键以逗号分隔
uiTrigger.enabled布尔值在 Factorial 内将该操作呈现为按钮
uiTrigger.locationId字符串按钮在 Factorial UI 中呈现的位置(例如 calendar.header.admin)。启用时必填,最多 200 个字符,由负责该页面的 Factorial 团队提供;位置规范见 fcode-ui-triggers
uiTrigger.label字符串按钮文字。唯一的 i18n 字段:纯文本或 fcode.i18n("key") 标记
uiTrigger.icon字符串允许使用的图标名称(fcode-ui-triggers);若按钮仅显示文字,则省略
form.enabled布尔值将参数作为表单公开(fcode-forms)。与 Forms 标志共用同一开关和配额
form.appTool布尔值,false在 Factorial 中 App 自己的页面上,向公司的用户提供该表单,列在 App 允许他们运行的功能中,例如“报告同步问题”或“立即同步”表单。如果表单由 App 自行打开,请保持为 false。这是该区块对旧字段 form.appRole: USER_FACING_FORM 的命名;两者会保持同步
form.public布尔值,false将表单开放给任何持有其链接的人,无需 Factorial 账号。此开关对应旧字段 form.authMode,表单端点会强制执行该字段;两者会保持同步:开启时写入 NONE,关闭时写入 FACTORIAL。除非表单面向 Factorial 以外的人群,否则请保持关闭;开启后,调用者不会经过任何身份验证
agentTool.enabled布尔值允许 Factorial One 助手将该进程作为工具调用。其余子区块定义了工具规范(见下一节)
backend.enabled布尔值允许 Factorial 后端任务在没有用户参与的情况下运行该进程

Factorial One 工具规范(agentTool)

参数架构会告诉助手工具接受什么;此区块会告诉助手工具的用途。使用纯文本,不支持 i18n。

字段类型对模型的作用
description字符串用一两句话说明工具的功能:指出它操作的对象和预期结果。系统会据此匹配用户请求
effectREAD | WRITE | DESTRUCTIVEREAD 无需询问即可运行;WRITE 需要请求批准;DESTRUCTIVE 会请求批准,并警告操作无法撤销。未设置 ⇒ DESTRUCTIVE
whenToUse字符串用用户可能提出请求的方式,描述适用场景
whenNotToUse字符串[]每项描述一种相似但不匹配的场景,并指出应改用的相邻工具
preconditions字符串[]每项说明调用前必须满足的条件;助手会先检查或询问,而不是直接调用后失败
doesNotDo字符串[]每项说明用户可能以为会发生、但该工具实际上不会产生的副作用
degradation字符串说明部分成功时会发生什么:完成了什么、跳过了什么,以及结果如何说明这些情况
simulatesFor字符串此工具预览的进程 slug(即该进程的试运行)。纯文本,尚未验证
编写优质的契约文本
  • 使用通俗语言,不做国际化,只用一种语言。 面向阅读英文的模型编写, 而不是面向屏幕。这里绝不要放入 fcode.i18n(...) 标记。
  • description:对象 + 结果。“批准团队所有待处理的休假申请” 胜过“休假工具”。
  • whenToUse 中只描述一种情况。 如果需要用“或”连接,你很可能有两个 工具。用用户会提出的方式来表述。
  • 在 whenNotToUse 中注明相邻工具。“用户想批准单个申请:使用 approve-timeoff-request。”每个条目只描述一种情况。
  • preconditions 是可核验的事实(“团队至少有一个待处理申请”), 而不是建议。
  • doesNotDo 列出用户可能会以为会发生的事情。 通知、附带写入、撤销—— 明确说明哪些事情不会发生,以便助手提供缺失的步骤。
  • degradation 描述部分完成的结果,这样助手就能解释结果,而不是报告失败。
  • 试运行流程通过 simulatesFor 指向对应的真实流程,且自身为 READ; 真实流程则为 WRITE 或 DESTRUCTIVE。

保留的生命周期 slug

Slug作用平台执行的操作
install公司安装 App 时 Factorial 显示的表单推导出 lifecycleRole: INSTALL
settings已安装 App 的公司用来配置 App 的表单SETTINGS
uninstall公司移除 App 时运行UNINSTALL
syncintegrations-framework App 的同步流程SYNC

lifecycleRole 是只读的:控制台会在流程列表和流程标题中显示带底色的 安装 / 设置 / 卸载 / 同步 标签,CLI 和 API 会报告它,但不会有任何操作写入它(它不在 metadata.json 中,也不包含在内容哈希中)。install、settings 和 uninstall 是 marketplace 生命周期表单;与任何表单一样,它们仍使用 factorial.form.enabled。重新打开的 install/settings 表单所用的预渲染模式见 fcode-forms。

它是根据流程声明的 form.appRole 推导出来的,只有流程未声明该字段时才使用保留 slug。顺序必须如此:marketplace 目录一直根据 appRole 来识别这些表单,而生产 环境中的 App 会将表单命名为 datev-install、net-empregos-install 等,并始终设置 正确的角色。如果仅根据 slug 识别,这些 App 就完全不会有 install、settings 或 uninstall 表单——而等待配置的安装只能看到自己的生命周期表单,因此也无法完成安装。

回退机制将永久保留。 appRole 没有弃用日期,也不要求你执行任何迁移。新流程 使用保留 slug,因为名称更清晰;声明了角色的流程仍会照常工作。

在现有 App 中采用保留 slug

这是可选操作,只有为了使用更清晰的名称时才值得做。fcode push 按 slug 将本地流程与远程流程匹配,因此重命名流程文件夹不会重命名流程:它会创建第二个 流程,并将第一个流程留在工作区中。请在同一次 push 中停用旧流程。

  1. 创建一个使用保留 slug 的流程,并迁移源文件和 factorial 块。
  2. 在旧流程上设置 factorial.form.enabled: false,并在同一次 push 中 推送。不要让两个流程都保持启用:两个流程争用一个生命周期槽位时,slug 按字典序 排在最前的流程会胜出;胜出的可能是旧名称(datev-install < install), 也可能是新名称(install < net-empregos-install)。冲突会作为 duplicate_role_warnings 条目报告在 App 的目录条目中。
  3. 执行 fcode push。

操作 ID 会发生变化,因为它是根据 slug 推导出来的。没有任何持久化内容会引用它, 因此安装及其配置不会受到影响;已加入书签的表单 URL 将无法再解析。

参数:与 Factorial 的契约

无论入口点是什么,Factorial 发送的内容都由流程的 parametersSchema.json(fcode-json-schema)描述。表单会根据它渲染,助手会读取它以了解工具接受哪些参数,Factorial 则会根据它进行验证。为每个参数提供 title 和 description,在 required 中列出必填项,并将任何架构变更视为Factorial 可以向你传入什么内容的变更。UI 触发器注入的可信键(company_id、 triggered_from_location、access_id)详见 fcode-ui-triggers。

从 form 和 uiTrigger 过渡

factorial 块取代 Forms 标志(form)和 UI trigger 按钮(uiTrigger)。目前两者都会保留,平台会让它们保持镜像同步:无论你修改哪一侧,另一侧都会更新,以最后一次写入为准。

写入 factorial…………会更新旧版设置
enabled && uiTrigger.enableduiTrigger.enabled
uiTrigger.locationId、uiTrigger.icon、uiTrigger.labeluiTrigger 中的同名键
awaitResultuiTrigger.awaitResult
enabled && form.enabledform.enabled
form.appToolform.appRole = USER_FACING_FORM

写入旧版块也会将相同字段镜像回去;只要启用了任一入口点,factorial.enabled 就会开启。

form.appRole 是该块部分管理的唯一旧版字段。它的四个值中,USER_FACING_FORM 对应 form.appTool——开启此标志会占用该值,关闭时仅当该值原本由它占用才会释放;如果写入时未提及 appTool,则角色保持不变。因此,在此标志出现之前写入的文件推送时会保留其角色:CLI 会读取 appRole 并发送相应标志。

INSTALL / SETTINGS / UNINSTALL 则属于另一类:该块没有对应的开关,因此它们仍保留在 form.appRole 中,lifecycleRole 也会优先读取该字段。继续在此处写入这些值——对于完全未声明角色的流程,预留 slug 是另一种选择,并非要求你迁移过去的替代方案。

优先使用 factorial:在新流程中写入它;修改旧的 metadata.json 时,也应将设置迁入其中,而不是编辑 uiTrigger。

示例——SILTRA FIE 导入(试运行 + 应用)

改编自 siltra-integration-app:fie-import 会读取公司从 SILTRA 下载的 .msj 文件,并报告它会在 Factorial 中创建、关联或关闭哪些病假记录,但不会写入任何内容;fie-import-apply 会在后台执行写入。

processes/fie-import/metadata.json:

{
  "name": "FIE Import",
  "description": "Reads the .msj file the company downloads from SILTRA and shows what importing it would change in Factorial. Writes nothing — the import only happens after confirmation.",
  "tags": ["inbound", "siltra", "fie"],
  "factorial": {
    "enabled": true,
    "requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
    "uiTrigger": {
      "enabled": true,
      "locationId": "calendar.header.admin",
      "label": "fcode.i18n(\"calendar.import.fie.button\")",
      "icon": "Upload"
    },
    "form": { "enabled": true },
    "agentTool": {
      "enabled": true,
      "description": "Reads a SILTRA FIE transmission (.msj) and reports which sick leaves it would create, link or close in Factorial, and which events it would leave out and why. It writes nothing.",
      "effect": "READ",
      "whenToUse": "The user has downloaded a FIE file from SILTRA and wants to know what importing it would change before anything is written.",
      "whenNotToUse": [
        "The user has already reviewed the report and wants the leaves written: use fie-import-apply.",
        "The file is not a SILTRA .msj transmission."
      ],
      "preconditions": ["The .msj file has been uploaded and its storage reference is passed as msj_file."],
      "doesNotDo": ["It does not create, change or close any leave.", "It does not notify employees or managers."],
      "degradation": "If Factorial cannot be consulted, the report still lists every event in the file, marked as not compared against Factorial.",
      "simulatesFor": "fie-import-apply"
    }
  }
}

processes/fie-import-apply/metadata.json——没有按钮、没有表单、异步执行,并负责这一对流程中的写入:

{
  "name": "FIE Import — apply",
  "tags": ["inbound", "siltra", "fie"],
  "factorial": {
    "enabled": true,
    "awaitResult": false,
    "requiredPolicies": [["company.manage_timeoff"], ["company.admin"]],
    "agentTool": {
      "enabled": true,
      "description": "Writes the sick leaves a SILTRA FIE transmission describes into Factorial: creates, links or closes them.",
      "effect": "WRITE",
      "whenToUse": "The user has seen the fie-import report for a file and asks to apply it.",
      "whenNotToUse": ["The user only wants to know what the file would change: use fie-import."],
      "preconditions": ["fie-import has reported on this same file and the user confirmed the result."],
      "doesNotDo": ["It does not send SILTRA anything back.", "It does not notify employees."],
      "degradation": "Leaves that fail to write are skipped and listed in the result; the rest are applied."
    },
    "backend": { "enabled": true }
  }
}

策略键仅供示例——请从 Factorial 的策略目录中获取实际键。照常读取参数(fcode.context.parameters.msj_file,一个 fcode.storage://… 引用——fcode-forms)。

发布前检查清单

  1. 启用 factorial.enabled,并且仅启用你打算公开的入口点。
  2. 明确决定 awaitResult 的值:任何耗时操作都设为 false;同步操作应返回其运行所在入口点能够读取的数据结构。
  3. requiredPolicies 的键应从 Factorial 的目录中复制,而非猜测——也可以有意留空,这会让任何能访问该操作的人都可以使用它。
  4. agentTool:设置 effect,在 whenNotToUse 中列出相邻工具;试运行应通过 simulatesFor 指向实际流程。
  5. 普通操作不得使用 install、settings、uninstall 或 sync 作为 slug;架构中应包含 title、description 和 required。

评论

支持 Markdown