agentix-cli 0.14.0 → 0.16.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.
Files changed (100) hide show
  1. package/README.md +70 -34
  2. package/dist/agent-BCDPSPKU.js +2 -0
  3. package/dist/board-dashboard-LA7WW56M.js +5668 -0
  4. package/dist/board-dashboard-LA7WW56M.js.map +1 -0
  5. package/dist/chunk-5F3J55LG.js +2 -0
  6. package/dist/chunk-5F3J55LG.js.map +1 -0
  7. package/dist/chunk-64P4QHSZ.js +2 -0
  8. package/dist/chunk-64P4QHSZ.js.map +1 -0
  9. package/dist/chunk-7FJQGD6U.js +6 -0
  10. package/dist/chunk-7FJQGD6U.js.map +1 -0
  11. package/dist/{chunk-ZRYBSI5G.js → chunk-7MBTMCKY.js} +2 -2
  12. package/dist/chunk-ABDP7TDL.js +71 -0
  13. package/dist/chunk-ABDP7TDL.js.map +1 -0
  14. package/dist/chunk-BFZUG66X.js +45 -0
  15. package/dist/chunk-BFZUG66X.js.map +1 -0
  16. package/dist/chunk-CXY2RYNI.js +11 -0
  17. package/dist/chunk-CXY2RYNI.js.map +1 -0
  18. package/dist/chunk-FIOB3MEX.js +2 -0
  19. package/dist/chunk-FLOGK2U7.js +7 -0
  20. package/dist/chunk-FLOGK2U7.js.map +1 -0
  21. package/dist/{chunk-XVYMZ4M7.js → chunk-HVC6FJNP.js} +5 -5
  22. package/dist/chunk-K7BE4E35.js +138 -0
  23. package/dist/chunk-K7BE4E35.js.map +1 -0
  24. package/dist/chunk-KXJSLLSQ.js +199 -0
  25. package/dist/chunk-KXJSLLSQ.js.map +1 -0
  26. package/dist/chunk-LEIYC3BF.js +104 -0
  27. package/dist/chunk-LEIYC3BF.js.map +1 -0
  28. package/dist/{chunk-O72LPMBR.js → chunk-RB7265LO.js} +2 -2
  29. package/dist/chunk-WZEYII2S.js +6 -0
  30. package/dist/chunk-WZEYII2S.js.map +1 -0
  31. package/dist/chunk-YI2T5QYB.js +5 -0
  32. package/dist/{chunk-NJR5WF5E.js.map → chunk-YI2T5QYB.js.map} +1 -1
  33. package/dist/cli.js +209 -181
  34. package/dist/cli.js.map +1 -1
  35. package/dist/{compaction-7I3E5QUO.js → compaction-JBZZ7I7Y.js} +4 -4
  36. package/dist/config-LN6JEHMF.js +2 -0
  37. package/dist/config-mutator-24TH7PHM.js +2 -0
  38. package/dist/debug-3MEUO2P2.js +2 -0
  39. package/dist/{heal-OTGT5HHJ.js → heal-WKY5I4KB.js} +2 -2
  40. package/dist/index.d.ts +1078 -13
  41. package/dist/index.js +47 -44
  42. package/dist/index.js.map +1 -1
  43. package/dist/loader-PTCXLJEX.js +2 -0
  44. package/dist/memory-extract-AD7KMRPC.js +2 -0
  45. package/dist/{providers-G3WZ4RVJ.js → providers-VUIWO3ZK.js} +2 -2
  46. package/dist/query-PKGYE5HR.js +2 -0
  47. package/dist/registry-ASC2QS5K.js +2 -0
  48. package/dist/scheduler-A3TQOCKT.js +2 -0
  49. package/dist/scheduler-A3TQOCKT.js.map +1 -0
  50. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  51. package/dist/types-OIFU3KSQ.js +2 -0
  52. package/dist/types-OIFU3KSQ.js.map +1 -0
  53. package/dist/{usage-dashboard-M7AFIGMJ.js → usage-dashboard-QX3CF6KK.js} +8 -8
  54. package/dist/usage-dashboard-QX3CF6KK.js.map +1 -0
  55. package/dist/whatsapp-state-6D6FHHD2.js +2 -0
  56. package/dist/whatsapp-state-6D6FHHD2.js.map +1 -0
  57. package/dist/wiki-UDPYFJBL.js +2 -0
  58. package/dist/wiki-UDPYFJBL.js.map +1 -0
  59. package/dist/workspace-setup-ZWTT42XT.js +2 -0
  60. package/dist/workspace-setup-ZWTT42XT.js.map +1 -0
  61. package/package.json +3 -2
  62. package/dist/agent-DW3NYIIL.js +0 -2
  63. package/dist/board-dashboard-6TKE6NMV.js +0 -1435
  64. package/dist/board-dashboard-6TKE6NMV.js.map +0 -1
  65. package/dist/chunk-4YCH6IZV.js +0 -2
  66. package/dist/chunk-DYOBFJLX.js +0 -11
  67. package/dist/chunk-DYOBFJLX.js.map +0 -1
  68. package/dist/chunk-ITZEVXZ4.js +0 -2
  69. package/dist/chunk-ITZEVXZ4.js.map +0 -1
  70. package/dist/chunk-M33CB2H4.js +0 -6
  71. package/dist/chunk-M33CB2H4.js.map +0 -1
  72. package/dist/chunk-NJR5WF5E.js +0 -5
  73. package/dist/chunk-RB73FZXJ.js +0 -133
  74. package/dist/chunk-RB73FZXJ.js.map +0 -1
  75. package/dist/chunk-UPT2UMF6.js +0 -159
  76. package/dist/chunk-UPT2UMF6.js.map +0 -1
  77. package/dist/config-7QJ2QJIC.js +0 -2
  78. package/dist/config-mutator-GAEVGHDD.js +0 -2
  79. package/dist/debug-N3B5NVJU.js +0 -2
  80. package/dist/loader-YQE6XJBT.js +0 -2
  81. package/dist/memory-extract-RGPUPMKU.js +0 -2
  82. package/dist/registry-YVAG4EO5.js +0 -2
  83. package/dist/usage-dashboard-M7AFIGMJ.js.map +0 -1
  84. package/dist/workspace-setup-QQTCJY4B.js +0 -2
  85. /package/dist/{agent-DW3NYIIL.js.map → agent-BCDPSPKU.js.map} +0 -0
  86. /package/dist/{chunk-ZRYBSI5G.js.map → chunk-7MBTMCKY.js.map} +0 -0
  87. /package/dist/{chunk-4YCH6IZV.js.map → chunk-FIOB3MEX.js.map} +0 -0
  88. /package/dist/{chunk-XVYMZ4M7.js.map → chunk-HVC6FJNP.js.map} +0 -0
  89. /package/dist/{chunk-O72LPMBR.js.map → chunk-RB7265LO.js.map} +0 -0
  90. /package/dist/{compaction-7I3E5QUO.js.map → compaction-JBZZ7I7Y.js.map} +0 -0
  91. /package/dist/{config-7QJ2QJIC.js.map → config-LN6JEHMF.js.map} +0 -0
  92. /package/dist/{config-mutator-GAEVGHDD.js.map → config-mutator-24TH7PHM.js.map} +0 -0
  93. /package/dist/{debug-N3B5NVJU.js.map → debug-3MEUO2P2.js.map} +0 -0
  94. /package/dist/{heal-OTGT5HHJ.js.map → heal-WKY5I4KB.js.map} +0 -0
  95. /package/dist/{loader-YQE6XJBT.js.map → loader-PTCXLJEX.js.map} +0 -0
  96. /package/dist/{memory-extract-RGPUPMKU.js.map → memory-extract-AD7KMRPC.js.map} +0 -0
  97. /package/dist/{providers-G3WZ4RVJ.js.map → providers-VUIWO3ZK.js.map} +0 -0
  98. /package/dist/{registry-YVAG4EO5.js.map → query-PKGYE5HR.js.map} +0 -0
  99. /package/dist/{workspace-setup-QQTCJY4B.js.map → registry-ASC2QS5K.js.map} +0 -0
  100. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
@@ -1,1435 +0,0 @@
1
- import{a as I}from"./chunk-6U2XOQHU.js";import{c as A,d as T}from"./chunk-ITZEVXZ4.js";import"./chunk-4YCH6IZV.js";import{createServer as O}from"http";import{appendFileSync as N,existsSync as R,mkdirSync as B}from"fs";import{resolve as _}from"path";var M='/*! Sortable 1.15.2 - MIT | git://github.com/SortableJS/Sortable.git */\n!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function I(o){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?e(Object(i),!0).forEach(function(t){var e,n;e=o,t=i[n=t],n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach(function(t){Object.defineProperty(o,t,Object.getOwnPropertyDescriptor(i,t))})}return o}function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,o=arguments[e];for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(t[n]=o[n])}return t}).apply(this,arguments)}function i(t,e){if(null==t)return{};var n,o=function(t,e){if(null==t)return{};for(var n,o={},i=Object.keys(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols)for(var i=Object.getOwnPropertySymbols(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n]);return o}function r(t){return function(t){if(Array.isArray(t))return l(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return l(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Map"===(n="Object"===n&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var y=t(/(?:Trident.*rv[ :]?11\\.|msie|iemobile|Windows Phone)/i),w=t(/Edge/i),s=t(/firefox/i),u=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),c=t(/chrome/i)&&t(/android/i),d={capture:!1,passive:!1};function h(t,e,n){t.addEventListener(e,n,!y&&d)}function f(t,e,n){t.removeEventListener(e,n,!y&&d)}function p(t,e){if(e&&(">"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i<r;i++)n(o[i],i);return o}return[]}function O(){var t=document.scrollingElement;return t||document.documentElement}function X(t,e,n,o,i){if(t.getBoundingClientRect||t===window){var r,a,l,s,c,u,d=t!==window&&t.parentNode&&t!==O()?(a=(r=t.getBoundingClientRect()).top,l=r.left,s=r.bottom,c=r.right,u=r.height,r.width):(l=a=0,s=window.innerHeight,c=window.innerWidth,u=window.innerHeight,window.innerWidth);if((e||n)&&t!==window&&(i=i||t.parentNode,!y))do{if(i&&i.getBoundingClientRect&&("none"!==R(i,"transform")||n&&"static"!==R(i,"position"))){var h=i.getBoundingClientRect();a-=h.top+parseInt(R(i,"border-top-width")),l-=h.left+parseInt(R(i,"border-left-width")),s=a+r.height,c=l+r.width;break}}while(i=i.parentNode);return o&&t!==window&&(o=(e=v(i||t))&&e.a,t=e&&e.d,e&&(s=(a/=t)+(u/=t),c=(l/=o)+(d/=o))),{top:a,left:l,bottom:s,right:c,width:d,height:u}}}function Y(t,e,n){for(var o=M(t,!0),i=X(t)[e];o;){var r=X(o)[n];if(!("top"===n||"left"===n?r<=i:i<=r))return o;if(o===O())break;o=M(o,!1)}return!1}function B(t,e,n,o){for(var i=0,r=0,a=t.children;r<a.length;){if("none"!==a[r].style.display&&a[r]!==Ft.ghost&&(o||a[r]!==Ft.dragged)&&P(a[r],n.draggable,t,!1)){if(i===e)return a[r];i++}r++}return null}function F(t,e){for(var n=t.lastElementChild;n&&(n===Ft.ghost||"none"===R(n,"display")||e&&!p(n,e));)n=n.previousElementSibling;return n||null}function j(t,e){var n=0;if(!t||!t.parentNode)return-1;for(;t=t.previousElementSibling;)"TEMPLATE"===t.nodeName.toUpperCase()||t===Ft.clone||e&&!p(t,e)||n++;return n}function E(t){var e=0,n=0,o=O();if(t)do{var i=v(t),r=i.a,i=i.d}while(e+=t.scrollLeft*r,n+=t.scrollTop*i,t!==o&&(t=t.parentNode));return[e,n]}function M(t,e){if(!t||!t.getBoundingClientRect)return O();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=R(n);if(n.clientWidth<n.scrollWidth&&("auto"==i.overflowX||"scroll"==i.overflowX)||n.clientHeight<n.scrollHeight&&("auto"==i.overflowY||"scroll"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return O();if(o||e)return n;o=!0}}}while(n=n.parentNode);return O()}function D(t,e){return Math.round(t.top)===Math.round(e.top)&&Math.round(t.left)===Math.round(e.left)&&Math.round(t.height)===Math.round(e.height)&&Math.round(t.width)===Math.round(e.width)}function S(e,n){return function(){var t;g||(1===(t=arguments).length?e.call(this,t[0]):e.apply(this,t),g=setTimeout(function(){g=void 0},n))}}function H(t,e,n){t.scrollLeft+=e,t.scrollTop+=n}function _(t){var e=window.Polymer,n=window.jQuery||window.Zepto;return e&&e.dom?e.dom(t).cloneNode(!0):n?n(t).clone(!0)[0]:t.cloneNode(!0)}function C(t,e){R(t,"position","absolute"),R(t,"top",e.top),R(t,"left",e.left),R(t,"width",e.width),R(t,"height",e.height)}function T(t){R(t,"position",""),R(t,"top",""),R(t,"left",""),R(t,"width",""),R(t,"height","")}function L(n,o,i){var r={};return Array.from(n.children).forEach(function(t){var e;P(t,o.draggable,n,!1)&&!t.animated&&t!==i&&(e=X(t),r.left=Math.min(null!==(t=r.left)&&void 0!==t?t:1/0,e.left),r.top=Math.min(null!==(t=r.top)&&void 0!==t?t:1/0,e.top),r.right=Math.max(null!==(t=r.right)&&void 0!==t?t:-1/0,e.right),r.bottom=Math.max(null!==(t=r.bottom)&&void 0!==t?t:-1/0,e.bottom))}),r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}var K="Sortable"+(new Date).getTime();function x(){var e,o=[];return{captureAnimationState:function(){o=[],this.options.animation&&[].slice.call(this.el.children).forEach(function(t){var e,n;"none"!==R(t,"display")&&t!==Ft.ghost&&(o.push({target:t,rect:X(t)}),e=I({},o[o.length-1].rect),!t.thisAnimationDuration||(n=v(t,!0))&&(e.top-=n.f,e.left-=n.e),t.fromRect=e)})},addAnimationState:function(t){o.push(t)},removeAnimationState:function(t){o.splice(function(t,e){for(var n in t)if(t.hasOwnProperty(n))for(var o in e)if(e.hasOwnProperty(o)&&e[o]===t[n][o])return Number(n);return-1}(o,{target:t}),1)},animateAll:function(t){var c=this;if(!this.options.animation)return clearTimeout(e),void("function"==typeof t&&t());var u=!1,d=0;o.forEach(function(t){var e=0,n=t.target,o=n.fromRect,i=X(n),r=n.prevFromRect,a=n.prevToRect,l=t.rect,s=v(n,!0);s&&(i.top-=s.f,i.left-=s.e),n.toRect=i,n.thisAnimationDuration&&D(r,i)&&!D(o,i)&&(l.top-i.top)/(l.left-i.left)==(o.top-i.top)/(o.left-i.left)&&(t=l,s=r,r=a,a=c.options,e=Math.sqrt(Math.pow(s.top-t.top,2)+Math.pow(s.left-t.left,2))/Math.sqrt(Math.pow(s.top-r.top,2)+Math.pow(s.left-r.left,2))*a.animation),D(i,o)||(n.prevFromRect=o,n.prevToRect=i,e=e||c.options.animation,c.animate(n,l,i,e)),e&&(u=!0,d=Math.max(d,e),clearTimeout(n.animationResetTimer),n.animationResetTimer=setTimeout(function(){n.animationTime=0,n.prevFromRect=null,n.fromRect=null,n.prevToRect=null,n.thisAnimationDuration=null},e),n.thisAnimationDuration=e)}),clearTimeout(e),u?e=setTimeout(function(){"function"==typeof t&&t()},d):"function"==typeof t&&t(),o=[]},animate:function(t,e,n,o){var i,r;o&&(R(t,"transition",""),R(t,"transform",""),i=(r=v(this.el))&&r.a,r=r&&r.d,i=(e.left-n.left)/(i||1),r=(e.top-n.top)/(r||1),t.animatingX=!!i,t.animatingY=!!r,R(t,"transform","translate3d("+i+"px,"+r+"px,0)"),this.forRepaintDummy=t.offsetWidth,R(t,"transition","transform "+o+"ms"+(this.options.easing?" "+this.options.easing:"")),R(t,"transform","translate3d(0,0,0)"),"number"==typeof t.animated&&clearTimeout(t.animated),t.animated=setTimeout(function(){R(t,"transition",""),R(t,"transform",""),t.animated=!1,t.animatingX=!1,t.animatingY=!1},o))}}}var A=[],N={initializeByDefault:!0},W={mount:function(e){for(var t in N)!N.hasOwnProperty(t)||t in e||(e[t]=N[t]);A.forEach(function(t){if(t.pluginName===e.pluginName)throw"Sortable: Cannot mount plugin ".concat(e.pluginName," more than once")}),A.push(e)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+"Global";A.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](I({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](I({sortable:n},o)))})},initializePlugins:function(n,o,i,t){for(var e in A.forEach(function(t){var e=t.pluginName;(n.options[e]||t.initializeByDefault)&&((t=new t(n,o,n.options)).sortable=n,t.options=n.options,n[e]=t,a(i,t.defaults))}),n.options){var r;n.options.hasOwnProperty(e)&&(void 0!==(r=this.modifyOption(n,e,n.options[e]))&&(n.options[e]=r))}},getEventProperties:function(e,n){var o={};return A.forEach(function(t){"function"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return A.forEach(function(t){e[t.pluginName]&&t.optionListeners&&"function"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}};function z(t){var e=t.sortable,n=t.rootEl,o=t.name,i=t.targetEl,r=t.cloneEl,a=t.toEl,l=t.fromEl,s=t.oldIndex,c=t.newIndex,u=t.oldDraggableIndex,d=t.newDraggableIndex,h=t.originalEvent,f=t.putSortable,p=t.extraEventProperties;if(e=e||n&&n[K]){var g,m=e.options,t="on"+o.charAt(0).toUpperCase()+o.substr(1);!window.CustomEvent||y||w?(g=document.createEvent("Event")).initEvent(o,!0,!0):g=new CustomEvent(o,{bubbles:!0,cancelable:!0}),g.to=a||n,g.from=l||n,g.item=i||n,g.clone=r,g.oldIndex=s,g.newIndex=c,g.oldDraggableIndex=u,g.newDraggableIndex=d,g.originalEvent=h,g.pullMode=f?f.lastPutMode:void 0;var v,b=I(I({},p),W.getEventProperties(o,e));for(v in b)g[v]=b[v];n&&n.dispatchEvent(g),m[t]&&m[t].call(e,g)}}function G(t,e){var n=(o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{}).evt,o=i(o,U);W.pluginEvent.bind(Ft)(t,e,I({dragEl:V,parentEl:Z,ghostEl:$,rootEl:Q,nextEl:J,lastDownEl:tt,cloneEl:et,cloneHidden:nt,dragStarted:gt,putSortable:st,activeSortable:Ft.active,originalEvent:n,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at,hideGhostForTarget:Rt,unhideGhostForTarget:Xt,cloneNowHidden:function(){nt=!0},cloneNowShown:function(){nt=!1},dispatchSortableEvent:function(t){q({sortable:e,name:t,originalEvent:n})}},o))}var U=["evt"];function q(t){z(I({putSortable:st,cloneEl:et,targetEl:V,rootEl:Q,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at},t))}var V,Z,$,Q,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,mt,vt,bt,yt,wt=!1,Et=!1,Dt=[],St=!1,_t=!1,Ct=[],Tt=!1,xt=[],Ot="undefined"!=typeof document,Mt=n,At=w||y?"cssFloat":"float",Nt=Ot&&!c&&!n&&"draggable"in document.createElement("div"),It=function(){if(Ot){if(y)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Pt=function(t,e){var n=R(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=B(t,0,e),r=B(t,1,e),a=i&&R(i),l=r&&R(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+X(i).width,t=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+X(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){e="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==e?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||o<=s&&"none"===n[At]||r&&"none"===n[At]&&o<s+t)?"vertical":"horizontal"},kt=function(t){function l(r,a){return function(t,e,n,o){var i=t.options.group.name&&e.options.group.name&&t.options.group.name===e.options.group.name;if(null==r&&(a||i))return!0;if(null==r||!1===r)return!1;if(a&&"clone"===r)return r;if("function"==typeof r)return l(r(t,e,n,o),a)(t,e,n,o);e=(a?t:e).options.group.name;return!0===r||"string"==typeof r&&r===e||r.join&&-1<r.indexOf(e)}}var e={},n=t.group;n&&"object"==o(n)||(n={name:n}),e.name=n.name,e.checkPull=l(n.pull,!0),e.checkPut=l(n.put),e.revertClone=n.revertClone,t.group=e},Rt=function(){!It&&$&&R($,"display","none")},Xt=function(){!It&&$&&R($,"display","")};Ot&&!c&&document.addEventListener("click",function(t){if(Et)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),Et=!1},!0);function Yt(t){if(V){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,Dt.some(function(t){var e=t[K].options.emptyInsertThreshold;if(e&&!F(t)){var n=X(t),o=i>=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),G("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),G("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style["will-change"]="all",o=function(){G("delayEnded",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:"choose",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(V,t.trim(),Ht)}),h(l,"dragover",Yt),h(l,"mousemove",Yt),h(l,"touchmove",Yt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(V,"dragend",this),h(Q,"dragstart",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}$&&(r?(r.e+=a-(dt||0),r.f+=l-(ht||0)):r={a:1,b:0,c:0,d:1,e:a,f:l},r="matrix(".concat(r.a,",").concat(r.b,",").concat(r.c,",").concat(r.d,",").concat(r.e,",").concat(r.f,")"),R($,"webkitTransform",r),R($,"mozTransform",r),R($,"msTransform",r),R($,"transform",r),dt=a,ht=l,ut=i),t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!$){var t=this.options.fallbackOnBody?document.body:Q,e=X(V,!0,Mt,!0,t),n=this.options;if(Mt){for(yt=t;"static"===R(yt,"position")&&"none"===R(yt,"transform")&&yt!==document;)yt=yt.parentNode;yt!==document.body&&yt!==document.documentElement?(yt===document&&(yt=O()),e.top+=yt.scrollTop,e.left+=yt.scrollLeft):yt=O(),Ct=E(yt)}k($=V.cloneNode(!0),n.ghostClass,!1),k($,n.fallbackClass,!0),k($,n.dragClass,!0),R($,"transition",""),R($,"transform",""),R($,"box-sizing","border-box"),R($,"margin",0),R($,"top",e.top),R($,"left",e.left),R($,"width",e.width),R($,"height",e.height),R($,"opacity","0.8"),R($,"position",Mt?"absolute":"fixed"),R($,"zIndex","100000"),R($,"pointerEvents","none"),Ft.ghost=$,t.appendChild($),R($,"transform-origin",ft/parseInt($.style.width)*100+"% "+pt/parseInt($.style.height)*100+"%")}},_onDragStart:function(t,e){var n=this,o=t.dataTransfer,i=n.options;G("dragStart",this,{evt:t}),Ft.eventCanceled?this._onDrop():(G("setupClone",this),Ft.eventCanceled||((et=_(V)).removeAttribute("id"),et.draggable=!1,et.style["will-change"]="",this._hideClone(),k(et,this.options.chosenClass,!1),Ft.clone=et),n.cloneId=Kt(function(){G("clone",n),Ft.eventCanceled||(n.options.removeCloneOnHide||Q.insertBefore(et,V),n._hideClone(),q({sortable:n,name:"clone"}))}),e||k(V,i.dragClass,!0),e?(Et=!0,n._loopId=setInterval(n._emulateDragOver,50)):(f(document,"mouseup",n._onDrop),f(document,"touchend",n._onDrop),f(document,"touchcancel",n._onDrop),o&&(o.effectAllowed="move",i.setData&&i.setData.call(n,o,V)),h(document,"drop",n),R(V,"transform","translateZ(0)")),wt=!0,n._dragStartId=Kt(n._dragStarted.bind(n,e,t)),h(document,"selectstart",n),gt=!0,u&&R(document.body,"user-select","none"))},_onDragOver:function(n){var o,i,r,t,e,a=this.el,l=n.target,s=this.options,c=s.group,u=Ft.active,d=lt===c,h=s.sort,f=st||u,p=this,g=!1;if(!Tt){if(void 0!==n.preventDefault&&n.cancelable&&n.preventDefault(),l=P(l,s.draggable,a,!0),O("dragOver"),Ft.eventCanceled)return g;if(V.contains(n.target)||l.animated&&l.animatingX&&l.animatingY||p._ignoreWhileAnimating===l)return A(!1);if(Et=!1,u&&!s.disabled&&(d?h||(i=Z!==Q):st===this||(this.lastPutMode=lt.checkPull(this,u,V,n))&&c.checkPut(this,u,V,n))){if(r="vertical"===this._getDirection(n,l),o=X(V),O("dragOverValid"),Ft.eventCanceled)return g;if(i)return Z=Q,M(),this._hideClone(),O("revert"),Ft.eventCanceled||(J?Q.insertBefore(V,J):Q.appendChild(V)),A(!0);var m=F(a,s.draggable);if(m&&(S=n,c=r,x=X(F((D=this).el,D.options.draggable)),D=L(D.el,D.options,$),!(c?S.clientX>D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY<C.top&&t.clientX<C.right:t.clientY<_.top-10||t.clientY<C.bottom&&t.clientX<C.left)){var v=B(a,0,s,!0);if(v===V)return A(!1);if(E=X(l=v),!1!==jt(Q,a,V,o,l,E,n,!1))return M(),a.insertBefore(V,v),Z=a,N(),A(!0)}else if(l.parentNode===a){var b,y,w,E=X(l),D=V.parentNode!==a,S=(S=V.animated&&V.toRect||o,x=l.animated&&l.toRect||E,_=(e=r)?S.left:S.top,t=e?S.right:S.bottom,C=e?S.width:S.height,v=e?x.left:x.top,S=e?x.right:x.bottom,x=e?x.width:x.height,!(_===v||t===S||_+C/2===v+x/2)),_=r?"top":"left",C=Y(l,"top","top")||Y(V,"top","top"),v=C?C.scrollTop:void 0;if(mt!==l&&(y=E[_],St=!1,_t=!S&&s.invertSwap||D),0!==(b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,t=o?n.top:n.left,o=o?n.bottom:n.right,n=!1;if(!a)if(l&&bt<c*i){if(St=!St&&(1===vt?t+c*r/2<s:s<o-c*r/2)?!0:St)n=!0;else if(1===vt?s<t+bt:o-bt<s)return-vt}else if(t+c*(1-i)/2<s&&s<o-c*(1-i)/2)return function(t){return j(V)<j(t)?1:-1}(e);if((n=n||a)&&(s<t+c*r/2||o-c*r/2<s))return t+c/2<s?1:-1;return 0}(n,l,E,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,_t,mt===l)))for(var T=j(V);(w=Z.children[T-=b])&&("none"===R(w,"display")||w===$););if(0===b||w===l)return A(!1);vt=b;var x=(mt=l).nextElementSibling,D=!1,S=jt(Q,a,V,o,l,E,n,D=1===b);if(!1!==S)return 1!==S&&-1!==S||(D=1===S),Tt=!0,setTimeout(Lt,30),M(),D&&!x?a.appendChild(V):l.parentNode.insertBefore(V,D?x:l),C&&H(C,0,v-C.scrollTop),Z=V.parentNode,void 0===y||_t||(bt=Math.abs(y-X(l)[_])),N(),A(!0)}}else{if(m===V)return A(!1);if((l=m&&a===n.target?m:l)&&(E=X(l)),!1!==jt(Q,a,V,o,l,E,n,!!l))return M(),m&&m.nextSibling?a.insertBefore(V,m.nextSibling):a.appendChild(V),Z=a,N(),A(!0)}if(a.contains(V))return A(!1)}return!1}function O(t,e){G(t,p,I({evt:n,isOwner:d,axis:r?"vertical":"horizontal",revert:i,dragRect:o,targetRect:E,canSort:h,fromSortable:f,target:l,completed:A,onMove:function(t,e){return jt(Q,a,V,o,t,X(t),n,e)},changed:N},e))}function M(){O("dragOverAnimationCapture"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function A(t){return O("dragOverCompleted",{insertion:t}),t&&(d?u._hideClone():u._showClone(p),p!==f&&(k(V,(st||u).options.ghostClass,!1),k(V,s.ghostClass,!0)),st!==p&&p!==Ft.active?st=p:p===Ft.active&&st&&(st=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll(function(){O("dragOverAnimationComplete"),p._ignoreWhileAnimating=null}),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===V&&!V.animated||l===a&&!l.animated)&&(mt=null),s.dragoverBubble||n.rootEl||l===document||(V.parentNode[K]._isOutsideThisEl(n.target),t||Yt(n)),!s.dragoverBubble&&n.stopPropagation&&n.stopPropagation(),g=!0}function N(){it=j(V),at=j(V,s.draggable),q({sortable:p,name:"change",toEl:a,newIndex:it,newDraggableIndex:at,originalEvent:n})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){f(document,"mousemove",this._onTouchMove),f(document,"touchmove",this._onTouchMove),f(document,"pointermove",this._onTouchMove),f(document,"dragover",Yt),f(document,"mousemove",Yt),f(document,"touchmove",Yt)},_offUpEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._onDrop),f(t,"touchend",this._onDrop),f(t,"pointerup",this._onDrop),f(t,"touchcancel",this._onDrop),f(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;it=j(V),at=j(V,n.draggable),G("drop",this,{evt:t}),Z=V&&V.parentNode,it=j(V),at=j(V,n.draggable),Ft.eventCanceled||(St=_t=wt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Wt(this.cloneId),Wt(this._dragStartId),this.nativeDraggable&&(f(document,"drop",this),f(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),u&&R(document.body,"user-select",""),R(V,"transform",""),t&&(gt&&(t.cancelable&&t.preventDefault(),n.dropBubble||t.stopPropagation()),$&&$.parentNode&&$.parentNode.removeChild($),(Q===Z||st&&"clone"!==st.lastPutMode)&&et&&et.parentNode&&et.parentNode.removeChild(et),V&&(this.nativeDraggable&&f(V,"dragend",this),Ht(V),V.style["will-change"]="",gt&&!wt&&k(V,(st||this).options.ghostClass,!1),k(V,this.options.chosenClass,!1),q({sortable:this,name:"unchoose",toEl:Z,newIndex:null,newDraggableIndex:null,originalEvent:t}),Q!==Z?(0<=it&&(q({rootEl:Z,name:"add",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"remove",toEl:Z,originalEvent:t}),q({rootEl:Z,name:"sort",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),st&&st.save()):it!==ot&&0<=it&&(q({sortable:this,name:"update",toEl:Z,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),Ft.active&&(null!=it&&-1!==it||(it=ot,at=rt),q({sortable:this,name:"end",toEl:Z,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){G("nulling",this),Q=V=Z=$=J=et=tt=nt=ct=ut=gt=it=at=ot=rt=mt=vt=st=lt=Ft.dragged=Ft.ghost=Ft.clone=Ft.active=null,xt.forEach(function(t){t.checked=!0}),xt.length=dt=ht=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":V&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;o<i;o++)P(t=n[o],r.draggable,this.el,!1)&&e.push(t.getAttribute(r.dataIdAttr)||function(t){var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;for(;n--;)o+=e.charCodeAt(n);return o.toString(36)}(t));return e},sort:function(t,e){var n={},o=this.el;this.toArray().forEach(function(t,e){e=o.children[e];P(e,this.options.draggable,o,!1)&&(n[t]=e)},this),e&&this.captureAnimationState(),t.forEach(function(t){n[t]&&(o.removeChild(n[t]),o.appendChild(n[t]))}),e&&this.animateAll()},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return P(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];var o=W.modifyOption(this,t,e);n[t]=void 0!==o?o:e,"group"===t&&kt(n)},destroy:function(){G("destroy",this);var t=this.el;t[K]=null,f(t,"mousedown",this._onTapStart),f(t,"touchstart",this._onTapStart),f(t,"pointerdown",this._onTapStart),this.nativeDraggable&&(f(t,"dragover",this),f(t,"dragenter",this)),Array.prototype.forEach.call(t.querySelectorAll("[draggable]"),function(t){t.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),Dt.splice(Dt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){nt||(G("hideClone",this),Ft.eventCanceled||(R(et,"display","none"),this.options.removeCloneOnHide&&et.parentNode&&et.parentNode.removeChild(et),nt=!0))},_showClone:function(t){"clone"===t.lastPutMode?nt&&(G("showClone",this),Ft.eventCanceled||(V.parentNode!=Q||this.options.group.revertClone?J?Q.insertBefore(et,J):Q.appendChild(et):Q.insertBefore(et,V),this.options.group.revertClone&&this.animate(V,et),R(et,"display",""),nt=!1)):this._hideClone()}},Ot&&h(document,"touchmove",function(t){(Ft.active||wt)&&t.cancelable&&t.preventDefault()}),Ft.utils={on:h,off:f,css:R,find:b,is:function(t,e){return!!P(t,e,t,!1)},extend:function(t,e){if(t&&e)for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t},throttle:S,closest:P,toggleClass:k,clone:_,index:j,nextTick:Kt,cancelNextTick:Wt,detectDirection:Pt,getChild:B},Ft.get=function(t){return t[K]},Ft.mount=function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];(e=e[0].constructor===Array?e[0]:e).forEach(function(t){if(!t.prototype||!t.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(t));t.utils&&(Ft.utils=I(I({},Ft.utils),t.utils)),W.mount(t)})},Ft.create=function(t,e){return new Ft(t,e)};var zt,Gt,Ut,qt,Vt,Zt,$t=[],Qt=!(Ft.version="1.15.2");function Jt(){$t.forEach(function(t){clearInterval(t.pid)}),$t=[]}function te(){clearInterval(Zt)}var ee,ne=S(function(n,t,e,o){if(t.scroll){var i,r=(n.touches?n.touches[0]:n).clientX,a=(n.touches?n.touches[0]:n).clientY,l=t.scrollSensitivity,s=t.scrollSpeed,c=O(),u=!1;Gt!==e&&(Gt=e,Jt(),zt=t.scroll,i=t.scrollFn,!0===zt&&(zt=M(e,!0)));var d=0,h=zt;do{var f=h,p=X(f),g=p.top,m=p.bottom,v=p.left,b=p.right,y=p.width,w=p.height,E=void 0,D=void 0,S=f.scrollWidth,_=f.scrollHeight,C=R(f),T=f.scrollLeft,p=f.scrollTop,D=f===c?(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX||"visible"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY||"visible"===C.overflowY)):(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY)),T=E&&(Math.abs(b-r)<=l&&T+y<S)-(Math.abs(v-r)<=l&&!!T),p=D&&(Math.abs(m-a)<=l&&p+w<_)-(Math.abs(g-a)<=l&&!!p);if(!$t[d])for(var x=0;x<=d;x++)$t[x]||($t[x]={});$t[d].vx==T&&$t[d].vy==p&&$t[d].el===f||($t[d].el=f,$t[d].vx=T,$t[d].vy=p,clearInterval($t[d].pid),0==T&&0==p||(u=!0,$t[d].pid=setInterval(function(){o&&0===this.layer&&Ft.active._onTouchMove(Vt);var t=$t[this.layer].vy?$t[this.layer].vy*s:0,e=$t[this.layer].vx?$t[this.layer].vx*s:0;"function"==typeof i&&"continue"!==i.call(Ft.dragged.parentNode[K],e,t,n,Vt,$t[this.layer].el)||H($t[this.layer].el,e,t)}.bind({layer:d}),24))),d++}while(t.bubbleScroll&&h!==c&&(h=M(h,!1)));Qt=u}},30),c=function(t){var e=t.originalEvent,n=t.putSortable,o=t.dragEl,i=t.activeSortable,r=t.dispatchSortableEvent,a=t.hideGhostForTarget,t=t.unhideGhostForTarget;e&&(i=n||i,a(),e=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,e=document.elementFromPoint(e.clientX,e.clientY),t(),i&&!i.el.contains(e)&&(r("spill"),this.onSpill({dragEl:o,putSortable:n})))};function oe(){}function ie(){}oe.prototype={startIndex:null,dragStart:function(t){t=t.oldDraggableIndex;this.startIndex=t},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();t=B(this.sortable.el,this.startIndex,this.options);t?this.sortable.el.insertBefore(e,t):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:c},a(oe,{pluginName:"revertOnSpill"}),ie.prototype={onSpill:function(t){var e=t.dragEl,t=t.putSortable||this.sortable;t.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),t.animateAll()},drop:c},a(ie,{pluginName:"removeOnSpill"});var re,ae,le,se,ce,ue=[],de=[],he=!1,fe=!1,pe=!1;function ge(n,o){de.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)})}function me(){ue.forEach(function(t){t!==le&&t.parentNode&&t.parentNode.removeChild(t)})}return Ft.mount(new function(){function t(){for(var t in this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0},this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this))}return t.prototype={dragStarted:function(t){t=t.originalEvent;this.sortable.nativeDraggable?h(document,"dragover",this._handleAutoScroll):this.options.supportPointer?h(document,"pointermove",this._handleFallbackAutoScroll):t.touches?h(document,"touchmove",this._handleFallbackAutoScroll):h(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(t){t=t.originalEvent;this.options.dragOverBubble||t.rootEl||this._handleAutoScroll(t)},drop:function(){this.sortable.nativeDraggable?f(document,"dragover",this._handleAutoScroll):(f(document,"pointermove",this._handleFallbackAutoScroll),f(document,"touchmove",this._handleFallbackAutoScroll),f(document,"mousemove",this._handleFallbackAutoScroll)),te(),Jt(),clearTimeout(g),g=void 0},nulling:function(){Vt=Gt=zt=Qt=Zt=Ut=qt=null,$t.length=0},_handleFallbackAutoScroll:function(t){this._handleAutoScroll(t,!0)},_handleAutoScroll:function(e,n){var o,i=this,r=(e.touches?e.touches[0]:e).clientX,a=(e.touches?e.touches[0]:e).clientY,t=document.elementFromPoint(r,a);Vt=e,n||this.options.forceAutoScrollFallback||w||y||u?(ne(e,this.options,t,n),o=M(t,!0),!Qt||Zt&&r===Ut&&a===qt||(Zt&&te(),Zt=setInterval(function(){var t=M(document.elementFromPoint(r,a),!0);t!==o&&(o=t,Jt()),ne(e,i.options,t,n)},10),Ut=r,qt=a)):this.options.bubbleScroll&&M(t,!0)!==O()?ne(e,this.options,M(t,!1),!1):Jt()}},a(t,{pluginName:"scroll",initializeByDefault:!0})}),Ft.mount(ie,oe),Ft.mount(new function(){function t(){this.defaults={swapClass:"sortable-swap-highlight"}}return t.prototype={dragStart:function(t){t=t.dragEl;ee=t},dragOverValid:function(t){var e=t.completed,n=t.target,o=t.onMove,i=t.activeSortable,r=t.changed,a=t.cancel;i.options.swap&&(t=this.sortable.el,i=this.options,n&&n!==t&&(t=ee,ee=!1!==o(n)?(k(n,i.swapClass,!0),n):null,t&&t!==ee&&k(t,i.swapClass,!1)),r(),e(!0),a())},drop:function(t){var e,n,o=t.activeSortable,i=t.putSortable,r=t.dragEl,a=i||this.sortable,l=this.options;ee&&k(ee,l.swapClass,!1),ee&&(l.swap||i&&i.options.swap)&&r!==ee&&(a.captureAnimationState(),a!==o&&o.captureAnimationState(),n=ee,t=(e=r).parentNode,l=n.parentNode,t&&l&&!t.isEqualNode(n)&&!l.isEqualNode(e)&&(i=j(e),r=j(n),t.isEqualNode(l)&&i<r&&r++,t.insertBefore(n,t.children[i]),l.insertBefore(e,l.children[r])),a.animateAll(),a!==o&&o.animateAll())},nulling:function(){ee=null}},a(t,{pluginName:"swap",eventProperties:function(){return{swapItem:ee}}})}),Ft.mount(new function(){function t(o){for(var t in this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this));o.options.avoidImplicitDeselect||(o.options.supportPointer?h(document,"pointerup",this._deselectMultiDrag):(h(document,"mouseup",this._deselectMultiDrag),h(document,"touchend",this._deselectMultiDrag))),h(document,"keydown",this._checkKeyDown),h(document,"keyup",this._checkKeyUp),this.defaults={selectedClass:"sortable-selected",multiDragKey:null,avoidImplicitDeselect:!1,setData:function(t,e){var n="";ue.length&&ae===o?ue.forEach(function(t,e){n+=(e?", ":"")+t.textContent}):n=e.textContent,t.setData("Text",n)}}}return t.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(t){t=t.dragEl;le=t},delayEnded:function(){this.isMultiDrag=~ue.indexOf(le)},setupClone:function(t){var e=t.sortable,t=t.cancel;if(this.isMultiDrag){for(var n=0;n<ue.length;n++)de.push(_(ue[n])),de[n].sortableIndex=ue[n].sortableIndex,de[n].draggable=!1,de[n].style["will-change"]="",k(de[n],this.options.selectedClass,!1),ue[n]===le&&k(de[n],this.options.chosenClass,!1);e._hideClone(),t()}},clone:function(t){var e=t.sortable,n=t.rootEl,o=t.dispatchSortableEvent,t=t.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||ue.length&&ae===e&&(ge(!0,n),o("clone"),t()))},showClone:function(t){var e=t.cloneNowShown,n=t.rootEl,t=t.cancel;this.isMultiDrag&&(ge(!1,n),de.forEach(function(t){R(t,"display","")}),e(),ce=!1,t())},hideClone:function(t){var e=this,n=(t.sortable,t.cloneNowHidden),t=t.cancel;this.isMultiDrag&&(de.forEach(function(t){R(t,"display","none"),e.options.removeCloneOnHide&&t.parentNode&&t.parentNode.removeChild(t)}),n(),ce=!0,t())},dragStartGlobal:function(t){t.sortable;!this.isMultiDrag&&ae&&ae.multiDrag._deselectMultiDrag(),ue.forEach(function(t){t.sortableIndex=j(t)}),ue=ue.sort(function(t,e){return t.sortableIndex-e.sortableIndex}),pe=!0},dragStarted:function(t){var e,n=this,t=t.sortable;this.isMultiDrag&&(this.options.sort&&(t.captureAnimationState(),this.options.animation&&(ue.forEach(function(t){t!==le&&R(t,"position","absolute")}),e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&C(t,e)}),he=fe=!0)),t.animateAll(function(){he=fe=!1,n.options.animation&&ue.forEach(function(t){T(t)}),n.options.sort&&me()}))},dragOver:function(t){var e=t.target,n=t.completed,t=t.cancel;fe&&~ue.indexOf(e)&&(n(!1),t())},revert:function(t){var n,o,e=t.fromSortable,i=t.rootEl,r=t.sortable,a=t.dragRect;1<ue.length&&(ue.forEach(function(t){r.addAnimationState({target:t,rect:fe?X(t):a}),T(t),t.fromRect=a,e.removeAnimationState(t)}),fe=!1,n=!this.options.removeCloneOnHide,o=i,ue.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)}))},dragOverCompleted:function(t){var e,n=t.sortable,o=t.isOwner,i=t.insertion,r=t.activeSortable,a=t.parentEl,l=t.putSortable,t=this.options;i&&(o&&r._hideClone(),he=!1,t.animation&&1<ue.length&&(fe||!o&&!r.options.sort&&!l)&&(e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&(C(t,e),a.appendChild(t))}),fe=!0),o||(fe||me(),1<ue.length?(o=ce,r._showClone(n),r.options.animation&&!ce&&o&&de.forEach(function(t){r.addAnimationState({target:t,rect:se}),t.fromRect=se,t.thisAnimationDuration=null})):r._showClone(n)))},dragOverAnimationCapture:function(t){var e=t.dragRect,n=t.isOwner,t=t.activeSortable;ue.forEach(function(t){t.thisAnimationDuration=null}),t.options.animation&&!n&&t.multiDrag.isMultiDrag&&(se=a({},e),e=v(le,!0),se.top-=e.f,se.left-=e.e)},dragOverAnimationComplete:function(){fe&&(fe=!1,me())},drop:function(t){var e=t.originalEvent,n=t.rootEl,o=t.parentEl,i=t.sortable,r=t.dispatchSortableEvent,a=t.oldIndex,l=t.putSortable,s=l||this.sortable;if(e){var c,u,d,h=this.options,f=o.children;if(!pe)if(h.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),k(le,h.selectedClass,!~ue.indexOf(le)),~ue.indexOf(le))ue.splice(ue.indexOf(le),1),re=null,z({sortable:i,rootEl:n,name:"deselect",targetEl:le,originalEvent:e});else{if(ue.push(le),z({sortable:i,rootEl:n,name:"select",targetEl:le,originalEvent:e}),e.shiftKey&&re&&i.el.contains(re)){var p=j(re),t=j(le);if(~p&&~t&&p!==t)for(var g,m=p<t?(g=p,t):(g=t,p+1);g<m;g++)~ue.indexOf(f[g])||(k(f[g],h.selectedClass,!0),ue.push(f[g]),z({sortable:i,rootEl:n,name:"select",targetEl:f[g],originalEvent:e}))}else re=le;ae=s}pe&&this.isMultiDrag&&(fe=!1,(o[K].options.sort||o!==n)&&1<ue.length&&(c=X(le),u=j(le,":not(."+this.options.selectedClass+")"),!he&&h.animation&&(le.thisAnimationDuration=null),s.captureAnimationState(),he||(h.animation&&(le.fromRect=c,ue.forEach(function(t){var e;t.thisAnimationDuration=null,t!==le&&(e=fe?X(t):c,t.fromRect=e,s.addAnimationState({target:t,rect:e}))})),me(),ue.forEach(function(t){f[u]?o.insertBefore(t,f[u]):o.appendChild(t),u++}),a===j(le)&&(d=!1,ue.forEach(function(t){t.sortableIndex!==j(t)&&(d=!0)}),d&&(r("update"),r("sort")))),ue.forEach(function(t){T(t)}),s.animateAll()),ae=s),(n===o||l&&"clone"!==l.lastPutMode)&&de.forEach(function(t){t.parentNode&&t.parentNode.removeChild(t)})}},nullingGlobal:function(){this.isMultiDrag=pe=!1,de.length=0},destroyGlobal:function(){this._deselectMultiDrag(),f(document,"pointerup",this._deselectMultiDrag),f(document,"mouseup",this._deselectMultiDrag),f(document,"touchend",this._deselectMultiDrag),f(document,"keydown",this._checkKeyDown),f(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(t){if(!(void 0!==pe&&pe||ae!==this.sortable||t&&P(t.target,this.options.draggable,this.sortable.el,!1)||t&&0!==t.button))for(;ue.length;){var e=ue[0];k(e,this.options.selectedClass,!1),ue.shift(),z({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:e,originalEvent:t})}},_checkKeyDown:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},a(t,{pluginName:"multiDrag",utils:{select:function(t){var e=t.parentNode[K];e&&e.options.multiDrag&&!~ue.indexOf(t)&&(ae&&ae!==e&&(ae.multiDrag._deselectMultiDrag(),ae=e),k(t,e.options.selectedClass,!0),ue.push(t))},deselect:function(t){var e=t.parentNode[K],n=ue.indexOf(t);e&&e.options.multiDrag&&~n&&(k(t,e.options.selectedClass,!1),ue.splice(n,1))}},eventProperties:function(){var n=this,o=[],i=[];return ue.forEach(function(t){var e;o.push({multiDragElement:t,index:t.sortableIndex}),e=fe&&t!==le?-1:fe?j(t,":not(."+n.options.selectedClass+")"):j(t),i.push({multiDragElement:t,index:e})}),{items:r(ue),clones:[].concat(de),oldIndicies:o,newIndicies:i}},optionListeners:{multiDragKey:function(t){return"ctrl"===(t=t.toLowerCase())?t="Control":1<t.length&&(t=t.charAt(0).toUpperCase()+t.substr(1)),t}}})}),Ft});';function st(n){let t=n.dashboard,o=n.boards;if(o.length===0){console.log(" No boards configured in agentx.json (boards[]). Nothing to serve.");return}let i=t.port,e=t.bind,r=t.token,m=new Map;for(let u of o)try{m.set(u.id,H(u,n))}catch(s){console.error(` Board "${u.id}" failed to initialize: ${s.message}`)}O(async(u,s)=>{try{await P(u,s,{boards:o,sources:m,token:r,config:n})}catch(y){c(s,500,{error:y.message||"internal error"})}}).listen(i,e,()=>{console.log(`
2
- Kanban dashboard: http://${e==="0.0.0.0"?"localhost":e}:${i}
3
- `),console.log(` Boards: ${o.map(s=>s.id).join(", ")}`),e==="127.0.0.1"&&!r?console.log(" (bound to localhost; no auth token \u2014 safe default)"):e!=="127.0.0.1"&&!r&&console.log(" \u26A0 bound to a non-loopback address without a token \u2014 writes are UNAUTHENTICATED")})}async function P(n,t,o){let i=new URL(n.url||"/","http://localhost"),e=i.pathname,r=(n.method||"GET").toUpperCase();if(t.setHeader("Access-Control-Allow-Origin","*"),t.setHeader("Access-Control-Allow-Methods","GET, POST, PATCH, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),r==="OPTIONS"){t.writeHead(204),t.end();return}if(r==="GET"&&e==="/"){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(K());return}if(r==="GET"&&e==="/live"){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(Y());return}if(r==="GET"&&e==="/sortable.min.js"){t.writeHead(200,{"Content-Type":"application/javascript; charset=utf-8","Cache-Control":"public, max-age=86400"}),t.end(M);return}if(r==="GET"&&e==="/api/live"){try{let a=await L(o.config);c(t,200,a)}catch(a){c(t,502,{error:a.message})}return}if(r==="GET"&&e==="/api/live/stream"){X(n,t,o.config);return}if(r==="GET"&&e==="/api/agents"){try{let a={};o.config.dashboard.token&&(a.Authorization=`Bearer ${o.config.dashboard.token}`);let l=await fetch(o.config.dashboard.daemonUrl.replace(/\/+$/,"")+"/agents",{headers:a});if(!l.ok)throw new Error(`HTTP ${l.status}`);let v=await l.json();c(t,200,v)}catch(a){c(t,502,{error:a.message||"agents fetch failed"})}return}if(r==="POST"&&e==="/api/draft"){let a=await w(n);try{let l=await W(o.config,a);c(t,200,l)}catch(l){c(t,502,{error:l.message||"draft failed"})}return}if(e.startsWith("/api/")&&o.token){let a=n.headers.authorization||"";if((a.startsWith("Bearer ")?a.slice(7):"")!==o.token){c(t,401,{error:"unauthorized"});return}}if((r==="POST"||r==="PATCH"||r==="DELETE")&&e.startsWith("/api/")&&n.headers["x-requested-with"]!=="agentx-board"){c(t,400,{error:"missing X-Requested-With: agentx-board"});return}if(r==="GET"&&e==="/api/boards"){c(t,200,o.boards.map(a=>({id:a.id,name:a.name,source:a.source,primaryToolLabel:a.primaryToolLabel,labels:a.labels,columns:a.columns,timeRangeDays:a.timeRangeDays})));return}let f=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/members$/);if(f&&r==="GET"){let a=o.sources.get(f[1]);if(!a){c(t,404,{error:"unknown board"});return}try{let l=typeof a.listMembers=="function"?await a.listMembers():[];c(t,200,l)}catch(l){c(t,502,{error:l.message})}return}let u=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/milestones$/);if(u&&r==="GET"){let a=o.sources.get(u[1]);if(!a){c(t,404,{error:"unknown board"});return}try{let l=typeof a.listMilestones=="function"?await a.listMilestones():[];c(t,200,l)}catch(l){c(t,502,{error:l.message})}return}let s=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/items\/(.+?)\/move$/),y=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/items(?:\/(.+))?$/);if(s&&r==="PATCH"){let[,a,l]=s,v=o.boards.find(d=>d.id===a);if(!v){c(t,404,{error:`unknown board: ${a}`});return}let h=o.sources.get(a);if(!h){c(t,503,{error:`board "${a}" source unavailable`});return}let p=await w(n),g=v.columns.find(d=>d.id===p.to),k=v.columns.find(d=>d.id===p.from);if(!g){c(t,400,{error:`unknown 'to' column: ${p.to}`});return}try{await z(h,l,k,g),E({actor:S(n),action:"move",boardId:a,itemId:l,payload:{from:p.from,to:p.to}}),c(t,200,{ok:!0})}catch(d){c(t,502,{error:d.message||"transition failed"})}return}if(y){let[,a,l]=y,v=o.boards.find(p=>p.id===a);if(!v){c(t,404,{error:`unknown board: ${a}`});return}let h=o.sources.get(a);if(!h){c(t,503,{error:`board "${a}" source unavailable`});return}if(r==="GET"&&!l){let{columns:p,totals:g}=await $(v,h,i.searchParams);c(t,200,{columns:p,totals:g});return}if(r==="GET"&&l){try{let p=typeof h.getItem=="function"?await h.getItem(l):null;if(!p){c(t,404,{error:"not found"});return}c(t,200,p)}catch(p){c(t,502,{error:p.message})}return}if(r==="PATCH"&&l){let p=await w(n);try{let g=typeof h.updateItem=="function"?await h.updateItem(l,p):null;if(!g){c(t,501,{error:"update unsupported"});return}E({actor:S(n),action:"update",boardId:a,itemId:l,payload:p}),c(t,200,g)}catch(g){c(t,502,{error:g.message})}return}if(r==="POST"&&!l){let p=await w(n);if(!p.title){c(t,400,{error:"title required"});return}if(!h.capabilities.create||!h.create){c(t,400,{error:`source ${h.type} does not support create`});return}try{let g=await h.create({title:p.title,description:p.description,labels:p.labels,assigneeUsernames:p.assigneeUsernames,milestoneTitle:p.milestoneTitle,projectHint:p.projectHint});E({actor:S(n),action:"create",boardId:a,itemId:g.id,payload:{title:p.title}}),c(t,201,g)}catch(g){c(t,502,{error:g.message})}return}}c(t,404,{error:"not found",path:e})}async function $(n,t,o){if(!t.capabilities.listAll||!t.listAll){let d={};for(let b of n.columns)d[b.id]=[];return{columns:d,totals:Object.fromEntries(n.columns.map(b=>[b.id,0]))}}let i=[];n.primaryToolLabel&&i.push(n.primaryToolLabel);let e=o.get("label")||void 0;e&&i.push(e);let r=o.get("q")||void 0,m=o.get("milestone")||void 0,f=o.get("assignee")||void 0,u=parseInt(o.get("days")||String(n.timeRangeDays),10),s=parseInt(o.get("closedDays")||String(n.closedWindowDays??30),10),y=n.columns.some(d=>d.kind!=="closed"),a=n.columns.some(d=>d.kind==="closed"),l=y?await t.listAll({sinceDays:u,labels:i.length?i:void 0,search:r,milestone:m,state:"opened"}):[],v=a?await t.listAll({sinceDays:s,labels:i.length?i:void 0,search:r,milestone:m,state:"closed"}):[],h={};for(let d of n.columns)h[d.id]=[];let p=n.columns.find(d=>d.kind==="closed");for(let d of v)p&&h[p.id].push({...d,stage:p.id});let g=n.columns.filter(d=>d.kind!=="closed");for(let d of l){let b=A(d,g);if(!b)continue;let x=h[b];x&&x.push({...d,stage:b})}if(f)for(let d of Object.keys(h))h[d]=h[d].filter(b=>b.assignee===f);for(let d of Object.keys(h))h[d].sort((b,x)=>{let D=(b.priority??99)-(x.priority??99);return D!==0?D:(x.updatedAt||"").localeCompare(b.updatedAt||"")});let k=Object.fromEntries(Object.entries(h).map(([d,b])=>[d,b.length]));return{columns:h,totals:k}}async function z(n,t,o,i){let e=T(o,i),r=n;e.closeIssue&&typeof r.setState=="function"?await r.setState(t,"close"):e.reopen&&typeof r.setState=="function"&&await r.setState(t,"reopen"),(e.addLabels?.length||e.removeLabels?.length)&&typeof r.transitionMany=="function"?await r.transitionMany(t,e.addLabels||[],e.removeLabels||[]):e.addLabels?.length&&n.transition&&await n.transition(t,e.addLabels[0],e.removeLabels?.[0])}function H(n,t){if(n.source.type==="gitlab"){let o=t.channels.gitlab;if(!o?.token)throw new Error("channels.gitlab.token is required for gitlab boards");let i={};for(let e of o.agentMappings)e.gitlabUsernames.length&&(i[e.agentId]=e.gitlabUsernames);return new I(o.host,o.token,n.source.projects,i,(...e)=>console.log("[board]",...e))}throw new Error(`unsupported board source: ${n.source.type}`)}function E(n){try{let t=_(process.cwd(),".agentx");R(t)||B(t,{recursive:!0});let o=JSON.stringify({ts:new Date().toISOString(),...n})+`
4
- `;N(_(t,"board-audit.jsonl"),o)}catch{}}function S(n){let t=n.headers["x-forwarded-for"];return(Array.isArray(t)?t[0]:t?.split(",")[0])||n.socket.remoteAddress||"unknown"}async function j(n,t,o){let i={};t&&(i.Authorization=`Bearer ${t}`);let e={id:n,name:n,url:n,reachable:!1,agents:[]};try{let[r,m,f]=await Promise.all([fetch(n+"/health",{headers:i,signal:o}).catch(()=>null),fetch(n+"/agents",{headers:i,signal:o}).catch(()=>null),fetch(n+"/mesh",{headers:i,signal:o}).catch(()=>null)]);if(!m||!m.ok)return e.error=m?`HTTP ${m.status}`:"unreachable",e;let u=await m.json();if(e.agents=u.map(s=>({id:s.id,name:s.name,tier:s.tier,model:s.model,active:s.active||0,total:s.total||0,errors:s.errors||0,lastActive:s.lastActive,runningTasks:Array.isArray(s.runningTasks)?s.runningTasks:[]})),r&&r.ok){let s=await r.json();e.uptimeSec=s.uptime,e.name=s.node?.name||s.node?.id||n,e.id=s.node?.id||n}e.reachable=!0,e._peers=Array.isArray(f&&await F(f))?e._peers:void 0}catch(r){e.error=r.message||String(r)}return e}var C=new WeakMap;async function F(n){if(C.has(n))return C.get(n);try{let t=n.ok?await n.json():null;return C.set(n,t),t}catch{return null}}async function V(n,t,o){try{let i={};t&&(i.Authorization=`Bearer ${t}`);let e=await fetch(n+"/mesh",{headers:i,signal:o});return e.ok?(await e.json()).filter(m=>m&&m.peerUrl).map(m=>({url:String(m.peerUrl).replace(/\/+$/,""),name:m.peer||m.peerUrl})):[]}catch{return[]}}async function L(n){let t=n.dashboard,o=t.daemonUrl.replace(/\/+$/,""),i=t.token,e=new Map;e.set(o,{name:"primary",url:o,token:i});for(let f of t.daemons){let u=f.url.replace(/\/+$/,"");e.has(u)||e.set(u,{name:f.name,url:u,token:f.token})}let r=new AbortController,m=setTimeout(()=>r.abort(),3e3);try{let f=await V(o,i,r.signal);for(let s of f)e.has(s.url)||e.set(s.url,s);let u=await Promise.all([...e.values()].map(s=>j(s.url,s.token,r.signal)));return{ts:new Date().toISOString(),nodes:u}}finally{clearTimeout(m)}}function X(n,t,o){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let i=!1,e=(f,u)=>{i||t.write(`event: ${f}
5
- data: ${JSON.stringify(u)}
6
-
7
- `)},r=async()=>{try{let f=await L(o);e("snapshot",f)}catch(f){e("error",{message:f.message})}};r();let m=setInterval(r,2e3);n.on("close",()=>{i=!0,clearInterval(m),t.end()})}var U=`
8
- You are helping draft a new GitLab issue. Follow these conventions strictly.
9
-
10
- Title pattern:
11
- <Kind>: <page|component> \u2013 <short symptom>
12
- Kinds: Bug, UX, Copy, Perf, A11y, Security, UI, Incident (defects)
13
- Feat, Enh, Deprecate (features)
14
- Epic, Story, Spike, Req (planning)
15
- Data, Env, Test, Refactor, Chore, Task, Mgmt (technical)
16
-
17
- Title guidance:
18
- - Be specific about the location ("Contact page", not "frontend").
19
- - Use lowercase for the symptom.
20
- - Keep the symptom 4-7 words max, present tense.
21
- - Do NOT include issue IDs.
22
-
23
- Description structure (Markdown):
24
- For bugs: Problem / Steps to Reproduce / Expected / Actual / Environment / Impact.
25
- For features/stories: User Story (As a \u2026 I want \u2026 So that \u2026) + Acceptance Criteria bullets.
26
- Otherwise: concise problem statement + any relevant context.
27
-
28
- Labels:
29
- - Pick ONE Role label from: Dev, Design, QA, Ops, PM, mgmt.
30
- - Pick ONE Kind label matching the title prefix (bug, task, ux, ui, perf, security, copy, enhancement, \u2026).
31
- - Pick ONE Priority: critical, high, medium, low.
32
- - Pick ONE Difficulty: diff-XS, diff-S, diff-M, diff-L, diff-XL.
33
- - Optional Area/feature labels if clearly applicable from the known label list.
34
-
35
- Return ONLY a single JSON object on one line \u2014 no prose, no code fences, no explanation.
36
- Shape:
37
- { "title": string, "description": string, "labels": string[], "assigneeUsername": string | null, "kind": string }
38
- If you don't have enough information to pick a label confidently, omit it rather than guessing.
39
- `;async function W(n,t){let o=(t?.rough||"").toString().trim();if(!o)throw new Error("rough description required");let i=t?.agent||n.dashboard.draftAgent;if(!i)throw new Error("no draft agent configured (set dashboard.draftAgent or pass agent in body)");let e=t?.context||{},r=["Rough description from the user:",'"""',o,'"""',"",`Board: ${e.boardName||"(unspecified)"}`,e.primaryLabel?`Primary board label (include in labels): ${e.primaryLabel}`:"",e.columnLabel?`Target column label (include in labels): ${e.columnLabel}`:"",e.project?`GitLab project: ${e.project}`:"",e.knownLabels?.length?`Known labels in this project (prefer picking from these when they fit):
40
- - ${e.knownLabels.slice(0,60).join(`
41
- - `)}`:"",e.members?.length?`Possible assignees (GitLab usernames): ${e.members.slice(0,20).join(", ")}`:"","","Produce the JSON draft now."].filter(Boolean).join(`
42
- `),m=n.dashboard.daemonUrl.replace(/\/+$/,"")+"/task",f={"Content-Type":"application/json"};n.dashboard.token&&(f.Authorization=`Bearer ${n.dashboard.token}`);let u=await fetch(m,{method:"POST",headers:f,body:JSON.stringify({agent:i,message:r,context:{channel:"a2a",sender:"board-dashboard",systemPrompt:U}})});if(!u.ok)throw new Error(`daemon /task HTTP ${u.status}: ${await u.text().catch(()=>"")}`);let s=await u.json();if(s.error)throw new Error(String(s.error));let y=(s.content||"").toString(),a=G(y);if(!a||typeof a.title!="string")throw new Error("agent did not return a valid JSON draft. Raw: "+y.slice(0,400));let l=Array.isArray(a.labels)?a.labels.filter(v=>typeof v=="string"):[];return e.primaryLabel&&!l.includes(e.primaryLabel)&&l.unshift(e.primaryLabel),e.columnLabel&&!l.includes(e.columnLabel)&&l.unshift(e.columnLabel),{title:a.title.trim(),description:(a.description||"").toString(),labels:l,assigneeUsername:typeof a.assigneeUsername=="string"?a.assigneeUsername:void 0,kind:typeof a.kind=="string"?a.kind:void 0,raw:y}}function G(n){if(!n)return null;try{return JSON.parse(n)}catch{}let t=n.match(/```(?:json)?\s*([\s\S]*?)```/);if(t)try{return JSON.parse(t[1])}catch{}let o=n.indexOf("{");if(o===-1)return null;let i=0;for(let e=o;e<n.length;e++){let r=n[e];if(r==="{")i++;else if(r==="}"&&(i--,i===0))try{return JSON.parse(n.slice(o,e+1))}catch{return null}}return null}function c(n,t,o){n.writeHead(t,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify(o))}async function w(n){return new Promise((t,o)=>{let i=[];n.on("data",e=>i.push(e)),n.on("end",()=>{try{let e=Buffer.concat(i).toString("utf-8");t(e?JSON.parse(e):{})}catch(e){o(e)}}),n.on("error",o)})}function K(){return`<!DOCTYPE html>
43
- <html lang="en">
44
- <head>
45
- <meta charset="utf-8">
46
- <meta name="viewport" content="width=device-width, initial-scale=1">
47
- <title>AgentX Boards</title>
48
- <style>${Q}</style>
49
- </head>
50
- <body>
51
- <header>
52
- <div class="brand">AgentX</div>
53
- <select id="boardPicker" title="Switch board"></select>
54
- <div class="search">
55
- <span class="icon">\u2315</span>
56
- <input id="filterSearch" type="search" placeholder="Search or filter\u2026" autocomplete="off" />
57
- </div>
58
- <select id="filterAssignee" title="Assignee"><option value="">All assignees</option></select>
59
- <select id="filterMilestone" title="Milestone"><option value="">Any milestone</option></select>
60
- <div id="filterLabels" class="chips"></div>
61
- <div class="spacer"></div>
62
- <a href="/live" class="link" title="Live mesh view (L)">\u25CE Live</a>
63
- <button id="activityBtn" title="Agent activity (a)">\u25C9 Activity</button>
64
- <button id="newIssueBtn" title="New issue">+ New</button>
65
- <button id="refreshBtn" title="Refresh (r)">\u21BB</button>
66
- <div id="conn" class="conn ok" title="ready">\u25CF</div>
67
- </header>
68
- <aside id="activity-panel" class="activity-panel hidden" aria-hidden="true">
69
- <header>
70
- <h2>Agent Activity</h2>
71
- <span id="activity-source" class="source"></span>
72
- <button id="activity-close" class="x" title="Close">\u2715</button>
73
- </header>
74
- <div id="activity-body">Loading\u2026</div>
75
- <footer id="activity-events" class="events"></footer>
76
- </aside>
77
- <main id="columns"></main>
78
- <div id="toast"></div>
79
- <div id="modal" class="modal" aria-hidden="true">
80
- <div class="modal-backdrop"></div>
81
- <div class="modal-panel" role="dialog" aria-modal="true">
82
- <header class="modal-h">
83
- <div class="modal-title-row">
84
- <span id="m-iid" class="iid"></span>
85
- <input id="m-title" type="text" placeholder="Issue title" />
86
- </div>
87
- <button id="m-close" class="x" title="Close (esc)">\u2715</button>
88
- </header>
89
- <div class="modal-body">
90
- <div class="m-main">
91
- <div id="m-draft" class="draft-box" hidden>
92
- <label class="fld">
93
- <span>\u2728 Describe the issue (the agent will draft a proper title + description)</span>
94
- <textarea id="m-rough" rows="3" placeholder="e.g. when I upload a CSV over 500 rows in Receiving Records the import silently fails"></textarea>
95
- </label>
96
- <div class="draft-row">
97
- <select id="m-draft-agent" title="Drafting agent"><option value="">Agent\u2026</option></select>
98
- <button id="m-draft-go" class="btn-primary" type="button">\u2728 Draft</button>
99
- <span id="m-draft-hint" class="hint"></span>
100
- </div>
101
- </div>
102
- <label class="fld">
103
- <span>Description</span>
104
- <textarea id="m-desc" rows="8" placeholder="Markdown supported"></textarea>
105
- </label>
106
- <div class="fld">
107
- <span>Labels</span>
108
- <div id="m-labels" class="chip-picker"></div>
109
- </div>
110
- </div>
111
- <aside class="m-side">
112
- <div class="fld">
113
- <span>State</span>
114
- <div id="m-state" class="state-toggle">
115
- <button data-state="opened">Open</button>
116
- <button data-state="closed">Closed</button>
117
- </div>
118
- </div>
119
- <div class="fld">
120
- <span>Assignees</span>
121
- <div id="m-assignees" class="chip-picker"></div>
122
- </div>
123
- <div class="fld">
124
- <span>Milestone</span>
125
- <select id="m-milestone"><option value="">None</option></select>
126
- </div>
127
- <div class="fld muted">
128
- <span>Author</span>
129
- <div id="m-author"></div>
130
- </div>
131
- <div class="fld muted">
132
- <span>Created</span>
133
- <div id="m-created"></div>
134
- </div>
135
- <div class="fld muted">
136
- <span>Updated</span>
137
- <div id="m-updated"></div>
138
- </div>
139
- <a id="m-link" class="ext-link" href="#" target="_blank" rel="noopener">Open in GitLab \u2197</a>
140
- </aside>
141
- </div>
142
- <footer class="modal-f">
143
- <div id="m-hint" class="hint"></div>
144
- <div class="spacer"></div>
145
- <button id="m-cancel" class="btn-ghost">Cancel</button>
146
- <button id="m-save" class="btn-primary">Save</button>
147
- </footer>
148
- </div>
149
- </div>
150
- <script src="/sortable.min.js"></script>
151
- <script>${Z}</script>
152
- </body>
153
- </html>`}function Y(){return`<!DOCTYPE html>
154
- <html lang="en">
155
- <head>
156
- <meta charset="utf-8">
157
- <meta name="viewport" content="width=device-width, initial-scale=1">
158
- <title>AgentX \u2014 Live</title>
159
- <style>${q}</style>
160
- </head>
161
- <body>
162
- <header>
163
- <div class="brand">AgentX <span class="sub">\xB7 Live</span></div>
164
- <div id="summary" class="summary"></div>
165
- <div class="spacer"></div>
166
- <a href="/" class="link">\u2190 Boards</a>
167
- <span id="ts" class="ts" title="Last update"></span>
168
- <span id="conn" class="conn ok" title="connected">\u25CF</span>
169
- </header>
170
- <main id="grid"></main>
171
- <script>${J}</script>
172
- </body>
173
- </html>`}var q=`
174
- :root {
175
- --bg: #0b0d14; --card: #151823; --node: #1a1d29; --border: #2a2d3a;
176
- --text: #e6e8ef; --muted: #8b8fa3; --accent: #6366f1;
177
- --green: #22c55e; --yellow: #f59e0b; --red: #ef4444; --blue: #3b82f6; --gray: #6b7280;
178
- }
179
- * { box-sizing: border-box; }
180
- html, body { margin: 0; min-height: 100%; background: var(--bg); color: var(--text);
181
- font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
182
- header { display: flex; align-items: center; gap: 14px; padding: 10px 18px;
183
- background: #10131c; border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10; }
184
- header .brand { font-weight: 600; color: var(--accent); font-size: 15px; }
185
- header .brand .sub { color: var(--muted); font-weight: 500; }
186
- header .summary { display: flex; gap: 16px; color: var(--muted); font-size: 12px; }
187
- header .summary b { color: var(--text); }
188
- header .spacer { flex: 1; }
189
- header .link { color: var(--muted); text-decoration: none; font-size: 13px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 6px; }
190
- header .link:hover { color: var(--accent); border-color: var(--accent); }
191
- header .ts { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; }
192
- header .conn { font-size: 14px; }
193
- header .conn.ok { color: var(--green); }
194
- header .conn.warn { color: var(--yellow); }
195
- header .conn.err { color: var(--red); }
196
-
197
- main#grid { padding: 16px; display: flex; flex-direction: column; gap: 16px; }
198
-
199
- .node { background: var(--node); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
200
- .node > header { background: transparent; position: static; padding: 12px 16px; border-bottom: 1px solid var(--border); gap: 10px; }
201
- .node .name { font-weight: 600; font-size: 14px; }
202
- .node .url { color: var(--muted); font-family: ui-monospace, monospace; font-size: 11px; }
203
- .node .tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
204
- padding: 2px 7px; border-radius: 10px; background: rgba(99,102,241,0.2); color: #c5c8d6; }
205
- .node .tag.down { background: rgba(239,68,68,0.25); color: #fca5a5; }
206
- .node .tag.up { background: rgba(34,197,94,0.2); color: #86efac; }
207
- .node .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
208
- gap: 10px; padding: 12px 14px; }
209
-
210
- .agent { background: var(--card); border: 1px solid var(--border); border-radius: 8px;
211
- padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; transition: border-color 0.2s; }
212
- .agent.busy { border-color: var(--green); box-shadow: 0 0 0 1px rgba(34,197,94,0.3); }
213
- .agent.errored { border-color: var(--red); }
214
- .agent > .top { display: flex; align-items: center; gap: 8px; }
215
- .agent .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray); flex-shrink: 0; }
216
- .agent.busy .dot { background: var(--green); box-shadow: 0 0 6px var(--green); animation: pulse 1.2s ease-in-out infinite; }
217
- .agent.errored .dot { background: var(--red); }
218
- .agent .name { font-weight: 600; font-size: 13px; flex: 1; }
219
- .agent .tier { font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); padding: 2px 6px; border: 1px solid var(--border); border-radius: 4px; }
220
- .agent .meta { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--muted); flex-wrap: wrap; }
221
- .agent .model { font-family: ui-monospace, monospace; font-size: 10px; color: var(--accent);
222
- background: rgba(99,102,241,0.12); padding: 1px 7px; border-radius: 3px; letter-spacing: 0.2px; }
223
- .agent .last { font-size: 10px; color: var(--muted); }
224
- .agent .last.muted { font-style: italic; opacity: 0.7; }
225
- .agent .stats { display: flex; gap: 10px; font-size: 11px; color: var(--muted); }
226
- .agent .stats b { color: var(--text); font-weight: 600; }
227
- .agent .stats .err b { color: var(--red); }
228
- .agent .tasks { display: flex; flex-direction: column; gap: 6px; padding-top: 6px; border-top: 1px dashed var(--border); }
229
- .agent .task { display: flex; align-items: center; gap: 6px; font-size: 11px; }
230
- .agent .task .channel { background: rgba(99,102,241,0.18); color: var(--accent);
231
- font-size: 9px; text-transform: uppercase; padding: 1px 6px; border-radius: 3px; letter-spacing: 0.5px; }
232
- .agent .task .elapsed { color: var(--muted); font-family: ui-monospace, monospace; font-size: 10px; }
233
- .agent .task .preview { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
234
- .agent .idle { color: var(--muted); font-size: 11px; font-style: italic; }
235
-
236
- @keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }
237
- `,J=`
238
- 'use strict';
239
-
240
- const ui = { nodes: new Map(), summary: { nodes: 0, agents: 0, busy: 0, errors: 0 } };
241
-
242
- function escapeHtml(s) {
243
- return String(s).replace(/[&<>"']/g, c => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;' }[c]));
244
- }
245
- function fmtElapsed(ms) {
246
- if (ms < 1000) return ms + 'ms';
247
- const s = Math.floor(ms / 1000); if (s < 60) return s + 's';
248
- const m = Math.floor(s / 60); const rs = s % 60;
249
- if (m < 60) return m + 'm ' + (rs < 10 ? '0' : '') + rs + 's';
250
- const h = Math.floor(m / 60); const rm = m % 60;
251
- return h + 'h ' + (rm < 10 ? '0' : '') + rm + 'm';
252
- }
253
-
254
- function render(snapshot) {
255
- const grid = document.getElementById('grid');
256
- const nowTs = new Date(snapshot.ts).toLocaleTimeString();
257
- document.getElementById('ts').textContent = nowTs;
258
- const summary = { nodes: snapshot.nodes.length, reachable: 0, agents: 0, busy: 0, errors: 0 };
259
- grid.innerHTML = '';
260
- for (const node of snapshot.nodes) {
261
- if (node.reachable) summary.reachable++;
262
- summary.agents += node.agents.length;
263
- for (const a of node.agents) {
264
- const busy = (a.runningTasks && a.runningTasks.length > 0) || (a.active || 0) > 0;
265
- if (busy) summary.busy++;
266
- summary.errors += (a.errors || 0);
267
- }
268
- grid.appendChild(renderNode(node));
269
- }
270
- document.getElementById('summary').innerHTML =
271
- '<span><b>' + summary.reachable + '/' + summary.nodes + '</b> nodes</span>' +
272
- '<span><b>' + summary.agents + '</b> agents</span>' +
273
- '<span><b>' + summary.busy + '</b> active now</span>' +
274
- '<span><b>' + summary.errors + '</b> errors</span>';
275
- }
276
-
277
- function renderNode(node) {
278
- const sec = document.createElement('section'); sec.className = 'node';
279
- const tag = node.reachable
280
- ? '<span class="tag up">online \xB7 ' + (node.uptimeSec ? Math.round(node.uptimeSec / 60) + 'm' : '\u2014') + '</span>'
281
- : '<span class="tag down">offline \u2014 ' + escapeHtml(node.error || 'unreachable') + '</span>';
282
- sec.innerHTML = '<header><div class="name">' + escapeHtml(node.name) + '</div>' +
283
- '<div class="url">' + escapeHtml(node.url) + '</div>' +
284
- tag + '</header><div class="grid"></div>';
285
- const g = sec.querySelector('.grid');
286
- if (!node.reachable || node.agents.length === 0) {
287
- const empty = document.createElement('div'); empty.style.color = 'var(--muted)'; empty.style.padding = '8px 4px';
288
- empty.textContent = node.reachable ? 'No agents on this node.' : 'Unreachable.';
289
- g.appendChild(empty);
290
- } else {
291
- for (const a of node.agents) g.appendChild(renderAgent(a));
292
- }
293
- return sec;
294
- }
295
-
296
- function renderAgent(a) {
297
- const card = document.createElement('div');
298
- const busy = (a.runningTasks && a.runningTasks.length > 0) || (a.active || 0) > 0;
299
- const errored = (a.errors || 0) > 0;
300
- card.className = 'agent' + (busy ? ' busy' : '') + (errored ? ' errored' : '');
301
- const tasks = (a.runningTasks || []).map(t => {
302
- const elapsed = fmtElapsed(Date.now() - new Date(t.startedAt).getTime());
303
- return '<div class="task">' +
304
- '<span class="channel">' + escapeHtml(t.channel || '\u2014') + '</span>' +
305
- '<span class="preview" title="' + escapeHtml(t.messagePreview || '') + '">' + escapeHtml(t.messagePreview || '(no preview)') + '</span>' +
306
- '<span class="elapsed">' + elapsed + '</span>' +
307
- '</div>';
308
- }).join('');
309
- const tasksBlock = tasks
310
- ? '<div class="tasks">' + tasks + '</div>'
311
- : (busy ? '<div class="tasks"><div class="task"><span class="elapsed">running \xB7 no preview</span></div></div>'
312
- : '<div class="idle">idle</div>');
313
- const modelLabel = a.model ? '<span class="model" title="Model">' + escapeHtml(shortenModel(a.model)) + '</span>' : '';
314
- const lastLabel = a.lastActive
315
- ? '<span class="last" title="' + escapeHtml(new Date(a.lastActive).toLocaleString()) + '">last ' + escapeHtml(fmtAgo(a.lastActive)) + '</span>'
316
- : '<span class="last muted">never ran</span>';
317
- card.innerHTML =
318
- '<div class="top">' +
319
- '<span class="dot"></span>' +
320
- '<span class="name">' + escapeHtml(a.name || a.id) + '</span>' +
321
- '<span class="tier">' + escapeHtml(a.tier || '') + '</span>' +
322
- '</div>' +
323
- '<div class="meta">' + modelLabel + lastLabel + '</div>' +
324
- '<div class="stats">' +
325
- '<span>Active <b>' + (a.active || 0) + '</b></span>' +
326
- '<span>Total <b>' + (a.total || 0) + '</b></span>' +
327
- '<span class="err">Err <b>' + (a.errors || 0) + '</b></span>' +
328
- '</div>' + tasksBlock;
329
- return card;
330
- }
331
-
332
- function shortenModel(m) {
333
- if (!m) return '';
334
- return String(m)
335
- .replace(/^claude-/, '')
336
- .replace(/-\\d{8}$/, '')
337
- .replace(/\\[1m\\]$/, ' \xB7 1M');
338
- }
339
-
340
- function fmtAgo(iso) {
341
- const t = new Date(iso).getTime();
342
- if (!t) return '\u2014';
343
- const s = Math.floor((Date.now() - t) / 1000);
344
- if (s < 60) return s + 's ago';
345
- const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';
346
- const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';
347
- const d = Math.floor(h / 24); return d + 'd ago';
348
- }
349
-
350
- function connect() {
351
- const conn = document.getElementById('conn');
352
- let es;
353
- const open = () => {
354
- try { es = new EventSource('/api/live/stream'); } catch (e) { setTimeout(open, 2000); return; }
355
- es.addEventListener('snapshot', (ev) => {
356
- conn.className = 'conn ok'; conn.title = 'connected';
357
- try { render(JSON.parse(ev.data)); } catch {}
358
- });
359
- es.addEventListener('error', () => {
360
- conn.className = 'conn err'; conn.title = 'reconnecting\u2026';
361
- es.close(); setTimeout(open, 2000);
362
- });
363
- };
364
- open();
365
- }
366
-
367
- // Re-render every second to tick elapsed counters even when no new snapshot arrives.
368
- let lastSnap = null;
369
- fetch('/api/live').then(r => r.json()).then(s => { lastSnap = s; render(s); }).catch(() => {});
370
- setInterval(() => { if (lastSnap) {
371
- // Patch elapsed times without a fresh snapshot
372
- document.querySelectorAll('.agent .task .elapsed').forEach((el) => { /* placeholder: server snapshot advances on stream */ });
373
- } }, 1000);
374
- connect();
375
- `,Q=`
376
- :root {
377
- --bg: #0f1117; --card: #1a1d27; --col: #161922; --col-hdr: #1e2230;
378
- --border: #2a2d3a; --border-soft: #22253220;
379
- --text: #e6e8ef; --muted: #8b8fa3; --accent: #6366f1;
380
- --green: #22c55e; --yellow: #f59e0b; --orange: #fb7a35;
381
- --red: #ef4444; --blue: #3b82f6; --gray: #6b7280;
382
- }
383
- * { box-sizing: border-box; }
384
- html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text);
385
- font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
386
- header { display: flex; align-items: center; gap: 10px; padding: 10px 16px;
387
- background: var(--card); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10;
388
- flex-wrap: wrap; }
389
- header .brand { font-weight: 600; letter-spacing: 0.5px; color: var(--accent); font-size: 14px; }
390
- header select, header input, header button { background: var(--col); color: var(--text);
391
- border: 1px solid var(--border); padding: 6px 10px; border-radius: 6px; font: inherit; outline: none; }
392
- header select:focus, header input:focus { border-color: var(--accent); }
393
- header .search { position: relative; }
394
- header .search .icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
395
- color: var(--muted); pointer-events: none; font-size: 14px; }
396
- header .search input { padding-left: 28px; width: 260px; }
397
- header button { cursor: pointer; }
398
- header button:hover { border-color: var(--accent); }
399
- header .chips { display: flex; gap: 4px; flex-wrap: wrap; }
400
- header .spacer { flex: 1; }
401
- header .conn { font-size: 16px; line-height: 1; }
402
- header .conn.ok { color: var(--green); }
403
- header .conn.warn { color: var(--yellow); }
404
- header .conn.err { color: var(--red); }
405
-
406
- main#columns { display: flex; gap: 10px; padding: 12px; overflow-x: auto; align-items: stretch;
407
- min-height: calc(100vh - 60px); }
408
- .col { background: var(--col); border: 1px solid var(--border); border-radius: 8px;
409
- min-width: 280px; max-width: 320px; flex: 1 0 280px; display: flex; flex-direction: column;
410
- max-height: calc(100vh - 84px); }
411
- .col .accent { height: 3px; border-top-left-radius: 7px; border-top-right-radius: 7px; background: var(--gray); }
412
- .col header.col-h { margin: 0; padding: 10px 12px; background: var(--col-hdr);
413
- border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px;
414
- position: sticky; top: 0; z-index: 1; border-radius: 0; }
415
- .col header.col-h h3 { margin: 0; font-size: 13px; font-weight: 600; color: var(--text); flex: 1; }
416
- .col header.col-h .count { background: rgba(255,255,255,0.08); color: var(--muted);
417
- font-size: 11px; padding: 2px 8px; border-radius: 10px; font-weight: 500; }
418
- .stack { padding: 8px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto;
419
- flex: 1; min-height: 40px; }
420
-
421
- .card { background: var(--card); border: 1px solid var(--border); border-radius: 6px;
422
- padding: 10px 12px; cursor: grab; transition: border-color .15s, transform .15s;
423
- display: flex; flex-direction: column; gap: 6px; }
424
- .card:hover { border-color: var(--accent); }
425
- .card.closed { opacity: 0.75; }
426
- .card .title { font-weight: 500; font-size: 13px; line-height: 1.4; color: var(--text);
427
- overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; word-break: break-word; }
428
- .card .labels { display: flex; gap: 4px; flex-wrap: wrap; }
429
- .card .label { font-size: 10px; padding: 2px 8px; border-radius: 10px; font-weight: 500;
430
- background: rgba(99, 102, 241, 0.25); color: #e6e8ef; white-space: nowrap; line-height: 1.4; }
431
- .card .foot { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted);
432
- flex-wrap: wrap; }
433
- .card .foot .iid { font-family: ui-monospace, monospace; }
434
- .card .foot .milestone { background: rgba(99,102,241,0.15); color: var(--accent);
435
- padding: 1px 6px; border-radius: 3px; font-size: 10px; }
436
- .card .foot .est { background: rgba(255,255,255,0.08); padding: 1px 6px; border-radius: 3px; font-size: 10px; }
437
- .card .foot .date { margin-left: auto; }
438
- .card .foot a.link { color: var(--muted); text-decoration: none; }
439
- .card .foot a.link:hover { color: var(--accent); }
440
-
441
- .avatar { display: inline-flex; align-items: center; justify-content: center;
442
- width: 22px; height: 22px; border-radius: 50%; font-size: 10px; font-weight: 600;
443
- color: white; flex-shrink: 0; }
444
- .avatar.unassigned { background: var(--gray); }
445
-
446
- .sortable-ghost { opacity: 0.4; }
447
- .sortable-drag { cursor: grabbing; }
448
- .sortable-chosen { border-color: var(--accent); }
449
- .empty { color: var(--muted); font-style: italic; text-align: center; padding: 16px; font-size: 12px; }
450
-
451
- #toast { position: fixed; right: 16px; bottom: 16px; padding: 10px 14px;
452
- background: var(--red); color: white; border-radius: 6px; font-size: 13px;
453
- max-width: 360px; display: none; z-index: 200; box-shadow: 0 4px 12px rgba(0,0,0,0.4); }
454
- #toast.show { display: block; }
455
-
456
- /* Multi-assignee stack on cards */
457
- .avatar-stack { display: inline-flex; }
458
- .avatar-stack .avatar { margin-left: -6px; border: 2px solid var(--card); }
459
- .avatar-stack .avatar:first-child { margin-left: 0; }
460
- .avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
461
-
462
- /* Column add button */
463
- .col header.col-h .add { background: transparent; color: var(--muted); border: 1px solid var(--border);
464
- padding: 0 8px; line-height: 22px; border-radius: 4px; cursor: pointer; font-size: 14px; }
465
- .col header.col-h .add:hover { color: var(--accent); border-color: var(--accent); }
466
-
467
- /* Header buttons */
468
- header #newIssueBtn { background: var(--accent); color: white; border-color: var(--accent); }
469
- header #newIssueBtn:hover { filter: brightness(1.1); }
470
-
471
- /* --- Modal --- */
472
- .modal { position: fixed; inset: 0; z-index: 150; display: none; }
473
- .modal.open { display: block; }
474
- .modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(2px); }
475
- .modal-panel { position: relative; max-width: 940px; width: calc(100% - 40px); height: calc(100vh - 60px);
476
- margin: 30px auto; background: var(--card); border: 1px solid var(--border); border-radius: 10px;
477
- display: flex; flex-direction: column; box-shadow: 0 16px 48px rgba(0,0,0,0.55); overflow: hidden; }
478
- .modal-h { display: flex; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--border); align-items: center; }
479
- .modal-h .modal-title-row { flex: 1; display: flex; gap: 10px; align-items: center; }
480
- .modal-h .iid { font-family: ui-monospace, monospace; color: var(--muted); font-size: 13px; }
481
- .modal-h input[type="text"] { flex: 1; background: var(--col); border: 1px solid var(--border); color: var(--text);
482
- padding: 8px 10px; border-radius: 6px; font-size: 15px; font-weight: 500; outline: none; }
483
- .modal-h input[type="text"]:focus { border-color: var(--accent); }
484
- .modal-h .x { background: transparent; color: var(--muted); border: none; font-size: 18px; cursor: pointer; padding: 6px 8px; }
485
- .modal-h .x:hover { color: var(--text); }
486
-
487
- .modal-body { flex: 1; display: grid; grid-template-columns: 1fr 300px; gap: 0;
488
- overflow: hidden; min-height: 0; }
489
- .modal-body .m-main { padding: 16px; overflow-y: auto; min-height: 0; }
490
- .modal-body .m-side { padding: 16px; border-left: 1px solid var(--border); background: var(--col);
491
- overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 14px; }
492
-
493
- .fld { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
494
- .fld > span { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
495
- .fld.muted > div { font-size: 12px; color: var(--muted); }
496
- .fld textarea { background: var(--col); border: 1px solid var(--border); color: var(--text);
497
- padding: 10px; border-radius: 6px; font: 13px/1.5 ui-monospace, "SF Mono", monospace; outline: none; resize: vertical; }
498
- .fld textarea:focus { border-color: var(--accent); }
499
- .fld select { background: var(--col); border: 1px solid var(--border); color: var(--text);
500
- padding: 6px 10px; border-radius: 6px; font: inherit; outline: none; }
501
-
502
- .chip-picker { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 6px;
503
- background: var(--col); border: 1px solid var(--border); border-radius: 6px; min-height: 36px; }
504
- .chip-picker .chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px;
505
- border-radius: 10px; font-size: 11px; background: rgba(99,102,241,0.25); color: #e6e8ef; font-weight: 500; }
506
- .chip-picker .chip .remove { cursor: pointer; color: rgba(255,255,255,0.7); margin-left: 2px; }
507
- .chip-picker .chip .remove:hover { color: white; }
508
- .chip-picker .add-chip { background: transparent; border: 1px dashed var(--border); color: var(--muted);
509
- padding: 3px 10px; border-radius: 10px; cursor: pointer; font: inherit; font-size: 11px; }
510
- .chip-picker .add-chip:hover { border-color: var(--accent); color: var(--accent); }
511
-
512
- .chip-picker .dropdown { position: relative; }
513
- .chip-picker .dropdown .menu { position: absolute; top: 100%; left: 0; margin-top: 4px;
514
- background: var(--card); border: 1px solid var(--border); border-radius: 6px; min-width: 240px;
515
- max-height: 280px; overflow-y: auto; box-shadow: 0 8px 20px rgba(0,0,0,0.4); z-index: 10; padding: 4px; }
516
- .chip-picker .dropdown .menu input.search { width: 100%; background: var(--col); border: 1px solid var(--border);
517
- color: var(--text); padding: 6px 10px; border-radius: 4px; font: inherit; margin-bottom: 4px; outline: none; }
518
- .chip-picker .dropdown .menu .opt { padding: 6px 10px; cursor: pointer; border-radius: 4px;
519
- font-size: 12px; display: flex; align-items: center; gap: 8px; }
520
- .chip-picker .dropdown .menu .opt:hover { background: var(--col); }
521
- .chip-picker .dropdown .menu .opt.selected { background: rgba(99,102,241,0.15); color: var(--accent); }
522
- .chip-picker .dropdown .menu .empty { padding: 10px; color: var(--muted); font-size: 12px; text-align: center; }
523
-
524
- .state-toggle { display: inline-flex; background: var(--col); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
525
- .state-toggle button { background: transparent; border: none; color: var(--muted); padding: 6px 12px;
526
- cursor: pointer; font: inherit; font-size: 12px; }
527
- .state-toggle button.active[data-state="opened"] { background: var(--green); color: white; }
528
- .state-toggle button.active[data-state="closed"] { background: var(--gray); color: white; }
529
-
530
- .ext-link { color: var(--muted); text-decoration: none; font-size: 12px; margin-top: auto; padding-top: 10px;
531
- border-top: 1px solid var(--border); }
532
- .ext-link:hover { color: var(--accent); }
533
-
534
- .modal-f { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); }
535
- .modal-f .spacer { flex: 1; }
536
- .modal-f .hint { color: var(--muted); font-size: 12px; }
537
- .btn-primary { background: var(--accent); color: white; border: 1px solid var(--accent);
538
- padding: 7px 16px; border-radius: 6px; cursor: pointer; font: inherit; font-weight: 500; }
539
- .btn-primary:hover { filter: brightness(1.1); }
540
- .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
541
- .btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border);
542
- padding: 7px 14px; border-radius: 6px; cursor: pointer; font: inherit; }
543
- .btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
544
-
545
- @media (max-width: 720px) {
546
- .modal-body { grid-template-columns: 1fr; }
547
- .modal-body .m-side { border-left: none; border-top: 1px solid var(--border); }
548
- }
549
-
550
- /* --- Activity panel --- */
551
- .activity-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 340px;
552
- background: var(--card); border-left: 1px solid var(--border); z-index: 140;
553
- display: flex; flex-direction: column; box-shadow: -8px 0 24px rgba(0,0,0,0.35);
554
- transition: transform 0.2s ease; }
555
- .activity-panel.hidden { transform: translateX(100%); pointer-events: none; }
556
- .activity-panel > header { display: flex; align-items: center; gap: 8px; padding: 12px 14px;
557
- border-bottom: 1px solid var(--border); background: var(--col-hdr); }
558
- .activity-panel > header h2 { margin: 0; font-size: 13px; font-weight: 600; flex: 1; }
559
- .activity-panel > header .source { font-size: 10px; color: var(--muted); font-family: ui-monospace, monospace; }
560
- .activity-panel > header .x { background: transparent; border: none; color: var(--muted); font-size: 16px;
561
- cursor: pointer; padding: 4px 8px; }
562
- .activity-panel > header .x:hover { color: var(--text); }
563
- #activity-body { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 8px; }
564
- .agent-card { background: var(--col); border: 1px solid var(--border); border-radius: 6px;
565
- padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
566
- .agent-card .top { display: flex; align-items: center; gap: 8px; }
567
- .agent-card .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray); flex-shrink: 0; }
568
- .agent-card .dot.active { background: var(--green); box-shadow: 0 0 6px var(--green); animation: pulse 1.2s ease-in-out infinite; }
569
- .agent-card .dot.errored { background: var(--red); }
570
- .agent-card .name { font-weight: 600; font-size: 13px; flex: 1; }
571
- .agent-card .tier { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
572
- .agent-card .meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
573
- .agent-card .model { font-family: ui-monospace, monospace; font-size: 10px; color: var(--accent);
574
- background: rgba(99,102,241,0.12); padding: 1px 7px; border-radius: 3px; letter-spacing: 0.2px; }
575
- .agent-card .last { font-size: 10px; color: var(--muted); }
576
- .agent-card .last.muted { font-style: italic; opacity: 0.7; }
577
- .agent-card .stats { display: flex; gap: 10px; font-size: 11px; color: var(--muted); }
578
- .agent-card .stats b { color: var(--text); font-weight: 600; }
579
- .agent-card .stats .errors b { color: var(--red); }
580
- .agent-card .current { font-size: 11px; color: var(--muted); padding-top: 4px; border-top: 1px dashed var(--border);
581
- overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
582
- .agent-card .current b { color: var(--text); font-weight: 500; }
583
- @keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }
584
-
585
- .activity-panel .events { padding: 10px 14px; max-height: 200px; overflow-y: auto;
586
- border-top: 1px solid var(--border); background: #0a0c11; font: 11px/1.5 ui-monospace, monospace; }
587
- .activity-panel .events .evt { color: var(--muted); margin-bottom: 4px; }
588
- .activity-panel .events .evt .ts { color: #4a4e5a; }
589
- .activity-panel .events .evt .who { color: var(--accent); }
590
- .activity-panel .events .evt .what { color: var(--text); }
591
- .activity-panel .events .evt.err .what { color: var(--red); }
592
-
593
- /* Shift the main grid left when activity panel is open so cards aren't hidden */
594
- body.panel-open main#columns { padding-right: 352px; }
595
- body.panel-open header { padding-right: 356px; }
596
-
597
- header #activityBtn.on { background: var(--accent); color: white; border-color: var(--accent); }
598
- header a.link { color: var(--text); text-decoration: none; padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 12px; }
599
- header a.link:hover { color: var(--accent); border-color: var(--accent); }
600
-
601
- /* AI draft row inside the create modal */
602
- .draft-box { background: var(--col); border: 1px dashed var(--accent); border-radius: 8px;
603
- padding: 10px 12px; margin-bottom: 14px; }
604
- .draft-box .fld { margin-bottom: 8px; }
605
- .draft-box .draft-row { display: flex; gap: 8px; align-items: center; }
606
- .draft-box .draft-row select { flex: 0 0 160px; }
607
- .draft-box .draft-row .hint { color: var(--muted); font-size: 12px; flex: 1; }
608
- .draft-box .draft-row .hint.err { color: var(--red); }
609
- .draft-box .draft-row .hint.ok { color: var(--green); }
610
- `,Z=`
611
- 'use strict';
612
- const state = {
613
- boards: [], current: null, items: {},
614
- filter: { q: '', assignee: '', label: '', milestone: '' },
615
- // Per-board caches \u2014 members + milestones + known labels.
616
- meta: { members: [], milestones: [], labels: [] },
617
- modal: { mode: null, id: null, original: null, draft: null }
618
- };
619
-
620
- function csrfHeaders(extra) {
621
- return Object.assign({ 'Content-Type': 'application/json', 'X-Requested-With': 'agentx-board' }, extra || {});
622
- }
623
- function toast(msg, kind) {
624
- const el = document.getElementById('toast');
625
- el.textContent = msg;
626
- el.style.background = kind === 'ok' ? 'var(--green)' : 'var(--red)';
627
- el.classList.add('show');
628
- clearTimeout(toast._t);
629
- toast._t = setTimeout(() => el.classList.remove('show'), 4000);
630
- }
631
-
632
- async function loadBoards() {
633
- const r = await fetch('/api/boards');
634
- if (!r.ok) throw new Error('failed to load boards');
635
- state.boards = await r.json();
636
- const picker = document.getElementById('boardPicker');
637
- picker.innerHTML = '';
638
- for (const b of state.boards) {
639
- const opt = document.createElement('option');
640
- opt.value = b.id; opt.textContent = b.name || b.id;
641
- picker.appendChild(opt);
642
- }
643
- picker.onchange = () => switchBoard(picker.value);
644
- if (state.boards.length) switchBoard(state.boards[0].id);
645
- }
646
-
647
- async function switchBoard(id) {
648
- state.current = state.boards.find(b => b.id === id);
649
- state.meta = { members: [], milestones: [], labels: [] };
650
- renderColumns();
651
- populateFilters();
652
- await loadItems();
653
- // Fetch members + milestones in the background for pickers.
654
- loadMeta();
655
- }
656
-
657
- async function loadMeta() {
658
- if (!state.current) return;
659
- try {
660
- const [mem, ms] = await Promise.all([
661
- fetch(\`/api/boards/\${state.current.id}/members\`).then(r => r.ok ? r.json() : []),
662
- fetch(\`/api/boards/\${state.current.id}/milestones\`).then(r => r.ok ? r.json() : []),
663
- ]);
664
- state.meta.members = mem;
665
- state.meta.milestones = ms;
666
- state.meta.labels = collectKnownLabels();
667
- } catch (e) { /* non-fatal */ }
668
- }
669
-
670
- function collectKnownLabels() {
671
- const set = new Set();
672
- for (const list of Object.values(state.items)) for (const i of list) {
673
- for (const l of (i.labels || [])) set.add(l);
674
- }
675
- return [...set].sort();
676
- }
677
-
678
- function populateFilters() {
679
- if (!state.current) return;
680
- const labelsEl = document.getElementById('filterLabels');
681
- labelsEl.innerHTML = '';
682
- for (const lbl of state.current.labels || []) {
683
- const chip = document.createElement('span');
684
- chip.className = 'label';
685
- chip.style.cursor = 'pointer';
686
- chip.style.background = lbl.color || labelColor(lbl.name);
687
- chip.style.color = pickTextColor(lbl.color);
688
- chip.textContent = lbl.name;
689
- chip.onclick = () => {
690
- state.filter.label = state.filter.label === lbl.name ? '' : lbl.name;
691
- renderChipStates(); loadItems();
692
- };
693
- labelsEl.appendChild(chip);
694
- }
695
- }
696
- function renderChipStates() {
697
- document.querySelectorAll('#filterLabels .label').forEach(chip => {
698
- chip.style.opacity = (state.filter.label && chip.textContent !== state.filter.label) ? '0.35' : '1';
699
- });
700
- }
701
-
702
- function renderColumns() {
703
- const main = document.getElementById('columns');
704
- main.innerHTML = '';
705
- if (!state.current) return;
706
- for (const col of state.current.columns) {
707
- const section = document.createElement('section');
708
- section.className = 'col';
709
- section.dataset.colId = col.id;
710
- const canAdd = col.kind !== 'closed';
711
- section.innerHTML = \`
712
- <div class="accent" style="background:\${col.accent || 'var(--gray)'}"></div>
713
- <header class="col-h">
714
- <h3>\${escapeHtml(col.title)}</h3>
715
- <span class="count">0</span>
716
- \${canAdd ? \`<button class="add" data-col-id="\${col.id}" title="New issue in this column">+</button>\` : ''}
717
- </header>
718
- <div class="stack" data-col-id="\${col.id}"></div>
719
- \`;
720
- main.appendChild(section);
721
- }
722
- document.querySelectorAll('.col .add').forEach(btn => {
723
- btn.onclick = () => openCreateModal(btn.dataset.colId);
724
- });
725
- document.querySelectorAll('.stack').forEach(el => {
726
- Sortable.create(el, {
727
- group: 'board', animation: 150,
728
- onEnd: async (ev) => {
729
- const from = ev.from.dataset.colId;
730
- const to = ev.to.dataset.colId;
731
- if (from === to) return;
732
- const itemId = ev.item.dataset.id;
733
- try {
734
- const r = await fetch(\`/api/boards/\${state.current.id}/items/\${encodeURIComponent(itemId)}/move\`, {
735
- method: 'PATCH', headers: csrfHeaders(), body: JSON.stringify({ from, to })
736
- });
737
- if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
738
- toast('Moved \u2713', 'ok');
739
- updateCounts();
740
- } catch (e) {
741
- ev.from.insertBefore(ev.item, ev.from.children[ev.oldIndex] || null);
742
- updateCounts();
743
- toast('Move failed: ' + (e.message || 'unknown'));
744
- }
745
- }
746
- });
747
- });
748
- }
749
-
750
- function updateCounts() {
751
- document.querySelectorAll('.col').forEach(col => {
752
- const n = col.querySelector('.stack').children.length;
753
- const empty = col.querySelector('.stack .empty');
754
- const real = empty ? 0 : n;
755
- col.querySelector('.count').textContent = real;
756
- });
757
- }
758
-
759
- async function loadItems() {
760
- const conn = document.getElementById('conn');
761
- conn.className = 'conn warn'; conn.title = 'loading\u2026';
762
- if (!state.current) return;
763
- const params = new URLSearchParams();
764
- if (state.filter.q) params.set('q', state.filter.q);
765
- if (state.filter.assignee) params.set('assignee', state.filter.assignee);
766
- if (state.filter.label) params.set('label', state.filter.label);
767
- if (state.filter.milestone) params.set('milestone', state.filter.milestone);
768
- try {
769
- const r = await fetch(\`/api/boards/\${state.current.id}/items?\${params.toString()}\`);
770
- if (!r.ok) throw new Error(await r.text());
771
- const data = await r.json();
772
- state.items = data.columns;
773
- paintItems();
774
- populateAssigneeFilter();
775
- populateMilestoneFilter();
776
- conn.className = 'conn ok'; conn.title = 'ok';
777
- } catch (e) {
778
- conn.className = 'conn err'; conn.title = 'load failed';
779
- toast('Load failed: ' + (e.message || 'unknown'));
780
- }
781
- }
782
-
783
- function populateAssigneeFilter() {
784
- const set = new Set();
785
- for (const list of Object.values(state.items)) for (const i of list) if (i.assignee) set.add(i.assignee);
786
- const sel = document.getElementById('filterAssignee');
787
- const current = sel.value;
788
- sel.innerHTML = '<option value="">All assignees</option>';
789
- for (const a of [...set].sort()) {
790
- const o = document.createElement('option'); o.value = a; o.textContent = a;
791
- sel.appendChild(o);
792
- }
793
- sel.value = current;
794
- }
795
- function populateMilestoneFilter() {
796
- const set = new Set();
797
- for (const list of Object.values(state.items)) for (const i of list) if (i.milestone) set.add(i.milestone);
798
- const sel = document.getElementById('filterMilestone');
799
- const current = sel.value;
800
- sel.innerHTML = '<option value="">Any milestone</option>';
801
- for (const m of [...set].sort()) {
802
- const o = document.createElement('option'); o.value = m; o.textContent = m;
803
- sel.appendChild(o);
804
- }
805
- sel.value = current;
806
- }
807
-
808
- function paintItems() {
809
- if (!state.current) return;
810
- for (const col of state.current.columns) {
811
- const stack = document.querySelector(\`.stack[data-col-id="\${col.id}"]\`);
812
- if (!stack) continue;
813
- stack.innerHTML = '';
814
- const list = state.items[col.id] || [];
815
- if (list.length === 0) {
816
- const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'Empty';
817
- stack.appendChild(empty);
818
- } else {
819
- for (const item of list) stack.appendChild(renderCard(item, col));
820
- }
821
- }
822
- updateCounts();
823
- }
824
-
825
- function renderCard(item, col) {
826
- const card = document.createElement('div');
827
- card.className = 'card' + (item.state === 'closed' ? ' closed' : '');
828
- card.dataset.id = item.id;
829
- const iid = item.id.split(':').slice(-1)[0];
830
-
831
- // Filter out Status::* scoped labels from display (represented by the column).
832
- const details = (item.labelDetails || (item.labels || []).map(n => ({ name: n })))
833
- .filter(l => !/^Status::/i.test(l.name));
834
- const labelChips = details.slice(0, 5).map(l => {
835
- const bg = l.color || labelColor(l.name);
836
- const fg = l.text_color || pickTextColor(bg);
837
- return \`<span class="label" title="\${escapeHtml(l.name)}" style="background:\${bg};color:\${fg}">\${escapeHtml(l.name)}</span>\`;
838
- }).join('');
839
-
840
- const assignees = (item.assignees && item.assignees.length)
841
- ? item.assignees
842
- : (item.assignee ? [{ username: item.assignee }] : []);
843
- const avatarStack = assignees.length
844
- ? \`<span class="avatar-stack">\${assignees.slice(0, 3).map(renderAvatar).join('')}</span>\`
845
- : \`<span class="avatar unassigned" title="unassigned">?</span>\`;
846
-
847
- const milestone = item.milestone ? \`<span class="milestone" title="Milestone">\u{1F3C1} \${escapeHtml(item.milestone)}</span>\` : '';
848
- const est = item.estimatedSeconds ? \`<span class="est" title="Estimate">\u23F1 \${fmtDuration(item.estimatedSeconds)}</span>\` : '';
849
- const date = item.updatedAt ? \`<span class="date" title="Updated \${escapeHtml(item.updatedAt)}">\${fmtDate(item.updatedAt)}</span>\` : '';
850
- const link = item.url ? \`<a class="link" href="\${escapeHtml(item.url)}" target="_blank" rel="noopener" title="Open in GitLab" onclick="event.stopPropagation()">\u2197</a>\` : '';
851
-
852
- card.innerHTML = \`
853
- <div class="title">\${escapeHtml(item.title || '(untitled)')}</div>
854
- \${labelChips ? \`<div class="labels">\${labelChips}</div>\` : ''}
855
- <div class="foot">
856
- \${avatarStack}
857
- <span class="iid">#\${escapeHtml(iid)}</span>
858
- \${milestone}
859
- \${est}
860
- \${date}
861
- \${link}
862
- </div>
863
- \`;
864
- card.addEventListener('click', (e) => {
865
- if (e.target.closest('a,button')) return;
866
- openDetailModal(item.id);
867
- });
868
- return card;
869
- }
870
-
871
- function renderAvatar(a) {
872
- const name = a.name || a.username || '?';
873
- const img = a.avatarUrl ? \`<img src="\${escapeHtml(a.avatarUrl)}" alt="">\` : '';
874
- const bg = labelColor(a.username || name);
875
- return \`<span class="avatar" title="\${escapeHtml(name)}" style="background:\${bg}">\${img || escapeHtml(initials(name))}</span>\`;
876
- }
877
-
878
- function escapeHtml(s) {
879
- return String(s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
880
- }
881
- function initials(s) {
882
- const parts = String(s).replace(/[_\\-\\.]+/g, ' ').trim().split(/\\s+/);
883
- if (!parts.length) return '?';
884
- if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
885
- return (parts[0][0] + parts[1][0]).toUpperCase();
886
- }
887
- function labelColor(s) {
888
- let h = 0;
889
- for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
890
- return 'hsl(' + (h % 360) + ', 55%, 45%)';
891
- }
892
- function pickTextColor(bg) {
893
- if (!bg) return '#fff';
894
- if (bg.startsWith('#') && bg.length >= 7) {
895
- const r = parseInt(bg.slice(1,3),16), g = parseInt(bg.slice(3,5),16), b = parseInt(bg.slice(5,7),16);
896
- return (r*299 + g*587 + b*114) / 1000 >= 160 ? '#111' : '#fff';
897
- }
898
- return '#fff';
899
- }
900
- function fmtDuration(sec) {
901
- if (!sec) return '';
902
- const m = Math.round(sec / 60);
903
- if (m < 60) return m + 'm';
904
- const h = Math.floor(m / 60); const rem = m % 60;
905
- return rem ? \`\${h}h\${rem}m\` : h + 'h';
906
- }
907
- function fmtDate(iso) {
908
- const d = new Date(iso); if (Number.isNaN(d.getTime())) return '';
909
- return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
910
- }
911
- function debounce(fn, ms) { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; }
912
-
913
- document.getElementById('filterSearch').oninput = debounce(e => { state.filter.q = e.target.value; loadItems(); }, 250);
914
- document.getElementById('filterAssignee').onchange = e => { state.filter.assignee = e.target.value; loadItems(); };
915
- document.getElementById('filterMilestone').onchange = e => { state.filter.milestone = e.target.value; loadItems(); };
916
- document.getElementById('refreshBtn').onclick = () => loadItems();
917
- document.getElementById('newIssueBtn').onclick = () => openCreateModal(null);
918
- document.getElementById('activityBtn').onclick = () => toggleActivity();
919
- document.getElementById('activity-close').onclick = () => toggleActivity(false);
920
- window.addEventListener('keydown', (e) => {
921
- if (e.key === 'Escape' && document.getElementById('modal').classList.contains('open')) { closeModal(); return; }
922
- if (e.target.matches('input,select,textarea')) return;
923
- if (e.key === 'r') loadItems();
924
- if (e.key === 'a') toggleActivity();
925
- });
926
-
927
- // --- Modal (detail view / create) ---
928
-
929
- async function openDetailModal(itemId) {
930
- openModal('edit', itemId);
931
- const hint = document.getElementById('m-hint');
932
- hint.textContent = 'Loading\u2026';
933
- try {
934
- const r = await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(itemId));
935
- if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);
936
- const detail = await r.json();
937
- state.modal.original = detail;
938
- state.modal.draft = cloneForDraft(detail);
939
- paintModal(state.modal.draft);
940
- hint.textContent = '';
941
- } catch (e) {
942
- hint.textContent = 'Load failed: ' + e.message;
943
- }
944
- }
945
-
946
- function openCreateModal(columnId) {
947
- state.modal.targetColumnId = columnId || null;
948
- const blank = {
949
- id: null, title: '', description: '',
950
- labels: [], labelDetails: [], assignees: [], milestone: '', state: 'opened',
951
- };
952
- // Pre-apply the target column's scoped label so the new issue lands in that column.
953
- if (columnId && state.current) {
954
- const col = state.current.columns.find(c => c.id === columnId);
955
- if (col && col.kind === 'scoped-label' && col.scopedLabel) {
956
- blank.labels.push(col.scopedLabel);
957
- blank.labelDetails.push({ name: col.scopedLabel, color: col.accent });
958
- } else if (col && col.kind === 'label' && col.mapsToLabel) {
959
- blank.labels.push(col.mapsToLabel);
960
- blank.labelDetails.push({ name: col.mapsToLabel, color: col.accent });
961
- }
962
- }
963
- // Carry the board's primary label (the team filter) so created issues show up here.
964
- if (state.current && state.current.primaryToolLabel && !blank.labels.includes(state.current.primaryToolLabel)) {
965
- blank.labels.push(state.current.primaryToolLabel);
966
- blank.labelDetails.push({ name: state.current.primaryToolLabel });
967
- }
968
- openModal('create', null);
969
- state.modal.original = null;
970
- state.modal.draft = blank;
971
- paintModal(blank);
972
- }
973
-
974
- function openModal(mode, id) {
975
- state.modal.mode = mode;
976
- state.modal.id = id;
977
- const m = document.getElementById('modal');
978
- m.classList.add('open');
979
- m.setAttribute('aria-hidden', 'false');
980
- document.body.style.overflow = 'hidden';
981
- document.getElementById('m-save').textContent = mode === 'create' ? 'Create' : 'Save';
982
- document.getElementById('m-iid').textContent = id ? ('#' + id.split(':').slice(-1)[0]) : 'NEW';
983
- // Draft box only in create mode.
984
- const draftBox = document.getElementById('m-draft');
985
- if (draftBox) {
986
- draftBox.hidden = mode !== 'create';
987
- if (mode === 'create') {
988
- document.getElementById('m-rough').value = '';
989
- document.getElementById('m-draft-hint').textContent = '';
990
- document.getElementById('m-draft-hint').className = 'hint';
991
- populateDraftAgents();
992
- }
993
- }
994
- // Pre-focus: rough for create, description for edit.
995
- setTimeout(() => {
996
- const el = document.getElementById(mode === 'create' ? 'm-rough' : 'm-desc');
997
- if (el) el.focus();
998
- }, 50);
999
- }
1000
-
1001
- async function populateDraftAgents() {
1002
- const sel = document.getElementById('m-draft-agent');
1003
- if (!sel) return;
1004
- if (sel.options.length > 1) {
1005
- // Already populated \u2014 just sync selection.
1006
- const saved = localStorage.getItem('agentx.draftAgent');
1007
- if (saved) sel.value = saved;
1008
- return;
1009
- }
1010
- try {
1011
- const r = await fetch('/api/agents');
1012
- if (!r.ok) return;
1013
- const list = await r.json();
1014
- sel.innerHTML = '<option value="">Agent\u2026</option>';
1015
- for (const a of list) {
1016
- const o = document.createElement('option');
1017
- o.value = a.id; o.textContent = a.name + ' (' + a.tier + ')';
1018
- sel.appendChild(o);
1019
- }
1020
- const saved = localStorage.getItem('agentx.draftAgent');
1021
- if (saved && list.some(a => a.id === saved)) sel.value = saved;
1022
- sel.onchange = () => localStorage.setItem('agentx.draftAgent', sel.value);
1023
- } catch {}
1024
- }
1025
-
1026
- async function draftWithAgent() {
1027
- const rough = document.getElementById('m-rough').value.trim();
1028
- const agentId = document.getElementById('m-draft-agent').value;
1029
- const hint = document.getElementById('m-draft-hint');
1030
- const goBtn = document.getElementById('m-draft-go');
1031
- if (!rough) { hint.textContent = 'Describe the issue first.'; hint.className = 'hint err'; return; }
1032
- if (!agentId) { hint.textContent = 'Pick a drafting agent.'; hint.className = 'hint err'; return; }
1033
- goBtn.disabled = true;
1034
- hint.textContent = 'Drafting with ' + agentId + '\u2026'; hint.className = 'hint';
1035
-
1036
- // Build board context for convention-aware prompting.
1037
- const board = state.current;
1038
- const col = state.modal.targetColumnId ? board.columns.find(c => c.id === state.modal.targetColumnId) : null;
1039
- const columnLabel = col && (col.scopedLabel || col.mapsToLabel);
1040
- const ctx = {
1041
- boardName: board.name,
1042
- primaryLabel: board.primaryToolLabel,
1043
- project: board.source && board.source.projects && board.source.projects[0],
1044
- knownLabels: state.meta.labels || [],
1045
- members: (state.meta.members || []).map(m => m.username),
1046
- columnLabel: columnLabel,
1047
- };
1048
-
1049
- try {
1050
- const r = await fetch('/api/draft', {
1051
- method: 'POST', headers: csrfHeaders(),
1052
- body: JSON.stringify({ rough, agent: agentId, context: ctx }),
1053
- });
1054
- if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
1055
- const drafted = await r.json();
1056
- // Populate the existing form fields.
1057
- document.getElementById('m-title').value = drafted.title || '';
1058
- document.getElementById('m-desc').value = drafted.description || '';
1059
- state.modal.draft.title = drafted.title || '';
1060
- state.modal.draft.description = drafted.description || '';
1061
- state.modal.draft.labels = Array.isArray(drafted.labels) ? drafted.labels.slice() : state.modal.draft.labels;
1062
- if (drafted.assigneeUsername) {
1063
- const m = state.meta.members.find(x => x.username === drafted.assigneeUsername);
1064
- if (!state.modal.draft.assignees.some(a => a.username === drafted.assigneeUsername)) {
1065
- state.modal.draft.assignees.push({
1066
- username: drafted.assigneeUsername, name: m && m.name, avatarUrl: m && m.avatarUrl,
1067
- });
1068
- }
1069
- }
1070
- paintModal(state.modal.draft);
1071
- hint.textContent = 'Draft ready \u2014 review and edit before creating.';
1072
- hint.className = 'hint ok';
1073
- } catch (e) {
1074
- hint.textContent = 'Draft failed: ' + e.message;
1075
- hint.className = 'hint err';
1076
- } finally {
1077
- goBtn.disabled = false;
1078
- }
1079
- }
1080
- function closeModal() {
1081
- const m = document.getElementById('modal');
1082
- m.classList.remove('open');
1083
- m.setAttribute('aria-hidden', 'true');
1084
- document.body.style.overflow = '';
1085
- state.modal = { mode: null, id: null, original: null, draft: null };
1086
- }
1087
- function cloneForDraft(d) {
1088
- return {
1089
- id: d.id, title: d.title || '', description: d.description || '',
1090
- labels: (d.labels || []).slice(), labelDetails: (d.labelDetails || []).slice(),
1091
- assignees: (d.assignees || []).slice(),
1092
- milestone: d.milestone || '',
1093
- state: d.state || 'opened',
1094
- stage: d.stage, url: d.url, createdAt: d.createdAt, updatedAt: d.updatedAt, author: d.author,
1095
- };
1096
- }
1097
-
1098
- function paintModal(d) {
1099
- document.getElementById('m-title').value = d.title || '';
1100
- document.getElementById('m-desc').value = d.description || '';
1101
-
1102
- document.querySelectorAll('#m-state button').forEach(b => {
1103
- b.classList.toggle('active', b.dataset.state === (d.state || 'opened'));
1104
- b.onclick = () => { state.modal.draft.state = b.dataset.state; paintModal(state.modal.draft); };
1105
- });
1106
-
1107
- renderChipPicker('m-labels', d.labels || [], state.meta.labels, {
1108
- chipStyle: (name) => {
1109
- const match = (d.labelDetails || []).find(l => l.name === name);
1110
- const bg = match && match.color ? match.color : labelColor(name);
1111
- return { background: bg, color: pickTextColor(bg) };
1112
- },
1113
- onAdd: (name) => { if (!state.modal.draft.labels.includes(name)) { state.modal.draft.labels.push(name); paintModal(state.modal.draft); } },
1114
- onRemove: (name) => { state.modal.draft.labels = state.modal.draft.labels.filter(l => l !== name); paintModal(state.modal.draft); },
1115
- placeholder: 'Add label\u2026',
1116
- allowCustom: true,
1117
- });
1118
-
1119
- const aNames = (d.assignees || []).map(a => a.username);
1120
- renderChipPicker('m-assignees', aNames, state.meta.members.map(m => m.username), {
1121
- chipLabel: (u) => {
1122
- const m = state.meta.members.find(x => x.username === u) || (d.assignees || []).find(x => x.username === u);
1123
- return (m && m.name) || u;
1124
- },
1125
- onAdd: (u) => {
1126
- if (state.modal.draft.assignees.some(a => a.username === u)) return;
1127
- const m = state.meta.members.find(x => x.username === u);
1128
- state.modal.draft.assignees.push({ username: u, name: m && m.name, avatarUrl: m && m.avatarUrl });
1129
- paintModal(state.modal.draft);
1130
- },
1131
- onRemove: (u) => { state.modal.draft.assignees = state.modal.draft.assignees.filter(a => a.username !== u); paintModal(state.modal.draft); },
1132
- placeholder: 'Assign\u2026',
1133
- });
1134
-
1135
- const ms = document.getElementById('m-milestone');
1136
- ms.innerHTML = '<option value="">None</option>';
1137
- for (const m of state.meta.milestones) {
1138
- const o = document.createElement('option');
1139
- o.value = m.title; o.textContent = m.title + (m.state === 'closed' ? ' (closed)' : '');
1140
- if (m.title === d.milestone) o.selected = true;
1141
- ms.appendChild(o);
1142
- }
1143
- if (d.milestone && !state.meta.milestones.some(m => m.title === d.milestone)) {
1144
- const o = document.createElement('option'); o.value = d.milestone; o.textContent = d.milestone; o.selected = true;
1145
- ms.appendChild(o);
1146
- }
1147
- ms.onchange = () => { state.modal.draft.milestone = ms.value; };
1148
-
1149
- document.getElementById('m-author').textContent = (d.author && (d.author.name || d.author.username)) || '\u2014';
1150
- document.getElementById('m-created').textContent = d.createdAt ? fmtDate(d.createdAt) : '\u2014';
1151
- document.getElementById('m-updated').textContent = d.updatedAt ? fmtDate(d.updatedAt) : '\u2014';
1152
- const link = document.getElementById('m-link');
1153
- if (d.url) { link.href = d.url; link.style.display = ''; } else { link.style.display = 'none'; }
1154
- }
1155
-
1156
- function renderChipPicker(elId, selected, options, opts) {
1157
- const el = document.getElementById(elId);
1158
- el.innerHTML = '';
1159
- for (const name of selected) {
1160
- const chip = document.createElement('span');
1161
- chip.className = 'chip';
1162
- chip.textContent = opts.chipLabel ? opts.chipLabel(name) : name;
1163
- if (opts.chipStyle) {
1164
- const st = opts.chipStyle(name);
1165
- if (st.background) chip.style.background = st.background;
1166
- if (st.color) chip.style.color = st.color;
1167
- }
1168
- const x = document.createElement('span');
1169
- x.className = 'remove'; x.textContent = '\u2715'; x.title = 'Remove';
1170
- x.onclick = (e) => { e.stopPropagation(); opts.onRemove(name); };
1171
- chip.appendChild(x);
1172
- el.appendChild(chip);
1173
- }
1174
- const dd = document.createElement('span');
1175
- dd.className = 'dropdown';
1176
- const btn = document.createElement('button');
1177
- btn.className = 'add-chip'; btn.textContent = '+ ' + (opts.placeholder || 'Add');
1178
- btn.type = 'button';
1179
- btn.onclick = (e) => {
1180
- e.stopPropagation();
1181
- let menu = dd.querySelector('.menu');
1182
- if (menu) { menu.remove(); return; }
1183
- menu = document.createElement('div');
1184
- menu.className = 'menu';
1185
- const search = document.createElement('input');
1186
- search.className = 'search'; search.type = 'search'; search.placeholder = opts.placeholder || 'Search\u2026';
1187
- menu.appendChild(search);
1188
- const list = document.createElement('div');
1189
- menu.appendChild(list);
1190
- const paintList = (q) => {
1191
- list.innerHTML = '';
1192
- const ql = (q || '').toLowerCase();
1193
- const filtered = options.filter(o => o.toLowerCase().includes(ql) && !selected.includes(o));
1194
- if (filtered.length === 0 && !(opts.allowCustom && q)) {
1195
- const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'No matches';
1196
- list.appendChild(empty);
1197
- }
1198
- for (const o of filtered.slice(0, 50)) {
1199
- const opt = document.createElement('div');
1200
- opt.className = 'opt'; opt.textContent = opts.chipLabel ? opts.chipLabel(o) : o;
1201
- opt.onclick = () => { opts.onAdd(o); menu.remove(); };
1202
- list.appendChild(opt);
1203
- }
1204
- if (opts.allowCustom && q && !options.includes(q) && !selected.includes(q)) {
1205
- const opt = document.createElement('div');
1206
- opt.className = 'opt'; opt.textContent = 'Create "' + q + '"';
1207
- opt.onclick = () => { opts.onAdd(q); menu.remove(); };
1208
- list.appendChild(opt);
1209
- }
1210
- };
1211
- search.oninput = (ev) => paintList(ev.target.value);
1212
- paintList('');
1213
- dd.appendChild(menu);
1214
- setTimeout(() => search.focus(), 0);
1215
- const away = (ev) => {
1216
- if (!dd.contains(ev.target)) { menu.remove(); document.removeEventListener('click', away); }
1217
- };
1218
- setTimeout(() => document.addEventListener('click', away), 0);
1219
- };
1220
- dd.appendChild(btn);
1221
- el.appendChild(dd);
1222
- }
1223
-
1224
- async function saveModal() {
1225
- const saveBtn = document.getElementById('m-save');
1226
- const hint = document.getElementById('m-hint');
1227
- saveBtn.disabled = true;
1228
- hint.textContent = 'Saving\u2026';
1229
- const d = state.modal.draft;
1230
- d.title = document.getElementById('m-title').value.trim();
1231
- d.description = document.getElementById('m-desc').value;
1232
- if (!d.title) { hint.textContent = 'Title required'; saveBtn.disabled = false; return; }
1233
-
1234
- try {
1235
- if (state.modal.mode === 'create') {
1236
- const r = await fetch('/api/boards/' + state.current.id + '/items', {
1237
- method: 'POST', headers: csrfHeaders(),
1238
- body: JSON.stringify({
1239
- title: d.title, description: d.description, labels: d.labels,
1240
- assigneeUsernames: (d.assignees || []).map(a => a.username),
1241
- milestoneTitle: d.milestone || undefined,
1242
- }),
1243
- });
1244
- if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);
1245
- toast('Created \u2713', 'ok');
1246
- } else {
1247
- const orig = state.modal.original || {};
1248
- const patch = {};
1249
- if (d.title !== (orig.title || '')) patch.title = d.title;
1250
- if (d.description !== (orig.description || '')) patch.description = d.description;
1251
- const origLabels = (orig.labels || []).slice().sort().join('|');
1252
- const newLabels = (d.labels || []).slice().sort().join('|');
1253
- if (origLabels !== newLabels) patch.labels = d.labels;
1254
- const origA = (orig.assignees || []).map(a => a.username).sort().join('|');
1255
- const newA = (d.assignees || []).map(a => a.username).sort().join('|');
1256
- if (origA !== newA) patch.assigneeUsernames = (d.assignees || []).map(a => a.username);
1257
- if (d.milestone !== (orig.milestone || '')) patch.milestoneTitle = d.milestone || null;
1258
-
1259
- if (Object.keys(patch).length > 0) {
1260
- const r = await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(state.modal.id), {
1261
- method: 'PATCH', headers: csrfHeaders(), body: JSON.stringify(patch),
1262
- });
1263
- if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);
1264
- }
1265
- // State change \u2192 move to the appropriate column (reuses transition logic on server).
1266
- if (d.state !== (orig.state || 'opened')) {
1267
- const fromCol = state.current.columns.find(c => c.id === orig.stage);
1268
- const toCol = d.state === 'closed'
1269
- ? state.current.columns.find(c => c.kind === 'closed')
1270
- : state.current.columns.find(c => c.kind === 'open-backlog');
1271
- if (toCol) {
1272
- await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(state.modal.id) + '/move', {
1273
- method: 'PATCH', headers: csrfHeaders(),
1274
- body: JSON.stringify({ from: fromCol && fromCol.id, to: toCol.id }),
1275
- });
1276
- }
1277
- }
1278
- toast('Saved \u2713', 'ok');
1279
- }
1280
- closeModal();
1281
- await loadItems();
1282
- state.meta.labels = collectKnownLabels();
1283
- } catch (e) {
1284
- hint.textContent = 'Save failed: ' + e.message;
1285
- } finally {
1286
- saveBtn.disabled = false;
1287
- }
1288
- }
1289
-
1290
- document.getElementById('m-close').onclick = closeModal;
1291
- document.getElementById('m-cancel').onclick = closeModal;
1292
- document.getElementById('m-save').onclick = saveModal;
1293
- document.querySelector('#modal .modal-backdrop').onclick = closeModal;
1294
- document.getElementById('m-title').addEventListener('keydown', (e) => {
1295
- if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) saveModal();
1296
- });
1297
- const draftGo = document.getElementById('m-draft-go');
1298
- if (draftGo) draftGo.onclick = draftWithAgent;
1299
- const roughEl = document.getElementById('m-rough');
1300
- if (roughEl) roughEl.addEventListener('keydown', (e) => {
1301
- if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) draftWithAgent();
1302
- });
1303
-
1304
- // --- Agent activity panel (live polling of the daemon API) ---
1305
-
1306
- const activity = {
1307
- open: false, timer: null, daemonUrl: null, lastEventAt: 0, eventsRing: [],
1308
- };
1309
-
1310
- function resolveDaemonUrl() {
1311
- // Allow ?daemon=http://host:port override; fall back to localhost:18800 (MacBook) then 19900 (clawd).
1312
- const q = new URLSearchParams(window.location.search).get('daemon');
1313
- if (q) return q.replace(/\\/+$/, '');
1314
- return localStorage.getItem('agentx.daemon') || 'http://localhost:18800';
1315
- }
1316
-
1317
- function toggleActivity(force) {
1318
- activity.open = typeof force === 'boolean' ? force : !activity.open;
1319
- const panel = document.getElementById('activity-panel');
1320
- const btn = document.getElementById('activityBtn');
1321
- panel.classList.toggle('hidden', !activity.open);
1322
- panel.setAttribute('aria-hidden', activity.open ? 'false' : 'true');
1323
- btn.classList.toggle('on', activity.open);
1324
- document.body.classList.toggle('panel-open', activity.open);
1325
- if (activity.open) startActivity(); else stopActivity();
1326
- }
1327
-
1328
- async function startActivity() {
1329
- activity.daemonUrl = resolveDaemonUrl();
1330
- document.getElementById('activity-source').textContent = activity.daemonUrl.replace(/^https?:\\/\\//, '');
1331
- await tickActivity();
1332
- activity.timer = setInterval(tickActivity, 3000);
1333
- }
1334
- function stopActivity() {
1335
- if (activity.timer) { clearInterval(activity.timer); activity.timer = null; }
1336
- }
1337
-
1338
- async function tickActivity() {
1339
- const body = document.getElementById('activity-body');
1340
- try {
1341
- // Fetch /agents (always present) + /business/status (optional \u2014 404 tolerated)
1342
- const [agentsRes, bizRes] = await Promise.all([
1343
- fetch(activity.daemonUrl + '/agents').catch(() => null),
1344
- fetch(activity.daemonUrl + '/business/status').catch(() => null),
1345
- ]);
1346
- if (!agentsRes || !agentsRes.ok) throw new Error('daemon unreachable at ' + activity.daemonUrl);
1347
- const agents = await agentsRes.json();
1348
- const biz = bizRes && bizRes.ok ? await bizRes.json() : null;
1349
-
1350
- // Merge business data (currentItem, onClock, lastReport) per-agent when available.
1351
- const bizByAgent = {};
1352
- if (biz && Array.isArray(biz.employees)) for (const e of biz.employees) bizByAgent[e.agentId || e.id] = e;
1353
-
1354
- body.innerHTML = '';
1355
- for (const a of agents) {
1356
- const b = bizByAgent[a.id];
1357
- body.appendChild(renderAgentCard(a, b));
1358
- }
1359
- if (agents.length === 0) {
1360
- body.innerHTML = '<div class="empty">No agents on this daemon.</div>';
1361
- }
1362
- // Events ring
1363
- if (biz && biz.recentEvents) appendEvents(biz.recentEvents);
1364
- } catch (e) {
1365
- body.innerHTML = '<div class="empty" style="color:var(--red)">' + escapeHtml(e.message) + '</div>';
1366
- }
1367
- }
1368
-
1369
- function renderAgentCard(a, biz) {
1370
- const card = document.createElement('div');
1371
- card.className = 'agent-card';
1372
- const active = (a.active || 0) > 0 || (biz && biz.onClock && biz.busy);
1373
- const dotClass = (a.errors || 0) > 0 ? 'errored' : (active ? 'active' : '');
1374
- const current = biz && biz.currentItem
1375
- ? '<div class="current">Working on <b>' + escapeHtml(biz.currentItem.title || biz.currentItem.id) + '</b></div>'
1376
- : (active ? '<div class="current"><b>' + a.active + ' active task' + (a.active === 1 ? '' : 's') + '</b></div>' : '');
1377
- const modelLabel = a.model ? '<span class="model" title="Model">' + escapeHtml(shortenModelName(a.model)) + '</span>' : '';
1378
- const lastLabel = a.lastActive
1379
- ? '<span class="last" title="' + escapeHtml(new Date(a.lastActive).toLocaleString()) + '">last ' + escapeHtml(fmtAgoBoard(a.lastActive)) + '</span>'
1380
- : '<span class="last muted">never ran</span>';
1381
- card.innerHTML =
1382
- '<div class="top">' +
1383
- '<span class="dot ' + dotClass + '"></span>' +
1384
- '<span class="name">' + escapeHtml(a.name || a.id) + '</span>' +
1385
- '<span class="tier">' + escapeHtml(a.tier || '') + '</span>' +
1386
- '</div>' +
1387
- '<div class="meta">' + modelLabel + lastLabel + '</div>' +
1388
- '<div class="stats">' +
1389
- '<span>Active <b>' + (a.active || 0) + '</b></span>' +
1390
- '<span>Total <b>' + (a.total || 0) + '</b></span>' +
1391
- '<span class="errors">Errors <b>' + (a.errors || 0) + '</b></span>' +
1392
- '</div>' + current;
1393
- return card;
1394
- }
1395
-
1396
- function shortenModelName(m) {
1397
- if (!m) return '';
1398
- return String(m)
1399
- .replace(/^claude-/, '')
1400
- .replace(/-\\d{8}$/, '')
1401
- .replace(/\\[1m\\]$/, ' \xB7 1M');
1402
- }
1403
-
1404
- function fmtAgoBoard(iso) {
1405
- const t = new Date(iso).getTime();
1406
- if (!t) return '\u2014';
1407
- const s = Math.floor((Date.now() - t) / 1000);
1408
- if (s < 60) return s + 's ago';
1409
- const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';
1410
- const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';
1411
- const d = Math.floor(h / 24); return d + 'd ago';
1412
- }
1413
-
1414
- function appendEvents(events) {
1415
- const holder = document.getElementById('activity-events');
1416
- const existingKeys = new Set([...holder.querySelectorAll('.evt')].map(e => e.dataset.key));
1417
- for (const ev of events.slice(-50)) {
1418
- const key = (ev.ts || '') + '|' + (ev.type || '') + '|' + (ev.agentId || '');
1419
- if (existingKeys.has(key)) continue;
1420
- const div = document.createElement('div');
1421
- div.className = 'evt' + (ev.type && ev.type.includes('error') ? ' err' : '');
1422
- div.dataset.key = key;
1423
- div.innerHTML = '<span class="ts">' + (ev.ts ? new Date(ev.ts).toLocaleTimeString() : '') + '</span> ' +
1424
- '<span class="who">' + escapeHtml(ev.agentId || '\u2014') + '</span> ' +
1425
- '<span class="what">' + escapeHtml(ev.summary || ev.type || '') + '</span>';
1426
- holder.appendChild(div);
1427
- }
1428
- // Keep last 50 entries
1429
- while (holder.children.length > 50) holder.removeChild(holder.firstChild);
1430
- holder.scrollTop = holder.scrollHeight;
1431
- }
1432
-
1433
- loadBoards().catch(e => toast('Init failed: ' + e.message));
1434
- `;export{st as startBoardDashboard};
1435
- //# sourceMappingURL=board-dashboard-6TKE6NMV.js.map