@oomol-lab/open-flow 0.1.0-beta.81 → 0.1.0-beta.82

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.
Files changed (48) hide show
  1. package/README.md +35 -0
  2. package/dist/browser/{FlowCanvas-CrsiDHK8.js → FlowCanvas-Dew2fmoT.js} +55 -274
  3. package/dist/browser/{createResourceDialog-CS3u9ga9.js → createResourceDialog-bdOJa5Zk.js} +2 -1
  4. package/dist/browser/{exportPng-BOPfjeEi.js → exportPng-Cjdb6COo.js} +2 -2
  5. package/dist/browser/{flowChanges-CFj5pL4Y.js → flowChanges-URBEd4DI.js} +2583 -71
  6. package/dist/browser/{flowWorkspace-BeQaeJ3e.js → flowWorkspace-BlXx-yAn.js} +440 -2578
  7. package/dist/browser/{markdownContent-CAG_CQfH.js → markdownContent-D8G_oNOe.js} +18 -41
  8. package/dist/browser/{markdownContent-frwDfQQU.js → markdownContent-IS8xXDoc.js} +18 -40
  9. package/dist/browser/preview.css +88 -80
  10. package/dist/browser/preview.js +150 -60
  11. package/dist/browser/ui.css +8 -0
  12. package/dist/browser/{dist-CHwj9BhV.js → videoLink-NAWDmBtf.js} +35 -1
  13. package/dist/browser/workbench-contract.d.ts +6 -0
  14. package/dist/browser/workbench.css +259 -251
  15. package/dist/browser/workbench.d.ts +2 -0
  16. package/dist/browser/workbench.js +289 -137
  17. package/dist/browser/{ContentIcon-CMn0c-kl.js → wrapLayout-DZvW9dBs.js} +4397 -4152
  18. package/dist/command/index.js +43 -41
  19. package/dist/common/authoring.d.ts +1 -1
  20. package/dist/common/control-api-conformance.js +306 -2
  21. package/dist/common/control-api.d.ts +20 -0
  22. package/dist/common/control-api.js +306 -4
  23. package/dist/common/control-requests.d.ts +3 -5
  24. package/dist/common/control-requests.js +5 -7
  25. package/dist/common/flow-encoding.d.ts +1 -0
  26. package/dist/common/flow-encoding.js +167 -1
  27. package/dist/common/flow-semantics.js +20 -0
  28. package/dist/common/flow-workflowBundle.d.ts +27 -0
  29. package/dist/common/flow-workflowFile.d.ts +2 -0
  30. package/dist/common/flow-workflowReferences.d.ts +4 -0
  31. package/dist/common/mcp.d.ts +2 -0
  32. package/dist/common/mcp.js +312 -4
  33. package/dist/render/browser/licenses.md +4047 -0
  34. package/dist/render/browser/runtime.css +14877 -0
  35. package/dist/render/browser/runtime.js +266765 -0
  36. package/dist/render/common/png.d.ts +24 -0
  37. package/dist/render/node/index.d.ts +11 -0
  38. package/dist/render/node/index.js +9491 -0
  39. package/dist/render/node/licenses.md +75 -0
  40. package/package.json +12 -2
  41. package/src/control/common/api.ts +50 -0
  42. package/src/control/common/flowDecoders.ts +2 -0
  43. package/src/control/common/mcp.ts +4 -4
  44. package/src/control/common/requests.ts +12 -1
  45. package/src/flow/common/encoding.ts +1 -0
  46. package/src/flow/common/workflowBundle.ts +117 -0
  47. package/src/flow/common/workflowFile.ts +4 -0
  48. package/src/flow/common/workflowReferences.ts +20 -0
@@ -8617,6 +8617,31 @@ function isSchemaKeyword(value) {
8617
8617
  return schemaKeywords.has(value);
8618
8618
  }
8619
8619
  //#endregion
8620
+ //#region src/flow/common/workflowReferences.ts
8621
+ /** Workflow declarations share discovery across authoring, admission and resource indexing. */
8622
+ function nodeWorkflowReferences(node) {
8623
+ if (node.kind != "task") return [];
8624
+ if ("moduleId" in node.task) return (node.task.capabilities ?? []).flatMap((item, index) => item.kind == "workflow" ? [{
8625
+ target: item,
8626
+ path: `task/capabilities/${index}`,
8627
+ tool: item.name
8628
+ }] : []);
8629
+ if (node.task.executor.kind == "workflow") return [{
8630
+ target: {
8631
+ ...node.task.executor,
8632
+ inputs: node.task.inputs.filter((port) => "handle" in port),
8633
+ outputs: node.task.outputs.filter((port) => "handle" in port)
8634
+ },
8635
+ path: "task/executor"
8636
+ }];
8637
+ if (node.task.executor.kind == "agent") return node.task.executor.tools.flatMap((item, index) => item.kind == "workflow" ? [{
8638
+ target: item,
8639
+ path: `task/executor/tools/${index}`,
8640
+ tool: item.name
8641
+ }] : []);
8642
+ return [];
8643
+ }
8644
+ //#endregion
8620
8645
  //#region src/workflow/common/workflow.ts
8621
8646
  var portSchema = object({
8622
8647
  handle: string$1().min(1),
@@ -10547,7 +10572,7 @@ var entrySchema = discriminatedUnion("type", [
10547
10572
  /** Project only the selected published definition and its current bindings. */
10548
10573
  function liveEntries(content, current, bindings) {
10549
10574
  const byNode = new Map(bindings.filter((item) => item.flowId == current.flowId && item.currentPublicationId == current.publicationId && item.currentRevisionId == current.revisionId).map((item) => [item.triggerNodeId, item]));
10550
- return decodeEntries(Object.entries(content.document.graph.nodes).toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([nodeId, node]) => {
10575
+ return decodeEntries$1(Object.entries(content.document.graph.nodes).toSorted(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([nodeId, node]) => {
10551
10576
  if (node.kind != "start" && node.kind != "manual" && node.kind != "webhook" && node.kind != "cron" && node.kind != "error" && node.kind != "poll" && node.kind != "integration") return [];
10552
10577
  const config = triggerConfiguration(node);
10553
10578
  return [{
@@ -10595,12 +10620,12 @@ function decodeLiveDetails(value) {
10595
10620
  publication: current,
10596
10621
  status,
10597
10622
  enabled: source.enabled,
10598
- entries: decodeEntries(source.entries, current),
10623
+ entries: decodeEntries$1(source.entries, current),
10599
10624
  version: 1
10600
10625
  };
10601
10626
  }
10602
10627
  /** Both producers and consumers enforce the same published-entry invariants. */
10603
- function decodeEntries(values, current) {
10628
+ function decodeEntries$1(values, current) {
10604
10629
  const parsed = array(entrySchema).safeParse(values);
10605
10630
  if (!parsed.success) return invalidResponse();
10606
10631
  const ids = /* @__PURE__ */ new Set();
@@ -10670,17 +10695,134 @@ function conditionInputPorts(node) {
10670
10695
  nullable: operand.kind === "value" ? operand.value === null : true
10671
10696
  }]));
10672
10697
  }
10698
+ function clearConditionBinding(operand) {
10699
+ return operand.kind == "source" && operand.source.kind == "binding" ? { kind: "value" } : operand;
10700
+ }
10701
+ function clearConditionBindings(node) {
10702
+ return {
10703
+ ...node,
10704
+ cases: node.cases.map((item) => ({
10705
+ ...item,
10706
+ groups: item.groups.map((group) => ({
10707
+ ...group,
10708
+ expressions: group.expressions.map((expression) => ({
10709
+ ...expression,
10710
+ left: clearConditionBinding(expression.left),
10711
+ ...expression.right == null ? {} : { right: clearConditionBinding(expression.right) }
10712
+ }))
10713
+ }))
10714
+ }))
10715
+ };
10716
+ }
10673
10717
  //#endregion
10674
10718
  //#region src/flow/common/model.ts
10675
10719
  function portsByHandle(ports) {
10676
10720
  return Object.fromEntries(ports.flatMap((port) => "handle" in port ? [[port.handle, port]] : []));
10677
10721
  }
10678
10722
  //#endregion
10723
+ //#region src/trigger/common/config.ts
10724
+ /** Deployment-bound selections owned by the Trigger configuration contract. */
10725
+ function clearTriggerExternalConfig(key, config) {
10726
+ const fields = key == "jira.on_issue_event" || key == "intercom.on_conversation_event" || key == "sentry.on_issue_alert" || key == "zoom.on_recording_ready" || key == "fireflies.on_meeting_ready" ? ["sourceId", "eventTypes"] : key == "slackbot.on_event" ? [
10727
+ "sourceId",
10728
+ "eventTypes",
10729
+ "channelIds"
10730
+ ] : key == "feishu_app_bot.on_event" ? [
10731
+ "sourceId",
10732
+ "eventTypes",
10733
+ "resource",
10734
+ "chatIds"
10735
+ ] : key == "linear.on_issue_changed" ? ["teamId", "stateIds"] : [];
10736
+ return {
10737
+ ...config,
10738
+ ...Object.fromEntries(fields.map((field) => [field, { kind: "unset" }]))
10739
+ };
10740
+ }
10741
+ //#endregion
10742
+ //#region src/flow/common/connectionUsage.ts
10743
+ function clearNodeConnections(graph, connectionId) {
10744
+ return {
10745
+ ...graph,
10746
+ nodes: Object.fromEntries(Object.entries(graph.nodes).map(([id, node]) => {
10747
+ if ((node.kind == "poll" || node.kind == "integration") && (connectionId == null || node.connectionId == connectionId)) {
10748
+ const { connectionId: _, ...remaining } = node;
10749
+ return [id, remaining];
10750
+ }
10751
+ if (node.kind != "task") return [id, node];
10752
+ if ("executor" in node.task) {
10753
+ const executor = node.task.executor;
10754
+ const clear = (value) => {
10755
+ if (value.connectionId === "") return value;
10756
+ if (connectionId != null && value.connectionId != connectionId) return value;
10757
+ const { connectionId: _, ...rest } = value;
10758
+ return rest;
10759
+ };
10760
+ if (executor.kind == "connector") return [id, {
10761
+ ...node,
10762
+ task: {
10763
+ ...node.task,
10764
+ executor: clear(executor)
10765
+ }
10766
+ }];
10767
+ if (executor.kind == "agent") return [id, {
10768
+ ...node,
10769
+ task: {
10770
+ ...node.task,
10771
+ executor: {
10772
+ ...executor,
10773
+ tools: executor.tools.map(clear),
10774
+ ...executor.notification == null ? {} : { notification: clear(executor.notification) }
10775
+ }
10776
+ }
10777
+ }];
10778
+ return [id, node];
10779
+ }
10780
+ const capabilities = node.task.capabilities?.map((capability) => {
10781
+ if (capability.kind == "workflow" || capability.kind == "http") return capability;
10782
+ if (!("mode" in capability)) {
10783
+ if ("action" in capability) {
10784
+ const { connectionId: selected, ...rest } = capability;
10785
+ return {
10786
+ ...rest,
10787
+ connections: capability.connections.filter((item) => connectionId != null && item.connectionId != connectionId),
10788
+ ...selected != null && connectionId != null && selected != connectionId ? { connectionId: selected } : {}
10789
+ };
10790
+ }
10791
+ return {
10792
+ ...capability,
10793
+ ...connectionId == null && (capability.connectionHints?.length ?? 0) > 0 ? { actionHints: [.../* @__PURE__ */ new Set([...capability.actionHints ?? [], ...capability.connectionHints.map((hint) => hint.action)])] } : {},
10794
+ ...capability.connectionHints == null ? {} : { connectionHints: capability.connectionHints.filter((item) => connectionId != null && item.connectionId != connectionId) }
10795
+ };
10796
+ }
10797
+ if (capability.mode != "independent") return capability;
10798
+ return {
10799
+ ...capability,
10800
+ actions: capability.actions.map((action) => {
10801
+ if (action.connectionId === "") return action;
10802
+ if (connectionId != null && action.connectionId != connectionId) return action;
10803
+ const { connectionId: _, ...remaining } = action;
10804
+ return remaining;
10805
+ })
10806
+ };
10807
+ });
10808
+ return [id, capabilities == null ? node : {
10809
+ ...node,
10810
+ task: {
10811
+ ...node.task,
10812
+ capabilities
10813
+ }
10814
+ }];
10815
+ }))
10816
+ };
10817
+ }
10818
+ //#endregion
10679
10819
  //#region src/flow/common/workflowFile.ts
10680
10820
  var envelope = strictObject({
10681
10821
  kind: literal("open-flow-workflow"),
10682
10822
  version: literal(1),
10683
10823
  name: string$1().optional(),
10824
+ icon: string$1().optional(),
10825
+ description: string$1().optional(),
10684
10826
  content: unknown(),
10685
10827
  presentation: record$2(string$1(), json$3()).optional()
10686
10828
  });
@@ -10693,6 +10835,126 @@ function decodeWorkflowFile(value) {
10693
10835
  content: decodeRevisionContent(file.content)
10694
10836
  };
10695
10837
  }
10838
+ function cleanInput(input) {
10839
+ if (input.kind != "sources" || !input.sources.some((source) => source.kind == "binding")) return input;
10840
+ const sources = input.sources.filter((source) => source.kind != "binding");
10841
+ return sources.length == 0 ? { kind: "unset" } : {
10842
+ kind: "sources",
10843
+ sources
10844
+ };
10845
+ }
10846
+ /** Clear only owned structured references, never strings in code or business values. */
10847
+ function cleanWorkflowFile(value) {
10848
+ const file = decodeWorkflowFile(value);
10849
+ const original = file.content.document.graph;
10850
+ const graph = clearNodeConnections(original);
10851
+ const nodes = Object.fromEntries(Object.entries(graph.nodes).map(([id, source]) => {
10852
+ let node = source;
10853
+ if ("inputs" in node) node = {
10854
+ ...node,
10855
+ inputs: Object.fromEntries(Object.entries(node.inputs).map(([key, input]) => [key, cleanInput(input)]))
10856
+ };
10857
+ if (node.kind == "outputs") node = {
10858
+ ...node,
10859
+ outputMappings: Object.fromEntries(Object.entries(node.outputMappings).map(([key, input]) => [key, cleanInput(input)]))
10860
+ };
10861
+ if (node.kind == "condition") node = clearConditionBindings(node);
10862
+ if (node.kind == "error" && node.sourceFlowIds != null) node = {
10863
+ ...node,
10864
+ sourceFlowIds: []
10865
+ };
10866
+ if (node.kind == "poll" || node.kind == "integration") node = {
10867
+ ...node,
10868
+ config: clearTriggerExternalConfig(node.definition.key, node.config)
10869
+ };
10870
+ return [id, node];
10871
+ }));
10872
+ return {
10873
+ file: {
10874
+ ...file,
10875
+ content: {
10876
+ ...file.content,
10877
+ document: {
10878
+ ...file.content.document,
10879
+ bindings: {},
10880
+ graph: {
10881
+ ...graph,
10882
+ nodes
10883
+ }
10884
+ }
10885
+ }
10886
+ },
10887
+ summary: {
10888
+ changedNodeIds: Object.keys(nodes).filter((id) => !dequal(nodes[id], original.nodes[id])),
10889
+ removedBindings: Object.keys(file.content.document.bindings).length
10890
+ }
10891
+ };
10892
+ }
10893
+ //#endregion
10894
+ //#region src/flow/common/workflowBundle.ts
10895
+ var entries = array(strictObject({
10896
+ flowId: string$1().min(1),
10897
+ file: unknown()
10898
+ })).min(1);
10899
+ var bundleSchema = strictObject({
10900
+ kind: literal("open-flow-workflow-bundle"),
10901
+ version: literal(1),
10902
+ entryFlowId: string$1().min(1),
10903
+ workflows: entries
10904
+ });
10905
+ var previewSchema = strictObject({
10906
+ version: literal(1),
10907
+ entryFlowId: string$1().min(1),
10908
+ workflows: entries,
10909
+ unavailableFlowIds: array(string$1().min(1))
10910
+ });
10911
+ function decodeEntries(value, entryFlowId, unavailableFlowIds = []) {
10912
+ const workflows = value.map(({ flowId, file }) => ({
10913
+ flowId,
10914
+ file: decodeWorkflowFile(file)
10915
+ }));
10916
+ const ids = new Set(workflows.map(({ flowId }) => flowId));
10917
+ if (ids.size != workflows.length || !ids.has(entryFlowId)) throw new TypeError("Workflow identities must be unique and include the entry workflow.");
10918
+ const unavailable = new Set(unavailableFlowIds);
10919
+ if (unavailable.size != unavailableFlowIds.length || unavailableFlowIds.some((id) => ids.has(id))) throw new TypeError("Invalid unavailable workflow identities.");
10920
+ for (const { file } of workflows) for (const node of Object.values(file.content.document.graph.nodes)) for (const { target } of nodeWorkflowReferences(node)) if (target.flowId && !ids.has(target.flowId) && !unavailable.has(target.flowId)) throw new TypeError("A referenced workflow is missing from the bundle.");
10921
+ return workflows;
10922
+ }
10923
+ function decodeWorkflowBundle(value) {
10924
+ checkJsonDepth(value, -4);
10925
+ const bundle = bundleSchema.parse(value);
10926
+ return {
10927
+ ...bundle,
10928
+ workflows: decodeEntries(bundle.workflows, bundle.entryFlowId)
10929
+ };
10930
+ }
10931
+ function decodeWorkflowExportPreview(value) {
10932
+ checkJsonDepth(value, -4);
10933
+ const preview = previewSchema.parse(value);
10934
+ return {
10935
+ ...preview,
10936
+ workflows: decodeEntries(preview.workflows, preview.entryFlowId, preview.unavailableFlowIds)
10937
+ };
10938
+ }
10939
+ function cleanWorkflowBundle(value) {
10940
+ const bundle = decodeWorkflowBundle(value);
10941
+ return {
10942
+ ...bundle,
10943
+ workflows: bundle.workflows.map(({ flowId, file }) => ({
10944
+ flowId,
10945
+ file: cleanWorkflowFile(file).file
10946
+ }))
10947
+ };
10948
+ }
10949
+ function workflowBundleFromPreview(preview) {
10950
+ if (preview.unavailableFlowIds.length) throw new TypeError("Some referenced workflows are unavailable.");
10951
+ return cleanWorkflowBundle({
10952
+ kind: "open-flow-workflow-bundle",
10953
+ version: 1,
10954
+ entryFlowId: preview.entryFlowId,
10955
+ workflows: preview.workflows
10956
+ });
10957
+ }
10696
10958
  //#endregion
10697
10959
  //#region src/flow/common/encoding.ts
10698
10960
  var encoder$1 = new TextEncoder();
@@ -11996,6 +12258,8 @@ function flow(value) {
11996
12258
  resourceReferences: resourceReferences(source.resourceReferences),
11997
12259
  flowId: string(source.flowId),
11998
12260
  name: string(source.name),
12261
+ ...source.icon === void 0 ? {} : { icon: string(source.icon) },
12262
+ ...source.description === void 0 ? {} : { description: string(source.description) },
11999
12263
  status,
12000
12264
  updatedAt: string(source.updatedAt),
12001
12265
  version: 1
@@ -12482,6 +12746,18 @@ function decodeTagListItem(value) {
12482
12746
  }
12483
12747
  //#endregion
12484
12748
  //#region src/control/common/api.ts
12749
+ function decodeWorkflowBundleImportResult(value) {
12750
+ const source = record(value);
12751
+ if (source.version != 1 || !Array.isArray(source.flows) || source.flows.length == 0) return invalidResponse();
12752
+ const entry = flow(source.flow);
12753
+ const flows = source.flows.map(flow);
12754
+ if (new Set(flows.map((item) => item.flowId)).size != flows.length || !flows.some((item) => item.flowId == entry.flowId)) return invalidResponse();
12755
+ return {
12756
+ version: 1,
12757
+ flow: entry,
12758
+ flows
12759
+ };
12760
+ }
12485
12761
  function isRunTerminalEvent(event) {
12486
12762
  return event?.kind == "run.completed" || event?.kind == "run.failed" || event?.kind == "run.canceled" || event?.kind == "run.indeterminate";
12487
12763
  }
@@ -12799,6 +13075,32 @@ var ControlClient = class {
12799
13075
  return invalidResponse();
12800
13076
  }
12801
13077
  }
13078
+ async getFlowExportPreview(flowId) {
13079
+ const value = await this.request(`/v1/flows/${segment(flowId)}/export-preview`);
13080
+ try {
13081
+ return decodeWorkflowExportPreview(value);
13082
+ } catch {
13083
+ return invalidResponse();
13084
+ }
13085
+ }
13086
+ async exportFlowBundle(flowId) {
13087
+ const preview = await this.getFlowExportPreview(flowId);
13088
+ if (preview.unavailableFlowIds.length) throw new ApiError(400, "flow.invalid", "Some referenced workflows are unavailable.");
13089
+ return workflowBundleFromPreview(preview);
13090
+ }
13091
+ async importFlowBundle(file, options = {}) {
13092
+ return decodeWorkflowBundleImportResult(await this.request("/v1/flows/import-bundle", {
13093
+ method: "POST",
13094
+ headers: { "Idempotency-Key": options.idempotencyKey ?? `flow-${randomId()}` },
13095
+ body: JSON.stringify({
13096
+ file,
13097
+ name: options.name,
13098
+ teamId: options.teamId,
13099
+ tagIds: options.tagIds,
13100
+ version: 1
13101
+ })
13102
+ }));
13103
+ }
12802
13104
  async importFlow(file, options = {}) {
12803
13105
  return flow(await this.request("/v1/flows/import", {
12804
13106
  method: "POST",
@@ -13455,4 +13757,4 @@ var ControlClient = class {
13455
13757
  }
13456
13758
  };
13457
13759
  //#endregion
13458
- export { ApiError, ControlClient, actionSummary, controlErrorCode, controlErrorMetadata, connectorAccess as decodeConnectorAccess, connectorAccessSnapshot as decodeConnectorAccessSnapshot, decodeEventSource, decodeEventSources, decodeResultList, decodeResultRead, decodeRunEvent, decodeTriggerCatalog, flowInputDefinitions, flowInspection, inspectFlowDraft, isRunTerminalEvent, liveEntries, nodeDetails, normalizeFlowQuery, parseProviderAccessSource, parseResultQuery, providerAccessBindingId, providerIconAppearance, readResult, searchTriggerKeys };
13760
+ export { ApiError, ControlClient, actionSummary, controlErrorCode, controlErrorMetadata, connectorAccess as decodeConnectorAccess, connectorAccessSnapshot as decodeConnectorAccessSnapshot, decodeEventSource, decodeEventSources, decodeResultList, decodeResultRead, decodeRunEvent, decodeTriggerCatalog, decodeWorkflowBundleImportResult, flowInputDefinitions, flowInspection, inspectFlowDraft, isRunTerminalEvent, liveEntries, nodeDetails, normalizeFlowQuery, parseProviderAccessSource, parseResultQuery, providerAccessBindingId, providerIconAppearance, readResult, searchTriggerKeys };
@@ -224,12 +224,10 @@ export declare const controlRequests: {
224
224
  version: 1;
225
225
  };
226
226
  updateFlow: (value: unknown) => {
227
- name: string;
228
- tagIds?: string[] | undefined;
229
- version: 1;
230
- } | {
231
227
  name?: string | undefined;
232
- tagIds: string[];
228
+ icon?: string | null | undefined;
229
+ description?: string | null | undefined;
230
+ tagIds?: string[] | undefined;
233
231
  version: 1;
234
232
  };
235
233
  renameFlow: (value: unknown) => {
@@ -17948,15 +17948,13 @@ var schemas = {
17948
17948
  tagIds,
17949
17949
  version
17950
17950
  }),
17951
- updateFlow: union([strictObject({
17952
- name: flowName,
17953
- tagIds: tagIds.optional(),
17954
- version
17955
- }), strictObject({
17951
+ updateFlow: strictObject({
17956
17952
  name: flowName.optional(),
17957
- tagIds,
17953
+ icon: string().nullable().optional(),
17954
+ description: string().nullable().optional(),
17955
+ tagIds: tagIds.optional(),
17958
17956
  version
17959
- })]),
17957
+ }).refine((value) => value.name !== void 0 || value.icon !== void 0 || value.description !== void 0 || value.tagIds !== void 0, "Flow settings are empty."),
17960
17958
  renameFlow: strictObject({
17961
17959
  name: flowName,
17962
17960
  version
@@ -1,5 +1,6 @@
1
1
  import type { CodeModule, FlowDocument, Graph, Group, InputMapping, InputPort, JsonValue, ManagedTaskDefinition, Port, RevisionContent, TriggerNode } from '@oomol-lab/open-flow/flow-change';
2
2
  export * from './flow-workflowFile.js';
3
+ export * from './flow-workflowBundle.js';
3
4
  export { maxJsonDepth } from './flow-json.js';
4
5
  export { decodeFlowDocument, decodeRevisionContent, upgradeWorkflowInterfaceDocument } from './flow-change-schema.js';
5
6
  export { upgradeRevisionContent } from '@oomol-lab/open-flow/flow-change';
@@ -9856,6 +9856,8 @@ var envelope = strictObject({
9856
9856
  kind: literal("open-flow-workflow"),
9857
9857
  version: literal(1),
9858
9858
  name: string().optional(),
9859
+ icon: string().optional(),
9860
+ description: string().optional(),
9859
9861
  content: unknown(),
9860
9862
  presentation: record$1(string(), json$1()).optional()
9861
9863
  });
@@ -9936,6 +9938,170 @@ function workflowNameFromFile(name) {
9936
9938
  return name.replace(/(?:\.openflow)?\.json$/i, "");
9937
9939
  }
9938
9940
  //#endregion
9941
+ //#region src/flow/common/workflowReferences.ts
9942
+ /** Rewrite only declared workflow identities, preserving business values and saved ports. */
9943
+ function mapNodeWorkflowTargets(node, map) {
9944
+ if (node.kind != "task") return node;
9945
+ const task = node.task;
9946
+ if ("moduleId" in task) return task.capabilities?.some((item) => item.kind == "workflow") ? {
9947
+ ...node,
9948
+ task: {
9949
+ ...task,
9950
+ capabilities: task.capabilities.map((item) => item.kind == "workflow" ? Object.assign({}, item, map(item)) : item)
9951
+ }
9952
+ } : node;
9953
+ const executor = task.executor;
9954
+ if (executor.kind == "workflow") return {
9955
+ ...node,
9956
+ task: {
9957
+ ...task,
9958
+ executor: {
9959
+ ...executor,
9960
+ ...map(executor)
9961
+ }
9962
+ }
9963
+ };
9964
+ if (executor.kind == "agent") return {
9965
+ ...node,
9966
+ task: {
9967
+ ...task,
9968
+ executor: {
9969
+ ...executor,
9970
+ tools: executor.tools.map((tool) => tool.kind == "workflow" ? Object.assign({}, tool, map(tool)) : tool)
9971
+ }
9972
+ }
9973
+ };
9974
+ return node;
9975
+ }
9976
+ /** Workflow declarations share discovery across authoring, admission and resource indexing. */
9977
+ function nodeWorkflowReferences(node) {
9978
+ if (node.kind != "task") return [];
9979
+ if ("moduleId" in node.task) return (node.task.capabilities ?? []).flatMap((item, index) => item.kind == "workflow" ? [{
9980
+ target: item,
9981
+ path: `task/capabilities/${index}`,
9982
+ tool: item.name
9983
+ }] : []);
9984
+ if (node.task.executor.kind == "workflow") return [{
9985
+ target: {
9986
+ ...node.task.executor,
9987
+ inputs: node.task.inputs.filter((port) => "handle" in port),
9988
+ outputs: node.task.outputs.filter((port) => "handle" in port)
9989
+ },
9990
+ path: "task/executor"
9991
+ }];
9992
+ if (node.task.executor.kind == "agent") return node.task.executor.tools.flatMap((item, index) => item.kind == "workflow" ? [{
9993
+ target: item,
9994
+ path: `task/executor/tools/${index}`,
9995
+ tool: item.name
9996
+ }] : []);
9997
+ return [];
9998
+ }
9999
+ //#endregion
10000
+ //#region src/flow/common/workflowBundle.ts
10001
+ var entries$1 = array(strictObject({
10002
+ flowId: string().min(1),
10003
+ file: unknown()
10004
+ })).min(1);
10005
+ var bundleSchema = strictObject({
10006
+ kind: literal("open-flow-workflow-bundle"),
10007
+ version: literal(1),
10008
+ entryFlowId: string().min(1),
10009
+ workflows: entries$1
10010
+ });
10011
+ var previewSchema = strictObject({
10012
+ version: literal(1),
10013
+ entryFlowId: string().min(1),
10014
+ workflows: entries$1,
10015
+ unavailableFlowIds: array(string().min(1))
10016
+ });
10017
+ function decodeEntries(value, entryFlowId, unavailableFlowIds = []) {
10018
+ const workflows = value.map(({ flowId, file }) => ({
10019
+ flowId,
10020
+ file: decodeWorkflowFile(file)
10021
+ }));
10022
+ const ids = new Set(workflows.map(({ flowId }) => flowId));
10023
+ if (ids.size != workflows.length || !ids.has(entryFlowId)) throw new TypeError("Workflow identities must be unique and include the entry workflow.");
10024
+ const unavailable = new Set(unavailableFlowIds);
10025
+ if (unavailable.size != unavailableFlowIds.length || unavailableFlowIds.some((id) => ids.has(id))) throw new TypeError("Invalid unavailable workflow identities.");
10026
+ for (const { file } of workflows) for (const node of Object.values(file.content.document.graph.nodes)) for (const { target } of nodeWorkflowReferences(node)) if (target.flowId && !ids.has(target.flowId) && !unavailable.has(target.flowId)) throw new TypeError("A referenced workflow is missing from the bundle.");
10027
+ return workflows;
10028
+ }
10029
+ function decodeWorkflowBundle(value) {
10030
+ checkJsonDepth(value, -4);
10031
+ const bundle = bundleSchema.parse(value);
10032
+ return {
10033
+ ...bundle,
10034
+ workflows: decodeEntries(bundle.workflows, bundle.entryFlowId)
10035
+ };
10036
+ }
10037
+ function decodeWorkflowExportPreview(value) {
10038
+ checkJsonDepth(value, -4);
10039
+ const preview = previewSchema.parse(value);
10040
+ return {
10041
+ ...preview,
10042
+ workflows: decodeEntries(preview.workflows, preview.entryFlowId, preview.unavailableFlowIds)
10043
+ };
10044
+ }
10045
+ function cleanWorkflowBundle(value) {
10046
+ const bundle = decodeWorkflowBundle(value);
10047
+ return {
10048
+ ...bundle,
10049
+ workflows: bundle.workflows.map(({ flowId, file }) => ({
10050
+ flowId,
10051
+ file: cleanWorkflowFile(file).file
10052
+ }))
10053
+ };
10054
+ }
10055
+ function workflowBundleFromPreview(preview) {
10056
+ if (preview.unavailableFlowIds.length) throw new TypeError("Some referenced workflows are unavailable.");
10057
+ return cleanWorkflowBundle({
10058
+ kind: "open-flow-workflow-bundle",
10059
+ version: 1,
10060
+ entryFlowId: preview.entryFlowId,
10061
+ workflows: preview.workflows
10062
+ });
10063
+ }
10064
+ function encodeWorkflowBundle(bundle) {
10065
+ return `${JSON.stringify(cleanWorkflowBundle(bundle), null, 2)}\n`;
10066
+ }
10067
+ function parseWorkflowImportFile(text) {
10068
+ const value = JSON.parse(text);
10069
+ return value != null && typeof value == "object" && "kind" in value && value.kind == "open-flow-workflow-bundle" ? decodeWorkflowBundle(value) : decodeWorkflowFile(value);
10070
+ }
10071
+ /** A bundle creates new drafts, so old published interface provenance must be refreshed. */
10072
+ function remapWorkflowBundle(bundle, flowIds) {
10073
+ const cleaned = cleanWorkflowBundle(bundle);
10074
+ function identity(flowId) {
10075
+ const mapped = flowIds.get(flowId);
10076
+ if (mapped == null || mapped.length == 0) throw new TypeError("Missing imported workflow identity.");
10077
+ return mapped;
10078
+ }
10079
+ const workflows = cleaned.workflows.map(({ flowId, file }) => ({
10080
+ flowId: identity(flowId),
10081
+ file: {
10082
+ ...file,
10083
+ content: {
10084
+ ...file.content,
10085
+ document: {
10086
+ ...file.content.document,
10087
+ graph: {
10088
+ ...file.content.document.graph,
10089
+ nodes: Object.fromEntries(Object.entries(file.content.document.graph.nodes).map(([id, node]) => [id, mapNodeWorkflowTargets(node, (target) => ({
10090
+ flowId: target.flowId ? identity(target.flowId) : "",
10091
+ revisionId: ""
10092
+ }))]))
10093
+ }
10094
+ }
10095
+ }
10096
+ }
10097
+ }));
10098
+ return decodeWorkflowBundle({
10099
+ ...cleaned,
10100
+ entryFlowId: identity(cleaned.entryFlowId),
10101
+ workflows
10102
+ });
10103
+ }
10104
+ //#endregion
9939
10105
  //#region src/flow/common/encoding.ts
9940
10106
  var encoder = new TextEncoder();
9941
10107
  function canonicalText(value) {
@@ -10315,4 +10481,4 @@ function repairRevision(bytes) {
10315
10481
  return repairRevisionEnvelope(revisionValue(bytes));
10316
10482
  }
10317
10483
  //#endregion
10318
- export { canonicalDocument, canonicalGraph, canonicalInputs, canonicalJsonBytes, canonicalModule, canonicalPorts, canonicalRevisionGraph, canonicalTask, cleanWorkflowFile, decodeFlowDocument, decodeRevision, decodeRevisionContent, decodeWorkflowFile, digestBytes, encodeRevision, encodeWorkflowFile, maxJsonDepth, parseWorkflowFile, repairRevision, revisionRepairKind, triggerRuntimeJson, upgradeRevisionContent, upgradeWorkflowInterfaceDocument, workflowFileName, workflowNameFromFile };
10484
+ export { canonicalDocument, canonicalGraph, canonicalInputs, canonicalJsonBytes, canonicalModule, canonicalPorts, canonicalRevisionGraph, canonicalTask, cleanWorkflowBundle, cleanWorkflowFile, decodeFlowDocument, decodeRevision, decodeRevisionContent, decodeWorkflowBundle, decodeWorkflowExportPreview, decodeWorkflowFile, digestBytes, encodeRevision, encodeWorkflowBundle, encodeWorkflowFile, maxJsonDepth, parseWorkflowFile, parseWorkflowImportFile, remapWorkflowBundle, repairRevision, revisionRepairKind, triggerRuntimeJson, upgradeRevisionContent, upgradeWorkflowInterfaceDocument, workflowBundleFromPreview, workflowFileName, workflowNameFromFile };
@@ -12336,10 +12336,30 @@ strictObject({
12336
12336
  kind: literal("open-flow-workflow"),
12337
12337
  version: literal(1),
12338
12338
  name: string().optional(),
12339
+ icon: string().optional(),
12340
+ description: string().optional(),
12339
12341
  content: unknown(),
12340
12342
  presentation: record$1(string(), json$1()).optional()
12341
12343
  });
12342
12344
  //#endregion
12345
+ //#region src/flow/common/workflowBundle.ts
12346
+ var entries$2 = array(strictObject({
12347
+ flowId: string().min(1),
12348
+ file: unknown()
12349
+ })).min(1);
12350
+ strictObject({
12351
+ kind: literal("open-flow-workflow-bundle"),
12352
+ version: literal(1),
12353
+ entryFlowId: string().min(1),
12354
+ workflows: entries$2
12355
+ });
12356
+ strictObject({
12357
+ version: literal(1),
12358
+ entryFlowId: string().min(1),
12359
+ workflows: entries$2,
12360
+ unavailableFlowIds: array(string().min(1))
12361
+ });
12362
+ //#endregion
12343
12363
  //#region src/flow/common/encoding.ts
12344
12364
  var encoder = new TextEncoder();
12345
12365
  function canonicalText(value) {
@@ -0,0 +1,27 @@
1
+ import type { WorkflowFile } from './flow-workflowFile.js';
2
+ export interface WorkflowBundleEntry {
3
+ readonly flowId: string;
4
+ readonly file: WorkflowFile;
5
+ }
6
+ export interface WorkflowBundle {
7
+ readonly kind: 'open-flow-workflow-bundle';
8
+ readonly version: 1;
9
+ readonly entryFlowId: string;
10
+ readonly workflows: readonly WorkflowBundleEntry[];
11
+ }
12
+ /** A saved export snapshot; unavailable targets carry no account metadata. */
13
+ export interface WorkflowExportPreview {
14
+ readonly version: 1;
15
+ readonly entryFlowId: string;
16
+ readonly workflows: readonly WorkflowBundleEntry[];
17
+ readonly unavailableFlowIds: readonly string[];
18
+ }
19
+ export declare function decodeWorkflowBundle(value: unknown): WorkflowBundle;
20
+ export declare function decodeWorkflowExportPreview(value: unknown): WorkflowExportPreview;
21
+ export declare function cleanWorkflowBundle(value: unknown): WorkflowBundle;
22
+ export declare function workflowBundleFromPreview(preview: WorkflowExportPreview): WorkflowBundle;
23
+ export declare function encodeWorkflowBundle(bundle: WorkflowBundle): string;
24
+ export type WorkflowImportFile = WorkflowFile | WorkflowBundle;
25
+ export declare function parseWorkflowImportFile(text: string): WorkflowImportFile;
26
+ /** A bundle creates new drafts, so old published interface provenance must be refreshed. */
27
+ export declare function remapWorkflowBundle(bundle: WorkflowBundle, flowIds: ReadonlyMap<string, string>): WorkflowBundle;
@@ -3,6 +3,8 @@ export interface WorkflowFile {
3
3
  readonly kind: 'open-flow-workflow';
4
4
  readonly version: 1;
5
5
  readonly name?: string;
6
+ readonly icon?: string;
7
+ readonly description?: string;
6
8
  readonly content: RevisionContent;
7
9
  readonly presentation?: Readonly<Record<string, JsonValue>>;
8
10
  }