@oomol-lab/open-flow 0.1.0-beta.73 → 0.1.0-beta.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +36 -0
- package/NOTICE +7 -0
- package/README.md +41 -0
- package/dist/browser/{ContentIcon-CUQHjQIw.js → ContentIcon-FCpKiwjp.js} +2 -1
- package/dist/browser/{FlowCanvas-Dtf8_cQp.js → FlowCanvas-dzRBnlUT.js} +1 -1
- package/dist/browser/{createResourceDialog-BWJkjiOO.js → createResourceDialog-BeYDdUl-.js} +2 -2
- package/dist/browser/{dialog-CUy_fwx9.js → dialog-ZtpsFbl4.js} +337 -31
- package/dist/browser/{exportSvg-BOoy7-Db.js → exportSvg-DqXw1yf-.js} +2 -2
- package/dist/browser/{flowChanges-CgSnSqqP.js → flowChanges-b_5UobFj.js} +586 -87
- package/dist/browser/{flowWorkspace-Bf7H-r0t.js → flowWorkspace-ItojZbO4.js} +21162 -20242
- package/dist/browser/preview.css +254 -179
- package/dist/browser/preview.js +22201 -20625
- package/dist/browser/{typeScriptSession-B0hC-C8y.js → typeScriptSession-CSRwWmZm.js} +1 -1
- package/dist/browser/ui.css +72 -2
- package/dist/browser/workbench.css +254 -179
- package/dist/browser/workbench.js +22 -78
- package/dist/browser/{workbenchSelect-C3e4J93X.js → workbenchSelect-M441Lz0d.js} +1 -1
- package/dist/command/index.js +42 -42
- package/dist/common/authoring.d.ts +5 -5
- package/dist/common/authoringExamples.d.ts +3 -2
- package/dist/common/authoringFields.d.ts +1 -1
- package/dist/common/authoringSchema.d.ts +95 -8
- package/dist/common/connector-proxy.d.ts +1 -1
- package/dist/common/control-api-conformance.d.ts +1 -1
- package/dist/common/control-api-conformance.js +119 -31
- package/dist/common/control-api.d.ts +4 -3
- package/dist/common/control-api.js +117 -29
- package/dist/common/control-requests.d.ts +5 -4
- package/dist/common/control-requests.js +437 -103
- package/dist/common/cron-trigger.d.ts +1 -1
- package/dist/common/decision.d.ts +1 -1
- package/dist/common/draftOperations.d.ts +1 -1
- package/dist/common/engine-contract.d.ts +4 -2
- package/dist/common/event-source-definition.d.ts +1 -1
- package/dist/common/flow-agent.d.ts +1 -1
- package/dist/{browser → common}/flow-authoring-node.d.ts +3 -3
- package/dist/{browser → common}/flow-authoring.js +131 -30
- package/dist/{browser → common}/flow-change-schema.d.ts +177 -6
- package/dist/{browser → common}/flow-change.d.ts +35 -15
- package/dist/{browser → common}/flow-change.js +133 -32
- package/dist/common/flow-connectionUsage.d.ts +1 -1
- package/dist/common/flow-encoding.d.ts +1 -1
- package/dist/common/flow-encoding.js +89 -23
- package/dist/common/flow-errorHandling.d.ts +1 -1
- package/dist/common/flow-graph.d.ts +3 -2
- package/dist/common/flow-modules.d.ts +1 -1
- package/dist/common/flow-promptTemplate.d.ts +1 -1
- package/dist/common/flow-retry.d.ts +2 -2
- package/dist/common/flow-semantics.d.ts +1 -1
- package/dist/common/flow-semantics.js +966 -614
- package/dist/common/flow-workflowFile.d.ts +1 -1
- package/dist/common/flowInspection.d.ts +3 -3
- package/dist/common/http-request.d.ts +156 -0
- package/dist/common/http-request.js +5254 -0
- package/dist/common/integration-trigger.d.ts +1 -1
- package/dist/common/licenses.md +118 -1
- package/dist/common/liveDetails.d.ts +1 -1
- package/dist/common/mcp.d.ts +5 -4
- package/dist/common/mcp.js +117 -29
- package/dist/common/openapi.d.ts +1 -1
- package/dist/common/poll-trigger.d.ts +1 -1
- package/dist/common/run-events.js +11 -9
- package/dist/common/run-results.d.ts +8 -1
- package/dist/common/runtime-contract.d.ts +8 -2
- package/dist/common/runtime-contract.js +25 -5
- package/dist/common/scheduler-checkpoint.d.ts +1 -1
- package/dist/common/scheduler-dependencies.d.ts +1 -1
- package/dist/common/scheduler-node-execution.d.ts +1 -1
- package/dist/common/scheduler.d.ts +3 -3
- package/dist/common/scheduler.js +101 -33
- package/dist/common/trigger-catalog.d.ts +1 -1
- package/dist/common/trigger-config-options.d.ts +1 -1
- package/dist/common/trigger-config.d.ts +1 -1
- package/dist/common/trigger-contract.d.ts +2 -2
- package/dist/common/trigger-localization.d.ts +1 -1
- package/dist/common/webhook-response.d.ts +1 -3
- package/dist/common/webhook-trigger.d.ts +1 -1
- package/dist/common/webhook-trigger.js +12 -11
- package/dist/common/workflow.d.ts +17 -1
- package/dist/common/workflow.js +68 -22
- package/dist/index.d.ts +6 -3
- package/package.json +11 -6
- package/skills/open-flow/references/flow-authoring.md +1 -0
- package/skills/open-flow/references/flow-n8n-conversion.md +19 -15
- package/src/base/common/json.ts +90 -0
- package/src/canvas/common/commands.ts +15 -0
- package/src/connector/common/actionSchema.ts +168 -0
- package/src/connector/common/proxy.ts +44 -0
- package/src/control/common/api.ts +1506 -0
- package/src/control/common/authoring.ts +923 -0
- package/src/control/common/authoringCondition.ts +110 -0
- package/src/control/common/authoringDiagnosticFields.ts +136 -0
- package/src/control/common/authoringExamples.ts +200 -0
- package/src/control/common/authoringFields.ts +53 -0
- package/src/control/common/authoringLlm.ts +70 -0
- package/src/control/common/authoringOpenApi.ts +129 -0
- package/src/control/common/authoringSchema.ts +410 -0
- package/src/control/common/authoringTools.ts +123 -0
- package/src/control/common/authoringTriggers.ts +26 -0
- package/src/control/common/checkDecoders.ts +65 -0
- package/src/control/common/conformance.ts +1989 -0
- package/src/control/common/connectorDecoders.ts +370 -0
- package/src/control/common/connectorQueries.ts +61 -0
- package/src/control/common/decoding.ts +60 -0
- package/src/control/common/draftOperations.ts +61 -0
- package/src/control/common/errors.ts +114 -0
- package/src/control/common/eventSourceSchemas.ts +13 -0
- package/src/control/common/eventSources.ts +186 -0
- package/src/control/common/flowDecoders.ts +153 -0
- package/src/control/common/flowInspection.ts +181 -0
- package/src/control/common/flowNotifications.ts +37 -0
- package/src/control/common/liveDetails.ts +124 -0
- package/src/control/common/mcp.ts +241 -0
- package/src/control/common/mcpConformance.ts +142 -0
- package/src/control/common/providerAccess.ts +28 -0
- package/src/control/common/providerIconSprite.ts +42 -0
- package/src/control/common/publicationDecoders.ts +98 -0
- package/src/control/common/random.ts +16 -0
- package/src/control/common/requests.ts +112 -0
- package/src/control/common/resultQuery.ts +8 -0
- package/src/control/common/results.ts +176 -0
- package/src/control/common/revisionDecoders.ts +52 -0
- package/src/control/common/runDecoders.ts +240 -0
- package/src/control/common/triggerCatalog.ts +89 -0
- package/src/control/common/triggerDecoders.ts +175 -0
- package/src/decision/common/decision.ts +168 -0
- package/src/event-source/common/catalog.generated.json +65 -0
- package/src/event-source/common/definition.ts +66 -0
- package/src/event-source/common/index.ts +99 -0
- package/src/execution/common/checkpoint.ts +311 -0
- package/src/execution/common/dependencies.ts +440 -0
- package/src/execution/common/engineContract.ts +84 -0
- package/src/execution/common/events.ts +331 -0
- package/src/execution/common/node-execution.ts +469 -0
- package/src/execution/common/runLifecycle.ts +250 -0
- package/src/execution/common/runtime.ts +563 -0
- package/src/execution/common/scheduler.ts +697 -0
- package/src/execution/common/wait.ts +36 -0
- package/src/flow/common/agent.ts +305 -0
- package/src/flow/common/authoring.ts +30 -0
- package/src/flow/common/change.ts +1037 -0
- package/src/flow/common/changeSchema.ts +792 -0
- package/src/flow/common/codePermissions.ts +2 -0
- package/src/flow/common/condition.ts +265 -0
- package/src/flow/common/connectionUsage.ts +131 -0
- package/src/flow/common/edgeChanges.ts +35 -0
- package/src/flow/common/encoding.ts +437 -0
- package/src/flow/common/errorHandling.ts +34 -0
- package/src/flow/common/graph.ts +898 -0
- package/src/flow/common/inputValue.ts +16 -0
- package/src/flow/common/json.ts +8 -0
- package/src/flow/common/moduleChanges.ts +31 -0
- package/src/flow/common/modules.ts +292 -0
- package/src/flow/common/nodeChanges.ts +762 -0
- package/src/flow/common/portChanges.ts +36 -0
- package/src/flow/common/promptTemplate.ts +15 -0
- package/src/flow/common/retry.ts +20 -0
- package/src/flow/common/schema.ts +162 -0
- package/src/flow/common/schemaCompare.ts +176 -0
- package/src/flow/common/schemaComparer.ts +178 -0
- package/src/flow/common/schemaKeywords.ts +53 -0
- package/src/flow/common/semantics.ts +531 -0
- package/src/flow/common/sourceField.ts +35 -0
- package/src/flow/common/triggerScheduleSchema.ts +8 -0
- package/src/flow/common/wait.ts +30 -0
- package/src/flow/common/webhookMethod.ts +7 -0
- package/src/flow/common/webhookResponse.ts +71 -0
- package/src/flow/common/workflowFile.ts +97 -0
- package/src/http/common/authBindings.ts +12 -0
- package/src/http/common/headers.ts +5 -0
- package/src/http/common/httpRequest.ts +251 -0
- package/src/json-schema-subset/checker/array.ts +142 -0
- package/src/json-schema-subset/checker/cache.ts +57 -0
- package/src/json-schema-subset/checker/checker.ts +230 -0
- package/src/json-schema-subset/checker/enumAndConst.ts +113 -0
- package/src/json-schema-subset/checker/equals.ts +93 -0
- package/src/json-schema-subset/checker/numeric/checker.ts +50 -0
- package/src/json-schema-subset/checker/numeric/float.ts +121 -0
- package/src/json-schema-subset/checker/numeric/index.ts +1 -0
- package/src/json-schema-subset/checker/numeric/integer.ts +119 -0
- package/src/json-schema-subset/checker/numeric/integerNormalizer.ts +72 -0
- package/src/json-schema-subset/checker/object.ts +354 -0
- package/src/json-schema-subset/checker/range.ts +91 -0
- package/src/json-schema-subset/checker/string.ts +60 -0
- package/src/json-schema-subset/checker/tools/index.ts +3 -0
- package/src/json-schema-subset/checker/tools/mark.ts +82 -0
- package/src/json-schema-subset/checker/tools/quantum.ts +106 -0
- package/src/json-schema-subset/checker/tools/range.ts +210 -0
- package/src/json-schema-subset/checker/validator.ts +23 -0
- package/src/json-schema-subset/compiler/array.ts +163 -0
- package/src/json-schema-subset/compiler/combination.ts +109 -0
- package/src/json-schema-subset/compiler/common.ts +99 -0
- package/src/json-schema-subset/compiler/compiler.ts +188 -0
- package/src/json-schema-subset/compiler/enumAndConst.ts +75 -0
- package/src/json-schema-subset/compiler/index.ts +11 -0
- package/src/json-schema-subset/compiler/object.ts +411 -0
- package/src/json-schema-subset/compiler/primitive.ts +144 -0
- package/src/json-schema-subset/compiler/utils.ts +24 -0
- package/src/json-schema-subset/compiler/wrapper.ts +192 -0
- package/src/json-schema-subset/error.ts +55 -0
- package/src/json-schema-subset/expression/calculator.ts +442 -0
- package/src/json-schema-subset/expression/combination.ts +49 -0
- package/src/json-schema-subset/expression/expression.ts +191 -0
- package/src/json-schema-subset/expression/index.ts +25 -0
- package/src/json-schema-subset/expression/result.ts +88 -0
- package/src/json-schema-subset/expression/types.ts +36 -0
- package/src/json-schema-subset/extends/extendsContext.ts +89 -0
- package/src/json-schema-subset/extends/index.ts +2 -0
- package/src/json-schema-subset/extends/types.ts +12 -0
- package/src/json-schema-subset/global.ts +60 -0
- package/src/json-schema-subset/index.ts +8 -0
- package/src/json-schema-subset/preprocessing.ts +74 -0
- package/src/json-schema-subset/types.ts +8 -0
- package/src/json-schema-subset/utils.ts +95 -0
- package/src/localization/common/languages.ts +118 -0
- package/src/localization/common/timeZone.ts +25 -0
- package/src/openapi/common/openapi.ts +459 -0
- package/src/trigger/common/config.ts +56 -0
- package/src/trigger/common/configOptions.ts +15 -0
- package/src/trigger/common/configSchema.ts +8 -0
- package/src/trigger/common/contract.ts +132 -0
- package/src/trigger/common/cron.ts +151 -0
- package/src/trigger/common/integration.ts +498 -0
- package/src/trigger/common/poll.ts +187 -0
- package/src/trigger/common/webhook.ts +413 -0
- package/src/trigger/providers/airtable/locales/fr.json +18 -0
- package/src/trigger/providers/airtable/locales/ja.json +18 -0
- package/src/trigger/providers/airtable/locales/ko.json +18 -0
- package/src/trigger/providers/airtable/locales/ru.json +18 -0
- package/src/trigger/providers/airtable/locales/zh-CN.json +18 -0
- package/src/trigger/providers/airtable/locales/zh-TW.json +18 -0
- package/src/trigger/providers/asana/locales/fr.json +15 -0
- package/src/trigger/providers/asana/locales/ja.json +15 -0
- package/src/trigger/providers/asana/locales/ko.json +15 -0
- package/src/trigger/providers/asana/locales/ru.json +15 -0
- package/src/trigger/providers/asana/locales/zh-CN.json +15 -0
- package/src/trigger/providers/asana/locales/zh-TW.json +15 -0
- package/src/trigger/providers/calendly/locales/fr.json +16 -0
- package/src/trigger/providers/calendly/locales/ja.json +16 -0
- package/src/trigger/providers/calendly/locales/ko.json +16 -0
- package/src/trigger/providers/calendly/locales/ru.json +16 -0
- package/src/trigger/providers/calendly/locales/zh-CN.json +16 -0
- package/src/trigger/providers/calendly/locales/zh-TW.json +16 -0
- package/src/trigger/providers/catalog.generated.json +4996 -0
- package/src/trigger/providers/clickup/locales/fr.json +16 -0
- package/src/trigger/providers/clickup/locales/ja.json +16 -0
- package/src/trigger/providers/clickup/locales/ko.json +16 -0
- package/src/trigger/providers/clickup/locales/ru.json +16 -0
- package/src/trigger/providers/clickup/locales/zh-CN.json +16 -0
- package/src/trigger/providers/clickup/locales/zh-TW.json +16 -0
- package/src/trigger/providers/definitions.ts +19 -0
- package/src/trigger/providers/feishu/locales/fr.json +20 -0
- package/src/trigger/providers/feishu/locales/ja.json +20 -0
- package/src/trigger/providers/feishu/locales/ko.json +20 -0
- package/src/trigger/providers/feishu/locales/ru.json +20 -0
- package/src/trigger/providers/feishu/locales/zh-CN.json +20 -0
- package/src/trigger/providers/feishu/locales/zh-TW.json +20 -0
- package/src/trigger/providers/fieldDescriptions.ts +392 -0
- package/src/trigger/providers/fieldLabels.ts +256 -0
- package/src/trigger/providers/fireflies/locales/fr.json +17 -0
- package/src/trigger/providers/fireflies/locales/ja.json +17 -0
- package/src/trigger/providers/fireflies/locales/ko.json +17 -0
- package/src/trigger/providers/fireflies/locales/ru.json +17 -0
- package/src/trigger/providers/fireflies/locales/zh-CN.json +17 -0
- package/src/trigger/providers/fireflies/locales/zh-TW.json +17 -0
- package/src/trigger/providers/github/locales/fr.json +30 -0
- package/src/trigger/providers/github/locales/ja.json +30 -0
- package/src/trigger/providers/github/locales/ko.json +30 -0
- package/src/trigger/providers/github/locales/ru.json +30 -0
- package/src/trigger/providers/github/locales/zh-CN.json +30 -0
- package/src/trigger/providers/github/locales/zh-TW.json +30 -0
- package/src/trigger/providers/gitlab/locales/fr.json +18 -0
- package/src/trigger/providers/gitlab/locales/ja.json +18 -0
- package/src/trigger/providers/gitlab/locales/ko.json +18 -0
- package/src/trigger/providers/gitlab/locales/ru.json +18 -0
- package/src/trigger/providers/gitlab/locales/zh-CN.json +18 -0
- package/src/trigger/providers/gitlab/locales/zh-TW.json +18 -0
- package/src/trigger/providers/gmail/locales/fr.json +18 -0
- package/src/trigger/providers/gmail/locales/ja.json +18 -0
- package/src/trigger/providers/gmail/locales/ko.json +18 -0
- package/src/trigger/providers/gmail/locales/ru.json +18 -0
- package/src/trigger/providers/gmail/locales/zh-CN.json +18 -0
- package/src/trigger/providers/gmail/locales/zh-TW.json +18 -0
- package/src/trigger/providers/google-calendar/locales/fr.json +15 -0
- package/src/trigger/providers/google-calendar/locales/ja.json +15 -0
- package/src/trigger/providers/google-calendar/locales/ko.json +15 -0
- package/src/trigger/providers/google-calendar/locales/ru.json +15 -0
- package/src/trigger/providers/google-calendar/locales/zh-CN.json +15 -0
- package/src/trigger/providers/google-calendar/locales/zh-TW.json +15 -0
- package/src/trigger/providers/google-drive/locales/fr.json +48 -0
- package/src/trigger/providers/google-drive/locales/ja.json +48 -0
- package/src/trigger/providers/google-drive/locales/ko.json +48 -0
- package/src/trigger/providers/google-drive/locales/ru.json +48 -0
- package/src/trigger/providers/google-drive/locales/zh-CN.json +48 -0
- package/src/trigger/providers/google-drive/locales/zh-TW.json +48 -0
- package/src/trigger/providers/google-sheets/locales/fr.json +19 -0
- package/src/trigger/providers/google-sheets/locales/ja.json +19 -0
- package/src/trigger/providers/google-sheets/locales/ko.json +19 -0
- package/src/trigger/providers/google-sheets/locales/ru.json +19 -0
- package/src/trigger/providers/google-sheets/locales/zh-CN.json +19 -0
- package/src/trigger/providers/google-sheets/locales/zh-TW.json +19 -0
- package/src/trigger/providers/intercom/locales/fr.json +17 -0
- package/src/trigger/providers/intercom/locales/ja.json +17 -0
- package/src/trigger/providers/intercom/locales/ko.json +17 -0
- package/src/trigger/providers/intercom/locales/ru.json +17 -0
- package/src/trigger/providers/intercom/locales/zh-CN.json +17 -0
- package/src/trigger/providers/intercom/locales/zh-TW.json +17 -0
- package/src/trigger/providers/jira/locales/fr.json +17 -0
- package/src/trigger/providers/jira/locales/ja.json +17 -0
- package/src/trigger/providers/jira/locales/ko.json +17 -0
- package/src/trigger/providers/jira/locales/ru.json +17 -0
- package/src/trigger/providers/jira/locales/zh-CN.json +17 -0
- package/src/trigger/providers/jira/locales/zh-TW.json +17 -0
- package/src/trigger/providers/linear/locales/fr.json +13 -0
- package/src/trigger/providers/linear/locales/ja.json +13 -0
- package/src/trigger/providers/linear/locales/ko.json +13 -0
- package/src/trigger/providers/linear/locales/ru.json +13 -0
- package/src/trigger/providers/linear/locales/zh-CN.json +13 -0
- package/src/trigger/providers/linear/locales/zh-TW.json +13 -0
- package/src/trigger/providers/locales/fr.ts +71 -0
- package/src/trigger/providers/locales/ja.ts +71 -0
- package/src/trigger/providers/locales/ko.ts +71 -0
- package/src/trigger/providers/locales/ru.ts +71 -0
- package/src/trigger/providers/locales/zh-CN.ts +71 -0
- package/src/trigger/providers/locales/zh-TW.ts +71 -0
- package/src/trigger/providers/localization.ts +59 -0
- package/src/trigger/providers/notion/locales/fr.json +16 -0
- package/src/trigger/providers/notion/locales/ja.json +16 -0
- package/src/trigger/providers/notion/locales/ko.json +16 -0
- package/src/trigger/providers/notion/locales/ru.json +16 -0
- package/src/trigger/providers/notion/locales/zh-CN.json +16 -0
- package/src/trigger/providers/notion/locales/zh-TW.json +16 -0
- package/src/trigger/providers/one-drive/locales/fr.json +16 -0
- package/src/trigger/providers/one-drive/locales/ja.json +16 -0
- package/src/trigger/providers/one-drive/locales/ko.json +16 -0
- package/src/trigger/providers/one-drive/locales/ru.json +16 -0
- package/src/trigger/providers/one-drive/locales/zh-CN.json +16 -0
- package/src/trigger/providers/one-drive/locales/zh-TW.json +16 -0
- package/src/trigger/providers/outlook/locales/fr.json +18 -0
- package/src/trigger/providers/outlook/locales/ja.json +18 -0
- package/src/trigger/providers/outlook/locales/ko.json +18 -0
- package/src/trigger/providers/outlook/locales/ru.json +18 -0
- package/src/trigger/providers/outlook/locales/zh-CN.json +18 -0
- package/src/trigger/providers/outlook/locales/zh-TW.json +18 -0
- package/src/trigger/providers/pandadoc/locales/fr.json +12 -0
- package/src/trigger/providers/pandadoc/locales/ja.json +12 -0
- package/src/trigger/providers/pandadoc/locales/ko.json +12 -0
- package/src/trigger/providers/pandadoc/locales/ru.json +12 -0
- package/src/trigger/providers/pandadoc/locales/zh-CN.json +12 -0
- package/src/trigger/providers/pandadoc/locales/zh-TW.json +12 -0
- package/src/trigger/providers/pipedrive/locales/fr.json +15 -0
- package/src/trigger/providers/pipedrive/locales/ja.json +15 -0
- package/src/trigger/providers/pipedrive/locales/ko.json +15 -0
- package/src/trigger/providers/pipedrive/locales/ru.json +15 -0
- package/src/trigger/providers/pipedrive/locales/zh-CN.json +15 -0
- package/src/trigger/providers/pipedrive/locales/zh-TW.json +15 -0
- package/src/trigger/providers/remote.ts +163 -0
- package/src/trigger/providers/resend/locales/fr.json +16 -0
- package/src/trigger/providers/resend/locales/ja.json +16 -0
- package/src/trigger/providers/resend/locales/ko.json +16 -0
- package/src/trigger/providers/resend/locales/ru.json +16 -0
- package/src/trigger/providers/resend/locales/zh-CN.json +16 -0
- package/src/trigger/providers/resend/locales/zh-TW.json +16 -0
- package/src/trigger/providers/sentry/locales/fr.json +19 -0
- package/src/trigger/providers/sentry/locales/ja.json +19 -0
- package/src/trigger/providers/sentry/locales/ko.json +19 -0
- package/src/trigger/providers/sentry/locales/ru.json +19 -0
- package/src/trigger/providers/sentry/locales/zh-CN.json +19 -0
- package/src/trigger/providers/sentry/locales/zh-TW.json +19 -0
- package/src/trigger/providers/shopify/locales/fr.json +18 -0
- package/src/trigger/providers/shopify/locales/ja.json +18 -0
- package/src/trigger/providers/shopify/locales/ko.json +18 -0
- package/src/trigger/providers/shopify/locales/ru.json +18 -0
- package/src/trigger/providers/shopify/locales/zh-CN.json +18 -0
- package/src/trigger/providers/shopify/locales/zh-TW.json +18 -0
- package/src/trigger/providers/slack/locales/fr.json +18 -0
- package/src/trigger/providers/slack/locales/ja.json +18 -0
- package/src/trigger/providers/slack/locales/ko.json +18 -0
- package/src/trigger/providers/slack/locales/ru.json +18 -0
- package/src/trigger/providers/slack/locales/zh-CN.json +18 -0
- package/src/trigger/providers/slack/locales/zh-TW.json +18 -0
- package/src/trigger/providers/slackbot/locales/fr.json +23 -0
- package/src/trigger/providers/slackbot/locales/ja.json +23 -0
- package/src/trigger/providers/slackbot/locales/ko.json +23 -0
- package/src/trigger/providers/slackbot/locales/ru.json +23 -0
- package/src/trigger/providers/slackbot/locales/zh-CN.json +23 -0
- package/src/trigger/providers/slackbot/locales/zh-TW.json +23 -0
- package/src/trigger/providers/source-events.ts +25 -0
- package/src/trigger/providers/stripe/locales/fr.json +17 -0
- package/src/trigger/providers/stripe/locales/ja.json +17 -0
- package/src/trigger/providers/stripe/locales/ko.json +17 -0
- package/src/trigger/providers/stripe/locales/ru.json +17 -0
- package/src/trigger/providers/stripe/locales/zh-CN.json +17 -0
- package/src/trigger/providers/stripe/locales/zh-TW.json +17 -0
- package/src/trigger/providers/tally/locales/fr.json +12 -0
- package/src/trigger/providers/tally/locales/ja.json +12 -0
- package/src/trigger/providers/tally/locales/ko.json +12 -0
- package/src/trigger/providers/tally/locales/ru.json +12 -0
- package/src/trigger/providers/tally/locales/zh-CN.json +12 -0
- package/src/trigger/providers/tally/locales/zh-TW.json +12 -0
- package/src/trigger/providers/telegram/locales/fr.json +17 -0
- package/src/trigger/providers/telegram/locales/ja.json +17 -0
- package/src/trigger/providers/telegram/locales/ko.json +17 -0
- package/src/trigger/providers/telegram/locales/ru.json +17 -0
- package/src/trigger/providers/telegram/locales/zh-CN.json +17 -0
- package/src/trigger/providers/telegram/locales/zh-TW.json +17 -0
- package/src/trigger/providers/typeform/locales/fr.json +12 -0
- package/src/trigger/providers/typeform/locales/ja.json +12 -0
- package/src/trigger/providers/typeform/locales/ko.json +12 -0
- package/src/trigger/providers/typeform/locales/ru.json +12 -0
- package/src/trigger/providers/typeform/locales/zh-CN.json +12 -0
- package/src/trigger/providers/typeform/locales/zh-TW.json +12 -0
- package/src/trigger/providers/vercel/locales/fr.json +18 -0
- package/src/trigger/providers/vercel/locales/ja.json +18 -0
- package/src/trigger/providers/vercel/locales/ko.json +18 -0
- package/src/trigger/providers/vercel/locales/ru.json +18 -0
- package/src/trigger/providers/vercel/locales/zh-CN.json +18 -0
- package/src/trigger/providers/vercel/locales/zh-TW.json +18 -0
- package/src/trigger/providers/woocommerce/locales/fr.json +19 -0
- package/src/trigger/providers/woocommerce/locales/ja.json +19 -0
- package/src/trigger/providers/woocommerce/locales/ko.json +19 -0
- package/src/trigger/providers/woocommerce/locales/ru.json +19 -0
- package/src/trigger/providers/woocommerce/locales/zh-CN.json +19 -0
- package/src/trigger/providers/woocommerce/locales/zh-TW.json +19 -0
- package/src/trigger/providers/zendesk/locales/fr.json +15 -0
- package/src/trigger/providers/zendesk/locales/ja.json +15 -0
- package/src/trigger/providers/zendesk/locales/ko.json +15 -0
- package/src/trigger/providers/zendesk/locales/ru.json +15 -0
- package/src/trigger/providers/zendesk/locales/zh-CN.json +15 -0
- package/src/trigger/providers/zendesk/locales/zh-TW.json +15 -0
- package/src/trigger/providers/zoom/locales/fr.json +17 -0
- package/src/trigger/providers/zoom/locales/ja.json +17 -0
- package/src/trigger/providers/zoom/locales/ko.json +17 -0
- package/src/trigger/providers/zoom/locales/ru.json +17 -0
- package/src/trigger/providers/zoom/locales/zh-CN.json +17 -0
- package/src/trigger/providers/zoom/locales/zh-TW.json +17 -0
- package/src/types/index.ts +112 -0
- package/src/workflow/common/workflow.ts +273 -0
- package/dist/browser/lib-DrKav3HG.js +0 -11403
- /package/dist/{browser → common}/flow-authoring-edge.d.ts +0 -0
- /package/dist/{browser → common}/flow-authoring-module.d.ts +0 -0
- /package/dist/{browser → common}/flow-authoring.d.ts +0 -0
- /package/dist/{browser → common}/flow-input-value.d.ts +0 -0
- /package/dist/{browser → common}/webhook-method.d.ts +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { JsonValue } from '../../flow/common/change.ts'
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
import { canvasCommandSchema } from '../../canvas/common/commands.ts'
|
|
5
|
+
import { runStatuses } from '../../execution/common/runLifecycle.ts'
|
|
6
|
+
import { waitCommentSchema } from '../../execution/common/wait.ts'
|
|
7
|
+
import { resourceNameIssue } from '../../flow/common/change.ts'
|
|
8
|
+
import { authoringReadSchema, authoringSearchSchema, authoringRequestSchema, authoringSchemaQuery } from './authoringSchema.ts'
|
|
9
|
+
import { resultQuerySchema } from './resultQuery.ts'
|
|
10
|
+
|
|
11
|
+
export const mcpProtocolVersion = '2026-07-28'
|
|
12
|
+
const json: z.ZodType<JsonValue> = z.json()
|
|
13
|
+
const id = z.string().min(1)
|
|
14
|
+
const mutationKey = id.max(256).describe('Stable identity for this mutation. Retry with the same key and identical arguments, even after a lost response.')
|
|
15
|
+
const pageLimit = z.int().min(1).max(100).default(50)
|
|
16
|
+
const flow = id.describe('Exact Flow ID returned by flow_list or flow_create.')
|
|
17
|
+
const run = id.describe('Exact Run ID returned by flow_run or run_list.')
|
|
18
|
+
export const mcpInstructions =
|
|
19
|
+
'Keep generated Flow and node names short enough to scan on the canvas. Aim for 2–4 English words or 4–8 Chinese characters, using the user\'s language unless the workflow has an established naming language. Name a Flow by its main purpose and a node by its action and object, such as "AI Trend Digest", "Fetch trends", "Verify sources", or "发布文章". Keep only qualifiers needed to distinguish similar nodes. Put detailed behavior in descriptions; avoid sentences, repeated workflow context, and obscure abbreviations. Preserve names explicitly supplied by the user, and do not rename unrelated existing nodes. ' +
|
|
20
|
+
'Use flow_list, flow_read and flow_search to inspect root workflows. Read node type schemas with flow_schema. Use flow_edit with the observed baseRevision and a stable requestId. Node edits affect only the selected node. Text edits require exactly one match. Execution edges and input sources are independent. Legacy Subflow content is unsupported. Edits can save incomplete drafts; inspect returned diagnostics and use flow_check before running. ' +
|
|
21
|
+
'Use flow_check before flow_run. Select an explicit Trigger node ID and fixed revision or publication. A new Flow has no Trigger until you add one. ' +
|
|
22
|
+
'Before adding a shared-source Trigger (Feishu, Slack Bot, Intercom, Sentry, Zoom, Fireflies, or Jira), use event_source_list and compare its Connection IDs with connector_connections in the Flow scope. If no source is available, create and verify one in Workbench. ' +
|
|
23
|
+
'Use flow_publish to publish a fixed Revision, then poll flow_publish_status until succeeded or failed. Use flow_live to inspect current online entries after publication. Use flow_set_enabled to enable or disable the observed Live publication. ' +
|
|
24
|
+
'flow_run returns an accepted Run, not its final result. Find Runs with run_list, poll run_get and use run_result after terminal. Resolve a waiting Run only with an explicit run_resolve_wait action allowed by run_get. ' +
|
|
25
|
+
'Use run_results and run_result_read to inspect stored Agent tool results; these are separate from the terminal Run result. ' +
|
|
26
|
+
'Retry flow_edit with identical arguments and the same requestId. Retry flow_create, flow_import, flow_publish and flow_run only with identical arguments and the same idempotencyKey; a new key can execute the Flow again. Canvas commands are transient broadcasts without execution confirmation; never automatically retry flow_canvas because pan is relative. Other mutations use the same resource identity and arguments when retrying. Cancel a Run explicitly with run_cancel. ' +
|
|
27
|
+
'Use nextCursor and nextAfter to read subsequent pages. Connector-backed deployments may require a Team; inspect connector_teams before creating a Flow.'
|
|
28
|
+
|
|
29
|
+
function tool<Args>(description: string, schema: z.ZodType<Args>, readOnly: boolean, idempotent = true) {
|
|
30
|
+
return {
|
|
31
|
+
description,
|
|
32
|
+
annotations: { readOnlyHint: readOnly, destructiveHint: !readOnly, idempotentHint: idempotent, openWorldHint: true },
|
|
33
|
+
inputSchema: {
|
|
34
|
+
'~standard': {
|
|
35
|
+
version: 1 as const,
|
|
36
|
+
vendor: 'open-flow',
|
|
37
|
+
jsonSchema: {
|
|
38
|
+
input: (): Record<string, unknown> => z.toJSONSchema(schema, { io: 'input' }),
|
|
39
|
+
output: (): Record<string, unknown> => z.toJSONSchema(schema),
|
|
40
|
+
},
|
|
41
|
+
validate(value: unknown): { value: Args } | { issues: { message: string; path: string[] }[] } {
|
|
42
|
+
const result = schema.safeParse(value)
|
|
43
|
+
return result.success
|
|
44
|
+
? { value: result.data }
|
|
45
|
+
: { issues: result.error.issues.map((issue) => ({ message: issue.message, path: issue.path.map(String) })) }
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const mcpTools = {
|
|
53
|
+
flow_canvas: tool(
|
|
54
|
+
'Broadcast a transient canvas command to connected Workbenches viewing this Flow. Success only confirms Server broadcast, not delivery or execution. No replay, session selection or Presentation save. Pan uses screen pixels (positive right/down); zoom is an absolute ratio (0.1–3); fit includes all nodes. Repeating pan moves again.',
|
|
55
|
+
z.strictObject({ flowId: flow, command: canvasCommandSchema }),
|
|
56
|
+
false,
|
|
57
|
+
false,
|
|
58
|
+
),
|
|
59
|
+
flow_live: tool(
|
|
60
|
+
'Read the current published Flow, enablement and online entries, including Webhook URLs and request fields. Draft changes do not affect this view. An unpublished Flow returns not-published with no entries.',
|
|
61
|
+
z.strictObject({ flowId: flow }),
|
|
62
|
+
true,
|
|
63
|
+
),
|
|
64
|
+
flow_list: tool(
|
|
65
|
+
'List Flows in this deployment. Continue with nextCursor; cursors are compatible with the Control API.',
|
|
66
|
+
z.strictObject({ cursor: id.optional(), limit: pageLimit }),
|
|
67
|
+
true,
|
|
68
|
+
),
|
|
69
|
+
flow_read: tool(
|
|
70
|
+
'Read an outline, selected node details, or a bounded code/prompt excerpt. Carry the returned revision into subsequent reads and edits.',
|
|
71
|
+
authoringReadSchema.safeExtend({ flowId: flow }).meta({ ...authoringReadSchema.meta() }),
|
|
72
|
+
true,
|
|
73
|
+
),
|
|
74
|
+
flow_search: tool(
|
|
75
|
+
'Find nodes by name, configuration or source text in a fixed Revision. Follow nextOffset for more matches.',
|
|
76
|
+
authoringSearchSchema.extend({ flowId: flow }),
|
|
77
|
+
true,
|
|
78
|
+
),
|
|
79
|
+
flow_schema: tool(
|
|
80
|
+
'Read node configuration schemas and the edit grammar, or an Action contract. Action lookup requires a flowId.',
|
|
81
|
+
authoringSchemaQuery.safeExtend({ flowId: flow.optional() }),
|
|
82
|
+
true,
|
|
83
|
+
),
|
|
84
|
+
flow_export: tool(
|
|
85
|
+
'Export the current draft as a shareable workflow JSON file. Structured external references are cleared. Literal secrets in code, prompts or business JSON remain; review before sharing.',
|
|
86
|
+
z.strictObject({ flowId: flow }),
|
|
87
|
+
true,
|
|
88
|
+
),
|
|
89
|
+
flow_import: tool(
|
|
90
|
+
'Import workflow JSON as a new unpublished draft. External references are cleared and must be configured again. Keep identical arguments and idempotencyKey when retrying.',
|
|
91
|
+
z.strictObject({ file: json, name: id.optional(), teamId: id.optional(), idempotencyKey: mutationKey }),
|
|
92
|
+
false,
|
|
93
|
+
),
|
|
94
|
+
flow_create: tool(
|
|
95
|
+
'Create an empty Flow. If this deployment uses OOMOL Connector, choose a teamId from connector_teams. Keep the key and arguments stable when retrying.',
|
|
96
|
+
z.strictObject({
|
|
97
|
+
name: id.refine((name) => name == name.trim() && resourceNameIssue(name) == null, 'Flow name is invalid.'),
|
|
98
|
+
idempotencyKey: mutationKey,
|
|
99
|
+
teamId: id.optional(),
|
|
100
|
+
}),
|
|
101
|
+
false,
|
|
102
|
+
),
|
|
103
|
+
flow_edit: tool(
|
|
104
|
+
'Atomically edit root nodes, input sources, execution edges and text. Use $alias for preceding creations. Preserve baseRevision and requestId when retrying. A saved draft may have validation diagnostics.',
|
|
105
|
+
authoringRequestSchema.extend({ flowId: flow }),
|
|
106
|
+
false,
|
|
107
|
+
),
|
|
108
|
+
flow_check: tool(
|
|
109
|
+
'Validate a fixed Revision and return diagnostics. Use the revision returned by flow_read or flow_edit as revisionId.',
|
|
110
|
+
z.strictObject({ flowId: flow, revisionId: id }),
|
|
111
|
+
true,
|
|
112
|
+
),
|
|
113
|
+
flow_publish: tool(
|
|
114
|
+
'Publish a fixed Revision. Supply the observed Live publication ID, or null for the first publication, and a stable idempotencyKey. Returns an operation; poll flow_publish_status for success or failure.',
|
|
115
|
+
z.strictObject({ flowId: flow, revisionId: id, expectedLivePublicationId: id.nullable(), idempotencyKey: mutationKey }),
|
|
116
|
+
false,
|
|
117
|
+
),
|
|
118
|
+
flow_publish_status: tool(
|
|
119
|
+
'Read a publish operation. Only succeeded confirms publication; failed includes the failure details. Keep polling while pending.',
|
|
120
|
+
z.strictObject({ flowId: flow, operationId: id }),
|
|
121
|
+
true,
|
|
122
|
+
),
|
|
123
|
+
flow_set_enabled: tool(
|
|
124
|
+
'Enable or disable a published Flow. Supply expectedPublicationId from flow_list; a changed Live publication is a conflict. This does not cancel accepted Runs; use run_cancel for those.',
|
|
125
|
+
z.strictObject({ flowId: flow, expectedPublicationId: id, enabled: z.boolean() }),
|
|
126
|
+
false,
|
|
127
|
+
),
|
|
128
|
+
flow_run: tool(
|
|
129
|
+
'Start a Run and return its runId. For draft supply flowId and revisionId; for live supply publicationId and select a Start node. Only draft Runs may simulate other Triggers. Always fix the source, Trigger and idempotencyKey. Start parameters come from the Start node’s flow_read config.inputDefs and are supplied in trigger.outputs; required parameters must be present, omitted nullable parameters become null, and unknown parameters are rejected. Poll run_get for completion.',
|
|
130
|
+
z
|
|
131
|
+
.discriminatedUnion('source', [
|
|
132
|
+
z.strictObject({
|
|
133
|
+
source: z.literal('draft'),
|
|
134
|
+
flowId: flow,
|
|
135
|
+
revisionId: id,
|
|
136
|
+
trigger: z.strictObject({ nodeId: id, outputs: z.record(z.string(), json) }),
|
|
137
|
+
inputs: z.record(z.string(), z.record(z.string(), json)).default({}),
|
|
138
|
+
idempotencyKey: mutationKey,
|
|
139
|
+
}),
|
|
140
|
+
z.strictObject({
|
|
141
|
+
source: z.literal('live'),
|
|
142
|
+
publicationId: id,
|
|
143
|
+
trigger: z.strictObject({ nodeId: id, outputs: z.record(z.string(), json) }),
|
|
144
|
+
inputs: z.record(z.string(), z.record(z.string(), json)).default({}),
|
|
145
|
+
idempotencyKey: mutationKey,
|
|
146
|
+
}),
|
|
147
|
+
])
|
|
148
|
+
.meta({ type: 'object' }),
|
|
149
|
+
false,
|
|
150
|
+
),
|
|
151
|
+
run_list: tool(
|
|
152
|
+
'List Runs for a Flow, optionally filtered by status. Continue with nextCursor; cursors are compatible with the Control API.',
|
|
153
|
+
z.strictObject({ flowId: flow, pendingWait: z.boolean().optional(), status: z.enum(runStatuses).optional(), cursor: id.optional(), limit: pageLimit }),
|
|
154
|
+
true,
|
|
155
|
+
),
|
|
156
|
+
run_get: tool(
|
|
157
|
+
'Read Run status, including waiting details and allowed actions. Completed, failed, canceled and indeterminate are terminal.',
|
|
158
|
+
z.strictObject({ runId: run }),
|
|
159
|
+
true,
|
|
160
|
+
),
|
|
161
|
+
run_events: tool(
|
|
162
|
+
'Read one page of Run events. Continue with nextAfter. Events can expire independently of the terminal result.',
|
|
163
|
+
z.strictObject({ runId: run, after: z.int().min(0).default(0), limit: pageLimit }),
|
|
164
|
+
true,
|
|
165
|
+
),
|
|
166
|
+
run_result: tool(
|
|
167
|
+
'Read the terminal result. A queued, running or waiting Run returns run.not-terminal; inspect run_get first.',
|
|
168
|
+
z.strictObject({ runId: run }),
|
|
169
|
+
true,
|
|
170
|
+
),
|
|
171
|
+
run_results: tool(
|
|
172
|
+
'List stored Agent tool result metadata for a Run, including Connector and code results. Continue with nextAfter as after. Use run_result_read to inspect content.',
|
|
173
|
+
z.strictObject({ runId: run, after: id.optional() }),
|
|
174
|
+
true,
|
|
175
|
+
),
|
|
176
|
+
run_result_read: tool(
|
|
177
|
+
'Read a bounded page of a stored Agent tool result. Select a JSON Pointer with pointer and continue with nextOffset as offset at the same pointer. A complete value contains all data at that pointer.',
|
|
178
|
+
resultQuerySchema.extend({ runId: run, resultId: id }),
|
|
179
|
+
true,
|
|
180
|
+
),
|
|
181
|
+
run_resolve_wait: tool(
|
|
182
|
+
'Explicitly resolve the waitId observed in run_get with one of its allowed actions. Optional comment records the reason (up to 2,000 Unicode code points). The first decision and comment win; inspect resolutionAccepted and action. Resume the same Run and poll run_get; do not create a replacement Run.',
|
|
183
|
+
z.strictObject({ runId: run, waitId: id, action: z.enum(['approve', 'continue', 'reject']), comment: waitCommentSchema }),
|
|
184
|
+
false,
|
|
185
|
+
),
|
|
186
|
+
run_cancel: tool(
|
|
187
|
+
'Explicitly cancel a Run. Inspect cancelAccepted and the authoritative status; completion can win the race.',
|
|
188
|
+
z.strictObject({ runId: run }),
|
|
189
|
+
false,
|
|
190
|
+
),
|
|
191
|
+
flow_code_connections: tool(
|
|
192
|
+
'Read the connections shared by all Code nodes in this Flow. With publicationId, read the fixed published snapshot; it cannot be edited.',
|
|
193
|
+
z.strictObject({ flowId: flow, publicationId: id.optional() }),
|
|
194
|
+
true,
|
|
195
|
+
),
|
|
196
|
+
flow_connection_candidates: tool(
|
|
197
|
+
'List connections and permissions available to select. Node selections do not add shared Code usage.',
|
|
198
|
+
z.strictObject({ flowId: flow, providerIds: z.array(id).min(1) }),
|
|
199
|
+
true,
|
|
200
|
+
),
|
|
201
|
+
flow_code_connection_set: tool(
|
|
202
|
+
'Add or remove shared Code usage in the Draft only. Does not change node selections or published versions. Use the observed accessRevision.',
|
|
203
|
+
z.strictObject({ flowId: flow, providerId: id, accessBindingId: id, selected: z.boolean(), expectedAccessRevision: z.int().nonnegative() }),
|
|
204
|
+
false,
|
|
205
|
+
),
|
|
206
|
+
flow_connection_usage_remove: tool(
|
|
207
|
+
'Remove a connection from all node selections and shared Code usage in this Draft atomically. Keeps the account, published versions and accepted Runs. Use the observed revisionId and accessRevision.',
|
|
208
|
+
z.strictObject({ flowId: flow, connectionId: id, expectedRevisionId: id, expectedAccessRevision: z.int().nonnegative(), idempotencyKey: mutationKey }),
|
|
209
|
+
false,
|
|
210
|
+
),
|
|
211
|
+
connector_teams: tool('List OOMOL Teams available for Flow creation. An unconfigured or custom Connector may not provide Teams.', z.strictObject({}), true),
|
|
212
|
+
connector_providers: tool('List Connector providers available in the Flow scope.', z.strictObject({ flowId: flow.optional() }), true),
|
|
213
|
+
connector_search: tool(
|
|
214
|
+
'Search Connector action summaries in the Flow scope. Read complete schemas with connector_get after selecting an action.',
|
|
215
|
+
z.strictObject({ query: z.string().trim().min(1).max(256), flowId: flow.optional() }),
|
|
216
|
+
true,
|
|
217
|
+
),
|
|
218
|
+
connector_get: tool(
|
|
219
|
+
'Read a Connector action, input/output definitions and connection requirements before authoring a node.',
|
|
220
|
+
z.strictObject({ actionId: id, flowId: flow.optional() }),
|
|
221
|
+
true,
|
|
222
|
+
),
|
|
223
|
+
connector_connections: tool(
|
|
224
|
+
'List Connector connections in the Flow scope. Use active connection IDs in bindings.',
|
|
225
|
+
z.strictObject({ serviceId: id.max(256), flowId: flow.optional() }),
|
|
226
|
+
true,
|
|
227
|
+
),
|
|
228
|
+
event_source_list: tool(
|
|
229
|
+
'List shared event sources for Feishu, Slack Bot, Intercom, Sentry, Zoom, Fireflies, and Jira, including Team, Connection, event types, enabled and verification status. Inspect consumers for Flows actually using each source.',
|
|
230
|
+
z.strictObject({}),
|
|
231
|
+
true,
|
|
232
|
+
),
|
|
233
|
+
trigger_search: tool(
|
|
234
|
+
'Search provider Trigger summaries; omit query to list all. Flow trigger instances are in flow_read. Start, Webhook and Cron are built-in node kinds described by flow_schema.',
|
|
235
|
+
z.strictObject({ query: z.string().trim().min(1).max(256).optional() }),
|
|
236
|
+
true,
|
|
237
|
+
),
|
|
238
|
+
trigger_get: tool('Read a provider Trigger definition before creating its node.', z.strictObject({ key: id }), true),
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { mcpConformanceCases } from './mcpConformance.ts'
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { ControlApiConformanceCase, ControlApiConformanceHarness } from './conformance.ts'
|
|
2
|
+
|
|
3
|
+
import { dequal } from 'dequal/lite'
|
|
4
|
+
import { ControlClient } from './api.ts'
|
|
5
|
+
import { mcpProtocolVersion, mcpTools } from './mcp.ts'
|
|
6
|
+
|
|
7
|
+
function assert(value: unknown, message: string): asserts value {
|
|
8
|
+
if (!value) throw new Error(message)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function rpc(harness: ControlApiConformanceHarness, method: string, params?: Record<string, unknown>) {
|
|
12
|
+
const response = await harness.request(
|
|
13
|
+
new Request(new URL('/v1/mcp', harness.origin), {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: {
|
|
16
|
+
'content-type': 'application/json',
|
|
17
|
+
'accept': 'application/json, text/event-stream',
|
|
18
|
+
'mcp-protocol-version': mcpProtocolVersion,
|
|
19
|
+
'mcp-method': method,
|
|
20
|
+
...(method == 'tools/call' ? { 'mcp-name': String(params?.name) } : {}),
|
|
21
|
+
},
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
jsonrpc: '2.0',
|
|
24
|
+
id: crypto.randomUUID(),
|
|
25
|
+
method,
|
|
26
|
+
params: { ...params, _meta: { 'io.modelcontextprotocol/protocolVersion': mcpProtocolVersion, 'io.modelcontextprotocol/clientCapabilities': {} } },
|
|
27
|
+
}),
|
|
28
|
+
}),
|
|
29
|
+
)
|
|
30
|
+
assert(response.ok, `MCP ${method} returned HTTP ${response.status}: ${response.ok ? '' : await response.text()}.`)
|
|
31
|
+
assert(response.headers.get('mcp-session-id') == null, 'MCP must not create a session.')
|
|
32
|
+
const source = await response.text()
|
|
33
|
+
const body = response.headers.get('content-type')?.includes('text/event-stream')
|
|
34
|
+
? JSON.parse(
|
|
35
|
+
source
|
|
36
|
+
.split('\n')
|
|
37
|
+
.findLast((line) => line.startsWith('data:'))
|
|
38
|
+
?.slice(5) ?? 'null',
|
|
39
|
+
)
|
|
40
|
+
: JSON.parse(source)
|
|
41
|
+
assert(body != null && body.error == null, `MCP ${method} failed: ${JSON.stringify(body)}.`)
|
|
42
|
+
return body.result as Record<string, unknown>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const mcpConformanceCases: readonly ControlApiConformanceCase[] = [
|
|
46
|
+
{
|
|
47
|
+
name: 'shares workflow import and export with REST and preserves idempotency',
|
|
48
|
+
async verify(harness) {
|
|
49
|
+
const client = new ControlClient((url, init) => harness.request(new Request(new URL(url, harness.origin), init)))
|
|
50
|
+
const original = await client.createFlow('Template source')
|
|
51
|
+
const exported = await rpc(harness, 'tools/call', { name: 'flow_export', arguments: { flowId: original.flowId } })
|
|
52
|
+
assert(exported.isError != true, 'MCP export failed.')
|
|
53
|
+
assert(dequal(exported.structuredContent, await client.exportFlow(original.flowId)), 'MCP and REST exports differ.')
|
|
54
|
+
const args = { file: exported.structuredContent, name: 'Imported template', idempotencyKey: crypto.randomUUID() }
|
|
55
|
+
const imported = await rpc(harness, 'tools/call', { name: 'flow_import', arguments: args })
|
|
56
|
+
assert(imported.isError != true, 'MCP import failed.')
|
|
57
|
+
const flow = imported.structuredContent as { flowId: string; draftRevisionId: string }
|
|
58
|
+
assert(flow.flowId != original.flowId && flow.draftRevisionId != original.draftRevisionId, 'Import reused resource identities.')
|
|
59
|
+
assert((await client.getEditor(flow.flowId)).live.publication == null, 'Import published the Flow.')
|
|
60
|
+
const retry = await rpc(harness, 'tools/call', { name: 'flow_import', arguments: args })
|
|
61
|
+
assert(dequal(retry.structuredContent, imported.structuredContent), 'Import retry created another Flow.')
|
|
62
|
+
const conflict = await rpc(harness, 'tools/call', { name: 'flow_import', arguments: { ...args, name: 'Changed import' } })
|
|
63
|
+
assert(conflict.isError == true, 'Changed import must conflict.')
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'publishes the public MCP catalog, input schemas and annotations',
|
|
68
|
+
async verify(harness) {
|
|
69
|
+
const result = await rpc(harness, 'tools/list')
|
|
70
|
+
assert(Array.isArray(result.tools), 'MCP tools must be an array.')
|
|
71
|
+
assert(result.tools.length == Object.keys(mcpTools).length, 'MCP tool count differs from the public contract.')
|
|
72
|
+
for (const [name, definition] of Object.entries(mcpTools)) {
|
|
73
|
+
const actual = result.tools.find((candidate) => candidate.name == name)
|
|
74
|
+
assert(actual != null, `Missing MCP tool ${name}.`)
|
|
75
|
+
assert(actual.description == definition.description, `${name} description differs.`)
|
|
76
|
+
assert(dequal(actual.annotations, definition.annotations), `${name} annotations differ.`)
|
|
77
|
+
const expected = definition.inputSchema['~standard'].jsonSchema.input()
|
|
78
|
+
const { $schema: _, ...schema } = expected
|
|
79
|
+
const { $schema: __, ...received } = actual.inputSchema
|
|
80
|
+
assert(dequal(received, schema), `${name} input schema differs.`)
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'shares Run pagination, status filters and cancellation between MCP and REST',
|
|
86
|
+
async verify(harness) {
|
|
87
|
+
const client = new ControlClient((url, init) => harness.request(new Request(new URL(url, harness.origin), init)))
|
|
88
|
+
const flow = await client.createFlow('MCP Run listing')
|
|
89
|
+
const changed = await client.changeDraft(flow.flowId, flow.draftRevisionId, [
|
|
90
|
+
{ kind: 'graph.node.create', nodeId: 'start', node: { inputDefs: [], kind: 'start', name: 'Start' } },
|
|
91
|
+
])
|
|
92
|
+
const args = {
|
|
93
|
+
source: 'draft',
|
|
94
|
+
flowId: flow.flowId,
|
|
95
|
+
revisionId: changed.revision.revisionId,
|
|
96
|
+
trigger: { nodeId: 'start', outputs: {} },
|
|
97
|
+
}
|
|
98
|
+
const runs: string[] = []
|
|
99
|
+
for (let index = 0; index < 2; index++) {
|
|
100
|
+
const accepted = await rpc(harness, 'tools/call', { name: 'flow_run', arguments: { ...args, idempotencyKey: crypto.randomUUID() } })
|
|
101
|
+
const run = accepted.structuredContent as Record<string, unknown>
|
|
102
|
+
assert(typeof run.runId == 'string', 'MCP must return an accepted Run ID.')
|
|
103
|
+
runs.push(run.runId)
|
|
104
|
+
}
|
|
105
|
+
const first = await rpc(harness, 'tools/call', { name: 'run_list', arguments: { flowId: flow.flowId, limit: 1 } })
|
|
106
|
+
const page = first.structuredContent as { runs: { runId: string }[]; nextCursor: string }
|
|
107
|
+
assert(page.runs.length == 1 && typeof page.nextCursor == 'string', 'Run listing must paginate.')
|
|
108
|
+
const rest = await client.listRuns(flow.flowId, { cursor: page.nextCursor, limit: 1 })
|
|
109
|
+
assert(rest.runs.length == 1 && rest.runs[0]?.runId != page.runs[0]?.runId, 'REST must continue the MCP Run cursor without duplicates.')
|
|
110
|
+
const restFirst = await client.listRuns(flow.flowId, { limit: 1 })
|
|
111
|
+
const next = await rpc(harness, 'tools/call', { name: 'run_list', arguments: { flowId: flow.flowId, cursor: restFirst.nextCursor, limit: 1 } })
|
|
112
|
+
assert(dequal(next.structuredContent, rest), 'MCP must continue the REST Run cursor.')
|
|
113
|
+
await rpc(harness, 'tools/call', { name: 'run_cancel', arguments: { runId: runs[0] } })
|
|
114
|
+
const filtered = await rpc(harness, 'tools/call', { name: 'run_list', arguments: { flowId: flow.flowId, status: 'canceled' } })
|
|
115
|
+
assert(dequal(filtered.structuredContent, await client.listRuns(flow.flowId, { status: 'canceled' })), 'MCP and REST status filters differ.')
|
|
116
|
+
const other = await client.createFlow('Other Run scope')
|
|
117
|
+
const invalid = await rpc(harness, 'tools/call', { name: 'run_list', arguments: { flowId: other.flowId, cursor: page.nextCursor } })
|
|
118
|
+
assert(invalid.isError == true, 'Run cursors must reject a different Flow scope.')
|
|
119
|
+
assert((invalid.structuredContent as { error: { code: string } }).error.code == 'page.invalid-cursor', 'Run cursor error must match REST.')
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'shares mutation receipts and authoritative Drafts between MCP and REST',
|
|
124
|
+
async verify(harness) {
|
|
125
|
+
const args = { name: 'MCP conformance', idempotencyKey: crypto.randomUUID() }
|
|
126
|
+
const call = () => rpc(harness, 'tools/call', { name: 'flow_create', arguments: args })
|
|
127
|
+
const created = await call()
|
|
128
|
+
assert(created.isError != true, 'MCP Flow creation failed.')
|
|
129
|
+
const flow = created.structuredContent as Record<string, unknown>
|
|
130
|
+
assert(typeof flow.flowId == 'string', 'MCP did not return a Flow ID.')
|
|
131
|
+
const retry = await call()
|
|
132
|
+
assert(dequal(retry.structuredContent, flow), 'MCP mutation replay changed identity.')
|
|
133
|
+
const response = await harness.request(new Request(new URL(`/v1/flows/${encodeURIComponent(flow.flowId)}/draft`, harness.origin)))
|
|
134
|
+
assert(response.ok, 'REST cannot read the Flow created through MCP.')
|
|
135
|
+
const draft = (await response.json()) as Record<string, unknown>
|
|
136
|
+
assert(draft.revisionId == flow.draftRevisionId, 'MCP and REST disagree on the Draft head.')
|
|
137
|
+
const conflict = await rpc(harness, 'tools/call', { name: 'flow_create', arguments: { ...args, name: 'Changed retry' } })
|
|
138
|
+
assert(conflict.isError == true, 'MCP must reject a changed idempotent replay.')
|
|
139
|
+
assert((conflict.structuredContent as { error: { code: string } }).error.code == 'flow.conflict', 'MCP must preserve the Control error code.')
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { canonicalJsonBytes, digestBytes } from '../../flow/common/encoding.ts'
|
|
3
|
+
|
|
4
|
+
const sourceSchema = z.discriminatedUnion('kind', [
|
|
5
|
+
z.strictObject({ kind: z.literal('admin-delegation') }),
|
|
6
|
+
z.strictObject({ kind: z.literal('policy'), ruleId: z.string().min(1).nullable() }),
|
|
7
|
+
])
|
|
8
|
+
|
|
9
|
+
export type ProviderAccessSource = { readonly kind: 'admin-delegation' } | { readonly kind: 'policy'; readonly ruleId: string | null }
|
|
10
|
+
|
|
11
|
+
export interface ProviderAccessIdentity {
|
|
12
|
+
readonly accessBindingId: string
|
|
13
|
+
readonly connectionId: string
|
|
14
|
+
readonly providerId: string
|
|
15
|
+
readonly source: ProviderAccessSource
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type ProviderAccessReference =
|
|
19
|
+
| ProviderAccessIdentity
|
|
20
|
+
| { readonly accessBindingId: string; readonly providerId: string; readonly connectionId: null; readonly source: null }
|
|
21
|
+
|
|
22
|
+
export function parseProviderAccessSource(value: unknown): ProviderAccessSource {
|
|
23
|
+
return sourceSchema.parse(value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function providerAccessBindingId(teamId: string, identity: Omit<ProviderAccessIdentity, 'accessBindingId'>): Promise<string> {
|
|
27
|
+
return await digestBytes(canonicalJsonBytes(['provider-access', 2, teamId, identity.connectionId, identity.providerId, identity.source]))
|
|
28
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Coordinates and dimensions are physical pixels in the sprite image. */
|
|
2
|
+
export interface ProviderIconSprite {
|
|
3
|
+
readonly version: string
|
|
4
|
+
readonly pixelRatio: number
|
|
5
|
+
readonly iconSize: number
|
|
6
|
+
readonly bleed: number
|
|
7
|
+
readonly width: number
|
|
8
|
+
readonly height: number
|
|
9
|
+
readonly lightUrl: string
|
|
10
|
+
readonly darkUrl: string
|
|
11
|
+
}
|
|
12
|
+
export interface ProviderIconAppearance {
|
|
13
|
+
readonly iconSprite?: ProviderIconSprite
|
|
14
|
+
readonly iconSpritePosition?: { readonly x: number; readonly y: number }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** One sprite sheet serves all provider positions in a catalog response. */
|
|
18
|
+
export interface ProviderIconSpriteCatalog {
|
|
19
|
+
readonly iconSprite: ProviderIconSprite
|
|
20
|
+
readonly positions: Readonly<Record<string, NonNullable<ProviderIconAppearance['iconSpritePosition']>>>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Optional artwork must never make an otherwise usable catalog unavailable. */
|
|
24
|
+
export function providerIconAppearance(metadata: unknown, position: unknown): ProviderIconAppearance {
|
|
25
|
+
if (!metadata || typeof metadata != 'object' || !position || typeof position != 'object') return {}
|
|
26
|
+
const m = metadata as ProviderIconSprite
|
|
27
|
+
const p = position as { x: number; y: number }
|
|
28
|
+
if (
|
|
29
|
+
typeof m.version != 'string' ||
|
|
30
|
+
![m.pixelRatio, m.iconSize, m.width, m.height].every((v) => Number.isInteger(v) && v > 0) ||
|
|
31
|
+
![m.bleed, p.x, p.y].every((v) => Number.isInteger(v) && v >= 0) ||
|
|
32
|
+
p.x + m.iconSize > m.width ||
|
|
33
|
+
p.y + m.iconSize > m.height
|
|
34
|
+
)
|
|
35
|
+
return {}
|
|
36
|
+
try {
|
|
37
|
+
for (const url of [m.lightUrl, m.darkUrl]) if (typeof url != 'string' || !['https:', 'http:', 'data:'].includes(new URL(url).protocol)) return {}
|
|
38
|
+
} catch {
|
|
39
|
+
return {}
|
|
40
|
+
}
|
|
41
|
+
return { iconSprite: m, iconSpritePosition: { x: p.x, y: p.y } }
|
|
42
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Live, Publication, PublicationPage, PublishOperation } from './api.ts'
|
|
2
|
+
|
|
3
|
+
import { requiredFields, integer, invalidResponse, record, string } from './decoding.ts'
|
|
4
|
+
|
|
5
|
+
export function publication(value: unknown): Publication {
|
|
6
|
+
const source = record(value)
|
|
7
|
+
const liveEnd = source.liveEnd == null ? undefined : record(source.liveEnd)
|
|
8
|
+
if (liveEnd != null && typeof liveEnd.enabled != 'boolean') return invalidResponse()
|
|
9
|
+
const operation = source.operation
|
|
10
|
+
const sourcePublicationId = source.sourcePublicationId
|
|
11
|
+
if (source.version != 1) return invalidResponse()
|
|
12
|
+
if (operation != 'publish' && operation != 'rollback') return invalidResponse()
|
|
13
|
+
if (sourcePublicationId != null && typeof sourcePublicationId != 'string') return invalidResponse()
|
|
14
|
+
return {
|
|
15
|
+
...(liveEnd == null ? {} : { liveEnd: { enabled: liveEnd.enabled as boolean, endedAt: string(liveEnd.endedAt) } }),
|
|
16
|
+
actorId: string(source.actorId),
|
|
17
|
+
closureDigest: string(source.closureDigest),
|
|
18
|
+
createdAt: string(source.createdAt),
|
|
19
|
+
engineContract: string(source.engineContract),
|
|
20
|
+
flowId: string(source.flowId),
|
|
21
|
+
modelVersion: integer(source.modelVersion),
|
|
22
|
+
operation: operation as Publication['operation'],
|
|
23
|
+
publicationId: string(source.publicationId),
|
|
24
|
+
sharedAccessDigest: string(source.sharedAccessDigest),
|
|
25
|
+
revisionDigest: string(source.revisionDigest),
|
|
26
|
+
revisionId: string(source.revisionId),
|
|
27
|
+
...(sourcePublicationId == null ? {} : { sourcePublicationId }),
|
|
28
|
+
version: 1,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function publishOperation(value: unknown): PublishOperation {
|
|
33
|
+
const source = record(value)
|
|
34
|
+
const status = source.status
|
|
35
|
+
const common = {
|
|
36
|
+
createdAt: string(source.createdAt),
|
|
37
|
+
flowId: string(source.flowId),
|
|
38
|
+
operationId: string(source.operationId),
|
|
39
|
+
revisionId: string(source.revisionId),
|
|
40
|
+
updatedAt: string(source.updatedAt),
|
|
41
|
+
version: 1 as const,
|
|
42
|
+
}
|
|
43
|
+
if (source.version != 1) return invalidResponse()
|
|
44
|
+
switch (status) {
|
|
45
|
+
case 'pending':
|
|
46
|
+
requiredFields(source, ['createdAt', 'flowId', 'operationId', 'revisionId', 'status', 'updatedAt', 'version'])
|
|
47
|
+
return { ...common, status }
|
|
48
|
+
case 'succeeded':
|
|
49
|
+
requiredFields(source, ['createdAt', 'flowId', 'operationId', 'publicationId', 'revisionId', 'status', 'updatedAt', 'version'])
|
|
50
|
+
return { ...common, publicationId: string(source.publicationId), status }
|
|
51
|
+
case 'failed': {
|
|
52
|
+
requiredFields(source, ['createdAt', 'flowId', 'issue', 'operationId', 'revisionId', 'status', 'updatedAt', 'version'])
|
|
53
|
+
const issue = record(source.issue)
|
|
54
|
+
const nodeId = issue.nodeId
|
|
55
|
+
requiredFields(issue, ['code', 'message', ...(nodeId == null ? [] : ['nodeId'])])
|
|
56
|
+
return {
|
|
57
|
+
...common,
|
|
58
|
+
issue: { code: string(issue.code), message: string(issue.message), ...(nodeId == null ? {} : { nodeId: string(nodeId) }) },
|
|
59
|
+
status,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
default:
|
|
63
|
+
return invalidResponse()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function live(value: unknown): Live {
|
|
68
|
+
const source = record(value)
|
|
69
|
+
const status = source.status
|
|
70
|
+
const candidate = source.publication
|
|
71
|
+
if (source.version != 1 || typeof source.hasUnpublishedChanges != 'boolean') return invalidResponse()
|
|
72
|
+
if (status != 'not-published' && status != 'runnable' && status != 'suspended') return invalidResponse()
|
|
73
|
+
if (status == 'not-published' && candidate !== null) return invalidResponse()
|
|
74
|
+
if (status != 'not-published' && candidate === null) return invalidResponse()
|
|
75
|
+
return {
|
|
76
|
+
flowId: string(source.flowId),
|
|
77
|
+
hasUnpublishedChanges: source.hasUnpublishedChanges,
|
|
78
|
+
publication: candidate === null ? null : publication(candidate),
|
|
79
|
+
revision: integer(source.revision),
|
|
80
|
+
status: status as Live['status'],
|
|
81
|
+
version: 1,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function publicationPage(value: unknown): PublicationPage {
|
|
86
|
+
const source = record(value)
|
|
87
|
+
const nextCursor = source.nextCursor
|
|
88
|
+
const total = source.total
|
|
89
|
+
if (source.version != 1 || !Array.isArray(source.publications)) return invalidResponse()
|
|
90
|
+
if (nextCursor != null && typeof nextCursor != 'string') return invalidResponse()
|
|
91
|
+
if (total != null && !Number.isSafeInteger(total)) return invalidResponse()
|
|
92
|
+
return {
|
|
93
|
+
...(nextCursor == null ? {} : { nextCursor }),
|
|
94
|
+
publications: source.publications.map(publication),
|
|
95
|
+
...(total == null ? {} : { total: total as number }),
|
|
96
|
+
version: 1,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function randomId(): string {
|
|
2
|
+
const crypto = globalThis.crypto
|
|
3
|
+
if (crypto != null) {
|
|
4
|
+
try {
|
|
5
|
+
if (typeof crypto.randomUUID == 'function') return crypto.randomUUID()
|
|
6
|
+
} catch {}
|
|
7
|
+
if (typeof crypto.getRandomValues == 'function') {
|
|
8
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16))
|
|
9
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40
|
|
10
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80
|
|
11
|
+
const value = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')
|
|
12
|
+
return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
|
|
16
|
+
}
|