@kiberon-labs/behave-graph-flow 1.0.0 → 3.0.0
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/.fallowrc.json +16 -0
- package/.storybook/main.ts +32 -0
- package/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +64 -0
- package/.storybook/styles.css +16 -0
- package/.turbo/turbo-build.log +7 -0
- package/CHANGELOG.md +368 -0
- package/LICENSE +6 -0
- package/README.md +2 -2
- package/data/Polynomial.json +510 -0
- package/data/sequence.json +337 -0
- package/data/trigger-event.json +241 -0
- package/data/variable-change.json +210 -0
- package/dist/AnyControlImpl-Ds-CShIB.js +20 -0
- package/dist/AnyControlImpl-Ds-CShIB.js.map +1 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js +166 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js.map +1 -0
- package/dist/entry.css +4 -0
- package/dist/index.css +42 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +3597 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18009 -0
- package/dist/index.js.map +1 -0
- package/dist/noteImpl-KkrrWgJd.js +242 -0
- package/dist/noteImpl-KkrrWgJd.js.map +1 -0
- package/dist/styles.module-CvmpDkZj.css +3 -0
- package/dist/styles.module-CvmpDkZj.css.map +1 -0
- package/dist/styles.module-DZxg8aW9.js +271 -0
- package/dist/styles.module-DZxg8aW9.js.map +1 -0
- package/dist/useChangeNodeData-ChQGK7AI.js +23 -0
- package/dist/useChangeNodeData-ChQGK7AI.js.map +1 -0
- package/docs/notifications.md +246 -0
- package/docs/protocol.md +702 -0
- package/docs/specifics.md +191 -0
- package/package.json +82 -22
- package/postcss.config.ts +3 -4
- package/src/annotations/index.ts +32 -0
- package/src/components/FloatingToolbar/index.module.css +37 -0
- package/src/components/FloatingToolbar/index.tsx +256 -0
- package/src/components/Flow.tsx +287 -75
- package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
- package/src/components/contextMenus/NodePicker.module.css +274 -0
- package/src/components/contextMenus/NodePicker.tsx +481 -0
- package/src/components/contextMenus/edge.tsx +22 -0
- package/src/components/contextMenus/node.tsx +15 -0
- package/src/components/contextMenus/selection.tsx +11 -0
- package/src/components/controls/any/AnyControlImpl.tsx +14 -0
- package/src/components/controls/any/index.tsx +19 -0
- package/src/components/controls/boolean/index.tsx +13 -0
- package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
- package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
- package/src/components/controls/colorPicker/index.module.css +18 -0
- package/src/components/controls/colorPicker/index.tsx +61 -0
- package/src/components/controls/number/index.tsx +35 -0
- package/src/components/controls/string/index.tsx +16 -0
- package/src/components/edges/index.tsx +475 -0
- package/src/components/edges/offsetBezier.ts +134 -0
- package/src/components/hotKeys.tsx +20 -0
- package/src/components/layoutController/index.module.css +13 -0
- package/src/components/layoutController/index.tsx +140 -0
- package/src/components/layoutController/utils.ts +248 -0
- package/src/components/menubar/defaults.tsx +516 -0
- package/src/components/menubar/index.tsx +49 -0
- package/src/components/menubar/menuItem.module.css +31 -0
- package/src/components/menubar/menuItem.tsx +65 -0
- package/src/components/nodes/behave/Node.module.css +23 -0
- package/src/components/nodes/behave/Node.tsx +176 -0
- package/src/components/nodes/behave/NodeContainer.module.css +88 -0
- package/src/components/nodes/behave/NodeContainer.tsx +46 -0
- package/src/components/nodes/behave/index.tsx +14 -0
- package/src/components/nodes/group/index.tsx +109 -0
- package/src/components/nodes/wrapper/index.tsx +73 -0
- package/src/components/nodes/wrapper/styles.module.css +87 -0
- package/src/components/notifications/NotificationProvider.tsx +81 -0
- package/src/components/notifications/index.ts +2 -0
- package/src/components/notifications/utils.ts +71 -0
- package/src/components/panels/alignment/index.module.css +10 -0
- package/src/components/panels/alignment/index.tsx +244 -0
- package/src/components/panels/base/index.tsx +5 -0
- package/src/components/panels/base/styles.module.css +12 -0
- package/src/components/panels/common/PanelHeader.module.css +24 -0
- package/src/components/panels/common/PanelHeader.tsx +22 -0
- package/src/components/panels/common/SectionTitle.module.css +13 -0
- package/src/components/panels/common/SectionTitle.tsx +10 -0
- package/src/components/panels/events/EditEventPanel.tsx +324 -0
- package/src/components/panels/events/ManageEventsPanel.tsx +101 -0
- package/src/components/panels/events/index.tsx +23 -0
- package/src/components/panels/events/styles.module.css +178 -0
- package/src/components/panels/graphProperties/index.tsx +125 -0
- package/src/components/panels/history/index.tsx +92 -0
- package/src/components/panels/history/styles.module.css +97 -0
- package/src/components/panels/keymaps/index.module.css +68 -0
- package/src/components/panels/keymaps/index.tsx +166 -0
- package/src/components/panels/layers/index.tsx +245 -0
- package/src/components/panels/layers/styles.module.css +107 -0
- package/src/components/panels/legend/index.module.css +6 -0
- package/src/components/panels/legend/index.tsx +76 -0
- package/src/components/panels/logs/index.module.css +218 -0
- package/src/components/panels/logs/index.tsx +288 -0
- package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
- package/src/components/panels/nodeInputs/InputsGroup.tsx +65 -0
- package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
- package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
- package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
- package/src/components/panels/nodeInputs/OutputsGroup.tsx +55 -0
- package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
- package/src/components/panels/nodeInputs/index.module.css +308 -0
- package/src/components/panels/nodeInputs/index.tsx +349 -0
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +153 -0
- package/src/components/panels/nodePicker/index.tsx +115 -0
- package/src/components/panels/panel/index.module.css +66 -0
- package/src/components/panels/panel/index.tsx +88 -0
- package/src/components/panels/search/index.module.css +16 -0
- package/src/components/panels/search/index.tsx +215 -0
- package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
- package/src/components/panels/systemSettings/index.tsx +251 -0
- package/src/components/panels/systemSettings/styles.module.css +138 -0
- package/src/components/panels/traces/GridLines.tsx +38 -0
- package/src/components/panels/traces/TimeGrid.tsx +48 -0
- package/src/components/panels/traces/TraceLane.tsx +62 -0
- package/src/components/panels/traces/TraceTooltip.tsx +22 -0
- package/src/components/panels/traces/TracesHeader.tsx +56 -0
- package/src/components/panels/traces/index.module.css +159 -0
- package/src/components/panels/traces/index.tsx +298 -0
- package/src/components/panels/traces/types.ts +48 -0
- package/src/components/panels/traces/useDerivedSpans.ts +307 -0
- package/src/components/panels/traces/utils.ts +33 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
- package/src/components/panels/variables/ManageVariablesScreen.tsx +147 -0
- package/src/components/panels/variables/index.tsx +125 -0
- package/src/components/panels/variables/styles.module.css +149 -0
- package/src/components/primitives/icon.module.css +45 -0
- package/src/components/primitives/icon.tsx +38 -0
- package/src/components/sockets/input/index.tsx +83 -0
- package/src/components/sockets/input/styles.module.css +26 -0
- package/src/components/sockets/output/index.tsx +68 -0
- package/src/components/sockets/output/styles.module.css +22 -0
- package/src/css/notes.css +135 -0
- package/src/css/prosemirror.css +57 -0
- package/src/css/rc-dock.css +212 -0
- package/src/css/rc-menu.css +101 -0
- package/src/css/themes/kiberon.css +127 -0
- package/src/css/vars.css +198 -0
- package/src/css/vscode-elements.css +124 -0
- package/src/entry.css +4 -0
- package/src/generators/CallSubgraphGenerator.tsx +136 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
- package/src/generators/GraphBoundaryGenerator.module.css +32 -0
- package/src/generators/GraphBoundaryGenerator.tsx +193 -0
- package/src/generators/SequenceGenerator.tsx +104 -0
- package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
- package/src/generators/SwitchOnStringGenerator.tsx +263 -0
- package/src/generators/callSubgraphSync.ts +126 -0
- package/src/generators/registerDefaultGenerators.ts +55 -0
- package/src/generators/registerDefaults.ts +26 -0
- package/src/hooks/useBehaveGraphFlow.ts +17 -16
- package/src/hooks/useFlowHandlers.ts +154 -30
- package/src/hooks/useWasdPan.ts +210 -0
- package/src/index.css +134 -0
- package/src/index.ts +53 -18
- package/src/manifest/contributionRegistry.ts +93 -0
- package/src/manifest/index.ts +4 -0
- package/src/manifest/loadManifest.ts +82 -0
- package/src/manifest/manifestPlugin.ts +29 -0
- package/src/manifest/passthroughValueType.ts +40 -0
- package/src/plugin/alignment/index.ts +91 -0
- package/src/plugin/autosave/controller.ts +366 -0
- package/src/plugin/autosave/index.tsx +114 -0
- package/src/plugin/autosave/panel/BackupPanel.tsx +141 -0
- package/src/plugin/autosave/panel/index.tsx +1 -0
- package/src/plugin/autosave/panel/styles.module.css +56 -0
- package/src/plugin/autosave/settings.ts +65 -0
- package/src/plugin/autosave/storage.ts +147 -0
- package/src/plugin/docs/index.tsx +297 -0
- package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
- package/src/plugin/docs/panel/index.tsx +21 -0
- package/src/plugin/docs/panel/styles.module.css +174 -0
- package/src/plugin/graphrunner/actions.ts +326 -0
- package/src/plugin/graphrunner/buttons.tsx +95 -0
- package/src/plugin/graphrunner/client.ts +707 -0
- package/src/plugin/graphrunner/index.tsx +184 -0
- package/src/plugin/graphrunner/panel.tsx +386 -0
- package/src/plugin/graphrunner/runController.ts +283 -0
- package/src/plugin/graphrunner/runner.ts +187 -0
- package/src/plugin/graphrunner/session.ts +243 -0
- package/src/plugin/graphrunner/store.ts +196 -0
- package/src/plugin/graphrunner/styles.module.css +171 -0
- package/src/plugin/graphrunner/transport.ts +250 -0
- package/src/plugin/graphrunner/types.ts +693 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +637 -0
- package/src/plugin/graphrunner-local/index.tsx +172 -0
- package/src/plugin/graphrunner-local/panel.tsx +187 -0
- package/src/plugin/graphrunner-local/store.ts +41 -0
- package/src/plugin/graphrunner-local/styles.module.css +82 -0
- package/src/plugin/graphrunner-local/transport.ts +1339 -0
- package/src/plugin/graphrunner-local/types.ts +10 -0
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +635 -0
- package/src/plugin/graphrunner-webworker/index.tsx +140 -0
- package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
- package/src/plugin/graphrunner-webworker/store.ts +98 -0
- package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
- package/src/plugin/kitchen-sink/index.ts +38 -0
- package/src/plugin/layout/dagre.ts +131 -0
- package/src/plugin/layout/elk.ts +216 -0
- package/src/plugin/layout/index.ts +80 -0
- package/src/plugin/notes/FormatToolbar.tsx +200 -0
- package/src/plugin/notes/index.tsx +191 -0
- package/src/plugin/notes/nodeActions.ts +100 -0
- package/src/plugin/notes/note.tsx +20 -0
- package/src/plugin/notes/noteImpl.tsx +89 -0
- package/src/plugin/realtime/realtimeRunner.ts +624 -0
- package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
- package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
- package/src/specifics/VariableGetSpecific.tsx +110 -0
- package/src/specifics/VariableSetSpecific.tsx +110 -0
- package/src/store/actions.tsx +698 -0
- package/src/store/commands.ts +278 -0
- package/src/store/contextMenu.ts +192 -0
- package/src/store/controls.tsx +62 -0
- package/src/store/conversions.ts +47 -0
- package/src/store/documentation.tsx +69 -0
- package/src/store/events.tsx +116 -0
- package/src/store/flow.tsx +230 -0
- package/src/store/graphMeta.ts +39 -0
- package/src/store/hotKeys.tsx +364 -0
- package/src/store/layers.ts +259 -0
- package/src/store/legend.tsx +76 -0
- package/src/store/logs.ts +28 -0
- package/src/store/menubar.ts +41 -0
- package/src/store/refs.ts +84 -0
- package/src/store/registry.ts +51 -0
- package/src/store/selection.ts +22 -0
- package/src/store/settings.ts +99 -0
- package/src/store/settingsSchema.ts +210 -0
- package/src/store/socketGenerator.tsx +54 -0
- package/src/store/specific.tsx +75 -0
- package/src/store/specs.tsx +35 -0
- package/src/store/tabs.ts +282 -0
- package/src/store/toolbar.tsx +45 -0
- package/src/store/traces.ts +240 -0
- package/src/store/variables.ts +37 -0
- package/src/system/graph.ts +131 -0
- package/src/system/graphSession.ts +172 -0
- package/src/system/index.ts +6 -0
- package/src/system/notifications.ts +111 -0
- package/src/system/persistence.ts +82 -0
- package/src/system/plugin.ts +55 -0
- package/src/system/provider.tsx +86 -0
- package/src/system/pubsub.ts +323 -0
- package/src/system/system.ts +653 -0
- package/src/system/tabLoader.tsx +303 -0
- package/src/system/undoRedo.ts +103 -0
- package/src/transformers/Uigraph.ts +61 -0
- package/src/transformers/behaveToFlow.ts +16 -4
- package/src/transformers/contract.ts +87 -0
- package/src/transformers/flowToBehave.ts +40 -12
- package/src/types/NodeMetadata.ts +27 -0
- package/src/types/graph.ts +49 -0
- package/src/types/nodes.ts +50 -0
- package/src/types.ts +18 -0
- package/src/util/autoConvert.ts +200 -0
- package/src/util/colors.ts +1 -29
- package/src/util/downloadJson.ts +18 -0
- package/src/util/extractNodeMetadata.ts +16 -0
- package/src/util/getPickerFilters.ts +1 -1
- package/src/util/isBehaveNode.ts +6 -0
- package/src/util/isValidConnection.ts +51 -17
- package/src/util/mergeSockets.ts +29 -0
- package/src/util/serializeVariables.ts +66 -0
- package/src/util/sockets.ts +43 -0
- package/stories/apex/layoutController/example-graph.worker.ts +39 -0
- package/stories/apex/layoutController/index.stories.tsx +48 -0
- package/stories/apex/layoutController/webworker.stories.tsx +103 -0
- package/stories/apex/menubar/menubar.stories.tsx +19 -0
- package/stories/components/colorpicker/index.stories.tsx +20 -0
- package/stories/components/contextMenus/edge.stories.tsx +32 -0
- package/stories/components/contextMenus/node.stories.tsx +26 -0
- package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
- package/stories/components/controls/any/index.stories.tsx +19 -0
- package/stories/components/controls/boolean/index.stories.tsx +19 -0
- package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
- package/stories/components/controls/number/index.stories.tsx +19 -0
- package/stories/components/controls/string/index.stories.tsx +19 -0
- package/stories/components/nodes/behaveNode.stories.tsx +108 -0
- package/stories/components/panels/alignment.stories.tsx +24 -0
- package/stories/components/panels/events.stories.tsx +38 -0
- package/stories/components/panels/graphRunner.stories.tsx +317 -0
- package/stories/components/panels/history.stories.tsx +37 -0
- package/stories/components/panels/keymaps.stories.tsx +21 -0
- package/stories/components/panels/legend.stories.tsx +37 -0
- package/stories/components/panels/logs.stories.tsx +24 -0
- package/stories/components/panels/nodeInputs.stories.tsx +21 -0
- package/stories/components/panels/nodePicker.stories.tsx +37 -0
- package/stories/components/panels/panel.stories.tsx +39 -0
- package/stories/components/panels/search.stories.tsx +24 -0
- package/stories/components/panels/systemSettings.stories.tsx +26 -0
- package/stories/components/panels/traces.stories.tsx +225 -0
- package/stories/components/panels/variables.stories.tsx +24 -0
- package/stories/defaults/defaultStoryProvider.tsx +170 -0
- package/stories/defaults/systemGenerator.ts +43 -0
- package/stories/plugins/notes.stories.tsx +100 -0
- package/tests/autoConvert.test.ts +329 -0
- package/tests/autosavePlugin.test.ts +204 -0
- package/tests/callSubgraphSync.test.ts +148 -0
- package/tests/commandRegistry.test.ts +137 -0
- package/tests/components/edges/offsetBezier.test.ts +51 -0
- package/tests/components/layoutController/utils.test.ts +68 -0
- package/tests/components/panels/traces/utils.test.ts +52 -0
- package/tests/contract.test.ts +51 -0
- package/tests/contractSerialize.test.ts +62 -0
- package/tests/deriveSpans.test.ts +71 -0
- package/tests/flowToBehave.test.ts +27 -4
- package/tests/hotkeys.test.ts +79 -0
- package/tests/keepAliveLifecycle.test.ts +167 -0
- package/tests/loadManifest.test.ts +113 -0
- package/tests/noteMarkdown.test.ts +65 -0
- package/tests/notesPlugin.test.ts +162 -0
- package/tests/notifications.test.ts +87 -0
- package/tests/persistence.test.ts +51 -0
- package/tests/saveLoad.test.ts +373 -0
- package/tests/settings.test.ts +178 -0
- package/tests/traceStore.test.ts +46 -0
- package/tests/util/calculateNewEdge.test.ts +98 -0
- package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
- package/tests/util/hasPositionMetaData.test.ts +33 -0
- package/tests/util/isBehaveNode.test.ts +22 -0
- package/tests/util/isHandleConnected.test.ts +37 -0
- package/tests/util/mergeSockets.test.ts +43 -0
- package/tests/visual/README.md +64 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-localGraphRunner-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-traces-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
- package/tests/visual/panels.visual.test.tsx +76 -0
- package/tests/wasdPan.test.ts +71 -0
- package/tsconfig.base.json +39 -0
- package/tsconfig.json +18 -59
- package/tsconfig.prod.json +23 -0
- package/tsdown.config.ts +15 -3
- package/typedoc.json +7 -7
- package/vite.config.js +7 -0
- package/vitest.config.ts +5 -2
- package/vitest.visual.config.ts +55 -0
- package/src/components/AutoSizeInput.tsx +0 -65
- package/src/components/Controls.tsx +0 -87
- package/src/components/InputSocket.tsx +0 -142
- package/src/components/Node.tsx +0 -68
- package/src/components/NodeContainer.tsx +0 -46
- package/src/components/NodePicker.tsx +0 -77
- package/src/components/OutputSocket.tsx +0 -58
- package/src/components/modals/ClearModal.tsx +0 -40
- package/src/components/modals/HelpModal.tsx +0 -36
- package/src/components/modals/LoadModal.tsx +0 -96
- package/src/components/modals/Modal.tsx +0 -64
- package/src/components/modals/SaveModal.tsx +0 -60
- package/src/hooks/useCustomNodeTypes.tsx +0 -31
- package/src/hooks/useGraphRunner.ts +0 -104
- package/src/hooks/useMergeMap.ts +0 -14
- package/src/hooks/useNodeSpecJson.ts +0 -20
- package/src/hooks/useQueriableDefinitions.ts +0 -22
- package/src/styles.css +0 -8
- package/tailwind.config.ts +0 -19
- package/tests/tsconfig.json +0 -10
- /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { createStore, type StoreApi } from 'zustand';
|
|
2
|
+
import type { GraphSession } from '@/system/graphSession';
|
|
3
|
+
import type { GraphRunner } from './runner';
|
|
4
|
+
import { buildUIGraphJSON } from '../../transformers/Uigraph';
|
|
5
|
+
import { isBehaveNode } from '@/util/isBehaveNode';
|
|
6
|
+
import { supportsExecutionControl } from './transport';
|
|
7
|
+
|
|
8
|
+
// Contribute the per-session run controller to the graph session as a typed,
|
|
9
|
+
// plugin-owned property. Core no longer declares this field , the graph runner
|
|
10
|
+
// plugin attaches it via `session.decorate('runController', …)` from a session
|
|
11
|
+
// extension (see ./index.tsx).
|
|
12
|
+
declare module '@/system/graphSession' {
|
|
13
|
+
interface IGraphSession {
|
|
14
|
+
runController?: GraphRunController;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Per-run state for a single graph. Each {@link GraphSession} owns one of these,
|
|
20
|
+
* so multiple graphs can run independently and concurrently.
|
|
21
|
+
*/
|
|
22
|
+
export interface RunControllerStore {
|
|
23
|
+
currentRunId: string | null;
|
|
24
|
+
currentGraphId: string | null;
|
|
25
|
+
isExecuting: boolean;
|
|
26
|
+
isPaused: boolean;
|
|
27
|
+
setCurrentRunId: (runId: string | null) => void;
|
|
28
|
+
setCurrentGraphId: (graphId: string | null) => void;
|
|
29
|
+
setIsExecuting: (isExecuting: boolean) => void;
|
|
30
|
+
setIsPaused: (isPaused: boolean) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const runControllerStoreFactory = (): StoreApi<RunControllerStore> =>
|
|
34
|
+
createStore<RunControllerStore>((set) => ({
|
|
35
|
+
currentRunId: null,
|
|
36
|
+
currentGraphId: null,
|
|
37
|
+
isExecuting: false,
|
|
38
|
+
isPaused: false,
|
|
39
|
+
setCurrentRunId: (currentRunId) => set({ currentRunId }),
|
|
40
|
+
setCurrentGraphId: (currentGraphId) => set({ currentGraphId }),
|
|
41
|
+
setIsExecuting: (isExecuting) => set({ isExecuting }),
|
|
42
|
+
setIsPaused: (isPaused) => set({ isPaused })
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Drives execution for a single graph session. Run lifecycle and run state are
|
|
47
|
+
* per-session; the underlying connection/client is shared via {@link GraphRunner}.
|
|
48
|
+
* Incoming server messages are routed back to the originating controller by run
|
|
49
|
+
* id (see `GraphRunner.runIndex`).
|
|
50
|
+
*/
|
|
51
|
+
export class GraphRunController {
|
|
52
|
+
public readonly session: GraphSession;
|
|
53
|
+
public readonly runner: GraphRunner;
|
|
54
|
+
public readonly store: StoreApi<RunControllerStore> =
|
|
55
|
+
runControllerStoreFactory();
|
|
56
|
+
private readonly disposers: Array<() => void> = [];
|
|
57
|
+
|
|
58
|
+
constructor(session: GraphSession, runner: GraphRunner) {
|
|
59
|
+
this.session = session;
|
|
60
|
+
this.runner = runner;
|
|
61
|
+
this.setupRealtimeForwarding();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private get notifications() {
|
|
65
|
+
return this.session.editor.notifications;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Run the graph for this session. */
|
|
69
|
+
async play(): Promise<void> {
|
|
70
|
+
const { clearLogsOnRun, clearTracesOnRun } = this.runner.store.getState();
|
|
71
|
+
|
|
72
|
+
if (clearLogsOnRun) {
|
|
73
|
+
this.session.logsStore.getState().clear();
|
|
74
|
+
}
|
|
75
|
+
if (clearTracesOnRun) {
|
|
76
|
+
this.session.traceStore.getState().clear();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const graphId = this.session.id;
|
|
80
|
+
const uiGraphData = buildUIGraphJSON(this.session);
|
|
81
|
+
try {
|
|
82
|
+
await this.runRemotely(graphId, { graph: uiGraphData.flow });
|
|
83
|
+
} catch {
|
|
84
|
+
// Error already surfaced in runRemotely
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async runRemotely(
|
|
89
|
+
graphId: string,
|
|
90
|
+
options?: { graph?: unknown; inputs?: unknown }
|
|
91
|
+
): Promise<void> {
|
|
92
|
+
const client = this.runner.store.getState().client;
|
|
93
|
+
const { enableTracing } = this.runner.store.getState();
|
|
94
|
+
const { setCurrentRunId, setCurrentGraphId, setIsExecuting, setIsPaused } =
|
|
95
|
+
this.store.getState();
|
|
96
|
+
|
|
97
|
+
if (!client) {
|
|
98
|
+
this.notifications.error('No graph runner connection');
|
|
99
|
+
throw new Error('No graph runner connection');
|
|
100
|
+
}
|
|
101
|
+
if (this.store.getState().isExecuting) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const runId = await client.runGraph(graphId, {
|
|
107
|
+
...options,
|
|
108
|
+
trace: enableTracing
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
setCurrentRunId(runId);
|
|
112
|
+
setCurrentGraphId(graphId);
|
|
113
|
+
setIsExecuting(true);
|
|
114
|
+
setIsPaused(false);
|
|
115
|
+
this.runner.registerRun(runId, this);
|
|
116
|
+
|
|
117
|
+
this.notifications.info(`Graph execution started: ${graphId}`);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
setIsExecuting(false);
|
|
121
|
+
setIsPaused(false);
|
|
122
|
+
setCurrentRunId(null);
|
|
123
|
+
setCurrentGraphId(null);
|
|
124
|
+
this.notifications.error(`Failed to run graph: ${message}`);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async stop(): Promise<void> {
|
|
130
|
+
const client = this.runner.store.getState().client;
|
|
131
|
+
const { currentRunId } = this.store.getState();
|
|
132
|
+
if (!client || !currentRunId) return;
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await client.stopGraph(currentRunId);
|
|
136
|
+
this.notifications.info('Stopping graph execution');
|
|
137
|
+
this.finishRun();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
this.notifications.error(`Failed to stop graph: ${message}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async pause(): Promise<void> {
|
|
145
|
+
const client = this.runner.store.getState().client;
|
|
146
|
+
const { currentRunId, setIsPaused } = this.store.getState();
|
|
147
|
+
if (!client || !currentRunId) return;
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const transport = client.transport;
|
|
151
|
+
if (supportsExecutionControl(transport)) {
|
|
152
|
+
transport.pauseExecution(currentRunId);
|
|
153
|
+
setIsPaused(true);
|
|
154
|
+
this.notifications.info('Execution paused');
|
|
155
|
+
} else {
|
|
156
|
+
await this.stop();
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
160
|
+
this.notifications.error(`Failed to pause graph: ${message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async resume(): Promise<void> {
|
|
165
|
+
const client = this.runner.store.getState().client;
|
|
166
|
+
const { currentRunId, setIsPaused } = this.store.getState();
|
|
167
|
+
if (!client || !currentRunId) return;
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const transport = client.transport;
|
|
171
|
+
if (supportsExecutionControl(transport)) {
|
|
172
|
+
setIsPaused(false);
|
|
173
|
+
this.notifications.info('Resuming execution');
|
|
174
|
+
await transport.resumeExecution(currentRunId);
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
178
|
+
this.notifications.error(`Failed to resume graph: ${message}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async step(): Promise<void> {
|
|
183
|
+
const client = this.runner.store.getState().client;
|
|
184
|
+
const { currentRunId, setIsPaused } = this.store.getState();
|
|
185
|
+
if (!client || !currentRunId) return;
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const transport = client.transport;
|
|
189
|
+
if (supportsExecutionControl(transport)) {
|
|
190
|
+
setIsPaused(true);
|
|
191
|
+
await transport.stepExecution(currentRunId);
|
|
192
|
+
} else {
|
|
193
|
+
this.notifications.info(
|
|
194
|
+
'Step execution not supported for this transport'
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
199
|
+
this.notifications.error(`Failed to step graph: ${message}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Reset run state after completion/stop/error and unregister the run. */
|
|
204
|
+
finishRun(): void {
|
|
205
|
+
const runId = this.store.getState().currentRunId;
|
|
206
|
+
if (runId) this.runner.unregisterRun(runId);
|
|
207
|
+
const s = this.store.getState();
|
|
208
|
+
s.setIsExecuting(false);
|
|
209
|
+
s.setIsPaused(false);
|
|
210
|
+
s.setCurrentRunId(null);
|
|
211
|
+
s.setCurrentGraphId(null);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Forward live graph edits to the server while this graph is running, tagged
|
|
216
|
+
* with this controller's run id so concurrent graphs stay isolated.
|
|
217
|
+
*/
|
|
218
|
+
private setupRealtimeForwarding(): void {
|
|
219
|
+
const pubsub = this.session.pubsub;
|
|
220
|
+
const canForward = () => {
|
|
221
|
+
const client = this.runner.store.getState().client;
|
|
222
|
+
const runId = this.store.getState().currentRunId;
|
|
223
|
+
const caps = this.runner.store.getState().connectionInfo.capabilities;
|
|
224
|
+
return client && runId && caps?.realtime ? { client, runId } : null;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const tokens = [
|
|
228
|
+
pubsub.subscribe('node:added', (_, node) => {
|
|
229
|
+
if (!isBehaveNode(node)) return;
|
|
230
|
+
const ctx = canForward();
|
|
231
|
+
if (!ctx) return;
|
|
232
|
+
ctx.client.addNode(
|
|
233
|
+
ctx.runId,
|
|
234
|
+
node.id,
|
|
235
|
+
node.data.type,
|
|
236
|
+
node.data as Record<string, unknown>,
|
|
237
|
+
node.position
|
|
238
|
+
);
|
|
239
|
+
}),
|
|
240
|
+
pubsub.subscribe('edge:added', (_, edge) => {
|
|
241
|
+
const ctx = canForward();
|
|
242
|
+
if (!ctx || !edge.source || !edge.target) return;
|
|
243
|
+
ctx.client.createLink(
|
|
244
|
+
ctx.runId,
|
|
245
|
+
edge.source,
|
|
246
|
+
edge.sourceHandle || '',
|
|
247
|
+
edge.target,
|
|
248
|
+
edge.targetHandle || ''
|
|
249
|
+
);
|
|
250
|
+
}),
|
|
251
|
+
pubsub.subscribe('edge:removed', (_, edge) => {
|
|
252
|
+
const ctx = canForward();
|
|
253
|
+
if (!ctx || !edge.source || !edge.target) return;
|
|
254
|
+
ctx.client.removeLink(
|
|
255
|
+
ctx.runId,
|
|
256
|
+
edge.source,
|
|
257
|
+
edge.sourceHandle || '',
|
|
258
|
+
edge.target,
|
|
259
|
+
edge.targetHandle || ''
|
|
260
|
+
);
|
|
261
|
+
}),
|
|
262
|
+
pubsub.subscribe('node:removed', (_, node) => {
|
|
263
|
+
if (!isBehaveNode(node)) return;
|
|
264
|
+
const ctx = canForward();
|
|
265
|
+
if (!ctx) return;
|
|
266
|
+
ctx.client.removeNode(ctx.runId, node.id);
|
|
267
|
+
})
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
for (const token of tokens) {
|
|
271
|
+
if (typeof token === 'string') {
|
|
272
|
+
this.disposers.push(() => pubsub.unsubscribe(token));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
dispose(): void {
|
|
278
|
+
const runId = this.store.getState().currentRunId;
|
|
279
|
+
if (runId) this.runner.unregisterRun(runId);
|
|
280
|
+
this.disposers.forEach((d) => d());
|
|
281
|
+
this.disposers.length = 0;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { System } from '../../system/system';
|
|
2
|
+
import type { StoreApi } from 'zustand';
|
|
3
|
+
import type { GraphRunnerClientStore } from './store';
|
|
4
|
+
import { GraphRunnerClient } from './client';
|
|
5
|
+
import { setupClientEventListeners } from './actions';
|
|
6
|
+
import type { GraphRunController } from './runController';
|
|
7
|
+
|
|
8
|
+
declare module '@/system/system' {
|
|
9
|
+
interface System {
|
|
10
|
+
runner: GraphRunner;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Shared connection to the graph runner server. Owns the client, connection
|
|
16
|
+
* lifecycle and server metadata; per-graph run state and run lifecycle live on
|
|
17
|
+
* {@link GraphRunController}. Incoming server messages are dispatched back to the
|
|
18
|
+
* owning controller via {@link GraphRunner.runIndex}, keyed by run id, so
|
|
19
|
+
* multiple graphs can run concurrently and independently.
|
|
20
|
+
*/
|
|
21
|
+
export class GraphRunner {
|
|
22
|
+
private system: System;
|
|
23
|
+
public readonly store: StoreApi<GraphRunnerClientStore>;
|
|
24
|
+
/** runId -> the controller that started it. */
|
|
25
|
+
public readonly runIndex = new Map<string, GraphRunController>();
|
|
26
|
+
|
|
27
|
+
constructor(system: System, store: StoreApi<GraphRunnerClientStore>) {
|
|
28
|
+
this.system = system;
|
|
29
|
+
this.store = store;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
registerRun(runId: string, controller: GraphRunController): void {
|
|
33
|
+
this.runIndex.set(runId, controller);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
unregisterRun(runId: string): void {
|
|
37
|
+
this.runIndex.delete(runId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Connect to the graph runner server
|
|
42
|
+
*/
|
|
43
|
+
async connect(): Promise<void> {
|
|
44
|
+
const {
|
|
45
|
+
client,
|
|
46
|
+
connectionConfig,
|
|
47
|
+
setClient,
|
|
48
|
+
setConnectionState,
|
|
49
|
+
setConnectionInfo,
|
|
50
|
+
setError,
|
|
51
|
+
clearServerMetadata,
|
|
52
|
+
addMessageActivity
|
|
53
|
+
} = this.store.getState();
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
setError(null);
|
|
57
|
+
setConnectionState('connecting');
|
|
58
|
+
|
|
59
|
+
const theClient =
|
|
60
|
+
client ??
|
|
61
|
+
new GraphRunnerClient({
|
|
62
|
+
url: connectionConfig.url,
|
|
63
|
+
auth: connectionConfig.auth,
|
|
64
|
+
autoReconnect: connectionConfig.autoReconnect,
|
|
65
|
+
onMessageActivity: (direction, message) => {
|
|
66
|
+
addMessageActivity(direction, message);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
setClient(theClient);
|
|
71
|
+
|
|
72
|
+
await theClient.connect();
|
|
73
|
+
|
|
74
|
+
// Setup persistent event listeners for trace, logs, and run completion.
|
|
75
|
+
// Messages are routed to the originating session by run id.
|
|
76
|
+
setupClientEventListeners(theClient, this);
|
|
77
|
+
|
|
78
|
+
setConnectionState('connected');
|
|
79
|
+
setConnectionInfo({
|
|
80
|
+
serverId: theClient.getServerId(),
|
|
81
|
+
userId: theClient.getUserId(),
|
|
82
|
+
sessionId: null,
|
|
83
|
+
authenticated: theClient.isAuthenticated(),
|
|
84
|
+
capabilities: null
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Create session
|
|
88
|
+
const sessionId = await theClient.createSession();
|
|
89
|
+
setConnectionInfo({ sessionId });
|
|
90
|
+
|
|
91
|
+
// Get capabilities
|
|
92
|
+
const capabilities = await theClient.getCapabilities();
|
|
93
|
+
setConnectionInfo({ capabilities });
|
|
94
|
+
|
|
95
|
+
// Fetch metadata if supported
|
|
96
|
+
if (capabilities.runtimeMetadata) {
|
|
97
|
+
await this.refreshMetadata();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Notify successful connection
|
|
101
|
+
this.system.notifications.success(
|
|
102
|
+
`Connected to ${theClient.getServerId() || 'graph runner server'}`
|
|
103
|
+
);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
setConnectionState('disconnected');
|
|
106
|
+
const errorMessage =
|
|
107
|
+
error instanceof Error ? error.message : String(error);
|
|
108
|
+
setError(errorMessage);
|
|
109
|
+
setClient(null);
|
|
110
|
+
clearServerMetadata();
|
|
111
|
+
|
|
112
|
+
// Notify connection error
|
|
113
|
+
this.system.notifications.error(`Failed to connect: ${errorMessage}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Disconnect from the graph runner server
|
|
119
|
+
*/
|
|
120
|
+
async disconnect(): Promise<void> {
|
|
121
|
+
const {
|
|
122
|
+
client,
|
|
123
|
+
setClient,
|
|
124
|
+
setConnectionState,
|
|
125
|
+
setConnectionInfo,
|
|
126
|
+
clearServerMetadata
|
|
127
|
+
} = this.store.getState();
|
|
128
|
+
|
|
129
|
+
console.log('Disconnecting from graph runner server...');
|
|
130
|
+
if (client) {
|
|
131
|
+
try {
|
|
132
|
+
await client.closeSession();
|
|
133
|
+
client.disconnect();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Error disconnecting:', error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setClient(null);
|
|
140
|
+
setConnectionState('disconnected');
|
|
141
|
+
setConnectionInfo({
|
|
142
|
+
serverId: null,
|
|
143
|
+
userId: null,
|
|
144
|
+
sessionId: null,
|
|
145
|
+
authenticated: false,
|
|
146
|
+
capabilities: null
|
|
147
|
+
});
|
|
148
|
+
clearServerMetadata();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Refresh server metadata (variables, events, node types)
|
|
153
|
+
*/
|
|
154
|
+
async refreshMetadata(): Promise<void> {
|
|
155
|
+
const {
|
|
156
|
+
client,
|
|
157
|
+
setServerVariables,
|
|
158
|
+
setServerEvents,
|
|
159
|
+
setNodeTypes,
|
|
160
|
+
setError
|
|
161
|
+
} = this.store.getState();
|
|
162
|
+
if (!client) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const capabilities = client.getCachedCapabilities();
|
|
168
|
+
if (!capabilities?.runtimeMetadata) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const [variables, events, nodeTypes] = await Promise.all([
|
|
173
|
+
client.getServerVariables(),
|
|
174
|
+
client.getServerEvents(),
|
|
175
|
+
client.getNodeTypes()
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
setServerVariables(variables);
|
|
179
|
+
setServerEvents(events);
|
|
180
|
+
setNodeTypes(nodeTypes);
|
|
181
|
+
|
|
182
|
+
setError(null);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
setError(error instanceof Error ? error.message : String(error));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management for local graph runner
|
|
3
|
+
* Allows customization of execution context and configuration per session
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { IRegistry } from '@kiberon-labs/behave-graph';
|
|
7
|
+
import type {
|
|
8
|
+
GraphRunnerCapabilities,
|
|
9
|
+
EventFilter
|
|
10
|
+
} from '../graphrunner/types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Session configuration for graph execution
|
|
14
|
+
*/
|
|
15
|
+
export interface SessionConfig {
|
|
16
|
+
/** Custom metadata attached to the session */
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
/** Default execution options for all runs in this session */
|
|
19
|
+
defaultExecutionOptions?: {
|
|
20
|
+
autoEnd?: boolean;
|
|
21
|
+
trace?: boolean;
|
|
22
|
+
eventFilter?: EventFilter;
|
|
23
|
+
maxExecutionTimeMs?: number;
|
|
24
|
+
};
|
|
25
|
+
/** Session-level execution settings */
|
|
26
|
+
executionSettings?: {
|
|
27
|
+
/** Delay between execution steps in milliseconds */
|
|
28
|
+
stepDelay?: number;
|
|
29
|
+
/** Execution speed multiplier (0.1 to 2.0) */
|
|
30
|
+
executionSpeed?: number;
|
|
31
|
+
/** Interval between tick events in milliseconds */
|
|
32
|
+
tickInterval?: number;
|
|
33
|
+
/** Maximum number of concurrent runs allowed */
|
|
34
|
+
maxConcurrentRuns?: number;
|
|
35
|
+
};
|
|
36
|
+
/** Custom hook for handling tick timing/delays */
|
|
37
|
+
tickStrategy?: () => Promise<void>;
|
|
38
|
+
/** Custom registry overrides for this session */
|
|
39
|
+
registryOverrides?: Partial<IRegistry>;
|
|
40
|
+
/** Session lifecycle hooks */
|
|
41
|
+
hooks?: SessionHooks;
|
|
42
|
+
/** Session-specific capabilities override */
|
|
43
|
+
capabilities?: Partial<GraphRunnerCapabilities>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Lifecycle hooks for session events
|
|
48
|
+
*/
|
|
49
|
+
export interface SessionHooks {
|
|
50
|
+
/** Called when the session is created */
|
|
51
|
+
onSessionCreated?: (session: Session) => void | Promise<void>;
|
|
52
|
+
/** Called when a run starts in this session */
|
|
53
|
+
onRunStarted?: (
|
|
54
|
+
session: Session,
|
|
55
|
+
runId: string,
|
|
56
|
+
graphId: string
|
|
57
|
+
) => void | Promise<void>;
|
|
58
|
+
/** Called when a run completes in this session */
|
|
59
|
+
onRunCompleted?: (
|
|
60
|
+
session: Session,
|
|
61
|
+
runId: string,
|
|
62
|
+
graphId: string,
|
|
63
|
+
result: unknown
|
|
64
|
+
) => void | Promise<void>;
|
|
65
|
+
/** Called when a run fails in this session */
|
|
66
|
+
onRunError?: (
|
|
67
|
+
session: Session,
|
|
68
|
+
runId: string,
|
|
69
|
+
graphId: string,
|
|
70
|
+
error: Error
|
|
71
|
+
) => void | Promise<void>;
|
|
72
|
+
/** Called when the session is closed */
|
|
73
|
+
onSessionClosed?: (session: Session) => void | Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Session state
|
|
78
|
+
*/
|
|
79
|
+
export interface Session {
|
|
80
|
+
/** Unique session identifier */
|
|
81
|
+
readonly sessionId: string;
|
|
82
|
+
/** Session expiration timestamp */
|
|
83
|
+
readonly expiresAt: number;
|
|
84
|
+
/** Session creation timestamp */
|
|
85
|
+
readonly createdAt: number;
|
|
86
|
+
/** Session configuration */
|
|
87
|
+
readonly config: SessionConfig;
|
|
88
|
+
/** Active run IDs in this session */
|
|
89
|
+
readonly activeRuns: Set<string>;
|
|
90
|
+
/** Custom session state (user-definable) */
|
|
91
|
+
state: Record<string, unknown>;
|
|
92
|
+
/** Session metadata */
|
|
93
|
+
metadata: Record<string, unknown>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Factory for creating sessions with custom configuration
|
|
98
|
+
*/
|
|
99
|
+
export interface SessionFactory {
|
|
100
|
+
/**
|
|
101
|
+
* Create a new session with optional configuration
|
|
102
|
+
*/
|
|
103
|
+
createSession(sessionId: string, config?: SessionConfig): Session;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Default session factory implementation
|
|
108
|
+
*/
|
|
109
|
+
export class DefaultSessionFactory implements SessionFactory {
|
|
110
|
+
private defaultConfig?: SessionConfig;
|
|
111
|
+
|
|
112
|
+
constructor(defaultConfig?: SessionConfig) {
|
|
113
|
+
this.defaultConfig = defaultConfig;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
createSession(sessionId: string, config?: SessionConfig): Session {
|
|
117
|
+
const mergedConfig: SessionConfig = {
|
|
118
|
+
...this.defaultConfig,
|
|
119
|
+
...config,
|
|
120
|
+
metadata: {
|
|
121
|
+
...this.defaultConfig?.metadata,
|
|
122
|
+
...config?.metadata
|
|
123
|
+
},
|
|
124
|
+
defaultExecutionOptions: {
|
|
125
|
+
...this.defaultConfig?.defaultExecutionOptions,
|
|
126
|
+
...config?.defaultExecutionOptions
|
|
127
|
+
},
|
|
128
|
+
executionSettings: {
|
|
129
|
+
...this.defaultConfig?.executionSettings,
|
|
130
|
+
...config?.executionSettings
|
|
131
|
+
},
|
|
132
|
+
hooks: {
|
|
133
|
+
...this.defaultConfig?.hooks,
|
|
134
|
+
...config?.hooks
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const session: Session = {
|
|
139
|
+
sessionId,
|
|
140
|
+
expiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours default
|
|
141
|
+
createdAt: Date.now(),
|
|
142
|
+
config: mergedConfig,
|
|
143
|
+
activeRuns: new Set(),
|
|
144
|
+
state: {},
|
|
145
|
+
metadata: mergedConfig.metadata ?? {}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Call creation hook
|
|
149
|
+
if (mergedConfig.hooks?.onSessionCreated) {
|
|
150
|
+
void mergedConfig.hooks.onSessionCreated(session);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return session;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Session manager to track and manage multiple sessions
|
|
159
|
+
*/
|
|
160
|
+
export class SessionManager {
|
|
161
|
+
private sessions = new Map<string, Session>();
|
|
162
|
+
private sessionFactory: SessionFactory;
|
|
163
|
+
|
|
164
|
+
constructor(sessionFactory?: SessionFactory) {
|
|
165
|
+
this.sessionFactory = sessionFactory ?? new DefaultSessionFactory();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create a new session
|
|
170
|
+
*/
|
|
171
|
+
createSession(sessionId: string, config?: SessionConfig): Session {
|
|
172
|
+
const session = this.sessionFactory.createSession(sessionId, config);
|
|
173
|
+
this.sessions.set(sessionId, session);
|
|
174
|
+
return session;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get an existing session
|
|
179
|
+
*/
|
|
180
|
+
getSession(sessionId: string): Session | undefined {
|
|
181
|
+
return this.sessions.get(sessionId);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if a session exists
|
|
186
|
+
*/
|
|
187
|
+
hasSession(sessionId: string): boolean {
|
|
188
|
+
return this.sessions.has(sessionId);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Close and remove a session
|
|
193
|
+
*/
|
|
194
|
+
async closeSession(sessionId: string): Promise<void> {
|
|
195
|
+
const session = this.sessions.get(sessionId);
|
|
196
|
+
if (session) {
|
|
197
|
+
// Call close hook
|
|
198
|
+
if (session.config.hooks?.onSessionClosed) {
|
|
199
|
+
await session.config.hooks.onSessionClosed(session);
|
|
200
|
+
}
|
|
201
|
+
this.sessions.delete(sessionId);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Add a run to a session
|
|
207
|
+
*/
|
|
208
|
+
addRunToSession(sessionId: string, runId: string): void {
|
|
209
|
+
const session = this.sessions.get(sessionId);
|
|
210
|
+
if (session) {
|
|
211
|
+
session.activeRuns.add(runId);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Remove a run from a session
|
|
217
|
+
*/
|
|
218
|
+
removeRunFromSession(sessionId: string, runId: string): void {
|
|
219
|
+
const session = this.sessions.get(sessionId);
|
|
220
|
+
if (session) {
|
|
221
|
+
session.activeRuns.delete(runId);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get all active sessions
|
|
227
|
+
*/
|
|
228
|
+
getActiveSessions(): Session[] {
|
|
229
|
+
return Array.from(this.sessions.values());
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Clean up expired sessions
|
|
234
|
+
*/
|
|
235
|
+
cleanupExpiredSessions(): void {
|
|
236
|
+
const now = Date.now();
|
|
237
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
238
|
+
if (session.expiresAt < now) {
|
|
239
|
+
void this.closeSession(sessionId);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|