@bian-womp/spark-workbench 0.3.5 → 0.3.7

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/lib/cjs/index.cjs CHANGED
@@ -163,6 +163,23 @@ class InMemoryWorkbench extends AbstractWorkbench {
163
163
  this._registry = registry;
164
164
  this.emit("registryChanged", { registry });
165
165
  }
166
+ /**
167
+ * Check if a node should auto run based on registry node description policy or node definition params policy.
168
+ * @param nodeId - The node ID to check
169
+ * @returns true if the node has autoRun enabled in either registry or params policy
170
+ */
171
+ shouldNodeAutoRun(nodeId) {
172
+ const node = this._def.nodes.find((n) => n.nodeId === nodeId);
173
+ if (!node)
174
+ return false;
175
+ // Check registry node description policy
176
+ const desc = this._registry.nodes.get(node.typeId);
177
+ const registryAutoRun = desc?.policy?.autoRun === true;
178
+ // Check node definition params policy (takes precedence over registry)
179
+ const paramsAutoRun = node?.params?.policy?.autoRun === true;
180
+ // Node has autoRun if either registry or params sets it
181
+ return registryAutoRun || paramsAutoRun;
182
+ }
166
183
  async load(def) {
167
184
  this._def = { nodes: [...def.nodes], edges: [...def.edges] };
168
185
  if (this.layout) {
@@ -1436,6 +1453,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
1436
1453
  displayName: n.displayName,
1437
1454
  inputs: n.inputs || {},
1438
1455
  outputs: n.outputs || {},
1456
+ policy: n.policy || {},
1439
1457
  impl: () => { },
1440
1458
  });
1441
1459
  }
@@ -4513,8 +4531,18 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
4513
4531
  setRunModeState(mode);
4514
4532
  }, [runMode, runner]);
4515
4533
  const runNodeAction = React.useCallback(async (nodeId) => {
4516
- await runner.computeNode(nodeId);
4517
- }, [runner]);
4534
+ // Check if we're in auto mode or if node has autoRun policy
4535
+ const nodeHasAutoRun = wb.shouldNodeAutoRun(nodeId);
4536
+ const shouldUseTriggerExternal = runMode === "auto" || nodeHasAutoRun;
4537
+ if (shouldUseTriggerExternal) {
4538
+ // In auto mode or when node has autoRun, use triggerExternal with invalidate event
4539
+ await runner.triggerExternal(nodeId, { action: "invalidate" });
4540
+ }
4541
+ else {
4542
+ // In manual mode without autoRun, use computeNode
4543
+ await runner.computeNode(nodeId);
4544
+ }
4545
+ }, [runner, wb, runMode]);
4518
4546
  const runFromHereAction = React.useCallback(async (nodeId) => {
4519
4547
  await runner.runFromHere(nodeId);
4520
4548
  }, [runner]);
@@ -4736,16 +4764,16 @@ function DefaultNodeHeader({ id, typeId, validation, right, showId, }) {
4736
4764
  return (jsxRuntime.jsxs("div", { className: "flex items-center justify-center px-2 border-b border-solid border-gray-500 dark:border-gray-400 text-gray-600 dark:text-gray-300", style: {
4737
4765
  maxHeight: NODE_HEADER_HEIGHT_PX,
4738
4766
  minHeight: NODE_HEADER_HEIGHT_PX,
4739
- }, children: [isEditing ? (jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: editValue, onChange: (e) => setEditValue(e.target.value), onBlur: handleSave, onKeyDown: handleKeyDown, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), className: "flex-1 h-full text-sm bg-transparent border border-blue-500 rounded px-1 outline-none wb-nodrag", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` } })) : (jsxRuntime.jsx("strong", { className: `react-flow__node-title flex-1 h-full text-sm select-none truncate ${typeId ? "cursor-text" : ""}`, style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, onDoubleClick: handleDoubleClick, title: typeId ? "Double-click to rename" : undefined, children: displayName })), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [ctx.runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { onClick: (e) => {
4740
- e.stopPropagation();
4741
- ctx.abortNode(id);
4742
- }, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 transition-colors", title: "Abort node", children: jsxRuntime.jsx(react$1.StopIcon, { size: 10, weight: "fill" }) }), jsxRuntime.jsx("button", { onClick: (e) => {
4767
+ }, children: [isEditing ? (jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: editValue, onChange: (e) => setEditValue(e.target.value), onBlur: handleSave, onKeyDown: handleKeyDown, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), className: "flex-1 h-full text-sm bg-transparent border border-blue-500 rounded px-1 outline-none wb-nodrag", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` } })) : (jsxRuntime.jsx("strong", { className: `react-flow__node-title flex-1 h-full text-sm select-none truncate ${typeId ? "cursor-text" : ""}`, style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, onDoubleClick: handleDoubleClick, title: typeId ? "Double-click to rename" : undefined, children: displayName })), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("button", { onClick: (e) => {
4768
+ e.stopPropagation();
4769
+ void ctx.runNode(id);
4770
+ }, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-green-600 dark:hover:text-green-400 transition-colors", title: "Run node", children: jsxRuntime.jsx(react$1.PlayIcon, { size: 10, weight: "fill" }) }), ctx.runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { onClick: (e) => {
4743
4771
  e.stopPropagation();
4744
4772
  void ctx.runFromHere(id);
4745
- }, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors", title: "Run from here", children: jsxRuntime.jsx(react$1.PlayIcon, { size: 10, weight: "fill" }) }), jsxRuntime.jsx("button", { onClick: (e) => {
4773
+ }, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors", title: "Run from here", children: jsxRuntime.jsx(react$1.FastForwardIcon, { size: 10, weight: "fill" }) }), jsxRuntime.jsx("button", { onClick: (e) => {
4746
4774
  e.stopPropagation();
4747
- void ctx.runNode(id);
4748
- }, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-green-600 dark:hover:text-green-400 transition-colors", title: "Run node", children: jsxRuntime.jsx(react$1.Circle, { size: 10, weight: "fill" }) })] })), right, validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
4775
+ ctx.abortNode(id);
4776
+ }, className: "w-4 h-4 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 rounded text-gray-600 dark:text-gray-400 hover:text-red-600 dark:hover:text-red-400 transition-colors", title: "Abort node", children: jsxRuntime.jsx(react$1.StopIcon, { size: 10, weight: "fill" }) })] })), right, validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
4749
4777
  ? "error"
4750
4778
  : "warning", size: 12, className: "w-3 h-3", title: validation.issues
4751
4779
  .map((v) => `${v.code}: ${v.message}`)
@@ -5556,6 +5584,10 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
5556
5584
  const isStartNode = wb
5557
5585
  ? !wb.def.edges.some((e) => e.target.nodeId === nodeId)
5558
5586
  : false;
5587
+ // Check if node has outbound edges (required for "Run workflow" / "Run from here")
5588
+ const hasOutboundEdges = wb
5589
+ ? wb.def.edges.some((e) => e.source.nodeId === nodeId)
5590
+ : false;
5559
5591
  // clamp
5560
5592
  const MENU_MIN_WIDTH = 180;
5561
5593
  const PADDING = 16;
@@ -5565,7 +5597,7 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
5565
5597
  return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
5566
5598
  e.preventDefault();
5567
5599
  e.stopPropagation();
5568
- }, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: isStartNode ? "Run workflow" : "Run from here" }))] })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
5600
+ }, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), runMode === "manual" && handlers.onRunFromHere && hasOutboundEdges && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: isStartNode ? "Run workflow" : "Run from here" })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
5569
5601
  }
5570
5602
 
5571
5603
  function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcuts = true, keyboardShortcuts = {