@runfusion/fusion 0.17.0 → 0.17.2

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 (34) hide show
  1. package/dist/bin.js +1729 -789
  2. package/dist/client/assets/{AgentDetailView-DGqT1oDt.js → AgentDetailView-17J-F0Rl.js} +3 -3
  3. package/dist/client/assets/{AgentsView-BmemrfrO.js → AgentsView-sbBkb7Wd.js} +45 -45
  4. package/dist/client/assets/ChatView-BR5cvK_B.js +1 -0
  5. package/dist/client/assets/{DevServerView-C3Q0XqDA.js → DevServerView-GFFVXHVP.js} +1 -1
  6. package/dist/client/assets/{DirectoryPicker-BZWVA9ND.js → DirectoryPicker-WPDSBdT6.js} +1 -1
  7. package/dist/client/assets/{DocumentsView-DO48ivSq.js → DocumentsView-BHpDsIIt.js} +1 -1
  8. package/dist/client/assets/{InsightsView-CAngTfMf.js → InsightsView-Bxu0TJkt.js} +1 -1
  9. package/dist/client/assets/{MemoryView-B3rNcAOW.js → MemoryView-CmnzZorw.js} +2 -2
  10. package/dist/client/assets/{NodesView-BnV1LWa8.js → NodesView-CO9_4hCr.js} +4 -4
  11. package/dist/client/assets/{PiExtensionsManager-C3_Lw4sa.js → PiExtensionsManager-4e3MlD62.js} +3 -3
  12. package/dist/client/assets/{PluginManager-Vv3nzrJ1.js → PluginManager-DGN2rvOY.js} +1 -1
  13. package/dist/client/assets/ResearchView-Dsa6Gykl.js +1 -0
  14. package/dist/client/assets/{RoadmapsView-BiIpE-b8.js → RoadmapsView-jHTOK0RQ.js} +2 -2
  15. package/dist/client/assets/{SettingsModal-CK4w8Ztb.js → SettingsModal-4Z8ZJMzD.js} +1 -1
  16. package/dist/client/assets/SettingsModal-D0kuJpBA.js +31 -0
  17. package/dist/client/assets/{SetupWizardModal-Dw6N4UvY.js → SetupWizardModal-Bhumd4Rf.js} +1 -1
  18. package/dist/client/assets/{SkillsView-C1196wgA.js → SkillsView-MHweJTz4.js} +1 -1
  19. package/dist/client/assets/{folder-open-WVtgE4k3.js → folder-open-BNQW9dE9.js} +1 -1
  20. package/dist/client/assets/{index-BIJgrHEn.css → index-DEVBHvyW.css} +1 -1
  21. package/dist/client/assets/index-k_85J1DS.js +682 -0
  22. package/dist/client/assets/{star-MSImEC8V.js → star-7L86NZrT.js} +1 -1
  23. package/dist/client/assets/{upload-Dmvy3xXd.js → upload-DsAS6tno.js} +1 -1
  24. package/dist/client/assets/{users-CncYvHNf.js → users-D3u6f2Rz.js} +1 -1
  25. package/dist/client/index.html +2 -2
  26. package/dist/client/version.json +1 -1
  27. package/dist/extension.js +1239 -527
  28. package/dist/pi-claude-cli/package.json +1 -1
  29. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  30. package/package.json +1 -1
  31. package/dist/client/assets/ChatView-CZQUBFlV.js +0 -1
  32. package/dist/client/assets/ResearchView-Dfdsuc21.js +0 -1
  33. package/dist/client/assets/SettingsModal-BN00HYJ2.js +0 -31
  34. package/dist/client/assets/index-Bv0TGzDH.js +0 -682
@@ -1,14 +1,14 @@
1
- import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{c as pe,aC as we,aD as Pe,aE as Me,aF as Ee,aG as ye,aH as be,A as je,aI as Re,V as Ae,aJ as De,aK as Le,Y as ie,R as de,a as Ne,M as ee,aL as _e,aM as $e,aN as Ke,X as ue,aO as xe,aP as ze,aQ as Ie,aR as Fe,G as Oe}from"./index-Bv0TGzDH.js";import{U as ve}from"./upload-Dmvy3xXd.js";import"./vendor-xterm-DzcZoU0P.js";/**
1
+ import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{c as pe,aD as we,aE as Pe,aF as Me,aG as Ee,aH as ye,aI as be,A as je,aJ as Re,W as Ae,aK as De,aL as Le,_ as ie,u as $e,R as de,a as Ne,O as ee,aM as _e,aN as Ke,aO as ze,X as ue,aP as xe,aQ as Ie,aR as Fe,aS as Oe,G as Ve}from"./index-k_85J1DS.js";import{U as ve}from"./upload-DsAS6tno.js";import"./vendor-xterm-DzcZoU0P.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
- */const Ve=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],Ue=pe("wifi-off",Ve);/**
6
+ */const Ue=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]],Be=pe("wifi-off",Ue);/**
7
7
  * @license lucide-react v1.7.0 - ISC
8
8
  *
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
- */const Be=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],He=pe("wifi",Be);function re(t){const{lastSyncAt:l,remoteReachable:a,diff:i}=t,u=i.global.length+i.project.length;return l===null?{syncState:"never-synced",lastSyncAt:l,diffCount:0}:a?u>0?{syncState:"diff",lastSyncAt:l,diffCount:u}:{syncState:"synced",lastSyncAt:l,diffCount:0}:{syncState:"error",lastSyncAt:l,diffCount:u}}function ke(t){if(t===null)return"Never synced";const l=new Date(t);if(Number.isNaN(l.getTime()))return"Never synced";const i=Date.now()-l.getTime(),u=Math.floor(i/1e3),h=Math.floor(u/60),o=Math.floor(h/60),f=Math.floor(o/24);return h<1?"Synced just now":h<60?`Synced ${h}m ago`:o<24?`Synced ${o}h ago`:`Synced ${f}d ago`}function Te(t){switch(t){case"synced":return"var(--color-success)";case"pending":return"var(--color-warning)";case"diff":return"var(--color-warning)";case"error":return"var(--color-error)";case"never-synced":return"var(--text-muted)"}}const qe=3e4;function Je(){const[t,l]=s.useState({}),[a,i]=s.useState(!1),[u,h]=s.useState({}),[o,f]=s.useState(null),c=s.useRef(new Set),y=s.useRef(!1),p=s.useRef(null),N=s.useRef(null),C=s.useCallback(async(m,r)=>{try{const b=await we(m);l(z=>({...z,[m]:b})),f(null)}catch(b){console.error(`Failed to fetch sync status for node ${m}:`,b),f(b instanceof Error?b.message:"Failed to fetch sync status")}},[]),v=s.useCallback(async()=>{const m=Array.from(c.current);if(m.length===0)return;p.current&&p.current.abort(),p.current=new AbortController;const r=!y.current;r&&i(!0),f(null);try{const b=await Promise.allSettled(m.map(F=>C(F,r)));y.current=!0,b.filter(F=>F.status==="rejected").length>0&&f("Some sync status requests failed")}catch(b){if(b instanceof Error&&b.name==="AbortError")return;f(b instanceof Error?b.message:"Failed to fetch sync status"),y.current=!0}finally{i(!1)}},[C]),P=s.useCallback(()=>{N.current&&clearInterval(N.current),N.current=setInterval(()=>{v()},qe)},[v]),j=s.useCallback(()=>{N.current&&(clearInterval(N.current),N.current=null)},[]);s.useEffect(()=>(v(),P(),()=>{j(),p.current&&p.current.abort()}),[v,P,j]);const n=s.useCallback(m=>{c.current.has(m)||(c.current.add(m),C(m,!y.current))},[C]),x=s.useCallback(m=>{c.current.delete(m),l(r=>{const b={...r};return delete b[m],b}),c.current.size===0&&j()},[j]),g=s.useCallback(async m=>{h(r=>({...r,[m]:!0})),f(null);try{const r=await Pe(m);return C(m,!1),!r.success&&r.error&&f(r.error),r}catch(r){const b=r instanceof Error?r.message:"Push settings failed";throw f(b),r}finally{h(r=>{const b={...r};return delete b[m],b})}},[C]),M=s.useCallback(async m=>{h(r=>({...r,[m]:!0})),f(null);try{const r=await Me(m);return C(m,!1),!r.success&&r.error&&f(r.error),r}catch(r){const b=r instanceof Error?r.message:"Pull settings failed";throw f(b),r}finally{h(r=>{const b={...r};return delete b[m],b})}},[C]),R=s.useCallback(async m=>{h(r=>({...r,[m]:!0})),f(null);try{return await Ee(m)}catch(r){const b=r instanceof Error?r.message:"Auth sync failed";throw f(b),r}finally{h(r=>{const b={...r};return delete b[m],b})}},[]),E=s.useCallback(m=>t[m]?.authMatch,[t]),D=s.useCallback(m=>t[m]?.authDiff,[t]);return{syncStatusMap:t,loading:a,actionLoading:u,error:o,refresh:v,trackNode:n,untrackNode:x,pushSettings:g,pullSettings:M,syncAuth:R,getAuthSyncState:E,getAuthProviders:D}}function Xe(t,l){return l.type==="remote"?t.nodeId===l.id:t.nodeId===l.id||t.nodeId===void 0||t.nodeId===null}function Ce(t,l){return t.filter(a=>Xe(a,l))}function me(t,l){return Ce(t,l).length}const Ge={online:{label:"Online",color:"var(--color-success)",className:"node-card__status--online"},offline:{label:"Offline",color:"var(--color-error)",className:"node-card__status--offline"},connecting:{label:"Connecting",color:"var(--color-warning)",className:"node-card__status--connecting"},error:{label:"Error",color:"var(--color-error)",className:"node-card__status--error"}},Ye={match:"var(--color-success)",differs:"var(--color-warning)","not-synced":"var(--text-muted)"};function We(t,l){if(t==="match")return"Auth credentials match";if(t==="not-synced")return"Auth not synced";if(l&&Object.keys(l).length>0){const a=Object.entries(l).filter(([,i])=>i==="differs").map(([i])=>i);if(a.length>0)return`Auth credentials differ: ${a.join(", ")}`}return"Auth credentials differ"}function Qe(t,l=42){return t.length<=l?t:`${t.slice(0,l-3)}...`}function Ze(t,l){const a=t.node,i=l.node;if(a.id!==i.id||a.name!==i.name||a.type!==i.type||a.url!==i.url||a.status!==i.status||a.maxConcurrent!==i.maxConcurrent||a.updatedAt!==i.updatedAt||t.isLoading!==l.isLoading)return!1;const u=t.syncStatus,h=l.syncStatus;if(!(!u&&!h)){if(!u||!h)return!1;if(u.syncState!==h.syncState||u.lastSyncAt!==h.lastSyncAt||u.diffCount!==h.diffCount)return!1}if(t.authSyncState!==l.authSyncState)return!1;const o=t.authSyncProviders,f=l.authSyncProviders;if(o!==f){if(!o||!f)return!1;{const p=Object.keys(o),N=Object.keys(f);if(p.length!==N.length||p.some(C=>o[C]!==f[C]))return!1}}const c=me(t.projects,a),y=me(l.projects,i);return c===y}function es({node:t,projects:l,onHealthCheck:a,onEdit:i,onRemove:u,isLoading:h=!1,syncStatus:o,authSyncState:f,authSyncProviders:c}){const[y,p]=s.useState(!1),N=Ge[t.status],C=s.useMemo(()=>me(l,t),[l,t]),v=s.useCallback(()=>{i(t)},[i,t]),P=s.useCallback(g=>{g.stopPropagation(),a(t.id)},[a,t.id]),j=s.useCallback(g=>{g.stopPropagation(),i(t)},[i,t]),n=s.useCallback(g=>{if(g.stopPropagation(),!y){p(!0);return}u(t.id),p(!1)},[y,u,t.id]),x=s.useCallback(g=>{(g.key==="Enter"||g.key===" ")&&(g.preventDefault(),i(t))},[i,t]);return e.jsxs("article",{className:`node-card ${h?"node-card--loading":""}`,"data-node-id":t.id,role:"button",tabIndex:0,onClick:v,onKeyDown:x,children:[e.jsx("header",{className:"node-card__header",children:e.jsxs("div",{className:"node-card__title-wrap",children:[e.jsx("div",{className:"node-card__icon",children:e.jsx(ye,{size:18})}),e.jsxs("div",{children:[e.jsx("h3",{className:"node-card__name",title:t.name,children:t.name}),e.jsxs("div",{className:"node-card__meta-row",children:[e.jsx("span",{className:"node-card__type-badge",children:t.type==="local"?"Local":"Remote"}),e.jsxs("span",{className:`node-card__status ${N.className}`,style:{color:N.color},"data-status":t.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:N.color},"aria-hidden":!0}),N.label]}),t.type==="remote"&&f&&e.jsx("span",{className:`node-card__auth-indicator node-card__auth-indicator--${f}`,title:We(f,c),"aria-label":`Auth sync: ${f==="match"?"credentials match":f==="differs"?"credentials differ":"not synced"}`,style:{color:Ye[f]},children:e.jsx(be,{size:14})})]})]})]})}),e.jsxs("div",{className:"node-card__body",children:[t.type==="remote"&&t.url&&e.jsx("div",{className:"node-card__url",title:t.url,children:Qe(t.url)}),e.jsxs("div",{className:"node-card__metrics",children:[e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Projects"}),e.jsx("span",{className:"node-card__metric-value",children:C})]}),e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Concurrency"}),e.jsx("span",{className:"node-card__metric-value",children:t.maxConcurrent})]})]}),t.type==="remote"&&o&&e.jsxs("div",{className:"node-card__sync","data-sync-state":o.syncState,"data-testid":"node-card-sync",children:[e.jsx("span",{className:"node-card__sync-dot",style:{backgroundColor:Te(o.syncState)},"aria-hidden":!0}),e.jsx("span",{className:"node-card__sync-time",children:ke(o.lastSyncAt)})]})]}),e.jsxs("footer",{className:"node-card__actions",children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:P,disabled:h,"aria-label":"Run node health check",title:"Health Check",children:[e.jsx(je,{size:14}),e.jsx("span",{children:"Health"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Edit node",title:"Edit",children:[e.jsx(Re,{size:14}),e.jsx("span",{children:"Edit"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Start node container",title:"Start Container",children:[e.jsx(Ae,{size:14}),e.jsx("span",{children:"Start"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Stop node container",title:"Stop Container",children:[e.jsx(De,{size:14}),e.jsx("span",{children:"Stop"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Restart node container",title:"Restart Container",children:[e.jsx(Le,{size:14}),e.jsx("span",{children:"Restart"})]}),e.jsxs("button",{className:`btn btn-sm node-card__action node-card__action--remove ${y?"btn-danger is-armed":""}`,type:"button",onClick:n,disabled:h,"aria-label":y?"Confirm remove node":"Remove node",title:y?"Confirm remove":"Remove",children:[e.jsx(ie,{size:14}),e.jsx("span",{children:y?"Confirm":"Remove"})]})]})]})}const ss=s.memo(es,Ze),Y={online:"var(--success, var(--color-success))",offline:"var(--text-dim)",connecting:"var(--triage)",error:"var(--color-error)"},G=28,ge=12,ts=300,as=120;function ns({nodes:t,className:l}){const a=s.useMemo(()=>t.find(c=>c.type==="local")??t[0],[t]),i=s.useMemo(()=>t.filter(c=>c.type==="remote"),[t]),u=s.useMemo(()=>{const c=ts,y=Math.max(0,i.length-4)*20;return c+y},[i.length]),h=u/2,o=u/2,f=s.useMemo(()=>{if(i.length===0)return[];const c=Math.min(as,u/2-G-10),y=2*Math.PI/i.length,p=-Math.PI/2;return i.map((N,C)=>{const v=p+C*y;return{node:N,x:h+c*Math.cos(v),y:o+c*Math.sin(v)}})},[i,u,h,o]);return t.length===0?e.jsx("div",{className:`mesh-topology mesh-topology--empty ${l??""}`,children:e.jsx("div",{className:"mesh-topology__empty-state",children:e.jsx("p",{children:"No nodes to display"})})}):e.jsxs("div",{className:`mesh-topology ${l??""}`,children:[e.jsxs("svg",{className:"mesh-topology__svg",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet","aria-label":"Node mesh topology visualization",children:[f.map(c=>e.jsx("line",{className:"mesh-topology__link",x1:h,y1:o,x2:c.x,y2:c.y},`link-${c.node.id}`)),a&&e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${h}, ${o})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:G,fill:Y[a.status],"aria-label":`${a.name} (${a.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:G+ge,textAnchor:"middle",children:a.name.length>12?`${a.name.slice(0,10)}…`:a.name}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-G-10})`,children:[e.jsx("circle",{className:"mesh-topology__node-type-badge",r:"8"}),e.jsx("text",{className:"mesh-topology__node-type-text",textAnchor:"middle",dominantBaseline:"middle",children:a.type==="local"?"L":"R"})]})]}),f.map(c=>e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${c.x}, ${c.y})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:G,fill:Y[c.node.status],"aria-label":`${c.node.name} (${c.node.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:G+ge,textAnchor:"middle",children:c.node.name.length>12?`${c.node.name.slice(0,10)}…`:c.node.name}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-G-10})`,children:[e.jsx("circle",{className:"mesh-topology__node-type-badge",r:"8"}),e.jsx("text",{className:"mesh-topology__node-type-text",textAnchor:"middle",dominantBaseline:"middle",children:c.node.type==="local"?"L":"R"})]})]},c.node.id))]}),e.jsxs("div",{className:"mesh-topology__legend",children:[e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:Y.online}}),e.jsx("span",{children:"Online"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:Y.offline}}),e.jsx("span",{children:"Offline"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:Y.connecting}}),e.jsx("span",{children:"Connecting"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:Y.error}}),e.jsx("span",{children:"Error"})]})]}),e.jsx("p",{className:"mesh-topology__notice",children:"Peer-to-peer discovery data unavailable."})]})}const ls=s.memo(ns),he=1,fe=10;function rs(t){const l={};return t.name.trim()||(l.name="Name is required"),t.type==="remote"&&!t.url?.trim()&&(l.url="URL is required for remote nodes"),(!Number.isFinite(t.maxConcurrent)||t.maxConcurrent<he||t.maxConcurrent>fe)&&(l.maxConcurrent=`Concurrency must be between ${he} and ${fe}`),l}function os({isOpen:t,onClose:l,onSubmit:a,addToast:i}){const[u,h]=s.useState(""),[o,f]=s.useState("local"),[c,y]=s.useState(""),[p,N]=s.useState(""),[C,v]=s.useState(2),[P,j]=s.useState("auto-generate"),[n,x]=s.useState({}),[g,M]=s.useState(!1),R=s.useCallback(()=>{h(""),f("local"),y(""),N(""),v(2),j("auto-generate"),x({}),M(!1)},[]),E=s.useCallback(()=>{g||(R(),l())},[g,l,R]);s.useEffect(()=>{if(!t){R();return}const r=b=>{b.key==="Escape"&&(b.preventDefault(),E())};return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r)}},[E,t,R]);const D=s.useMemo(()=>({name:u.trim(),type:o,url:o==="remote"&&c.trim()||void 0,apiKey:o==="remote"&&P==="provide"&&p||void 0,maxConcurrent:C,apiKeyMode:P}),[p,P,C,u,o,c]),m=s.useCallback(async()=>{if(g)return;const r=rs(D);if(x(r),!(Object.keys(r).length>0)){M(!0);try{await a(D),i(`Node "${D.name}" registered`,"success"),E()}catch(b){const z=b instanceof Error?b.message:"Failed to register node";i(z,"error")}finally{M(!1)}}},[i,E,D,g,a]);return t?e.jsx("div",{className:"modal-overlay open",onClick:E,children:e.jsxs("div",{className:"modal modal-md add-node-modal",onClick:r=>r.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Add Node",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Add Node"}),e.jsx("button",{className:"modal-close",onClick:E,disabled:g,"aria-label":"Close add node modal",children:"×"})]}),e.jsxs("div",{className:"modal-body add-node-modal__body",children:[e.jsx("p",{className:"add-node-modal__description",children:"Register an existing Fusion node by providing its connection details and concurrency settings."}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{className:"input",type:"text",value:u,onChange:r=>h(r.target.value),placeholder:"Build Machine",disabled:g,"aria-invalid":!!n.name,autoFocus:!0}),n.name&&e.jsx("span",{className:"form-error add-node-modal__error",children:n.name})]}),e.jsxs("div",{className:"add-node-modal__type-toggle",children:[e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${o==="local"?"active":""}`,"data-type":"local",onClick:()=>f("local"),disabled:g,"aria-pressed":o==="local",children:"Local"}),e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${o==="remote"?"active":""}`,"data-type":"remote",onClick:()=>f("remote"),disabled:g,"aria-pressed":o==="remote",children:"Remote"})]}),o==="remote"&&e.jsxs("div",{className:"add-node-modal__remote-fields","data-testid":"remote-fields-container","data-visible":!0,children:[e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Reachable URL / Hostname"}),e.jsx("input",{className:"input",type:"text",value:c,onChange:r=>y(r.target.value),placeholder:"https://node.example.com",disabled:g,"aria-invalid":!!n.url}),n.url&&e.jsx("span",{className:"form-error add-node-modal__error",children:n.url})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key Mode"}),e.jsxs("select",{className:"select",value:P,onChange:r=>j(r.target.value),disabled:g,children:[e.jsx("option",{value:"auto-generate",children:"Auto-generate"}),e.jsx("option",{value:"provide",children:"Provide key manually"})]})]}),P==="provide"&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:p,onChange:r=>N(r.target.value),placeholder:"Enter node API key",disabled:g})]})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),e.jsx("input",{className:"input",type:"number",min:he,max:fe,value:C,onChange:r=>v(Number(r.target.value)),disabled:g,"aria-invalid":!!n.maxConcurrent}),e.jsx("span",{className:"add-node-modal__hint",children:"Max simultaneous task agents (1–10)"}),n.maxConcurrent&&e.jsx("span",{className:"form-error add-node-modal__error",children:n.maxConcurrent})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:E,disabled:g,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm","data-testid":"add-node-submit",onClick:m,disabled:g,children:g?"Adding...":"Add Node"})]})]})}):null}function cs(){const[t,l]=s.useState([]),[a,i]=s.useState(!1),[u,h]=s.useState(null),[o,f]=s.useState(!1),[c,y]=s.useState(null),[p,N]=s.useState(!1),C=s.useCallback(async()=>{i(!0),h(null);try{const j=await fetch("/api/docker/contexts");if(!j.ok)throw new Error(`Failed to load Docker contexts (${j.status})`);const n=await j.json();return l(n),n}catch(j){const n=j instanceof Error?j.message:String(j);throw h(n),j}finally{i(!1)}},[]),v=s.useCallback(async j=>{f(!0);try{const n=await fetch("/api/docker/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hostConfig:j})});if(!n.ok)throw new Error(`Failed to test Docker connection (${n.status})`);const x=await n.json();return y(x),x}finally{f(!1)}},[]),P=s.useCallback(async()=>{N(!0);try{const j=await fetch("/api/docker/local-available");if(!j.ok)throw new Error(`Failed to check local Docker availability (${j.status})`);return await j.json()}finally{N(!1)}},[]);return{contexts:t,isLoadingContexts:a,contextsError:u,loadContexts:C,isTestingConnection:o,lastTestResult:c,testConnection:v,isCheckingLocal:p,checkLocalDocker:P}}function is({value:t,onChange:l}){const[a,i]=s.useState(!!(t?.tlsCaPath||t?.tlsCertPath||t?.tlsKeyPath||t?.tlsVerify));s.useEffect(()=>{a||l({tlsVerify:void 0,tlsCaPath:void 0,tlsCertPath:void 0,tlsKeyPath:void 0})},[a,l]);const u=s.useMemo(()=>({tlsVerify:t?.tlsVerify??!0,tlsCaPath:t?.tlsCaPath??"",tlsCertPath:t?.tlsCertPath??"",tlsKeyPath:t?.tlsKeyPath??""}),[t]);return e.jsxs("div",{className:"docker-tls-config",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:a,onChange:h=>i(h.target.checked)}),"Use TLS"]}),a&&e.jsxs("div",{className:"docker-tls-config__fields",children:[e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-ca-path",children:"CA Certificate Path"}),e.jsx("input",{id:"docker-tls-ca-path",className:"input",value:u.tlsCaPath,onChange:h=>l({...u,tlsCaPath:h.target.value}),placeholder:"/etc/docker/ca.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-cert-path",children:"Client Certificate Path"}),e.jsx("input",{id:"docker-tls-cert-path",className:"input",value:u.tlsCertPath,onChange:h=>l({...u,tlsCertPath:h.target.value}),placeholder:"/etc/docker/cert.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-key-path",children:"Client Key Path"}),e.jsx("input",{id:"docker-tls-key-path",className:"input",value:u.tlsKeyPath,onChange:h=>l({...u,tlsKeyPath:h.target.value}),placeholder:"/etc/docker/key.pem"})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:u.tlsVerify,onChange:h=>l({...u,tlsVerify:h.target.checked})}),"Verify TLS Certificate"]})]})]})}function ds({value:t,onChange:l,onError:a}){const i=t?.context?"context":t?.host?"host":"local",[u,h]=s.useState(i),[o,f]=s.useState(t?.context??""),[c,y]=s.useState(t?.host??""),[p,N]=s.useState(null),{contexts:C,isLoadingContexts:v,contextsError:P,loadContexts:j,testConnection:n,isTestingConnection:x,lastTestResult:g,checkLocalDocker:M,isCheckingLocal:R}=cs(),E=s.useMemo(()=>({tlsVerify:t?.tlsVerify,tlsCaPath:t?.tlsCaPath,tlsCertPath:t?.tlsCertPath,tlsKeyPath:t?.tlsKeyPath}),[t]);s.useEffect(()=>{if(u==="local"){l({});return}if(u==="context"){j().catch(m=>a?.(m instanceof Error?m.message:String(m))),l(o?{context:o}:{});return}l({host:c,...E})},[u]);const D=s.useCallback(m=>{u==="host"&&l({host:c,...m})},[c,u,l]);return e.jsxs("div",{className:"docker-target-selector",children:[e.jsxs("div",{className:"docker-target-selector__modes",role:"group","aria-label":"Docker target mode",children:[e.jsx("button",{type:"button",className:`btn btn-sm ${u==="local"?"docker-target-selector__mode-active":""}`,onClick:()=>{h("local"),M().then(m=>N(m.available?`Docker is available${m.version?` (${m.version})`:""}`:`Docker not found${m.error?`: ${m.error}`:""}`)).catch(m=>{const r=m instanceof Error?m.message:String(m);N(`Docker not found: ${r}`),a?.(r)})},children:"Local Docker"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="context"?"docker-target-selector__mode-active":""}`,onClick:()=>h("context"),children:"Docker Context"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="host"?"docker-target-selector__mode-active":""}`,onClick:()=>h("host"),children:"Remote Host"})]}),u==="local"&&p&&e.jsx("div",{className:"docker-target-selector__status",children:p}),u==="context"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__context-row",children:[e.jsxs("select",{className:"select",value:o,onChange:m=>{const r=m.target.value;f(r),l(r?{context:r}:{})},children:[e.jsx("option",{value:"",children:"Select context"}),C.map(m=>e.jsxs("option",{value:m.name,children:[m.name,m.isCurrentContext?" (current)":"",m.dockerHost?` — ${m.dockerHost}`:""]},m.name))]}),e.jsx("button",{type:"button",className:"btn btn-sm btn-icon",onClick:()=>void j(),disabled:v,"aria-label":"Refresh contexts",children:e.jsx(de,{size:14})})]}),P&&e.jsx("div",{className:"docker-target-selector__error",children:P})]}),u==="host"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__field",children:[e.jsx("label",{htmlFor:"docker-target-selector-host",children:"Docker Host"}),e.jsx("input",{id:"docker-target-selector-host",className:"input",placeholder:"tcp://host:2376",value:c,onChange:m=>{const r=m.target.value;y(r),l({host:r,...E})}})]}),e.jsx(is,{value:E,onChange:D})]}),e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void n(u==="local"?void 0:u==="context"?{context:o}:{host:c,...E}),disabled:x||R,children:x?"Testing...":"Test Connection"}),g&&e.jsx("div",{className:g.success?"docker-target-selector__success":"docker-target-selector__error",children:g.success?`Connected${g.dockerVersion?` (Docker ${g.dockerVersion})`:""}`:g.error??"Connection failed"})]})}const oe="http://localhost:4040";function us({isOpen:t,onClose:l,onSubmit:a,addToast:i}){const[u,h]=s.useState(""),[o,f]=s.useState({}),[c,y]=s.useState(oe),[p,N]=s.useState("auto"),[C,v]=s.useState(""),[P,j]=s.useState(!1),[n,x]=s.useState(!1),[g,M]=s.useState(!0),[R,E]=s.useState(4096),[D,m]=s.useState(2),[r,b]=s.useState(!1),[z,F]=s.useState("runfusion/fusion"),[H,q]=s.useState("latest"),[O,V]=s.useState([]),[T,k]=s.useState([]),[S,K]=s.useState({}),[w,J]=s.useState(!1),L=s.useCallback(()=>{h(""),f({}),y(oe),N("auto"),v(""),j(!1),x(!1),M(!0),E(4096),m(2),b(!1),F("runfusion/fusion"),q("latest"),V([]),k([]),K({}),J(!1)},[]),U=s.useCallback(()=>{w||(L(),l())},[l,L,w]);s.useEffect(()=>{if(!t){L();return}const d=A=>{A.key==="Escape"&&(A.preventDefault(),U())};return document.addEventListener("keydown",d),()=>document.removeEventListener("keydown",d)},[U,t,L]);const X=s.useMemo(()=>({nodeId:null,name:u.trim(),imageName:z.trim()||"runfusion/fusion",imageTag:H.trim()||"latest",hostConfig:{context:o.context?.trim()||void 0,host:o.host?.trim()||void 0,tlsVerify:o.tlsVerify,tlsCaPath:o.tlsCaPath?.trim()||void 0,tlsCertPath:o.tlsCertPath?.trim()||void 0,tlsKeyPath:o.tlsKeyPath?.trim()||void 0},envVars:Object.fromEntries(O.map(d=>[d.key.trim(),d.value]).filter(([d])=>!!d)),volumeMounts:T.map(d=>({hostPath:d.hostPath.trim(),containerPath:d.containerPath.trim(),mode:d.mode})).filter(d=>d.hostPath&&d.containerPath),resourceSizing:{memoryMB:R,cpus:D},extraClis:[P?"claude-cli":null,n?"droid-cli":null].filter(Boolean),persistentStorage:g,reachableUrl:c.trim()||null,apiKey:p==="manual"&&C.trim()||null}),[C,p,D,o,O,z,H,P,n,R,T,u,g,c]),te=s.useCallback(()=>{V(d=>[...d,{key:"",value:""}])},[]),se=s.useCallback((d,A)=>{V($=>$.map((B,Z)=>Z===d?A:B))},[]),W=s.useCallback(d=>{V(A=>A.filter(($,B)=>B!==d))},[]),ae=s.useCallback(()=>{k(d=>[...d,{hostPath:"",containerPath:"",mode:"rw"}])},[]),Q=s.useCallback((d,A)=>{k($=>$.map((B,Z)=>Z===d?A:B))},[]),ne=s.useCallback(d=>{k(A=>A.filter(($,B)=>B!==d))},[]),le=s.useCallback(async()=>{if(w)return;const d={};if((!X.name||X.name.length>64)&&(d.name="Name is required and must be 64 characters or fewer"),X.reachableUrl||(d.reachableUrl="URL is required"),R<512&&(d.memoryMB="Memory must be at least 512 MB"),D<.5&&(d.cpus="CPUs must be at least 0.5"),K(d),!(Object.keys(d).length>0)){J(!0);try{await a(X),U()}catch{}finally{J(!1)}}},[U,D,X,R,a,w]);return t?e.jsx("div",{className:"modal-overlay open",onClick:U,children:e.jsxs("div",{className:"modal docker-onboarding",role:"dialog","aria-modal":"true","aria-label":"Docker node onboarding",onClick:d=>d.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Provision Docker Node"}),e.jsx("button",{className:"modal-close",onClick:U,disabled:w,"aria-label":"Close onboarding modal",children:"×"})]}),e.jsxs("div",{className:"modal-body docker-onboarding__body",children:[e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsx("h4",{className:"docker-onboarding__section-title",children:"Required Settings"}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Node Name"}),e.jsx("input",{className:"input",value:u,onChange:d=>h(d.target.value),disabled:w,placeholder:"my-docker-node",autoFocus:!0})]}),S.name&&e.jsx("div",{className:"form-error",children:S.name}),e.jsx(ds,{value:o,onChange:f}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Reachable URL"}),e.jsx("input",{className:"input",value:c,onChange:d=>y(d.target.value),disabled:w,placeholder:oe})]}),S.reachableUrl&&e.jsx("div",{className:"form-error",children:S.reachableUrl}),e.jsxs("div",{className:"docker-onboarding__radio-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:p==="auto",onChange:()=>N("auto"),disabled:w}),"Auto-generate"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:p==="manual",onChange:()=>N("manual"),disabled:w}),"Provide manually"]})]}),p==="manual"&&e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:C,onChange:d=>v(d.target.value),disabled:w,placeholder:"Enter API key"})]}),e.jsxs("div",{className:"docker-onboarding__checkbox-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:P,onChange:d=>j(d.target.checked),disabled:w}),"Claude CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:n,onChange:d=>x(d.target.checked),disabled:w}),"Droid CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:g,onChange:d=>M(d.target.checked),disabled:w}),"Keep data across container recreations"]})]}),e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Memory (MB)"}),e.jsx("input",{className:"input",type:"number",min:512,value:R,onChange:d=>E(Number(d.target.value)),disabled:w})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"CPUs"}),e.jsx("input",{className:"input",type:"number",min:.5,step:.5,value:D,onChange:d=>m(Number(d.target.value)),disabled:w})]})]}),S.memoryMB&&e.jsx("div",{className:"form-error",children:S.memoryMB}),S.cpus&&e.jsx("div",{className:"form-error",children:S.cpus})]}),e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsxs("button",{type:"button",className:`docker-onboarding__advanced-toggle ${r?"is-expanded":""}`,onClick:()=>b(d=>!d),disabled:w,children:[e.jsx("span",{children:"Advanced"}),e.jsx(Ne,{})]}),e.jsx("div",{className:`docker-onboarding__advanced-content ${r?"is-expanded":""}`,children:e.jsxs("div",{children:[e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:z,onChange:d=>F(d.target.value),disabled:w,placeholder:"runfusion/fusion"})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Tag"}),e.jsx("input",{className:"input",value:H,onChange:d=>q(d.target.value),disabled:w,placeholder:"latest"})]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Environment Variables"}),O.map((d,A)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--env",children:[e.jsx("input",{className:"input",placeholder:"KEY",value:d.key,disabled:w,onChange:$=>se(A,{key:$.target.value,value:d.value})}),e.jsx("input",{className:"input",placeholder:"Value",value:d.value,disabled:w,onChange:$=>se(A,{key:d.key,value:$.target.value})}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove environment variable",onClick:()=>W(A),disabled:w,children:e.jsx(ie,{size:14})})]},`env-${A}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:te,disabled:w,children:[e.jsx(ee,{size:14}),"Add variable"]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Volume Mounts"}),T.map((d,A)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--mount",children:[e.jsx("input",{className:"input",placeholder:"Host path",value:d.hostPath,disabled:w,onChange:$=>Q(A,{hostPath:$.target.value,containerPath:d.containerPath,mode:d.mode})}),e.jsx("input",{className:"input",placeholder:"Container path",value:d.containerPath,disabled:w,onChange:$=>Q(A,{hostPath:d.hostPath,containerPath:$.target.value,mode:d.mode})}),e.jsxs("select",{className:"select",value:d.mode,disabled:w,onChange:$=>Q(A,{hostPath:d.hostPath,containerPath:d.containerPath,mode:$.target.value==="ro"?"ro":"rw"}),children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove volume mount",onClick:()=>ne(A),disabled:w,children:e.jsx(ie,{size:14})})]},`mount-${A}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:ae,disabled:w,children:[e.jsx(ee,{size:14}),"Add mount"]})]})]})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",onClick:U,disabled:w,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",onClick:()=>void le(),disabled:w,children:w?"Creating...":"Create Docker Node"})]})]})}):null}function ms({nodeId:t,entries:l,loading:a=!1,singleNode:i=!1}){const[u,h]=s.useState(!1),[o,f]=s.useState("all"),[c,y]=s.useState("all"),p=s.useCallback(()=>{h(n=>!n)},[]),N=s.useMemo(()=>{const n=new Set;for(const x of l)n.add(x.nodeName);return Array.from(n).sort()},[l]),C=s.useMemo(()=>{let n=[...l];return o!=="all"&&(n=n.filter(x=>x.direction===o)),!i&&c!=="all"&&(n=n.filter(x=>x.nodeName===c)),n.sort((x,g)=>{const M=new Date(x.timestamp).getTime();return new Date(g.timestamp).getTime()-M}),n},[l,o,c,i]),v=s.useCallback(n=>new Date(n).toLocaleString(),[]),P=s.useCallback(n=>{switch(n){case"success":return"settings-sync-log__badge--success";case"conflict":return"settings-sync-log__badge--conflict";case"error":return"settings-sync-log__badge--error";default:return""}},[]),j=s.useCallback(n=>{switch(n){case"success":return"Success";case"conflict":return"Conflict";case"error":return"Error";default:return n}},[]);return e.jsxs("div",{className:"settings-sync-log",children:[e.jsxs("button",{className:"settings-sync-log__header",type:"button",onClick:p,"aria-expanded":u,"data-testid":"settings-sync-log-header",children:[e.jsx(Ne,{size:16,className:`settings-sync-log__chevron ${u?"settings-sync-log__chevron--expanded":""}`}),e.jsxs("span",{children:[l.length," ",l.length===1?"entry":"entries"]})]}),u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"settings-sync-log__filters",children:[e.jsxs("label",{children:["Direction:",e.jsxs("select",{value:o,onChange:n=>f(n.target.value),children:[e.jsx("option",{value:"all",children:"All"}),e.jsx("option",{value:"push",children:"Push"}),e.jsx("option",{value:"pull",children:"Pull"})]})]}),!i&&e.jsxs("label",{children:["Node:",e.jsxs("select",{value:c,onChange:n=>y(n.target.value),children:[e.jsx("option",{value:"all",children:"All Nodes"}),N.map(n=>e.jsx("option",{value:n,children:n},n))]})]})]}),a&&l.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"Loading..."}):C.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"No sync history available"}):e.jsx("div",{className:"settings-sync-log__list",children:C.map(n=>e.jsxs("div",{className:"settings-sync-log__entry",children:[e.jsx("span",{className:"settings-sync-log__entry-timestamp",children:v(n.timestamp)}),e.jsx("span",{className:"settings-sync-log__entry-direction",children:n.direction==="push"?e.jsx(ve,{size:14,"data-testid":"upload-icon"}):e.jsx(_e,{size:14,"data-testid":"download-icon"})}),e.jsx("span",{className:`settings-sync-log__entry-result ${P(n.result)}`,children:j(n.result)}),!i&&e.jsx("span",{className:"settings-sync-log__entry-node",children:n.nodeName}),n.details&&e.jsx("span",{className:"settings-sync-log__entry-details",title:n.details,children:n.details})]},n.id))})]})]})}function hs(t,l){const a=typeof t=="string"?t:JSON.stringify(t,null,2),i=typeof l=="string"?l:JSON.stringify(l,null,2);if(a===i)return a;const u=a.split(`
11
+ */const He=[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]],Te=pe("wifi",He);function re(t){const{lastSyncAt:l,remoteReachable:a,diff:i}=t,u=i.global.length+i.project.length;return l===null?{syncState:"never-synced",lastSyncAt:l,diffCount:0}:a?u>0?{syncState:"diff",lastSyncAt:l,diffCount:u}:{syncState:"synced",lastSyncAt:l,diffCount:0}:{syncState:"error",lastSyncAt:l,diffCount:u}}function ke(t){if(t===null)return"Never synced";const l=new Date(t);if(Number.isNaN(l.getTime()))return"Never synced";const i=Date.now()-l.getTime(),u=Math.floor(i/1e3),h=Math.floor(u/60),o=Math.floor(h/60),f=Math.floor(o/24);return h<1?"Synced just now":h<60?`Synced ${h}m ago`:o<24?`Synced ${o}h ago`:`Synced ${f}d ago`}function qe(t){switch(t){case"synced":return"var(--color-success)";case"pending":return"var(--color-warning)";case"diff":return"var(--color-warning)";case"error":return"var(--color-error)";case"never-synced":return"var(--text-muted)"}}const Je=3e4;function Xe(){const[t,l]=s.useState({}),[a,i]=s.useState(!1),[u,h]=s.useState({}),[o,f]=s.useState(null),c=s.useRef(new Set),y=s.useRef(!1),p=s.useRef(null),N=s.useRef(null),C=s.useCallback(async(m,r)=>{try{const b=await we(m);l(z=>({...z,[m]:b})),f(null)}catch(b){console.error(`Failed to fetch sync status for node ${m}:`,b),f(b instanceof Error?b.message:"Failed to fetch sync status")}},[]),v=s.useCallback(async()=>{const m=Array.from(c.current);if(m.length===0)return;p.current&&p.current.abort(),p.current=new AbortController;const r=!y.current;r&&i(!0),f(null);try{const b=await Promise.allSettled(m.map(F=>C(F,r)));y.current=!0,b.filter(F=>F.status==="rejected").length>0&&f("Some sync status requests failed")}catch(b){if(b instanceof Error&&b.name==="AbortError")return;f(b instanceof Error?b.message:"Failed to fetch sync status"),y.current=!0}finally{i(!1)}},[C]),P=s.useCallback(()=>{N.current&&clearInterval(N.current),N.current=setInterval(()=>{v()},Je)},[v]),j=s.useCallback(()=>{N.current&&(clearInterval(N.current),N.current=null)},[]);s.useEffect(()=>(v(),P(),()=>{j(),p.current&&p.current.abort()}),[v,P,j]);const n=s.useCallback(m=>{c.current.has(m)||(c.current.add(m),C(m,!y.current))},[C]),x=s.useCallback(m=>{c.current.delete(m),l(r=>{const b={...r};return delete b[m],b}),c.current.size===0&&j()},[j]),g=s.useCallback(async m=>{h(r=>({...r,[m]:!0})),f(null);try{const r=await Pe(m);return C(m,!1),!r.success&&r.error&&f(r.error),r}catch(r){const b=r instanceof Error?r.message:"Push settings failed";throw f(b),r}finally{h(r=>{const b={...r};return delete b[m],b})}},[C]),M=s.useCallback(async m=>{h(r=>({...r,[m]:!0})),f(null);try{const r=await Me(m);return C(m,!1),!r.success&&r.error&&f(r.error),r}catch(r){const b=r instanceof Error?r.message:"Pull settings failed";throw f(b),r}finally{h(r=>{const b={...r};return delete b[m],b})}},[C]),R=s.useCallback(async m=>{h(r=>({...r,[m]:!0})),f(null);try{return await Ee(m)}catch(r){const b=r instanceof Error?r.message:"Auth sync failed";throw f(b),r}finally{h(r=>{const b={...r};return delete b[m],b})}},[]),E=s.useCallback(m=>t[m]?.authMatch,[t]),D=s.useCallback(m=>t[m]?.authDiff,[t]);return{syncStatusMap:t,loading:a,actionLoading:u,error:o,refresh:v,trackNode:n,untrackNode:x,pushSettings:g,pullSettings:M,syncAuth:R,getAuthSyncState:E,getAuthProviders:D}}function Ge(t,l){return l.type==="remote"?t.nodeId===l.id:t.nodeId===l.id||t.nodeId===void 0||t.nodeId===null}function Ce(t,l){return t.filter(a=>Ge(a,l))}function me(t,l){return Ce(t,l).length}const We={online:{label:"Online",color:"var(--color-success)",className:"node-card__status--online"},offline:{label:"Offline",color:"var(--color-error)",className:"node-card__status--offline"},connecting:{label:"Connecting",color:"var(--color-warning)",className:"node-card__status--connecting"},error:{label:"Error",color:"var(--color-error)",className:"node-card__status--error"}},Ye={match:"var(--color-success)",differs:"var(--color-warning)","not-synced":"var(--text-muted)"};function Qe(t,l){if(t==="match")return"Auth credentials match";if(t==="not-synced")return"Auth not synced";if(l&&Object.keys(l).length>0){const a=Object.entries(l).filter(([,i])=>i==="differs").map(([i])=>i);if(a.length>0)return`Auth credentials differ: ${a.join(", ")}`}return"Auth credentials differ"}function Ze(t,l=42){return t.length<=l?t:`${t.slice(0,l-3)}...`}function es(t,l){const a=t.node,i=l.node;if(a.id!==i.id||a.name!==i.name||a.type!==i.type||a.url!==i.url||a.status!==i.status||a.maxConcurrent!==i.maxConcurrent||a.updatedAt!==i.updatedAt||t.isLoading!==l.isLoading)return!1;const u=t.syncStatus,h=l.syncStatus;if(!(!u&&!h)){if(!u||!h)return!1;if(u.syncState!==h.syncState||u.lastSyncAt!==h.lastSyncAt||u.diffCount!==h.diffCount)return!1}if(t.authSyncState!==l.authSyncState)return!1;const o=t.authSyncProviders,f=l.authSyncProviders;if(o!==f){if(!o||!f)return!1;{const p=Object.keys(o),N=Object.keys(f);if(p.length!==N.length||p.some(C=>o[C]!==f[C]))return!1}}const c=me(t.projects,a),y=me(l.projects,i);return c===y}function ss({node:t,projects:l,onHealthCheck:a,onEdit:i,onRemove:u,isLoading:h=!1,syncStatus:o,authSyncState:f,authSyncProviders:c}){const[y,p]=s.useState(!1),N=We[t.status],C=s.useMemo(()=>me(l,t),[l,t]),v=s.useCallback(()=>{i(t)},[i,t]),P=s.useCallback(g=>{g.stopPropagation(),a(t.id)},[a,t.id]),j=s.useCallback(g=>{g.stopPropagation(),i(t)},[i,t]),n=s.useCallback(g=>{if(g.stopPropagation(),!y){p(!0);return}u(t.id),p(!1)},[y,u,t.id]),x=s.useCallback(g=>{(g.key==="Enter"||g.key===" ")&&(g.preventDefault(),i(t))},[i,t]);return e.jsxs("article",{className:`node-card ${h?"node-card--loading":""}`,"data-node-id":t.id,role:"button",tabIndex:0,onClick:v,onKeyDown:x,children:[e.jsx("header",{className:"node-card__header",children:e.jsxs("div",{className:"node-card__title-wrap",children:[e.jsx("div",{className:"node-card__icon",children:e.jsx(ye,{size:18})}),e.jsxs("div",{children:[e.jsx("h3",{className:"node-card__name",title:t.name,children:t.name}),e.jsxs("div",{className:"node-card__meta-row",children:[e.jsx("span",{className:"node-card__type-badge",children:t.type==="local"?"Local":"Remote"}),e.jsxs("span",{className:`node-card__status ${N.className}`,style:{color:N.color},"data-status":t.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:N.color},"aria-hidden":!0}),N.label]}),t.type==="remote"&&f&&e.jsx("span",{className:`node-card__auth-indicator node-card__auth-indicator--${f}`,title:Qe(f,c),"aria-label":`Auth sync: ${f==="match"?"credentials match":f==="differs"?"credentials differ":"not synced"}`,style:{color:Ye[f]},children:e.jsx(be,{size:14})})]})]})]})}),e.jsxs("div",{className:"node-card__body",children:[t.type==="remote"&&t.url&&e.jsx("div",{className:"node-card__url",title:t.url,children:Ze(t.url)}),e.jsxs("div",{className:"node-card__metrics",children:[e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Projects"}),e.jsx("span",{className:"node-card__metric-value",children:C})]}),e.jsxs("div",{className:"node-card__metric",children:[e.jsx("span",{className:"node-card__metric-label",children:"Concurrency"}),e.jsx("span",{className:"node-card__metric-value",children:t.maxConcurrent})]})]}),t.type==="remote"&&o&&e.jsxs("div",{className:"node-card__sync","data-sync-state":o.syncState,"data-testid":"node-card-sync",children:[e.jsx("span",{className:"node-card__sync-dot",style:{backgroundColor:qe(o.syncState)},"aria-hidden":!0}),e.jsx("span",{className:"node-card__sync-time",children:ke(o.lastSyncAt)})]})]}),e.jsxs("footer",{className:"node-card__actions",children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:P,disabled:h,"aria-label":"Run node health check",title:"Health Check",children:[e.jsx(je,{size:14}),e.jsx("span",{children:"Health"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Edit node",title:"Edit",children:[e.jsx(Re,{size:14}),e.jsx("span",{children:"Edit"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Start node container",title:"Start Container",children:[e.jsx(Ae,{size:14}),e.jsx("span",{children:"Start"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Stop node container",title:"Stop Container",children:[e.jsx(De,{size:14}),e.jsx("span",{children:"Stop"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:h,"aria-label":"Restart node container",title:"Restart Container",children:[e.jsx(Le,{size:14}),e.jsx("span",{children:"Restart"})]}),e.jsxs("button",{className:`btn btn-sm node-card__action node-card__action--remove ${y?"btn-danger is-armed":""}`,type:"button",onClick:n,disabled:h,"aria-label":y?"Confirm remove node":"Remove node",title:y?"Confirm remove":"Remove",children:[e.jsx(ie,{size:14}),e.jsx("span",{children:y?"Confirm":"Remove"})]})]})]})}const ts=s.memo(ss,es),W={online:"var(--success, var(--color-success))",offline:"var(--text-dim)",connecting:"var(--triage)",error:"var(--color-error)"},G=28,ge=12,as=300,ns=120;function ls({nodes:t,className:l}){const a=s.useMemo(()=>t.find(c=>c.type==="local")??t[0],[t]),i=s.useMemo(()=>t.filter(c=>c.type==="remote"),[t]),u=s.useMemo(()=>{const c=as,y=Math.max(0,i.length-4)*20;return c+y},[i.length]),h=u/2,o=u/2,f=s.useMemo(()=>{if(i.length===0)return[];const c=Math.min(ns,u/2-G-10),y=2*Math.PI/i.length,p=-Math.PI/2;return i.map((N,C)=>{const v=p+C*y;return{node:N,x:h+c*Math.cos(v),y:o+c*Math.sin(v)}})},[i,u,h,o]);return t.length===0?e.jsx("div",{className:`mesh-topology mesh-topology--empty ${l??""}`,children:e.jsx("div",{className:"mesh-topology__empty-state",children:e.jsx("p",{children:"No nodes to display"})})}):e.jsxs("div",{className:`mesh-topology ${l??""}`,children:[e.jsxs("svg",{className:"mesh-topology__svg",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet","aria-label":"Node mesh topology visualization",children:[f.map(c=>e.jsx("line",{className:"mesh-topology__link",x1:h,y1:o,x2:c.x,y2:c.y},`link-${c.node.id}`)),a&&e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${h}, ${o})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:G,fill:W[a.status],"aria-label":`${a.name} (${a.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:G+ge,textAnchor:"middle",children:a.name.length>12?`${a.name.slice(0,10)}…`:a.name}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-G-10})`,children:[e.jsx("circle",{className:"mesh-topology__node-type-badge",r:"8"}),e.jsx("text",{className:"mesh-topology__node-type-text",textAnchor:"middle",dominantBaseline:"middle",children:a.type==="local"?"L":"R"})]})]}),f.map(c=>e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${c.x}, ${c.y})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:G,fill:W[c.node.status],"aria-label":`${c.node.name} (${c.node.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:G+ge,textAnchor:"middle",children:c.node.name.length>12?`${c.node.name.slice(0,10)}…`:c.node.name}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-G-10})`,children:[e.jsx("circle",{className:"mesh-topology__node-type-badge",r:"8"}),e.jsx("text",{className:"mesh-topology__node-type-text",textAnchor:"middle",dominantBaseline:"middle",children:c.node.type==="local"?"L":"R"})]})]},c.node.id))]}),e.jsxs("div",{className:"mesh-topology__legend",children:[e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:W.online}}),e.jsx("span",{children:"Online"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:W.offline}}),e.jsx("span",{children:"Offline"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:W.connecting}}),e.jsx("span",{children:"Connecting"})]}),e.jsxs("div",{className:"mesh-topology__legend-item",children:[e.jsx("span",{className:"mesh-topology__legend-dot",style:{background:W.error}}),e.jsx("span",{children:"Error"})]})]}),e.jsx("p",{className:"mesh-topology__notice",children:"Peer-to-peer discovery data unavailable."})]})}const rs=s.memo(ls),he=1,fe=10;function os(t){const l={};return t.name.trim()||(l.name="Name is required"),t.type==="remote"&&!t.url?.trim()&&(l.url="URL is required for remote nodes"),(!Number.isFinite(t.maxConcurrent)||t.maxConcurrent<he||t.maxConcurrent>fe)&&(l.maxConcurrent=`Concurrency must be between ${he} and ${fe}`),l}function cs({isOpen:t,onClose:l,onSubmit:a,addToast:i}){$e(t);const[u,h]=s.useState(""),[o,f]=s.useState("local"),[c,y]=s.useState(""),[p,N]=s.useState(""),[C,v]=s.useState(2),[P,j]=s.useState("auto-generate"),[n,x]=s.useState({}),[g,M]=s.useState(!1),R=s.useCallback(()=>{h(""),f("local"),y(""),N(""),v(2),j("auto-generate"),x({}),M(!1)},[]),E=s.useCallback(()=>{g||(R(),l())},[g,l,R]);s.useEffect(()=>{if(!t){R();return}const r=b=>{b.key==="Escape"&&(b.preventDefault(),E())};return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r)}},[E,t,R]);const D=s.useMemo(()=>({name:u.trim(),type:o,url:o==="remote"&&c.trim()||void 0,apiKey:o==="remote"&&P==="provide"&&p||void 0,maxConcurrent:C,apiKeyMode:P}),[p,P,C,u,o,c]),m=s.useCallback(async()=>{if(g)return;const r=os(D);if(x(r),!(Object.keys(r).length>0)){M(!0);try{await a(D),i(`Node "${D.name}" registered`,"success"),E()}catch(b){const z=b instanceof Error?b.message:"Failed to register node";i(z,"error")}finally{M(!1)}}},[i,E,D,g,a]);return t?e.jsx("div",{className:"modal-overlay open",onClick:E,children:e.jsxs("div",{className:"modal modal-md add-node-modal",onClick:r=>r.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Add Node",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Add Node"}),e.jsx("button",{className:"modal-close",onClick:E,disabled:g,"aria-label":"Close add node modal",children:"×"})]}),e.jsxs("div",{className:"modal-body add-node-modal__body",children:[e.jsx("p",{className:"add-node-modal__description",children:"Register an existing Fusion node by providing its connection details and concurrency settings."}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Name"}),e.jsx("input",{className:"input",type:"text",value:u,onChange:r=>h(r.target.value),placeholder:"Build Machine",disabled:g,"aria-invalid":!!n.name,autoFocus:!0}),n.name&&e.jsx("span",{className:"form-error add-node-modal__error",children:n.name})]}),e.jsxs("div",{className:"add-node-modal__type-toggle",children:[e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${o==="local"?"active":""}`,"data-type":"local",onClick:()=>f("local"),disabled:g,"aria-pressed":o==="local",children:"Local"}),e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${o==="remote"?"active":""}`,"data-type":"remote",onClick:()=>f("remote"),disabled:g,"aria-pressed":o==="remote",children:"Remote"})]}),o==="remote"&&e.jsxs("div",{className:"add-node-modal__remote-fields","data-testid":"remote-fields-container","data-visible":!0,children:[e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Reachable URL / Hostname"}),e.jsx("input",{className:"input",type:"text",value:c,onChange:r=>y(r.target.value),placeholder:"https://node.example.com",disabled:g,"aria-invalid":!!n.url}),n.url&&e.jsx("span",{className:"form-error add-node-modal__error",children:n.url})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key Mode"}),e.jsxs("select",{className:"select",value:P,onChange:r=>j(r.target.value),disabled:g,children:[e.jsx("option",{value:"auto-generate",children:"Auto-generate"}),e.jsx("option",{value:"provide",children:"Provide key manually"})]})]}),P==="provide"&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:p,onChange:r=>N(r.target.value),placeholder:"Enter node API key",disabled:g})]})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),e.jsx("input",{className:"input",type:"number",min:he,max:fe,value:C,onChange:r=>v(Number(r.target.value)),disabled:g,"aria-invalid":!!n.maxConcurrent}),e.jsx("span",{className:"add-node-modal__hint",children:"Max simultaneous task agents (1–10)"}),n.maxConcurrent&&e.jsx("span",{className:"form-error add-node-modal__error",children:n.maxConcurrent})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:E,disabled:g,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm","data-testid":"add-node-submit",onClick:m,disabled:g,children:g?"Adding...":"Add Node"})]})]})}):null}function is(){const[t,l]=s.useState([]),[a,i]=s.useState(!1),[u,h]=s.useState(null),[o,f]=s.useState(!1),[c,y]=s.useState(null),[p,N]=s.useState(!1),C=s.useCallback(async()=>{i(!0),h(null);try{const j=await fetch("/api/docker/contexts");if(!j.ok)throw new Error(`Failed to load Docker contexts (${j.status})`);const n=await j.json();return l(n),n}catch(j){const n=j instanceof Error?j.message:String(j);throw h(n),j}finally{i(!1)}},[]),v=s.useCallback(async j=>{f(!0);try{const n=await fetch("/api/docker/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hostConfig:j})});if(!n.ok)throw new Error(`Failed to test Docker connection (${n.status})`);const x=await n.json();return y(x),x}finally{f(!1)}},[]),P=s.useCallback(async()=>{N(!0);try{const j=await fetch("/api/docker/local-available");if(!j.ok)throw new Error(`Failed to check local Docker availability (${j.status})`);return await j.json()}finally{N(!1)}},[]);return{contexts:t,isLoadingContexts:a,contextsError:u,loadContexts:C,isTestingConnection:o,lastTestResult:c,testConnection:v,isCheckingLocal:p,checkLocalDocker:P}}function ds({value:t,onChange:l}){const[a,i]=s.useState(!!(t?.tlsCaPath||t?.tlsCertPath||t?.tlsKeyPath||t?.tlsVerify));s.useEffect(()=>{a||l({tlsVerify:void 0,tlsCaPath:void 0,tlsCertPath:void 0,tlsKeyPath:void 0})},[a,l]);const u=s.useMemo(()=>({tlsVerify:t?.tlsVerify??!0,tlsCaPath:t?.tlsCaPath??"",tlsCertPath:t?.tlsCertPath??"",tlsKeyPath:t?.tlsKeyPath??""}),[t]);return e.jsxs("div",{className:"docker-tls-config",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:a,onChange:h=>i(h.target.checked)}),"Use TLS"]}),a&&e.jsxs("div",{className:"docker-tls-config__fields",children:[e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-ca-path",children:"CA Certificate Path"}),e.jsx("input",{id:"docker-tls-ca-path",className:"input",value:u.tlsCaPath,onChange:h=>l({...u,tlsCaPath:h.target.value}),placeholder:"/etc/docker/ca.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-cert-path",children:"Client Certificate Path"}),e.jsx("input",{id:"docker-tls-cert-path",className:"input",value:u.tlsCertPath,onChange:h=>l({...u,tlsCertPath:h.target.value}),placeholder:"/etc/docker/cert.pem"})]}),e.jsxs("div",{className:"docker-tls-config__field",children:[e.jsx("label",{htmlFor:"docker-tls-key-path",children:"Client Key Path"}),e.jsx("input",{id:"docker-tls-key-path",className:"input",value:u.tlsKeyPath,onChange:h=>l({...u,tlsKeyPath:h.target.value}),placeholder:"/etc/docker/key.pem"})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:u.tlsVerify,onChange:h=>l({...u,tlsVerify:h.target.checked})}),"Verify TLS Certificate"]})]})]})}function us({value:t,onChange:l,onError:a}){const i=t?.context?"context":t?.host?"host":"local",[u,h]=s.useState(i),[o,f]=s.useState(t?.context??""),[c,y]=s.useState(t?.host??""),[p,N]=s.useState(null),{contexts:C,isLoadingContexts:v,contextsError:P,loadContexts:j,testConnection:n,isTestingConnection:x,lastTestResult:g,checkLocalDocker:M,isCheckingLocal:R}=is(),E=s.useMemo(()=>({tlsVerify:t?.tlsVerify,tlsCaPath:t?.tlsCaPath,tlsCertPath:t?.tlsCertPath,tlsKeyPath:t?.tlsKeyPath}),[t]);s.useEffect(()=>{if(u==="local"){l({});return}if(u==="context"){j().catch(m=>a?.(m instanceof Error?m.message:String(m))),l(o?{context:o}:{});return}l({host:c,...E})},[u]);const D=s.useCallback(m=>{u==="host"&&l({host:c,...m})},[c,u,l]);return e.jsxs("div",{className:"docker-target-selector",children:[e.jsxs("div",{className:"docker-target-selector__modes",role:"group","aria-label":"Docker target mode",children:[e.jsx("button",{type:"button",className:`btn btn-sm ${u==="local"?"docker-target-selector__mode-active":""}`,onClick:()=>{h("local"),M().then(m=>N(m.available?`Docker is available${m.version?` (${m.version})`:""}`:`Docker not found${m.error?`: ${m.error}`:""}`)).catch(m=>{const r=m instanceof Error?m.message:String(m);N(`Docker not found: ${r}`),a?.(r)})},children:"Local Docker"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="context"?"docker-target-selector__mode-active":""}`,onClick:()=>h("context"),children:"Docker Context"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="host"?"docker-target-selector__mode-active":""}`,onClick:()=>h("host"),children:"Remote Host"})]}),u==="local"&&p&&e.jsx("div",{className:"docker-target-selector__status",children:p}),u==="context"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__context-row",children:[e.jsxs("select",{className:"select",value:o,onChange:m=>{const r=m.target.value;f(r),l(r?{context:r}:{})},children:[e.jsx("option",{value:"",children:"Select context"}),C.map(m=>e.jsxs("option",{value:m.name,children:[m.name,m.isCurrentContext?" (current)":"",m.dockerHost?` — ${m.dockerHost}`:""]},m.name))]}),e.jsx("button",{type:"button",className:"btn btn-sm btn-icon",onClick:()=>void j(),disabled:v,"aria-label":"Refresh contexts",children:e.jsx(de,{size:14})})]}),P&&e.jsx("div",{className:"docker-target-selector__error",children:P})]}),u==="host"&&e.jsxs("div",{className:"docker-target-selector__panel",children:[e.jsxs("div",{className:"docker-target-selector__field",children:[e.jsx("label",{htmlFor:"docker-target-selector-host",children:"Docker Host"}),e.jsx("input",{id:"docker-target-selector-host",className:"input",placeholder:"tcp://host:2376",value:c,onChange:m=>{const r=m.target.value;y(r),l({host:r,...E})}})]}),e.jsx(ds,{value:E,onChange:D})]}),e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void n(u==="local"?void 0:u==="context"?{context:o}:{host:c,...E}),disabled:x||R,children:x?"Testing...":"Test Connection"}),g&&e.jsx("div",{className:g.success?"docker-target-selector__success":"docker-target-selector__error",children:g.success?`Connected${g.dockerVersion?` (Docker ${g.dockerVersion})`:""}`:g.error??"Connection failed"})]})}const oe="http://localhost:4040";function ms({isOpen:t,onClose:l,onSubmit:a,addToast:i}){const[u,h]=s.useState(""),[o,f]=s.useState({}),[c,y]=s.useState(oe),[p,N]=s.useState("auto"),[C,v]=s.useState(""),[P,j]=s.useState(!1),[n,x]=s.useState(!1),[g,M]=s.useState(!0),[R,E]=s.useState(4096),[D,m]=s.useState(2),[r,b]=s.useState(!1),[z,F]=s.useState("runfusion/fusion"),[H,q]=s.useState("latest"),[O,V]=s.useState([]),[T,k]=s.useState([]),[S,K]=s.useState({}),[w,J]=s.useState(!1),L=s.useCallback(()=>{h(""),f({}),y(oe),N("auto"),v(""),j(!1),x(!1),M(!0),E(4096),m(2),b(!1),F("runfusion/fusion"),q("latest"),V([]),k([]),K({}),J(!1)},[]),U=s.useCallback(()=>{w||(L(),l())},[l,L,w]);s.useEffect(()=>{if(!t){L();return}const d=A=>{A.key==="Escape"&&(A.preventDefault(),U())};return document.addEventListener("keydown",d),()=>document.removeEventListener("keydown",d)},[U,t,L]);const X=s.useMemo(()=>({nodeId:null,name:u.trim(),imageName:z.trim()||"runfusion/fusion",imageTag:H.trim()||"latest",hostConfig:{context:o.context?.trim()||void 0,host:o.host?.trim()||void 0,tlsVerify:o.tlsVerify,tlsCaPath:o.tlsCaPath?.trim()||void 0,tlsCertPath:o.tlsCertPath?.trim()||void 0,tlsKeyPath:o.tlsKeyPath?.trim()||void 0},envVars:Object.fromEntries(O.map(d=>[d.key.trim(),d.value]).filter(([d])=>!!d)),volumeMounts:T.map(d=>({hostPath:d.hostPath.trim(),containerPath:d.containerPath.trim(),mode:d.mode})).filter(d=>d.hostPath&&d.containerPath),resourceSizing:{memoryMB:R,cpus:D},extraClis:[P?"claude-cli":null,n?"droid-cli":null].filter(Boolean),persistentStorage:g,reachableUrl:c.trim()||null,apiKey:p==="manual"&&C.trim()||null}),[C,p,D,o,O,z,H,P,n,R,T,u,g,c]),te=s.useCallback(()=>{V(d=>[...d,{key:"",value:""}])},[]),se=s.useCallback((d,A)=>{V($=>$.map((B,Z)=>Z===d?A:B))},[]),Y=s.useCallback(d=>{V(A=>A.filter(($,B)=>B!==d))},[]),ae=s.useCallback(()=>{k(d=>[...d,{hostPath:"",containerPath:"",mode:"rw"}])},[]),Q=s.useCallback((d,A)=>{k($=>$.map((B,Z)=>Z===d?A:B))},[]),ne=s.useCallback(d=>{k(A=>A.filter(($,B)=>B!==d))},[]),le=s.useCallback(async()=>{if(w)return;const d={};if((!X.name||X.name.length>64)&&(d.name="Name is required and must be 64 characters or fewer"),X.reachableUrl||(d.reachableUrl="URL is required"),R<512&&(d.memoryMB="Memory must be at least 512 MB"),D<.5&&(d.cpus="CPUs must be at least 0.5"),K(d),!(Object.keys(d).length>0)){J(!0);try{await a(X),U()}catch{}finally{J(!1)}}},[U,D,X,R,a,w]);return t?e.jsx("div",{className:"modal-overlay open",onClick:U,children:e.jsxs("div",{className:"modal docker-onboarding",role:"dialog","aria-modal":"true","aria-label":"Docker node onboarding",onClick:d=>d.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Provision Docker Node"}),e.jsx("button",{className:"modal-close",onClick:U,disabled:w,"aria-label":"Close onboarding modal",children:"×"})]}),e.jsxs("div",{className:"modal-body docker-onboarding__body",children:[e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsx("h4",{className:"docker-onboarding__section-title",children:"Required Settings"}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Node Name"}),e.jsx("input",{className:"input",value:u,onChange:d=>h(d.target.value),disabled:w,placeholder:"my-docker-node",autoFocus:!0})]}),S.name&&e.jsx("div",{className:"form-error",children:S.name}),e.jsx(us,{value:o,onChange:f}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Reachable URL"}),e.jsx("input",{className:"input",value:c,onChange:d=>y(d.target.value),disabled:w,placeholder:oe})]}),S.reachableUrl&&e.jsx("div",{className:"form-error",children:S.reachableUrl}),e.jsxs("div",{className:"docker-onboarding__radio-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:p==="auto",onChange:()=>N("auto"),disabled:w}),"Auto-generate"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:p==="manual",onChange:()=>N("manual"),disabled:w}),"Provide manually"]})]}),p==="manual"&&e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:C,onChange:d=>v(d.target.value),disabled:w,placeholder:"Enter API key"})]}),e.jsxs("div",{className:"docker-onboarding__checkbox-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:P,onChange:d=>j(d.target.checked),disabled:w}),"Claude CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:n,onChange:d=>x(d.target.checked),disabled:w}),"Droid CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:g,onChange:d=>M(d.target.checked),disabled:w}),"Keep data across container recreations"]})]}),e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Memory (MB)"}),e.jsx("input",{className:"input",type:"number",min:512,value:R,onChange:d=>E(Number(d.target.value)),disabled:w})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"CPUs"}),e.jsx("input",{className:"input",type:"number",min:.5,step:.5,value:D,onChange:d=>m(Number(d.target.value)),disabled:w})]})]}),S.memoryMB&&e.jsx("div",{className:"form-error",children:S.memoryMB}),S.cpus&&e.jsx("div",{className:"form-error",children:S.cpus})]}),e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsxs("button",{type:"button",className:`docker-onboarding__advanced-toggle ${r?"is-expanded":""}`,onClick:()=>b(d=>!d),disabled:w,children:[e.jsx("span",{children:"Advanced"}),e.jsx(Ne,{})]}),e.jsx("div",{className:`docker-onboarding__advanced-content ${r?"is-expanded":""}`,children:e.jsxs("div",{children:[e.jsxs("div",{className:"docker-onboarding__inline-fields",children:[e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:z,onChange:d=>F(d.target.value),disabled:w,placeholder:"runfusion/fusion"})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Tag"}),e.jsx("input",{className:"input",value:H,onChange:d=>q(d.target.value),disabled:w,placeholder:"latest"})]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Environment Variables"}),O.map((d,A)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--env",children:[e.jsx("input",{className:"input",placeholder:"KEY",value:d.key,disabled:w,onChange:$=>se(A,{key:$.target.value,value:d.value})}),e.jsx("input",{className:"input",placeholder:"Value",value:d.value,disabled:w,onChange:$=>se(A,{key:d.key,value:$.target.value})}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove environment variable",onClick:()=>Y(A),disabled:w,children:e.jsx(ie,{size:14})})]},`env-${A}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:te,disabled:w,children:[e.jsx(ee,{size:14}),"Add variable"]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Volume Mounts"}),T.map((d,A)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--mount",children:[e.jsx("input",{className:"input",placeholder:"Host path",value:d.hostPath,disabled:w,onChange:$=>Q(A,{hostPath:$.target.value,containerPath:d.containerPath,mode:d.mode})}),e.jsx("input",{className:"input",placeholder:"Container path",value:d.containerPath,disabled:w,onChange:$=>Q(A,{hostPath:d.hostPath,containerPath:$.target.value,mode:d.mode})}),e.jsxs("select",{className:"select",value:d.mode,disabled:w,onChange:$=>Q(A,{hostPath:d.hostPath,containerPath:d.containerPath,mode:$.target.value==="ro"?"ro":"rw"}),children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove volume mount",onClick:()=>ne(A),disabled:w,children:e.jsx(ie,{size:14})})]},`mount-${A}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:ae,disabled:w,children:[e.jsx(ee,{size:14}),"Add mount"]})]})]})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",onClick:U,disabled:w,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",onClick:()=>void le(),disabled:w,children:w?"Creating...":"Create Docker Node"})]})]})}):null}function hs({nodeId:t,entries:l,loading:a=!1,singleNode:i=!1}){const[u,h]=s.useState(!1),[o,f]=s.useState("all"),[c,y]=s.useState("all"),p=s.useCallback(()=>{h(n=>!n)},[]),N=s.useMemo(()=>{const n=new Set;for(const x of l)n.add(x.nodeName);return Array.from(n).sort()},[l]),C=s.useMemo(()=>{let n=[...l];return o!=="all"&&(n=n.filter(x=>x.direction===o)),!i&&c!=="all"&&(n=n.filter(x=>x.nodeName===c)),n.sort((x,g)=>{const M=new Date(x.timestamp).getTime();return new Date(g.timestamp).getTime()-M}),n},[l,o,c,i]),v=s.useCallback(n=>new Date(n).toLocaleString(),[]),P=s.useCallback(n=>{switch(n){case"success":return"settings-sync-log__badge--success";case"conflict":return"settings-sync-log__badge--conflict";case"error":return"settings-sync-log__badge--error";default:return""}},[]),j=s.useCallback(n=>{switch(n){case"success":return"Success";case"conflict":return"Conflict";case"error":return"Error";default:return n}},[]);return e.jsxs("div",{className:"settings-sync-log",children:[e.jsxs("button",{className:"settings-sync-log__header",type:"button",onClick:p,"aria-expanded":u,"data-testid":"settings-sync-log-header",children:[e.jsx(Ne,{size:16,className:`settings-sync-log__chevron ${u?"settings-sync-log__chevron--expanded":""}`}),e.jsxs("span",{children:[l.length," ",l.length===1?"entry":"entries"]})]}),u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"settings-sync-log__filters",children:[e.jsxs("label",{children:["Direction:",e.jsxs("select",{value:o,onChange:n=>f(n.target.value),children:[e.jsx("option",{value:"all",children:"All"}),e.jsx("option",{value:"push",children:"Push"}),e.jsx("option",{value:"pull",children:"Pull"})]})]}),!i&&e.jsxs("label",{children:["Node:",e.jsxs("select",{value:c,onChange:n=>y(n.target.value),children:[e.jsx("option",{value:"all",children:"All Nodes"}),N.map(n=>e.jsx("option",{value:n,children:n},n))]})]})]}),a&&l.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"Loading..."}):C.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"No sync history available"}):e.jsx("div",{className:"settings-sync-log__list",children:C.map(n=>e.jsxs("div",{className:"settings-sync-log__entry",children:[e.jsx("span",{className:"settings-sync-log__entry-timestamp",children:v(n.timestamp)}),e.jsx("span",{className:"settings-sync-log__entry-direction",children:n.direction==="push"?e.jsx(ve,{size:14,"data-testid":"upload-icon"}):e.jsx(_e,{size:14,"data-testid":"download-icon"})}),e.jsx("span",{className:`settings-sync-log__entry-result ${P(n.result)}`,children:j(n.result)}),!i&&e.jsx("span",{className:"settings-sync-log__entry-node",children:n.nodeName}),n.details&&e.jsx("span",{className:"settings-sync-log__entry-details",title:n.details,children:n.details})]},n.id))})]})]})}function fs(t,l){const a=typeof t=="string"?t:JSON.stringify(t,null,2),i=typeof l=="string"?l:JSON.stringify(l,null,2);if(a===i)return a;const u=a.split(`
12
12
  `),h=i.split(`
13
13
  `),o=[],f=Math.max(u.length,h.length);for(let c=0;c<f;c++){const y=u[c],p=h[c];y!==void 0&&y!==p&&o.push(`- ${y}`),p!==void 0&&p!==y&&o.push(`+ ${p}`),y!==void 0&&y===p&&o.push(` ${y}`)}return o.join(`
14
- `)}function fs({isOpen:t,onClose:l,onResolve:a,conflicts:i,localNodeName:u,remoteNodeName:h,addToast:o}){const[f,c]=s.useState({}),[y,p]=s.useState(!1);s.useEffect(()=>{const n={};for(const x of i)f[x.key]||(n[x.key]={resolution:"local"});Object.keys(n).length>0&&c(x=>({...x,...n}))},[i,f]),s.useEffect(()=>{if(!t)return;const n=x=>{x.key==="Escape"&&(x.preventDefault(),l())};return document.addEventListener("keydown",n),()=>document.removeEventListener("keydown",n)},[t,l]);const N=s.useCallback((n,x)=>{c(g=>{const M=g[n]??{};return x==="manual"?{...g,[n]:{resolution:"manual",manualValue:M.manualValue??JSON.stringify(i.find(R=>R.key===n)?.localValue??null,null,2)}}:{...g,[n]:{resolution:x}}})},[i]),C=s.useCallback((n,x)=>{c(g=>({...g,[n]:{...g[n],resolution:"manual",manualValue:x}}))},[]),v=s.useCallback(n=>{const x={};for(const g of i)x[g.key]={resolution:n};c(x)},[i]),P=s.useCallback(async()=>{p(!0);try{const n=i.map(x=>{const g=f[x.key]??{resolution:"local"};let M;switch(g.resolution){case"remote":M=x.remoteValue;break;case"manual":try{M=JSON.parse(g.manualValue??"null")}catch{M=g.manualValue??null}break;case"local":default:M=x.localValue;break}return{key:x.key,value:M}});await a(n),o("Settings conflicts resolved successfully","success"),l()}catch(n){const x=n instanceof Error?n.message:"Failed to resolve conflicts";o(x,"error")}finally{p(!1)}},[o,i,l,a,f]),j=s.useMemo(()=>{const n={};for(const x of i)n[x.key]=hs(x.localValue,x.remoteValue);return n},[i]);return!t||i.length===0?null:e.jsx("div",{className:"modal-overlay open",onClick:l,children:e.jsxs("div",{className:"modal modal-lg settings-sync-conflict-modal",onClick:n=>n.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Resolve Settings Conflicts",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Resolve Settings Conflicts"}),e.jsx("button",{className:"modal-close",onClick:l,"aria-label":"Close conflict modal",children:"×"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsx("div",{className:"settings-sync-conflict-modal__conflict-list",children:i.map(n=>{const x=f[n.key]??{resolution:"local"},g=j[n.key];return e.jsxs("div",{className:"settings-sync-conflict-modal__conflict-item",children:[e.jsx("div",{className:"settings-sync-conflict-modal__key",children:n.key}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-panel",children:[e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:u}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:g})})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:h}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:g})})]})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__resolution",children:[e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${n.key}`,checked:x.resolution==="local",onChange:()=>N(n.key,"local")}),"Keep Local"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${n.key}`,checked:x.resolution==="remote",onChange:()=>N(n.key,"remote")}),"Keep Remote"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${n.key}`,checked:x.resolution==="manual",onChange:()=>N(n.key,"manual")}),"Merge Manually"]})]}),x.resolution==="manual"&&e.jsx("textarea",{className:"settings-sync-conflict-modal__manual-input",value:x.manualValue??"",onChange:M=>C(n.key,M.target.value),placeholder:"Enter JSON value..."})]},n.key)})}),e.jsxs("div",{className:"settings-sync-conflict-modal__bulk-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>v("local"),type:"button",children:"Resolve All: Keep Local"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>v("remote"),type:"button",children:"Resolve All: Keep Remote"})]})]}),e.jsxs("div",{className:"modal-actions settings-sync-conflict-modal__footer",children:[e.jsx("button",{className:"btn btn-sm",onClick:l,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm",onClick:P,disabled:y,children:y?"Resolving...":"Confirm"})]})]})})}function ce(t){if(!t)return"—";const l=new Date(t);return Number.isNaN(l.getTime())?"—":l.toLocaleString()}function xs(t){switch(t){case"synced":return"node-detail-modal__sync-dot--synced";case"diff":return"node-detail-modal__sync-dot--diff";case"error":return"node-detail-modal__sync-dot--error";case"pending":return"node-detail-modal__sync-dot--pending";case"never-synced":default:return"node-detail-modal__sync-dot--never"}}function gs({isOpen:t,onClose:l,node:a,projects:i,onUpdate:u,onHealthCheck:h,addToast:o,syncStatus:f,onPushSettings:c,onPullSettings:y,onSyncAuth:p,syncHistory:N=[],onResolveConflicts:C}){const v=s.useRef(!0),[P,j]=s.useState(!1),[n,x]=s.useState(""),[g,M]=s.useState(""),[R,E]=s.useState(""),[D,m]=s.useState(2),[r,b]=s.useState(!1),[z,F]=s.useState(!1),[H,q]=s.useState(!1),[O,V]=s.useState(!1),[T,k]=s.useState(null),[S,K]=s.useState(!1),[w]=s.useState([]),[J,L]=s.useState("running"),[U,X]=s.useState("FUSION_LOG_LEVEL=info"),[te,se]=s.useState("/srv/fusion:/data:rw");s.useEffect(()=>(v.current=!0,()=>{v.current=!1}),[]),s.useEffect(()=>{if(!a||!t){j(!1);return}x(a.name),M(a.url??""),E(a.apiKey??""),m(a.maxConcurrent),j(!1)},[t,a]),s.useEffect(()=>{if(!t)return;const _=I=>{I.key==="Escape"&&(I.preventDefault(),l())};return document.addEventListener("keydown",_),()=>document.removeEventListener("keydown",_)},[t,l]);const W=s.useMemo(()=>a?Ce(i,a):[],[a,i]),ae=s.useMemo(()=>a?.capabilities?.includes("docker-managed")??!1,[a]),Q=s.useCallback(async()=>{if(a)try{if(await h(a.id),!v.current)return;o(`Health check completed for ${a.name}`,"success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Health check failed";o(I,"error")}},[o,a,h]),ne=s.useCallback(async()=>{if(!(!a||!c)){k(null),F(!0);try{if(await c(a.id),!v.current)return;o("Settings pushed successfully","success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Push settings failed";k(I),o(I,"error")}finally{v.current&&F(!1)}}},[o,a,c]),le=s.useCallback(async()=>{if(!(!a||!y)){k(null),q(!0);try{if(await y(a.id),!v.current)return;o("Settings pulled successfully","success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Pull settings failed";k(I),o(I,"error")}finally{v.current&&q(!1)}}},[o,a,y]),d=s.useCallback(async()=>{if(!(!a||!p)){k(null),V(!0);try{if(await p(a.id),!v.current)return;o("Auth credentials synced successfully","success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Auth sync failed";k(I),o(I,"error")}finally{v.current&&V(!1)}}},[o,a,p]),A=s.useCallback(()=>{k(null)},[]),$=s.useCallback(_=>{_==="start"&&L("running"),_==="stop"&&L("stopped"),_==="restart"&&L("running"),_==="recreate"&&L("recreating"),_==="upgrade"&&L("recreating"),o(`Docker action queued: ${_}`,"success")},[o]),B=s.useCallback(async()=>{if(!a||r)return;const _=n.trim();if(!_){o("Name is required","error");return}if(a.type==="remote"&&!g.trim()){o("URL is required for remote nodes","error");return}if(!Number.isFinite(D)||D<1){o("Concurrency must be at least 1","error");return}b(!0);try{await u(a.id,{name:_,url:a.type==="remote"&&g.trim()||void 0,apiKey:a.type==="remote"&&R||void 0,maxConcurrent:D}),o(`Updated ${_}`,"success"),j(!1)}catch(I){const Se=I instanceof Error?I.message:"Failed to update node";o(Se,"error")}finally{b(!1)}},[o,R,r,D,n,a,u,g]),Z=s.useCallback(()=>{a&&(x(a.name),M(a.url??""),E(a.apiKey??""),m(a.maxConcurrent),j(!1))},[a]);return!t||!a?null:e.jsxs("div",{className:"modal-overlay open",onClick:l,children:[e.jsxs("div",{className:"modal modal-lg node-detail-modal",onClick:_=>_.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":`Node details for ${a.name}`,children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Node Details"}),e.jsx("button",{className:"modal-close",onClick:l,"aria-label":"Close node detail modal",children:"×"})]}),e.jsxs("div",{className:"modal-body node-detail-modal__body",children:[e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("div",{className:"node-detail-modal__section-header",children:[e.jsx("h4",{children:"Overview"}),!P&&e.jsxs("button",{className:"btn btn-sm",onClick:()=>j(!0),children:[e.jsx($e,{size:14}),"Edit"]})]}),e.jsxs("div",{className:"node-detail-modal__grid",children:[e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Name"}),P?e.jsx("input",{className:"input",value:n,onChange:_=>x(_.target.value),disabled:r}):e.jsx("strong",{children:a.name})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Type"}),e.jsx("strong",{children:a.type==="local"?"Local":"Remote"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Status"}),e.jsx("strong",{children:a.status})]}),e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),P?e.jsx("input",{className:"input",type:"number",min:1,max:10,value:D,onChange:_=>m(Number(_.target.value)),disabled:r}):e.jsx("strong",{children:a.maxConcurrent})]}),a.type==="remote"&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"URL"}),P?e.jsx("input",{className:"input",value:g,onChange:_=>M(_.target.value),disabled:r}):e.jsx("strong",{children:a.url??"—"})]}),e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"API Key"}),P?e.jsx("input",{className:"input",type:"password",value:R,onChange:_=>E(_.target.value),placeholder:"Leave blank to keep unchanged",disabled:r}):e.jsx("strong",{children:a.apiKey?"••••••••":"Not configured"})]})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Created"}),e.jsx("strong",{children:ce(a.createdAt)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Updated"}),e.jsx("strong",{children:ce(a.updatedAt)})]})]}),P&&e.jsxs("div",{className:"node-detail-modal__edit-actions",children:[e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:B,disabled:r,children:[e.jsx(Ke,{size:14}),r?"Saving...":"Save"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Z,disabled:r,children:[e.jsx(ue,{size:14}),"Cancel"]})]})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("h4",{children:[a.type==="local"?"Projects":"Assigned Projects"," (",W.length,")"]}),W.length===0?e.jsx("p",{className:"node-detail-modal__empty",children:a.type==="local"?"No projects are running on this node.":"No projects are assigned to this node."}):e.jsx("ul",{className:"node-detail-modal__project-list",children:W.map(_=>e.jsxs("li",{className:"node-detail-modal__project-item",children:[e.jsx("span",{children:_.name}),e.jsx("code",{children:_.id})]},_.id))})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Health"}),e.jsxs("div",{className:"node-detail-modal__health-row",children:[e.jsxs("span",{children:["Status: ",e.jsx("strong",{children:a.status})]}),e.jsxs("span",{children:["Last check: ",e.jsx("strong",{children:ce(a.updatedAt)})]})]})]}),ae&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Docker Management"}),e.jsxs("div",{className:"node-detail-modal__health-row",children:[e.jsxs("span",{children:["Container: ",e.jsx("strong",{children:J})]}),e.jsxs("span",{children:["Image: ",e.jsx("strong",{children:"runfusion/fusion:latest"})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>$("start"),children:"Start"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("stop"),children:"Stop"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("restart"),children:"Restart"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("recreate"),children:"Recreate"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("upgrade"),children:"Upgrade Image"})]}),e.jsxs("div",{className:"node-detail-modal__docker-grid",children:[e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Environment Variables"}),e.jsx("textarea",{className:"input node-detail-modal__textarea",value:U,onChange:_=>X(_.target.value)})]}),e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Volume Mounts"}),e.jsx("textarea",{className:"input node-detail-modal__textarea",value:te,onChange:_=>se(_.target.value)})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>o("Container logs opened","success"),children:"View Logs"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>o("Config changes saved","success"),children:"Save Config"}),e.jsx("button",{className:"btn btn-danger btn-sm",onClick:()=>o("Delete flow opened (retain/remove volumes)","warning"),children:"Delete Node…"})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Settings Sync"}),f&&e.jsxs("div",{className:"node-detail-modal__sync-status",children:[e.jsx("span",{className:`node-detail-modal__sync-dot ${xs(f.syncState)}`,"aria-hidden":!0}),e.jsxs("span",{children:["Last sync:"," ",e.jsx("strong",{children:f.lastSyncAt?ke(f.lastSyncAt):"Never synced"})]}),f.diffCount>0&&e.jsxs("span",{className:"node-detail-modal__sync-diff",children:["Differences: ",e.jsx("strong",{children:f.diffCount})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:ne,disabled:z||!c,children:[e.jsx(ve,{size:14}),z?"Pushing...":"Push Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:le,disabled:H||!y,children:[e.jsx(_e,{size:14}),H?"Pulling...":"Pull Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:d,disabled:O||!p,children:[e.jsx(be,{size:14}),O?"Syncing...":"Sync Auth"]})]}),T&&e.jsxs("div",{className:"node-detail-modal__sync-error",children:[e.jsx("span",{children:T}),e.jsx("button",{className:"node-detail-modal__sync-error-dismiss",onClick:A,"aria-label":"Dismiss error",children:e.jsx(ue,{size:14})})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Sync History"}),e.jsx(ms,{nodeId:a.id,entries:N,singleNode:!0})]})]}),e.jsxs("div",{className:"modal-actions node-detail-modal__actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:Q,children:[e.jsx(je,{size:14}),"Health Check"]}),e.jsx("button",{className:"btn btn-sm",onClick:l,children:"Close"})]})]}),a.type==="remote"&&e.jsx(fs,{isOpen:S,onClose:()=>K(!1),onResolve:C??(async()=>{}),conflicts:w,localNodeName:"Local",remoteNodeName:a.name,addToast:o})]})}function ps(){const[t,l]=s.useState([]),[a,i]=s.useState(!0),[u,h]=s.useState(null),o=s.useCallback(async()=>{try{h(null);const c=await xe();l(c)}catch(c){h(c instanceof Error?c.message:"Failed to fetch managed Docker nodes")}},[]);s.useEffect(()=>{let c=!1;async function y(){i(!0);try{const p=await xe();c||(l(p),h(null))}catch(p){c||h(p instanceof Error?p.message:"Failed to fetch managed Docker nodes")}finally{c||i(!1)}}return y(),()=>{c=!0}},[]);const f=s.useCallback(async c=>{const y=await ze(c);return l(p=>[...p,y]),y},[]);return{dockerNodes:t,loading:a,error:u,refresh:o,create:f}}function _s({addToast:t,onClose:l}){const{nodes:a,loading:i,error:u,refresh:h,register:o,update:f,unregister:c,healthCheck:y}=Ie(),{projects:p}=Fe(),{syncStatusMap:N,pushSettings:C,pullSettings:v,syncAuth:P,trackNode:j,getAuthSyncState:n,getAuthProviders:x}=Je(),{dockerNodes:g,create:M}=ps(),[R,E]=s.useState(!1),[D,m]=s.useState(!1),[r,b]=s.useState(null);s.useEffect(()=>{const k=a.filter(S=>S.type==="remote");for(const S of k)j(S.id)},[a,j]),s.useEffect(()=>{if(!r)return;const k=a.find(S=>S.id===r.id)??null;b(k)},[a,r]);const z=s.useMemo(()=>{const k=a.length,S=a.filter(L=>L.status==="online").length,K=a.filter(L=>L.status==="offline"||L.status==="error").length,w=a.filter(L=>L.type==="remote").length,J=a.filter(L=>L.type==="remote"&&N[L.id]&&re(N[L.id]).syncState==="synced").length;return{total:k,online:S,offline:K,remote:w,synced:J}},[a,N]),F=s.useCallback(async k=>{await o(k)},[o]),H=s.useCallback(async k=>{try{await M(k),t(`Docker node "${k.name}" created`,"success"),m(!1)}catch(S){const K=S instanceof Error?S.message:"Failed to create Docker node";throw t(K,"error"),S}},[t,M]),q=s.useCallback(async()=>{try{await h()}catch{t("Failed to refresh nodes","error")}},[t,h]),O=s.useCallback(async k=>{try{await y(k),t("Node health check complete","success")}catch(S){const K=S instanceof Error?S.message:"Health check failed";t(K,"error")}},[t,y]),V=s.useCallback(async k=>{try{await c(k),t("Node removed","success"),r?.id===k&&b(null)}catch(S){const K=S instanceof Error?S.message:"Failed to remove node";t(K,"error")}},[t,r?.id,c]),T=s.useCallback(async(k,S)=>{await f(k,S)},[f]);return e.jsxs("div",{className:"nodes-view","data-testid":"nodes-view",children:[e.jsxs("div",{className:"nodes-view-header",children:[e.jsxs("div",{className:"nodes-view-title",children:[e.jsxs("h2",{children:[e.jsx(ye,{size:20}),"Nodes"]}),e.jsxs("span",{className:"nodes-view-count",children:[a.length," registered"]})]}),e.jsxs("div",{className:"nodes-view-actions",children:[e.jsx("button",{className:"btn-icon nodes-view-close",onClick:l,"aria-label":"Close nodes view",children:e.jsx(ue,{size:16})}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void q(),disabled:i,children:[e.jsx(de,{size:14,className:i?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>E(!0),children:[e.jsx(ee,{size:14}),"Add Node"]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>m(!0),children:[e.jsx(ee,{size:14}),"Provision Docker Node"]})]})]}),e.jsxs("div",{className:"nodes-view-stats",children:[e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-total",children:[e.jsx("span",{children:"Total"}),e.jsx("strong",{children:z.total})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--online","data-testid":"nodes-stat-online",children:[e.jsxs("span",{children:[e.jsx(He,{size:14})," Online"]}),e.jsx("strong",{children:z.online})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--offline","data-testid":"nodes-stat-offline",children:[e.jsxs("span",{children:[e.jsx(Ue,{size:14})," Offline"]}),e.jsx("strong",{children:z.offline})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-remote",children:[e.jsxs("span",{children:[e.jsx(Oe,{size:14})," Remote"]}),e.jsx("strong",{children:z.remote})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--synced","data-testid":"nodes-stat-synced",children:[e.jsxs("span",{children:[e.jsx(de,{size:14})," Synced"]}),e.jsx("strong",{children:z.synced})]})]}),u&&e.jsx("div",{className:"nodes-view-error",children:u}),e.jsxs("section",{className:"nodes-view-topology","aria-label":"Docker Nodes Summary",children:[e.jsx("h3",{className:"nodes-view-section-title",children:"Docker Nodes"}),e.jsxs("div",{className:"nodes-view-stat",children:[e.jsx("span",{children:"Managed Docker Nodes"}),e.jsx("strong",{children:g.length}),e.jsx("button",{className:"btn btn-sm",onClick:()=>m(!0),children:"Provision"})]})]}),!i&&a.length>0&&e.jsxs("section",{className:"nodes-view-topology","aria-label":"Mesh Topology",children:[e.jsx("h3",{className:"nodes-view-section-title",children:"Mesh Topology"}),e.jsx(ls,{nodes:a})]}),i?e.jsx("div",{className:"nodes-view-grid",children:Array.from({length:4}).map((k,S)=>e.jsx("div",{className:"node-card node-card--loading","aria-hidden":!0},S))}):a.length===0?e.jsxs("div",{className:"nodes-view-empty",children:[e.jsx("p",{children:"No nodes are registered yet."}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>E(!0),children:[e.jsx(ee,{size:14}),"Add First Node"]})]}):e.jsx("div",{className:"nodes-view-grid",children:a.map(k=>{const S=k.type==="remote"&&N[k.id]?re(N[k.id]):void 0;return e.jsx(ss,{node:k,projects:p,onHealthCheck:K=>{O(K)},onEdit:K=>b(K),onRemove:K=>{V(K)},isLoading:i,syncStatus:S,authSyncState:k.type==="remote"?n(k.id):void 0,authSyncProviders:k.type==="remote"?x(k.id):void 0},k.id)})}),e.jsx(os,{isOpen:R,onClose:()=>E(!1),onSubmit:F,addToast:t}),e.jsx(us,{isOpen:D,onClose:()=>m(!1),onSubmit:H,addToast:t}),e.jsx(gs,{isOpen:r!==null,onClose:()=>b(null),node:r,projects:p,onUpdate:T,onHealthCheck:O,addToast:t,syncStatus:r?.type==="remote"&&r&&N[r.id]?re(N[r.id]):void 0,onPushSettings:C,onPullSettings:v,onSyncAuth:P})]})}export{_s as NodesView};
14
+ `)}function xs({isOpen:t,onClose:l,onResolve:a,conflicts:i,localNodeName:u,remoteNodeName:h,addToast:o}){const[f,c]=s.useState({}),[y,p]=s.useState(!1);s.useEffect(()=>{const n={};for(const x of i)f[x.key]||(n[x.key]={resolution:"local"});Object.keys(n).length>0&&c(x=>({...x,...n}))},[i,f]),s.useEffect(()=>{if(!t)return;const n=x=>{x.key==="Escape"&&(x.preventDefault(),l())};return document.addEventListener("keydown",n),()=>document.removeEventListener("keydown",n)},[t,l]);const N=s.useCallback((n,x)=>{c(g=>{const M=g[n]??{};return x==="manual"?{...g,[n]:{resolution:"manual",manualValue:M.manualValue??JSON.stringify(i.find(R=>R.key===n)?.localValue??null,null,2)}}:{...g,[n]:{resolution:x}}})},[i]),C=s.useCallback((n,x)=>{c(g=>({...g,[n]:{...g[n],resolution:"manual",manualValue:x}}))},[]),v=s.useCallback(n=>{const x={};for(const g of i)x[g.key]={resolution:n};c(x)},[i]),P=s.useCallback(async()=>{p(!0);try{const n=i.map(x=>{const g=f[x.key]??{resolution:"local"};let M;switch(g.resolution){case"remote":M=x.remoteValue;break;case"manual":try{M=JSON.parse(g.manualValue??"null")}catch{M=g.manualValue??null}break;case"local":default:M=x.localValue;break}return{key:x.key,value:M}});await a(n),o("Settings conflicts resolved successfully","success"),l()}catch(n){const x=n instanceof Error?n.message:"Failed to resolve conflicts";o(x,"error")}finally{p(!1)}},[o,i,l,a,f]),j=s.useMemo(()=>{const n={};for(const x of i)n[x.key]=fs(x.localValue,x.remoteValue);return n},[i]);return!t||i.length===0?null:e.jsx("div",{className:"modal-overlay open",onClick:l,children:e.jsxs("div",{className:"modal modal-lg settings-sync-conflict-modal",onClick:n=>n.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Resolve Settings Conflicts",children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Resolve Settings Conflicts"}),e.jsx("button",{className:"modal-close",onClick:l,"aria-label":"Close conflict modal",children:"×"})]}),e.jsxs("div",{className:"modal-body",children:[e.jsx("div",{className:"settings-sync-conflict-modal__conflict-list",children:i.map(n=>{const x=f[n.key]??{resolution:"local"},g=j[n.key];return e.jsxs("div",{className:"settings-sync-conflict-modal__conflict-item",children:[e.jsx("div",{className:"settings-sync-conflict-modal__key",children:n.key}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-panel",children:[e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:u}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:g})})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:h}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:g})})]})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__resolution",children:[e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${n.key}`,checked:x.resolution==="local",onChange:()=>N(n.key,"local")}),"Keep Local"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${n.key}`,checked:x.resolution==="remote",onChange:()=>N(n.key,"remote")}),"Keep Remote"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${n.key}`,checked:x.resolution==="manual",onChange:()=>N(n.key,"manual")}),"Merge Manually"]})]}),x.resolution==="manual"&&e.jsx("textarea",{className:"settings-sync-conflict-modal__manual-input",value:x.manualValue??"",onChange:M=>C(n.key,M.target.value),placeholder:"Enter JSON value..."})]},n.key)})}),e.jsxs("div",{className:"settings-sync-conflict-modal__bulk-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>v("local"),type:"button",children:"Resolve All: Keep Local"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>v("remote"),type:"button",children:"Resolve All: Keep Remote"})]})]}),e.jsxs("div",{className:"modal-actions settings-sync-conflict-modal__footer",children:[e.jsx("button",{className:"btn btn-sm",onClick:l,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm",onClick:P,disabled:y,children:y?"Resolving...":"Confirm"})]})]})})}function ce(t){if(!t)return"—";const l=new Date(t);return Number.isNaN(l.getTime())?"—":l.toLocaleString()}function gs(t){switch(t){case"synced":return"node-detail-modal__sync-dot--synced";case"diff":return"node-detail-modal__sync-dot--diff";case"error":return"node-detail-modal__sync-dot--error";case"pending":return"node-detail-modal__sync-dot--pending";case"never-synced":default:return"node-detail-modal__sync-dot--never"}}function ps({isOpen:t,onClose:l,node:a,projects:i,onUpdate:u,onHealthCheck:h,addToast:o,syncStatus:f,onPushSettings:c,onPullSettings:y,onSyncAuth:p,syncHistory:N=[],onResolveConflicts:C}){const v=s.useRef(!0),[P,j]=s.useState(!1),[n,x]=s.useState(""),[g,M]=s.useState(""),[R,E]=s.useState(""),[D,m]=s.useState(2),[r,b]=s.useState(!1),[z,F]=s.useState(!1),[H,q]=s.useState(!1),[O,V]=s.useState(!1),[T,k]=s.useState(null),[S,K]=s.useState(!1),[w]=s.useState([]),[J,L]=s.useState("running"),[U,X]=s.useState("FUSION_LOG_LEVEL=info"),[te,se]=s.useState("/srv/fusion:/data:rw");s.useEffect(()=>(v.current=!0,()=>{v.current=!1}),[]),s.useEffect(()=>{if(!a||!t){j(!1);return}x(a.name),M(a.url??""),E(a.apiKey??""),m(a.maxConcurrent),j(!1)},[t,a]),s.useEffect(()=>{if(!t)return;const _=I=>{I.key==="Escape"&&(I.preventDefault(),l())};return document.addEventListener("keydown",_),()=>document.removeEventListener("keydown",_)},[t,l]);const Y=s.useMemo(()=>a?Ce(i,a):[],[a,i]),ae=s.useMemo(()=>a?.capabilities?.includes("docker-managed")??!1,[a]),Q=s.useCallback(async()=>{if(a)try{if(await h(a.id),!v.current)return;o(`Health check completed for ${a.name}`,"success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Health check failed";o(I,"error")}},[o,a,h]),ne=s.useCallback(async()=>{if(!(!a||!c)){k(null),F(!0);try{if(await c(a.id),!v.current)return;o("Settings pushed successfully","success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Push settings failed";k(I),o(I,"error")}finally{v.current&&F(!1)}}},[o,a,c]),le=s.useCallback(async()=>{if(!(!a||!y)){k(null),q(!0);try{if(await y(a.id),!v.current)return;o("Settings pulled successfully","success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Pull settings failed";k(I),o(I,"error")}finally{v.current&&q(!1)}}},[o,a,y]),d=s.useCallback(async()=>{if(!(!a||!p)){k(null),V(!0);try{if(await p(a.id),!v.current)return;o("Auth credentials synced successfully","success")}catch(_){if(!v.current)return;const I=_ instanceof Error?_.message:"Auth sync failed";k(I),o(I,"error")}finally{v.current&&V(!1)}}},[o,a,p]),A=s.useCallback(()=>{k(null)},[]),$=s.useCallback(_=>{_==="start"&&L("running"),_==="stop"&&L("stopped"),_==="restart"&&L("running"),_==="recreate"&&L("recreating"),_==="upgrade"&&L("recreating"),o(`Docker action queued: ${_}`,"success")},[o]),B=s.useCallback(async()=>{if(!a||r)return;const _=n.trim();if(!_){o("Name is required","error");return}if(a.type==="remote"&&!g.trim()){o("URL is required for remote nodes","error");return}if(!Number.isFinite(D)||D<1){o("Concurrency must be at least 1","error");return}b(!0);try{await u(a.id,{name:_,url:a.type==="remote"&&g.trim()||void 0,apiKey:a.type==="remote"&&R||void 0,maxConcurrent:D}),o(`Updated ${_}`,"success"),j(!1)}catch(I){const Se=I instanceof Error?I.message:"Failed to update node";o(Se,"error")}finally{b(!1)}},[o,R,r,D,n,a,u,g]),Z=s.useCallback(()=>{a&&(x(a.name),M(a.url??""),E(a.apiKey??""),m(a.maxConcurrent),j(!1))},[a]);return!t||!a?null:e.jsxs("div",{className:"modal-overlay open",onClick:l,children:[e.jsxs("div",{className:"modal modal-lg node-detail-modal",onClick:_=>_.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":`Node details for ${a.name}`,children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Node Details"}),e.jsx("button",{className:"modal-close",onClick:l,"aria-label":"Close node detail modal",children:"×"})]}),e.jsxs("div",{className:"modal-body node-detail-modal__body",children:[e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("div",{className:"node-detail-modal__section-header",children:[e.jsx("h4",{children:"Overview"}),!P&&e.jsxs("button",{className:"btn btn-sm",onClick:()=>j(!0),children:[e.jsx(Ke,{size:14}),"Edit"]})]}),e.jsxs("div",{className:"node-detail-modal__grid",children:[e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Name"}),P?e.jsx("input",{className:"input",value:n,onChange:_=>x(_.target.value),disabled:r}):e.jsx("strong",{children:a.name})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Type"}),e.jsx("strong",{children:a.type==="local"?"Local":"Remote"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Status"}),e.jsx("strong",{children:a.status})]}),e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),P?e.jsx("input",{className:"input",type:"number",min:1,max:10,value:D,onChange:_=>m(Number(_.target.value)),disabled:r}):e.jsx("strong",{children:a.maxConcurrent})]}),a.type==="remote"&&e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"URL"}),P?e.jsx("input",{className:"input",value:g,onChange:_=>M(_.target.value),disabled:r}):e.jsx("strong",{children:a.url??"—"})]}),e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"API Key"}),P?e.jsx("input",{className:"input",type:"password",value:R,onChange:_=>E(_.target.value),placeholder:"Leave blank to keep unchanged",disabled:r}):e.jsx("strong",{children:a.apiKey?"••••••••":"Not configured"})]})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Created"}),e.jsx("strong",{children:ce(a.createdAt)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Updated"}),e.jsx("strong",{children:ce(a.updatedAt)})]})]}),P&&e.jsxs("div",{className:"node-detail-modal__edit-actions",children:[e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:B,disabled:r,children:[e.jsx(ze,{size:14}),r?"Saving...":"Save"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Z,disabled:r,children:[e.jsx(ue,{size:14}),"Cancel"]})]})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsxs("h4",{children:[a.type==="local"?"Projects":"Assigned Projects"," (",Y.length,")"]}),Y.length===0?e.jsx("p",{className:"node-detail-modal__empty",children:a.type==="local"?"No projects are running on this node.":"No projects are assigned to this node."}):e.jsx("ul",{className:"node-detail-modal__project-list",children:Y.map(_=>e.jsxs("li",{className:"node-detail-modal__project-item",children:[e.jsx("span",{children:_.name}),e.jsx("code",{children:_.id})]},_.id))})]}),e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Health"}),e.jsxs("div",{className:"node-detail-modal__health-row",children:[e.jsxs("span",{children:["Status: ",e.jsx("strong",{children:a.status})]}),e.jsxs("span",{children:["Last check: ",e.jsx("strong",{children:ce(a.updatedAt)})]})]})]}),ae&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Docker Management"}),e.jsxs("div",{className:"node-detail-modal__health-row",children:[e.jsxs("span",{children:["Container: ",e.jsx("strong",{children:J})]}),e.jsxs("span",{children:["Image: ",e.jsx("strong",{children:"runfusion/fusion:latest"})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>$("start"),children:"Start"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("stop"),children:"Stop"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("restart"),children:"Restart"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("recreate"),children:"Recreate"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>$("upgrade"),children:"Upgrade Image"})]}),e.jsxs("div",{className:"node-detail-modal__docker-grid",children:[e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Environment Variables"}),e.jsx("textarea",{className:"input node-detail-modal__textarea",value:U,onChange:_=>X(_.target.value)})]}),e.jsxs("label",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Volume Mounts"}),e.jsx("textarea",{className:"input node-detail-modal__textarea",value:te,onChange:_=>se(_.target.value)})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>o("Container logs opened","success"),children:"View Logs"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>o("Config changes saved","success"),children:"Save Config"}),e.jsx("button",{className:"btn btn-danger btn-sm",onClick:()=>o("Delete flow opened (retain/remove volumes)","warning"),children:"Delete Node…"})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Settings Sync"}),f&&e.jsxs("div",{className:"node-detail-modal__sync-status",children:[e.jsx("span",{className:`node-detail-modal__sync-dot ${gs(f.syncState)}`,"aria-hidden":!0}),e.jsxs("span",{children:["Last sync:"," ",e.jsx("strong",{children:f.lastSyncAt?ke(f.lastSyncAt):"Never synced"})]}),f.diffCount>0&&e.jsxs("span",{className:"node-detail-modal__sync-diff",children:["Differences: ",e.jsx("strong",{children:f.diffCount})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:ne,disabled:z||!c,children:[e.jsx(ve,{size:14}),z?"Pushing...":"Push Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:le,disabled:H||!y,children:[e.jsx(_e,{size:14}),H?"Pulling...":"Pull Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:d,disabled:O||!p,children:[e.jsx(be,{size:14}),O?"Syncing...":"Sync Auth"]})]}),T&&e.jsxs("div",{className:"node-detail-modal__sync-error",children:[e.jsx("span",{children:T}),e.jsx("button",{className:"node-detail-modal__sync-error-dismiss",onClick:A,"aria-label":"Dismiss error",children:e.jsx(ue,{size:14})})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Sync History"}),e.jsx(hs,{nodeId:a.id,entries:N,singleNode:!0})]})]}),e.jsxs("div",{className:"modal-actions node-detail-modal__actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:Q,children:[e.jsx(je,{size:14}),"Health Check"]}),e.jsx("button",{className:"btn btn-sm",onClick:l,children:"Close"})]})]}),a.type==="remote"&&e.jsx(xs,{isOpen:S,onClose:()=>K(!1),onResolve:C??(async()=>{}),conflicts:w,localNodeName:"Local",remoteNodeName:a.name,addToast:o})]})}function ys(){const[t,l]=s.useState([]),[a,i]=s.useState(!0),[u,h]=s.useState(null),o=s.useCallback(async()=>{try{h(null);const c=await xe();l(c)}catch(c){h(c instanceof Error?c.message:"Failed to fetch managed Docker nodes")}},[]);s.useEffect(()=>{let c=!1;async function y(){i(!0);try{const p=await xe();c||(l(p),h(null))}catch(p){c||h(p instanceof Error?p.message:"Failed to fetch managed Docker nodes")}finally{c||i(!1)}}return y(),()=>{c=!0}},[]);const f=s.useCallback(async c=>{const y=await Ie(c);return l(p=>[...p,y]),y},[]);return{dockerNodes:t,loading:a,error:u,refresh:o,create:f}}function vs({addToast:t,onClose:l}){const{nodes:a,loading:i,error:u,refresh:h,register:o,update:f,unregister:c,healthCheck:y}=Fe(),{projects:p}=Oe(),{syncStatusMap:N,pushSettings:C,pullSettings:v,syncAuth:P,trackNode:j,getAuthSyncState:n,getAuthProviders:x}=Xe(),{dockerNodes:g,create:M}=ys(),[R,E]=s.useState(!1),[D,m]=s.useState(!1),[r,b]=s.useState(null);s.useEffect(()=>{const k=a.filter(S=>S.type==="remote");for(const S of k)j(S.id)},[a,j]),s.useEffect(()=>{if(!r)return;const k=a.find(S=>S.id===r.id)??null;b(k)},[a,r]);const z=s.useMemo(()=>{const k=a.length,S=a.filter(L=>L.status==="online").length,K=a.filter(L=>L.status==="offline"||L.status==="error").length,w=a.filter(L=>L.type==="remote").length,J=a.filter(L=>L.type==="remote"&&N[L.id]&&re(N[L.id]).syncState==="synced").length;return{total:k,online:S,offline:K,remote:w,synced:J}},[a,N]),F=s.useCallback(async k=>{await o(k)},[o]),H=s.useCallback(async k=>{try{await M(k),t(`Docker node "${k.name}" created`,"success"),m(!1)}catch(S){const K=S instanceof Error?S.message:"Failed to create Docker node";throw t(K,"error"),S}},[t,M]),q=s.useCallback(async()=>{try{await h()}catch{t("Failed to refresh nodes","error")}},[t,h]),O=s.useCallback(async k=>{try{await y(k),t("Node health check complete","success")}catch(S){const K=S instanceof Error?S.message:"Health check failed";t(K,"error")}},[t,y]),V=s.useCallback(async k=>{try{await c(k),t("Node removed","success"),r?.id===k&&b(null)}catch(S){const K=S instanceof Error?S.message:"Failed to remove node";t(K,"error")}},[t,r?.id,c]),T=s.useCallback(async(k,S)=>{await f(k,S)},[f]);return e.jsxs("div",{className:"nodes-view","data-testid":"nodes-view",children:[e.jsxs("div",{className:"nodes-view-header",children:[e.jsxs("div",{className:"nodes-view-title",children:[e.jsxs("h2",{children:[e.jsx(ye,{size:20}),"Nodes"]}),e.jsxs("span",{className:"nodes-view-count",children:[a.length," registered"]})]}),e.jsxs("div",{className:"nodes-view-actions",children:[e.jsx("button",{className:"btn-icon nodes-view-close",onClick:l,"aria-label":"Close nodes view",children:e.jsx(ue,{size:16})}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void q(),disabled:i,children:[e.jsx(de,{size:14,className:i?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>E(!0),children:[e.jsx(ee,{size:14}),"Add Node"]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>m(!0),children:[e.jsx(ee,{size:14}),"Provision Docker Node"]})]})]}),e.jsxs("div",{className:"nodes-view-stats",children:[e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-total",children:[e.jsx("span",{children:"Total"}),e.jsx("strong",{children:z.total})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--online","data-testid":"nodes-stat-online",children:[e.jsxs("span",{children:[e.jsx(Te,{size:14})," Online"]}),e.jsx("strong",{children:z.online})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--offline","data-testid":"nodes-stat-offline",children:[e.jsxs("span",{children:[e.jsx(Be,{size:14})," Offline"]}),e.jsx("strong",{children:z.offline})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-remote",children:[e.jsxs("span",{children:[e.jsx(Ve,{size:14})," Remote"]}),e.jsx("strong",{children:z.remote})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--synced","data-testid":"nodes-stat-synced",children:[e.jsxs("span",{children:[e.jsx(de,{size:14})," Synced"]}),e.jsx("strong",{children:z.synced})]})]}),u&&e.jsx("div",{className:"nodes-view-error",children:u}),e.jsxs("section",{className:"nodes-view-topology","aria-label":"Docker Nodes Summary",children:[e.jsx("h3",{className:"nodes-view-section-title",children:"Docker Nodes"}),e.jsxs("div",{className:"nodes-view-stat",children:[e.jsx("span",{children:"Managed Docker Nodes"}),e.jsx("strong",{children:g.length}),e.jsx("button",{className:"btn btn-sm",onClick:()=>m(!0),children:"Provision"})]})]}),!i&&a.length>0&&e.jsxs("section",{className:"nodes-view-topology","aria-label":"Mesh Topology",children:[e.jsx("h3",{className:"nodes-view-section-title",children:"Mesh Topology"}),e.jsx(rs,{nodes:a})]}),i?e.jsx("div",{className:"nodes-view-grid",children:Array.from({length:4}).map((k,S)=>e.jsx("div",{className:"node-card node-card--loading","aria-hidden":!0},S))}):a.length===0?e.jsxs("div",{className:"nodes-view-empty",children:[e.jsx("p",{children:"No nodes are registered yet."}),e.jsxs("button",{className:"btn btn-primary",onClick:()=>E(!0),children:[e.jsx(ee,{size:14}),"Add First Node"]})]}):e.jsx("div",{className:"nodes-view-grid",children:a.map(k=>{const S=k.type==="remote"&&N[k.id]?re(N[k.id]):void 0;return e.jsx(ts,{node:k,projects:p,onHealthCheck:K=>{O(K)},onEdit:K=>b(K),onRemove:K=>{V(K)},isLoading:i,syncStatus:S,authSyncState:k.type==="remote"?n(k.id):void 0,authSyncProviders:k.type==="remote"?x(k.id):void 0},k.id)})}),e.jsx(cs,{isOpen:R,onClose:()=>E(!1),onSubmit:F,addToast:t}),e.jsx(ms,{isOpen:D,onClose:()=>m(!1),onSubmit:H,addToast:t}),e.jsx(ps,{isOpen:r!==null,onClose:()=>b(null),node:r,projects:p,onUpdate:T,onHealthCheck:O,addToast:t,syncStatus:r?.type==="remote"&&r&&N[r.id]?re(N[r.id]):void 0,onPushSettings:C,onPullSettings:v,onSyncAuth:P})]})}export{vs as NodesView};
@@ -1,11 +1,11 @@
1
- import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{c as W,dW as H,dX as J,dY as Q,R as $,dO as y,M as V,a as T,b as ee,Y as se,cl as L,F as _,dZ as ae,d_ as te,d$ as D,X as ie}from"./index-Bv0TGzDH.js";import"./vendor-xterm-DzcZoU0P.js";/**
1
+ import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{c as A,dZ as J,d_ as Q,d$ as V,R as $,dR as y,O as Y,a as T,b as ee,_ as se,cm as L,F as _,e0 as ae,e1 as te,e2 as D,X as ie}from"./index-k_85J1DS.js";import"./vendor-xterm-DzcZoU0P.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
5
5
  * See the LICENSE file in the root directory of this source tree.
6
- */const ne=[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z",key:"e79jfc"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}]],M=W("palette",ne);/**
6
+ */const ne=[["path",{d:"M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z",key:"e79jfc"}],["circle",{cx:"13.5",cy:"6.5",r:".5",fill:"currentColor",key:"1okk4w"}],["circle",{cx:"17.5",cy:"10.5",r:".5",fill:"currentColor",key:"f64h9f"}],["circle",{cx:"6.5",cy:"12.5",r:".5",fill:"currentColor",key:"qy21gx"}],["circle",{cx:"8.5",cy:"7.5",r:".5",fill:"currentColor",key:"fotxhn"}]],M=A("palette",ne);/**
7
7
  * @license lucide-react v1.7.0 - ISC
8
8
  *
9
9
  * This source code is licensed under the ISC license.
10
10
  * See the LICENSE file in the root directory of this source tree.
11
- */const le=[["path",{d:"M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z",key:"w46dr5"}]],O=W("puzzle",le);function ce(t){return t.replace(/-/g,"-")}function re(t){return{"fusion-global":"Fusion Global","pi-global":"Pi Global","fusion-project":"Fusion Project","pi-project":"Pi Project",package:"Package"}[t]??t}function oe(t){return t.startsWith("npm:")?"npm":t.startsWith("git:")?"git":"local"}function de(t){return t.replace(/^(npm:|git:)/,"")}function he({addToast:t,projectId:x}){const[c,A]=l.useState(null),[f,P]=l.useState(!0),[N,S]=l.useState(!1),[E,w]=l.useState(!1),[h,F]=l.useState(""),[G,I]=l.useState(new Set),[p,X]=l.useState([]),[b,C]=l.useState(!0),[Y,z]=l.useState(!1),r=l.useCallback(async()=>{try{P(!0);const s=await H();A(s)}catch(s){t(`Failed to load Pi settings: ${s instanceof Error?s.message:String(s)}`,"error")}finally{P(!1)}},[t]),m=l.useCallback(async()=>{try{C(!0);const s=await J(x);X(s.extensions)}catch(s){t(`Failed to load extensions: ${s instanceof Error?s.message:String(s)}`,"error")}finally{C(!1)}},[t,x]),q=l.useCallback(async s=>{try{z(!0);const i=s.enabled?[...p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id),s.id]:p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id);await Q(i,x),await m(),t(s.enabled?"Extension disabled":"Extension enabled","success")}catch(i){t(`Failed to update extension: ${i instanceof Error?i.message:String(i)}`,"error")}finally{z(!1)}},[p,x,m,t]);l.useEffect(()=>{r()},[r]),l.useEffect(()=>{m()},[m]);const B=s=>{I(i=>{const a=new Set(i);return a.has(s)?a.delete(s):a.add(s),a})},R=async()=>{if(!h.trim()){t("Please enter a package source","error");return}try{S(!0),await ae(h.trim()),t("Package installed successfully","success"),F(""),await r()}catch(s){t(`Failed to install package: ${s instanceof Error?s.message:String(s)}`,"error")}finally{S(!1)}},K=async()=>{try{w(!0),await te(x),await Promise.all([r(),m()]),t("Fusion skill reinstalled successfully","success")}catch(s){t(`Failed to reinstall Fusion skill: ${s instanceof Error?s.message:String(s)}`,"error")}finally{w(!1)}},U=async s=>{if(!c)return;const i=c.packages.filter(a=>(typeof a=="string"?a:a.source)!==s);try{await D({packages:i}),t("Package removed","success"),await r()}catch(a){t(`Failed to remove package: ${a instanceof Error?a.message:String(a)}`,"error")}},Z=async(s,i)=>{if(!c)return;const a=c[s].filter(n=>n!==i);try{await D({[s]:a}),t(`${s.slice(0,-1)} removed`,"success"),await r()}catch(n){t(`Failed to update settings: ${n instanceof Error?n.message:String(n)}`,"error")}},u=(s,i,a)=>!c||c[a].length===0?null:e.jsxs("div",{className:"pi-ext-section",children:[e.jsxs("div",{className:"pi-ext-section-header",children:[e.jsx(i,{size:14}),e.jsx("span",{children:s}),e.jsx("span",{className:"pi-ext-count",children:c[a].length})]}),e.jsx("div",{className:"pi-ext-resource-list",children:c[a].map((n,g)=>e.jsxs("span",{className:"pi-ext-resource-tag",children:[e.jsx("span",{className:"pi-ext-resource-path",children:n}),e.jsx("button",{className:"btn-icon touch-target pi-ext-resource-remove",onClick:()=>Z(a,n),title:`Remove ${n}`,"aria-label":`Remove ${n}`,children:e.jsx(ie,{})})]},g))})]});return e.jsxs("div",{className:"pi-ext-manager",children:[e.jsxs("div",{className:"pi-ext-manager-header",children:[e.jsx("h4",{className:"pi-ext-manager-title",children:"Pi Extensions"}),e.jsx("div",{className:"pi-ext-manager-actions",children:e.jsx("button",{className:"btn-icon",onClick:r,title:"Refresh",disabled:f,children:e.jsx($,{size:16,className:f?"spin":""})})})]}),f?e.jsx("div",{className:"loading-state",children:"Loading Pi settings…"}):c?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pi-ext-add-form",children:[e.jsxs("div",{className:"pi-ext-add-form-row",children:[e.jsx("input",{type:"text",className:"input",placeholder:"npm:pi-extension-name or git:https://github.com/...",value:h,onChange:s=>F(s.target.value),onKeyDown:s=>{s.key==="Enter"&&(s.preventDefault(),R())},disabled:N}),e.jsxs("button",{className:"btn btn-primary",onClick:R,disabled:N||!h.trim(),children:[e.jsx(V,{size:14}),N?"Installing…":"Add"]})]}),e.jsx("div",{className:"pi-ext-add-form-row",children:e.jsx("button",{className:"btn",onClick:K,disabled:E,children:E?"Reinstalling Fusion…":"Reinstall Fusion skill"})})]}),c.packages.length>0?e.jsx("div",{className:"pi-ext-package-list",children:c.packages.map((s,i)=>{const a=typeof s=="string"?s:s.source,n=oe(a),g=de(a),j=typeof s=="object"&&s!==null,v=j&&(s.extensions?.length??0)>0||(s.skills?.length??0)>0||(s.prompts?.length??0)>0||(s.themes?.length??0)>0,k=G.has(i);return e.jsxs("div",{className:"pi-ext-package-card",children:[e.jsxs("div",{className:"pi-ext-package-header",children:[j&&v?e.jsx("button",{className:"pi-ext-expand-btn",onClick:()=>B(i),"aria-expanded":k,children:k?e.jsx(T,{size:14}):e.jsx(ee,{size:14})}):e.jsx("span",{className:"pi-ext-expand-placeholder"}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${n}`,children:n}),e.jsx("span",{className:"pi-ext-package-source",children:g}),e.jsxs("div",{className:"pi-ext-package-actions",children:[j&&v&&e.jsxs("span",{className:"pi-ext-filter-hint",children:[s.extensions?.length??0," ext,"," ",s.skills?.length??0," skill,"," ",s.prompts?.length??0," prompt,"," ",s.themes?.length??0," theme"]}),e.jsx("button",{className:"btn-icon touch-target pi-ext-remove-btn",onClick:()=>U(a),title:"Remove package","aria-label":`Remove package ${g}`,children:e.jsx(se,{size:14})})]})]}),j&&v&&k&&e.jsxs("div",{className:"pi-ext-filter-list",children:[s.extensions?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(O,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Extensions:"}),s.extensions.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.skills?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(L,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Skills:"}),s.skills.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.prompts?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(_,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Prompts:"}),s.prompts.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.themes?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(M,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Themes:"}),s.themes.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null]})]},i)})}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No packages configured."}),e.jsx("p",{className:"text-muted",children:"Add a package source above to get started."})]}),e.jsxs("div",{className:"pi-ext-top-level",children:[u("Extensions",O,"extensions"),u("Skills",L,"skills"),u("Prompts",_,"prompts"),u("Themes",M,"themes")]})]}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"Failed to load Pi settings."})]}),e.jsxs("div",{className:"pi-ext-discovered-section",children:[e.jsxs("div",{className:"pi-ext-discovered-header",children:[e.jsx("h4",{children:"Discovered Extensions"}),e.jsx("button",{className:"btn-icon",onClick:m,disabled:b,title:"Refresh extensions",children:e.jsx($,{className:b?"spin":""})})]}),e.jsx("p",{className:"pi-ext-description",children:"Installed extensions resolved from packages and configured paths."}),b?e.jsx("div",{className:"loading-state",children:"Loading extensions…"}):p.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No extensions discovered."})]}):e.jsx("div",{className:"pi-ext-list",children:p.map(s=>e.jsxs("div",{className:"pi-ext-item",children:[e.jsxs("div",{className:"pi-ext-item-content",children:[e.jsxs("div",{className:"pi-ext-info",children:[e.jsx("span",{className:"pi-ext-name",children:s.name}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${ce(s.source)}`,children:re(s.source)})]}),e.jsx("span",{className:"pi-ext-path",children:s.path})]}),e.jsx("div",{className:"pi-ext-actions",children:e.jsxs("label",{className:"toggle-switch",children:[e.jsx("input",{type:"checkbox",checked:s.enabled,onChange:()=>void q(s),disabled:Y,"aria-label":`Toggle ${s.name}`}),e.jsx("span",{className:"toggle-slider"})]})})]},s.id))})]})]})}export{he as PiExtensionsManager};
11
+ */const le=[["path",{d:"M15.39 4.39a1 1 0 0 0 1.68-.474 2.5 2.5 0 1 1 3.014 3.015 1 1 0 0 0-.474 1.68l1.683 1.682a2.414 2.414 0 0 1 0 3.414L19.61 15.39a1 1 0 0 1-1.68-.474 2.5 2.5 0 1 0-3.014 3.015 1 1 0 0 1 .474 1.68l-1.683 1.682a2.414 2.414 0 0 1-3.414 0L8.61 19.61a1 1 0 0 0-1.68.474 2.5 2.5 0 1 1-3.014-3.015 1 1 0 0 0 .474-1.68l-1.683-1.682a2.414 2.414 0 0 1 0-3.414L4.39 8.61a1 1 0 0 1 1.68.474 2.5 2.5 0 1 0 3.014-3.015 1 1 0 0 1-.474-1.68l1.683-1.682a2.414 2.414 0 0 1 3.414 0z",key:"w46dr5"}]],O=A("puzzle",le);function ce(t){return t.replace(/-/g,"-")}function re(t){return{"fusion-global":"Fusion Global","pi-global":"Pi Global","fusion-project":"Fusion Project","pi-project":"Pi Project",package:"Package"}[t]??t}function oe(t){return t.startsWith("npm:")?"npm":t.startsWith("git:")?"git":"local"}function de(t){return t.replace(/^(npm:|git:)/,"")}function he({addToast:t,projectId:x}){const[c,G]=l.useState(null),[f,P]=l.useState(!0),[N,S]=l.useState(!1),[E,w]=l.useState(!1),[h,F]=l.useState(""),[I,W]=l.useState(new Set),[p,q]=l.useState([]),[b,C]=l.useState(!0),[B,z]=l.useState(!1),r=l.useCallback(async()=>{try{P(!0);const s=await J();G(s)}catch(s){t(`Failed to load Pi settings: ${s instanceof Error?s.message:String(s)}`,"error")}finally{P(!1)}},[t]),m=l.useCallback(async()=>{try{C(!0);const s=await Q(x);q(s.extensions)}catch(s){t(`Failed to load extensions: ${s instanceof Error?s.message:String(s)}`,"error")}finally{C(!1)}},[t,x]),K=l.useCallback(async s=>{try{z(!0);const i=s.enabled?[...p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id),s.id]:p.filter(a=>a.enabled&&a.id!==s.id).map(a=>a.id);await V(i,x),await m(),t(s.enabled?"Extension disabled":"Extension enabled","success")}catch(i){t(`Failed to update extension: ${i instanceof Error?i.message:String(i)}`,"error")}finally{z(!1)}},[p,x,m,t]);l.useEffect(()=>{r()},[r]),l.useEffect(()=>{m()},[m]);const U=s=>{W(i=>{const a=new Set(i);return a.has(s)?a.delete(s):a.add(s),a})},R=async()=>{if(!h.trim()){t("Please enter a package source","error");return}try{S(!0),await ae(h.trim()),t("Package installed successfully","success"),F(""),await r()}catch(s){t(`Failed to install package: ${s instanceof Error?s.message:String(s)}`,"error")}finally{S(!1)}},X=async()=>{try{w(!0),await te(x),await Promise.all([r(),m()]),t("Fusion skill reinstalled successfully","success")}catch(s){t(`Failed to reinstall Fusion skill: ${s instanceof Error?s.message:String(s)}`,"error")}finally{w(!1)}},Z=async s=>{if(!c)return;const i=c.packages.filter(a=>(typeof a=="string"?a:a.source)!==s);try{await D({packages:i}),t("Package removed","success"),await r()}catch(a){t(`Failed to remove package: ${a instanceof Error?a.message:String(a)}`,"error")}},H=async(s,i)=>{if(!c)return;const a=c[s].filter(n=>n!==i);try{await D({[s]:a}),t(`${s.slice(0,-1)} removed`,"success"),await r()}catch(n){t(`Failed to update settings: ${n instanceof Error?n.message:String(n)}`,"error")}},u=(s,i,a)=>!c||c[a].length===0?null:e.jsxs("div",{className:"pi-ext-section",children:[e.jsxs("div",{className:"pi-ext-section-header",children:[e.jsx(i,{size:14}),e.jsx("span",{children:s}),e.jsx("span",{className:"pi-ext-count",children:c[a].length})]}),e.jsx("div",{className:"pi-ext-resource-list",children:c[a].map((n,g)=>e.jsxs("span",{className:"pi-ext-resource-tag",children:[e.jsx("span",{className:"pi-ext-resource-path",children:n}),e.jsx("button",{className:"btn-icon touch-target pi-ext-resource-remove",onClick:()=>H(a,n),title:`Remove ${n}`,"aria-label":`Remove ${n}`,children:e.jsx(ie,{})})]},g))})]});return e.jsxs("div",{className:"pi-ext-manager",children:[e.jsxs("div",{className:"pi-ext-manager-header",children:[e.jsx("h4",{className:"pi-ext-manager-title",children:"Pi Extensions"}),e.jsx("div",{className:"pi-ext-manager-actions",children:e.jsx("button",{className:"btn-icon",onClick:r,title:"Refresh",disabled:f,children:e.jsx($,{size:16,className:f?"spin":""})})})]}),f?e.jsx("div",{className:"loading-state",children:"Loading Pi settings…"}):c?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"pi-ext-add-form",children:[e.jsxs("div",{className:"pi-ext-add-form-row",children:[e.jsx("input",{type:"text",className:"input",placeholder:"npm:pi-extension-name or git:https://github.com/...",value:h,onChange:s=>F(s.target.value),onKeyDown:s=>{s.key==="Enter"&&(s.preventDefault(),R())},disabled:N}),e.jsxs("button",{className:"btn btn-primary",onClick:R,disabled:N||!h.trim(),children:[e.jsx(Y,{size:14}),N?"Installing…":"Add"]})]}),e.jsx("div",{className:"pi-ext-add-form-row",children:e.jsx("button",{className:"btn",onClick:X,disabled:E,children:E?"Reinstalling Fusion…":"Reinstall Fusion skill"})})]}),c.packages.length>0?e.jsx("div",{className:"pi-ext-package-list",children:c.packages.map((s,i)=>{const a=typeof s=="string"?s:s.source,n=oe(a),g=de(a),j=typeof s=="object"&&s!==null,v=j&&(s.extensions?.length??0)>0||(s.skills?.length??0)>0||(s.prompts?.length??0)>0||(s.themes?.length??0)>0,k=I.has(i);return e.jsxs("div",{className:"pi-ext-package-card",children:[e.jsxs("div",{className:"pi-ext-package-header",children:[j&&v?e.jsx("button",{className:"pi-ext-expand-btn",onClick:()=>U(i),"aria-expanded":k,children:k?e.jsx(T,{size:14}):e.jsx(ee,{size:14})}):e.jsx("span",{className:"pi-ext-expand-placeholder"}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${n}`,children:n}),e.jsx("span",{className:"pi-ext-package-source",children:g}),e.jsxs("div",{className:"pi-ext-package-actions",children:[j&&v&&e.jsxs("span",{className:"pi-ext-filter-hint",children:[s.extensions?.length??0," ext,"," ",s.skills?.length??0," skill,"," ",s.prompts?.length??0," prompt,"," ",s.themes?.length??0," theme"]}),e.jsx("button",{className:"btn-icon touch-target pi-ext-remove-btn",onClick:()=>Z(a),title:"Remove package","aria-label":`Remove package ${g}`,children:e.jsx(se,{size:14})})]})]}),j&&v&&k&&e.jsxs("div",{className:"pi-ext-filter-list",children:[s.extensions?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(O,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Extensions:"}),s.extensions.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.skills?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(L,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Skills:"}),s.skills.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.prompts?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(_,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Prompts:"}),s.prompts.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null,s.themes?.length?e.jsxs("div",{className:"pi-ext-filter-section",children:[e.jsx(M,{size:12}),e.jsx("span",{className:"pi-ext-filter-label",children:"Themes:"}),s.themes.map((o,d)=>e.jsx("span",{className:"pi-ext-filter-tag",children:o},d))]}):null]})]},i)})}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No packages configured."}),e.jsx("p",{className:"text-muted",children:"Add a package source above to get started."})]}),e.jsxs("div",{className:"pi-ext-top-level",children:[u("Extensions",O,"extensions"),u("Skills",L,"skills"),u("Prompts",_,"prompts"),u("Themes",M,"themes")]})]}):e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"Failed to load Pi settings."})]}),e.jsxs("div",{className:"pi-ext-discovered-section",children:[e.jsxs("div",{className:"pi-ext-discovered-header",children:[e.jsx("h4",{children:"Discovered Extensions"}),e.jsx("button",{className:"btn-icon",onClick:m,disabled:b,title:"Refresh extensions",children:e.jsx($,{className:b?"spin":""})})]}),e.jsx("p",{className:"pi-ext-description",children:"Installed extensions resolved from packages and configured paths."}),b?e.jsx("div",{className:"loading-state",children:"Loading extensions…"}):p.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx(y,{size:32,className:"text-muted"}),e.jsx("p",{children:"No extensions discovered."})]}):e.jsx("div",{className:"pi-ext-list",children:p.map(s=>e.jsxs("div",{className:"pi-ext-item",children:[e.jsxs("div",{className:"pi-ext-item-content",children:[e.jsxs("div",{className:"pi-ext-info",children:[e.jsx("span",{className:"pi-ext-name",children:s.name}),e.jsx("span",{className:`pi-ext-source-badge pi-ext-source-badge--${ce(s.source)}`,children:re(s.source)})]}),e.jsx("span",{className:"pi-ext-path",children:s.path})]}),e.jsx("div",{className:"pi-ext-actions",children:e.jsxs("label",{className:"toggle-switch",children:[e.jsx("input",{type:"checkbox",checked:s.enabled,onChange:()=>void K(s),disabled:B,"aria-label":`Toggle ${s.name}`}),e.jsx("span",{className:"toggle-slider"})]})})]},s.id))})]})]})}export{he as PiExtensionsManager};
@@ -1 +1 @@
1
- import{r as o,j as s}from"./vendor-react-K0fH_qHe.js";import{y as T,dM as ee,s as se,X as O,ap as ne,M as A,dN as M,Y as _,R as te,dO as ie,aI as ae,cb as le,dP as re,dQ as ce,dR as de,dS as ue,dT as V,c8 as oe}from"./index-Bv0TGzDH.js";import{D as me}from"./DirectoryPicker-BZWVA9ND.js";import"./vendor-xterm-DzcZoU0P.js";import"./folder-open-WVtgE4k3.js";const ge=[{id:"fusion-plugin-agent-browser-runtime",name:"Agent Browser Runtime",path:"./plugins/fusion-plugin-agent-browser-runtime",experimental:!0},{id:"fusion-plugin-hermes-runtime",name:"Hermes Runtime",path:"./plugins/fusion-plugin-hermes-runtime",experimental:!0},{id:"fusion-plugin-paperclip-runtime",name:"Paperclip Runtime",path:"./plugins/fusion-plugin-paperclip-runtime"},{id:"fusion-plugin-openclaw-runtime",name:"OpenClaw Runtime",path:"./plugins/fusion-plugin-openclaw-runtime",experimental:!0}],N={started:"var(--color-success)",loaded:"var(--color-warning)",error:"var(--color-error)",stopped:"var(--color-muted)",installed:"var(--color-info)"};function fe({addToast:l,projectId:c}){const[x,b]=o.useState([]),[S,w]=o.useState(!0),[H,f]=o.useState(!1),[j,v]=o.useState(""),[C,P]=o.useState(!1),[h,R]=o.useState(null),[i,y]=o.useState(null),[a,p]=o.useState({}),[G,I]=o.useState(!1),[$,E]=o.useState(null),{confirm:J}=T(),m=o.useCallback(async()=>{try{w(!0);const e=await ee(c);b(e)}catch(e){l(`Failed to load plugins: ${e instanceof Error?e.message:String(e)}`,"error")}finally{w(!1)}},[c,l]);o.useEffect(()=>{m()},[m]);const K=o.useRef([]);K.current=x,o.useEffect(()=>{const e=c?`?projectId=${encodeURIComponent(c)}`:"",n=d=>{try{const t=JSON.parse(d.data);if(c&&t.projectId&&t.projectId!==c)return;switch(t.transition){case"installing":case"enabled":case"disabled":case"settings-updated":b(u=>{const g=u.findIndex(r=>r.id===t.pluginId);if(g>=0){const r=[...u];return r[g]={...r[g],enabled:t.enabled,state:t.state,settings:t.settings,error:t.error},r}else return m(),u});break;case"uninstalled":b(u=>u.filter(g=>g.id!==t.pluginId));break;case"error":b(u=>{const g=u.findIndex(r=>r.id===t.pluginId);if(g>=0){const r=[...u];return r[g]={...r[g],state:t.state,error:t.error},r}return u});break}}catch{}};return se(`/api/events${e}`,{events:{"plugin:lifecycle":n},onReconnect:()=>{m()}})},[c,m]);const z=async()=>{if(!j.trim()){l("Please enter a plugin path","error");return}try{P(!0),await V({path:j},c),l("Plugin installed successfully","success"),f(!1),v(""),await m()}catch(e){l(`Failed to install plugin: ${e instanceof Error?e.message:String(e)}`,"error")}finally{P(!1)}},Q=async e=>{try{E(e.id),await V({path:e.path},c),l(`${e.name} installed successfully`,"success"),await m()}catch(n){l(`Failed to install ${e.name}: ${n instanceof Error?n.message:String(n)}`,"error")}finally{E(null)}},B=async e=>{try{await de(e.id,c),l(`${e.name} enabled`,"success"),await m()}catch(n){l(`Failed to enable plugin: ${n instanceof Error?n.message:String(n)}`,"error")}},F=async e=>{try{await ce(e.id,c),l(`${e.name} disabled`,"success"),await m()}catch(n){l(`Failed to disable plugin: ${n instanceof Error?n.message:String(n)}`,"error")}},L=async e=>{try{R(e.id),await re(e.id,c),l(`${e.name} reloaded`,"success"),await m()}catch(n){l(`Failed to reload plugin: ${n instanceof Error?n.message:String(n)}`,"error")}finally{R(null)}},U=async e=>{if(await J({title:"Uninstall Plugin",message:`Are you sure you want to uninstall "${e.name}"?`,danger:!0}))try{await ue(e.id,c),l(`${e.name} uninstalled`,"success"),await m(),y(null)}catch(d){l(`Failed to uninstall plugin: ${d instanceof Error?d.message:String(d)}`,"error")}},q=async e=>{y(e);try{I(!0);const n=await oe(e.id,c);p(n)}catch{p({})}finally{I(!1)}},X=async()=>{if(i)try{await le(i.id,a,c),l("Settings saved","success")}catch(e){l(`Failed to save settings: ${e instanceof Error?e.message:String(e)}`,"error")}};if(i)return s.jsxs("div",{className:"plugin-manager-detail","data-testid":"plugin-manager-detail",children:[s.jsxs("div",{className:"plugin-manager-detail-header",children:[s.jsx("button",{className:"btn-icon",onClick:()=>y(null),"aria-label":"Back to plugin list",children:s.jsx(O,{size:16})}),s.jsxs("div",{className:"plugin-detail-title",children:[s.jsx("h4",{className:"plugin-detail-name",children:i.name}),s.jsx("span",{className:"plugin-state-badge",style:{color:N[i.state]||N.installed},children:i.state})]})]}),s.jsxs("div",{className:"plugin-detail-content",children:[s.jsxs("div",{className:"plugin-detail-card",children:[i.description&&s.jsx("p",{className:"plugin-description",children:i.description}),i.author&&s.jsxs("p",{className:"plugin-detail-meta-row",children:[s.jsx("span",{className:"text-muted",children:"Author:"}),i.author]}),i.homepage&&s.jsxs("p",{className:"plugin-detail-meta-row plugin-homepage",children:[s.jsx("span",{className:"text-muted",children:"Homepage:"}),s.jsxs("a",{href:i.homepage,target:"_blank",rel:"noopener noreferrer",children:[i.homepage,s.jsx(ne,{size:12})]})]}),s.jsxs("p",{className:"plugin-detail-meta-row",children:[s.jsx("span",{className:"text-muted",children:"Version:"}),i.version]})]}),s.jsxs("div",{className:"plugin-detail-card",children:[s.jsx("h5",{className:"plugin-detail-section-heading",children:"Settings"}),G?s.jsx("p",{className:"text-muted",children:"Loading..."}):i.settingsSchema&&Object.keys(i.settingsSchema).length>0?s.jsxs("div",{className:"plugin-settings-form",children:[Object.entries(i.settingsSchema).map(([e,n])=>{const d=`setting-${e}-help`;return s.jsxs("div",{className:"form-group",children:[s.jsxs("label",{htmlFor:`setting-${e}`,children:[n.label||e,n.required&&" *"]}),n.type==="string"&&!n.multiline&&s.jsx("input",{className:"input",type:"text",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="string"&&n.multiline&&s.jsx("textarea",{className:"input",id:`setting-${e}`,rows:4,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="password"&&s.jsx("input",{className:"input",type:"password",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="number"&&s.jsx("input",{className:"input",type:"number",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:Number(t.target.value)}),"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="boolean"&&s.jsxs("label",{className:"checkbox-label",children:[s.jsx("input",{type:"checkbox",checked:a[e]??!1,onChange:t=>p({...a,[e]:t.target.checked})}),n.description]}),n.type==="enum"&&s.jsxs("select",{className:"select",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),"aria-describedby":n.description&&!n.required?d:void 0,children:[s.jsx("option",{value:"",children:"Select..."}),n.enumValues?.map(t=>s.jsx("option",{value:t,children:t},t))]}),n.type==="array"&&s.jsxs("div",{className:"plugin-settings-array",children:[a[e]?.map((t,u)=>s.jsxs("div",{className:"plugin-settings-array-item",children:[s.jsx("input",{className:"input",type:n.itemType==="number"?"number":"text",value:t??"",onChange:g=>{const r=g.target.value,k=[...a[e]||[]];k[u]=n.itemType==="number"?Number(r):r,p({...a,[e]:k})}}),s.jsx("button",{className:"btn-icon",onClick:()=>{const r=[...a[e]||[]];r.splice(u,1),p({...a,[e]:r})},"aria-label":"Remove item",children:s.jsx(O,{size:14})})]},u)),s.jsxs("button",{className:"btn btn-secondary",onClick:()=>{const t=a[e]||[],u=n.itemType==="number"?0:"";p({...a,[e]:[...t,u]})},children:[s.jsx(A,{size:14})," Add Item"]})]}),n.description&&!n.required&&!n.multiline&&s.jsx("span",{id:d,className:"form-help",children:n.description})]},e)}),s.jsx("button",{className:"btn btn-primary",onClick:X,children:"Save Settings"})]}):s.jsx("p",{className:"text-muted",children:"No configurable settings."})]}),s.jsxs("div",{className:"plugin-detail-actions",children:[i.state==="started"&&s.jsxs("button",{className:"btn btn-secondary",onClick:()=>L(i),disabled:h===i.id,children:[s.jsx(M,{size:14,className:h===i.id?"spin":""}),h===i.id?"Reloading...":"Reload"]}),i.enabled?s.jsx("button",{className:"btn btn-secondary",onClick:()=>F(i),children:"Disable"}):s.jsx("button",{className:"btn btn-primary",onClick:()=>B(i),children:"Enable"}),s.jsxs("button",{className:"btn btn-danger",onClick:()=>U(i),children:[s.jsx(_,{size:14})," Uninstall"]})]})]})]});const Y=new Set(x.map(e=>e.id)),W=new Map(x.map(e=>[e.id,e])),D=x,Z=()=>s.jsxs("section",{className:"plugin-bundled-runtime-section","aria-label":"Bundled Runtime Plugins",children:[s.jsxs("div",{className:"plugin-bundled-runtime-header",children:[s.jsx("h4",{className:"plugin-bundled-runtime-heading",children:"Bundled Runtime Plugins"}),s.jsx("p",{className:"plugin-bundled-runtime-description",children:"Install Fusion's bundled runtimes directly from this screen."})]}),s.jsx("div",{className:"plugin-bundled-runtime-list","aria-label":"Bundled runtime plugin recommendations",children:ge.map(e=>{const n=Y.has(e.id);return s.jsxs("div",{className:"plugin-bundled-runtime-item",children:[s.jsxs("div",{className:"plugin-bundled-runtime-meta",children:[s.jsx("span",{className:"plugin-bundled-runtime-name",children:e.name}),e.experimental&&s.jsx("span",{className:"plugin-bundled-runtime-badge",children:"Experimental"}),s.jsx("span",{className:`plugin-bundled-runtime-status ${n?"plugin-bundled-runtime-status--installed":"plugin-bundled-runtime-status--available"}`,children:n?"Installed":"Not installed"})]}),s.jsx("button",{className:`btn ${n?"btn-secondary":"btn-primary"} btn-sm`,onClick:()=>{if(n){const d=W.get(e.id);d&&q(d);return}Q(e)},disabled:$===e.id,children:n?"Manage":$===e.id?"Installing...":`Install ${e.name}`})]},e.id)})})]});return s.jsxs("div",{className:"plugin-manager","data-testid":"plugin-manager",children:[s.jsxs("div",{className:"plugin-manager-header",children:[s.jsx("span",{className:"plugin-manager-header-title",children:"Installed Plugins"}),s.jsxs("div",{className:"plugin-manager-actions",children:[s.jsxs("button",{className:"btn btn-sm",onClick:m,title:"Refresh","aria-label":"Refresh plugin list",children:[s.jsx(te,{size:14,className:S?"spin":""}),"Refresh"]}),s.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>f(!0),children:[s.jsx(A,{size:14})," Install"]})]})]}),H&&s.jsxs("div",{className:"plugin-install-form",children:[s.jsxs("p",{className:"plugin-install-hint",children:["Browse to a plugin package root (contains ",s.jsx("code",{children:"manifest.json"}),") or a built ",s.jsx("code",{children:"dist"})," directory."]}),s.jsx(me,{value:j,onChange:v,placeholder:"Absolute path to plugin directory or dist folder",onInputKeyDown:e=>{e.key==="Enter"&&(e.preventDefault(),z())}}),s.jsxs("div",{className:"plugin-install-actions",children:[s.jsx("button",{className:"btn btn-primary",onClick:z,disabled:C||!j.trim(),children:C?"Installing...":"Install Plugin"}),s.jsx("button",{className:"btn btn-secondary",onClick:()=>{f(!1),v("")},children:"Cancel"})]})]}),S?s.jsx("div",{className:"settings-empty-state",children:"Loading plugins..."}):s.jsxs(s.Fragment,{children:[D.length===0?s.jsxs("div",{className:"settings-empty-state",children:[s.jsx(ie,{size:32,className:"text-muted"}),s.jsx("p",{children:"No plugins installed."}),s.jsx("p",{className:"text-muted",children:"Install a plugin to get started, or use a bundled runtime below."})]}):s.jsx("div",{className:"plugin-list",children:D.map(e=>s.jsxs("div",{className:"plugin-item",children:[s.jsxs("div",{className:"plugin-info",children:[s.jsx("span",{className:"plugin-name",children:e.name}),s.jsxs("span",{className:"plugin-version text-muted",children:["v",e.version]}),s.jsx("span",{className:"plugin-state-badge",style:{color:N[e.state]||N.installed},children:e.state})]}),s.jsxs("div",{className:"plugin-actions",children:[e.state==="started"&&s.jsx("button",{className:"btn-icon",onClick:()=>L(e),disabled:h===e.id,title:"Reload",children:s.jsx(M,{size:14,className:h===e.id?"spin":""})}),s.jsxs("label",{className:"toggle-switch",children:[s.jsx("input",{type:"checkbox",checked:e.enabled,onChange:()=>e.enabled?F(e):B(e)}),s.jsx("span",{className:"toggle-slider"})]}),s.jsx("button",{className:"btn-icon",onClick:()=>q(e),title:"Settings",children:s.jsx(ae,{size:14})}),s.jsx("button",{className:"btn-icon",onClick:()=>U(e),title:"Uninstall",children:s.jsx(_,{size:14})})]})]},e.id))}),Z()]})]})}export{fe as PluginManager,N as STATE_COLORS};
1
+ import{r as o,j as s}from"./vendor-react-K0fH_qHe.js";import{z as T,dP as ee,s as se,X as O,aq as ne,O as A,dQ as _,_ as M,R as te,dR as ie,aJ as ae,cc as le,dS as re,dT as ce,dU as de,dV as ue,dW as V,c9 as oe}from"./index-k_85J1DS.js";import{D as me}from"./DirectoryPicker-WPDSBdT6.js";import"./vendor-xterm-DzcZoU0P.js";import"./folder-open-BNQW9dE9.js";const ge=[{id:"fusion-plugin-agent-browser-runtime",name:"Agent Browser Runtime",path:"./plugins/fusion-plugin-agent-browser-runtime",experimental:!0},{id:"fusion-plugin-hermes-runtime",name:"Hermes Runtime",path:"./plugins/fusion-plugin-hermes-runtime",experimental:!0},{id:"fusion-plugin-paperclip-runtime",name:"Paperclip Runtime",path:"./plugins/fusion-plugin-paperclip-runtime"},{id:"fusion-plugin-openclaw-runtime",name:"OpenClaw Runtime",path:"./plugins/fusion-plugin-openclaw-runtime",experimental:!0}],N={started:"var(--color-success)",loaded:"var(--color-warning)",error:"var(--color-error)",stopped:"var(--color-muted)",installed:"var(--color-info)"};function fe({addToast:l,projectId:c}){const[x,b]=o.useState([]),[S,w]=o.useState(!0),[H,f]=o.useState(!1),[j,v]=o.useState(""),[C,P]=o.useState(!1),[h,R]=o.useState(null),[i,y]=o.useState(null),[a,p]=o.useState({}),[J,$]=o.useState(!1),[I,E]=o.useState(null),{confirm:G}=T(),m=o.useCallback(async()=>{try{w(!0);const e=await ee(c);b(e)}catch(e){l(`Failed to load plugins: ${e instanceof Error?e.message:String(e)}`,"error")}finally{w(!1)}},[c,l]);o.useEffect(()=>{m()},[m]);const K=o.useRef([]);K.current=x,o.useEffect(()=>{const e=c?`?projectId=${encodeURIComponent(c)}`:"",n=d=>{try{const t=JSON.parse(d.data);if(c&&t.projectId&&t.projectId!==c)return;switch(t.transition){case"installing":case"enabled":case"disabled":case"settings-updated":b(u=>{const g=u.findIndex(r=>r.id===t.pluginId);if(g>=0){const r=[...u];return r[g]={...r[g],enabled:t.enabled,state:t.state,settings:t.settings,error:t.error},r}else return m(),u});break;case"uninstalled":b(u=>u.filter(g=>g.id!==t.pluginId));break;case"error":b(u=>{const g=u.findIndex(r=>r.id===t.pluginId);if(g>=0){const r=[...u];return r[g]={...r[g],state:t.state,error:t.error},r}return u});break}}catch{}};return se(`/api/events${e}`,{events:{"plugin:lifecycle":n},onReconnect:()=>{m()}})},[c,m]);const z=async()=>{if(!j.trim()){l("Please enter a plugin path","error");return}try{P(!0),await V({path:j},c),l("Plugin installed successfully","success"),f(!1),v(""),await m()}catch(e){l(`Failed to install plugin: ${e instanceof Error?e.message:String(e)}`,"error")}finally{P(!1)}},Q=async e=>{try{E(e.id),await V({path:e.path},c),l(`${e.name} installed successfully`,"success"),await m()}catch(n){l(`Failed to install ${e.name}: ${n instanceof Error?n.message:String(n)}`,"error")}finally{E(null)}},B=async e=>{try{await de(e.id,c),l(`${e.name} enabled`,"success"),await m()}catch(n){l(`Failed to enable plugin: ${n instanceof Error?n.message:String(n)}`,"error")}},F=async e=>{try{await ce(e.id,c),l(`${e.name} disabled`,"success"),await m()}catch(n){l(`Failed to disable plugin: ${n instanceof Error?n.message:String(n)}`,"error")}},U=async e=>{try{R(e.id),await re(e.id,c),l(`${e.name} reloaded`,"success"),await m()}catch(n){l(`Failed to reload plugin: ${n instanceof Error?n.message:String(n)}`,"error")}finally{R(null)}},L=async e=>{if(await G({title:"Uninstall Plugin",message:`Are you sure you want to uninstall "${e.name}"?`,danger:!0}))try{await ue(e.id,c),l(`${e.name} uninstalled`,"success"),await m(),y(null)}catch(d){l(`Failed to uninstall plugin: ${d instanceof Error?d.message:String(d)}`,"error")}},q=async e=>{y(e);try{$(!0);const n=await oe(e.id,c);p(n)}catch{p({})}finally{$(!1)}},W=async()=>{if(i)try{await le(i.id,a,c),l("Settings saved","success")}catch(e){l(`Failed to save settings: ${e instanceof Error?e.message:String(e)}`,"error")}};if(i)return s.jsxs("div",{className:"plugin-manager-detail","data-testid":"plugin-manager-detail",children:[s.jsxs("div",{className:"plugin-manager-detail-header",children:[s.jsx("button",{className:"btn-icon",onClick:()=>y(null),"aria-label":"Back to plugin list",children:s.jsx(O,{size:16})}),s.jsxs("div",{className:"plugin-detail-title",children:[s.jsx("h4",{className:"plugin-detail-name",children:i.name}),s.jsx("span",{className:"plugin-state-badge",style:{color:N[i.state]||N.installed},children:i.state})]})]}),s.jsxs("div",{className:"plugin-detail-content",children:[s.jsxs("div",{className:"plugin-detail-card",children:[i.description&&s.jsx("p",{className:"plugin-description",children:i.description}),i.author&&s.jsxs("p",{className:"plugin-detail-meta-row",children:[s.jsx("span",{className:"text-muted",children:"Author:"}),i.author]}),i.homepage&&s.jsxs("p",{className:"plugin-detail-meta-row plugin-homepage",children:[s.jsx("span",{className:"text-muted",children:"Homepage:"}),s.jsxs("a",{href:i.homepage,target:"_blank",rel:"noopener noreferrer",children:[i.homepage,s.jsx(ne,{size:12})]})]}),s.jsxs("p",{className:"plugin-detail-meta-row",children:[s.jsx("span",{className:"text-muted",children:"Version:"}),i.version]})]}),s.jsxs("div",{className:"plugin-detail-card",children:[s.jsx("h5",{className:"plugin-detail-section-heading",children:"Settings"}),J?s.jsx("p",{className:"text-muted",children:"Loading..."}):i.settingsSchema&&Object.keys(i.settingsSchema).length>0?s.jsxs("div",{className:"plugin-settings-form",children:[Object.entries(i.settingsSchema).map(([e,n])=>{const d=`setting-${e}-help`;return s.jsxs("div",{className:"form-group",children:[s.jsxs("label",{htmlFor:`setting-${e}`,children:[n.label||e,n.required&&" *"]}),n.type==="string"&&!n.multiline&&s.jsx("input",{className:"input",type:"text",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="string"&&n.multiline&&s.jsx("textarea",{className:"input",id:`setting-${e}`,rows:4,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="password"&&s.jsx("input",{className:"input",type:"password",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:n.description,"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="number"&&s.jsx("input",{className:"input",type:"number",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:Number(t.target.value)}),"aria-describedby":n.description&&!n.required?d:void 0}),n.type==="boolean"&&s.jsxs("label",{className:"checkbox-label",children:[s.jsx("input",{type:"checkbox",checked:a[e]??!1,onChange:t=>p({...a,[e]:t.target.checked})}),n.description]}),n.type==="enum"&&s.jsxs("select",{className:"select",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),"aria-describedby":n.description&&!n.required?d:void 0,children:[s.jsx("option",{value:"",children:"Select..."}),n.enumValues?.map(t=>s.jsx("option",{value:t,children:t},t))]}),n.type==="array"&&s.jsxs("div",{className:"plugin-settings-array",children:[a[e]?.map((t,u)=>s.jsxs("div",{className:"plugin-settings-array-item",children:[s.jsx("input",{className:"input",type:n.itemType==="number"?"number":"text",value:t??"",onChange:g=>{const r=g.target.value,k=[...a[e]||[]];k[u]=n.itemType==="number"?Number(r):r,p({...a,[e]:k})}}),s.jsx("button",{className:"btn-icon",onClick:()=>{const r=[...a[e]||[]];r.splice(u,1),p({...a,[e]:r})},"aria-label":"Remove item",children:s.jsx(O,{size:14})})]},u)),s.jsxs("button",{className:"btn btn-secondary",onClick:()=>{const t=a[e]||[],u=n.itemType==="number"?0:"";p({...a,[e]:[...t,u]})},children:[s.jsx(A,{size:14})," Add Item"]})]}),n.description&&!n.required&&!n.multiline&&s.jsx("span",{id:d,className:"form-help",children:n.description})]},e)}),s.jsx("button",{className:"btn btn-primary",onClick:W,children:"Save Settings"})]}):s.jsx("p",{className:"text-muted",children:"No configurable settings."})]}),s.jsxs("div",{className:"plugin-detail-actions",children:[i.state==="started"&&s.jsxs("button",{className:"btn btn-secondary",onClick:()=>U(i),disabled:h===i.id,children:[s.jsx(_,{size:14,className:h===i.id?"spin":""}),h===i.id?"Reloading...":"Reload"]}),i.enabled?s.jsx("button",{className:"btn btn-secondary",onClick:()=>F(i),children:"Disable"}):s.jsx("button",{className:"btn btn-primary",onClick:()=>B(i),children:"Enable"}),s.jsxs("button",{className:"btn btn-danger",onClick:()=>L(i),children:[s.jsx(M,{size:14})," Uninstall"]})]})]})]});const X=new Set(x.map(e=>e.id)),Y=new Map(x.map(e=>[e.id,e])),D=x,Z=()=>s.jsxs("section",{className:"plugin-bundled-runtime-section","aria-label":"Bundled Runtime Plugins",children:[s.jsxs("div",{className:"plugin-bundled-runtime-header",children:[s.jsx("h4",{className:"plugin-bundled-runtime-heading",children:"Bundled Runtime Plugins"}),s.jsx("p",{className:"plugin-bundled-runtime-description",children:"Install Fusion's bundled runtimes directly from this screen."})]}),s.jsx("div",{className:"plugin-bundled-runtime-list","aria-label":"Bundled runtime plugin recommendations",children:ge.map(e=>{const n=X.has(e.id);return s.jsxs("div",{className:"plugin-bundled-runtime-item",children:[s.jsxs("div",{className:"plugin-bundled-runtime-meta",children:[s.jsx("span",{className:"plugin-bundled-runtime-name",children:e.name}),e.experimental&&s.jsx("span",{className:"plugin-bundled-runtime-badge",children:"Experimental"}),s.jsx("span",{className:`plugin-bundled-runtime-status ${n?"plugin-bundled-runtime-status--installed":"plugin-bundled-runtime-status--available"}`,children:n?"Installed":"Not installed"})]}),s.jsx("button",{className:`btn ${n?"btn-secondary":"btn-primary"} btn-sm`,onClick:()=>{if(n){const d=Y.get(e.id);d&&q(d);return}Q(e)},disabled:I===e.id,children:n?"Manage":I===e.id?"Installing...":`Install ${e.name}`})]},e.id)})})]});return s.jsxs("div",{className:"plugin-manager","data-testid":"plugin-manager",children:[s.jsxs("div",{className:"plugin-manager-header",children:[s.jsx("span",{className:"plugin-manager-header-title",children:"Installed Plugins"}),s.jsxs("div",{className:"plugin-manager-actions",children:[s.jsxs("button",{className:"btn btn-sm",onClick:m,title:"Refresh","aria-label":"Refresh plugin list",children:[s.jsx(te,{size:14,className:S?"spin":""}),"Refresh"]}),s.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>f(!0),children:[s.jsx(A,{size:14})," Install"]})]})]}),H&&s.jsxs("div",{className:"plugin-install-form",children:[s.jsxs("p",{className:"plugin-install-hint",children:["Browse to a plugin package root (contains ",s.jsx("code",{children:"manifest.json"}),") or a built ",s.jsx("code",{children:"dist"})," directory."]}),s.jsx(me,{value:j,onChange:v,placeholder:"Absolute path to plugin directory or dist folder",onInputKeyDown:e=>{e.key==="Enter"&&(e.preventDefault(),z())}}),s.jsxs("div",{className:"plugin-install-actions",children:[s.jsx("button",{className:"btn btn-primary",onClick:z,disabled:C||!j.trim(),children:C?"Installing...":"Install Plugin"}),s.jsx("button",{className:"btn btn-secondary",onClick:()=>{f(!1),v("")},children:"Cancel"})]})]}),S?s.jsx("div",{className:"settings-empty-state",children:"Loading plugins..."}):s.jsxs(s.Fragment,{children:[D.length===0?s.jsxs("div",{className:"settings-empty-state",children:[s.jsx(ie,{size:32,className:"text-muted"}),s.jsx("p",{children:"No plugins installed."}),s.jsx("p",{className:"text-muted",children:"Install a plugin to get started, or use a bundled runtime below."})]}):s.jsx("div",{className:"plugin-list",children:D.map(e=>s.jsxs("div",{className:"plugin-item",children:[s.jsxs("div",{className:"plugin-info",children:[s.jsx("span",{className:"plugin-name",children:e.name}),s.jsxs("span",{className:"plugin-version text-muted",children:["v",e.version]}),s.jsx("span",{className:"plugin-state-badge",style:{color:N[e.state]||N.installed},children:e.state})]}),s.jsxs("div",{className:"plugin-actions",children:[e.state==="started"&&s.jsx("button",{className:"btn-icon",onClick:()=>U(e),disabled:h===e.id,title:"Reload",children:s.jsx(_,{size:14,className:h===e.id?"spin":""})}),s.jsxs("label",{className:"toggle-switch",children:[s.jsx("input",{type:"checkbox",checked:e.enabled,onChange:()=>e.enabled?F(e):B(e)}),s.jsx("span",{className:"toggle-slider"})]}),s.jsx("button",{className:"btn-icon",onClick:()=>q(e),title:"Settings",children:s.jsx(ae,{size:14})}),s.jsx("button",{className:"btn-icon",onClick:()=>L(e),title:"Uninstall",children:s.jsx(M,{size:14})})]})]},e.id))}),Z()]})]})}export{fe as PluginManager,N as STATE_COLORS};
@@ -0,0 +1 @@
1
+ import{r as a,j as e}from"./vendor-react-K0fH_qHe.js";import{at as ie,au as ce,s as le,av as oe,aw as de,ax as ue,ay as he,az as me,aA as ve,u as be,aB as xe,E as fe,aC as pe,L as je,S as ge}from"./index-k_85J1DS.js";import"./vendor-xterm-DzcZoU0P.js";const D={webSearch:!0,pageFetch:!0,github:!1,localDocs:!0,llmSynthesis:!0};function W(d){const t=d?.researchGlobalDefaults,u=d?.researchSettings;return{enabled:u?.enabled??d?.researchEnabled??d?.researchGlobalEnabled??!0,searchProvider:u?.searchProvider??t?.searchProvider,synthesisProvider:u?.synthesisProvider??t?.synthesisProvider,synthesisModelId:u?.synthesisModelId??t?.synthesisModelId,enabledSources:{webSearch:u?.enabledSources?.webSearch??t?.enabledSources?.webSearch??D.webSearch,pageFetch:u?.enabledSources?.pageFetch??t?.enabledSources?.pageFetch??D.pageFetch,github:u?.enabledSources?.github??t?.enabledSources?.github??D.github,localDocs:u?.enabledSources?.localDocs??t?.enabledSources?.localDocs??D.localDocs,llmSynthesis:u?.enabledSources?.llmSynthesis??t?.enabledSources?.llmSynthesis??D.llmSynthesis},limits:{maxConcurrentRuns:u?.limits?.maxConcurrentRuns??d?.researchMaxConcurrentRuns??d?.researchGlobalMaxConcurrentRuns??3,maxSourcesPerRun:u?.limits?.maxSourcesPerRun??t?.maxSourcesPerRun??d?.researchMaxSourcesPerRun??d?.researchGlobalMaxSourcesPerRun??20,maxDurationMs:u?.limits?.maxDurationMs??d?.researchDefaultTimeout??d?.researchGlobalDefaultTimeout??3e5,requestTimeoutMs:u?.limits?.requestTimeoutMs??d?.researchFetchTimeoutMs??3e4},defaultExportFormat:t?.defaultExportFormat??"markdown"}}const ye=300,Se=4e3;function _e(d){const t=d?.projectId,[u,x]=a.useState([]),[m,r]=a.useState(null),[I,g]=a.useState(null),[f,S]=a.useState({available:!0}),[R,N]=a.useState(!0),[M,k]=a.useState(null),[y,C]=a.useState(""),_=a.useRef(0),E=a.useRef(0),T=a.useRef(t);a.useEffect(()=>{T.current!==t&&(T.current=t,E.current++)},[t]);const p=a.useCallback(async(c=y)=>{const l=++_.current,i=t;k(null);try{const n=await ie({q:c||void 0,limit:100},i);if(l!==_.current||i!==t)return;x(n.runs),S(n.availability),m&&!n.runs.some(b=>b.id===m)&&(r(null),g(null))}catch(n){if(l!==_.current||i!==t)return;k(n instanceof Error?n.message:"Failed to load research runs")}finally{l===_.current&&N(!1)}},[t,y,m]),j=a.useCallback(async c=>{const l=await ce(c,t);return g(l.run),S(l.availability),l.run},[t]);return a.useEffect(()=>{N(!0);const c=window.setTimeout(()=>{p(y)},ye);return()=>window.clearTimeout(c)},[p,y]),a.useEffect(()=>{if(!m){g(null);return}j(m)},[j,m]),a.useEffect(()=>{const c=E.current,l=()=>E.current!==c,i=t?`?projectId=${encodeURIComponent(t)}`:"";let n=!0;const b=()=>{!n||l()||(p(),m&&j(m))},L=le(`/api/events${i}`,{events:{"research:run:created":b,"research:run:updated":b,"research:run:completed":b,"research:run:failed":b,"research:run:cancelled":b},onReconnect:b}),q=window.setInterval(b,Se);return()=>{n=!1,L(),window.clearInterval(q)}},[t,p,m,j]),{runs:u,selectedRun:I,selectedRunId:m,setSelectedRunId:r,availability:f,loading:R,error:M,searchQuery:y,setSearchQuery:C,refresh:p,createRun:c=>ve(c,t),cancelRun:async c=>{const l=await me(c,t);return m===c&&g(l.run),await p(),l},retryRun:async c=>{const l=await he(c,t);return m===c&&g(l.run),await p(),l},exportRun:(c,l)=>ue(c,l,t),createTaskFromRun:(c,l,i,n,b,L)=>de(c,{title:l,findingId:i,description:n,priority:b,attachExport:L},t),attachRunToTask:(c,l,i,n)=>oe(c,{taskId:l,findingId:i,attachExport:n},t),statusCounts:u.reduce((c,l)=>(c[l.status]+=1,c),{queued:0,running:0,cancelling:0,retry_waiting:0,completed:0,failed:0,cancelled:0,timed_out:0,retry_exhausted:0})}}function Re({open:d,mode:t,run:u,finding:x,projectId:m,onClose:r,onConfirm:I}){be(d);const[g,f]=a.useState(!1),[S,R]=a.useState(""),[N,M]=a.useState(""),[k,y]=a.useState("normal"),[C,_]=a.useState(""),[E,T]=a.useState([]),[p,j]=a.useState(!1),[c,l]=a.useState(!1),i=a.useMemo(()=>{const n=(x.content??"").split(/(?<=[.!?])\s+/)[0]??"";return`${x.heading||"Research finding"} — ${n}`.trim()},[x.content,x.heading]);return a.useEffect(()=>{d&&(f(!1),R(`Research: ${x.heading||u.title}`),M(i),y("normal"),_(""),t==="enrich"&&(j(!0),xe(50,0,m).then(n=>T(n.filter(b=>b.column!=="archived"))).finally(()=>j(!1))))},[d,t,m,x.heading,i,u.title]),d?e.jsx("div",{className:"modal-overlay open",role:"presentation",onClick:r,children:e.jsxs("div",{className:"modal modal-lg research-task-action-modal",role:"dialog","aria-modal":"true",onClick:n=>n.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:t==="create"?"Create task from finding":"Enrich existing task"}),e.jsx("button",{className:"modal-close",type:"button","aria-label":"Close",onClick:r,children:"×"})]}),e.jsxs("div",{className:"research-task-action-modal__body",children:[e.jsxs("div",{className:"card research-task-action-modal__preview",children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Run:"})," ",u.id]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Finding:"})," ",x.id,x.heading?` — ${x.heading}`:""]}),e.jsx("p",{children:i||"No preview available."})]}),t==="create"?e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"research-task-action-modal__field",children:["Title",e.jsx("input",{className:"input",value:S,onChange:n=>R(n.target.value)})]}),e.jsxs("label",{className:"research-task-action-modal__field",children:["Description",e.jsx("textarea",{className:"input research-task-action-modal__textarea",value:N,onChange:n=>M(n.target.value)})]}),e.jsxs("label",{className:"research-task-action-modal__field",children:["Priority",e.jsxs("select",{className:"select",value:k,onChange:n=>y(n.target.value),children:[e.jsx("option",{value:"low",children:"Low"}),e.jsx("option",{value:"normal",children:"Normal"}),e.jsx("option",{value:"high",children:"High"}),e.jsx("option",{value:"urgent",children:"Urgent"})]})]})]}):e.jsxs("label",{className:"research-task-action-modal__field",children:["Target task",e.jsx("input",{className:"input",list:"research-task-action-task-list",value:C,placeholder:p?"Loading tasks…":"Enter task ID",onChange:n=>_(n.target.value)}),e.jsx("datalist",{id:"research-task-action-task-list",children:E.map(n=>e.jsx("option",{value:n.id,children:n.title},n.id))})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:g,onChange:n=>f(n.target.checked)}),e.jsx("span",{children:"Attach markdown export artifact"})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",type:"button",onClick:r,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",type:"button",disabled:c||t==="enrich"&&!C,onClick:()=>{l(!0),I({taskId:t==="enrich"?C:void 0,title:t==="create"?S.trim():void 0,description:t==="create"?N.trim():void 0,priority:t==="create"?k:void 0,attachExport:g}).finally(()=>l(!1))},children:t==="create"?"Create Task":"Enrich Task"})]})]})}):null}const we=["web-search","page-fetch","github","local-docs","llm-synthesis"],Ne={"web-search":"webSearch","page-fetch":"pageFetch",github:"github","local-docs":"localDocs","llm-synthesis":"llmSynthesis"},ke={"web-search":"Web Search","page-fetch":"Page Fetch",github:"GitHub","local-docs":"Local Docs","llm-synthesis":"LLM Synthesis"};function Te({projectId:d,addToast:t,onOpenSettings:u,readinessVersion:x=0}){const{runs:m,selectedRun:r,selectedRunId:I,setSelectedRunId:g,availability:f,loading:S,error:R,searchQuery:N,setSearchQuery:M,createRun:k,cancelRun:y,retryRun:C,exportRun:_,createTaskFromRun:E,attachRunToTask:T,statusCounts:p,refresh:j}=_e({projectId:d}),[c,l]=a.useState(""),[i,n]=a.useState(()=>W(void 0)),[b,L]=a.useState([]),[q,Q]=a.useState(!1),[Y,X]=a.useState([]),[A,O]=a.useState(null),[P,$]=a.useState(null),B=f.supportedProviders??we,U=s=>i.enabledSources[Ne[s]];a.useEffect(()=>{const s=B.filter(o=>U(o));X(o=>{const v=o.filter(h=>s.includes(h));return v.length>0?v:s})},[i.enabledSources,B]),a.useEffect(()=>{let s=!1;return Promise.all([fe(d),pe().catch(()=>({providers:[]}))]).then(([o,v])=>{s||(n(W(o)),L(v.providers.filter(h=>h.type==="api_key").map(h=>({id:h.id,authenticated:h.authenticated}))))}).catch(()=>{s||n(W(void 0))}),()=>{s=!0}},[d,x]);const re=a.useMemo(()=>r?r.status:"No run selected",[r]),ae=a.useMemo(()=>r?r.status==="queued"||r.status==="retry_waiting"?"status-dot status-dot--pending":r.status==="running"?"status-dot status-dot--connecting":r.status==="completed"?"status-dot status-dot--online":r.status==="failed"||r.status==="cancelled"?"status-dot status-dot--error":"status-dot":"status-dot",[r]),H=f.supportedExportFormats??["markdown","json","html"],V=i.searchProvider,Z=i.enabledSources.webSearch&&!V,ee=i.enabledSources.llmSynthesis&&(!i.synthesisProvider||!i.synthesisModelId),z=a.useMemo(()=>new Map(b.map(s=>[s.id,s.authenticated])),[b]),K=a.useMemo(()=>{const s=new Set;return i.enabledSources.webSearch&&V&&s.add(V),i.enabledSources.llmSynthesis&&i.synthesisProvider&&s.add(i.synthesisProvider),[...s].filter(o=>z.has(o))},[i.enabledSources.llmSynthesis,i.enabledSources.webSearch,i.synthesisProvider,V,z]).find(s=>z.get(s)!==!0),F=a.useMemo(()=>f.available?i.enabled?Z||ee?{reason:"Research defaults are incomplete.",details:"Select the required provider/model defaults in Research settings.",settingsSection:"research-global"}:K?{reason:`Missing API key for ${K}.`,details:"Add provider credentials in Authentication settings.",settingsSection:"authentication"}:null:{reason:"Research is disabled for this project.",details:"Enable project research settings to create runs.",settingsSection:"research-project"}:{reason:f.reason??"Research is unavailable for this project.",details:f.setupInstructions,settingsSection:"research-project"},[f.available,f.reason,f.setupInstructions,i.enabled,K,Z,ee]),G=async(s,o,v)=>{O(s);try{await o(),t?.(v,"success"),await j()}catch(h){t?.(h instanceof Error?h.message:"Action failed","error")}finally{O(null)}},J=async s=>{if(r){O(`export-${s}`);try{const o=await _(r.id,s),v=new Blob([o.content],{type:"text/plain;charset=utf-8"}),h=URL.createObjectURL(v),w=document.createElement("a");w.href=h,w.download=o.filename,document.body.appendChild(w),w.click(),w.remove(),URL.revokeObjectURL(h),t?.(`Exported ${o.filename}`,"success")}catch(o){t?.(o instanceof Error?o.message:"Export failed","error")}finally{O(null)}}},ne=async()=>{if(c.trim()){Q(!0);try{const s=Y.filter(v=>U(v));if(s.length===0){Q(!1),t?.("No enabled research sources are available for this project.","error");return}const o=await k({query:c.trim(),providers:s});g(o.run.id),l(""),t?.("Research run created","success"),await j()}catch(s){t?.(s instanceof Error?s.message:"Failed to create run","error")}finally{Q(!1)}}};return e.jsxs("section",{className:"research-view","aria-label":"Research view",children:[e.jsxs("header",{className:"research-view__header",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"research-view__title",children:"Research"}),e.jsx("p",{className:"research-view__subtitle",children:"Create and track research runs with cited findings."})]}),e.jsx("button",{className:"btn",type:"button",onClick:()=>void j(),children:"Refresh"})]}),F?e.jsxs("div",{className:"research-view__state research-view__state--error card","data-testid":"research-state-unavailable",children:[e.jsx("p",{children:F.reason}),F.details&&e.jsx("p",{children:F.details}),e.jsxs("p",{children:["Current defaults: provider ",i.searchProvider??"(not set)",", max sources ",i.limits.maxSourcesPerRun]}),e.jsxs("div",{className:"research-view__actions",children:[e.jsx("button",{className:"btn",type:"button",onClick:()=>void j(),children:"Refresh"}),e.jsx("button",{className:"btn btn-primary",type:"button",onClick:()=>u?.(F.settingsSection),children:"Open Settings"})]})]}):e.jsxs("div",{className:"research-view__layout",children:[e.jsxs("aside",{className:"research-view__sidebar card",children:[e.jsxs("div",{className:"research-view__form",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"research-query",children:"Query"}),e.jsx("textarea",{id:"research-query",className:"input research-view__textarea",value:c,onChange:s=>l(s.target.value)})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Providers"}),e.jsx("div",{className:"research-view__providers",children:B.map(s=>e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:Y.includes(s),disabled:!U(s),onChange:()=>{U(s)&&X(o=>o.includes(s)?o.filter(v=>v!==s):[...o,s])}}),e.jsx("span",{children:ke[s]??s})]},s))})]}),e.jsxs("button",{className:"btn btn-primary",type:"button",disabled:!c.trim()||q,onClick:()=>void ne(),children:[q?e.jsx(je,{className:"animate-spin",size:14}):null,"Create Run"]})]}),e.jsxs("div",{className:"research-view__history-header form-group",children:[e.jsx("label",{htmlFor:"research-run-search",children:"Search"}),e.jsxs("div",{className:"research-view__history-search-row",children:[e.jsx(ge,{size:14}),e.jsx("input",{id:"research-run-search",className:"input",placeholder:"Search runs",value:N,onChange:s=>M(s.target.value)})]})]}),e.jsx("div",{className:"research-view__history","data-testid":"research-state-running",children:m.map(s=>e.jsxs("button",{type:"button",className:`research-view__history-item card${I===s.id?" research-view__history-item--active":""}`,onClick:()=>g(s.id),children:[e.jsx("span",{className:"card-id",children:s.id}),e.jsx("span",{children:s.title})]},s.id))})]}),e.jsxs("div",{className:"research-view__reader card",children:[S&&e.jsx("p",{"data-testid":"research-state-loading",children:"Loading research runs…"}),!S&&R&&e.jsx("p",{"data-testid":"research-state-error",children:R}),!S&&!R&&m.length===0&&e.jsx("p",{"data-testid":"research-state-empty",children:"No research runs yet"}),r&&e.jsxs("div",{children:[e.jsxs("div",{className:"research-view__status-row",children:[e.jsx("span",{className:ae}),e.jsx("strong",{children:re})]}),e.jsx("h3",{className:"research-view__run-title",children:r.title}),e.jsx("p",{className:"research-view__run-query",children:r.query}),e.jsx("p",{className:"research-view__run-summary","data-testid":"research-state-results",children:r.results?.summary??"No summary yet."}),e.jsxs("div",{className:"research-view__actions",children:[e.jsx("button",{className:"btn",type:"button",disabled:A==="cancel",onClick:()=>void G("cancel",()=>y(r.id),"Run cancelled"),children:"Cancel"}),e.jsx("button",{className:"btn",type:"button",disabled:A==="retry",onClick:()=>void G("retry",()=>C(r.id),"Run retried"),children:"Retry"}),H.includes("markdown")&&e.jsx("button",{className:"btn",type:"button",disabled:A==="export-markdown",onClick:()=>void J("markdown"),children:"Export MD"}),H.includes("json")&&e.jsx("button",{className:"btn",type:"button",disabled:A==="export-json",onClick:()=>void J("json"),children:"Export JSON"}),H.includes("html")&&e.jsx("button",{className:"btn",type:"button",disabled:A==="export-html",onClick:()=>void J("html"),children:"Export HTML"})]}),r.error&&e.jsx("p",{className:"research-view__error",children:r.error}),Array.isArray(r.results?.findings)&&r.results.findings.length>0&&e.jsx("div",{className:"research-view__findings",children:r.results.findings.map((s,o)=>{const h=s.id?.trim()||`finding-${o+1}`;return e.jsxs("article",{className:"research-view__finding card",children:[e.jsx("h4",{children:s.heading}),e.jsx("p",{children:s.content}),e.jsxs("div",{className:"research-view__actions research-view__finding-actions",children:[e.jsx("button",{className:"btn btn-primary btn-sm",type:"button",onClick:()=>$({mode:"create",findingId:h}),children:"Create Task"}),e.jsx("button",{className:"btn btn-sm",type:"button",onClick:()=>$({mode:"enrich",findingId:h}),children:"Enrich Task"})]})]},h)})}),Array.isArray(r.results?.citations)&&r.results.citations.length>0&&e.jsx("ul",{className:"research-view__citations",children:r.results.citations.map(s=>e.jsx("li",{children:e.jsx("a",{href:s,target:"_blank",rel:"noreferrer",children:s})},s))}),r.events.length>0&&e.jsxs("details",{children:[e.jsx("summary",{children:"Run history"}),e.jsx("ul",{className:"research-view__events",children:r.events.map(s=>e.jsx("li",{children:s.message},s.id))})]})]}),!r&&m.length>0&&e.jsx("p",{children:"Select a run to view details."}),e.jsxs("div",{className:"research-view__stats",children:[e.jsxs("div",{className:"research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Running"}),e.jsx("div",{className:"research-view__stat-value",children:p.running})]}),e.jsxs("div",{className:"research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Completed"}),e.jsx("div",{className:"research-view__stat-value",children:p.completed})]}),e.jsxs("div",{className:"research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Failed"}),e.jsx("div",{className:"research-view__stat-value",children:p.failed})]})]})]})]}),r&&P&&(()=>{const s=r.results?.findings?.findIndex((v,h)=>(v.id?.trim()||`finding-${h+1}`)===P.findingId)??-1,o=s>=0?r.results.findings[s]:null;return o?e.jsx(Re,{open:!0,mode:P.mode,run:r,finding:{id:P.findingId,heading:o.heading,content:o.content},projectId:d,onClose:()=>$(null),onConfirm:async({taskId:v,title:h,description:w,priority:se,attachExport:te})=>{P.mode==="create"?await G("create-task",()=>E(r.id,h,P.findingId,w,se,te),"Task created from research"):v&&await G("attach-task",()=>T(r.id,v,P.findingId,te),"Task enriched from research"),$(null)}}):null})()]})}export{Te as ResearchView};