clementine-agent 1.2.3 → 1.3.1
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/dist/cli/dashboard.js +2554 -680
- package/dist/cli/static/LICENSE-NOTICES.md +12 -0
- package/dist/cli/static/drawflow.min.css +1 -0
- package/dist/cli/static/drawflow.min.js +1 -0
- package/dist/dashboard/builder/dry-run.d.ts +31 -0
- package/dist/dashboard/builder/dry-run.js +138 -0
- package/dist/dashboard/builder/events.d.ts +23 -0
- package/dist/dashboard/builder/events.js +28 -0
- package/dist/dashboard/builder/mcp-invoke.d.ts +25 -0
- package/dist/dashboard/builder/mcp-invoke.js +143 -0
- package/dist/dashboard/builder/runner.d.ts +68 -0
- package/dist/dashboard/builder/runner.js +418 -0
- package/dist/dashboard/builder/serializer.d.ts +79 -0
- package/dist/dashboard/builder/serializer.js +547 -0
- package/dist/dashboard/builder/snapshots.d.ts +32 -0
- package/dist/dashboard/builder/snapshots.js +138 -0
- package/dist/dashboard/builder/validation.d.ts +26 -0
- package/dist/dashboard/builder/validation.js +183 -0
- package/dist/gateway/router.js +31 -2
- package/dist/index.js +18 -0
- package/dist/tools/builder-tools.d.ts +13 -0
- package/dist/tools/builder-tools.js +437 -0
- package/dist/tools/mcp-server.js +2 -0
- package/dist/types.d.ts +46 -0
- package/package.json +2 -2
- package/vault/00-System/skills/builder-canvas.md +126 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Vendored static assets
|
|
2
|
+
|
|
3
|
+
## Drawflow
|
|
4
|
+
- File: `drawflow.min.js`, `drawflow.min.css`
|
|
5
|
+
- Version: 0.0.59
|
|
6
|
+
- License: MIT
|
|
7
|
+
- Source: https://github.com/jerosoler/Drawflow
|
|
8
|
+
- Used by: dashboard Builder page (visual workflow canvas)
|
|
9
|
+
|
|
10
|
+
The minified files are vendored verbatim from the official jsdelivr CDN
|
|
11
|
+
for reproducibility. To upgrade, replace both files and update the
|
|
12
|
+
version line above.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.drawflow,.drawflow .parent-node{position:relative}.parent-drawflow{display:flex;overflow:hidden;touch-action:none;outline:0}.drawflow{width:100%;height:100%;user-select:none;perspective:0}.drawflow .drawflow-node{display:flex;align-items:center;position:absolute;background:#0ff;width:160px;min-height:40px;border-radius:4px;border:2px solid #000;color:#000;z-index:2;padding:15px}.drawflow .drawflow-node.selected{background:red}.drawflow .drawflow-node:hover{cursor:move}.drawflow .drawflow-node .inputs,.drawflow .drawflow-node .outputs{width:0}.drawflow .drawflow-node .drawflow_content_node{width:100%;display:block}.drawflow .drawflow-node .input,.drawflow .drawflow-node .output{position:relative;width:20px;height:20px;background:#fff;border-radius:50%;border:2px solid #000;cursor:crosshair;z-index:1;margin-bottom:5px}.drawflow .drawflow-node .input{left:-27px;top:2px;background:#ff0}.drawflow .drawflow-node .output{right:-3px;top:2px}.drawflow svg{z-index:0;position:absolute;overflow:visible!important}.drawflow .connection{position:absolute;pointer-events:none;aspect-ratio:1/1}.drawflow .connection .main-path{fill:none;stroke-width:5px;stroke:#4682b4;pointer-events:all}.drawflow .connection .main-path:hover{stroke:#1266ab;cursor:pointer}.drawflow .connection .main-path.selected{stroke:#43b993}.drawflow .connection .point{cursor:move;stroke:#000;stroke-width:2;fill:#fff;pointer-events:all}.drawflow .connection .point.selected,.drawflow .connection .point:hover{fill:#1266ab}.drawflow .main-path{fill:none;stroke-width:5px;stroke:#4682b4}.drawflow-delete{position:absolute;display:block;width:30px;height:30px;background:#000;color:#fff;z-index:4;border:2px solid #fff;line-height:30px;font-weight:700;text-align:center;border-radius:50%;font-family:monospace;cursor:pointer}.drawflow>.drawflow-delete{margin-left:-15px;margin-top:15px}.parent-node .drawflow-delete{right:-15px;top:-15px}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Drawflow=t():e.Drawflow=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var s=t[i]={i:i,l:!1,exports:{}};return e[i].call(s.exports,s,s.exports,n),s.l=!0,s.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(i,s,function(t){return e[t]}.bind(null,s));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t),n.d(t,"default",(function(){return i}));class i{constructor(e,t=null,n=null){this.events={},this.container=e,this.precanvas=null,this.nodeId=1,this.ele_selected=null,this.node_selected=null,this.drag=!1,this.reroute=!1,this.reroute_fix_curvature=!1,this.curvature=.5,this.reroute_curvature_start_end=.5,this.reroute_curvature=.5,this.reroute_width=6,this.drag_point=!1,this.editor_selected=!1,this.connection=!1,this.connection_ele=null,this.connection_selected=null,this.canvas_x=0,this.canvas_y=0,this.pos_x=0,this.pos_x_start=0,this.pos_y=0,this.pos_y_start=0,this.mouse_x=0,this.mouse_y=0,this.line_path=5,this.first_click=null,this.force_first_input=!1,this.draggable_inputs=!0,this.useuuid=!1,this.parent=n,this.noderegister={},this.render=t,this.drawflow={drawflow:{Home:{data:{}}}},this.module="Home",this.editor_mode="edit",this.zoom=1,this.zoom_max=1.6,this.zoom_min=.5,this.zoom_value=.1,this.zoom_last_value=1,this.evCache=new Array,this.prevDiff=-1}start(){this.container.classList.add("parent-drawflow"),this.container.tabIndex=0,this.precanvas=document.createElement("div"),this.precanvas.classList.add("drawflow"),this.container.appendChild(this.precanvas),this.container.addEventListener("mouseup",this.dragEnd.bind(this)),this.container.addEventListener("mousemove",this.position.bind(this)),this.container.addEventListener("mousedown",this.click.bind(this)),this.container.addEventListener("touchend",this.dragEnd.bind(this)),this.container.addEventListener("touchmove",this.position.bind(this)),this.container.addEventListener("touchstart",this.click.bind(this)),this.container.addEventListener("contextmenu",this.contextmenu.bind(this)),this.container.addEventListener("keydown",this.key.bind(this)),this.container.addEventListener("wheel",this.zoom_enter.bind(this)),this.container.addEventListener("input",this.updateNodeValue.bind(this)),this.container.addEventListener("dblclick",this.dblclick.bind(this)),this.container.onpointerdown=this.pointerdown_handler.bind(this),this.container.onpointermove=this.pointermove_handler.bind(this),this.container.onpointerup=this.pointerup_handler.bind(this),this.container.onpointercancel=this.pointerup_handler.bind(this),this.container.onpointerout=this.pointerup_handler.bind(this),this.container.onpointerleave=this.pointerup_handler.bind(this),this.load()}pointerdown_handler(e){this.evCache.push(e)}pointermove_handler(e){for(var t=0;t<this.evCache.length;t++)if(e.pointerId==this.evCache[t].pointerId){this.evCache[t]=e;break}if(2==this.evCache.length){var n=Math.abs(this.evCache[0].clientX-this.evCache[1].clientX);this.prevDiff>100&&(n>this.prevDiff&&this.zoom_in(),n<this.prevDiff&&this.zoom_out()),this.prevDiff=n}}pointerup_handler(e){this.remove_event(e),this.evCache.length<2&&(this.prevDiff=-1)}remove_event(e){for(var t=0;t<this.evCache.length;t++)if(this.evCache[t].pointerId==e.pointerId){this.evCache.splice(t,1);break}}load(){for(var e in this.drawflow.drawflow[this.module].data)this.addNodeImport(this.drawflow.drawflow[this.module].data[e],this.precanvas);if(this.reroute)for(var e in this.drawflow.drawflow[this.module].data)this.addRerouteImport(this.drawflow.drawflow[this.module].data[e]);for(var e in this.drawflow.drawflow[this.module].data)this.updateConnectionNodes("node-"+e);const t=this.drawflow.drawflow;let n=1;Object.keys(t).map((function(e,i){Object.keys(t[e].data).map((function(e,t){parseInt(e)>=n&&(n=parseInt(e)+1)}))})),this.nodeId=n}removeReouteConnectionSelected(){this.dispatch("connectionUnselected",!0),this.reroute_fix_curvature&&this.connection_selected.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.remove("selected")})}click(e){if(this.dispatch("click",e),"fixed"===this.editor_mode){if(e.preventDefault(),"parent-drawflow"!==e.target.classList[0]&&"drawflow"!==e.target.classList[0])return!1;this.ele_selected=e.target.closest(".parent-drawflow")}else"view"===this.editor_mode?(null!=e.target.closest(".drawflow")||e.target.matches(".parent-drawflow"))&&(this.ele_selected=e.target.closest(".parent-drawflow"),e.preventDefault()):(this.first_click=e.target,this.ele_selected=e.target,0===e.button&&this.contextmenuDel(),null!=e.target.closest(".drawflow_content_node")&&(this.ele_selected=e.target.closest(".drawflow_content_node").parentElement));switch(this.ele_selected.classList[0]){case"drawflow-node":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected!=this.ele_selected&&this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.node_selected!=this.ele_selected&&this.dispatch("nodeSelected",this.ele_selected.id.slice(5)),this.node_selected=this.ele_selected,this.node_selected.classList.add("selected"),this.draggable_inputs?"SELECT"!==e.target.tagName&&(this.drag=!0):"INPUT"!==e.target.tagName&&"TEXTAREA"!==e.target.tagName&&"SELECT"!==e.target.tagName&&!0!==e.target.hasAttribute("contenteditable")&&(this.drag=!0);break;case"output":this.connection=!0,null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.drawConnection(e.target);break;case"parent-drawflow":case"drawflow":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.editor_selected=!0;break;case"main-path":null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null),this.connection_selected=this.ele_selected,this.connection_selected.classList.add("selected");const t=this.connection_selected.parentElement.classList;t.length>1&&(this.dispatch("connectionSelected",{output_id:t[2].slice(14),input_id:t[1].slice(13),output_class:t[3],input_class:t[4]}),this.reroute_fix_curvature&&this.connection_selected.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.add("selected")}));break;case"point":this.drag_point=!0,this.ele_selected.classList.add("selected");break;case"drawflow-delete":this.node_selected&&this.removeNodeId(this.node_selected.id),this.connection_selected&&this.removeConnection(),null!=this.node_selected&&(this.node_selected.classList.remove("selected"),this.node_selected=null,this.dispatch("nodeUnselected",!0)),null!=this.connection_selected&&(this.connection_selected.classList.remove("selected"),this.removeReouteConnectionSelected(),this.connection_selected=null)}"touchstart"===e.type?(this.pos_x=e.touches[0].clientX,this.pos_x_start=e.touches[0].clientX,this.pos_y=e.touches[0].clientY,this.pos_y_start=e.touches[0].clientY,this.mouse_x=e.touches[0].clientX,this.mouse_y=e.touches[0].clientY):(this.pos_x=e.clientX,this.pos_x_start=e.clientX,this.pos_y=e.clientY,this.pos_y_start=e.clientY),["input","output","main-path"].includes(this.ele_selected.classList[0])&&e.preventDefault(),this.dispatch("clickEnd",e)}position(e){if("touchmove"===e.type)var t=e.touches[0].clientX,n=e.touches[0].clientY;else t=e.clientX,n=e.clientY;if(this.connection&&this.updateConnection(t,n),this.editor_selected&&(i=this.canvas_x+-(this.pos_x-t),s=this.canvas_y+-(this.pos_y-n),this.dispatch("translate",{x:i,y:s}),this.precanvas.style.transform="translate("+i+"px, "+s+"px) scale("+this.zoom+")"),this.drag){e.preventDefault();var i=(this.pos_x-t)*this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom),s=(this.pos_y-n)*this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom);this.pos_x=t,this.pos_y=n,this.ele_selected.style.top=this.ele_selected.offsetTop-s+"px",this.ele_selected.style.left=this.ele_selected.offsetLeft-i+"px",this.drawflow.drawflow[this.module].data[this.ele_selected.id.slice(5)].pos_x=this.ele_selected.offsetLeft-i,this.drawflow.drawflow[this.module].data[this.ele_selected.id.slice(5)].pos_y=this.ele_selected.offsetTop-s,this.updateConnectionNodes(this.ele_selected.id)}if(this.drag_point){i=(this.pos_x-t)*this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom),s=(this.pos_y-n)*this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom);this.pos_x=t,this.pos_y=n;var o=this.pos_x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),l=this.pos_y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom));this.ele_selected.setAttributeNS(null,"cx",o),this.ele_selected.setAttributeNS(null,"cy",l);const e=this.ele_selected.parentElement.classList[2].slice(9),c=this.ele_selected.parentElement.classList[1].slice(13),d=this.ele_selected.parentElement.classList[3],a=this.ele_selected.parentElement.classList[4];let r=Array.from(this.ele_selected.parentElement.children).indexOf(this.ele_selected)-1;if(this.reroute_fix_curvature){r-=this.ele_selected.parentElement.querySelectorAll(".main-path").length-1,r<0&&(r=0)}const h=e.slice(5),u=this.drawflow.drawflow[this.module].data[h].outputs[d].connections.findIndex((function(e,t){return e.node===c&&e.output===a}));this.drawflow.drawflow[this.module].data[h].outputs[d].connections[u].points[r]={pos_x:o,pos_y:l};const p=this.ele_selected.parentElement.classList[2].slice(9);this.updateConnectionNodes(p)}"touchmove"===e.type&&(this.mouse_x=t,this.mouse_y=n),this.dispatch("mouseMove",{x:t,y:n})}dragEnd(e){if("touchend"===e.type)var t=this.mouse_x,n=this.mouse_y,i=document.elementFromPoint(t,n);else t=e.clientX,n=e.clientY,i=e.target;if(this.drag&&(this.pos_x_start==t&&this.pos_y_start==n||this.dispatch("nodeMoved",this.ele_selected.id.slice(5))),this.drag_point&&(this.ele_selected.classList.remove("selected"),this.pos_x_start==t&&this.pos_y_start==n||this.dispatch("rerouteMoved",this.ele_selected.parentElement.classList[2].slice(14))),this.editor_selected&&(this.canvas_x=this.canvas_x+-(this.pos_x-t),this.canvas_y=this.canvas_y+-(this.pos_y-n),this.editor_selected=!1),!0===this.connection)if("input"===i.classList[0]||this.force_first_input&&(null!=i.closest(".drawflow_content_node")||"drawflow-node"===i.classList[0])){if(!this.force_first_input||null==i.closest(".drawflow_content_node")&&"drawflow-node"!==i.classList[0])s=i.parentElement.parentElement.id,o=i.classList[1];else{if(null!=i.closest(".drawflow_content_node"))var s=i.closest(".drawflow_content_node").parentElement.id;else var s=i.id;if(0===Object.keys(this.getNodeFromId(s.slice(5)).inputs).length)var o=!1;else var o="input_1"}var l=this.ele_selected.parentElement.parentElement.id,c=this.ele_selected.classList[1];if(l!==s&&!1!==o){if(0===this.container.querySelectorAll(".connection.node_in_"+s+".node_out_"+l+"."+c+"."+o).length){this.connection_ele.classList.add("node_in_"+s),this.connection_ele.classList.add("node_out_"+l),this.connection_ele.classList.add(c),this.connection_ele.classList.add(o);var d=s.slice(5),a=l.slice(5);this.drawflow.drawflow[this.module].data[a].outputs[c].connections.push({node:d,output:o}),this.drawflow.drawflow[this.module].data[d].inputs[o].connections.push({node:a,input:c}),this.updateConnectionNodes("node-"+a),this.updateConnectionNodes("node-"+d),this.dispatch("connectionCreated",{output_id:a,input_id:d,output_class:c,input_class:o})}else this.dispatch("connectionCancel",!0),this.connection_ele.remove();this.connection_ele=null}else this.dispatch("connectionCancel",!0),this.connection_ele.remove(),this.connection_ele=null}else this.dispatch("connectionCancel",!0),this.connection_ele.remove(),this.connection_ele=null;this.drag=!1,this.drag_point=!1,this.connection=!1,this.ele_selected=null,this.editor_selected=!1,this.dispatch("mouseUp",e)}contextmenu(e){if(this.dispatch("contextmenu",e),e.preventDefault(),"fixed"===this.editor_mode||"view"===this.editor_mode)return!1;if(this.precanvas.getElementsByClassName("drawflow-delete").length&&this.precanvas.getElementsByClassName("drawflow-delete")[0].remove(),this.node_selected||this.connection_selected){var t=document.createElement("div");t.classList.add("drawflow-delete"),t.innerHTML="x",this.node_selected&&this.node_selected.appendChild(t),this.connection_selected&&this.connection_selected.parentElement.classList.length>1&&(t.style.top=e.clientY*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))+"px",t.style.left=e.clientX*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))+"px",this.precanvas.appendChild(t))}}contextmenuDel(){this.precanvas.getElementsByClassName("drawflow-delete").length&&this.precanvas.getElementsByClassName("drawflow-delete")[0].remove()}key(e){if(this.dispatch("keydown",e),"fixed"===this.editor_mode||"view"===this.editor_mode)return!1;("Delete"===e.key||"Backspace"===e.key&&e.metaKey)&&(null!=this.node_selected&&"INPUT"!==this.first_click.tagName&&"TEXTAREA"!==this.first_click.tagName&&!0!==this.first_click.hasAttribute("contenteditable")&&this.removeNodeId(this.node_selected.id),null!=this.connection_selected&&this.removeConnection())}zoom_enter(e,t){e.ctrlKey&&(e.preventDefault(),e.deltaY>0?this.zoom_out():this.zoom_in())}zoom_refresh(){this.dispatch("zoom",this.zoom),this.canvas_x=this.canvas_x/this.zoom_last_value*this.zoom,this.canvas_y=this.canvas_y/this.zoom_last_value*this.zoom,this.zoom_last_value=this.zoom,this.precanvas.style.transform="translate("+this.canvas_x+"px, "+this.canvas_y+"px) scale("+this.zoom+")"}zoom_in(){this.zoom<this.zoom_max&&(this.zoom+=this.zoom_value,this.zoom_refresh())}zoom_out(){this.zoom>this.zoom_min&&(this.zoom-=this.zoom_value,this.zoom_refresh())}zoom_reset(){1!=this.zoom&&(this.zoom=1,this.zoom_refresh())}createCurvature(e,t,n,i,s,o){var l=e,c=t,d=n,a=i,r=s;switch(o){case"open":if(e>=n)var h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*(-1*r);else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;case"close":if(e>=n)h=l+Math.abs(d-l)*(-1*r),u=d-Math.abs(d-l)*r;else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;case"other":if(e>=n)h=l+Math.abs(d-l)*(-1*r),u=d-Math.abs(d-l)*(-1*r);else h=l+Math.abs(d-l)*r,u=d-Math.abs(d-l)*r;return" M "+l+" "+c+" C "+h+" "+c+" "+u+" "+a+" "+d+" "+a;default:return" M "+l+" "+c+" C "+(h=l+Math.abs(d-l)*r)+" "+c+" "+(u=d-Math.abs(d-l)*r)+" "+a+" "+d+" "+a}}drawConnection(e){var t=document.createElementNS("http://www.w3.org/2000/svg","svg");this.connection_ele=t;var n=document.createElementNS("http://www.w3.org/2000/svg","path");n.classList.add("main-path"),n.setAttributeNS(null,"d",""),t.classList.add("connection"),t.appendChild(n),this.precanvas.appendChild(t);var i=e.parentElement.parentElement.id.slice(5),s=e.classList[1];this.dispatch("connectionStart",{output_id:i,output_class:s})}updateConnection(e,t){const n=this.precanvas,i=this.zoom;let s=n.clientWidth/(n.clientWidth*i);s=s||0;let o=n.clientHeight/(n.clientHeight*i);o=o||0;var l=this.connection_ele.children[0],c=this.ele_selected.offsetWidth/2+(this.ele_selected.getBoundingClientRect().x-n.getBoundingClientRect().x)*s,d=this.ele_selected.offsetHeight/2+(this.ele_selected.getBoundingClientRect().y-n.getBoundingClientRect().y)*o,a=e*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),r=t*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom)),h=this.curvature,u=this.createCurvature(c,d,a,r,h,"openclose");l.setAttributeNS(null,"d",u)}addConnection(e,t,n,i){var s=this.getModuleFromNodeId(e);if(s===this.getModuleFromNodeId(t)){var o=this.getNodeFromId(e),l=!1;for(var c in o.outputs[n].connections){var d=o.outputs[n].connections[c];d.node==t&&d.output==i&&(l=!0)}if(!1===l){if(this.drawflow.drawflow[s].data[e].outputs[n].connections.push({node:t.toString(),output:i}),this.drawflow.drawflow[s].data[t].inputs[i].connections.push({node:e.toString(),input:n}),this.module===s){var a=document.createElementNS("http://www.w3.org/2000/svg","svg"),r=document.createElementNS("http://www.w3.org/2000/svg","path");r.classList.add("main-path"),r.setAttributeNS(null,"d",""),a.classList.add("connection"),a.classList.add("node_in_node-"+t),a.classList.add("node_out_node-"+e),a.classList.add(n),a.classList.add(i),a.appendChild(r),this.precanvas.appendChild(a),this.updateConnectionNodes("node-"+e),this.updateConnectionNodes("node-"+t)}this.dispatch("connectionCreated",{output_id:e,input_id:t,output_class:n,input_class:i})}}}updateConnectionNodes(e){const t="node_in_"+e,n="node_out_"+e;this.line_path;const i=this.container,s=this.precanvas,o=this.curvature,l=this.createCurvature,c=this.reroute_curvature,d=this.reroute_curvature_start_end,a=this.reroute_fix_curvature,r=this.reroute_width,h=this.zoom;let u=s.clientWidth/(s.clientWidth*h);u=u||0;let p=s.clientHeight/(s.clientHeight*h);p=p||0;const f=i.querySelectorAll("."+n);Object.keys(f).map((function(t,n){if(null===f[t].querySelector(".point")){var m=i.querySelector("#"+e),g=f[t].classList[1].replace("node_in_",""),_=i.querySelector("#"+g).querySelectorAll("."+f[t].classList[4])[0],w=_.offsetWidth/2+(_.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=_.offsetHeight/2+(_.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=m.querySelectorAll("."+f[t].classList[3])[0],C=y.offsetWidth/2+(y.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,x=y.offsetHeight/2+(y.getBoundingClientRect().y-s.getBoundingClientRect().y)*p;const n=l(C,x,w,v,o,"openclose");f[t].children[0].setAttributeNS(null,"d",n)}else{const n=f[t].querySelectorAll(".point");let o="";const m=[];n.forEach((t,a)=>{if(0===a&&n.length-1==0){var f=i.querySelector("#"+e),g=((x=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(L=f.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(L.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=L.offsetHeight/2+(L.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=l(w,v,g,_,d,"open");o+=y,m.push(y);f=t;var C=t.parentElement.classList[1].replace("node_in_",""),x=(E=i.querySelector("#"+C)).querySelectorAll("."+t.parentElement.classList[4])[0];g=(R=E.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(R.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,_=R.offsetHeight/2+(R.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,y=l(w,v,g,_,d,"close");o+=y,m.push(y)}else if(0===a){var L;f=i.querySelector("#"+e),g=((x=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(L=f.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(L.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,v=L.offsetHeight/2+(L.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,y=l(w,v,g,_,d,"open");o+=y,m.push(y);f=t,g=((x=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,y=l(w,v,g,_,c,"other");o+=y,m.push(y)}else if(a===n.length-1){var E,R;f=t,C=t.parentElement.classList[1].replace("node_in_",""),x=(E=i.querySelector("#"+C)).querySelectorAll("."+t.parentElement.classList[4])[0],g=(R=E.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(R.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,_=R.offsetHeight/2+(R.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,y=l(w,v,g,_,d,"close");o+=y,m.push(y)}else{f=t,g=((x=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,_=(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,w=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*(s.clientWidth/(s.clientWidth*h))+r,v=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*(s.clientHeight/(s.clientHeight*h))+r,y=l(w,v,g,_,c,"other");o+=y,m.push(y)}}),a?m.forEach((e,n)=>{f[t].children[n].setAttributeNS(null,"d",e)}):f[t].children[0].setAttributeNS(null,"d",o)}}));const m=i.querySelectorAll("."+t);Object.keys(m).map((function(t,n){if(null===m[t].querySelector(".point")){var h=i.querySelector("#"+e),f=m[t].classList[2].replace("node_out_",""),g=i.querySelector("#"+f).querySelectorAll("."+m[t].classList[3])[0],_=g.offsetWidth/2+(g.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=g.offsetHeight/2+(g.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,v=(h=h.querySelectorAll("."+m[t].classList[4])[0]).offsetWidth/2+(h.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,y=h.offsetHeight/2+(h.getBoundingClientRect().y-s.getBoundingClientRect().y)*p;const n=l(_,w,v,y,o,"openclose");m[t].children[0].setAttributeNS(null,"d",n)}else{const n=m[t].querySelectorAll(".point");let o="";const h=[];n.forEach((t,a)=>{if(0===a&&n.length-1==0){var f=i.querySelector("#"+e),m=((C=t).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,_=(E=f.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(E.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=E.offsetHeight/2+(E.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,v=l(m,g,_,w,d,"close");o+=v,h.push(v);f=t;var y=t.parentElement.classList[2].replace("node_out_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[3])[0];m=(x=L.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(x.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,g=x.offsetHeight/2+(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,_=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,d,"open");o+=v,h.push(v)}else if(0===a){var x;f=t,y=t.parentElement.classList[2].replace("node_out_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[3])[0],m=(x=L.querySelectorAll("."+t.parentElement.classList[3])[0]).offsetWidth/2+(x.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,g=x.offsetHeight/2+(x.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,_=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,d,"open");o+=v,h.push(v);f=t,_=((C=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,m=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,c,"other");o+=v,h.push(v)}else if(a===n.length-1){var L,E;f=t,y=t.parentElement.classList[1].replace("node_in_",""),C=(L=i.querySelector("#"+y)).querySelectorAll("."+t.parentElement.classList[4])[0],_=(E=L.querySelectorAll("."+t.parentElement.classList[4])[0]).offsetWidth/2+(E.getBoundingClientRect().x-s.getBoundingClientRect().x)*u,w=E.offsetHeight/2+(E.getBoundingClientRect().y-s.getBoundingClientRect().y)*p,m=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,d,"close");o+=v,h.push(v)}else{f=t,_=((C=n[a+1]).getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,w=(C.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,m=(f.getBoundingClientRect().x-s.getBoundingClientRect().x)*u+r,g=(f.getBoundingClientRect().y-s.getBoundingClientRect().y)*p+r,v=l(m,g,_,w,c,"other");o+=v,h.push(v)}}),a?h.forEach((e,n)=>{m[t].children[n].setAttributeNS(null,"d",e)}):m[t].children[0].setAttributeNS(null,"d",o)}}))}dblclick(e){null!=this.connection_selected&&this.reroute&&this.createReroutePoint(this.connection_selected),"point"===e.target.classList[0]&&this.removeReroutePoint(e.target)}createReroutePoint(e){this.connection_selected.classList.remove("selected");const t=this.connection_selected.parentElement.classList[2].slice(9),n=this.connection_selected.parentElement.classList[1].slice(13),i=this.connection_selected.parentElement.classList[3],s=this.connection_selected.parentElement.classList[4];this.connection_selected=null;const o=document.createElementNS("http://www.w3.org/2000/svg","circle");o.classList.add("point");var l=this.pos_x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom))-this.precanvas.getBoundingClientRect().x*(this.precanvas.clientWidth/(this.precanvas.clientWidth*this.zoom)),c=this.pos_y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom))-this.precanvas.getBoundingClientRect().y*(this.precanvas.clientHeight/(this.precanvas.clientHeight*this.zoom));o.setAttributeNS(null,"cx",l),o.setAttributeNS(null,"cy",c),o.setAttributeNS(null,"r",this.reroute_width);let d=0;if(this.reroute_fix_curvature){const t=e.parentElement.querySelectorAll(".main-path").length;var a=document.createElementNS("http://www.w3.org/2000/svg","path");if(a.classList.add("main-path"),a.setAttributeNS(null,"d",""),e.parentElement.insertBefore(a,e.parentElement.children[t]),1===t)e.parentElement.appendChild(o);else{const n=Array.from(e.parentElement.children).indexOf(e);d=n,e.parentElement.insertBefore(o,e.parentElement.children[n+t+1])}}else e.parentElement.appendChild(o);const r=t.slice(5),h=this.drawflow.drawflow[this.module].data[r].outputs[i].connections.findIndex((function(e,t){return e.node===n&&e.output===s}));void 0===this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points&&(this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points=[]),this.reroute_fix_curvature?(d>0||this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points!==[]?this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.splice(d,0,{pos_x:l,pos_y:c}):this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.push({pos_x:l,pos_y:c}),e.parentElement.querySelectorAll(".main-path").forEach((e,t)=>{e.classList.remove("selected")})):this.drawflow.drawflow[this.module].data[r].outputs[i].connections[h].points.push({pos_x:l,pos_y:c}),this.dispatch("addReroute",r),this.updateConnectionNodes(t)}removeReroutePoint(e){const t=e.parentElement.classList[2].slice(9),n=e.parentElement.classList[1].slice(13),i=e.parentElement.classList[3],s=e.parentElement.classList[4];let o=Array.from(e.parentElement.children).indexOf(e);const l=t.slice(5),c=this.drawflow.drawflow[this.module].data[l].outputs[i].connections.findIndex((function(e,t){return e.node===n&&e.output===s}));if(this.reroute_fix_curvature){const t=e.parentElement.querySelectorAll(".main-path").length;e.parentElement.children[t-1].remove(),o-=t,o<0&&(o=0)}else o--;this.drawflow.drawflow[this.module].data[l].outputs[i].connections[c].points.splice(o,1),e.remove(),this.dispatch("removeReroute",l),this.updateConnectionNodes(t)}registerNode(e,t,n=null,i=null){this.noderegister[e]={html:t,props:n,options:i}}getNodeFromId(e){var t=this.getModuleFromNodeId(e);return JSON.parse(JSON.stringify(this.drawflow.drawflow[t].data[e]))}getNodesFromName(e){var t=[];const n=this.drawflow.drawflow;return Object.keys(n).map((function(i,s){for(var o in n[i].data)n[i].data[o].name==e&&t.push(n[i].data[o].id)})),t}addNode(e,t,n,i,s,o,l,c,d=!1){if(this.useuuid)var a=this.getUuid();else a=this.nodeId;const r=document.createElement("div");r.classList.add("parent-node");const h=document.createElement("div");h.innerHTML="",h.setAttribute("id","node-"+a),h.classList.add("drawflow-node"),""!=o&&h.classList.add(...o.split(" "));const u=document.createElement("div");u.classList.add("inputs");const p=document.createElement("div");p.classList.add("outputs");const f={};for(var m=0;m<t;m++){const e=document.createElement("div");e.classList.add("input"),e.classList.add("input_"+(m+1)),f["input_"+(m+1)]={connections:[]},u.appendChild(e)}const g={};for(m=0;m<n;m++){const e=document.createElement("div");e.classList.add("output"),e.classList.add("output_"+(m+1)),g["output_"+(m+1)]={connections:[]},p.appendChild(e)}const _=document.createElement("div");if(_.classList.add("drawflow_content_node"),!1===d)_.innerHTML=c;else if(!0===d)_.appendChild(this.noderegister[c].html.cloneNode(!0));else if(3===parseInt(this.render.version)){let e=this.render.h(this.noderegister[c].html,this.noderegister[c].props,this.noderegister[c].options);e.appContext=this.parent,this.render.render(e,_)}else{let e=new this.render({parent:this.parent,render:e=>e(this.noderegister[c].html,{props:this.noderegister[c].props}),...this.noderegister[c].options}).$mount();_.appendChild(e.$el)}Object.entries(l).forEach((function(e,t){if("object"==typeof e[1])!function e(t,n,i){if(null===t)t=l[n];else t=t[n];null!==t&&Object.entries(t).forEach((function(n,s){if("object"==typeof n[1])e(t,n[0],i+"-"+n[0]);else for(var o=_.querySelectorAll("[df-"+i+"-"+n[0]+"]"),l=0;l<o.length;l++)o[l].value=n[1],o[l].isContentEditable&&(o[l].innerText=n[1])}))}(null,e[0],e[0]);else for(var n=_.querySelectorAll("[df-"+e[0]+"]"),i=0;i<n.length;i++)n[i].value=e[1],n[i].isContentEditable&&(n[i].innerText=e[1])})),h.appendChild(u),h.appendChild(_),h.appendChild(p),h.style.top=s+"px",h.style.left=i+"px",r.appendChild(h),this.precanvas.appendChild(r);var w={id:a,name:e,data:l,class:o,html:c,typenode:d,inputs:f,outputs:g,pos_x:i,pos_y:s};return this.drawflow.drawflow[this.module].data[a]=w,this.dispatch("nodeCreated",a),this.useuuid||this.nodeId++,a}addNodeImport(e,t){const n=document.createElement("div");n.classList.add("parent-node");const i=document.createElement("div");i.innerHTML="",i.setAttribute("id","node-"+e.id),i.classList.add("drawflow-node"),""!=e.class&&i.classList.add(...e.class.split(" "));const s=document.createElement("div");s.classList.add("inputs");const o=document.createElement("div");o.classList.add("outputs"),Object.keys(e.inputs).map((function(n,i){const o=document.createElement("div");o.classList.add("input"),o.classList.add(n),s.appendChild(o),Object.keys(e.inputs[n].connections).map((function(i,s){var o=document.createElementNS("http://www.w3.org/2000/svg","svg"),l=document.createElementNS("http://www.w3.org/2000/svg","path");l.classList.add("main-path"),l.setAttributeNS(null,"d",""),o.classList.add("connection"),o.classList.add("node_in_node-"+e.id),o.classList.add("node_out_node-"+e.inputs[n].connections[i].node),o.classList.add(e.inputs[n].connections[i].input),o.classList.add(n),o.appendChild(l),t.appendChild(o)}))}));for(var l=0;l<Object.keys(e.outputs).length;l++){const e=document.createElement("div");e.classList.add("output"),e.classList.add("output_"+(l+1)),o.appendChild(e)}const c=document.createElement("div");if(c.classList.add("drawflow_content_node"),!1===e.typenode)c.innerHTML=e.html;else if(!0===e.typenode)c.appendChild(this.noderegister[e.html].html.cloneNode(!0));else if(3===parseInt(this.render.version)){let t=this.render.h(this.noderegister[e.html].html,this.noderegister[e.html].props,this.noderegister[e.html].options);t.appContext=this.parent,this.render.render(t,c)}else{let t=new this.render({parent:this.parent,render:t=>t(this.noderegister[e.html].html,{props:this.noderegister[e.html].props}),...this.noderegister[e.html].options}).$mount();c.appendChild(t.$el)}Object.entries(e.data).forEach((function(t,n){if("object"==typeof t[1])!function t(n,i,s){if(null===n)n=e.data[i];else n=n[i];null!==n&&Object.entries(n).forEach((function(e,i){if("object"==typeof e[1])t(n,e[0],s+"-"+e[0]);else for(var o=c.querySelectorAll("[df-"+s+"-"+e[0]+"]"),l=0;l<o.length;l++)o[l].value=e[1],o[l].isContentEditable&&(o[l].innerText=e[1])}))}(null,t[0],t[0]);else for(var i=c.querySelectorAll("[df-"+t[0]+"]"),s=0;s<i.length;s++)i[s].value=t[1],i[s].isContentEditable&&(i[s].innerText=t[1])})),i.appendChild(s),i.appendChild(c),i.appendChild(o),i.style.top=e.pos_y+"px",i.style.left=e.pos_x+"px",n.appendChild(i),this.precanvas.appendChild(n)}addRerouteImport(e){const t=this.reroute_width,n=this.reroute_fix_curvature,i=this.container;Object.keys(e.outputs).map((function(s,o){Object.keys(e.outputs[s].connections).map((function(o,l){const c=e.outputs[s].connections[o].points;void 0!==c&&c.forEach((l,d)=>{const a=e.outputs[s].connections[o].node,r=e.outputs[s].connections[o].output,h=i.querySelector(".connection.node_in_node-"+a+".node_out_node-"+e.id+"."+s+"."+r);if(n&&0===d)for(var u=0;u<c.length;u++){var p=document.createElementNS("http://www.w3.org/2000/svg","path");p.classList.add("main-path"),p.setAttributeNS(null,"d",""),h.appendChild(p)}const f=document.createElementNS("http://www.w3.org/2000/svg","circle");f.classList.add("point");var m=l.pos_x,g=l.pos_y;f.setAttributeNS(null,"cx",m),f.setAttributeNS(null,"cy",g),f.setAttributeNS(null,"r",t),h.appendChild(f)})}))}))}updateNodeValue(e){for(var t=e.target.attributes,n=0;n<t.length;n++)if(t[n].nodeName.startsWith("df-")){for(var i=t[n].nodeName.slice(3).split("-"),s=this.drawflow.drawflow[this.module].data[e.target.closest(".drawflow_content_node").parentElement.id.slice(5)].data,o=0;o<i.length-1;o+=1)null==s[i[o]]&&(s[i[o]]={}),s=s[i[o]];s[i[i.length-1]]=e.target.value,e.target.isContentEditable&&(s[i[i.length-1]]=e.target.innerText),this.dispatch("nodeDataChanged",e.target.closest(".drawflow_content_node").parentElement.id.slice(5))}}updateNodeDataFromId(e,t){var n=this.getModuleFromNodeId(e);if(this.drawflow.drawflow[n].data[e].data=t,this.module===n){const n=this.container.querySelector("#node-"+e);Object.entries(t).forEach((function(e,i){if("object"==typeof e[1])!function e(i,s,o){if(null===i)i=t[s];else i=i[s];null!==i&&Object.entries(i).forEach((function(t,s){if("object"==typeof t[1])e(i,t[0],o+"-"+t[0]);else for(var l=n.querySelectorAll("[df-"+o+"-"+t[0]+"]"),c=0;c<l.length;c++)l[c].value=t[1],l[c].isContentEditable&&(l[c].innerText=t[1])}))}(null,e[0],e[0]);else for(var s=n.querySelectorAll("[df-"+e[0]+"]"),o=0;o<s.length;o++)s[o].value=e[1],s[o].isContentEditable&&(s[o].innerText=e[1])}))}}addNodeInput(e){var t=this.getModuleFromNodeId(e);const n=this.getNodeFromId(e),i=Object.keys(n.inputs).length;if(this.module===t){const t=document.createElement("div");t.classList.add("input"),t.classList.add("input_"+(i+1)),this.container.querySelector("#node-"+e+" .inputs").appendChild(t),this.updateConnectionNodes("node-"+e)}this.drawflow.drawflow[t].data[e].inputs["input_"+(i+1)]={connections:[]}}addNodeOutput(e){var t=this.getModuleFromNodeId(e);const n=this.getNodeFromId(e),i=Object.keys(n.outputs).length;if(this.module===t){const t=document.createElement("div");t.classList.add("output"),t.classList.add("output_"+(i+1)),this.container.querySelector("#node-"+e+" .outputs").appendChild(t),this.updateConnectionNodes("node-"+e)}this.drawflow.drawflow[t].data[e].outputs["output_"+(i+1)]={connections:[]}}removeNodeInput(e,t){var n=this.getModuleFromNodeId(e);const i=this.getNodeFromId(e);this.module===n&&this.container.querySelector("#node-"+e+" .inputs .input."+t).remove();const s=[];Object.keys(i.inputs[t].connections).map((function(n,o){const l=i.inputs[t].connections[o].node,c=i.inputs[t].connections[o].input;s.push({id_output:l,id:e,output_class:c,input_class:t})})),s.forEach((e,t)=>{this.removeSingleConnection(e.id_output,e.id,e.output_class,e.input_class)}),delete this.drawflow.drawflow[n].data[e].inputs[t];const o=[],l=this.drawflow.drawflow[n].data[e].inputs;Object.keys(l).map((function(e,t){o.push(l[e])})),this.drawflow.drawflow[n].data[e].inputs={};const c=t.slice(6);let d=[];if(o.forEach((t,i)=>{t.connections.forEach((e,t)=>{d.push(e)}),this.drawflow.drawflow[n].data[e].inputs["input_"+(i+1)]=t}),d=new Set(d.map(e=>JSON.stringify(e))),d=Array.from(d).map(e=>JSON.parse(e)),this.module===n){this.container.querySelectorAll("#node-"+e+" .inputs .input").forEach((e,t)=>{const n=e.classList[1].slice(6);parseInt(c)<parseInt(n)&&(e.classList.remove("input_"+n),e.classList.add("input_"+(n-1)))})}d.forEach((t,i)=>{this.drawflow.drawflow[n].data[t.node].outputs[t.input].connections.forEach((i,s)=>{if(i.node==e){const o=i.output.slice(6);if(parseInt(c)<parseInt(o)){if(this.module===n){const n=this.container.querySelector(".connection.node_in_node-"+e+".node_out_node-"+t.node+"."+t.input+".input_"+o);n.classList.remove("input_"+o),n.classList.add("input_"+(o-1))}i.points?this.drawflow.drawflow[n].data[t.node].outputs[t.input].connections[s]={node:i.node,output:"input_"+(o-1),points:i.points}:this.drawflow.drawflow[n].data[t.node].outputs[t.input].connections[s]={node:i.node,output:"input_"+(o-1)}}}})}),this.updateConnectionNodes("node-"+e)}removeNodeOutput(e,t){var n=this.getModuleFromNodeId(e);const i=this.getNodeFromId(e);this.module===n&&this.container.querySelector("#node-"+e+" .outputs .output."+t).remove();const s=[];Object.keys(i.outputs[t].connections).map((function(n,o){const l=i.outputs[t].connections[o].node,c=i.outputs[t].connections[o].output;s.push({id:e,id_input:l,output_class:t,input_class:c})})),s.forEach((e,t)=>{this.removeSingleConnection(e.id,e.id_input,e.output_class,e.input_class)}),delete this.drawflow.drawflow[n].data[e].outputs[t];const o=[],l=this.drawflow.drawflow[n].data[e].outputs;Object.keys(l).map((function(e,t){o.push(l[e])})),this.drawflow.drawflow[n].data[e].outputs={};const c=t.slice(7);let d=[];if(o.forEach((t,i)=>{t.connections.forEach((e,t)=>{d.push({node:e.node,output:e.output})}),this.drawflow.drawflow[n].data[e].outputs["output_"+(i+1)]=t}),d=new Set(d.map(e=>JSON.stringify(e))),d=Array.from(d).map(e=>JSON.parse(e)),this.module===n){this.container.querySelectorAll("#node-"+e+" .outputs .output").forEach((e,t)=>{const n=e.classList[1].slice(7);parseInt(c)<parseInt(n)&&(e.classList.remove("output_"+n),e.classList.add("output_"+(n-1)))})}d.forEach((t,i)=>{this.drawflow.drawflow[n].data[t.node].inputs[t.output].connections.forEach((i,s)=>{if(i.node==e){const o=i.input.slice(7);if(parseInt(c)<parseInt(o)){if(this.module===n){const n=this.container.querySelector(".connection.node_in_node-"+t.node+".node_out_node-"+e+".output_"+o+"."+t.output);n.classList.remove("output_"+o),n.classList.remove(t.output),n.classList.add("output_"+(o-1)),n.classList.add(t.output)}i.points?this.drawflow.drawflow[n].data[t.node].inputs[t.output].connections[s]={node:i.node,input:"output_"+(o-1),points:i.points}:this.drawflow.drawflow[n].data[t.node].inputs[t.output].connections[s]={node:i.node,input:"output_"+(o-1)}}}})}),this.updateConnectionNodes("node-"+e)}removeNodeId(e){this.removeConnectionNodeId(e);var t=this.getModuleFromNodeId(e.slice(5));this.module===t&&this.container.querySelector("#"+e).remove(),delete this.drawflow.drawflow[t].data[e.slice(5)],this.dispatch("nodeRemoved",e.slice(5))}removeConnection(){if(null!=this.connection_selected){var e=this.connection_selected.parentElement.classList;this.connection_selected.parentElement.remove();var t=this.drawflow.drawflow[this.module].data[e[2].slice(14)].outputs[e[3]].connections.findIndex((function(t,n){return t.node===e[1].slice(13)&&t.output===e[4]}));this.drawflow.drawflow[this.module].data[e[2].slice(14)].outputs[e[3]].connections.splice(t,1);var n=this.drawflow.drawflow[this.module].data[e[1].slice(13)].inputs[e[4]].connections.findIndex((function(t,n){return t.node===e[2].slice(14)&&t.input===e[3]}));this.drawflow.drawflow[this.module].data[e[1].slice(13)].inputs[e[4]].connections.splice(n,1),this.dispatch("connectionRemoved",{output_id:e[2].slice(14),input_id:e[1].slice(13),output_class:e[3],input_class:e[4]}),this.connection_selected=null}}removeSingleConnection(e,t,n,i){var s=this.getModuleFromNodeId(e);if(s===this.getModuleFromNodeId(t)){if(this.drawflow.drawflow[s].data[e].outputs[n].connections.findIndex((function(e,n){return e.node==t&&e.output===i}))>-1){this.module===s&&this.container.querySelector(".connection.node_in_node-"+t+".node_out_node-"+e+"."+n+"."+i).remove();var o=this.drawflow.drawflow[s].data[e].outputs[n].connections.findIndex((function(e,n){return e.node==t&&e.output===i}));this.drawflow.drawflow[s].data[e].outputs[n].connections.splice(o,1);var l=this.drawflow.drawflow[s].data[t].inputs[i].connections.findIndex((function(t,i){return t.node==e&&t.input===n}));return this.drawflow.drawflow[s].data[t].inputs[i].connections.splice(l,1),this.dispatch("connectionRemoved",{output_id:e,input_id:t,output_class:n,input_class:i}),!0}return!1}return!1}removeConnectionNodeId(e){const t="node_in_"+e,n="node_out_"+e,i=this.container.querySelectorAll("."+n);for(var s=i.length-1;s>=0;s--){var o=i[s].classList,l=this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.findIndex((function(e,t){return e.node===o[2].slice(14)&&e.input===o[3]}));this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.splice(l,1);var c=this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.findIndex((function(e,t){return e.node===o[1].slice(13)&&e.output===o[4]}));this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.splice(c,1),i[s].remove(),this.dispatch("connectionRemoved",{output_id:o[2].slice(14),input_id:o[1].slice(13),output_class:o[3],input_class:o[4]})}const d=this.container.querySelectorAll("."+t);for(s=d.length-1;s>=0;s--){o=d[s].classList,c=this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.findIndex((function(e,t){return e.node===o[1].slice(13)&&e.output===o[4]}));this.drawflow.drawflow[this.module].data[o[2].slice(14)].outputs[o[3]].connections.splice(c,1);l=this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.findIndex((function(e,t){return e.node===o[2].slice(14)&&e.input===o[3]}));this.drawflow.drawflow[this.module].data[o[1].slice(13)].inputs[o[4]].connections.splice(l,1),d[s].remove(),this.dispatch("connectionRemoved",{output_id:o[2].slice(14),input_id:o[1].slice(13),output_class:o[3],input_class:o[4]})}}getModuleFromNodeId(e){var t;const n=this.drawflow.drawflow;return Object.keys(n).map((function(i,s){Object.keys(n[i].data).map((function(n,s){n==e&&(t=i)}))})),t}addModule(e){this.drawflow.drawflow[e]={data:{}},this.dispatch("moduleCreated",e)}changeModule(e){this.dispatch("moduleChanged",e),this.module=e,this.precanvas.innerHTML="",this.canvas_x=0,this.canvas_y=0,this.pos_x=0,this.pos_y=0,this.mouse_x=0,this.mouse_y=0,this.zoom=1,this.zoom_last_value=1,this.precanvas.style.transform="",this.import(this.drawflow,!1)}removeModule(e){this.module===e&&this.changeModule("Home"),delete this.drawflow.drawflow[e],this.dispatch("moduleRemoved",e)}clearModuleSelected(){this.precanvas.innerHTML="",this.drawflow.drawflow[this.module]={data:{}}}clear(){this.precanvas.innerHTML="",this.drawflow={drawflow:{Home:{data:{}}}}}export(){const e=JSON.parse(JSON.stringify(this.drawflow));return this.dispatch("export",e),e}import(e,t=!0){this.clear(),this.drawflow=JSON.parse(JSON.stringify(e)),this.load(),t&&this.dispatch("import","import")}on(e,t){return"function"!=typeof t?(console.error("The listener callback must be a function, the given type is "+typeof t),!1):"string"!=typeof e?(console.error("The event name must be a string, the given type is "+typeof e),!1):(void 0===this.events[e]&&(this.events[e]={listeners:[]}),void this.events[e].listeners.push(t))}removeListener(e,t){if(!this.events[e])return!1;const n=this.events[e].listeners,i=n.indexOf(t);i>-1&&n.splice(i,1)}dispatch(e,t){if(void 0===this.events[e])return!1;this.events[e].listeners.forEach(e=>{e(t)})}getUuid(){for(var e=[],t=0;t<36;t++)e[t]="0123456789abcdef".substr(Math.floor(16*Math.random()),1);return e[14]="4",e[19]="0123456789abcdef".substr(3&e[19]|8,1),e[8]=e[13]=e[18]=e[23]="-",e.join("")}}}]).default}));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder dry-run.
|
|
3
|
+
*
|
|
4
|
+
* Walks a workflow's step DAG in topological order and produces a
|
|
5
|
+
* human-readable description of what each step *would* do, without
|
|
6
|
+
* executing anything. Crucial for long-running jobs (multi-hour
|
|
7
|
+
* outreach batches, brain ingestion runs) where the user wants to
|
|
8
|
+
* preview changes safely before scheduling a real run.
|
|
9
|
+
*/
|
|
10
|
+
import type { WorkflowDefinition, WorkflowStepKind } from '../../types.js';
|
|
11
|
+
import { validateWorkflow } from './validation.js';
|
|
12
|
+
export interface DryRunStep {
|
|
13
|
+
stepId: string;
|
|
14
|
+
kind: WorkflowStepKind;
|
|
15
|
+
wave: number;
|
|
16
|
+
description: string;
|
|
17
|
+
warnings: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface DryRunResult {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
validationIssues: ReturnType<typeof validateWorkflow>['issues'];
|
|
22
|
+
steps: DryRunStep[];
|
|
23
|
+
estimatedTokens?: {
|
|
24
|
+
promptSteps: number;
|
|
25
|
+
perPromptEstimate: number;
|
|
26
|
+
total: number;
|
|
27
|
+
};
|
|
28
|
+
notes: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare function dryRunWorkflow(wf: WorkflowDefinition): DryRunResult;
|
|
31
|
+
//# sourceMappingURL=dry-run.d.ts.map
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder dry-run.
|
|
3
|
+
*
|
|
4
|
+
* Walks a workflow's step DAG in topological order and produces a
|
|
5
|
+
* human-readable description of what each step *would* do, without
|
|
6
|
+
* executing anything. Crucial for long-running jobs (multi-hour
|
|
7
|
+
* outreach batches, brain ingestion runs) where the user wants to
|
|
8
|
+
* preview changes safely before scheduling a real run.
|
|
9
|
+
*/
|
|
10
|
+
import { validateWorkflow } from './validation.js';
|
|
11
|
+
export function dryRunWorkflow(wf) {
|
|
12
|
+
const validation = validateWorkflow(wf);
|
|
13
|
+
const result = {
|
|
14
|
+
ok: validation.ok,
|
|
15
|
+
validationIssues: validation.issues,
|
|
16
|
+
steps: [],
|
|
17
|
+
notes: [],
|
|
18
|
+
};
|
|
19
|
+
if (!validation.ok) {
|
|
20
|
+
result.notes.push('Validation failed — fix errors before scheduling. Steps below are descriptive only.');
|
|
21
|
+
}
|
|
22
|
+
const waveOf = computeWaves(wf.steps);
|
|
23
|
+
let promptSteps = 0;
|
|
24
|
+
for (const step of wf.steps) {
|
|
25
|
+
const kind = step.kind ?? 'prompt';
|
|
26
|
+
if (kind === 'prompt')
|
|
27
|
+
promptSteps++;
|
|
28
|
+
result.steps.push({
|
|
29
|
+
stepId: step.id,
|
|
30
|
+
kind,
|
|
31
|
+
wave: waveOf[step.id] ?? 0,
|
|
32
|
+
description: describeStep(step),
|
|
33
|
+
warnings: stepWarnings(step),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (promptSteps > 0) {
|
|
37
|
+
const perPromptEstimate = 1500; // very rough heuristic; real cost depends on prior step outputs
|
|
38
|
+
result.estimatedTokens = {
|
|
39
|
+
promptSteps,
|
|
40
|
+
perPromptEstimate,
|
|
41
|
+
total: promptSteps * perPromptEstimate,
|
|
42
|
+
};
|
|
43
|
+
result.notes.push(`Cost estimate is rough — actual usage depends on prior step outputs and tool calls.`);
|
|
44
|
+
}
|
|
45
|
+
if (wf.trigger.schedule) {
|
|
46
|
+
result.notes.push(`Trigger: cron "${wf.trigger.schedule}". This describes one execution.`);
|
|
47
|
+
}
|
|
48
|
+
else if (wf.trigger.manual) {
|
|
49
|
+
result.notes.push('Trigger: manual. Workflow runs only when invoked directly.');
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
function describeStep(step) {
|
|
54
|
+
const kind = step.kind ?? 'prompt';
|
|
55
|
+
const deps = step.dependsOn.length ? ` (after: ${step.dependsOn.join(', ')})` : '';
|
|
56
|
+
switch (kind) {
|
|
57
|
+
case 'prompt':
|
|
58
|
+
return `Prompt step "${step.id}"${deps}: would send a prompt to the agent (model: ${step.model ?? 'default'}, maxTurns: ${step.maxTurns}). Prompt preview: ${truncate(step.prompt, 160)}`;
|
|
59
|
+
case 'mcp':
|
|
60
|
+
if (!step.mcp)
|
|
61
|
+
return `MCP step "${step.id}"${deps}: misconfigured (no mcp config)`;
|
|
62
|
+
return `MCP step "${step.id}"${deps}: would call ${step.mcp.server}.${step.mcp.tool}${step.mcp.inputs ? ` with inputs ${JSON.stringify(step.mcp.inputs)}` : ' (no inputs)'}`;
|
|
63
|
+
case 'channel':
|
|
64
|
+
if (!step.channel)
|
|
65
|
+
return `Channel step "${step.id}"${deps}: misconfigured`;
|
|
66
|
+
return `Channel step "${step.id}"${deps}: would send to ${step.channel.channel} → ${step.channel.target}. Content preview: ${truncate(step.channel.content, 120)}`;
|
|
67
|
+
case 'transform':
|
|
68
|
+
if (!step.transform)
|
|
69
|
+
return `Transform step "${step.id}"${deps}: misconfigured`;
|
|
70
|
+
return `Transform step "${step.id}"${deps}: would evaluate ${truncate(step.transform.expression, 160)}`;
|
|
71
|
+
case 'conditional':
|
|
72
|
+
if (!step.conditional)
|
|
73
|
+
return `Conditional step "${step.id}"${deps}: misconfigured`;
|
|
74
|
+
return `Conditional step "${step.id}"${deps}: branches on ${truncate(step.conditional.condition, 120)}. True → ${(step.conditional.trueNext ?? []).join(',') || '(none)'}, False → ${(step.conditional.falseNext ?? []).join(',') || '(none)'}`;
|
|
75
|
+
case 'loop':
|
|
76
|
+
if (!step.loop)
|
|
77
|
+
return `Loop step "${step.id}"${deps}: misconfigured`;
|
|
78
|
+
return `Loop step "${step.id}"${deps}: iterates over ${truncate(step.loop.items, 80)}, body: ${step.loop.bodyStepIds.join(', ')}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function stepWarnings(step) {
|
|
82
|
+
const warnings = [];
|
|
83
|
+
const kind = step.kind ?? 'prompt';
|
|
84
|
+
if (kind === 'channel' && step.channel) {
|
|
85
|
+
if (step.channel.channel === 'email' && !step.channel.target.includes('@')) {
|
|
86
|
+
warnings.push('Email target does not look like an email address');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (kind === 'mcp' && step.mcp) {
|
|
90
|
+
if (/(create|delete|update|push|send|post|drop)/i.test(step.mcp.tool)) {
|
|
91
|
+
warnings.push('Looks like a write/destructive MCP tool — confirm before running for real');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (kind === 'prompt' && step.prompt && step.prompt.length > 5000) {
|
|
95
|
+
warnings.push('Prompt is very long — may exceed reasonable cost per run');
|
|
96
|
+
}
|
|
97
|
+
if (kind === 'prompt' && step.maxTurns > 60) {
|
|
98
|
+
warnings.push('maxTurns is high; this step may run for many minutes');
|
|
99
|
+
}
|
|
100
|
+
return warnings;
|
|
101
|
+
}
|
|
102
|
+
function truncate(s, n) {
|
|
103
|
+
if (!s)
|
|
104
|
+
return '';
|
|
105
|
+
if (s.length <= n)
|
|
106
|
+
return s;
|
|
107
|
+
return s.slice(0, n) + '…';
|
|
108
|
+
}
|
|
109
|
+
/** Topological wave numbers — same logic as serializer's, kept local to avoid circular import. */
|
|
110
|
+
function computeWaves(steps) {
|
|
111
|
+
const wave = {};
|
|
112
|
+
const ids = new Set(steps.map(s => s.id));
|
|
113
|
+
const remaining = new Set(steps.map(s => s.id));
|
|
114
|
+
let current = 0;
|
|
115
|
+
const maxIter = steps.length + 1;
|
|
116
|
+
for (let iter = 0; iter < maxIter && remaining.size > 0; iter++) {
|
|
117
|
+
const ready = [];
|
|
118
|
+
for (const s of steps) {
|
|
119
|
+
if (!remaining.has(s.id))
|
|
120
|
+
continue;
|
|
121
|
+
const depsResolved = s.dependsOn.every(d => !ids.has(d) || (wave[d] != null && wave[d] < current + 1));
|
|
122
|
+
if (depsResolved)
|
|
123
|
+
ready.push(s.id);
|
|
124
|
+
}
|
|
125
|
+
if (ready.length === 0) {
|
|
126
|
+
for (const id of remaining)
|
|
127
|
+
wave[id] = current;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
for (const id of ready) {
|
|
131
|
+
wave[id] = current;
|
|
132
|
+
remaining.delete(id);
|
|
133
|
+
}
|
|
134
|
+
current++;
|
|
135
|
+
}
|
|
136
|
+
return wave;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=dry-run.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder event bus.
|
|
3
|
+
*
|
|
4
|
+
* Decouples MCP tool handlers (which mutate workflows) from the dashboard
|
|
5
|
+
* WebSocket server (which streams updates to open builder tabs). Tools
|
|
6
|
+
* call `emitBuilderEvent(...)` after a successful write; the WS server
|
|
7
|
+
* subscribes and forwards to clients in the matching workflow room.
|
|
8
|
+
*
|
|
9
|
+
* Test-side / runs (Phase 2+) will reuse the same bus to stream per-step
|
|
10
|
+
* status during long-running test/dry-run flows.
|
|
11
|
+
*/
|
|
12
|
+
export type BuilderEventType = 'workflow:created' | 'workflow:updated' | 'workflow:deleted' | 'workflow:renamed' | 'workflow:enabled-changed' | 'workflow:patched' | 'run:started' | 'run:step-status' | 'run:step-output' | 'run:completed' | 'run:cancelled' | 'run:error';
|
|
13
|
+
export interface BuilderEvent {
|
|
14
|
+
type: BuilderEventType;
|
|
15
|
+
workflowId: string;
|
|
16
|
+
runId?: string;
|
|
17
|
+
payload?: unknown;
|
|
18
|
+
ts: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function emitBuilderEvent(event: Omit<BuilderEvent, 'ts'>): void;
|
|
21
|
+
export declare function onBuilderEvent(workflowId: string, listener: (e: BuilderEvent) => void): () => void;
|
|
22
|
+
export declare function onAnyBuilderEvent(listener: (e: BuilderEvent) => void): () => void;
|
|
23
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder event bus.
|
|
3
|
+
*
|
|
4
|
+
* Decouples MCP tool handlers (which mutate workflows) from the dashboard
|
|
5
|
+
* WebSocket server (which streams updates to open builder tabs). Tools
|
|
6
|
+
* call `emitBuilderEvent(...)` after a successful write; the WS server
|
|
7
|
+
* subscribes and forwards to clients in the matching workflow room.
|
|
8
|
+
*
|
|
9
|
+
* Test-side / runs (Phase 2+) will reuse the same bus to stream per-step
|
|
10
|
+
* status during long-running test/dry-run flows.
|
|
11
|
+
*/
|
|
12
|
+
import { EventEmitter } from 'node:events';
|
|
13
|
+
const bus = new EventEmitter();
|
|
14
|
+
bus.setMaxListeners(50); // allow many open builder tabs simultaneously
|
|
15
|
+
export function emitBuilderEvent(event) {
|
|
16
|
+
const full = { ...event, ts: new Date().toISOString() };
|
|
17
|
+
bus.emit(event.workflowId, full);
|
|
18
|
+
bus.emit('*', full);
|
|
19
|
+
}
|
|
20
|
+
export function onBuilderEvent(workflowId, listener) {
|
|
21
|
+
bus.on(workflowId, listener);
|
|
22
|
+
return () => bus.off(workflowId, listener);
|
|
23
|
+
}
|
|
24
|
+
export function onAnyBuilderEvent(listener) {
|
|
25
|
+
bus.on('*', listener);
|
|
26
|
+
return () => bus.off('*', listener);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP invoke handler for the Builder runner.
|
|
3
|
+
*
|
|
4
|
+
* Spawns and pools MCP clients keyed by server name, then routes
|
|
5
|
+
* `runner.executeMcpStep` calls through `client.callTool(...)`.
|
|
6
|
+
*
|
|
7
|
+
* Designed to be wired into the daemon at startup via
|
|
8
|
+
* `runner.registerMcpInvokeHandler(...)`. When this module is loaded
|
|
9
|
+
* but the registration call hasn't happened yet, the runner falls back
|
|
10
|
+
* to its mock-stub behavior.
|
|
11
|
+
*
|
|
12
|
+
* Pooling is per-process: each server gets one stdio client that lives
|
|
13
|
+
* until the daemon shuts down. Idle clients are torn down after
|
|
14
|
+
* IDLE_TIMEOUT_MS to free resources, then re-spawned on demand.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Wire the handler into the runner. Idempotent — safe to call from any
|
|
18
|
+
* daemon entry path.
|
|
19
|
+
*/
|
|
20
|
+
export declare function installBuilderMcpHandler(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Tear down all pooled clients. Called from daemon graceful shutdown.
|
|
23
|
+
*/
|
|
24
|
+
export declare function shutdownBuilderMcpHandler(): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=mcp-invoke.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP invoke handler for the Builder runner.
|
|
3
|
+
*
|
|
4
|
+
* Spawns and pools MCP clients keyed by server name, then routes
|
|
5
|
+
* `runner.executeMcpStep` calls through `client.callTool(...)`.
|
|
6
|
+
*
|
|
7
|
+
* Designed to be wired into the daemon at startup via
|
|
8
|
+
* `runner.registerMcpInvokeHandler(...)`. When this module is loaded
|
|
9
|
+
* but the registration call hasn't happened yet, the runner falls back
|
|
10
|
+
* to its mock-stub behavior.
|
|
11
|
+
*
|
|
12
|
+
* Pooling is per-process: each server gets one stdio client that lives
|
|
13
|
+
* until the daemon shuts down. Idle clients are torn down after
|
|
14
|
+
* IDLE_TIMEOUT_MS to free resources, then re-spawned on demand.
|
|
15
|
+
*/
|
|
16
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
17
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
18
|
+
import { discoverMcpServers } from '../../agent/mcp-bridge.js';
|
|
19
|
+
import { logger } from '../../tools/shared.js';
|
|
20
|
+
import { registerMcpInvokeHandler } from './runner.js';
|
|
21
|
+
const PER_CALL_TIMEOUT_MS = 30_000;
|
|
22
|
+
const IDLE_TIMEOUT_MS = 5 * 60_000;
|
|
23
|
+
const pool = new Map();
|
|
24
|
+
async function getOrSpawnClient(name) {
|
|
25
|
+
const existing = pool.get(name);
|
|
26
|
+
if (existing && !existing.closing) {
|
|
27
|
+
existing.lastUsedAt = Date.now();
|
|
28
|
+
rescheduleIdleTimer(existing);
|
|
29
|
+
return existing;
|
|
30
|
+
}
|
|
31
|
+
const servers = discoverMcpServers();
|
|
32
|
+
const cfg = servers.find(s => s.name === name);
|
|
33
|
+
if (!cfg || !cfg.enabled)
|
|
34
|
+
return null;
|
|
35
|
+
if (cfg.type !== 'stdio' || !cfg.command) {
|
|
36
|
+
// HTTP/SSE servers aren't pooled here — fall back to mock for now.
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
let transport = null;
|
|
40
|
+
let client = null;
|
|
41
|
+
try {
|
|
42
|
+
transport = new StdioClientTransport({
|
|
43
|
+
command: cfg.command,
|
|
44
|
+
args: cfg.args ?? [],
|
|
45
|
+
env: { ...process.env, ...(cfg.env ?? {}) },
|
|
46
|
+
stderr: 'ignore',
|
|
47
|
+
});
|
|
48
|
+
client = new Client({ name: 'clementine-builder-runner', version: '1.0.0' }, { capabilities: {} });
|
|
49
|
+
await client.connect(transport);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
logger.warn({ err, server: name }, 'Builder MCP client spawn failed');
|
|
53
|
+
try {
|
|
54
|
+
await client?.close();
|
|
55
|
+
}
|
|
56
|
+
catch { /* */ }
|
|
57
|
+
try {
|
|
58
|
+
await transport?.close();
|
|
59
|
+
}
|
|
60
|
+
catch { /* */ }
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const pooled = {
|
|
64
|
+
name,
|
|
65
|
+
client,
|
|
66
|
+
transport,
|
|
67
|
+
lastUsedAt: Date.now(),
|
|
68
|
+
closing: false,
|
|
69
|
+
};
|
|
70
|
+
rescheduleIdleTimer(pooled);
|
|
71
|
+
pool.set(name, pooled);
|
|
72
|
+
return pooled;
|
|
73
|
+
}
|
|
74
|
+
function rescheduleIdleTimer(p) {
|
|
75
|
+
if (p.idleTimer)
|
|
76
|
+
clearTimeout(p.idleTimer);
|
|
77
|
+
p.idleTimer = setTimeout(() => { void closeClient(p.name); }, IDLE_TIMEOUT_MS);
|
|
78
|
+
}
|
|
79
|
+
async function closeClient(name) {
|
|
80
|
+
const p = pool.get(name);
|
|
81
|
+
if (!p)
|
|
82
|
+
return;
|
|
83
|
+
p.closing = true;
|
|
84
|
+
pool.delete(name);
|
|
85
|
+
if (p.idleTimer)
|
|
86
|
+
clearTimeout(p.idleTimer);
|
|
87
|
+
try {
|
|
88
|
+
await p.client.close();
|
|
89
|
+
}
|
|
90
|
+
catch { /* */ }
|
|
91
|
+
try {
|
|
92
|
+
await p.transport.close();
|
|
93
|
+
}
|
|
94
|
+
catch { /* */ }
|
|
95
|
+
}
|
|
96
|
+
const handler = async ({ server, tool, inputs, signal }) => {
|
|
97
|
+
if (signal.aborted)
|
|
98
|
+
throw Object.assign(new Error('Aborted'), { name: 'AbortError' });
|
|
99
|
+
const pooled = await getOrSpawnClient(server);
|
|
100
|
+
if (!pooled) {
|
|
101
|
+
return {
|
|
102
|
+
_builderMock: true,
|
|
103
|
+
reason: `MCP server "${server}" not available (not configured, disabled, or non-stdio transport).`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const work = pooled.client.callTool({ name: tool, arguments: inputs });
|
|
107
|
+
const timeout = new Promise((_, reject) => setTimeout(() => reject(Object.assign(new Error(`MCP tool ${server}.${tool} timed out`), { name: 'TimeoutError' })), PER_CALL_TIMEOUT_MS));
|
|
108
|
+
const onAbort = new Promise((_, reject) => signal.addEventListener('abort', () => reject(Object.assign(new Error('Aborted'), { name: 'AbortError' })), { once: true }));
|
|
109
|
+
try {
|
|
110
|
+
const result = await Promise.race([work, timeout, onAbort]);
|
|
111
|
+
pooled.lastUsedAt = Date.now();
|
|
112
|
+
rescheduleIdleTimer(pooled);
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
// If the connection died, drop it from the pool so the next call respawns.
|
|
117
|
+
const e = err;
|
|
118
|
+
if (e.code === 'ERR_PIPE_CLOSED' || /closed|killed|EPIPE/.test(e.message ?? '')) {
|
|
119
|
+
void closeClient(server);
|
|
120
|
+
}
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
let _registered = false;
|
|
125
|
+
/**
|
|
126
|
+
* Wire the handler into the runner. Idempotent — safe to call from any
|
|
127
|
+
* daemon entry path.
|
|
128
|
+
*/
|
|
129
|
+
export function installBuilderMcpHandler() {
|
|
130
|
+
if (_registered)
|
|
131
|
+
return;
|
|
132
|
+
registerMcpInvokeHandler(handler);
|
|
133
|
+
_registered = true;
|
|
134
|
+
logger.info({}, 'Builder MCP invoke handler installed');
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Tear down all pooled clients. Called from daemon graceful shutdown.
|
|
138
|
+
*/
|
|
139
|
+
export async function shutdownBuilderMcpHandler() {
|
|
140
|
+
const names = [...pool.keys()];
|
|
141
|
+
await Promise.all(names.map(n => closeClient(n)));
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=mcp-invoke.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builder workflow test runner.
|
|
3
|
+
*
|
|
4
|
+
* Executes a workflow's step DAG in topological waves, streaming per-step
|
|
5
|
+
* status + output events through the events bus so the dashboard canvas
|
|
6
|
+
* can light up in real time.
|
|
7
|
+
*
|
|
8
|
+
* Safety posture for **test mode** (the default for canvas runs):
|
|
9
|
+
* - Prompt steps: stubbed by default (no LLM tokens, no agent turns).
|
|
10
|
+
* Real-mode runs through the production scheduler, not here.
|
|
11
|
+
* - MCP steps: read-only-shaped tools invoke for real (list_/get_/
|
|
12
|
+
* read_/search_); write-shaped tools are stubbed unless mode='real'.
|
|
13
|
+
* - Channel steps: always stubbed in the test runner. Real channel
|
|
14
|
+
* sends should happen on a scheduled run, not from a canvas test.
|
|
15
|
+
* - Transform / conditional / loop: real evaluation (sandboxed JS,
|
|
16
|
+
* short timeout). They have no external side effects.
|
|
17
|
+
*
|
|
18
|
+
* Long-running awareness:
|
|
19
|
+
* - Per-step timeout (default 30s)
|
|
20
|
+
* - Total budget cap (default 60s wall-clock)
|
|
21
|
+
* - AbortSignal cancellation; canceller killed between steps and
|
|
22
|
+
* during transform/conditional/loop evaluation.
|
|
23
|
+
*/
|
|
24
|
+
import type { WorkflowDefinition } from '../../types.js';
|
|
25
|
+
export type RunMode = 'mock' | 'real';
|
|
26
|
+
export type StepStatus = 'pending' | 'running' | 'done' | 'failed' | 'skipped' | 'cancelled' | 'timeout';
|
|
27
|
+
export interface RunOptions {
|
|
28
|
+
workflowId: string;
|
|
29
|
+
runId?: string;
|
|
30
|
+
mode?: RunMode;
|
|
31
|
+
perStepTimeoutMs?: number;
|
|
32
|
+
totalBudgetMs?: number;
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
export interface StepResult {
|
|
36
|
+
stepId: string;
|
|
37
|
+
status: StepStatus;
|
|
38
|
+
durationMs: number;
|
|
39
|
+
output?: unknown;
|
|
40
|
+
error?: string;
|
|
41
|
+
mocked?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface RunResult {
|
|
44
|
+
runId: string;
|
|
45
|
+
workflowId: string;
|
|
46
|
+
mode: RunMode;
|
|
47
|
+
status: 'ok' | 'error' | 'cancelled' | 'timeout';
|
|
48
|
+
startedAt: string;
|
|
49
|
+
finishedAt: string;
|
|
50
|
+
durationMs: number;
|
|
51
|
+
stepResults: StepResult[];
|
|
52
|
+
}
|
|
53
|
+
export declare function cancelRun(runId: string): boolean;
|
|
54
|
+
export declare function isRunActive(runId: string): boolean;
|
|
55
|
+
export declare function runWorkflowTest(wf: WorkflowDefinition, opts: RunOptions): Promise<RunResult>;
|
|
56
|
+
export type McpInvokeFn = (args: {
|
|
57
|
+
server: string;
|
|
58
|
+
tool: string;
|
|
59
|
+
inputs: Record<string, unknown>;
|
|
60
|
+
signal: AbortSignal;
|
|
61
|
+
}) => Promise<unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* Daemon registers a callback the runner uses to invoke MCP tools for
|
|
64
|
+
* real. Kept as a runtime injection so the runner doesn't pull the
|
|
65
|
+
* agent-bridge into its module graph (which would create a cycle).
|
|
66
|
+
*/
|
|
67
|
+
export declare function registerMcpInvokeHandler(handler: McpInvokeFn): void;
|
|
68
|
+
//# sourceMappingURL=runner.d.ts.map
|