@oomol-lab/open-flow 0.1.0-beta.72 → 0.1.0-beta.74
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 +40 -0
- package/dist/browser/{createResourceDialog-BWJkjiOO.js → createResourceDialog-DI9pL-Yr.js} +2 -2
- package/dist/browser/{dialog-CUy_fwx9.js → dialog-BYJGvItc.js} +54 -19
- package/dist/browser/{flowChanges-CgSnSqqP.js → flowChanges-RL-H4CjD.js} +306 -48
- package/dist/browser/{flowWorkspace-Bf7H-r0t.js → flowWorkspace-BkDsETj0.js} +20745 -20186
- package/dist/browser/preview.css +73 -3
- package/dist/browser/preview.js +21410 -20607
- package/dist/browser/{typeScriptSession-B0hC-C8y.js → typeScriptSession-CSRwWmZm.js} +1 -1
- package/dist/browser/ui.css +72 -2
- package/dist/browser/workbench.css +73 -3
- package/dist/browser/workbench.js +14 -76
- package/dist/browser/{workbenchSelect-C3e4J93X.js → workbenchSelect-BcWPWGzw.js} +1 -1
- package/dist/command/index.js +34 -34
- package/dist/common/authoring.d.ts +1 -1
- package/dist/common/authoringFields.d.ts +1 -1
- package/dist/common/authoringSchema.d.ts +28 -4
- 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 +61 -24
- package/dist/common/control-api.d.ts +4 -3
- package/dist/common/control-api.js +90 -42
- package/dist/common/control-requests.d.ts +2 -1
- package/dist/common/control-requests.js +204 -42
- 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 +3 -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 +75 -23
- 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 +77 -25
- package/dist/common/flow-connectionUsage.d.ts +1 -1
- package/dist/common/flow-encoding.d.ts +1 -1
- package/dist/common/flow-encoding.js +34 -16
- package/dist/common/flow-errorHandling.d.ts +1 -1
- package/dist/common/flow-graph.d.ts +1 -1
- 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 +1 -1
- package/dist/common/flow-semantics.d.ts +1 -1
- package/dist/common/flow-semantics.js +151 -32
- package/dist/common/flow-workflowFile.d.ts +1 -1
- package/dist/common/flowInspection.d.ts +3 -3
- 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 +2 -1
- package/dist/common/mcp.js +59 -22
- package/dist/common/openapi.d.ts +1 -1
- package/dist/common/poll-trigger.d.ts +1 -1
- package/dist/common/run-results.d.ts +8 -1
- package/dist/common/runtime-contract.d.ts +8 -2
- package/dist/common/runtime-contract.js +22 -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 +2 -2
- package/dist/common/scheduler.js +31 -12
- 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 -1
- package/dist/common/webhook-trigger.d.ts +1 -1
- 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 +7 -6
- 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 +1505 -0
- package/src/control/common/authoring.ts +912 -0
- package/src/control/common/authoringCondition.ts +110 -0
- package/src/control/common/authoringDiagnosticFields.ts +136 -0
- package/src/control/common/authoringExamples.ts +192 -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 +404 -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 +80 -0
- package/src/execution/common/events.ts +328 -0
- package/src/execution/common/node-execution.ts +467 -0
- package/src/execution/common/runLifecycle.ts +250 -0
- package/src/execution/common/runtime.ts +563 -0
- package/src/execution/common/scheduler.ts +696 -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 +1036 -0
- package/src/flow/common/changeSchema.ts +783 -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 +892 -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 +474 -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 +75 -0
- package/src/flow/common/workflowFile.ts +97 -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/authBindings.ts +12 -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 +412 -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,912 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChangeOperation,
|
|
3
|
+
GraphNode,
|
|
4
|
+
InputMapping,
|
|
5
|
+
InputPort,
|
|
6
|
+
JsonValue,
|
|
7
|
+
ManagedTaskDefinition,
|
|
8
|
+
RevisionContent,
|
|
9
|
+
TriggerKeySnapshot,
|
|
10
|
+
} from '../../flow/common/change.ts'
|
|
11
|
+
import type { WorkflowContract, WorkflowCandidate } from '../../workflow/common/workflow.ts'
|
|
12
|
+
import type { ConnectorAction, Diagnostic } from './api.ts'
|
|
13
|
+
import type { AuthoringConditionConfiguration, AuthoringConditionSource } from './authoringCondition.ts'
|
|
14
|
+
import type { AuthoringOpenApiConfig } from './authoringOpenApi.ts'
|
|
15
|
+
import type { AuthoringRead, AuthoringRequest, AuthoringSearch, AuthoringSource, AuthoringType } from './authoringSchema.ts'
|
|
16
|
+
import type { AuthoringAgentTool, AuthoringCapability } from './authoringTools.ts'
|
|
17
|
+
|
|
18
|
+
import { dequal } from 'dequal/lite'
|
|
19
|
+
import { connectorOutputMode } from '../../connector/common/actionSchema.ts'
|
|
20
|
+
import { decisionTask } from '../../decision/common/decision.ts'
|
|
21
|
+
import { supportsNodeRetry, isTriggerNode, applyFlowChanges, decodeChangeOperations, upgradeRevisionContent } from '../../flow/common/change.ts'
|
|
22
|
+
import { nodeInputMappings } from '../../flow/common/condition.ts'
|
|
23
|
+
import { connect, disconnect } from '../../flow/common/edgeChanges.ts'
|
|
24
|
+
import { nodeSystemOutputPorts, supportsNodeErrorHandling } from '../../flow/common/errorHandling.ts'
|
|
25
|
+
import { nodeInputPorts, nodeOutputPorts } from '../../flow/common/graph.ts'
|
|
26
|
+
import { imports as moduleImports } from '../../flow/common/moduleChanges.ts'
|
|
27
|
+
import {
|
|
28
|
+
createAgentTask,
|
|
29
|
+
createCodeTask,
|
|
30
|
+
defaultCodeTaskPorts,
|
|
31
|
+
createCondition,
|
|
32
|
+
createDecisionTask,
|
|
33
|
+
createLlmTask,
|
|
34
|
+
createManagedTask,
|
|
35
|
+
createProviderTrigger,
|
|
36
|
+
createValue,
|
|
37
|
+
createWait,
|
|
38
|
+
createApproval,
|
|
39
|
+
cleanVariableBindings,
|
|
40
|
+
updateStartInputDefs,
|
|
41
|
+
updateOutputsDefs,
|
|
42
|
+
renameFlowOutputSources,
|
|
43
|
+
} from '../../flow/common/nodeChanges.ts'
|
|
44
|
+
import { retryConfigurationSchema } from '../../flow/common/retry.ts'
|
|
45
|
+
import { withWorkflowTask, workflowTask, workflowSelectionTask, workflowCapability, workflowAgentTool } from '../../workflow/common/workflow.ts'
|
|
46
|
+
import { conditionConfigurationView, prepareConditionConfiguration } from './authoringCondition.ts'
|
|
47
|
+
import { authoringDiagnosticField } from './authoringDiagnosticFields.ts'
|
|
48
|
+
import { fieldsView, fieldsToPorts } from './authoringFields.ts'
|
|
49
|
+
import { llmConfigurationView, prepareLlmConfiguration, initialAuthoringLlmTask } from './authoringLlm.ts'
|
|
50
|
+
import { AuthoringOpenApiAuthenticationError, openApiConfigView, openApiAuthenticationChoices, prepareOpenApiTask } from './authoringOpenApi.ts'
|
|
51
|
+
import { authoringConfigurations, authoringReadSchema, authoringRequestSchema, authoringSearchSchema } from './authoringSchema.ts'
|
|
52
|
+
import { AuthoringToolInputError, outwardAgentTools, prepareAgentTools, outwardCapabilities, prepareCapabilities } from './authoringTools.ts'
|
|
53
|
+
import { triggerConfiguration } from './authoringTriggers.ts'
|
|
54
|
+
export * from './authoringSchema.ts'
|
|
55
|
+
|
|
56
|
+
const record = (value: unknown): Record<string, unknown> =>
|
|
57
|
+
value != null && typeof value == 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}
|
|
58
|
+
const json = (value: unknown): JsonValue => JSON.parse(JSON.stringify(value)) as JsonValue
|
|
59
|
+
export class AuthoringError extends Error {
|
|
60
|
+
readonly code: string
|
|
61
|
+
readonly details: Record<string, unknown>
|
|
62
|
+
constructor(code: string, message: string, details: Record<string, unknown> = {}) {
|
|
63
|
+
super(message)
|
|
64
|
+
this.code = code
|
|
65
|
+
this.details = details
|
|
66
|
+
this.name = 'AuthoringError'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function fail(code: string, message: string, details: Record<string, unknown> = {}): never {
|
|
70
|
+
throw new AuthoringError(code, message, details)
|
|
71
|
+
}
|
|
72
|
+
function nodeAt(content: RevisionContent, ref: string): GraphNode {
|
|
73
|
+
return (
|
|
74
|
+
(Object.hasOwn(content.document.graph.nodes, ref) ? content.document.graph.nodes[ref] : undefined) ??
|
|
75
|
+
fail('node.not-found', `Node ${JSON.stringify(ref)} does not exist.`, { node: ref })
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
function taskAt(node: GraphNode) {
|
|
79
|
+
return node.kind == 'task' ? node.task : undefined
|
|
80
|
+
}
|
|
81
|
+
const llmReservedInputs = ['model', 'template', 'messages']
|
|
82
|
+
function inputDefinitions(node: GraphNode) {
|
|
83
|
+
if (node.kind == 'condition') return {}
|
|
84
|
+
if (node.kind == 'poll' || node.kind == 'integration')
|
|
85
|
+
return Object.fromEntries(node.definition.configInputs.flatMap((port) => ('handle' in port ? [[port.handle, port]] : [])))
|
|
86
|
+
return nodeInputPorts(node)
|
|
87
|
+
}
|
|
88
|
+
function inputMappings(node: GraphNode) {
|
|
89
|
+
return node.kind == 'condition' ? {} : node.kind == 'poll' || node.kind == 'integration' ? node.config : nodeInputMappings(node)
|
|
90
|
+
}
|
|
91
|
+
function typeOf(node: GraphNode): AuthoringType {
|
|
92
|
+
const task = taskAt(node)
|
|
93
|
+
return node.kind == 'manual' ? 'start' : node.kind != 'task' ? node.kind : task != null && 'executor' in task ? task.executor.kind : 'code'
|
|
94
|
+
}
|
|
95
|
+
function outwardSource(content: RevisionContent, value: unknown): unknown {
|
|
96
|
+
if (Array.isArray(value)) return value.map((v) => outwardSource(content, v))
|
|
97
|
+
if (value == null || typeof value != 'object') return value
|
|
98
|
+
const v = record(value)
|
|
99
|
+
if (v.kind == 'node' && typeof v.nodeId == 'string' && typeof v.output == 'string')
|
|
100
|
+
return { kind: v.system === true ? 'system' : 'output', node: v.nodeId, port: v.output, ...(v.field == null ? {} : { field: v.field }) }
|
|
101
|
+
if (v.kind == 'binding' && typeof v.bindingId == 'string')
|
|
102
|
+
return { kind: 'variable', name: content.document.bindings[v.bindingId]?.target ?? '[missing variable]' }
|
|
103
|
+
// Literal JSON is user data, including objects that resemble references.
|
|
104
|
+
if (v.kind == 'value') return value
|
|
105
|
+
return Object.fromEntries(Object.entries(v).map(([k, item]) => [k, outwardSource(content, item)]))
|
|
106
|
+
}
|
|
107
|
+
function authoringEdges(edges: RevisionContent['document']['graph']['edges']) {
|
|
108
|
+
return edges.map((edge) =>
|
|
109
|
+
edge.sourceHandle == null ? { source: edge.source, target: edge.target } : { source: edge.source, target: edge.target, branch: edge.sourceHandle },
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
export function authoringNode(content: RevisionContent, ref: string) {
|
|
113
|
+
const node = nodeAt(content, ref),
|
|
114
|
+
type = typeOf(node),
|
|
115
|
+
task = taskAt(node)
|
|
116
|
+
let config: Record<string, unknown> = {}
|
|
117
|
+
const texts: Record<string, string> = {}
|
|
118
|
+
if (task != null && 'executor' in task) {
|
|
119
|
+
const { kind: _, ...executor } = task.executor
|
|
120
|
+
config = { ...executor }
|
|
121
|
+
if (task.executor.kind == 'agent') {
|
|
122
|
+
const { prompt, tools, notification, ...rest } = task.executor
|
|
123
|
+
const { kind: _agentKind, ...settings } = rest
|
|
124
|
+
config = {
|
|
125
|
+
...settings,
|
|
126
|
+
inputs: fieldsView([...task.inputs, ...(node.kind == 'task' ? (node.additionalInputs ?? []) : [])]),
|
|
127
|
+
resultSchema: task.outputs.find((port) => 'handle' in port)?.jsonSchema ?? { type: 'string' },
|
|
128
|
+
tools: outwardAgentTools(tools),
|
|
129
|
+
}
|
|
130
|
+
texts.prompt = prompt
|
|
131
|
+
if (notification != null) {
|
|
132
|
+
const { inputDefinitions: _definitions, ...noticeSettings } = notification
|
|
133
|
+
config.notification = noticeSettings
|
|
134
|
+
}
|
|
135
|
+
} else if (task.executor.kind == 'connector') {
|
|
136
|
+
const { kind: _kind, outputMode: _outputMode, ...settings } = task.executor
|
|
137
|
+
config = settings
|
|
138
|
+
} else if (task.executor.kind == 'llm')
|
|
139
|
+
config = {
|
|
140
|
+
...llmConfigurationView(task),
|
|
141
|
+
inputs: fieldsView(
|
|
142
|
+
[...task.inputs, ...(node.kind == 'task' ? (node.additionalInputs ?? []) : [])].filter(
|
|
143
|
+
(port) => !('handle' in port) || !llmReservedInputs.includes(port.handle),
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
}
|
|
147
|
+
else if (task.executor.kind == 'workflow') config = { flowId: task.executor.flowId }
|
|
148
|
+
else if (task.executor.kind == 'openapi') {
|
|
149
|
+
config = { ...openApiConfigView(task.executor) }
|
|
150
|
+
}
|
|
151
|
+
} else if (node.kind == 'task' && 'moduleId' in node.task) {
|
|
152
|
+
config = {
|
|
153
|
+
inputs: fieldsView([...node.task.inputs, ...(node.additionalInputs ?? [])]),
|
|
154
|
+
outputs: fieldsView(node.task.outputs),
|
|
155
|
+
...(node.task.capabilities == null ? {} : { capabilities: outwardCapabilities(node.task.capabilities) }),
|
|
156
|
+
}
|
|
157
|
+
texts.code = content.modules[node.task.moduleId]?.source ?? ''
|
|
158
|
+
} else {
|
|
159
|
+
switch (node.kind) {
|
|
160
|
+
case 'outputs':
|
|
161
|
+
config = { outputDefs: fieldsView(node.outputDefs) }
|
|
162
|
+
break
|
|
163
|
+
case 'condition':
|
|
164
|
+
config = { ...conditionConfigurationView(node, (value) => outwardSource(content, value) as AuthoringConditionSource) }
|
|
165
|
+
break
|
|
166
|
+
case 'value':
|
|
167
|
+
config = {
|
|
168
|
+
values: Object.fromEntries(node.values.filter((port) => Object.hasOwn(port, 'value')).map((port) => [port.handle, port.value])),
|
|
169
|
+
outputs: fieldsView(node.values.map(({ value: _value, ...port }) => port)),
|
|
170
|
+
}
|
|
171
|
+
break
|
|
172
|
+
case 'wait':
|
|
173
|
+
case 'approval':
|
|
174
|
+
config = { inputs: fieldsView(node.inputDefinitions), ...(node.kind == 'wait' && node.autoContinue != null ? { autoContinue: node.autoContinue } : {}) }
|
|
175
|
+
texts.prompt = node.prompt
|
|
176
|
+
break
|
|
177
|
+
case 'webhook-response':
|
|
178
|
+
case 'manual':
|
|
179
|
+
config = {}
|
|
180
|
+
break
|
|
181
|
+
case 'start':
|
|
182
|
+
case 'webhook':
|
|
183
|
+
case 'cron':
|
|
184
|
+
case 'error':
|
|
185
|
+
case 'poll':
|
|
186
|
+
case 'integration':
|
|
187
|
+
config = triggerConfiguration(node)
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const edges = authoringEdges(content.document.graph.edges)
|
|
192
|
+
return {
|
|
193
|
+
ref,
|
|
194
|
+
type,
|
|
195
|
+
name: node.name ?? ref,
|
|
196
|
+
readOnly: false,
|
|
197
|
+
...(node.description == null ? {} : { description: node.description }),
|
|
198
|
+
...(node.icon == null ? {} : { icon: node.icon }),
|
|
199
|
+
...('timeoutMs' in node && node.timeoutMs != null ? { timeoutMs: node.timeoutMs } : {}),
|
|
200
|
+
...('maxExecutions' in node && node.maxExecutions != null ? { maxExecutions: node.maxExecutions } : {}),
|
|
201
|
+
...(node.onError == null ? {} : { onError: node.onError }),
|
|
202
|
+
...('retry' in node && node.retry != null ? { retry: node.retry } : {}),
|
|
203
|
+
config: json(config),
|
|
204
|
+
texts: Object.fromEntries(Object.entries(texts).map(([field, text]) => [field, { lines: text.split('\n').length, characters: text.length }])),
|
|
205
|
+
inputs: outwardSource(content, inputMappings(node)),
|
|
206
|
+
inputPorts: fieldsView(Object.entries(inputDefinitions(node)).map(([handle, port]) => Object.assign({}, port, { handle }))),
|
|
207
|
+
...(Object.keys(nodeSystemOutputPorts(node)).length == 0
|
|
208
|
+
? {}
|
|
209
|
+
: { systemOutputs: fieldsView(Object.entries(nodeSystemOutputPorts(node)).map(([handle, port]) => Object.assign({ handle }, port))) }),
|
|
210
|
+
outputPorts: fieldsView(Object.entries(nodeOutputPorts(node)).map(([handle, port]) => Object.assign({}, port, { handle }))),
|
|
211
|
+
...(task != null && 'executor' in task && task.executor.kind == 'openapi' ? { authenticationChoices: openApiAuthenticationChoices(task.executor) } : {}),
|
|
212
|
+
incoming: edges.filter((e) => e.target == ref),
|
|
213
|
+
outgoing: edges.filter((e) => e.source == ref),
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function textAt(content: RevisionContent, node: GraphNode, field: 'code' | 'prompt'): string {
|
|
217
|
+
if (field == 'code' && node.kind == 'task' && 'moduleId' in node.task)
|
|
218
|
+
return content.modules[node.task.moduleId]?.source ?? fail('text.missing', 'Node source is missing.')
|
|
219
|
+
const task = taskAt(node)
|
|
220
|
+
if (field == 'prompt' && task != null && 'executor' in task && task.executor.kind == 'agent') return task.executor.prompt
|
|
221
|
+
if (field == 'prompt' && (node.kind == 'wait' || node.kind == 'approval')) return node.prompt
|
|
222
|
+
return fail('text.unsupported', `This node has no ${field} field.`)
|
|
223
|
+
}
|
|
224
|
+
export function readAuthoring(content: RevisionContent, query: AuthoringRead = {}) {
|
|
225
|
+
const q = authoringReadSchema.parse(query)
|
|
226
|
+
if (q.text != null) {
|
|
227
|
+
const node = nodeAt(content, q.text.node),
|
|
228
|
+
source = textAt(content, node, q.text.field),
|
|
229
|
+
lines = source.split('\n')
|
|
230
|
+
const selected = lines.slice(q.text.start - 1, q.text.start - 1 + q.text.lines)
|
|
231
|
+
// Bound by characters as well as lines; never present a partial line as complete.
|
|
232
|
+
let count = 0,
|
|
233
|
+
characters = 0
|
|
234
|
+
for (const line of selected) {
|
|
235
|
+
if (characters + line.length + 1 > 24000) break
|
|
236
|
+
characters += line.length + 1
|
|
237
|
+
count++
|
|
238
|
+
}
|
|
239
|
+
if (count == 0 && selected.length > 0)
|
|
240
|
+
fail('text.line-too-large', 'Selected line exceeds 24000 characters; use search to locate a smaller edit.', { node: q.text.node, field: q.text.field })
|
|
241
|
+
const end = q.text.start - 1 + count
|
|
242
|
+
return {
|
|
243
|
+
text: {
|
|
244
|
+
...q.text,
|
|
245
|
+
content: selected.slice(0, count).join('\n'),
|
|
246
|
+
totalLines: lines.length,
|
|
247
|
+
...(end < lines.length ? { nextStart: end + 1 } : {}),
|
|
248
|
+
truncated: end < lines.length,
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (q.nodes != null) return { nodes: q.nodes.map((ref) => authoringNode(content, ref)) }
|
|
253
|
+
return {
|
|
254
|
+
nodes: Object.keys(content.document.graph.nodes).map((ref) => {
|
|
255
|
+
const node = authoringNode(content, ref)
|
|
256
|
+
return {
|
|
257
|
+
ref,
|
|
258
|
+
type: node.type,
|
|
259
|
+
name: node.name,
|
|
260
|
+
readOnly: node.readOnly,
|
|
261
|
+
inputHandles: Object.keys(node.inputPorts),
|
|
262
|
+
outputHandles: Object.keys(node.outputPorts),
|
|
263
|
+
inputs: compactValue(node.inputs),
|
|
264
|
+
texts: node.texts,
|
|
265
|
+
}
|
|
266
|
+
}),
|
|
267
|
+
edges: authoringEdges(content.document.graph.edges),
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function compactValue(value: unknown, depth = 0): unknown {
|
|
271
|
+
if (depth >= 4 && value != null && typeof value == 'object') return { truncated: true }
|
|
272
|
+
if (typeof value == 'string') return value.length > 160 ? { preview: value.slice(0, 160), truncated: true } : value
|
|
273
|
+
if (Array.isArray(value))
|
|
274
|
+
return value.length > 8
|
|
275
|
+
? { preview: value.slice(0, 8).map((v) => compactValue(v, depth + 1)), truncated: true }
|
|
276
|
+
: value.map((v) => compactValue(v, depth + 1))
|
|
277
|
+
if (value != null && typeof value == 'object')
|
|
278
|
+
return Object.fromEntries([
|
|
279
|
+
...Object.entries(value)
|
|
280
|
+
.slice(0, 8)
|
|
281
|
+
.map(([key, v]) => [key, compactValue(v, depth + 1)]),
|
|
282
|
+
...(Object.keys(value).length > 8 ? [['truncated', true]] : []),
|
|
283
|
+
])
|
|
284
|
+
return value
|
|
285
|
+
}
|
|
286
|
+
export function searchAuthoring(content: RevisionContent, query: AuthoringSearch) {
|
|
287
|
+
const q = authoringSearchSchema.parse(query),
|
|
288
|
+
needle = q.query.toLowerCase()
|
|
289
|
+
const matches: { node?: string; field: string; line?: number; excerpt: string }[] = []
|
|
290
|
+
for (const [ref, node] of Object.entries(content.document.graph.nodes)) {
|
|
291
|
+
const view = authoringNode(content, ref)
|
|
292
|
+
if (q.type != null && q.type != view.type) continue
|
|
293
|
+
const fields = {
|
|
294
|
+
name: view.name,
|
|
295
|
+
description: view.description ?? '',
|
|
296
|
+
onError: view.onError ?? '',
|
|
297
|
+
retry: JSON.stringify(view.retry ?? {}),
|
|
298
|
+
inputs: JSON.stringify(view.inputs),
|
|
299
|
+
config: JSON.stringify(view.config),
|
|
300
|
+
...Object.fromEntries(Object.keys(view.texts).map((field) => [field, textAt(content, node, field as 'code' | 'prompt')])),
|
|
301
|
+
}
|
|
302
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
303
|
+
for (const [index, line] of value.split('\n').entries()) {
|
|
304
|
+
const at = line.toLowerCase().indexOf(needle)
|
|
305
|
+
if (at >= 0) matches.push({ node: ref, field, line: index + 1, excerpt: line.slice(Math.max(0, at - 100), at + Math.max(needle.length, 100)) })
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const end = q.offset + q.limit
|
|
310
|
+
return { matches: matches.slice(q.offset, end), total: matches.length, ...(end < matches.length ? { nextOffset: end } : {}) }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export interface AuthoringHost {
|
|
314
|
+
workflow?(flowId: string): Promise<WorkflowContract | WorkflowCandidate>
|
|
315
|
+
id(label: string): string
|
|
316
|
+
action(id: string): Promise<ConnectorAction>
|
|
317
|
+
trigger(key: string): TriggerKeySnapshot
|
|
318
|
+
openapi(url: string): Promise<JsonValue>
|
|
319
|
+
}
|
|
320
|
+
function merge(base: unknown, patch: unknown): unknown {
|
|
321
|
+
if (patch == null || typeof patch != 'object' || Array.isArray(patch)) return patch
|
|
322
|
+
const result = { ...record(base) }
|
|
323
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
324
|
+
if (['__proto__', 'constructor', 'prototype'].includes(key)) fail('edit.invalid-field', `Invalid field ${key}.`)
|
|
325
|
+
Object.defineProperty(result, key, { value: merge(result[key], value), enumerable: true, writable: true, configurable: true })
|
|
326
|
+
}
|
|
327
|
+
return result
|
|
328
|
+
}
|
|
329
|
+
function clearPath(value: Record<string, unknown>, path: readonly string[]) {
|
|
330
|
+
let current = value
|
|
331
|
+
for (const part of path.slice(0, -1)) {
|
|
332
|
+
if (!Object.hasOwn(current, part) || Array.isArray(current[part]) || current[part] == null || typeof current[part] != 'object')
|
|
333
|
+
fail('edit.invalid-field', `Cannot clear ${path.join('.')}.`)
|
|
334
|
+
current = record(current[part])
|
|
335
|
+
}
|
|
336
|
+
const last = path.at(-1)!
|
|
337
|
+
if (!Object.hasOwn(current, last)) fail('edit.invalid-field', `Cannot clear absent field ${path.join('.')}.`)
|
|
338
|
+
delete current[last]
|
|
339
|
+
}
|
|
340
|
+
export async function compileAuthoring(base: RevisionContent, request: AuthoringRequest, host: AuthoringHost) {
|
|
341
|
+
const parsed = authoringRequestSchema.parse(request)
|
|
342
|
+
const upgraded = upgradeRevisionContent(base)
|
|
343
|
+
let content = upgraded,
|
|
344
|
+
index = 0,
|
|
345
|
+
sequence = 0
|
|
346
|
+
const operations: ChangeOperation[] = [],
|
|
347
|
+
aliases: Record<string, string> = {}
|
|
348
|
+
const touchedModules = new Set<string>()
|
|
349
|
+
const explicitMessages = new Set<string>()
|
|
350
|
+
function fresh(label: string) {
|
|
351
|
+
return host.id(`${index}:${label}:${sequence++}`)
|
|
352
|
+
}
|
|
353
|
+
function apply(changes: readonly ChangeOperation[]) {
|
|
354
|
+
const checked = decodeChangeOperations(changes)
|
|
355
|
+
content = applyFlowChanges(content, checked)
|
|
356
|
+
operations.push(...checked)
|
|
357
|
+
}
|
|
358
|
+
function resolve(ref: string) {
|
|
359
|
+
return ref.startsWith('$')
|
|
360
|
+
? ((Object.hasOwn(aliases, ref.slice(1)) ? aliases[ref.slice(1)] : undefined) ?? fail('node.alias-missing', `Unknown alias ${ref}.`))
|
|
361
|
+
: ref
|
|
362
|
+
}
|
|
363
|
+
function source(
|
|
364
|
+
value: Exclude<AuthoringSource, { kind: 'value' | 'default' | 'unset' | 'sources' }>,
|
|
365
|
+
): Extract<InputMapping, { kind: 'sources' }>['sources'][number] {
|
|
366
|
+
if (value.kind == 'output' || value.kind == 'system') {
|
|
367
|
+
const ref = resolve(value.node)
|
|
368
|
+
const choices = Object.keys(nodeOutputPorts(nodeAt(content, ref)))
|
|
369
|
+
const port = value.port ?? (choices.length == 1 ? choices[0] : undefined)
|
|
370
|
+
if (port == null) fail('source.port-required', 'Choose an output name; this node does not have exactly one output.', { node: ref, choices })
|
|
371
|
+
return {
|
|
372
|
+
kind: 'node',
|
|
373
|
+
...(value.kind == 'system' ? { system: true as const } : {}),
|
|
374
|
+
nodeId: ref,
|
|
375
|
+
output: port,
|
|
376
|
+
...(value.field == null ? {} : { field: value.field }),
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const existing = Object.entries(content.document.bindings).find(([, binding]) => binding.target == value.name)
|
|
380
|
+
const bindingId = existing?.[0] ?? fresh('binding')
|
|
381
|
+
if (existing == null) apply([{ kind: 'binding.create', bindingId, binding: { kind: 'variable', target: value.name } }])
|
|
382
|
+
return { kind: 'binding', bindingId }
|
|
383
|
+
}
|
|
384
|
+
function replace(ref: string, node: GraphNode) {
|
|
385
|
+
const before = nodeAt(content, ref)
|
|
386
|
+
if (!dequal(before, node)) {
|
|
387
|
+
const previous = before.kind == 'task' ? before.task.outputs : before.kind == 'value' ? before.values : []
|
|
388
|
+
const next = node.kind == 'task' ? node.task.outputs : node.kind == 'value' ? node.values : []
|
|
389
|
+
apply([
|
|
390
|
+
{ kind: 'graph.node.replace', nodeId: ref, before, node },
|
|
391
|
+
...(node.kind == 'task' && 'executor' in node.task && node.task.executor.kind == 'workflow'
|
|
392
|
+
? []
|
|
393
|
+
: renameFlowOutputSources(content, new Set([ref]), previous, next)),
|
|
394
|
+
])
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function taskNode(ref: string, task: ManagedTaskDefinition) {
|
|
398
|
+
const node = nodeAt(content, ref)
|
|
399
|
+
if (node.kind != 'task' || !('executor' in node.task)) fail('node.invalid-type', 'Expected a managed node.')
|
|
400
|
+
replace(ref, { ...node, task })
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async function connectorTask(config: Record<string, unknown>, name: string): Promise<ManagedTaskDefinition> {
|
|
404
|
+
const action = await host.action(String(config.action))
|
|
405
|
+
return {
|
|
406
|
+
name,
|
|
407
|
+
executor: {
|
|
408
|
+
kind: 'connector',
|
|
409
|
+
action: action.actionId,
|
|
410
|
+
...(config.connectionId == null ? {} : { connectionId: String(config.connectionId) }),
|
|
411
|
+
...(action.outputSchema !== undefined && connectorOutputMode(action.outputSchema) == 'value' ? { outputMode: 'value' as const } : {}),
|
|
412
|
+
},
|
|
413
|
+
inputs: Object.entries(action.inputs).map(([handle, port]) => Object.assign({ handle }, port)),
|
|
414
|
+
outputs: Object.entries(action.outputs).map(([handle, port]) => Object.assign({ handle }, port)),
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
async function workflowTarget(flowId: string) {
|
|
418
|
+
if (host.workflow == null) fail('workflow.unavailable', 'Workflow calls are unavailable.')
|
|
419
|
+
return host.workflow(flowId)
|
|
420
|
+
}
|
|
421
|
+
async function resolveWorkflow(flowId: string, name: string, previous?: ManagedTaskDefinition) {
|
|
422
|
+
const target = await workflowTarget(flowId)
|
|
423
|
+
return 'inputs' in target ? workflowTask(target, name) : workflowSelectionTask({ ...target, name }, undefined, previous)
|
|
424
|
+
}
|
|
425
|
+
async function configure(ref: string, patch: Record<string, unknown> = {}, clear: readonly (readonly string[])[] = []) {
|
|
426
|
+
const node = nodeAt(content, ref),
|
|
427
|
+
view = authoringNode(content, ref),
|
|
428
|
+
type = view.type as AuthoringType
|
|
429
|
+
const original = {
|
|
430
|
+
name: view.name,
|
|
431
|
+
...(view.description == null ? {} : { description: view.description }),
|
|
432
|
+
...(view.icon == null ? {} : { icon: view.icon }),
|
|
433
|
+
...(view.timeoutMs == null ? {} : { timeoutMs: view.timeoutMs }),
|
|
434
|
+
...(view.maxExecutions == null ? {} : { maxExecutions: view.maxExecutions }),
|
|
435
|
+
...(view.onError == null ? {} : { onError: view.onError }),
|
|
436
|
+
...(view.retry == null ? {} : { retry: view.retry }),
|
|
437
|
+
config: view.config,
|
|
438
|
+
}
|
|
439
|
+
const update = record(merge(original, patch))
|
|
440
|
+
const explicit = (key: string) => Object.hasOwn(record(patch.config), key) || clear.some((path) => path[0] == 'config' && path[1] == key)
|
|
441
|
+
if (type == 'wait') {
|
|
442
|
+
const timing = record(record(patch.config).autoContinue)
|
|
443
|
+
if (Object.hasOwn(timing, 'kind') && timing.kind != record(record(view.config).autoContinue).kind) record(update.config).autoContinue = timing
|
|
444
|
+
}
|
|
445
|
+
if (type == 'openapi' && Object.hasOwn(record(patch.config), 'authentication')) record(update.config).authentication = record(patch.config).authentication
|
|
446
|
+
for (const path of clear) clearPath(update, path)
|
|
447
|
+
if (typeof update.name != 'string') fail('node.name-required', 'Node name cannot be cleared.')
|
|
448
|
+
const config = authoringConfigurations[type].parse(update.config) as Record<string, unknown>
|
|
449
|
+
if (Object.hasOwn(update, 'retry')) update.retry = retryConfigurationSchema.parse(update.retry)
|
|
450
|
+
const { config: _, ...metadata } = update
|
|
451
|
+
const { name: _name, description: _description, icon: _icon, ...rest } = node
|
|
452
|
+
let next = { ...rest, ...metadata } as GraphNode
|
|
453
|
+
if (!isTriggerNode(node)) {
|
|
454
|
+
const clean = { ...next } as Record<string, unknown>
|
|
455
|
+
if (!Object.hasOwn(update, 'onError')) delete clean.onError
|
|
456
|
+
if (!Object.hasOwn(update, 'retry')) delete clean.retry
|
|
457
|
+
if (!Object.hasOwn(update, 'timeoutMs')) delete clean.timeoutMs
|
|
458
|
+
if (!Object.hasOwn(update, 'maxExecutions')) delete clean.maxExecutions
|
|
459
|
+
next = clean as unknown as GraphNode
|
|
460
|
+
} else if (update.timeoutMs != null || update.maxExecutions != null) fail('node.invalid-field', 'Trigger nodes do not have execution limits.')
|
|
461
|
+
if (update.onError != null && (!supportsNodeErrorHandling(next) || !['stop', 'error'].includes(String(update.onError))))
|
|
462
|
+
fail('node.invalid-field', 'This node does not support this error handling setting.')
|
|
463
|
+
if (update.retry != null && !supportsNodeRetry(next)) fail('node.invalid-field', 'This node does not support retries.')
|
|
464
|
+
if (node.kind == 'start' || node.kind == 'outputs') {
|
|
465
|
+
replace(ref, next)
|
|
466
|
+
const fields = node.kind == 'start' ? config.inputDefs : config.outputDefs
|
|
467
|
+
const ports = fieldsToPorts(fields, node.kind == 'start' ? node.inputDefs : node.outputDefs).filter((port): port is InputPort => 'handle' in port)
|
|
468
|
+
const changes = node.kind == 'start' ? updateStartInputDefs(content, ref, ports) : updateOutputsDefs(content, ref, ports)
|
|
469
|
+
if (changes.length > 0) apply(changes)
|
|
470
|
+
return
|
|
471
|
+
}
|
|
472
|
+
function taskInputs(task: ManagedTaskDefinition | NonNullable<Extract<GraphNode, { kind: 'task' }>['task']>, reserved: readonly string[] = []) {
|
|
473
|
+
if (node.kind != 'task') throw new Error('Expected a task.')
|
|
474
|
+
const fields = record(config.inputs)
|
|
475
|
+
for (const key of reserved)
|
|
476
|
+
if (Object.hasOwn(fields, key)) fail('input.reserved', `Configure ${key} through config.${key} or input.set.`, { node: ref, input: key })
|
|
477
|
+
const extraNames = new Set(node.additionalInputs?.map((port) => port.handle) ?? [])
|
|
478
|
+
const main = Object.fromEntries(Object.entries(fields).filter(([key]) => !extraNames.has(key)))
|
|
479
|
+
for (const port of task.inputs)
|
|
480
|
+
if ('handle' in port && (reserved.includes(port.handle) || (extraNames.has(port.handle) && Object.hasOwn(fields, port.handle))))
|
|
481
|
+
main[port.handle] = fieldsView([port])[port.handle]
|
|
482
|
+
if (node.additionalInputs != null) {
|
|
483
|
+
const extra = Object.fromEntries(Object.entries(fields).filter(([key]) => extraNames.has(key)))
|
|
484
|
+
for (const port of node.additionalInputs) if (reserved.includes(port.handle)) extra[port.handle] = fieldsView([port])[port.handle]
|
|
485
|
+
next = { ...next, additionalInputs: fieldsToPorts(extra, node.additionalInputs) as readonly InputPort[] } as GraphNode
|
|
486
|
+
}
|
|
487
|
+
return fieldsToPorts(main, task.inputs)
|
|
488
|
+
}
|
|
489
|
+
if (node.kind == 'task' && 'moduleId' in node.task) {
|
|
490
|
+
const inputs = taskInputs(node.task)
|
|
491
|
+
next = {
|
|
492
|
+
...next,
|
|
493
|
+
task: {
|
|
494
|
+
moduleId: node.task.moduleId,
|
|
495
|
+
name: String(update.name),
|
|
496
|
+
...config,
|
|
497
|
+
...(config.capabilities == null
|
|
498
|
+
? {}
|
|
499
|
+
: { capabilities: await prepareCapabilities(config.capabilities as readonly AuthoringCapability[], node.task.capabilities ?? [], workflowTarget) }),
|
|
500
|
+
inputs,
|
|
501
|
+
outputs: fieldsToPorts(config.outputs, node.task.outputs),
|
|
502
|
+
},
|
|
503
|
+
} as GraphNode
|
|
504
|
+
} else if (node.kind == 'task' && 'executor' in node.task) {
|
|
505
|
+
const before = node.task
|
|
506
|
+
let task: ManagedTaskDefinition
|
|
507
|
+
if (type == 'workflow') {
|
|
508
|
+
task =
|
|
509
|
+
before.executor.kind == 'workflow' && before.executor.flowId == config.flowId
|
|
510
|
+
? { ...before, name: String(update.name) }
|
|
511
|
+
: await resolveWorkflow(String(config.flowId), String(update.name))
|
|
512
|
+
next = withWorkflowTask({ ...node, ...metadata } as typeof node, task)
|
|
513
|
+
} else if (type == 'connector') {
|
|
514
|
+
if (before.executor.kind == 'connector' && config.action == before.executor.action) {
|
|
515
|
+
const { connectionId: _connectionId, ...executor } = before.executor
|
|
516
|
+
task = { ...before, name: String(update.name), executor: { ...executor, ...config } } as ManagedTaskDefinition
|
|
517
|
+
} else task = await connectorTask(config, String(update.name))
|
|
518
|
+
} else if (type == 'decision') task = decisionTask(config.questions as Parameters<typeof decisionTask>[0], String(update.name))
|
|
519
|
+
else if (type == 'openapi') {
|
|
520
|
+
task = await prepareOpenApiTask(config as AuthoringOpenApiConfig, before, (url) => host.openapi(url), String(update.name), {
|
|
521
|
+
authentication: explicit('authentication'),
|
|
522
|
+
serverUrl: explicit('serverUrl'),
|
|
523
|
+
})
|
|
524
|
+
} else if (type == 'llm') {
|
|
525
|
+
task = prepareLlmConfiguration(config, before, String(update.name))
|
|
526
|
+
task = { ...task, inputs: taskInputs(task, llmReservedInputs) }
|
|
527
|
+
} else {
|
|
528
|
+
const { inputs: _inputs, resultSchema, notification, tools, ...executor } = config
|
|
529
|
+
const prepared = { ...executor, kind: type, ...(before.executor.kind == 'agent' ? { prompt: before.executor.prompt } : {}) }
|
|
530
|
+
if (type == 'agent') {
|
|
531
|
+
const previous = before.executor.kind == 'agent' ? before.executor : undefined
|
|
532
|
+
Object.assign(prepared, {
|
|
533
|
+
tools: await prepareAgentTools(
|
|
534
|
+
tools as AuthoringAgentTool[],
|
|
535
|
+
previous?.tools ?? [],
|
|
536
|
+
(id) => host.action(id),
|
|
537
|
+
() => fresh('tool'),
|
|
538
|
+
workflowTarget,
|
|
539
|
+
),
|
|
540
|
+
})
|
|
541
|
+
Reflect.deleteProperty(prepared, 'notification')
|
|
542
|
+
if (notification != null) {
|
|
543
|
+
if (dequal(record(view.config).notification, notification) && previous?.notification != null)
|
|
544
|
+
Object.assign(prepared, { notification: previous.notification })
|
|
545
|
+
else {
|
|
546
|
+
const settings = record(notification)
|
|
547
|
+
const definition = await connectorTask(settings, `${update.name} notification`)
|
|
548
|
+
Object.assign(prepared, {
|
|
549
|
+
notification: {
|
|
550
|
+
action: settings.action,
|
|
551
|
+
...(settings.connectionId == null ? {} : { connectionId: settings.connectionId }),
|
|
552
|
+
inputDefinitions: definition.inputs,
|
|
553
|
+
messageHandle: settings.messageHandle,
|
|
554
|
+
inputs: settings.inputs,
|
|
555
|
+
},
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
const outputs = dequal(record(view.config).resultSchema, resultSchema)
|
|
561
|
+
? before.outputs
|
|
562
|
+
: before.outputs.map((port) => ('handle' in port ? { ...port, jsonSchema: resultSchema as JsonValue } : port))
|
|
563
|
+
task = {
|
|
564
|
+
name: String(update.name),
|
|
565
|
+
inputs: type == 'agent' ? taskInputs(before) : before.inputs,
|
|
566
|
+
outputs,
|
|
567
|
+
executor: prepared,
|
|
568
|
+
} as ManagedTaskDefinition
|
|
569
|
+
}
|
|
570
|
+
next = { ...next, task } as GraphNode
|
|
571
|
+
} else
|
|
572
|
+
switch (node.kind) {
|
|
573
|
+
case 'condition':
|
|
574
|
+
next = { ...next, ...prepareConditionConfiguration(config as unknown as AuthoringConditionConfiguration, source) } as GraphNode
|
|
575
|
+
break
|
|
576
|
+
case 'value': {
|
|
577
|
+
const fields = { ...record(config.outputs) }
|
|
578
|
+
for (const key of Object.keys(record(config.values))) if (!Object.hasOwn(fields, key)) fields[key] = {}
|
|
579
|
+
const values = fieldsToPorts(
|
|
580
|
+
fields,
|
|
581
|
+
node.values.map(({ value: _value, ...port }) => port),
|
|
582
|
+
)
|
|
583
|
+
.filter((port): port is InputPort => 'handle' in port)
|
|
584
|
+
.map((port) => Object.assign({}, port, Object.hasOwn(record(config.values), port.handle) ? { value: record(config.values)[port.handle] } : {}))
|
|
585
|
+
next = { ...next, values } as GraphNode
|
|
586
|
+
break
|
|
587
|
+
}
|
|
588
|
+
case 'wait':
|
|
589
|
+
case 'approval':
|
|
590
|
+
next = { ...next, inputDefinitions: fieldsToPorts(config.inputs, node.inputDefinitions) } as GraphNode
|
|
591
|
+
if (node.kind == 'wait') {
|
|
592
|
+
Reflect.deleteProperty(next, 'autoContinue')
|
|
593
|
+
if (config.autoContinue != null) Object.assign(next, { autoContinue: config.autoContinue })
|
|
594
|
+
}
|
|
595
|
+
break
|
|
596
|
+
case 'webhook': {
|
|
597
|
+
const { body, ...settings } = config
|
|
598
|
+
const responseOptions = record(settings.options)
|
|
599
|
+
const responsePolicy = record(responseOptions.responseBody)
|
|
600
|
+
if (responsePolicy.kind === 'webhook-response' && typeof responsePolicy.nodeId === 'string')
|
|
601
|
+
settings.options = { ...responseOptions, responseBody: { ...responsePolicy, nodeId: resolve(responsePolicy.nodeId) } }
|
|
602
|
+
const { options: _options, ...withoutOptions } = next as Extract<GraphNode, { kind: 'webhook' }>
|
|
603
|
+
next = { ...withoutOptions, ...settings, bodyFields: fieldsToPorts(body, node.bodyFields) } as GraphNode
|
|
604
|
+
break
|
|
605
|
+
}
|
|
606
|
+
case 'cron':
|
|
607
|
+
next = { ...next, cronTimes: config.schedule } as GraphNode
|
|
608
|
+
break
|
|
609
|
+
case 'poll':
|
|
610
|
+
case 'integration': {
|
|
611
|
+
if (config.key != node.definition.key) fail('trigger.key-immutable', 'Create a new node to change its Trigger type.')
|
|
612
|
+
const { connectionId: _connection, ...withoutConnection } = next as Extract<GraphNode, { kind: 'poll' | 'integration' }>
|
|
613
|
+
next = {
|
|
614
|
+
...withoutConnection,
|
|
615
|
+
...(config.connectionId == null ? {} : { connectionId: config.connectionId }),
|
|
616
|
+
...(node.kind == 'poll' ? { pollTimes: config.schedule } : {}),
|
|
617
|
+
} as GraphNode
|
|
618
|
+
break
|
|
619
|
+
}
|
|
620
|
+
default: {
|
|
621
|
+
const fields = { ...next } as Record<string, unknown>
|
|
622
|
+
for (const key of Object.keys(record(view.config))) delete fields[key]
|
|
623
|
+
next = { ...fields, ...config } as unknown as GraphNode
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
replace(ref, next)
|
|
627
|
+
}
|
|
628
|
+
async function setText(ref: string, field: 'code' | 'prompt', text: string) {
|
|
629
|
+
const node = nodeAt(content, ref),
|
|
630
|
+
before = textAt(content, node, field)
|
|
631
|
+
if (before == text) return
|
|
632
|
+
if (field == 'code' && node.kind == 'task' && 'moduleId' in node.task) {
|
|
633
|
+
const moduleId = node.task.moduleId,
|
|
634
|
+
module = content.modules[moduleId]!
|
|
635
|
+
const shared =
|
|
636
|
+
Object.values(content.document.graph.nodes).some((n) => n.kind == 'task' && 'moduleId' in n.task && n.task.moduleId == moduleId && n !== node) ||
|
|
637
|
+
Object.values(content.modules).some((m) => m.imports.includes(moduleId))
|
|
638
|
+
const imports = await moduleImports(text)
|
|
639
|
+
if (shared) {
|
|
640
|
+
const id = fresh('module')
|
|
641
|
+
apply([{ kind: 'module.create', moduleId: id, module: { ...module, source: text, imports } }])
|
|
642
|
+
replace(ref, { ...node, task: { ...node.task, moduleId: id } })
|
|
643
|
+
} else apply([{ kind: 'module.source.replace', moduleId, beforeSource: before, beforeImports: module.imports, source: text, imports }])
|
|
644
|
+
} else if (node.kind == 'task' && 'executor' in node.task) {
|
|
645
|
+
const task = node.task
|
|
646
|
+
taskNode(ref, { ...task, executor: { ...task.executor, prompt: text } } as ManagedTaskDefinition)
|
|
647
|
+
} else if (node.kind == 'wait' || node.kind == 'approval') replace(ref, { ...node, prompt: text })
|
|
648
|
+
}
|
|
649
|
+
async function setInput(ref: string, input: string, value: AuthoringSource) {
|
|
650
|
+
if (input == 'message') explicitMessages.add(ref)
|
|
651
|
+
const node = nodeAt(content, ref)
|
|
652
|
+
if (node.kind == 'condition')
|
|
653
|
+
fail('input.unsupported', 'Edit the branch condition operands through config.branches.', { node: ref, field: 'config.branches' })
|
|
654
|
+
if (node.kind == 'poll' || node.kind == 'integration') {
|
|
655
|
+
if (!Object.hasOwn(inputDefinitions(node), input))
|
|
656
|
+
fail('input.not-found', `Unknown input ${input}.`, { node: ref, input, choices: Object.keys(inputDefinitions(node)) })
|
|
657
|
+
if (value.kind != 'value' && value.kind != 'unset' && value.kind != 'default')
|
|
658
|
+
fail('input.source-unsupported', 'Trigger parameters accept only value, unset or default; they cannot depend on execution outputs or variables.', {
|
|
659
|
+
node: ref,
|
|
660
|
+
input,
|
|
661
|
+
allowed: ['value', 'unset', 'default'],
|
|
662
|
+
})
|
|
663
|
+
const config = { ...node.config }
|
|
664
|
+
if (value.kind == 'default') delete config[input]
|
|
665
|
+
else config[input] = value
|
|
666
|
+
replace(ref, { ...node, config })
|
|
667
|
+
return
|
|
668
|
+
}
|
|
669
|
+
if (!Object.hasOwn(inputDefinitions(node), input)) {
|
|
670
|
+
const type = typeOf(node)
|
|
671
|
+
if (!['agent', 'code', 'llm', 'wait', 'approval'].includes(type))
|
|
672
|
+
fail('input.not-found', `Unknown input ${input}.`, { node: ref, input, choices: Object.keys(inputDefinitions(node)) })
|
|
673
|
+
if (type == 'llm' && llmReservedInputs.includes(input))
|
|
674
|
+
fail('input.not-found', `Missing built-in LLM input ${input}.`, { node: ref, input, choices: Object.keys(inputDefinitions(node)) })
|
|
675
|
+
await configure(ref, { config: { inputs: { [input]: {} } } })
|
|
676
|
+
}
|
|
677
|
+
const mapping: InputMapping | undefined =
|
|
678
|
+
value.kind == 'default'
|
|
679
|
+
? undefined
|
|
680
|
+
: value.kind == 'value' || value.kind == 'unset'
|
|
681
|
+
? value
|
|
682
|
+
: { kind: 'sources', sources: (value.kind == 'sources' ? value.sources : [value]).map(source) }
|
|
683
|
+
const before = nodeInputMappings(nodeAt(content, ref))[input]
|
|
684
|
+
if (!dequal(before, mapping)) apply([{ kind: 'graph.node.input.set', nodeId: ref, handle: input, before, ...(mapping == null ? {} : { value: mapping }) }])
|
|
685
|
+
}
|
|
686
|
+
for (const edit of parsed.edits) {
|
|
687
|
+
try {
|
|
688
|
+
switch (edit.op) {
|
|
689
|
+
case 'node.refresh': {
|
|
690
|
+
const ref = resolve(edit.node)
|
|
691
|
+
const node = nodeAt(content, ref)
|
|
692
|
+
if (node.kind != 'task') fail('node.invalid-type', 'Only workflow nodes and tools support refresh.')
|
|
693
|
+
if (edit.tool != null) {
|
|
694
|
+
if ('moduleId' in node.task) {
|
|
695
|
+
const selected = node.task.capabilities?.find((item) => item.kind == 'workflow' && item.name == edit.tool)
|
|
696
|
+
if (selected?.kind != 'workflow') fail('tool.not-found', 'Workflow tool was not found.')
|
|
697
|
+
const target = await workflowTarget(selected.flowId)
|
|
698
|
+
if (!('inputs' in target)) fail('workflow.unavailable', 'The workflow interface is unavailable.')
|
|
699
|
+
replace(ref, {
|
|
700
|
+
...node,
|
|
701
|
+
task: {
|
|
702
|
+
...node.task,
|
|
703
|
+
capabilities: node.task.capabilities!.map((item) => (item === selected ? workflowCapability(selected.name, target) : item)),
|
|
704
|
+
},
|
|
705
|
+
})
|
|
706
|
+
} else if (node.task.executor.kind == 'agent') {
|
|
707
|
+
const selected = node.task.executor.tools.find((item) => item.kind == 'workflow' && item.name == edit.tool)
|
|
708
|
+
if (selected?.kind != 'workflow') fail('tool.not-found', 'Workflow tool was not found.')
|
|
709
|
+
const target = await workflowTarget(selected.flowId)
|
|
710
|
+
if (!('inputs' in target)) fail('workflow.unavailable', 'The workflow interface is unavailable.')
|
|
711
|
+
replace(ref, {
|
|
712
|
+
...node,
|
|
713
|
+
task: {
|
|
714
|
+
...node.task,
|
|
715
|
+
executor: {
|
|
716
|
+
...node.task.executor,
|
|
717
|
+
tools: node.task.executor.tools.map((item) => (item === selected ? workflowAgentTool(selected.id, selected.name, target, selected) : item)),
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
})
|
|
721
|
+
} else fail('node.invalid-type', 'Only Agent and Code nodes have workflow tools.')
|
|
722
|
+
} else {
|
|
723
|
+
if (!('executor' in node.task) || node.task.executor.kind != 'workflow') fail('node.invalid-type', 'Specify a workflow tool name.')
|
|
724
|
+
const task = await resolveWorkflow(node.task.executor.flowId, node.name ?? node.task.name, node.task)
|
|
725
|
+
apply([{ kind: 'graph.node.replace', nodeId: ref, before: node, node: withWorkflowTask(node, task) }])
|
|
726
|
+
}
|
|
727
|
+
break
|
|
728
|
+
}
|
|
729
|
+
case 'node.add': {
|
|
730
|
+
if (Object.hasOwn(aliases, edit.as)) fail('node.alias-duplicate', `Duplicate alias ${edit.as}.`)
|
|
731
|
+
const nodeId = host.id(`node:${edit.as}`),
|
|
732
|
+
identity = { nodeId, moduleId: fresh('module') },
|
|
733
|
+
name = edit.name
|
|
734
|
+
aliases[edit.as] = nodeId
|
|
735
|
+
const config = edit.config ?? {}
|
|
736
|
+
let changes: readonly ChangeOperation[]
|
|
737
|
+
switch (edit.type) {
|
|
738
|
+
case 'workflow':
|
|
739
|
+
changes = createManagedTask(identity, await resolveWorkflow(String(authoringConfigurations.workflow.parse(config).flowId), name))
|
|
740
|
+
break
|
|
741
|
+
case 'webhook-response':
|
|
742
|
+
changes = [{ kind: 'graph.node.create', nodeId, node: { kind: 'webhook-response', name, inputs: {} } }]
|
|
743
|
+
break
|
|
744
|
+
case 'outputs':
|
|
745
|
+
changes = [{ kind: 'graph.node.create', nodeId, node: { outputDefs: [], outputMappings: {}, kind: 'outputs', name } }]
|
|
746
|
+
break
|
|
747
|
+
case 'code':
|
|
748
|
+
changes = createCodeTask(identity, name, undefined, {
|
|
749
|
+
inputs: [],
|
|
750
|
+
outputs: config.outputs == null ? defaultCodeTaskPorts.outputs : [],
|
|
751
|
+
})
|
|
752
|
+
break
|
|
753
|
+
case 'agent':
|
|
754
|
+
changes = createAgentTask(identity, name, { inputs: [], prompt: edit.prompt ?? '' })
|
|
755
|
+
break
|
|
756
|
+
case 'llm':
|
|
757
|
+
changes = createLlmTask(identity, name, 'chat', 'Generated response.').map((change) =>
|
|
758
|
+
change.kind == 'graph.node.create' && change.node.kind == 'task' && 'executor' in change.node.task
|
|
759
|
+
? Object.assign({}, change, { node: Object.assign({}, change.node, { task: initialAuthoringLlmTask(change.node.task) }) })
|
|
760
|
+
: change,
|
|
761
|
+
)
|
|
762
|
+
break
|
|
763
|
+
case 'decision':
|
|
764
|
+
changes = createDecisionTask(identity, name)
|
|
765
|
+
break
|
|
766
|
+
case 'connector':
|
|
767
|
+
changes = createManagedTask(identity, await connectorTask(config, name))
|
|
768
|
+
break
|
|
769
|
+
case 'openapi':
|
|
770
|
+
changes = createManagedTask(
|
|
771
|
+
identity,
|
|
772
|
+
await prepareOpenApiTask(authoringConfigurations.openapi.parse(config), undefined, (url) => host.openapi(url), name),
|
|
773
|
+
)
|
|
774
|
+
break
|
|
775
|
+
case 'condition':
|
|
776
|
+
changes = createCondition(nodeId, name)
|
|
777
|
+
break
|
|
778
|
+
case 'value':
|
|
779
|
+
changes = createValue(nodeId, name, [])
|
|
780
|
+
break
|
|
781
|
+
case 'wait':
|
|
782
|
+
changes = createWait(nodeId, name)
|
|
783
|
+
break
|
|
784
|
+
case 'approval':
|
|
785
|
+
changes = createApproval(nodeId, name)
|
|
786
|
+
break
|
|
787
|
+
case 'poll':
|
|
788
|
+
case 'integration': {
|
|
789
|
+
const definition = host.trigger(String(config.key))
|
|
790
|
+
if (definition.type != edit.type) fail('trigger.type-mismatch', 'Trigger key has a different type.')
|
|
791
|
+
changes = createProviderTrigger(nodeId, definition, { name, config: {} })
|
|
792
|
+
break
|
|
793
|
+
}
|
|
794
|
+
default:
|
|
795
|
+
changes = [
|
|
796
|
+
{
|
|
797
|
+
kind: 'graph.node.create',
|
|
798
|
+
nodeId,
|
|
799
|
+
node: {
|
|
800
|
+
kind: edit.type,
|
|
801
|
+
name,
|
|
802
|
+
...(edit.type == 'start' ? { inputDefs: [] } : {}),
|
|
803
|
+
...(edit.type == 'cron' ? { cronTimes: [{ type: 'every', unit: 'hour', value: 1 }] } : {}),
|
|
804
|
+
...(edit.type == 'webhook' ? { method: 'POST', bodyFields: [] } : {}),
|
|
805
|
+
} as GraphNode,
|
|
806
|
+
},
|
|
807
|
+
]
|
|
808
|
+
}
|
|
809
|
+
apply(changes)
|
|
810
|
+
await configure(nodeId, { config, ...(edit.onError == null ? {} : { onError: edit.onError }), ...(edit.retry == null ? {} : { retry: edit.retry }) })
|
|
811
|
+
const added = nodeAt(content, nodeId)
|
|
812
|
+
if (added.kind == 'task') replace(nodeId, { ...added, inputs: {} })
|
|
813
|
+
if (edit.code != null) await setText(nodeId, 'code', edit.code)
|
|
814
|
+
if (edit.prompt != null) await setText(nodeId, 'prompt', edit.prompt)
|
|
815
|
+
for (const [input, binding] of Object.entries(edit.inputs ?? {})) await setInput(nodeId, input, binding)
|
|
816
|
+
break
|
|
817
|
+
}
|
|
818
|
+
case 'node.update':
|
|
819
|
+
await configure(resolve(edit.node), edit.set, edit.clear)
|
|
820
|
+
break
|
|
821
|
+
case 'node.remove': {
|
|
822
|
+
const ref = resolve(edit.node),
|
|
823
|
+
node = nodeAt(content, ref)
|
|
824
|
+
if (node.kind == 'task' && 'moduleId' in node.task) touchedModules.add(node.task.moduleId)
|
|
825
|
+
apply([{ kind: 'graph.node.delete', nodeId: ref }])
|
|
826
|
+
break
|
|
827
|
+
}
|
|
828
|
+
case 'input.set': {
|
|
829
|
+
await setInput(resolve(edit.node), edit.input, edit.source)
|
|
830
|
+
break
|
|
831
|
+
}
|
|
832
|
+
case 'edge.connect':
|
|
833
|
+
case 'edge.disconnect': {
|
|
834
|
+
const from = resolve(edit.source),
|
|
835
|
+
to = resolve(edit.target)
|
|
836
|
+
nodeAt(content, from)
|
|
837
|
+
nodeAt(content, to)
|
|
838
|
+
const edge = { source: from, target: to, ...(edit.branch == null ? {} : { sourceHandle: edit.branch }) }
|
|
839
|
+
const changes = edit.op == 'edge.connect' ? connect(content, edge) : disconnect(content, edge)
|
|
840
|
+
apply(explicitMessages.has(to) ? changes.filter((change) => change.kind != 'graph.node.input.set') : changes)
|
|
841
|
+
break
|
|
842
|
+
}
|
|
843
|
+
case 'text.set':
|
|
844
|
+
await setText(resolve(edit.node), edit.field, edit.text)
|
|
845
|
+
break
|
|
846
|
+
case 'text.edit': {
|
|
847
|
+
const ref = resolve(edit.node),
|
|
848
|
+
value = textAt(content, nodeAt(content, ref), edit.field),
|
|
849
|
+
count = value.split(edit.oldText).length - 1
|
|
850
|
+
if (count != 1)
|
|
851
|
+
fail('text.match-count', `Expected one exact match, found ${count}. Read the field and include more context.`, {
|
|
852
|
+
node: ref,
|
|
853
|
+
field: edit.field,
|
|
854
|
+
matches: count,
|
|
855
|
+
})
|
|
856
|
+
await setText(
|
|
857
|
+
ref,
|
|
858
|
+
edit.field,
|
|
859
|
+
value.replace(edit.oldText, () => edit.newText),
|
|
860
|
+
)
|
|
861
|
+
break
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
} catch (error) {
|
|
865
|
+
if (error instanceof AuthoringError || error instanceof AuthoringToolInputError || error instanceof AuthoringOpenApiAuthenticationError)
|
|
866
|
+
throw new AuthoringError(error.code, error.message, { ...error.details, editIndex: index })
|
|
867
|
+
throw new AuthoringError('edit.invalid', error instanceof Error ? error.message : String(error), { editIndex: index })
|
|
868
|
+
}
|
|
869
|
+
index++
|
|
870
|
+
}
|
|
871
|
+
for (const moduleId of touchedModules)
|
|
872
|
+
if (
|
|
873
|
+
!Object.values(content.document.graph.nodes).some((n) => n.kind == 'task' && 'moduleId' in n.task && n.task.moduleId == moduleId) &&
|
|
874
|
+
!Object.values(content.modules).some((m) => m.imports.includes(moduleId))
|
|
875
|
+
)
|
|
876
|
+
apply([{ kind: 'module.delete', moduleId }])
|
|
877
|
+
// Shared binding lifecycle remains owned by the existing authoring implementation.
|
|
878
|
+
const cleaned = cleanVariableBindings(upgraded, operations)
|
|
879
|
+
return { operations: cleaned, content: cleaned.length == 0 ? upgraded : applyFlowChanges(upgraded, cleaned), nodes: aliases }
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const escapePointer = (value: string) => value.replaceAll('~', '~0').replaceAll('/', '~1')
|
|
883
|
+
export function authoringDiagnostics(content: RevisionContent, diagnostics: readonly Diagnostic[]) {
|
|
884
|
+
const nodes = Object.entries(content.document.graph.nodes)
|
|
885
|
+
return diagnostics.map((diagnostic) => {
|
|
886
|
+
const affected = nodes.filter(([id, node]) => {
|
|
887
|
+
const roots = [`/document/graph/nodes/${escapePointer(id)}`]
|
|
888
|
+
if (node.kind == 'task' && 'moduleId' in node.task) roots.push(`/modules/${escapePointer(node.task.moduleId)}`)
|
|
889
|
+
return roots.some((root) => diagnostic.path == root || diagnostic.path.startsWith(`${root}/`))
|
|
890
|
+
})
|
|
891
|
+
// Diagnostic wording comes from the shared validators; storage identities are replaced at this boundary.
|
|
892
|
+
const message = diagnostic.message
|
|
893
|
+
.replace(/(CodeModule|Managed Task|Task|Binding) "([^"\n]+)"/g, (_, kind: string, id: string) => {
|
|
894
|
+
if (kind == 'Binding') return `Variable "${content.document.bindings[id]?.target ?? 'unresolved'}"`
|
|
895
|
+
const owners = nodes.filter(([, n]) => n.kind == 'task' && kind == 'CodeModule' && 'moduleId' in n.task && n.task.moduleId == id)
|
|
896
|
+
return owners.length
|
|
897
|
+
? `${kind == 'CodeModule' ? 'Code for' : 'Node'} ${owners.map(([ref, n]) => JSON.stringify(n.name ?? ref)).join(', ')}`
|
|
898
|
+
: kind == 'CodeModule'
|
|
899
|
+
? 'Referenced code'
|
|
900
|
+
: 'Node configuration'
|
|
901
|
+
})
|
|
902
|
+
.replaceAll('CodeModule', 'Code')
|
|
903
|
+
const parts = diagnostic.path.split('/').map((part) => part.replaceAll('~1', '/').replaceAll('~0', '~'))
|
|
904
|
+
return {
|
|
905
|
+
code: diagnostic.code,
|
|
906
|
+
message,
|
|
907
|
+
nodes: affected.map(([id]) => id),
|
|
908
|
+
field: authoringDiagnosticField(affected[0]?.[1], parts),
|
|
909
|
+
...(diagnostic.line ? { line: diagnostic.line, column: diagnostic.column } : {}),
|
|
910
|
+
}
|
|
911
|
+
})
|
|
912
|
+
}
|