@oomol-lab/open-flow 0.1.0-beta.62 → 0.1.0-beta.64

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 (55) hide show
  1. package/dist/browser/{createResourceDialog-DZgJsr3e.js → createResourceDialog-BAJXbzNH.js} +20 -7
  2. package/dist/browser/flow-authoring.js +8 -7
  3. package/dist/browser/flow-change-schema.d.ts +1 -0
  4. package/dist/browser/flow-change.d.ts +1 -0
  5. package/dist/browser/flow-change.js +8 -7
  6. package/dist/browser/flow-notifications.d.ts +6 -0
  7. package/dist/browser/{flowChanges-CzYIVevI.js → flowChanges-BAMkSaKF.js} +1771 -1331
  8. package/dist/browser/{flowRunInputEditorStore-Dsxyfwae.js → flowRunInputEditorStore-B2Rw4VsT.js} +1 -1
  9. package/dist/browser/{flowWorkspace-BgZaFKZm.js → flowWorkspace-DxLc1Ybv.js} +429 -205
  10. package/dist/browser/{icons-CajfDpy_.js → icons-Db2Jb2iX.js} +30 -10
  11. package/dist/browser/preview.css +40 -52
  12. package/dist/browser/preview.js +7752 -7452
  13. package/dist/browser/theme.css +4 -0
  14. package/dist/browser/{typeScriptSession-fXPMFGjW.js → typeScriptSession-D0bjYDeg.js} +1 -1
  15. package/dist/browser/ui.css +39 -51
  16. package/dist/browser/ui.js +5 -5
  17. package/dist/browser/{value-DL_poNiC.js → value-BADHeZ9I.js} +12 -1
  18. package/dist/browser/workbench-contract.d.ts +10 -0
  19. package/dist/browser/workbench.css +43 -59
  20. package/dist/browser/workbench.d.ts +5 -3
  21. package/dist/browser/workbench.js +255 -216
  22. package/dist/browser/{workbenchSelect-H9wzEFkH.js → workbenchSelect-C-jf6NIM.js} +1 -1
  23. package/dist/command/index.js +49 -48
  24. package/dist/common/authoringSchema.d.ts +1 -0
  25. package/dist/common/canvas-commands.d.ts +19 -0
  26. package/dist/common/connector-action.d.ts +1 -0
  27. package/dist/common/connector-action.js +15 -12
  28. package/dist/common/control-api-conformance.js +127 -8
  29. package/dist/common/control-api.d.ts +16 -0
  30. package/dist/common/control-api.js +67 -8
  31. package/dist/common/control-requests.d.ts +23 -0
  32. package/dist/common/control-requests.js +64 -19
  33. package/dist/common/decision.js +8 -7
  34. package/dist/common/flow-connectionUsage.d.ts +1 -0
  35. package/dist/common/flow-encoding.d.ts +1 -0
  36. package/dist/common/flow-encoding.js +202 -10
  37. package/dist/common/flow-notifications.d.ts +6 -0
  38. package/dist/common/flow-semantics.js +64 -46
  39. package/dist/common/flow-workflowFile.d.ts +23 -0
  40. package/dist/common/integration-trigger.js +8 -7
  41. package/dist/common/localization.d.ts +1 -0
  42. package/dist/common/localization.js +12 -1
  43. package/dist/common/mcp.d.ts +103 -0
  44. package/dist/common/mcp.js +135 -11
  45. package/dist/common/openapi.js +8 -7
  46. package/dist/common/poll-trigger.js +8 -7
  47. package/dist/common/runtime-contract.js +1 -1
  48. package/dist/common/scheduler.d.ts +22 -2
  49. package/dist/common/scheduler.js +354 -83
  50. package/dist/common/trigger-config.d.ts +2 -0
  51. package/dist/common/webhook-trigger.js +8 -7
  52. package/dist/index.d.ts +2 -2
  53. package/package.json +1 -1
  54. package/skills/open-flow/SKILL.md +1 -1
  55. package/skills/open-flow/references/flow-authoring.md +49 -0
@@ -1,9 +1,9 @@
1
- import { $ as RunStore, $n as connection, $t as DropdownMenuContent, At as followWorkbenchLink, B as CacheResource, Bn as errorNotice, Cn as Badge, Ct as setCodeActions, Dn as createI18n, Dt as updateTriggerConfig, Et as setTriggerConnection, F as feishuResourceKind, Gn as decodeTriggerCatalog, Gt as Empty, H as resourceData, Hn as randomId, Ht as InputGroup, I as supportsFeishuChatFilter, It as PopoverTitle, J as providerIcon, Jn as sampleErrorOutputs, Jt as EmptyHeader, Kt as EmptyContent, L as combineSources, M as addNodeIntent, Mt as PopoverContent, N as deriveAddNodeOptions, Ot as updateTriggerSchedule, St as resetInputValues, Tt as setInputSources, U as resourceValue, Un as allConnectorConnectionsQuery, Ut as InputGroupAddon, Vn as WorkbenchClient, W as designerGraph, Wt as InputGroupInput, Xn as isJsonObject, Xt as EmptyTitle, Yn as triggerOutputDefinitions, Yt as EmptyMedia, Zt as DropdownMenu, _ as updateValue, _t as setComment, a as nameCreatedNodes, an as DropdownMenuSeparator, at as deriveInspectorDiagnostics, bt as setNodePositions, c as updateCondition, d as updateNodeName, dr as invalidResponse, dt as connectionCatalog, er as connectorActionMetadata, f as updateNodeSettings, fr as jsonValue, ft as canvasPresentationChange, g as updateTaskPorts, gr as controlErrorCode, gt as restoreCanvasPresentation, h as updateTaskAdditionalInputs, hr as ApiError, ht as removeComments, i as deleteSelection, it as Latest, jt as Popover, kt as IdTooltip, l as updateNodeDescription, ln as DropdownMenuTrigger, m as updateTask, mr as string, mt as flowPresentation, nt as revisionView, o as setInputValue, ot as diagnosticItems, p as updateResolution, pr as record, pt as commentIds, qt as EmptyDescription, r as applyFlowChanges, rt as flowResourceReferences, s as setInputVariable, t as addNode, tn as DropdownMenuItem, tr as providerIconAppearance, u as updateNodeIcon, ur as inputValue, ut as actionWithConnections, v as updateWebhook, vt as setFlowViewport, wn as EventSourcesPage, wt as setConnectorConnection, xt as repairNodeNames, yr as IconThemeContext, yt as setNodeContentHidden, z as scopedValue } from "./flowChanges-CzYIVevI.js";
1
+ import { $n as isJsonObject, $t as EmptyTitle, An as createI18n, At as updateTriggerSchedule, B as CacheResource, Cr as controlErrorCode, Ct as repairNodeNames, Dn as EventSourcesPage, Dt as setInputSources, En as Badge, Er as IconThemeContext, Et as setConnectorConnection, F as feishuResourceKind, Ft as PopoverContent, G as designerGraph, Gn as randomId, Gt as InputGroup, H as resourceData, I as supportsFeishuChatFilter, Jn as decodeTriggerCatalog, Jt as Empty, Kn as allConnectorConnectionsQuery, Kt as InputGroupAddon, L as combineSources, M as addNodeIntent, Mt as IdTooltip, N as deriveAddNodeOptions, Nt as followWorkbenchLink, Ot as setTriggerConnection, Pt as Popover, Qn as triggerOutputDefinitions, Qt as EmptyMedia, Sr as ApiError, St as setNodePositions, Tt as setCodeActions, U as resourceValue, Un as errorNotice, W as connectorActionPorts, Wn as WorkbenchClient, Xt as EmptyDescription, Y as providerIcon, Yt as EmptyContent, Zn as sampleErrorOutputs, Zt as EmptyHeader, _ as updateValue, _r as inputValue, _t as removeComments, a as nameCreatedNodes, ar as workflowNameFromFile, at as flowResourceReferences, br as record, bt as setFlowViewport, c as updateCondition, cn as DropdownMenuSeparator, cr as providerIconAppearance, ct as diagnosticItems, d as updateNodeName, en as DropdownMenu, er as connection, f as updateNodeSettings, fn as DropdownMenuTrigger, ft as actionWithConnections, g as updateTaskPorts, gt as flowPresentation, h as updateTaskAdditionalInputs, ht as commentIds, i as deleteSelection, in as DropdownMenuItem, ir as workflowFileName, it as revisionView, jt as WorkflowExportMenuItem, kt as updateTriggerConfig, l as updateNodeDescription, m as updateTask, mt as canvasPresentationChange, nn as DropdownMenuContent, nr as encodeWorkflowFile, o as setInputValue, ot as Latest, p as updateResolution, pt as connectionCatalog, qt as InputGroupInput, r as applyFlowChanges, rr as parseWorkflowFile, s as setInputVariable, st as deriveInspectorDiagnostics, t as addNode, tr as connectorActionMetadata, tt as RunStore, u as updateNodeIcon, v as updateWebhook, vr as invalidResponse, vt as restoreCanvasPresentation, wt as resetInputValues, xr as string, xt as setNodeContentHidden, yr as jsonValue, yt as setComment, z as scopedValue, zt as PopoverTitle } from "./flowChanges-BAMkSaKF.js";
2
2
  import { a as derive, i as compute, l as val } from "./dist-bO8nvHFH.js";
3
- import { a as toPlainObject, c as useLang, l as useTranslate, o as I18nProvider, u as useVal } from "./dist-BFmpkX4P.js";
4
- import { B as applyFlowChanges$1, E as FieldLabel, K as resourceNameIssue, S as Field, T as FieldGroup, X as nodeInputMappings, Y as mapConditionSources, a as DialogDescription, c as DialogTitle, d as TooltipContent, i as DialogContent, n as Dialog, o as DialogFooter, p as TooltipTrigger, r as DialogClose, rt as dequal$1, s as DialogHeader, sr as Input, t as Icon, u as Tooltip, w as FieldError, x as Spinner, xi as cn, yi as Button, z as FlowChangeError } from "./icons-CajfDpy_.js";
5
- import { c as isUiLanguage, d as uiLanguages, l as resolveUiLanguage, u as uiLanguageNames } from "./value-DL_poNiC.js";
6
- import { Suspense, lazy, useEffect, useRef, useState } from "react";
3
+ import { c as useLang, l as useTranslate, o as I18nProvider, u as useVal } from "./dist-BFmpkX4P.js";
4
+ import { B as FlowChangeError, Ci as cn, E as FieldLabel, Q as nodeInputMappings, S as Field, T as FieldGroup, V as applyFlowChanges$1, Z as mapConditionSources, a as DialogDescription, at as dequal$1, c as DialogTitle, d as TooltipContent, i as DialogContent, lr as Input, n as Dialog, o as DialogFooter, p as TooltipTrigger, q as resourceNameIssue, r as DialogClose, s as DialogHeader, t as Icon, u as Tooltip, w as FieldError, x as Spinner, xi as Button } from "./icons-Db2Jb2iX.js";
5
+ import { c as importedWorkflowName, d as uiLanguageNames, f as uiLanguages, l as isUiLanguage, u as resolveUiLanguage } from "./value-BADHeZ9I.js";
6
+ import { Suspense, forwardRef, lazy, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
7
7
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
  import * as Cause from "effect/Cause";
9
9
  import * as Effect from "effect/Effect";
@@ -95,11 +95,17 @@ var NavigationStore = class {
95
95
  this.#syncing = false;
96
96
  this.#write(view, false, { runSource });
97
97
  }
98
+ async importFlow(file, name, key, importer) {
99
+ return await this.#openNewFlow(() => this.#store.importFlow(file, name, key, importer));
100
+ }
98
101
  async createFlow(name, create) {
102
+ return await this.#openNewFlow(() => this.#store.createFlow(name, create));
103
+ }
104
+ async #openNewFlow(create) {
99
105
  const change = ++this.#change;
100
106
  this.#syncing = true;
101
107
  try {
102
- const created = await this.#store.createFlow(name, create);
108
+ const created = await create();
103
109
  if (created && change == this.#change) this.#write("design", false);
104
110
  return created;
105
111
  } finally {
@@ -184,10 +190,9 @@ function Skeleton({ className, ...props }) {
184
190
  }
185
191
  //#endregion
186
192
  //#region src/workbench/browser/runtime/shell/resourceBrowser.tsx
187
- var CreateResourceDialog = lazy(() => import("./createResourceDialog-DZgJsr3e.js"));
193
+ var CreateResourceDialog = lazy(() => import("./createResourceDialog-BAJXbzNH.js"));
188
194
  var flowIdTooltipAlignOffset = 44;
189
195
  var renamePopoverAlignOffset = -12;
190
- var renamePopoverVerticalShift = 8;
191
196
  function compactFlowId(flowId) {
192
197
  return flowId.length <= 20 ? flowId : `${flowId.slice(0, 10)}…${flowId.slice(-6)}`;
193
198
  }
@@ -364,54 +369,56 @@ function FlowItem({ showTeam, badge, busy, flow, href, onSelect, store }) {
364
369
  finalFocus: actionsButton,
365
370
  positionMethod: "fixed",
366
371
  side: "top",
367
- sideOffset: ({ anchor, positioner }) => -(anchor.height + positioner.height) / 2 - renamePopoverVerticalShift,
372
+ sideOffset: ({ anchor, positioner }) => -(anchor.height + positioner.height) / 2,
368
373
  children: [/* @__PURE__ */ jsx(PopoverTitle, {
369
374
  className: "sr-only",
370
375
  children: t("sidebar.renameFlow", { name: flow.name })
371
- }), /* @__PURE__ */ jsxs("form", {
372
- className: "resource-rename-form",
376
+ }), /* @__PURE__ */ jsx("form", {
373
377
  onSubmit: (event) => void rename(event),
374
- children: [
375
- /* @__PURE__ */ jsxs(Field, {
376
- className: "min-w-0 flex-1",
377
- "data-invalid": name.length > 0 && issue != null,
378
- children: [
379
- /* @__PURE__ */ jsx(FieldLabel, {
380
- className: "sr-only",
381
- htmlFor: `rename-flow-${flow.flowId}`,
382
- children: t("resource.flowName")
383
- }),
384
- /* @__PURE__ */ jsx(Input, {
385
- autoComplete: "off",
386
- "aria-invalid": name.length > 0 && issue != null,
387
- "aria-describedby": name.length > 0 && issue != null ? `rename-error-${flow.flowId}` : void 0,
388
- id: `rename-flow-${flow.flowId}`,
389
- name: "flow-name",
390
- onChange: (event) => setName(event.target.value),
391
- ref: nameInput,
392
- required: true,
393
- value: name
394
- }),
395
- name.length > 0 && issue != null && /* @__PURE__ */ jsx(FieldError, {
396
- id: `rename-error-${flow.flowId}`,
397
- children: t(`resource.nameIssue.${issue}`, { max: 80 })
398
- })
399
- ]
400
- }),
401
- /* @__PURE__ */ jsx(Button, {
402
- onClick: () => setMode("idle"),
403
- size: "sm",
404
- type: "button",
405
- variant: "ghost",
406
- children: t("common.cancel")
407
- }),
408
- /* @__PURE__ */ jsx(Button, {
409
- disabled: busy != null || issue != null,
410
- size: "sm",
411
- type: "submit",
412
- children: t("common.save")
413
- })
414
- ]
378
+ children: /* @__PURE__ */ jsxs(Field, {
379
+ "data-invalid": name.length > 0 && issue != null,
380
+ children: [
381
+ /* @__PURE__ */ jsx(FieldLabel, {
382
+ className: "sr-only",
383
+ htmlFor: `rename-flow-${flow.flowId}`,
384
+ children: t("resource.flowName")
385
+ }),
386
+ /* @__PURE__ */ jsxs("div", {
387
+ className: "resource-rename-form",
388
+ children: [
389
+ /* @__PURE__ */ jsx(Input, {
390
+ controlSize: "field",
391
+ autoComplete: "off",
392
+ "aria-invalid": name.length > 0 && issue != null,
393
+ "aria-describedby": name.length > 0 && issue != null ? `rename-error-${flow.flowId}` : void 0,
394
+ id: `rename-flow-${flow.flowId}`,
395
+ name: "flow-name",
396
+ onChange: (event) => setName(event.target.value),
397
+ ref: nameInput,
398
+ required: true,
399
+ value: name
400
+ }),
401
+ /* @__PURE__ */ jsx(Button, {
402
+ onClick: () => setMode("idle"),
403
+ size: "sm",
404
+ type: "button",
405
+ variant: "ghost",
406
+ children: t("common.cancel")
407
+ }),
408
+ /* @__PURE__ */ jsx(Button, {
409
+ disabled: busy != null || issue != null,
410
+ size: "sm",
411
+ type: "submit",
412
+ children: t("common.save")
413
+ })
414
+ ]
415
+ }),
416
+ name.length > 0 && issue != null && /* @__PURE__ */ jsx(FieldError, {
417
+ id: `rename-error-${flow.flowId}`,
418
+ children: t(`resource.nameIssue.${issue}`, { max: 80 })
419
+ })
420
+ ]
421
+ })
415
422
  })]
416
423
  })
417
424
  })]
@@ -531,6 +538,10 @@ function FlowItem({ showTeam, badge, busy, flow, href, onSelect, store }) {
531
538
  }), t(flow.live.enabled ? "resource.stop" : "resource.start")]
532
539
  }),
533
540
  /* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "mx-2 bg-border/50" }),
541
+ /* @__PURE__ */ jsx(WorkflowExportMenuItem, {
542
+ disabled: flow.status != "active" || busy != null,
543
+ onClick: () => void store.workspace.exportFlow(flow.flowId)
544
+ }),
534
545
  /* @__PURE__ */ jsxs(DropdownMenuItem, {
535
546
  disabled: flow.status != "active",
536
547
  onClick: () => onSelect(flow, "publications"),
@@ -622,7 +633,7 @@ function FlowSkeleton({ showTeam }) {
622
633
  })]
623
634
  });
624
635
  }
625
- function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBadges, hrefForFlow, initializing = false, onCreateFlow, onSelectFlow, store }) {
636
+ var FlowBrowser = forwardRef(function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBadges, hrefForFlow, initializing = false, onCreateFlow, onImportFlow, onSelectFlow, store, language }, ref) {
626
637
  const t = useTranslate();
627
638
  const busy = useVal(store.workspace.$.busy);
628
639
  const loadFailed = useVal(store.workspace.$.flowLoadFailed);
@@ -634,17 +645,54 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
634
645
  const refreshing = useVal(store.workspace.$.flowRefreshing);
635
646
  const flows = useVal(store.workspace.$.flows);
636
647
  const total = useVal(store.workspace.$.flowTotal);
637
- const [creating, setCreating] = useState(false);
648
+ const [creation$] = useState(() => val(void 0));
649
+ const creation = useVal(creation$);
650
+ const [importError, setImportError] = useState(false);
651
+ const fileInput = useRef(null);
638
652
  const [filter, setFilter] = useState("");
639
653
  const [name, setName] = useState("");
640
654
  const showTeam = flows.some((flow) => (flowBadges?.[flow.flowId]?.trim().length ?? 0) > 0);
641
655
  const normalized = filter.trim().toLocaleLowerCase();
642
656
  const visible = flows.filter((flow) => flow.name.toLocaleLowerCase().includes(normalized));
657
+ const command = useRef();
658
+ useImperativeHandle(ref, () => ({ openImport: (input) => command.current?.(input) ?? "unavailable" }), []);
659
+ useLayoutEffect(() => {
660
+ command.current = openImport;
661
+ return () => {
662
+ command.current = void 0;
663
+ };
664
+ });
665
+ function openImport(input) {
666
+ if (initializing || store.workspace.$.busy.value != null || creation$.value != null || onImportFlow == null) return "unavailable";
667
+ setImportError(false);
668
+ let file;
669
+ try {
670
+ file = parseWorkflowFile(input.text);
671
+ } catch {
672
+ setImportError(true);
673
+ return "invalid-file";
674
+ }
675
+ setName(file.name ?? (input.fileName == null ? importedWorkflowName(language) : workflowNameFromFile(input.fileName)));
676
+ creation$.set({
677
+ kind: "import",
678
+ file,
679
+ key: randomId(),
680
+ ...input.sourceUrl == null ? {} : { sourceUrl: input.sourceUrl }
681
+ });
682
+ return "opened";
683
+ }
684
+ function openCreate() {
685
+ if (creation$.value != null) return;
686
+ setName("");
687
+ creation$.set({ kind: "create" });
688
+ }
643
689
  async function create(event) {
644
690
  event.preventDefault();
691
+ const request = creation$.value;
645
692
  const nextName = name.trim();
646
- if (resourceNameIssue(nextName) != null || !await onCreateFlow(nextName)) return;
647
- setCreating(false);
693
+ if (request == null || resourceNameIssue(nextName) != null) return;
694
+ if (!(request.kind == "create" ? await onCreateFlow(nextName) : await onImportFlow?.(request.file, nextName, request.key)) || command.current == null || creation$.value !== request) return;
695
+ creation$.set(void 0);
648
696
  setName("");
649
697
  }
650
698
  return /* @__PURE__ */ jsx("main", {
@@ -675,30 +723,61 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
675
723
  }), !loading && /* @__PURE__ */ jsx("span", { children: t("resource.flowCount", { count: total ?? flows.length }) })]
676
724
  }), /* @__PURE__ */ jsxs("div", {
677
725
  className: "resource-list-actions",
678
- children: [/* @__PURE__ */ jsxs(InputGroup, {
679
- className: "w-full sm:w-56",
680
- children: [/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, {
681
- name: "search",
682
- size: 17
683
- }) }), /* @__PURE__ */ jsx(InputGroupInput, {
684
- autoComplete: "off",
685
- "aria-label": t("resource.searchByName"),
686
- name: "flow-search",
687
- onChange: (event) => setFilter(event.target.value),
688
- placeholder: t("resource.searchByName"),
689
- value: filter
690
- })]
691
- }), /* @__PURE__ */ jsxs(Button, {
692
- className: "pr-3",
693
- disabled: initializing || busy != null,
694
- onClick: () => setCreating(true),
695
- children: [/* @__PURE__ */ jsx(Icon, {
696
- "data-icon": "inline-start",
697
- name: "plus"
698
- }), t("resource.newFlow")]
699
- })]
726
+ children: [
727
+ /* @__PURE__ */ jsxs(InputGroup, {
728
+ className: "w-full sm:w-56",
729
+ children: [/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Icon, {
730
+ name: "search",
731
+ size: 17
732
+ }) }), /* @__PURE__ */ jsx(InputGroupInput, {
733
+ autoComplete: "off",
734
+ "aria-label": t("resource.searchByName"),
735
+ name: "flow-search",
736
+ onChange: (event) => setFilter(event.target.value),
737
+ placeholder: t("resource.searchByName"),
738
+ value: filter
739
+ })]
740
+ }),
741
+ onImportFlow != null && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Input, {
742
+ ref: fileInput,
743
+ className: "hidden",
744
+ type: "file",
745
+ accept: ".openflow.json,.json,application/json",
746
+ "aria-label": t("resource.importFlow"),
747
+ onChange: (event) => {
748
+ const file = event.target.files?.[0];
749
+ event.target.value = "";
750
+ if (file == null) return;
751
+ setImportError(false);
752
+ file.text().then((text) => {
753
+ command.current?.({
754
+ text,
755
+ fileName: file.name
756
+ });
757
+ }).catch(() => setImportError(true));
758
+ }
759
+ }), /* @__PURE__ */ jsx(Button, {
760
+ variant: "outline",
761
+ disabled: initializing || busy != null || createFlowDisabled,
762
+ onClick: () => fileInput.current?.click(),
763
+ children: t("resource.importFlow")
764
+ })] }),
765
+ /* @__PURE__ */ jsxs(Button, {
766
+ className: "pr-3",
767
+ disabled: initializing || busy != null,
768
+ onClick: openCreate,
769
+ children: [/* @__PURE__ */ jsx(Icon, {
770
+ "data-icon": "inline-start",
771
+ name: "plus"
772
+ }), t("resource.newFlow")]
773
+ })
774
+ ]
700
775
  })]
701
776
  }),
777
+ importError && /* @__PURE__ */ jsx("p", {
778
+ role: "alert",
779
+ children: t("resource.importInvalid")
780
+ }),
702
781
  /* @__PURE__ */ jsx("div", {
703
782
  className: "resource-table-scroll",
704
783
  children: /* @__PURE__ */ jsxs("div", {
@@ -762,7 +841,7 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
762
841
  /* @__PURE__ */ jsx(EmptyTitle, { children: t(normalized.length == 0 ? "resource.noFlows" : "resource.noMatchingFlows") }),
763
842
  /* @__PURE__ */ jsx(EmptyDescription, { children: t(normalized.length == 0 ? "resource.noFlowsDescription" : "resource.noMatchingDescription") })
764
843
  ] }), normalized.length == 0 && /* @__PURE__ */ jsx(EmptyContent, { children: /* @__PURE__ */ jsxs(Button, {
765
- onClick: () => setCreating(true),
844
+ onClick: openCreate,
766
845
  variant: "outline",
767
846
  children: [/* @__PURE__ */ jsx(Icon, {
768
847
  "data-icon": "inline-start",
@@ -792,7 +871,7 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
792
871
  })
793
872
  ]
794
873
  }),
795
- creating && /* @__PURE__ */ jsx(Suspense, {
874
+ creation != null && /* @__PURE__ */ jsx(Suspense, {
796
875
  fallback: null,
797
876
  children: /* @__PURE__ */ jsx(CreateResourceDialog, {
798
877
  disabled: createFlowDisabled,
@@ -802,16 +881,23 @@ function FlowBrowser({ catalogWidth, createFlowDisabled, createFlowField, flowBa
802
881
  label: t("resource.flowName"),
803
882
  name,
804
883
  onNameChange: setName,
805
- onOpenChange: setCreating,
884
+ onOpenChange: (open) => {
885
+ if (!open) {
886
+ creation$.set(void 0);
887
+ setName("");
888
+ }
889
+ },
806
890
  onSubmit: (event) => void create(event),
807
891
  pending: busy == "flow",
808
- title: t("resource.newFlow")
892
+ sourceUrl: creation.kind == "import" ? creation.sourceUrl : void 0,
893
+ submitLabel: creation.kind == "import" ? t("resource.importFlow") : void 0,
894
+ title: t(creation.kind == "create" ? "resource.newFlow" : "resource.importFlow")
809
895
  })
810
896
  })
811
897
  ]
812
898
  })
813
899
  });
814
- }
900
+ });
815
901
  //#endregion
816
902
  //#region ../../node_modules/.bun/dequal@2.0.3/node_modules/dequal/dist/index.mjs
817
903
  var has = Object.prototype.hasOwnProperty;
@@ -1542,7 +1628,7 @@ function inputSignature(spec) {
1542
1628
  }
1543
1629
  async function inputEditor(spec, language, values) {
1544
1630
  if (spec == null) return void 0;
1545
- const { FlowRunInputEditorStore } = await import("./flowRunInputEditorStore-Dsxyfwae.js").then((n) => n.t);
1631
+ const { FlowRunInputEditorStore } = await import("./flowRunInputEditorStore-B2Rw4VsT.js").then((n) => n.t);
1546
1632
  const editor = new FlowRunInputEditorStore(spec.definitions, language);
1547
1633
  const initial = values ?? spec.samples;
1548
1634
  if (initial != null) editor.replaceValues(initial);
@@ -2506,116 +2592,6 @@ function getConnectionsCache(options) {
2506
2592
  return getSingleton(singletonKey(oomolNamespaces.connections, options, [query.ownerId], options.sessionId), () => createConnectionsCacheInternal(options, validated));
2507
2593
  }
2508
2594
  //#endregion
2509
- //#region src/connector/common/actionSchema.ts
2510
- function connectorActionPorts(inputSchema, outputSchema) {
2511
- const inputs = inputPorts(inputSchema);
2512
- return {
2513
- inputs,
2514
- initialInputs: inputs.flatMap((input) => input.value === void 0 ? [] : [{
2515
- handle: input.handle,
2516
- value: input.value
2517
- }]),
2518
- outputs: outputPorts(outputSchema)
2519
- };
2520
- }
2521
- function inputPorts(schema) {
2522
- const root = toPlainObject(schema);
2523
- if (root == null || root.type != "object" && root.properties == null) throw new Error("Connector action inputSchema must be an object schema.");
2524
- const properties = toPlainObject(root.properties);
2525
- if (properties == null) return [];
2526
- const required = stringSet(root.required);
2527
- return Object.entries(properties).map(([handle, propertySchema]) => {
2528
- const nullable = !required.has(handle) || schemaAllowsNull(propertySchema, root);
2529
- return {
2530
- description: schemaDescription(root, propertySchema),
2531
- handle,
2532
- json_schema: propertySchemaWithDefinitions(root, propertySchema),
2533
- nullable,
2534
- value: schemaDefault(root, propertySchema, nullable)
2535
- };
2536
- });
2537
- }
2538
- function outputPorts(schema) {
2539
- const root = toPlainObject(schema);
2540
- const properties = toPlainObject(root?.properties);
2541
- if (root != null && (root.type == "object" || properties != null)) {
2542
- if (properties == null) return [];
2543
- const required = stringSet(root.required);
2544
- return Object.entries(properties).map(([handle, propertySchema]) => ({
2545
- description: schemaDescription(root, propertySchema),
2546
- handle,
2547
- json_schema: propertySchemaWithDefinitions(root, propertySchema),
2548
- nullable: !required.has(handle) || schemaAllowsNull(propertySchema, root)
2549
- }));
2550
- } else return [{
2551
- description: schemaDescription(root, schema),
2552
- handle: "output",
2553
- json_schema: schema,
2554
- nullable: schemaAllowsNull(schema)
2555
- }];
2556
- }
2557
- function propertySchemaWithDefinitions(root, schema) {
2558
- const property = toPlainObject(schema);
2559
- if (property == null) return schema;
2560
- const result = Object.assign({}, property);
2561
- if (result.$defs == null && root.$defs != null) result.$defs = root.$defs;
2562
- if (result.definitions == null && root.definitions != null) result.definitions = root.definitions;
2563
- return result;
2564
- }
2565
- function schemaDescription(root, schema) {
2566
- for (const object of referencedSchemas(root, schema)) if (typeof object.description == "string") return object.description;
2567
- }
2568
- function schemaDefault(root, schema, nullable) {
2569
- for (const object of referencedSchemas(root, schema)) if (Object.hasOwn(object, "default")) return object.default;
2570
- return nullable ? null : void 0;
2571
- }
2572
- function stringSet(value) {
2573
- if (!Array.isArray(value)) return /* @__PURE__ */ new Set();
2574
- return new Set(value.filter((item) => typeof item == "string"));
2575
- }
2576
- function schemaAllowsNull(schema, root = schema, references = /* @__PURE__ */ new Set()) {
2577
- const object = toPlainObject(schema);
2578
- if (object == null) return false;
2579
- if (Object.hasOwn(object, "type")) return object.type == "null" || Array.isArray(object.type) && object.type.includes("null");
2580
- if (Object.hasOwn(object, "enum")) return Array.isArray(object.enum) && object.enum.includes(null);
2581
- for (const key of ["anyOf", "oneOf"]) if (Object.hasOwn(object, key)) return Array.isArray(object[key]) && object[key].some((item) => schemaAllowsNull(item, root, references));
2582
- const reference = typeof object.$ref == "string" ? object.$ref : void 0;
2583
- if (reference == null || references.has(reference)) return false;
2584
- const target = resolveLocalReference(root, reference);
2585
- if (target === void 0) return false;
2586
- const nextReferences = new Set(references);
2587
- nextReferences.add(reference);
2588
- return schemaAllowsNull(target, root, nextReferences);
2589
- }
2590
- function referencedSchemas(root, schema) {
2591
- const result = [];
2592
- const references = /* @__PURE__ */ new Set();
2593
- let current = schema;
2594
- while (true) {
2595
- const object = toPlainObject(current);
2596
- if (object == null) return result;
2597
- result.push(object);
2598
- const reference = typeof object.$ref == "string" ? object.$ref : void 0;
2599
- if (reference == null || references.has(reference)) return result;
2600
- const target = resolveLocalReference(root, reference);
2601
- if (target === void 0) return result;
2602
- references.add(reference);
2603
- current = target;
2604
- }
2605
- }
2606
- function resolveLocalReference(root, reference) {
2607
- if (reference == "#") return root;
2608
- if (!reference.startsWith("#/")) return void 0;
2609
- let current = root;
2610
- for (const encodedToken of reference.slice(2).split("/")) {
2611
- const object = toPlainObject(current);
2612
- const token = encodedToken.replaceAll("~1", "/").replaceAll("~0", "~");
2613
- if (object == null || !Object.hasOwn(object, token)) return void 0;
2614
- current = object[token];
2615
- }
2616
- return current;
2617
- }
2618
- //#endregion
2619
2595
  //#region src/workbench/browser/runtime/stores/proxyCatalog.ts
2620
2596
  /** Validate the fields we consume without discarding upstream representation fields. */
2621
2597
  function proxyResponse(value, validate) {
@@ -4903,12 +4879,6 @@ var FlowCatalog = class {
4903
4879
  this.#pending.delete(flow.flowId);
4904
4880
  }
4905
4881
  }
4906
- async create(name) {
4907
- const flow = await this.#client.createFlow(name);
4908
- if (this.#disposed) return;
4909
- this.insert(flow);
4910
- return flow;
4911
- }
4912
4882
  include(flow) {
4913
4883
  const index = this.#state.value.flows.findIndex((candidate) => candidate.flowId == flow.flowId);
4914
4884
  if (index < 0) {
@@ -5092,6 +5062,7 @@ var PresentationChanges = class {
5092
5062
  #changes = Promise.resolve();
5093
5063
  #committed;
5094
5064
  #disposed = false;
5065
+ #failed = false;
5095
5066
  #pending = [];
5096
5067
  constructor(client, setNotice, setPresentation, i18n) {
5097
5068
  this.#client = client;
@@ -5100,6 +5071,7 @@ var PresentationChanges = class {
5100
5071
  this.#i18n = i18n;
5101
5072
  }
5102
5073
  reset(presentation) {
5074
+ this.#failed = false;
5103
5075
  this.#committed = presentation;
5104
5076
  this.#pending = [];
5105
5077
  }
@@ -5107,8 +5079,16 @@ var PresentationChanges = class {
5107
5079
  this.#disposed = true;
5108
5080
  this.#pending = [];
5109
5081
  }
5082
+ async flush() {
5083
+ await this.settled();
5084
+ return !this.#failed && !this.#disposed;
5085
+ }
5110
5086
  async settled() {
5111
- await this.#changes;
5087
+ let pending;
5088
+ do {
5089
+ pending = this.#changes;
5090
+ await pending;
5091
+ } while (pending !== this.#changes);
5112
5092
  }
5113
5093
  async change(flowId, presentation, current, update) {
5114
5094
  if (this.#disposed || this.#committed == null) return false;
@@ -5141,6 +5121,7 @@ var PresentationChanges = class {
5141
5121
  success = true;
5142
5122
  } catch (error) {
5143
5123
  if (this.#disposed || !current()) return;
5124
+ this.#failed = true;
5144
5125
  if (error instanceof ApiError && error.code == "flow.presentation-conflict") try {
5145
5126
  const latest = await this.#client.getPresentation(flowId);
5146
5127
  if (this.#disposed || !current()) return;
@@ -5306,6 +5287,7 @@ var deletionMessageKeys = {
5306
5287
  webhookBody: "history.deleted.webhookBody"
5307
5288
  };
5308
5289
  var WorkspaceStore = class {
5290
+ #canvasCommandListeners;
5309
5291
  #client;
5310
5292
  #draftChanges;
5311
5293
  #draftSession;
@@ -5335,6 +5317,7 @@ var WorkspaceStore = class {
5335
5317
  this.flowCreated = flowCreated;
5336
5318
  this.accessChanged = accessChanged;
5337
5319
  this.filterTriggerConnections = filterTriggerConnections;
5320
+ this.#canvasCommandListeners = /* @__PURE__ */ new Set();
5338
5321
  this.#draftSession = new Latest();
5339
5322
  this.#history = new CanvasHistory();
5340
5323
  this.history$ = this.#history.state$;
@@ -5345,6 +5328,12 @@ var WorkspaceStore = class {
5345
5328
  this.#started = false;
5346
5329
  this.#draftSyncQueued = false;
5347
5330
  this.#nodeFocusId = 0;
5331
+ this.subscribeCanvasCommands = (execute) => {
5332
+ this.#canvasCommandListeners.add(execute);
5333
+ return () => {
5334
+ this.#canvasCommandListeners.delete(execute);
5335
+ };
5336
+ };
5348
5337
  this.#client = client;
5349
5338
  this.#setNotice = setNotice;
5350
5339
  this.#identity = identity;
@@ -5380,6 +5369,7 @@ var WorkspaceStore = class {
5380
5369
  this.#presentationChanges.dispose();
5381
5370
  this.#stopCatalogWatch?.();
5382
5371
  this.#stopFlowWatch?.();
5372
+ this.#canvasCommandListeners.clear();
5383
5373
  this.history$.dispose();
5384
5374
  this.#model.dispose();
5385
5375
  this.#flows.dispose();
@@ -5468,6 +5458,8 @@ var WorkspaceStore = class {
5468
5458
  this.accessChanged(flowId, accessRevision);
5469
5459
  this.#refreshLive(flowId, current);
5470
5460
  }
5461
+ }, (command) => {
5462
+ if (current() && !this.#model.value.workspaceLoading) for (const execute of this.#canvasCommandListeners) execute(command);
5471
5463
  });
5472
5464
  this.#stopFlowWatch = subscription.stop;
5473
5465
  await subscription.ready;
@@ -5555,14 +5547,49 @@ var WorkspaceStore = class {
5555
5547
  });
5556
5548
  return true;
5557
5549
  }
5558
- async createFlow(name, create) {
5550
+ async exportFlow(flowId) {
5551
+ const current = this.#draftSession.capture();
5552
+ const selected = this.#model.value.flowId == flowId;
5553
+ if (selected && (!await this.saveDraft() || !await this.#presentationChanges.flush() || !current())) {
5554
+ this.#setNotice({
5555
+ kind: "error",
5556
+ message: this.#i18n.t("notice.exportSaveFailed")
5557
+ });
5558
+ return;
5559
+ }
5560
+ this.#set({ busy: "flow" });
5561
+ try {
5562
+ const file = await this.#client.exportFlow(flowId);
5563
+ if (this.#disposed || selected && !current()) return;
5564
+ const url = URL.createObjectURL(new Blob([encodeWorkflowFile(file)], { type: "application/json;charset=utf-8" }));
5565
+ const link = document.createElement("a");
5566
+ link.href = url;
5567
+ link.download = workflowFileName(file.name ?? "workflow");
5568
+ link.click();
5569
+ setTimeout(() => URL.revokeObjectURL(url), 0);
5570
+ } catch (error) {
5571
+ if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
5572
+ } finally {
5573
+ this.#set({ busy: void 0 });
5574
+ }
5575
+ }
5576
+ createFlow(name, create) {
5577
+ return this.#createFlow(async () => create == null ? this.#client.createFlow(name) : this.#client.getFlow(await create(name)));
5578
+ }
5579
+ importFlow(file, name, key, importer) {
5580
+ return this.#createFlow(async () => importer == null ? this.#client.importFlow(file, {
5581
+ name,
5582
+ idempotencyKey: key
5583
+ }) : this.#client.getFlow(await importer(file, name, key)));
5584
+ }
5585
+ async #createFlow(create) {
5559
5586
  if (!await this.saveModuleEditor()) return;
5560
5587
  this.#set({ busy: "flow" });
5561
5588
  try {
5562
- if (create == null) return await this.#flows.create(name);
5563
- const flow = await this.#client.getFlow(await create(name));
5564
- this.#flows.insert(flow);
5565
- return flow;
5589
+ const created = await create();
5590
+ if (this.#disposed) return;
5591
+ this.#flows.insert(created);
5592
+ return created;
5566
5593
  } catch (error) {
5567
5594
  if (!this.#disposed) this.#setNotice(errorNotice(error, this.#i18n.t));
5568
5595
  } finally {
@@ -6607,13 +6634,18 @@ var WorkbenchStore = class {
6607
6634
  this.#openingCreatedFlow = false;
6608
6635
  }
6609
6636
  }
6637
+ async importFlow(file, name, key, importer) {
6638
+ return await this.#openNewFlow(await this.workspace.importFlow(file, name, key, importer), "notice.flowImported");
6639
+ }
6610
6640
  async createFlow(name, create) {
6611
- const flow = await this.workspace.createFlow(name, create);
6612
- if (this.#disposed || flow == null) return false;
6613
- await this.selectFlow(flow.flowId);
6641
+ return await this.#openNewFlow(await this.workspace.createFlow(name, create), "notice.created");
6642
+ }
6643
+ async #openNewFlow(created, notice) {
6644
+ if (this.#disposed || created == null) return false;
6645
+ await this.selectFlow(created.flowId);
6614
6646
  if (!this.#disposed) this.#notice.set({
6615
6647
  kind: "success",
6616
- message: this.#i18n.t("notice.created", { name: flow.name })
6648
+ message: this.#i18n.t(notice, { name: created.name })
6617
6649
  });
6618
6650
  return true;
6619
6651
  }
@@ -6793,7 +6825,7 @@ var WorkbenchStore = class {
6793
6825
  };
6794
6826
  //#endregion
6795
6827
  //#region src/workbench/browser/runtime/openFlowWorkbench.tsx
6796
- var FlowWorkspace = lazy(() => import("./flowWorkspace-BgZaFKZm.js"));
6828
+ var FlowWorkspace = lazy(() => import("./flowWorkspace-DxLc1Ybv.js"));
6797
6829
  function OpenFlowSessionGate({ action, description, error, onSubmit, onTokenChange, pending = false, title, token, tokenLabel }) {
6798
6830
  const hasToken = token != null && tokenLabel != null && onTokenChange != null;
6799
6831
  const tokenValue = token ?? "";
@@ -6899,11 +6931,12 @@ function FlowNameBridge({ store, onChange }) {
6899
6931
  useEffect(() => () => onChange(void 0), [onChange]);
6900
6932
  return null;
6901
6933
  }
6902
- function Workbench({ catalogWidth, createFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, hrefFor, initializing, language, navigation, onConfigureConnector, onManageConnectorAccess, onLanguageChange, store, theme }) {
6934
+ function Workbench({ importRef, catalogWidth, createFlow, importFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, hrefFor, initializing, language, navigation, onConfigureConnector, onManageConnectorAccess, onLanguageChange, store, theme }) {
6903
6935
  const flowId = useVal(store.workspace.$.flowId);
6904
6936
  return /* @__PURE__ */ jsx("div", {
6905
6937
  className: "app-shell",
6906
6938
  children: flowId == null ? /* @__PURE__ */ jsx(FlowBrowser, {
6939
+ ref: importRef,
6907
6940
  catalogWidth,
6908
6941
  createFlowDisabled,
6909
6942
  createFlowField,
@@ -6915,6 +6948,7 @@ function Workbench({ catalogWidth, createFlow, createFlowDisabled, createFlowFie
6915
6948
  initializing,
6916
6949
  language,
6917
6950
  onCreateFlow: (name) => navigation.createFlow(name, createFlow),
6951
+ onImportFlow: (file, name, key) => navigation.importFlow(file, name, key, importFlow),
6918
6952
  onLanguageChange,
6919
6953
  onSelectFlow: (flow, view) => void navigation.selectFlow(flow, view),
6920
6954
  store
@@ -6935,7 +6969,9 @@ function Workbench({ catalogWidth, createFlow, createFlowDisabled, createFlowFie
6935
6969
  })
6936
6970
  });
6937
6971
  }
6938
- function Session({ catalogWidth, createFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, host, hrefFor, language, location, onConfigureConnector, onManageConnectorAccess, onLanguageChange, onNavigate, onFlowNameChange, preferences, sessionKey, theme, variables = true }) {
6972
+ var Session = forwardRef(function Session({ catalogWidth, createFlow, importFlow, createFlowDisabled, createFlowField, flowBadges, connectionHref, host, hrefFor, language, location, onConfigureConnector, onManageConnectorAccess, onLanguageChange, onNavigate, onFlowNameChange, preferences, sessionKey, theme, variables = true }, ref) {
6973
+ const importRef = useRef(null);
6974
+ useImperativeHandle(ref, () => ({ openImport: (input) => importRef.current?.openImport(input) ?? "unavailable" }), []);
6939
6975
  const navigate = useRef(onNavigate);
6940
6976
  navigate.current = onNavigate;
6941
6977
  const [{ i18n, navigation, store }] = useState(() => {
@@ -6999,8 +7035,10 @@ function Session({ catalogWidth, createFlow, createFlowDisabled, createFlowField
6999
7035
  onChange: onFlowNameChange
7000
7036
  }),
7001
7037
  started || location.flowId == null ? /* @__PURE__ */ jsx(Workbench, {
7038
+ importRef: location.flowId == null ? importRef : void 0,
7002
7039
  catalogWidth,
7003
7040
  createFlow,
7041
+ importFlow,
7004
7042
  createFlowDisabled,
7005
7043
  createFlowField,
7006
7044
  flowBadges,
@@ -7022,12 +7060,13 @@ function Session({ catalogWidth, createFlow, createFlowDisabled, createFlowField
7022
7060
  })
7023
7061
  })
7024
7062
  });
7025
- }
7026
- function OpenFlowWorkbench({ sessionKey, ...props }) {
7063
+ });
7064
+ var OpenFlowWorkbench = forwardRef(function OpenFlowWorkbench({ sessionKey, ...props }, ref) {
7027
7065
  return /* @__PURE__ */ jsx(Session, {
7066
+ ref,
7028
7067
  sessionKey,
7029
7068
  ...props
7030
7069
  }, `${sessionKey}:${props.host.connectorOwnerId ?? ""}`);
7031
- }
7070
+ });
7032
7071
  //#endregion
7033
7072
  export { EventSourcesPage, OpenFlowSessionGate, OpenFlowWorkbench, isUiLanguage as isWorkbenchLanguage, resolveUiLanguage as resolveWorkbenchLanguage, uiLanguageNames as workbenchLanguageNames, uiLanguages as workbenchLanguages };