@runfusion/fusion 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/bin.js +6505 -2184
  2. package/dist/client/assets/AgentDetailView-C1XceMgi.js +18 -0
  3. package/dist/client/assets/AgentDetailView-CeO_1MK7.css +1 -0
  4. package/dist/client/assets/AgentsView-DSGQWObq.css +1 -0
  5. package/dist/client/assets/AgentsView-Deh125ss.js +527 -0
  6. package/dist/client/assets/ChatView-7D_RQDqT.js +1 -0
  7. package/dist/client/assets/DevServerView-C9lzHrcT.js +1 -0
  8. package/dist/client/assets/DirectoryPicker-aVdFaV37.js +1 -0
  9. package/dist/client/assets/DocumentsView-DIpg3NSP.js +1 -0
  10. package/dist/client/assets/{InsightsView-CqDethVs.js → InsightsView-jKjEFAx_.js} +2 -2
  11. package/dist/client/assets/{MemoryView-BLIm9Vr7.js → MemoryView-nXlTqebk.js} +2 -2
  12. package/dist/client/assets/{NodesView-DEXvp3WT.js → NodesView-Di2SvOhg.js} +3 -3
  13. package/dist/client/assets/{PiExtensionsManager-C2YjI9o2.js → PiExtensionsManager-Buopv-jb.js} +2 -2
  14. package/dist/client/assets/PluginManager-B9-NbQ8f.js +1 -0
  15. package/dist/client/assets/PluginManager-C1DbPaar.css +1 -0
  16. package/dist/client/assets/ResearchView-_BHXUv2j.js +1 -0
  17. package/dist/client/assets/{RoadmapsView-DPcfX5MS.js → RoadmapsView-DHWjUoc8.js} +2 -2
  18. package/dist/client/assets/{SettingsModal-BRNAPR1u.js → SettingsModal-C89Ikhfm.js} +1 -1
  19. package/dist/client/assets/SettingsModal-DHitIpsa.css +1 -0
  20. package/dist/client/assets/SettingsModal-DR_yirvK.js +31 -0
  21. package/dist/client/assets/SetupWizardModal-BtDMY9pa.js +1 -0
  22. package/dist/client/assets/SkillsView-hDpTBdFT.js +1 -0
  23. package/dist/client/assets/agentSkills-B-w5wFHh.js +1 -0
  24. package/dist/client/assets/agentSkills-DDHJnrkn.css +1 -0
  25. package/dist/client/assets/folder-open-usZkXdq2.js +6 -0
  26. package/dist/client/assets/index-Bc6ZdGMz.css +1 -0
  27. package/dist/client/assets/index-D__RMku8.js +694 -0
  28. package/dist/client/assets/{star-B314SwLA.js → star-BAT_ObKE.js} +2 -2
  29. package/dist/client/assets/upload-BC2YKNEV.js +6 -0
  30. package/dist/client/assets/{users-Bu_ltePs.js → users-Dkd4rtrN.js} +2 -2
  31. package/dist/client/index.html +2 -2
  32. package/dist/client/version.json +1 -1
  33. package/dist/droid-cli/package.json +1 -1
  34. package/dist/extension.js +4411 -932
  35. package/dist/pi-claude-cli/package.json +1 -1
  36. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  37. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.css +12 -3
  38. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/storage.test.ts +12 -2
  39. package/dist/plugins/fusion-plugin-dependency-graph/src/storage.ts +6 -7
  40. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +649 -0
  41. package/dist/plugins/fusion-plugin-hermes-runtime/manifest.json +14 -0
  42. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +11 -0
  43. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +369 -0
  44. package/dist/plugins/fusion-plugin-openclaw-runtime/manifest.json +14 -0
  45. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +11 -0
  46. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +966 -0
  47. package/dist/plugins/fusion-plugin-paperclip-runtime/manifest.json +15 -0
  48. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +11 -0
  49. package/package.json +2 -1
  50. package/skill/fusion/references/engine-tools.md +6 -3
  51. package/dist/client/assets/AgentDetailView-CUtWvXBn.css +0 -1
  52. package/dist/client/assets/AgentDetailView-Dg7Qa1rG.js +0 -18
  53. package/dist/client/assets/ChatView-ODq-kBk6.js +0 -1
  54. package/dist/client/assets/DevServerView-6PS9Lvl7.js +0 -1
  55. package/dist/client/assets/DirectoryPicker-B3dza2Dq.js +0 -1
  56. package/dist/client/assets/DocumentsView-Bu9YYlki.js +0 -1
  57. package/dist/client/assets/PluginManager-DA_T0GHn.css +0 -1
  58. package/dist/client/assets/PluginManager-Dnf-LhYw.js +0 -1
  59. package/dist/client/assets/ResearchView-Z0TZ7WGo.js +0 -1
  60. package/dist/client/assets/SettingsModal-B6RN9VYe.js +0 -31
  61. package/dist/client/assets/SettingsModal-BWe0KrGY.css +0 -1
  62. package/dist/client/assets/SetupWizardModal-BFc3xID2.js +0 -1
  63. package/dist/client/assets/SkillsView-CipGahOR.js +0 -1
  64. package/dist/client/assets/index-Df1bHDY4.css +0 -1
  65. package/dist/client/assets/index-NFptaeUQ.js +0 -1222
@@ -1,4 +1,4 @@
1
- import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{h as Ie,W as os,Y as cs,Z as is,_ as ds,$ as Ve,a0 as ye,a1 as Ke,a2 as Fe,a3 as us,a4 as Oe,a5 as Ue,a6 as ms,a7 as ve,N as hs,R as _e,C as we,P as me,a8 as Be,a9 as Te,aa as fs,ab as Le,X as ue,E as gs,d as xs,ac as $e,F as ps,ad as De,ae as js,af as bs,ag as ys,ah as vs,ai as _s,aj as Ns}from"./index-NFptaeUQ.js";import"./vendor-xterm-DzcZoU0P.js";/**
1
+ import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{c as Ie,aJ as os,aK as cs,aL as is,aM as ds,aN as Ve,aO as ye,aP as Ke,A as Fe,aQ as us,$ as Oe,aR as Ue,aS as ms,a1 as ve,u as hs,R as _e,a as we,Q as me,aT as Te,aU as fs,aV as Le,X as ue,ad as gs,ae as xs,aW as $e,F as ps,aX as De,aY as js,aZ as bs,a_ as ys,a$ as vs,b0 as _s,G as Ns}from"./index-D__RMku8.js";import{U as Be}from"./upload-BC2YKNEV.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.
@@ -8,7 +8,7 @@ import{r as s,j as e}from"./vendor-react-K0fH_qHe.js";import{h as Ie,W as os,Y a
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 Ss=[["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"}]],ws=Ie("wifi",Ss);function pe(t){const{lastSyncAt:n,remoteReachable:a,diff:i}=t,u=i.global.length+i.project.length;return n===null?{syncState:"never-synced",lastSyncAt:n,diffCount:0}:a?u>0?{syncState:"diff",lastSyncAt:n,diffCount:u}:{syncState:"synced",lastSyncAt:n,diffCount:0}:{syncState:"error",lastSyncAt:n,diffCount:u}}function Ne(t){if(t===null)return"Never synced";const n=new Date(t);if(Number.isNaN(n.getTime()))return"Never synced";const i=Date.now()-n.getTime(),u=Math.floor(i/1e3),g=Math.floor(u/60),o=Math.floor(g/60),p=Math.floor(o/24);return g<1?"Synced just now":g<60?`Synced ${g}m ago`:o<24?`Synced ${o}h ago`:`Synced ${p}d ago`}function Ms(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 Ps=3e4;function Es(){const[t,n]=s.useState({}),[a,i]=s.useState(!1),[u,g]=s.useState({}),[o,p]=s.useState(null),d=s.useRef(new Set),b=s.useRef(!1),N=s.useRef(null),P=s.useRef(null),y=s.useCallback(async(x,m)=>{try{const C=await os(x);n(T=>({...T,[x]:C})),p(null)}catch(C){console.error(`Failed to fetch sync status for node ${x}:`,C),p(C instanceof Error?C.message:"Failed to fetch sync status")}},[]),c=s.useCallback(async()=>{const x=Array.from(d.current);if(x.length===0)return;N.current&&N.current.abort(),N.current=new AbortController;const m=!b.current;m&&i(!0),p(null);try{const C=await Promise.allSettled(x.map(H=>y(H,m)));b.current=!0,C.filter(H=>H.status==="rejected").length>0&&p("Some sync status requests failed")}catch(C){if(C instanceof Error&&C.name==="AbortError")return;p(C instanceof Error?C.message:"Failed to fetch sync status"),b.current=!0}finally{i(!1)}},[y]),w=s.useCallback(()=>{P.current&&clearInterval(P.current),P.current=setInterval(()=>{c()},Ps)},[c]),v=s.useCallback(()=>{P.current&&(clearInterval(P.current),P.current=null)},[]);s.useEffect(()=>(c(),w(),()=>{v(),N.current&&N.current.abort()}),[c,w,v]);const l=s.useCallback(x=>{d.current.has(x)||(d.current.add(x),y(x,!b.current))},[y]),j=s.useCallback(x=>{d.current.delete(x),n(m=>{const C={...m};return delete C[x],C}),d.current.size===0&&v()},[v]),_=s.useCallback(async x=>{g(m=>({...m,[x]:!0})),p(null);try{const m=await cs(x);return y(x,!1),!m.success&&m.error&&p(m.error),m}catch(m){const C=m instanceof Error?m.message:"Push settings failed";throw p(C),m}finally{g(m=>{const C={...m};return delete C[x],C})}},[y]),k=s.useCallback(async x=>{g(m=>({...m,[x]:!0})),p(null);try{const m=await is(x);return y(x,!1),!m.success&&m.error&&p(m.error),m}catch(m){const C=m instanceof Error?m.message:"Pull settings failed";throw p(C),m}finally{g(m=>{const C={...m};return delete C[x],C})}},[y]),L=s.useCallback(async x=>{g(m=>({...m,[x]:!0})),p(null);try{return await ds(x)}catch(m){const C=m instanceof Error?m.message:"Auth sync failed";throw p(C),m}finally{g(m=>{const C={...m};return delete C[x],C})}},[]),E=s.useCallback(x=>t[x]?.authMatch,[t]),V=s.useCallback(x=>t[x]?.authDiff,[t]);return{syncStatusMap:t,loading:a,actionLoading:u,error:o,refresh:c,trackNode:l,untrackNode:j,pushSettings:_,pullSettings:k,syncAuth:L,getAuthSyncState:E,getAuthProviders:V}}function Rs(t,n){return n.type==="remote"?t.nodeId===n.id:t.nodeId===n.id||t.nodeId===void 0||t.nodeId===null}function He(t,n){return t.filter(a=>Rs(a,n))}function ke(t,n){return He(t,n).length}const de={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"},creating:{label:"Creating",color:"var(--color-warning)",className:"node-card__status--creating"},recreating:{label:"Recreating",color:"var(--color-warning)",className:"node-card__status--recreating"},deleting:{label:"Deleting",color:"var(--color-error)",className:"node-card__status--deleting"},running:{label:"Running",color:"var(--color-success)",className:"node-card__status--online"},stopped:{label:"Stopped",color:"var(--color-error)",className:"node-card__status--offline"},exited:{label:"Exited",color:"var(--color-error)",className:"node-card__status--offline"}},As={match:"var(--color-success)",differs:"var(--color-warning)","not-synced":"var(--text-muted)"};function Ls(t,n){if(t==="match")return"Auth credentials match";if(t==="not-synced")return"Auth not synced";if(n&&Object.keys(n).length>0){const a=Object.entries(n).filter(([,i])=>i==="differs").map(([i])=>i);if(a.length>0)return`Auth credentials differ: ${a.join(", ")}`}return"Auth credentials differ"}function $s(t,n=42){return t.length<=n?t:`${t.slice(0,n-3)}...`}function Ds(t,n){const a=t.node,i=n.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!==n.isLoading)return!1;const u=t.managedDockerNode,g=n.managedDockerNode;if(!!u!=!!g||u&&g&&(u.id!==g.id||u.status!==g.status||u.imageTag!==g.imageTag||u.updatedAt!==g.updatedAt))return!1;const o=t.syncStatus,p=n.syncStatus;if(!(!o&&!p)){if(!o||!p)return!1;if(o.syncState!==p.syncState||o.lastSyncAt!==p.lastSyncAt||o.diffCount!==p.diffCount)return!1}if(t.authSyncState!==n.authSyncState)return!1;const d=t.authSyncProviders,b=n.authSyncProviders;if(d!==b){if(!d||!b)return!1;{const y=Object.keys(d),c=Object.keys(b);if(y.length!==c.length||y.some(w=>d[w]!==b[w]))return!1}}const N=ke(t.projects,a),P=ke(n.projects,i);return N===P}function zs({node:t,projects:n,onHealthCheck:a,onEdit:i,onRemove:u,isLoading:g=!1,syncStatus:o,authSyncState:p,authSyncProviders:d,managedDockerNode:b}){const[N,P]=s.useState(!1),y=de[t.status]??de.offline,c=b?de[b.status]??de.error:null,w=b?.hostConfig.type==="remote"?`Remote: ${b.hostConfig.host??"unknown"}`:"Local Docker",v=s.useMemo(()=>ke(n,t),[n,t]),l=s.useCallback(()=>{i(t)},[i,t]),j=s.useCallback(E=>{E.stopPropagation(),a(t.id)},[a,t.id]),_=s.useCallback(E=>{E.stopPropagation(),i(t)},[i,t]),k=s.useCallback(E=>{if(E.stopPropagation(),!N){P(!0);return}u(t.id),P(!1)},[N,u,t.id]),L=s.useCallback(E=>{(E.key==="Enter"||E.key===" ")&&(E.preventDefault(),i(t))},[i,t]);return e.jsxs("article",{className:`node-card ${g?"node-card--loading":""}`,"data-node-id":t.id,role:"button",tabIndex:0,onClick:l,onKeyDown:L,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(Ve,{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"}),b&&e.jsxs("span",{className:"node-card__docker-badge",title:"Managed Docker node",children:[e.jsx(ye,{size:12,"aria-hidden":!0}),"Docker"]}),e.jsxs("span",{className:`node-card__status ${y.className}`,style:{color:y.color},"data-status":t.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:y.color},"aria-hidden":!0}),y.label]}),b&&c&&e.jsxs("span",{className:`node-card__status ${c.className}`,style:{color:c.color},"data-status":b.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:c.color},"aria-hidden":!0}),c.label]}),t.type==="remote"&&p&&e.jsx("span",{className:`node-card__auth-indicator node-card__auth-indicator--${p}`,title:Ls(p,d),"aria-label":`Auth sync: ${p==="match"?"credentials match":p==="differs"?"credentials differ":"not synced"}`,style:{color:As[p]},children:e.jsx(Ke,{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:$s(t.url)}),b&&e.jsxs("div",{className:"node-card__docker-meta",children:[e.jsxs("span",{title:`${b.imageName}:${b.imageTag}`,children:[b.imageName,":",b.imageTag]}),e.jsx("span",{title:w,children:w})]}),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:v})]}),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:Ms(o.syncState)},"aria-hidden":!0}),e.jsx("span",{className:"node-card__sync-time",children:Ne(o.lastSyncAt)})]})]}),e.jsxs("footer",{className:"node-card__actions",children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:g,"aria-label":"Run node health check",title:"Health Check",children:[e.jsx(Fe,{size:14}),e.jsx("span",{children:"Health"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:_,disabled:g,"aria-label":"Edit node",title:"Edit",children:[e.jsx(us,{size:14}),e.jsx("span",{children:"Edit"})]}),b&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Start node container",title:"Available after FN-3113",children:[e.jsx(Oe,{size:14}),e.jsx("span",{children:"Start"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Stop node container",title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),e.jsx("span",{children:"Stop"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Restart node container",title:"Available after FN-3113",children:[e.jsx(ms,{size:14}),e.jsx("span",{children:"Restart"})]})]}),e.jsxs("button",{className:`btn btn-sm node-card__action node-card__action--remove ${N?"btn-danger is-armed":""}`,type:"button",onClick:k,disabled:g,"aria-label":N?"Confirm remove node":"Remove node",title:N?"Confirm remove":"Remove",children:[e.jsx(ve,{size:14}),e.jsx("span",{children:N?"Confirm":"Remove"})]})]})]})}const Is=s.memo(zs,Ds),ce={online:"var(--success, var(--color-success))",offline:"var(--text-dim)",connecting:"var(--triage)",error:"var(--color-error)"},le=28,ze=12,Vs=300,Ks=120;function Fs({nodes:t,className:n}){const a=s.useMemo(()=>t.find(d=>d.type==="local")??t[0],[t]),i=s.useMemo(()=>t.filter(d=>d.type==="remote"),[t]),u=s.useMemo(()=>{const d=Vs,b=Math.max(0,i.length-4)*20;return d+b},[i.length]),g=u/2,o=u/2,p=s.useMemo(()=>{if(i.length===0)return[];const d=Math.min(Ks,u/2-le-10),b=2*Math.PI/i.length,N=-Math.PI/2;return i.map((P,y)=>{const c=N+y*b;return{node:P,x:g+d*Math.cos(c),y:o+d*Math.sin(c)}})},[i,u,g,o]);return t.length===0?e.jsx("div",{className:`mesh-topology mesh-topology--empty ${n??""}`,children:e.jsx("div",{className:"mesh-topology__empty-state",children:e.jsx("p",{children:"No nodes to display"})})}):e.jsxs("div",{className:`mesh-topology ${n??""}`,children:[e.jsxs("svg",{className:"mesh-topology__svg",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet","aria-label":"Node mesh topology visualization",children:[p.map(d=>e.jsx("line",{className:"mesh-topology__link",x1:g,y1:o,x2:d.x,y2:d.y},`link-${d.node.id}`)),a&&e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${g}, ${o})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:le,fill:ce[a.status],"aria-label":`${a.name} (${a.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:le+ze,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 ${-le-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"})]})]}),p.map(d=>e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${d.x}, ${d.y})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:le,fill:ce[d.node.status],"aria-label":`${d.node.name} (${d.node.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:le+ze,textAnchor:"middle",children:d.node.name.length>12?`${d.node.name.slice(0,10)}…`:d.node.name}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-le-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:d.node.type==="local"?"L":"R"})]})]},d.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:ce.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:ce.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:ce.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:ce.error}}),e.jsx("span",{children:"Error"})]})]}),e.jsx("p",{className:"mesh-topology__notice",children:"Peer-to-peer discovery data unavailable."})]})}const Os=s.memo(Fs),Ce=1,Se=10;function Us(t){const n={};return t.name.trim()||(n.name="Name is required"),t.type==="remote"&&!t.url?.trim()&&(n.url="URL is required for remote nodes"),(!Number.isFinite(t.maxConcurrent)||t.maxConcurrent<Ce||t.maxConcurrent>Se)&&(n.maxConcurrent=`Concurrency must be between ${Ce} and ${Se}`),n}function Bs({isOpen:t,onClose:n,onSubmit:a,addToast:i}){hs(t);const[u,g]=s.useState(""),[o,p]=s.useState("local"),[d,b]=s.useState(""),[N,P]=s.useState(""),[y,c]=s.useState(2),[w,v]=s.useState("auto-generate"),[l,j]=s.useState({}),[_,k]=s.useState(!1),L=s.useCallback(()=>{g(""),p("local"),b(""),P(""),c(2),v("auto-generate"),j({}),k(!1)},[]),E=s.useCallback(()=>{_||(L(),n())},[_,n,L]);s.useEffect(()=>{if(!t){L();return}const m=C=>{C.key==="Escape"&&(C.preventDefault(),E())};return document.addEventListener("keydown",m),()=>{document.removeEventListener("keydown",m)}},[E,t,L]);const V=s.useMemo(()=>({name:u.trim(),type:o,url:o==="remote"&&d.trim()||void 0,apiKey:o==="remote"&&w==="provide"&&N||void 0,maxConcurrent:y,apiKeyMode:w}),[N,w,y,u,o,d]),x=s.useCallback(async()=>{if(_)return;const m=Us(V);if(j(m),!(Object.keys(m).length>0)){k(!0);try{await a(V),i(`Node "${V.name}" registered`,"success"),E()}catch(C){const T=C instanceof Error?C.message:"Failed to register node";i(T,"error")}finally{k(!1)}}},[i,E,V,_,a]);return t?e.jsx("div",{className:"modal-overlay open",onClick:E,children:e.jsxs("div",{className:"modal modal-md add-node-modal",onClick:m=>m.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:_,"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:m=>g(m.target.value),placeholder:"Build Machine",disabled:_,"aria-invalid":!!l.name,autoFocus:!0}),l.name&&e.jsx("span",{className:"form-error add-node-modal__error",children:l.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:()=>p("local"),disabled:_,"aria-pressed":o==="local",children:"Local"}),e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${o==="remote"?"active":""}`,"data-type":"remote",onClick:()=>p("remote"),disabled:_,"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:d,onChange:m=>b(m.target.value),placeholder:"https://node.example.com",disabled:_,"aria-invalid":!!l.url}),l.url&&e.jsx("span",{className:"form-error add-node-modal__error",children:l.url})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key Mode"}),e.jsxs("select",{className:"select",value:w,onChange:m=>v(m.target.value),disabled:_,children:[e.jsx("option",{value:"auto-generate",children:"Auto-generate"}),e.jsx("option",{value:"provide",children:"Provide key manually"})]})]}),w==="provide"&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:N,onChange:m=>P(m.target.value),placeholder:"Enter node API key",disabled:_})]})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),e.jsx("input",{className:"input",type:"number",min:Ce,max:Se,value:y,onChange:m=>c(Number(m.target.value)),disabled:_,"aria-invalid":!!l.maxConcurrent}),e.jsx("span",{className:"add-node-modal__hint",children:"Max simultaneous task agents (1–10)"}),l.maxConcurrent&&e.jsx("span",{className:"form-error add-node-modal__error",children:l.maxConcurrent})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:E,disabled:_,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm","data-testid":"add-node-submit",onClick:x,disabled:_,children:_?"Adding...":"Add Node"})]})]})}):null}function Ts(){const[t,n]=s.useState([]),[a,i]=s.useState(!1),[u,g]=s.useState(null),[o,p]=s.useState(!1),[d,b]=s.useState(null),[N,P]=s.useState(!1),y=s.useCallback(async()=>{i(!0),g(null);try{const v=await fetch("/api/docker/contexts");if(!v.ok)throw new Error(`Failed to load Docker contexts (${v.status})`);const l=await v.json();return n(l),l}catch(v){const l=v instanceof Error?v.message:String(v);throw g(l),v}finally{i(!1)}},[]),c=s.useCallback(async v=>{p(!0);try{const l=await fetch("/api/docker/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hostConfig:v})});if(!l.ok)throw new Error(`Failed to test Docker connection (${l.status})`);const j=await l.json();return b(j),j}finally{p(!1)}},[]),w=s.useCallback(async()=>{P(!0);try{const v=await fetch("/api/docker/local-available");if(!v.ok)throw new Error(`Failed to check local Docker availability (${v.status})`);return await v.json()}finally{P(!1)}},[]);return{contexts:t,isLoadingContexts:a,contextsError:u,loadContexts:y,isTestingConnection:o,lastTestResult:d,testConnection:c,isCheckingLocal:N,checkLocalDocker:w}}function Hs({value:t,onChange:n}){const[a,i]=s.useState(!!(t?.tlsCaPath||t?.tlsCertPath||t?.tlsKeyPath||t?.tlsVerify));s.useEffect(()=>{a||n({tlsVerify:void 0,tlsCaPath:void 0,tlsCertPath:void 0,tlsKeyPath:void 0})},[a,n]);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:g=>i(g.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:g=>n({...u,tlsCaPath:g.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:g=>n({...u,tlsCertPath:g.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:g=>n({...u,tlsKeyPath:g.target.value}),placeholder:"/etc/docker/key.pem"})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:u.tlsVerify,onChange:g=>n({...u,tlsVerify:g.target.checked})}),"Verify TLS Certificate"]})]})]})}function qs({value:t,onChange:n,onError:a}){const i=t?.context?"context":t?.host?"host":"local",[u,g]=s.useState(i),[o,p]=s.useState(t?.context??""),[d,b]=s.useState(t?.host??""),[N,P]=s.useState(null),{contexts:y,isLoadingContexts:c,contextsError:w,loadContexts:v,testConnection:l,isTestingConnection:j,lastTestResult:_,checkLocalDocker:k,isCheckingLocal:L}=Ts(),E=s.useMemo(()=>({tlsVerify:t?.tlsVerify,tlsCaPath:t?.tlsCaPath,tlsCertPath:t?.tlsCertPath,tlsKeyPath:t?.tlsKeyPath}),[t]);s.useEffect(()=>{if(u==="local"){n({});return}if(u==="context"){v().catch(x=>a?.(x instanceof Error?x.message:String(x))),n(o?{context:o}:{});return}n({host:d,...E})},[u]);const V=s.useCallback(x=>{u==="host"&&n({host:d,...x})},[d,u,n]);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:()=>{g("local"),k().then(x=>P(x.available?`Docker is available${x.version?` (${x.version})`:""}`:`Docker not found${x.error?`: ${x.error}`:""}`)).catch(x=>{const m=x instanceof Error?x.message:String(x);P(`Docker not found: ${m}`),a?.(m)})},children:"Local Docker"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="context"?"docker-target-selector__mode-active":""}`,onClick:()=>g("context"),children:"Docker Context"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="host"?"docker-target-selector__mode-active":""}`,onClick:()=>g("host"),children:"Remote Host"})]}),u==="local"&&N&&e.jsx("div",{className:"docker-target-selector__status",children:N}),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:x=>{const m=x.target.value;p(m),n(m?{context:m}:{})},children:[e.jsx("option",{value:"",children:"Select context"}),y.map(x=>e.jsxs("option",{value:x.name,children:[x.name,x.isCurrentContext?" (current)":"",x.dockerHost?` — ${x.dockerHost}`:""]},x.name))]}),e.jsx("button",{type:"button",className:"btn btn-sm btn-icon",onClick:()=>void v(),disabled:c,"aria-label":"Refresh contexts",children:e.jsx(_e,{size:14})})]}),w&&e.jsx("div",{className:"docker-target-selector__error",children:w})]}),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:d,onChange:x=>{const m=x.target.value;b(m),n({host:m,...E})}})]}),e.jsx(Hs,{value:E,onChange:V})]}),e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void l(u==="local"?void 0:u==="context"?{context:o}:{host:d,...E}),disabled:j||L,children:j?"Testing...":"Test Connection"}),_&&e.jsx("div",{className:_.success?"docker-target-selector__success":"docker-target-selector__error",children:_.success?`Connected${_.dockerVersion?` (Docker ${_.dockerVersion})`:""}`:_.error??"Connection failed"})]})}const je="http://localhost:4040";function Ys({isOpen:t,onClose:n,onSubmit:a,addToast:i}){const[u,g]=s.useState(""),[o,p]=s.useState({}),[d,b]=s.useState(je),[N,P]=s.useState("auto"),[y,c]=s.useState(""),[w,v]=s.useState(!1),[l,j]=s.useState(!1),[_,k]=s.useState(!0),[L,E]=s.useState(4096),[V,x]=s.useState(2),[m,C]=s.useState(!1),[T,H]=s.useState("runfusion/fusion"),[J,W]=s.useState("latest"),[D,X]=s.useState([]),[q,Z]=s.useState([]),[Y,te]=s.useState({}),[R,Q]=s.useState(!1),ee=s.useCallback(()=>{g(""),p({}),b(je),P("auto"),c(""),v(!1),j(!1),k(!0),E(4096),x(2),C(!1),H("runfusion/fusion"),W("latest"),X([]),Z([]),te({}),Q(!1)},[]),O=s.useCallback(()=>{R||(ee(),n())},[n,ee,R]);s.useEffect(()=>{if(!t){ee();return}const f=I=>{I.key==="Escape"&&(I.preventDefault(),O())};return document.addEventListener("keydown",f),()=>document.removeEventListener("keydown",f)},[O,t,ee]);const M=s.useMemo(()=>({nodeId:null,name:u.trim(),imageName:T.trim()||"runfusion/fusion",imageTag:J.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(D.map(f=>[f.key.trim(),f.value]).filter(([f])=>!!f)),volumeMounts:q.map(f=>({hostPath:f.hostPath.trim(),containerPath:f.containerPath.trim(),mode:f.mode})).filter(f=>f.hostPath&&f.containerPath),resourceSizing:{memoryMB:L,cpus:V},extraClis:[w?"claude-cli":null,l?"droid-cli":null].filter(Boolean),persistentStorage:_,reachableUrl:d.trim()||null,apiKey:N==="manual"&&y.trim()||null}),[y,N,V,o,D,T,J,w,l,L,q,u,_,d]),$=s.useCallback(()=>{X(f=>[...f,{key:"",value:""}])},[]),F=s.useCallback((f,I)=>{X(K=>K.map((se,oe)=>oe===f?I:se))},[]),G=s.useCallback(f=>{X(I=>I.filter((K,se)=>se!==f))},[]),re=s.useCallback(()=>{Z(f=>[...f,{hostPath:"",containerPath:"",mode:"rw"}])},[]),ae=s.useCallback((f,I)=>{Z(K=>K.map((se,oe)=>oe===f?I:se))},[]),U=s.useCallback(f=>{Z(I=>I.filter((K,se)=>se!==f))},[]),he=s.useCallback(async()=>{if(R)return;const f={};if((!M.name||M.name.length>64)&&(f.name="Name is required and must be 64 characters or fewer"),M.reachableUrl||(f.reachableUrl="URL is required"),L<512&&(f.memoryMB="Memory must be at least 512 MB"),V<.5&&(f.cpus="CPUs must be at least 0.5"),te(f),!(Object.keys(f).length>0)){Q(!0);try{await a(M),O()}catch{}finally{Q(!1)}}},[O,V,M,L,a,R]);return t?e.jsx("div",{className:"modal-overlay open",onClick:O,children:e.jsxs("div",{className:"modal docker-onboarding",role:"dialog","aria-modal":"true","aria-label":"Docker node onboarding",onClick:f=>f.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Provision Docker Node"}),e.jsx("button",{className:"modal-close",onClick:O,disabled:R,"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:f=>g(f.target.value),disabled:R,placeholder:"my-docker-node",autoFocus:!0})]}),Y.name&&e.jsx("div",{className:"form-error",children:Y.name}),e.jsx(qs,{value:o,onChange:p}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Reachable URL"}),e.jsx("input",{className:"input",value:d,onChange:f=>b(f.target.value),disabled:R,placeholder:je})]}),Y.reachableUrl&&e.jsx("div",{className:"form-error",children:Y.reachableUrl}),e.jsxs("div",{className:"docker-onboarding__radio-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="auto",onChange:()=>P("auto"),disabled:R}),"Auto-generate"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="manual",onChange:()=>P("manual"),disabled:R}),"Provide manually"]})]}),N==="manual"&&e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:y,onChange:f=>c(f.target.value),disabled:R,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:w,onChange:f=>v(f.target.checked),disabled:R}),"Claude CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:l,onChange:f=>j(f.target.checked),disabled:R}),"Droid CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:_,onChange:f=>k(f.target.checked),disabled:R}),"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:L,onChange:f=>E(Number(f.target.value)),disabled:R})]}),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:V,onChange:f=>x(Number(f.target.value)),disabled:R})]})]}),Y.memoryMB&&e.jsx("div",{className:"form-error",children:Y.memoryMB}),Y.cpus&&e.jsx("div",{className:"form-error",children:Y.cpus})]}),e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsxs("button",{type:"button",className:`docker-onboarding__advanced-toggle ${m?"is-expanded":""}`,onClick:()=>C(f=>!f),disabled:R,children:[e.jsx("span",{children:"Advanced"}),e.jsx(we,{})]}),e.jsx("div",{className:`docker-onboarding__advanced-content ${m?"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:T,onChange:f=>H(f.target.value),disabled:R,placeholder:"runfusion/fusion"})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Tag"}),e.jsx("input",{className:"input",value:J,onChange:f=>W(f.target.value),disabled:R,placeholder:"latest"})]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Environment Variables"}),D.map((f,I)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--env",children:[e.jsx("input",{className:"input",placeholder:"KEY",value:f.key,disabled:R,onChange:K=>F(I,{key:K.target.value,value:f.value})}),e.jsx("input",{className:"input",placeholder:"Value",value:f.value,disabled:R,onChange:K=>F(I,{key:f.key,value:K.target.value})}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove environment variable",onClick:()=>G(I),disabled:R,children:e.jsx(ve,{size:14})})]},`env-${I}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:$,disabled:R,children:[e.jsx(me,{size:14}),"Add variable"]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Volume Mounts"}),q.map((f,I)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--mount",children:[e.jsx("input",{className:"input",placeholder:"Host path",value:f.hostPath,disabled:R,onChange:K=>ae(I,{hostPath:K.target.value,containerPath:f.containerPath,mode:f.mode})}),e.jsx("input",{className:"input",placeholder:"Container path",value:f.containerPath,disabled:R,onChange:K=>ae(I,{hostPath:f.hostPath,containerPath:K.target.value,mode:f.mode})}),e.jsxs("select",{className:"select",value:f.mode,disabled:R,onChange:K=>ae(I,{hostPath:f.hostPath,containerPath:f.containerPath,mode:K.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:()=>U(I),disabled:R,children:e.jsx(ve,{size:14})})]},`mount-${I}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:re,disabled:R,children:[e.jsx(me,{size:14}),"Add mount"]})]})]})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",onClick:O,disabled:R,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",onClick:()=>void he(),disabled:R,children:R?"Creating...":"Create Docker Node"})]})]})}):null}function Xs({nodeId:t,entries:n,loading:a=!1,singleNode:i=!1}){const[u,g]=s.useState(!1),[o,p]=s.useState("all"),[d,b]=s.useState("all"),N=s.useCallback(()=>{g(l=>!l)},[]),P=s.useMemo(()=>{const l=new Set;for(const j of n)l.add(j.nodeName);return Array.from(l).sort()},[n]),y=s.useMemo(()=>{let l=[...n];return o!=="all"&&(l=l.filter(j=>j.direction===o)),!i&&d!=="all"&&(l=l.filter(j=>j.nodeName===d)),l.sort((j,_)=>{const k=new Date(j.timestamp).getTime();return new Date(_.timestamp).getTime()-k}),l},[n,o,d,i]),c=s.useCallback(l=>new Date(l).toLocaleString(),[]),w=s.useCallback(l=>{switch(l){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""}},[]),v=s.useCallback(l=>{switch(l){case"success":return"Success";case"conflict":return"Conflict";case"error":return"Error";default:return l}},[]);return e.jsxs("div",{className:"settings-sync-log",children:[e.jsxs("button",{className:"settings-sync-log__header",type:"button",onClick:N,"aria-expanded":u,"data-testid":"settings-sync-log-header",children:[e.jsx(we,{size:16,className:`settings-sync-log__chevron ${u?"settings-sync-log__chevron--expanded":""}`}),e.jsxs("span",{children:[n.length," ",n.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:l=>p(l.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:d,onChange:l=>b(l.target.value),children:[e.jsx("option",{value:"all",children:"All Nodes"}),P.map(l=>e.jsx("option",{value:l,children:l},l))]})]})]}),a&&n.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"Loading..."}):y.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"No sync history available"}):e.jsx("div",{className:"settings-sync-log__list",children:y.map(l=>e.jsxs("div",{className:"settings-sync-log__entry",children:[e.jsx("span",{className:"settings-sync-log__entry-timestamp",children:c(l.timestamp)}),e.jsx("span",{className:"settings-sync-log__entry-direction",children:l.direction==="push"?e.jsx(Be,{size:14,"data-testid":"upload-icon"}):e.jsx(Te,{size:14,"data-testid":"download-icon"})}),e.jsx("span",{className:`settings-sync-log__entry-result ${w(l.result)}`,children:v(l.result)}),!i&&e.jsx("span",{className:"settings-sync-log__entry-node",children:l.nodeName}),l.details&&e.jsx("span",{className:"settings-sync-log__entry-details",title:l.details,children:l.details})]},l.id))})]})]})}function Js(t,n){const a=typeof t=="string"?t:JSON.stringify(t,null,2),i=typeof n=="string"?n:JSON.stringify(n,null,2);if(a===i)return a;const u=a.split(`
11
+ */const Ss=[["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"}]],ws=Ie("wifi",Ss);function pe(t){const{lastSyncAt:n,remoteReachable:a,diff:i}=t,u=i.global.length+i.project.length;return n===null?{syncState:"never-synced",lastSyncAt:n,diffCount:0}:a?u>0?{syncState:"diff",lastSyncAt:n,diffCount:u}:{syncState:"synced",lastSyncAt:n,diffCount:0}:{syncState:"error",lastSyncAt:n,diffCount:u}}function Ne(t){if(t===null)return"Never synced";const n=new Date(t);if(Number.isNaN(n.getTime()))return"Never synced";const i=Date.now()-n.getTime(),u=Math.floor(i/1e3),g=Math.floor(u/60),o=Math.floor(g/60),p=Math.floor(o/24);return g<1?"Synced just now":g<60?`Synced ${g}m ago`:o<24?`Synced ${o}h ago`:`Synced ${p}d ago`}function Ms(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 Ps=3e4;function Es(){const[t,n]=s.useState({}),[a,i]=s.useState(!1),[u,g]=s.useState({}),[o,p]=s.useState(null),d=s.useRef(new Set),b=s.useRef(!1),N=s.useRef(null),P=s.useRef(null),y=s.useCallback(async(x,m)=>{try{const C=await os(x);n(B=>({...B,[x]:C})),p(null)}catch(C){console.error(`Failed to fetch sync status for node ${x}:`,C),p(C instanceof Error?C.message:"Failed to fetch sync status")}},[]),c=s.useCallback(async()=>{const x=Array.from(d.current);if(x.length===0)return;N.current&&N.current.abort(),N.current=new AbortController;const m=!b.current;m&&i(!0),p(null);try{const C=await Promise.allSettled(x.map(H=>y(H,m)));b.current=!0,C.filter(H=>H.status==="rejected").length>0&&p("Some sync status requests failed")}catch(C){if(C instanceof Error&&C.name==="AbortError")return;p(C instanceof Error?C.message:"Failed to fetch sync status"),b.current=!0}finally{i(!1)}},[y]),w=s.useCallback(()=>{P.current&&clearInterval(P.current),P.current=setInterval(()=>{c()},Ps)},[c]),v=s.useCallback(()=>{P.current&&(clearInterval(P.current),P.current=null)},[]);s.useEffect(()=>(c(),w(),()=>{v(),N.current&&N.current.abort()}),[c,w,v]);const l=s.useCallback(x=>{d.current.has(x)||(d.current.add(x),y(x,!b.current))},[y]),j=s.useCallback(x=>{d.current.delete(x),n(m=>{const C={...m};return delete C[x],C}),d.current.size===0&&v()},[v]),_=s.useCallback(async x=>{g(m=>({...m,[x]:!0})),p(null);try{const m=await cs(x);return y(x,!1),!m.success&&m.error&&p(m.error),m}catch(m){const C=m instanceof Error?m.message:"Push settings failed";throw p(C),m}finally{g(m=>{const C={...m};return delete C[x],C})}},[y]),k=s.useCallback(async x=>{g(m=>({...m,[x]:!0})),p(null);try{const m=await is(x);return y(x,!1),!m.success&&m.error&&p(m.error),m}catch(m){const C=m instanceof Error?m.message:"Pull settings failed";throw p(C),m}finally{g(m=>{const C={...m};return delete C[x],C})}},[y]),L=s.useCallback(async x=>{g(m=>({...m,[x]:!0})),p(null);try{return await ds(x)}catch(m){const C=m instanceof Error?m.message:"Auth sync failed";throw p(C),m}finally{g(m=>{const C={...m};return delete C[x],C})}},[]),E=s.useCallback(x=>t[x]?.authMatch,[t]),V=s.useCallback(x=>t[x]?.authDiff,[t]);return{syncStatusMap:t,loading:a,actionLoading:u,error:o,refresh:c,trackNode:l,untrackNode:j,pushSettings:_,pullSettings:k,syncAuth:L,getAuthSyncState:E,getAuthProviders:V}}function Rs(t,n){return n.type==="remote"?t.nodeId===n.id:t.nodeId===n.id||t.nodeId===void 0||t.nodeId===null}function He(t,n){return t.filter(a=>Rs(a,n))}function ke(t,n){return He(t,n).length}const de={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"},creating:{label:"Creating",color:"var(--color-warning)",className:"node-card__status--creating"},recreating:{label:"Recreating",color:"var(--color-warning)",className:"node-card__status--recreating"},deleting:{label:"Deleting",color:"var(--color-error)",className:"node-card__status--deleting"},running:{label:"Running",color:"var(--color-success)",className:"node-card__status--online"},stopped:{label:"Stopped",color:"var(--color-error)",className:"node-card__status--offline"},exited:{label:"Exited",color:"var(--color-error)",className:"node-card__status--offline"}},As={match:"var(--color-success)",differs:"var(--color-warning)","not-synced":"var(--text-muted)"};function Ls(t,n){if(t==="match")return"Auth credentials match";if(t==="not-synced")return"Auth not synced";if(n&&Object.keys(n).length>0){const a=Object.entries(n).filter(([,i])=>i==="differs").map(([i])=>i);if(a.length>0)return`Auth credentials differ: ${a.join(", ")}`}return"Auth credentials differ"}function $s(t,n=42){return t.length<=n?t:`${t.slice(0,n-3)}...`}function Ds(t,n){const a=t.node,i=n.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!==n.isLoading)return!1;const u=t.managedDockerNode,g=n.managedDockerNode;if(!!u!=!!g||u&&g&&(u.id!==g.id||u.status!==g.status||u.imageTag!==g.imageTag||u.updatedAt!==g.updatedAt))return!1;const o=t.syncStatus,p=n.syncStatus;if(!(!o&&!p)){if(!o||!p)return!1;if(o.syncState!==p.syncState||o.lastSyncAt!==p.lastSyncAt||o.diffCount!==p.diffCount)return!1}if(t.authSyncState!==n.authSyncState)return!1;const d=t.authSyncProviders,b=n.authSyncProviders;if(d!==b){if(!d||!b)return!1;{const y=Object.keys(d),c=Object.keys(b);if(y.length!==c.length||y.some(w=>d[w]!==b[w]))return!1}}const N=ke(t.projects,a),P=ke(n.projects,i);return N===P}function zs({node:t,projects:n,onHealthCheck:a,onEdit:i,onRemove:u,isLoading:g=!1,syncStatus:o,authSyncState:p,authSyncProviders:d,managedDockerNode:b}){const[N,P]=s.useState(!1),y=de[t.status]??de.offline,c=b?de[b.status]??de.error:null,w=b?.hostConfig.type==="remote"?`Remote: ${b.hostConfig.host??"unknown"}`:"Local Docker",v=s.useMemo(()=>ke(n,t),[n,t]),l=s.useCallback(()=>{i(t)},[i,t]),j=s.useCallback(E=>{E.stopPropagation(),a(t.id)},[a,t.id]),_=s.useCallback(E=>{E.stopPropagation(),i(t)},[i,t]),k=s.useCallback(E=>{if(E.stopPropagation(),!N){P(!0);return}u(t.id),P(!1)},[N,u,t.id]),L=s.useCallback(E=>{(E.key==="Enter"||E.key===" ")&&(E.preventDefault(),i(t))},[i,t]);return e.jsxs("article",{className:`node-card ${g?"node-card--loading":""}`,"data-node-id":t.id,role:"button",tabIndex:0,onClick:l,onKeyDown:L,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(Ve,{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"}),b&&e.jsxs("span",{className:"node-card__docker-badge",title:"Managed Docker node",children:[e.jsx(ye,{size:12,"aria-hidden":!0}),"Docker"]}),e.jsxs("span",{className:`node-card__status ${y.className}`,style:{color:y.color},"data-status":t.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:y.color},"aria-hidden":!0}),y.label]}),b&&c&&e.jsxs("span",{className:`node-card__status ${c.className}`,style:{color:c.color},"data-status":b.status,children:[e.jsx("span",{className:"node-card__status-indicator",style:{backgroundColor:c.color},"aria-hidden":!0}),c.label]}),t.type==="remote"&&p&&e.jsx("span",{className:`node-card__auth-indicator node-card__auth-indicator--${p}`,title:Ls(p,d),"aria-label":`Auth sync: ${p==="match"?"credentials match":p==="differs"?"credentials differ":"not synced"}`,style:{color:As[p]},children:e.jsx(Ke,{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:$s(t.url)}),b&&e.jsxs("div",{className:"node-card__docker-meta",children:[e.jsxs("span",{title:`${b.imageName}:${b.imageTag}`,children:[b.imageName,":",b.imageTag]}),e.jsx("span",{title:w,children:w})]}),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:v})]}),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:Ms(o.syncState)},"aria-hidden":!0}),e.jsx("span",{className:"node-card__sync-time",children:Ne(o.lastSyncAt)})]})]}),e.jsxs("footer",{className:"node-card__actions",children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:j,disabled:g,"aria-label":"Run node health check",title:"Health Check",children:[e.jsx(Fe,{size:14}),e.jsx("span",{children:"Health"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",onClick:_,disabled:g,"aria-label":"Edit node",title:"Edit",children:[e.jsx(us,{size:14}),e.jsx("span",{children:"Edit"})]}),b&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Start node container",title:"Available after FN-3113",children:[e.jsx(Oe,{size:14}),e.jsx("span",{children:"Start"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Stop node container",title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),e.jsx("span",{children:"Stop"})]}),e.jsxs("button",{className:"btn btn-sm node-card__action",type:"button",disabled:!0,"aria-label":"Restart node container",title:"Available after FN-3113",children:[e.jsx(ms,{size:14}),e.jsx("span",{children:"Restart"})]})]}),e.jsxs("button",{className:`btn btn-sm node-card__action node-card__action--remove ${N?"btn-danger is-armed":""}`,type:"button",onClick:k,disabled:g,"aria-label":N?"Confirm remove node":"Remove node",title:N?"Confirm remove":"Remove",children:[e.jsx(ve,{size:14}),e.jsx("span",{children:N?"Confirm":"Remove"})]})]})]})}const Is=s.memo(zs,Ds),ce={online:"var(--success, var(--color-success))",offline:"var(--text-dim)",connecting:"var(--triage)",error:"var(--color-error)"},le=28,ze=12,Vs=300,Ks=120;function Fs({nodes:t,className:n}){const a=s.useMemo(()=>t.find(d=>d.type==="local")??t[0],[t]),i=s.useMemo(()=>t.filter(d=>d.type==="remote"),[t]),u=s.useMemo(()=>{const d=Vs,b=Math.max(0,i.length-4)*20;return d+b},[i.length]),g=u/2,o=u/2,p=s.useMemo(()=>{if(i.length===0)return[];const d=Math.min(Ks,u/2-le-10),b=2*Math.PI/i.length,N=-Math.PI/2;return i.map((P,y)=>{const c=N+y*b;return{node:P,x:g+d*Math.cos(c),y:o+d*Math.sin(c)}})},[i,u,g,o]);return t.length===0?e.jsx("div",{className:`mesh-topology mesh-topology--empty ${n??""}`,children:e.jsx("div",{className:"mesh-topology__empty-state",children:e.jsx("p",{children:"No nodes to display"})})}):e.jsxs("div",{className:`mesh-topology ${n??""}`,children:[e.jsxs("svg",{className:"mesh-topology__svg",viewBox:`0 0 ${u} ${u}`,preserveAspectRatio:"xMidYMid meet","aria-label":"Node mesh topology visualization",children:[p.map(d=>e.jsx("line",{className:"mesh-topology__link",x1:g,y1:o,x2:d.x,y2:d.y},`link-${d.node.id}`)),a&&e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${g}, ${o})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:le,fill:ce[a.status],"aria-label":`${a.name} (${a.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:le+ze,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 ${-le-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"})]})]}),p.map(d=>e.jsxs("g",{className:"mesh-topology__node",transform:`translate(${d.x}, ${d.y})`,children:[e.jsx("circle",{className:"mesh-topology__node-circle",r:le,fill:ce[d.node.status],"aria-label":`${d.node.name} (${d.node.status})`}),e.jsx("text",{className:"mesh-topology__node-label",y:le+ze,textAnchor:"middle",children:d.node.name.length>12?`${d.node.name.slice(0,10)}…`:d.node.name}),e.jsxs("g",{className:"mesh-topology__node-type",transform:`translate(0 ${-le-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:d.node.type==="local"?"L":"R"})]})]},d.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:ce.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:ce.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:ce.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:ce.error}}),e.jsx("span",{children:"Error"})]})]}),e.jsx("p",{className:"mesh-topology__notice",children:"Peer-to-peer discovery data unavailable."})]})}const Os=s.memo(Fs),Ce=1,Se=10;function Us(t){const n={};return t.name.trim()||(n.name="Name is required"),t.type==="remote"&&!t.url?.trim()&&(n.url="URL is required for remote nodes"),(!Number.isFinite(t.maxConcurrent)||t.maxConcurrent<Ce||t.maxConcurrent>Se)&&(n.maxConcurrent=`Concurrency must be between ${Ce} and ${Se}`),n}function Ts({isOpen:t,onClose:n,onSubmit:a,addToast:i}){hs(t);const[u,g]=s.useState(""),[o,p]=s.useState("local"),[d,b]=s.useState(""),[N,P]=s.useState(""),[y,c]=s.useState(2),[w,v]=s.useState("auto-generate"),[l,j]=s.useState({}),[_,k]=s.useState(!1),L=s.useCallback(()=>{g(""),p("local"),b(""),P(""),c(2),v("auto-generate"),j({}),k(!1)},[]),E=s.useCallback(()=>{_||(L(),n())},[_,n,L]);s.useEffect(()=>{if(!t){L();return}const m=C=>{C.key==="Escape"&&(C.preventDefault(),E())};return document.addEventListener("keydown",m),()=>{document.removeEventListener("keydown",m)}},[E,t,L]);const V=s.useMemo(()=>({name:u.trim(),type:o,url:o==="remote"&&d.trim()||void 0,apiKey:o==="remote"&&w==="provide"&&N||void 0,maxConcurrent:y,apiKeyMode:w}),[N,w,y,u,o,d]),x=s.useCallback(async()=>{if(_)return;const m=Us(V);if(j(m),!(Object.keys(m).length>0)){k(!0);try{await a(V),i(`Node "${V.name}" registered`,"success"),E()}catch(C){const B=C instanceof Error?C.message:"Failed to register node";i(B,"error")}finally{k(!1)}}},[i,E,V,_,a]);return t?e.jsx("div",{className:"modal-overlay open",onClick:E,children:e.jsxs("div",{className:"modal modal-md add-node-modal",onClick:m=>m.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:_,"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:m=>g(m.target.value),placeholder:"Build Machine",disabled:_,"aria-invalid":!!l.name,autoFocus:!0}),l.name&&e.jsx("span",{className:"form-error add-node-modal__error",children:l.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:()=>p("local"),disabled:_,"aria-pressed":o==="local",children:"Local"}),e.jsx("button",{type:"button",className:`add-node-modal__type-btn ${o==="remote"?"active":""}`,"data-type":"remote",onClick:()=>p("remote"),disabled:_,"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:d,onChange:m=>b(m.target.value),placeholder:"https://node.example.com",disabled:_,"aria-invalid":!!l.url}),l.url&&e.jsx("span",{className:"form-error add-node-modal__error",children:l.url})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key Mode"}),e.jsxs("select",{className:"select",value:w,onChange:m=>v(m.target.value),disabled:_,children:[e.jsx("option",{value:"auto-generate",children:"Auto-generate"}),e.jsx("option",{value:"provide",children:"Provide key manually"})]})]}),w==="provide"&&e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:N,onChange:m=>P(m.target.value),placeholder:"Enter node API key",disabled:_})]})]}),e.jsxs("label",{className:"add-node-modal__field",children:[e.jsx("span",{children:"Max Concurrent"}),e.jsx("input",{className:"input",type:"number",min:Ce,max:Se,value:y,onChange:m=>c(Number(m.target.value)),disabled:_,"aria-invalid":!!l.maxConcurrent}),e.jsx("span",{className:"add-node-modal__hint",children:"Max simultaneous task agents (1–10)"}),l.maxConcurrent&&e.jsx("span",{className:"form-error add-node-modal__error",children:l.maxConcurrent})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:E,disabled:_,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm","data-testid":"add-node-submit",onClick:x,disabled:_,children:_?"Adding...":"Add Node"})]})]})}):null}function Bs(){const[t,n]=s.useState([]),[a,i]=s.useState(!1),[u,g]=s.useState(null),[o,p]=s.useState(!1),[d,b]=s.useState(null),[N,P]=s.useState(!1),y=s.useCallback(async()=>{i(!0),g(null);try{const v=await fetch("/api/docker/contexts");if(!v.ok)throw new Error(`Failed to load Docker contexts (${v.status})`);const l=await v.json();return n(l),l}catch(v){const l=v instanceof Error?v.message:String(v);throw g(l),v}finally{i(!1)}},[]),c=s.useCallback(async v=>{p(!0);try{const l=await fetch("/api/docker/test-connection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hostConfig:v})});if(!l.ok)throw new Error(`Failed to test Docker connection (${l.status})`);const j=await l.json();return b(j),j}finally{p(!1)}},[]),w=s.useCallback(async()=>{P(!0);try{const v=await fetch("/api/docker/local-available");if(!v.ok)throw new Error(`Failed to check local Docker availability (${v.status})`);return await v.json()}finally{P(!1)}},[]);return{contexts:t,isLoadingContexts:a,contextsError:u,loadContexts:y,isTestingConnection:o,lastTestResult:d,testConnection:c,isCheckingLocal:N,checkLocalDocker:w}}function Hs({value:t,onChange:n}){const[a,i]=s.useState(!!(t?.tlsCaPath||t?.tlsCertPath||t?.tlsKeyPath||t?.tlsVerify));s.useEffect(()=>{a||n({tlsVerify:void 0,tlsCaPath:void 0,tlsCertPath:void 0,tlsKeyPath:void 0})},[a,n]);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:g=>i(g.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:g=>n({...u,tlsCaPath:g.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:g=>n({...u,tlsCertPath:g.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:g=>n({...u,tlsKeyPath:g.target.value}),placeholder:"/etc/docker/key.pem"})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:u.tlsVerify,onChange:g=>n({...u,tlsVerify:g.target.checked})}),"Verify TLS Certificate"]})]})]})}function qs({value:t,onChange:n,onError:a}){const i=t?.context?"context":t?.host?"host":"local",[u,g]=s.useState(i),[o,p]=s.useState(t?.context??""),[d,b]=s.useState(t?.host??""),[N,P]=s.useState(null),{contexts:y,isLoadingContexts:c,contextsError:w,loadContexts:v,testConnection:l,isTestingConnection:j,lastTestResult:_,checkLocalDocker:k,isCheckingLocal:L}=Bs(),E=s.useMemo(()=>({tlsVerify:t?.tlsVerify,tlsCaPath:t?.tlsCaPath,tlsCertPath:t?.tlsCertPath,tlsKeyPath:t?.tlsKeyPath}),[t]);s.useEffect(()=>{if(u==="local"){n({});return}if(u==="context"){v().catch(x=>a?.(x instanceof Error?x.message:String(x))),n(o?{context:o}:{});return}n({host:d,...E})},[u]);const V=s.useCallback(x=>{u==="host"&&n({host:d,...x})},[d,u,n]);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:()=>{g("local"),k().then(x=>P(x.available?`Docker is available${x.version?` (${x.version})`:""}`:`Docker not found${x.error?`: ${x.error}`:""}`)).catch(x=>{const m=x instanceof Error?x.message:String(x);P(`Docker not found: ${m}`),a?.(m)})},children:"Local Docker"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="context"?"docker-target-selector__mode-active":""}`,onClick:()=>g("context"),children:"Docker Context"}),e.jsx("button",{type:"button",className:`btn btn-sm ${u==="host"?"docker-target-selector__mode-active":""}`,onClick:()=>g("host"),children:"Remote Host"})]}),u==="local"&&N&&e.jsx("div",{className:"docker-target-selector__status",children:N}),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:x=>{const m=x.target.value;p(m),n(m?{context:m}:{})},children:[e.jsx("option",{value:"",children:"Select context"}),y.map(x=>e.jsxs("option",{value:x.name,children:[x.name,x.isCurrentContext?" (current)":"",x.dockerHost?` — ${x.dockerHost}`:""]},x.name))]}),e.jsx("button",{type:"button",className:"btn btn-sm btn-icon",onClick:()=>void v(),disabled:c,"aria-label":"Refresh contexts",children:e.jsx(_e,{size:14})})]}),w&&e.jsx("div",{className:"docker-target-selector__error",children:w})]}),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:d,onChange:x=>{const m=x.target.value;b(m),n({host:m,...E})}})]}),e.jsx(Hs,{value:E,onChange:V})]}),e.jsx("button",{type:"button",className:"btn btn-sm",onClick:()=>void l(u==="local"?void 0:u==="context"?{context:o}:{host:d,...E}),disabled:j||L,children:j?"Testing...":"Test Connection"}),_&&e.jsx("div",{className:_.success?"docker-target-selector__success":"docker-target-selector__error",children:_.success?`Connected${_.dockerVersion?` (Docker ${_.dockerVersion})`:""}`:_.error??"Connection failed"})]})}const je="http://localhost:4040";function Ys({isOpen:t,onClose:n,onSubmit:a,addToast:i}){const[u,g]=s.useState(""),[o,p]=s.useState({}),[d,b]=s.useState(je),[N,P]=s.useState("auto"),[y,c]=s.useState(""),[w,v]=s.useState(!1),[l,j]=s.useState(!1),[_,k]=s.useState(!0),[L,E]=s.useState(4096),[V,x]=s.useState(2),[m,C]=s.useState(!1),[B,H]=s.useState("runfusion/fusion"),[J,W]=s.useState("latest"),[D,X]=s.useState([]),[q,Q]=s.useState([]),[Y,te]=s.useState({}),[R,Z]=s.useState(!1),ee=s.useCallback(()=>{g(""),p({}),b(je),P("auto"),c(""),v(!1),j(!1),k(!0),E(4096),x(2),C(!1),H("runfusion/fusion"),W("latest"),X([]),Q([]),te({}),Z(!1)},[]),O=s.useCallback(()=>{R||(ee(),n())},[n,ee,R]);s.useEffect(()=>{if(!t){ee();return}const f=I=>{I.key==="Escape"&&(I.preventDefault(),O())};return document.addEventListener("keydown",f),()=>document.removeEventListener("keydown",f)},[O,t,ee]);const M=s.useMemo(()=>({nodeId:null,name:u.trim(),imageName:B.trim()||"runfusion/fusion",imageTag:J.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(D.map(f=>[f.key.trim(),f.value]).filter(([f])=>!!f)),volumeMounts:q.map(f=>({hostPath:f.hostPath.trim(),containerPath:f.containerPath.trim(),mode:f.mode})).filter(f=>f.hostPath&&f.containerPath),resourceSizing:{memoryMB:L,cpus:V},extraClis:[w?"claude-cli":null,l?"droid-cli":null].filter(Boolean),persistentStorage:_,reachableUrl:d.trim()||null,apiKey:N==="manual"&&y.trim()||null}),[y,N,V,o,D,B,J,w,l,L,q,u,_,d]),$=s.useCallback(()=>{X(f=>[...f,{key:"",value:""}])},[]),F=s.useCallback((f,I)=>{X(K=>K.map((se,oe)=>oe===f?I:se))},[]),G=s.useCallback(f=>{X(I=>I.filter((K,se)=>se!==f))},[]),re=s.useCallback(()=>{Q(f=>[...f,{hostPath:"",containerPath:"",mode:"rw"}])},[]),ae=s.useCallback((f,I)=>{Q(K=>K.map((se,oe)=>oe===f?I:se))},[]),U=s.useCallback(f=>{Q(I=>I.filter((K,se)=>se!==f))},[]),he=s.useCallback(async()=>{if(R)return;const f={};if((!M.name||M.name.length>64)&&(f.name="Name is required and must be 64 characters or fewer"),M.reachableUrl||(f.reachableUrl="URL is required"),L<512&&(f.memoryMB="Memory must be at least 512 MB"),V<.5&&(f.cpus="CPUs must be at least 0.5"),te(f),!(Object.keys(f).length>0)){Z(!0);try{await a(M),O()}catch{}finally{Z(!1)}}},[O,V,M,L,a,R]);return t?e.jsx("div",{className:"modal-overlay open",onClick:O,children:e.jsxs("div",{className:"modal docker-onboarding",role:"dialog","aria-modal":"true","aria-label":"Docker node onboarding",onClick:f=>f.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:"Provision Docker Node"}),e.jsx("button",{className:"modal-close",onClick:O,disabled:R,"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:f=>g(f.target.value),disabled:R,placeholder:"my-docker-node",autoFocus:!0})]}),Y.name&&e.jsx("div",{className:"form-error",children:Y.name}),e.jsx(qs,{value:o,onChange:p}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Reachable URL"}),e.jsx("input",{className:"input",value:d,onChange:f=>b(f.target.value),disabled:R,placeholder:je})]}),Y.reachableUrl&&e.jsx("div",{className:"form-error",children:Y.reachableUrl}),e.jsxs("div",{className:"docker-onboarding__radio-group",children:[e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="auto",onChange:()=>P("auto"),disabled:R}),"Auto-generate"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"radio",checked:N==="manual",onChange:()=>P("manual"),disabled:R}),"Provide manually"]})]}),N==="manual"&&e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"API Key"}),e.jsx("input",{className:"input",type:"password",value:y,onChange:f=>c(f.target.value),disabled:R,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:w,onChange:f=>v(f.target.checked),disabled:R}),"Claude CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:l,onChange:f=>j(f.target.checked),disabled:R}),"Droid CLI"]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:_,onChange:f=>k(f.target.checked),disabled:R}),"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:L,onChange:f=>E(Number(f.target.value)),disabled:R})]}),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:V,onChange:f=>x(Number(f.target.value)),disabled:R})]})]}),Y.memoryMB&&e.jsx("div",{className:"form-error",children:Y.memoryMB}),Y.cpus&&e.jsx("div",{className:"form-error",children:Y.cpus})]}),e.jsxs("section",{className:"docker-onboarding__section",children:[e.jsxs("button",{type:"button",className:`docker-onboarding__advanced-toggle ${m?"is-expanded":""}`,onClick:()=>C(f=>!f),disabled:R,children:[e.jsx("span",{children:"Advanced"}),e.jsx(we,{})]}),e.jsx("div",{className:`docker-onboarding__advanced-content ${m?"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:B,onChange:f=>H(f.target.value),disabled:R,placeholder:"runfusion/fusion"})]}),e.jsxs("label",{className:"docker-onboarding__field",children:[e.jsx("span",{children:"Tag"}),e.jsx("input",{className:"input",value:J,onChange:f=>W(f.target.value),disabled:R,placeholder:"latest"})]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Environment Variables"}),D.map((f,I)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--env",children:[e.jsx("input",{className:"input",placeholder:"KEY",value:f.key,disabled:R,onChange:K=>F(I,{key:K.target.value,value:f.value})}),e.jsx("input",{className:"input",placeholder:"Value",value:f.value,disabled:R,onChange:K=>F(I,{key:f.key,value:K.target.value})}),e.jsx("button",{type:"button",className:"btn btn-icon","aria-label":"Remove environment variable",onClick:()=>G(I),disabled:R,children:e.jsx(ve,{size:14})})]},`env-${I}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:$,disabled:R,children:[e.jsx(me,{size:14}),"Add variable"]})]}),e.jsxs("div",{className:"docker-onboarding__kv-list",children:[e.jsx("h5",{children:"Volume Mounts"}),q.map((f,I)=>e.jsxs("div",{className:"docker-onboarding__kv-row docker-onboarding__kv-row--mount",children:[e.jsx("input",{className:"input",placeholder:"Host path",value:f.hostPath,disabled:R,onChange:K=>ae(I,{hostPath:K.target.value,containerPath:f.containerPath,mode:f.mode})}),e.jsx("input",{className:"input",placeholder:"Container path",value:f.containerPath,disabled:R,onChange:K=>ae(I,{hostPath:f.hostPath,containerPath:K.target.value,mode:f.mode})}),e.jsxs("select",{className:"select",value:f.mode,disabled:R,onChange:K=>ae(I,{hostPath:f.hostPath,containerPath:f.containerPath,mode:K.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:()=>U(I),disabled:R,children:e.jsx(ve,{size:14})})]},`mount-${I}`)),e.jsxs("button",{type:"button",className:"btn btn-sm docker-onboarding__kv-add",onClick:re,disabled:R,children:[e.jsx(me,{size:14}),"Add mount"]})]})]})})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",onClick:O,disabled:R,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",onClick:()=>void he(),disabled:R,children:R?"Creating...":"Create Docker Node"})]})]})}):null}function Xs({nodeId:t,entries:n,loading:a=!1,singleNode:i=!1}){const[u,g]=s.useState(!1),[o,p]=s.useState("all"),[d,b]=s.useState("all"),N=s.useCallback(()=>{g(l=>!l)},[]),P=s.useMemo(()=>{const l=new Set;for(const j of n)l.add(j.nodeName);return Array.from(l).sort()},[n]),y=s.useMemo(()=>{let l=[...n];return o!=="all"&&(l=l.filter(j=>j.direction===o)),!i&&d!=="all"&&(l=l.filter(j=>j.nodeName===d)),l.sort((j,_)=>{const k=new Date(j.timestamp).getTime();return new Date(_.timestamp).getTime()-k}),l},[n,o,d,i]),c=s.useCallback(l=>new Date(l).toLocaleString(),[]),w=s.useCallback(l=>{switch(l){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""}},[]),v=s.useCallback(l=>{switch(l){case"success":return"Success";case"conflict":return"Conflict";case"error":return"Error";default:return l}},[]);return e.jsxs("div",{className:"settings-sync-log",children:[e.jsxs("button",{className:"settings-sync-log__header",type:"button",onClick:N,"aria-expanded":u,"data-testid":"settings-sync-log-header",children:[e.jsx(we,{size:16,className:`settings-sync-log__chevron ${u?"settings-sync-log__chevron--expanded":""}`}),e.jsxs("span",{children:[n.length," ",n.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:l=>p(l.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:d,onChange:l=>b(l.target.value),children:[e.jsx("option",{value:"all",children:"All Nodes"}),P.map(l=>e.jsx("option",{value:l,children:l},l))]})]})]}),a&&n.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"Loading..."}):y.length===0?e.jsx("div",{className:"settings-sync-log__empty",children:"No sync history available"}):e.jsx("div",{className:"settings-sync-log__list",children:y.map(l=>e.jsxs("div",{className:"settings-sync-log__entry",children:[e.jsx("span",{className:"settings-sync-log__entry-timestamp",children:c(l.timestamp)}),e.jsx("span",{className:"settings-sync-log__entry-direction",children:l.direction==="push"?e.jsx(Be,{size:14,"data-testid":"upload-icon"}):e.jsx(Te,{size:14,"data-testid":"download-icon"})}),e.jsx("span",{className:`settings-sync-log__entry-result ${w(l.result)}`,children:v(l.result)}),!i&&e.jsx("span",{className:"settings-sync-log__entry-node",children:l.nodeName}),l.details&&e.jsx("span",{className:"settings-sync-log__entry-details",title:l.details,children:l.details})]},l.id))})]})]})}function Js(t,n){const a=typeof t=="string"?t:JSON.stringify(t,null,2),i=typeof n=="string"?n:JSON.stringify(n,null,2);if(a===i)return a;const u=a.split(`
12
12
  `),g=i.split(`
13
13
  `),o=[],p=Math.max(u.length,g.length);for(let d=0;d<p;d++){const b=u[d],N=g[d];b!==void 0&&b!==N&&o.push(`- ${b}`),N!==void 0&&N!==b&&o.push(`+ ${N}`),b!==void 0&&b===N&&o.push(` ${b}`)}return o.join(`
14
- `)}function Ws({isOpen:t,onClose:n,onResolve:a,conflicts:i,localNodeName:u,remoteNodeName:g,addToast:o}){const[p,d]=s.useState({}),[b,N]=s.useState(!1);s.useEffect(()=>{const l={};for(const j of i)p[j.key]||(l[j.key]={resolution:"local"});Object.keys(l).length>0&&d(j=>({...j,...l}))},[i,p]),s.useEffect(()=>{if(!t)return;const l=j=>{j.key==="Escape"&&(j.preventDefault(),n())};return document.addEventListener("keydown",l),()=>document.removeEventListener("keydown",l)},[t,n]);const P=s.useCallback((l,j)=>{d(_=>{const k=_[l]??{};return j==="manual"?{..._,[l]:{resolution:"manual",manualValue:k.manualValue??JSON.stringify(i.find(L=>L.key===l)?.localValue??null,null,2)}}:{..._,[l]:{resolution:j}}})},[i]),y=s.useCallback((l,j)=>{d(_=>({..._,[l]:{..._[l],resolution:"manual",manualValue:j}}))},[]),c=s.useCallback(l=>{const j={};for(const _ of i)j[_.key]={resolution:l};d(j)},[i]),w=s.useCallback(async()=>{N(!0);try{const l=i.map(j=>{const _=p[j.key]??{resolution:"local"};let k;switch(_.resolution){case"remote":k=j.remoteValue;break;case"manual":try{k=JSON.parse(_.manualValue??"null")}catch{k=_.manualValue??null}break;case"local":default:k=j.localValue;break}return{key:j.key,value:k}});await a(l),o("Settings conflicts resolved successfully","success"),n()}catch(l){const j=l instanceof Error?l.message:"Failed to resolve conflicts";o(j,"error")}finally{N(!1)}},[o,i,n,a,p]),v=s.useMemo(()=>{const l={};for(const j of i)l[j.key]=Js(j.localValue,j.remoteValue);return l},[i]);return!t||i.length===0?null:e.jsx("div",{className:"modal-overlay open",onClick:n,children:e.jsxs("div",{className:"modal modal-lg settings-sync-conflict-modal",onClick:l=>l.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:n,"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(l=>{const j=p[l.key]??{resolution:"local"},_=v[l.key];return e.jsxs("div",{className:"settings-sync-conflict-modal__conflict-item",children:[e.jsx("div",{className:"settings-sync-conflict-modal__key",children:l.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:_})})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:g}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:_})})]})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__resolution",children:[e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:j.resolution==="local",onChange:()=>P(l.key,"local")}),"Keep Local"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:j.resolution==="remote",onChange:()=>P(l.key,"remote")}),"Keep Remote"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:j.resolution==="manual",onChange:()=>P(l.key,"manual")}),"Merge Manually"]})]}),j.resolution==="manual"&&e.jsx("textarea",{className:"settings-sync-conflict-modal__manual-input",value:j.manualValue??"",onChange:k=>y(l.key,k.target.value),placeholder:"Enter JSON value..."})]},l.key)})}),e.jsxs("div",{className:"settings-sync-conflict-modal__bulk-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>c("local"),type:"button",children:"Resolve All: Keep Local"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>c("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:n,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm",onClick:w,disabled:b,children:b?"Resolving...":"Confirm"})]})]})})}const qe=/(KEY|TOKEN|SECRET|PASSWORD)/i;function be(t){if(!t)return"—";const n=new Date(t);return Number.isNaN(n.getTime())?"—":n.toLocaleString()}function Gs(t){if(!t)return"—";const n=new Date(t),a=Date.now();if(Number.isNaN(n.getTime())||n.getTime()>a)return"—";const i=Math.floor((a-n.getTime())/1e3);if(i<60)return`${i}s`;const u=Math.floor(i/60);if(u<60)return`${u}m`;const g=Math.floor(u/60);return g<24?`${g}h ${u%60}m`:`${Math.floor(g/24)}d ${g%24}h`}function Zs(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 Qs(t){return t==="running"?"success":t==="creating"||t==="recreating"||t==="restarting"?"warning":"error"}function et(t){return t?`${t.charAt(0).toUpperCase()}${t.slice(1)}`:"Unknown"}function st(t){if(!t)return"—";try{const n=new URL(t);return n.port?n.port:n.protocol==="https:"?"443":n.protocol==="http:"?"80":"—"}catch{return"—"}}function tt(t,n){return qe.test(t)?"••••••••":n}function at({isOpen:t,onClose:n,node:a,projects:i,onUpdate:u,onHealthCheck:g,addToast:o,syncStatus:p,onPushSettings:d,onPullSettings:b,onSyncAuth:N,syncHistory:P=[],onResolveConflicts:y,managedDockerNode:c,containerStatus:w,onFetchContainerStatus:v,onFetchLogs:l,onUpdateDockerConfig:j,onFetchDockerConfigDiff:_}){const k=s.useRef(!0),[L,E]=s.useState(!1),[V,x]=s.useState(""),[m,C]=s.useState(""),[T,H]=s.useState(""),[J,W]=s.useState(2),[D,X]=s.useState(!1),[q,Z]=s.useState(!1),[Y,te]=s.useState(!1),[R,Q]=s.useState(!1),[ee,O]=s.useState(null),[M,$]=s.useState(!1),[F]=s.useState([]),[G,re]=s.useState(w),[ae,U]=s.useState(!1),[he,f]=s.useState(!1),[I,K]=s.useState(""),[se,oe]=s.useState(!1),[fe,Me]=s.useState(!1),[h,z]=s.useState(a?.dockerConfig??null),[Pe,Ee]=s.useState({}),[ie,Re]=s.useState(!1),[Ye,Ae]=s.useState(!1);s.useEffect(()=>(k.current=!0,()=>{k.current=!1}),[]),s.useEffect(()=>{re(w)},[w]),s.useEffect(()=>{if(!a||!t){E(!1),f(!1),K("");return}x(a.name),C(a.url??""),H(a.apiKey??""),W(a.maxConcurrent),E(!1),z(a.dockerConfig??null),Me(!1),Ee({})},[t,a]),s.useEffect(()=>{if(!t)return;const r=S=>{S.key==="Escape"&&(S.preventDefault(),n())};return document.addEventListener("keydown",r),()=>document.removeEventListener("keydown",r)},[t,n]);const ge=s.useMemo(()=>a?He(i,a):[],[a,i]),Xe=s.useMemo(()=>c?c.hostConfig.type==="remote"?c.hostConfig.host??"—":"Local Docker":"—",[c]),Je=s.useMemo(()=>!c?.resourceSizing?.cpuLimit&&!c?.resourceSizing?.memoryLimit?"Default":`${c.resourceSizing?.cpuLimit??"Default CPU"} / ${c.resourceSizing?.memoryLimit??"Default memory"}`,[c]),We=s.useCallback(async()=>{if(a)try{if(await g(a.id),!k.current)return;o(`Health check completed for ${a.name}`,"success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Health check failed";o(S,"error")}},[o,a,g]),Ge=s.useCallback(async()=>{if(!(!a||!d)){O(null),Z(!0);try{if(await d(a.id),!k.current)return;o("Settings pushed successfully","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Push settings failed";O(S),o(S,"error")}finally{k.current&&Z(!1)}}},[o,a,d]),Ze=s.useCallback(async()=>{if(!(!a||!b)){O(null),te(!0);try{if(await b(a.id),!k.current)return;o("Settings pulled successfully","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Pull settings failed";O(S),o(S,"error")}finally{k.current&&te(!1)}}},[o,a,b]),Qe=s.useCallback(async()=>{if(!(!a||!N)){O(null),Q(!0);try{if(await N(a.id),!k.current)return;o("Auth credentials synced successfully","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Auth sync failed";O(S),o(S,"error")}finally{k.current&&Q(!1)}}},[o,a,N]),es=s.useCallback(()=>{O(null)},[]),ss=s.useCallback(async()=>{if(!(!c||!v)){U(!0);try{const r=await v(c.id);if(!k.current)return;re(r)}catch(r){const S=r instanceof Error?r.message:"Failed to fetch container status";o(S,"error")}finally{k.current&&U(!1)}}},[o,c,v]),ts=s.useCallback(async()=>{if(!(!c||!l)){f(!0),oe(!0);try{const r=await l(c.id);if(!k.current)return;K(r)}catch(r){if(!k.current)return;K("");const S=r instanceof Error?r.message:"Failed to fetch container logs";o(S,"error")}finally{k.current&&oe(!1)}}},[o,c,l]),as=s.useCallback(async()=>{if(!a||D)return;const r=V.trim();if(!r){o("Name is required","error");return}if(a.type==="remote"&&!m.trim()){o("URL is required for remote nodes","error");return}if(!Number.isFinite(J)||J<1){o("Concurrency must be at least 1","error");return}X(!0);try{await u(a.id,{name:r,url:a.type==="remote"&&m.trim()||void 0,apiKey:a.type==="remote"&&T||void 0,maxConcurrent:J}),o(`Updated ${r}`,"success"),E(!1)}catch(S){const B=S instanceof Error?S.message:"Failed to update node";o(B,"error")}finally{X(!1)}},[o,T,D,J,V,a,u,m]);s.useEffect(()=>{!a?.dockerConfig||!_||!t||_(a.id).then(r=>{k.current&&Ae(r.needsRecreate)}).catch(()=>{k.current&&Ae(!1)})},[t,a,_]);const ns=s.useCallback(async()=>{if(!(!a||!h||!j||ie)){Re(!0);try{const r=await j(a.id,{image:h.image,volumeMounts:h.volumeMounts,environment:h.environment,resources:h.resources,host:h.host,extraClis:h.extraClis,persistence:h.persistence,containerName:h.containerName});if(!k.current)return;z(r),o("Docker config saved","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Failed to save Docker config";o(S,"error")}finally{k.current&&Re(!1)}}},[o,h,ie,a,j]),ls=s.useCallback(()=>{a&&(x(a.name),C(a.url??""),H(a.apiKey??""),W(a.maxConcurrent),E(!1))},[a]);if(!t||!a)return null;const ne=G?.status??c?.status,rs=Qs(ne);return e.jsxs("div",{className:"modal-overlay open",onClick:n,children:[e.jsxs("div",{className:"modal modal-lg node-detail-modal",onClick:r=>r.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:n,"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"}),!L&&e.jsxs("button",{className:"btn btn-sm",onClick:()=>E(!0),children:[e.jsx(fs,{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"}),L?e.jsx("input",{className:"input",value:V,onChange:r=>x(r.target.value),disabled:D}):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"}),L?e.jsx("input",{className:"input",type:"number",min:1,max:10,value:J,onChange:r=>W(Number(r.target.value)),disabled:D}):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"}),L?e.jsx("input",{className:"input",value:m,onChange:r=>C(r.target.value),disabled:D}):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"}),L?e.jsx("input",{className:"input",type:"password",value:T,onChange:r=>H(r.target.value),placeholder:"Leave blank to keep unchanged",disabled:D}):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:be(a.createdAt)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Updated"}),e.jsx("strong",{children:be(a.updatedAt)})]})]}),L&&e.jsxs("div",{className:"node-detail-modal__edit-actions",children:[e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:as,disabled:D,children:[e.jsx(Le,{size:14}),D?"Saving...":"Save"]}),e.jsxs("button",{className:"btn btn-sm",onClick:ls,disabled:D,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"," (",ge.length,")"]}),ge.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:ge.map(r=>e.jsxs("li",{className:"node-detail-modal__project-item",children:[e.jsx("span",{children:r.name}),e.jsx("code",{children:r.id})]},r.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:be(a.updatedAt)})]})]})]}),h&&e.jsxs("section",{className:"node-detail-modal__section node-detail-modal__docker-config",children:[e.jsxs("button",{className:"btn btn-sm node-detail-modal__docker-toggle",onClick:()=>Me(r=>!r),"aria-expanded":fe,children:[e.jsx(we,{size:14,className:fe?"node-detail-modal__docker-toggle-icon--expanded":""}),"Docker Configuration"]}),fe&&e.jsxs("div",{className:"node-detail-modal__docker-config-content",children:[e.jsx("div",{className:"node-detail-modal__grid",children:e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:h.image,onChange:r=>z({...h,image:r.target.value})})]})}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[h.volumeMounts.map((r,S)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:r.hostPath,placeholder:"Host path",onChange:B=>{const A=[...h.volumeMounts];A[S]={...A[S],hostPath:B.target.value},z({...h,volumeMounts:A})}}),e.jsx("input",{className:"input",value:r.containerPath,placeholder:"Container path",onChange:B=>{const A=[...h.volumeMounts];A[S]={...A[S],containerPath:B.target.value},z({...h,volumeMounts:A})}}),e.jsxs("select",{className:"input",value:r.mode??"rw",onChange:B=>{const A=[...h.volumeMounts];A[S]={...A[S],mode:B.target.value},z({...h,volumeMounts:A})},children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsxs("select",{className:"input",value:r.type??"volume",onChange:B=>{const A=[...h.volumeMounts];A[S]={...A[S],type:B.target.value},z({...h,volumeMounts:A})},children:[e.jsx("option",{value:"volume",children:"volume"}),e.jsx("option",{value:"bind",children:"bind"})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,volumeMounts:h.volumeMounts.filter((B,A)=>A!==S)}),children:"Remove"})]},`${r.hostPath}-${r.containerPath}-${S}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,volumeMounts:[...h.volumeMounts,{hostPath:"",containerPath:"",mode:"rw",type:"volume"}]}),children:"Add Mount"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[Object.entries(h.environment).map(([r,S])=>{const B=qe.test(r)&&!Pe[r];return e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:r,onChange:A=>{const xe={...h.environment};delete xe[r],xe[A.target.value]=S,z({...h,environment:xe})}}),e.jsx("input",{className:"input",value:B?"***":String(S),onChange:A=>z({...h,environment:{...h.environment,[r]:A.target.value}})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>Ee(A=>({...A,[r]:!A[r]})),children:Pe[r]?e.jsx(gs,{size:14}):e.jsx(xs,{size:14})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const A={...h.environment};delete A[r],z({...h,environment:A})},children:"Remove"})]},r)}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const r=`NEW_VAR_${Object.keys(h.environment).length+1}`;z({...h,environment:{...h.environment,[r]:""}})},children:"Add Variable"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Resources"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",type:"number",placeholder:"Memory bytes (2 GB = 2147483648)",value:h.resources?.memoryBytes??"",onChange:r=>z({...h,resources:{...h.resources,memoryBytes:r.target.value?Number(r.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"CPU count",value:h.resources?.cpuCount??"",onChange:r=>z({...h,resources:{...h.resources,cpuCount:r.target.value?Number(r.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"PIDs limit",value:h.resources?.pidsLimit??"",onChange:r=>z({...h,resources:{...h.resources,pidsLimit:r.target.value?Number(r.target.value):void 0}})})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Host Config"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Context name",value:h.host?.contextName??"",onChange:r=>z({...h,host:{...h.host,contextName:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"Docker host URL",value:h.host?.dockerHost??"",onChange:r=>z({...h,host:{...h.host,dockerHost:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS CA cert path",value:h.host?.tlsCaCert??"",onChange:r=>z({...h,host:{...h.host,tlsCaCert:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS cert path",value:h.host?.tlsCert??"",onChange:r=>z({...h,host:{...h.host,tlsCert:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS key path",value:h.host?.tlsKey??"",onChange:r=>z({...h,host:{...h.host,tlsKey:r.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:h.host?.tlsVerify??!0,onChange:r=>z({...h,host:{...h.host,tlsVerify:r.target.checked}})}),"TLS verify"]})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Extra CLIs"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[(h.extraClis??[]).map((r,S)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:r,onChange:B=>{const A=[...h.extraClis??[]];A[S]=B.target.value,z({...h,extraClis:A})}}),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,extraClis:(h.extraClis??[]).filter((B,A)=>A!==S)}),children:"Remove"})]},`${r}-${S}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,extraClis:[...h.extraClis??[],""]}),children:"Add CLI"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Persistence"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Volume name",value:h.persistence?.volumeName??"",onChange:r=>z({...h,persistence:{...h.persistence,volumeName:r.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:h.persistence?.retainOnDelete??!1,onChange:r=>z({...h,persistence:{...h.persistence,retainOnDelete:r.target.checked}})}),"Retain on delete"]})]})]}),e.jsxs("div",{className:"node-detail-modal__docker-meta",children:[e.jsxs("span",{children:["Config v",h.configVersion," • Updated ",Ne(h.lastUpdated??a.updatedAt)]}),Ye&&e.jsx("span",{className:"node-detail-modal__docker-recreate",children:"Needs Recreate"})]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>void ns(),disabled:ie,children:[e.jsx(Le,{size:14}),ie?"Saving...":"Save Docker Config"]})]})]}),c&&e.jsxs("section",{className:"node-detail-modal__section docker-management",children:[e.jsx("h4",{children:"Docker Management"}),e.jsxs("div",{className:"docker-management__status-card",children:[e.jsxs("div",{className:"docker-management__status-row",children:[e.jsx("span",{className:`docker-management__status-dot docker-management__status-dot--${rs}`,"aria-hidden":!0}),e.jsx("strong",{children:et(ne)}),(ne==="creating"||ne==="recreating"||ne==="restarting")&&e.jsx($e,{size:14,className:"spin","aria-hidden":!0})]}),e.jsxs("div",{className:"docker-management__status-meta",children:[ne==="running"&&e.jsxs("span",{children:["Uptime: ",Gs(G?.startedAt)]}),ne!=="running"&&G?.exitCode!==void 0&&e.jsxs("span",{children:["Exit code: ",G.exitCode]}),(G?.error||c.errorMessage)&&e.jsx("span",{children:G?.error??c.errorMessage})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>void ss(),disabled:!v||ae,children:ae?"Refreshing...":"Refresh Status"})]}),e.jsxs("div",{className:"node-detail-modal__grid docker-management__info-grid",children:[e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Image"}),e.jsx("strong",{children:e.jsxs("code",{children:[c.imageName,":",c.imageTag]})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Container ID"}),e.jsx("strong",{children:e.jsx("code",{children:c.containerId?c.containerId.slice(0,12):"—"})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Host"}),e.jsx("strong",{children:Xe})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Persistent Storage"}),e.jsx("strong",{children:c.persistentStorage?"Yes":"No"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Port"}),e.jsx("strong",{children:st(c.reachableUrl)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Resource Sizing"}),e.jsx("strong",{children:Je})]})]}),e.jsxs("div",{className:"docker-management__actions",children:[e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Oe,{size:14}),"Start"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),"Stop"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx($e,{size:14}),"Restart"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void ts(),disabled:!l,children:[e.jsx(ps,{size:14}),"View Logs"]})]}),he&&e.jsxs("div",{className:"docker-management__log-viewer",children:[e.jsxs("div",{className:"docker-management__log-viewer-header",children:[e.jsx("strong",{children:"Container Logs"}),e.jsx("button",{className:"btn-icon",onClick:()=>f(!1),"aria-label":"Close logs",children:e.jsx(ue,{size:14})})]}),se?e.jsx("p",{children:"Fetching logs..."}):e.jsx("pre",{children:I.trim()||"No logs available"})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsx("dl",{className:"docker-management__env-list",children:Object.entries(c.envVars).map(([r,S])=>e.jsxs("div",{children:[e.jsx("dt",{children:r}),e.jsx("dd",{children:tt(r,S)})]},r))})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsx("ul",{className:"docker-management__mounts-list",children:c.volumeMounts.map(r=>e.jsxs("li",{children:[e.jsxs("span",{children:[r.hostPath," → ",r.containerPath]}),r.readOnly&&e.jsx("span",{className:"node-card__type-badge",children:"Read-only"})]},`${r.hostPath}:${r.containerPath}`))})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Settings Sync"}),p&&e.jsxs("div",{className:"node-detail-modal__sync-status",children:[e.jsx("span",{className:`node-detail-modal__sync-dot ${Zs(p.syncState)}`,"aria-hidden":!0}),e.jsxs("span",{children:["Last sync: ",e.jsx("strong",{children:p.lastSyncAt?Ne(p.lastSyncAt):"Never synced"})]}),p.diffCount>0&&e.jsxs("span",{className:"node-detail-modal__sync-diff",children:["Differences: ",e.jsx("strong",{children:p.diffCount})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:Ge,disabled:q||!d,children:[e.jsx(Be,{size:14}),q?"Pushing...":"Push Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Ze,disabled:Y||!b,children:[e.jsx(Te,{size:14}),Y?"Pulling...":"Pull Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Qe,disabled:R||!N,children:[e.jsx(Ke,{size:14}),R?"Syncing...":"Sync Auth"]})]}),ee&&e.jsxs("div",{className:"node-detail-modal__sync-error",children:[e.jsx("span",{children:ee}),e.jsx("button",{className:"node-detail-modal__sync-error-dismiss",onClick:es,"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(Xs,{nodeId:a.id,entries:P,singleNode:!0})]})]}),e.jsxs("div",{className:"modal-actions node-detail-modal__actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:We,children:[e.jsx(Fe,{size:14}),"Health Check"]}),e.jsx("button",{className:"btn btn-sm",onClick:n,children:"Close"})]})]}),a.type==="remote"&&e.jsx(Ws,{isOpen:M,onClose:()=>$(!1),onResolve:y??(async()=>{}),conflicts:F,localNodeName:"Local",remoteNodeName:a.name,addToast:o})]})}const nt=15e3,lt=1e3;function rt(){const[t,n]=s.useState([]),[a,i]=s.useState(!0),[u,g]=s.useState(null),o=s.useRef(null),p=s.useRef(0),d=s.useCallback(async()=>{try{g(null);const y=await De();n(y)}catch(y){g(y instanceof Error?y.message:"Failed to fetch managed Docker nodes")}},[]);s.useEffect(()=>{let y=!1;async function c(){i(!0);try{const v=await De();y||(n(v),g(null))}catch(v){y||g(v instanceof Error?v.message:"Failed to fetch managed Docker nodes")}finally{y||i(!1)}}c();const w=()=>{if(document.visibilityState!=="visible")return;const v=Date.now();v-p.current<lt||(p.current=v,d())};return document.addEventListener("visibilitychange",w),()=>{y=!0,document.removeEventListener("visibilitychange",w)}},[d]),s.useEffect(()=>{if(!a)return o.current=setInterval(()=>{d()},nt),()=>{o.current&&(clearInterval(o.current),o.current=null)}},[a,d]);const b=s.useCallback(async y=>js(y),[]),N=s.useCallback(async(y,c)=>(await bs(y,c)).logs,[]),P=s.useCallback(async y=>{const c=await ys(y),w={...c,nodeId:c.nodeId??void 0,containerId:c.containerId??void 0,status:c.status,hostConfig:{type:c.hostConfig.host||c.hostConfig.context?"remote":"local",host:c.hostConfig.host,context:c.hostConfig.context},reachableUrl:c.reachableUrl??void 0,volumeMounts:c.volumeMounts.map(v=>({hostPath:v.hostPath,containerPath:v.containerPath,readOnly:v.mode==="ro"?!0:void 0})),persistentStorage:c.persistentStorage,resourceSizing:{cpuLimit:c.resourceSizing.cpus!==void 0?String(c.resourceSizing.cpus):void 0,memoryLimit:c.resourceSizing.memoryMB!==void 0?`${c.resourceSizing.memoryMB}MB`:void 0},errorMessage:c.errorMessage??void 0};return n(v=>[...v,w]),w},[]);return{dockerNodes:t,loading:a,error:u,refresh:d,getContainerStatus:b,getLogs:N,create:P}}function dt({addToast:t,onClose:n}){const{nodes:a,loading:i,error:u,refresh:g,register:o,update:p,unregister:d,healthCheck:b,patchDockerConfig:N,fetchDockerDiff:P}=vs(),{projects:y}=_s(),{syncStatusMap:c,pushSettings:w,pullSettings:v,syncAuth:l,trackNode:j,getAuthSyncState:_,getAuthProviders:k}=Es(),{dockerNodes:L,loading:E,refresh:V,getContainerStatus:x,getLogs:m,create:C}=rt(),[T,H]=s.useState(!1),[J,W]=s.useState(!1),[D,X]=s.useState(null);s.useEffect(()=>{const M=a.filter($=>$.type==="remote");for(const $ of M)j($.id)},[a,j]),s.useEffect(()=>{if(!D)return;const M=a.find($=>$.id===D.id)??null;X(M)},[a,D]);const q=s.useMemo(()=>{const M=a.length,$=a.filter(U=>U.status==="online").length,F=a.filter(U=>U.status==="offline"||U.status==="error").length,G=a.filter(U=>U.type==="remote").length,re=a.filter(U=>U.type==="remote"&&c[U.id]&&pe(c[U.id]).syncState==="synced").length,ae=L.length;return{total:M,online:$,offline:F,remote:G,synced:re,docker:ae}},[L.length,a,c]),Z=s.useCallback(async M=>{await o(M)},[o]),Y=s.useCallback(async M=>{try{await C(M),t(`Docker node "${M.name}" created`,"success"),W(!1)}catch($){const F=$ instanceof Error?$.message:"Failed to create Docker node";throw t(F,"error"),$}},[t,C]),te=s.useMemo(()=>{const M=new Map;for(const $ of L)$.nodeId&&M.set($.nodeId,$);return M},[L]),R=s.useCallback(async()=>{try{await Promise.all([g(),V()])}catch{t("Failed to refresh nodes","error")}},[t,g,V]),Q=s.useCallback(async M=>{try{await b(M),t("Node health check complete","success")}catch($){const F=$ instanceof Error?$.message:"Health check failed";t(F,"error")}},[t,b]),ee=s.useCallback(async M=>{try{await d(M),t("Node removed","success"),D?.id===M&&X(null)}catch($){const F=$ instanceof Error?$.message:"Failed to remove node";t(F,"error")}},[t,D?.id,d]),O=s.useCallback(async(M,$)=>{await p(M,$)},[p]);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(Ve,{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:n,"aria-label":"Close nodes view",children:e.jsx(ue,{size:16})}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void R(),disabled:i||E,children:[e.jsx(_e,{size:14,className:i?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>H(!0),children:[e.jsx(me,{size:14}),"Add Node"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>W(!0),title:"Add a managed Docker node",children:[e.jsx(ye,{size:14}),"Add 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:q.total})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--online","data-testid":"nodes-stat-online",children:[e.jsxs("span",{children:[e.jsx(ws,{size:14})," Online"]}),e.jsx("strong",{children:q.online})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--offline","data-testid":"nodes-stat-offline",children:[e.jsxs("span",{children:[e.jsx(Cs,{size:14})," Offline"]}),e.jsx("strong",{children:q.offline})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-remote",children:[e.jsxs("span",{children:[e.jsx(Ns,{size:14})," Remote"]}),e.jsx("strong",{children:q.remote})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--synced","data-testid":"nodes-stat-synced",children:[e.jsxs("span",{children:[e.jsx(_e,{size:14})," Synced"]}),e.jsx("strong",{children:q.synced})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-docker",children:[e.jsxs("span",{children:[e.jsx(ye,{size:14})," Docker"]}),e.jsx("strong",{children:q.docker})]})]}),u&&e.jsx("div",{className:"nodes-view-error",children:u}),!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(Os,{nodes:a})]}),i?e.jsx("div",{className:"nodes-view-grid",children:Array.from({length:4}).map((M,$)=>e.jsx("div",{className:"node-card node-card--loading","aria-hidden":!0},$))}):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:()=>H(!0),children:[e.jsx(me,{size:14}),"Add First Node"]})]}):e.jsx("div",{className:"nodes-view-grid",children:a.map(M=>{const $=M.type==="remote"&&c[M.id]?pe(c[M.id]):void 0;return e.jsx(Is,{node:M,projects:y,onHealthCheck:F=>{Q(F)},onEdit:F=>X(F),onRemove:F=>{ee(F)},isLoading:i,syncStatus:$,authSyncState:M.type==="remote"?_(M.id):void 0,authSyncProviders:M.type==="remote"?k(M.id):void 0,managedDockerNode:te.get(M.id)},M.id)})}),e.jsx(Bs,{isOpen:T,onClose:()=>H(!1),onSubmit:Z,addToast:t}),e.jsx(Ys,{isOpen:J,onClose:()=>W(!1),onSubmit:Y,addToast:t}),e.jsx(at,{isOpen:D!==null,onClose:()=>X(null),node:D,projects:y,onUpdate:O,onHealthCheck:Q,addToast:t,syncStatus:D?.type==="remote"&&D&&c[D.id]?pe(c[D.id]):void 0,onPushSettings:w,onPullSettings:v,onSyncAuth:l,managedDockerNode:D?te.get(D.id):void 0,onFetchContainerStatus:x,onFetchLogs:m,onUpdateDockerConfig:N,onFetchDockerConfigDiff:P})]})}export{dt as NodesView};
14
+ `)}function Ws({isOpen:t,onClose:n,onResolve:a,conflicts:i,localNodeName:u,remoteNodeName:g,addToast:o}){const[p,d]=s.useState({}),[b,N]=s.useState(!1);s.useEffect(()=>{const l={};for(const j of i)p[j.key]||(l[j.key]={resolution:"local"});Object.keys(l).length>0&&d(j=>({...j,...l}))},[i,p]),s.useEffect(()=>{if(!t)return;const l=j=>{j.key==="Escape"&&(j.preventDefault(),n())};return document.addEventListener("keydown",l),()=>document.removeEventListener("keydown",l)},[t,n]);const P=s.useCallback((l,j)=>{d(_=>{const k=_[l]??{};return j==="manual"?{..._,[l]:{resolution:"manual",manualValue:k.manualValue??JSON.stringify(i.find(L=>L.key===l)?.localValue??null,null,2)}}:{..._,[l]:{resolution:j}}})},[i]),y=s.useCallback((l,j)=>{d(_=>({..._,[l]:{..._[l],resolution:"manual",manualValue:j}}))},[]),c=s.useCallback(l=>{const j={};for(const _ of i)j[_.key]={resolution:l};d(j)},[i]),w=s.useCallback(async()=>{N(!0);try{const l=i.map(j=>{const _=p[j.key]??{resolution:"local"};let k;switch(_.resolution){case"remote":k=j.remoteValue;break;case"manual":try{k=JSON.parse(_.manualValue??"null")}catch{k=_.manualValue??null}break;case"local":default:k=j.localValue;break}return{key:j.key,value:k}});await a(l),o("Settings conflicts resolved successfully","success"),n()}catch(l){const j=l instanceof Error?l.message:"Failed to resolve conflicts";o(j,"error")}finally{N(!1)}},[o,i,n,a,p]),v=s.useMemo(()=>{const l={};for(const j of i)l[j.key]=Js(j.localValue,j.remoteValue);return l},[i]);return!t||i.length===0?null:e.jsx("div",{className:"modal-overlay open",onClick:n,children:e.jsxs("div",{className:"modal modal-lg settings-sync-conflict-modal",onClick:l=>l.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:n,"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(l=>{const j=p[l.key]??{resolution:"local"},_=v[l.key];return e.jsxs("div",{className:"settings-sync-conflict-modal__conflict-item",children:[e.jsx("div",{className:"settings-sync-conflict-modal__key",children:l.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:_})})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__diff-side",children:[e.jsx("div",{className:"settings-sync-conflict-modal__diff-label",children:g}),e.jsx("div",{className:"settings-sync-conflict-modal__diff-content",children:e.jsx("pre",{style:{margin:0,whiteSpace:"pre-wrap"},children:_})})]})]}),e.jsxs("div",{className:"settings-sync-conflict-modal__resolution",children:[e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:j.resolution==="local",onChange:()=>P(l.key,"local")}),"Keep Local"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:j.resolution==="remote",onChange:()=>P(l.key,"remote")}),"Keep Remote"]}),e.jsxs("label",{children:[e.jsx("input",{type:"radio",name:`resolution-${l.key}`,checked:j.resolution==="manual",onChange:()=>P(l.key,"manual")}),"Merge Manually"]})]}),j.resolution==="manual"&&e.jsx("textarea",{className:"settings-sync-conflict-modal__manual-input",value:j.manualValue??"",onChange:k=>y(l.key,k.target.value),placeholder:"Enter JSON value..."})]},l.key)})}),e.jsxs("div",{className:"settings-sync-conflict-modal__bulk-actions",children:[e.jsx("button",{className:"btn btn-sm",onClick:()=>c("local"),type:"button",children:"Resolve All: Keep Local"}),e.jsx("button",{className:"btn btn-sm",onClick:()=>c("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:n,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary btn-sm",onClick:w,disabled:b,children:b?"Resolving...":"Confirm"})]})]})})}const qe=/(KEY|TOKEN|SECRET|PASSWORD)/i;function be(t){if(!t)return"—";const n=new Date(t);return Number.isNaN(n.getTime())?"—":n.toLocaleString()}function Gs(t){if(!t)return"—";const n=new Date(t),a=Date.now();if(Number.isNaN(n.getTime())||n.getTime()>a)return"—";const i=Math.floor((a-n.getTime())/1e3);if(i<60)return`${i}s`;const u=Math.floor(i/60);if(u<60)return`${u}m`;const g=Math.floor(u/60);return g<24?`${g}h ${u%60}m`:`${Math.floor(g/24)}d ${g%24}h`}function Qs(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 Zs(t){return t==="running"?"success":t==="creating"||t==="recreating"||t==="restarting"?"warning":"error"}function et(t){return t?`${t.charAt(0).toUpperCase()}${t.slice(1)}`:"Unknown"}function st(t){if(!t)return"—";try{const n=new URL(t);return n.port?n.port:n.protocol==="https:"?"443":n.protocol==="http:"?"80":"—"}catch{return"—"}}function tt(t,n){return qe.test(t)?"••••••••":n}function at({isOpen:t,onClose:n,node:a,projects:i,onUpdate:u,onHealthCheck:g,addToast:o,syncStatus:p,onPushSettings:d,onPullSettings:b,onSyncAuth:N,syncHistory:P=[],onResolveConflicts:y,managedDockerNode:c,containerStatus:w,onFetchContainerStatus:v,onFetchLogs:l,onUpdateDockerConfig:j,onFetchDockerConfigDiff:_}){const k=s.useRef(!0),[L,E]=s.useState(!1),[V,x]=s.useState(""),[m,C]=s.useState(""),[B,H]=s.useState(""),[J,W]=s.useState(2),[D,X]=s.useState(!1),[q,Q]=s.useState(!1),[Y,te]=s.useState(!1),[R,Z]=s.useState(!1),[ee,O]=s.useState(null),[M,$]=s.useState(!1),[F]=s.useState([]),[G,re]=s.useState(w),[ae,U]=s.useState(!1),[he,f]=s.useState(!1),[I,K]=s.useState(""),[se,oe]=s.useState(!1),[fe,Me]=s.useState(!1),[h,z]=s.useState(a?.dockerConfig??null),[Pe,Ee]=s.useState({}),[ie,Re]=s.useState(!1),[Ye,Ae]=s.useState(!1);s.useEffect(()=>(k.current=!0,()=>{k.current=!1}),[]),s.useEffect(()=>{re(w)},[w]),s.useEffect(()=>{if(!a||!t){E(!1),f(!1),K("");return}x(a.name),C(a.url??""),H(a.apiKey??""),W(a.maxConcurrent),E(!1),z(a.dockerConfig??null),Me(!1),Ee({})},[t,a]),s.useEffect(()=>{if(!t)return;const r=S=>{S.key==="Escape"&&(S.preventDefault(),n())};return document.addEventListener("keydown",r),()=>document.removeEventListener("keydown",r)},[t,n]);const ge=s.useMemo(()=>a?He(i,a):[],[a,i]),Xe=s.useMemo(()=>c?c.hostConfig.type==="remote"?c.hostConfig.host??"—":"Local Docker":"—",[c]),Je=s.useMemo(()=>!c?.resourceSizing?.cpuLimit&&!c?.resourceSizing?.memoryLimit?"Default":`${c.resourceSizing?.cpuLimit??"Default CPU"} / ${c.resourceSizing?.memoryLimit??"Default memory"}`,[c]),We=s.useCallback(async()=>{if(a)try{if(await g(a.id),!k.current)return;o(`Health check completed for ${a.name}`,"success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Health check failed";o(S,"error")}},[o,a,g]),Ge=s.useCallback(async()=>{if(!(!a||!d)){O(null),Q(!0);try{if(await d(a.id),!k.current)return;o("Settings pushed successfully","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Push settings failed";O(S),o(S,"error")}finally{k.current&&Q(!1)}}},[o,a,d]),Qe=s.useCallback(async()=>{if(!(!a||!b)){O(null),te(!0);try{if(await b(a.id),!k.current)return;o("Settings pulled successfully","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Pull settings failed";O(S),o(S,"error")}finally{k.current&&te(!1)}}},[o,a,b]),Ze=s.useCallback(async()=>{if(!(!a||!N)){O(null),Z(!0);try{if(await N(a.id),!k.current)return;o("Auth credentials synced successfully","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Auth sync failed";O(S),o(S,"error")}finally{k.current&&Z(!1)}}},[o,a,N]),es=s.useCallback(()=>{O(null)},[]),ss=s.useCallback(async()=>{if(!(!c||!v)){U(!0);try{const r=await v(c.id);if(!k.current)return;re(r)}catch(r){const S=r instanceof Error?r.message:"Failed to fetch container status";o(S,"error")}finally{k.current&&U(!1)}}},[o,c,v]),ts=s.useCallback(async()=>{if(!(!c||!l)){f(!0),oe(!0);try{const r=await l(c.id);if(!k.current)return;K(r)}catch(r){if(!k.current)return;K("");const S=r instanceof Error?r.message:"Failed to fetch container logs";o(S,"error")}finally{k.current&&oe(!1)}}},[o,c,l]),as=s.useCallback(async()=>{if(!a||D)return;const r=V.trim();if(!r){o("Name is required","error");return}if(a.type==="remote"&&!m.trim()){o("URL is required for remote nodes","error");return}if(!Number.isFinite(J)||J<1){o("Concurrency must be at least 1","error");return}X(!0);try{await u(a.id,{name:r,url:a.type==="remote"&&m.trim()||void 0,apiKey:a.type==="remote"&&B||void 0,maxConcurrent:J}),o(`Updated ${r}`,"success"),E(!1)}catch(S){const T=S instanceof Error?S.message:"Failed to update node";o(T,"error")}finally{X(!1)}},[o,B,D,J,V,a,u,m]);s.useEffect(()=>{!a?.dockerConfig||!_||!t||_(a.id).then(r=>{k.current&&Ae(r.needsRecreate)}).catch(()=>{k.current&&Ae(!1)})},[t,a,_]);const ns=s.useCallback(async()=>{if(!(!a||!h||!j||ie)){Re(!0);try{const r=await j(a.id,{image:h.image,volumeMounts:h.volumeMounts,environment:h.environment,resources:h.resources,host:h.host,extraClis:h.extraClis,persistence:h.persistence,containerName:h.containerName});if(!k.current)return;z(r),o("Docker config saved","success")}catch(r){if(!k.current)return;const S=r instanceof Error?r.message:"Failed to save Docker config";o(S,"error")}finally{k.current&&Re(!1)}}},[o,h,ie,a,j]),ls=s.useCallback(()=>{a&&(x(a.name),C(a.url??""),H(a.apiKey??""),W(a.maxConcurrent),E(!1))},[a]);if(!t||!a)return null;const ne=G?.status??c?.status,rs=Zs(ne);return e.jsxs("div",{className:"modal-overlay open",onClick:n,children:[e.jsxs("div",{className:"modal modal-lg node-detail-modal",onClick:r=>r.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:n,"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"}),!L&&e.jsxs("button",{className:"btn btn-sm",onClick:()=>E(!0),children:[e.jsx(fs,{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"}),L?e.jsx("input",{className:"input",value:V,onChange:r=>x(r.target.value),disabled:D}):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"}),L?e.jsx("input",{className:"input",type:"number",min:1,max:10,value:J,onChange:r=>W(Number(r.target.value)),disabled:D}):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"}),L?e.jsx("input",{className:"input",value:m,onChange:r=>C(r.target.value),disabled:D}):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"}),L?e.jsx("input",{className:"input",type:"password",value:B,onChange:r=>H(r.target.value),placeholder:"Leave blank to keep unchanged",disabled:D}):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:be(a.createdAt)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Updated"}),e.jsx("strong",{children:be(a.updatedAt)})]})]}),L&&e.jsxs("div",{className:"node-detail-modal__edit-actions",children:[e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:as,disabled:D,children:[e.jsx(Le,{size:14}),D?"Saving...":"Save"]}),e.jsxs("button",{className:"btn btn-sm",onClick:ls,disabled:D,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"," (",ge.length,")"]}),ge.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:ge.map(r=>e.jsxs("li",{className:"node-detail-modal__project-item",children:[e.jsx("span",{children:r.name}),e.jsx("code",{children:r.id})]},r.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:be(a.updatedAt)})]})]})]}),h&&e.jsxs("section",{className:"node-detail-modal__section node-detail-modal__docker-config",children:[e.jsxs("button",{className:"btn btn-sm node-detail-modal__docker-toggle",onClick:()=>Me(r=>!r),"aria-expanded":fe,children:[e.jsx(we,{size:14,className:fe?"node-detail-modal__docker-toggle-icon--expanded":""}),"Docker Configuration"]}),fe&&e.jsxs("div",{className:"node-detail-modal__docker-config-content",children:[e.jsx("div",{className:"node-detail-modal__grid",children:e.jsxs("label",{className:"node-detail-modal__field node-detail-modal__field--full",children:[e.jsx("span",{children:"Image"}),e.jsx("input",{className:"input",value:h.image,onChange:r=>z({...h,image:r.target.value})})]})}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[h.volumeMounts.map((r,S)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:r.hostPath,placeholder:"Host path",onChange:T=>{const A=[...h.volumeMounts];A[S]={...A[S],hostPath:T.target.value},z({...h,volumeMounts:A})}}),e.jsx("input",{className:"input",value:r.containerPath,placeholder:"Container path",onChange:T=>{const A=[...h.volumeMounts];A[S]={...A[S],containerPath:T.target.value},z({...h,volumeMounts:A})}}),e.jsxs("select",{className:"input",value:r.mode??"rw",onChange:T=>{const A=[...h.volumeMounts];A[S]={...A[S],mode:T.target.value},z({...h,volumeMounts:A})},children:[e.jsx("option",{value:"rw",children:"rw"}),e.jsx("option",{value:"ro",children:"ro"})]}),e.jsxs("select",{className:"input",value:r.type??"volume",onChange:T=>{const A=[...h.volumeMounts];A[S]={...A[S],type:T.target.value},z({...h,volumeMounts:A})},children:[e.jsx("option",{value:"volume",children:"volume"}),e.jsx("option",{value:"bind",children:"bind"})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,volumeMounts:h.volumeMounts.filter((T,A)=>A!==S)}),children:"Remove"})]},`${r.hostPath}-${r.containerPath}-${S}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,volumeMounts:[...h.volumeMounts,{hostPath:"",containerPath:"",mode:"rw",type:"volume"}]}),children:"Add Mount"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[Object.entries(h.environment).map(([r,S])=>{const T=qe.test(r)&&!Pe[r];return e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:r,onChange:A=>{const xe={...h.environment};delete xe[r],xe[A.target.value]=S,z({...h,environment:xe})}}),e.jsx("input",{className:"input",value:T?"***":String(S),onChange:A=>z({...h,environment:{...h.environment,[r]:A.target.value}})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>Ee(A=>({...A,[r]:!A[r]})),children:Pe[r]?e.jsx(gs,{size:14}):e.jsx(xs,{size:14})}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const A={...h.environment};delete A[r],z({...h,environment:A})},children:"Remove"})]},r)}),e.jsx("button",{className:"btn btn-sm",onClick:()=>{const r=`NEW_VAR_${Object.keys(h.environment).length+1}`;z({...h,environment:{...h.environment,[r]:""}})},children:"Add Variable"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Resources"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",type:"number",placeholder:"Memory bytes (2 GB = 2147483648)",value:h.resources?.memoryBytes??"",onChange:r=>z({...h,resources:{...h.resources,memoryBytes:r.target.value?Number(r.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"CPU count",value:h.resources?.cpuCount??"",onChange:r=>z({...h,resources:{...h.resources,cpuCount:r.target.value?Number(r.target.value):void 0}})}),e.jsx("input",{className:"input",type:"number",placeholder:"PIDs limit",value:h.resources?.pidsLimit??"",onChange:r=>z({...h,resources:{...h.resources,pidsLimit:r.target.value?Number(r.target.value):void 0}})})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Host Config"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Context name",value:h.host?.contextName??"",onChange:r=>z({...h,host:{...h.host,contextName:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"Docker host URL",value:h.host?.dockerHost??"",onChange:r=>z({...h,host:{...h.host,dockerHost:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS CA cert path",value:h.host?.tlsCaCert??"",onChange:r=>z({...h,host:{...h.host,tlsCaCert:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS cert path",value:h.host?.tlsCert??"",onChange:r=>z({...h,host:{...h.host,tlsCert:r.target.value}})}),e.jsx("input",{className:"input",placeholder:"TLS key path",value:h.host?.tlsKey??"",onChange:r=>z({...h,host:{...h.host,tlsKey:r.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:h.host?.tlsVerify??!0,onChange:r=>z({...h,host:{...h.host,tlsVerify:r.target.checked}})}),"TLS verify"]})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Extra CLIs"}),e.jsxs("div",{className:"node-detail-modal__docker-list",children:[(h.extraClis??[]).map((r,S)=>e.jsxs("div",{className:"node-detail-modal__docker-row",children:[e.jsx("input",{className:"input",value:r,onChange:T=>{const A=[...h.extraClis??[]];A[S]=T.target.value,z({...h,extraClis:A})}}),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,extraClis:(h.extraClis??[]).filter((T,A)=>A!==S)}),children:"Remove"})]},`${r}-${S}`)),e.jsx("button",{className:"btn btn-sm",onClick:()=>z({...h,extraClis:[...h.extraClis??[],""]}),children:"Add CLI"})]})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Persistence"}),e.jsxs("div",{className:"node-detail-modal__docker-stack",children:[e.jsx("input",{className:"input",placeholder:"Volume name",value:h.persistence?.volumeName??"",onChange:r=>z({...h,persistence:{...h.persistence,volumeName:r.target.value}})}),e.jsxs("label",{className:"node-detail-modal__checkbox",children:[e.jsx("input",{type:"checkbox",checked:h.persistence?.retainOnDelete??!1,onChange:r=>z({...h,persistence:{...h.persistence,retainOnDelete:r.target.checked}})}),"Retain on delete"]})]})]}),e.jsxs("div",{className:"node-detail-modal__docker-meta",children:[e.jsxs("span",{children:["Config v",h.configVersion," • Updated ",Ne(h.lastUpdated??a.updatedAt)]}),Ye&&e.jsx("span",{className:"node-detail-modal__docker-recreate",children:"Needs Recreate"})]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>void ns(),disabled:ie,children:[e.jsx(Le,{size:14}),ie?"Saving...":"Save Docker Config"]})]})]}),c&&e.jsxs("section",{className:"node-detail-modal__section docker-management",children:[e.jsx("h4",{children:"Docker Management"}),e.jsxs("div",{className:"docker-management__status-card",children:[e.jsxs("div",{className:"docker-management__status-row",children:[e.jsx("span",{className:`docker-management__status-dot docker-management__status-dot--${rs}`,"aria-hidden":!0}),e.jsx("strong",{children:et(ne)}),(ne==="creating"||ne==="recreating"||ne==="restarting")&&e.jsx($e,{size:14,className:"spin","aria-hidden":!0})]}),e.jsxs("div",{className:"docker-management__status-meta",children:[ne==="running"&&e.jsxs("span",{children:["Uptime: ",Gs(G?.startedAt)]}),ne!=="running"&&G?.exitCode!==void 0&&e.jsxs("span",{children:["Exit code: ",G.exitCode]}),(G?.error||c.errorMessage)&&e.jsx("span",{children:G?.error??c.errorMessage})]}),e.jsx("button",{className:"btn btn-sm",onClick:()=>void ss(),disabled:!v||ae,children:ae?"Refreshing...":"Refresh Status"})]}),e.jsxs("div",{className:"node-detail-modal__grid docker-management__info-grid",children:[e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Image"}),e.jsx("strong",{children:e.jsxs("code",{children:[c.imageName,":",c.imageTag]})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Container ID"}),e.jsx("strong",{children:e.jsx("code",{children:c.containerId?c.containerId.slice(0,12):"—"})})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Host"}),e.jsx("strong",{children:Xe})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Persistent Storage"}),e.jsx("strong",{children:c.persistentStorage?"Yes":"No"})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Port"}),e.jsx("strong",{children:st(c.reachableUrl)})]}),e.jsxs("div",{className:"node-detail-modal__field",children:[e.jsx("span",{children:"Resource Sizing"}),e.jsx("strong",{children:Je})]})]}),e.jsxs("div",{className:"docker-management__actions",children:[e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Oe,{size:14}),"Start"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx(Ue,{size:14}),"Stop"]}),e.jsxs("button",{className:"btn btn-sm",disabled:!0,title:"Available after FN-3113",children:[e.jsx($e,{size:14}),"Restart"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void ts(),disabled:!l,children:[e.jsx(ps,{size:14}),"View Logs"]})]}),he&&e.jsxs("div",{className:"docker-management__log-viewer",children:[e.jsxs("div",{className:"docker-management__log-viewer-header",children:[e.jsx("strong",{children:"Container Logs"}),e.jsx("button",{className:"btn-icon",onClick:()=>f(!1),"aria-label":"Close logs",children:e.jsx(ue,{size:14})})]}),se?e.jsx("p",{children:"Fetching logs..."}):e.jsx("pre",{children:I.trim()||"No logs available"})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Environment Variables"}),e.jsx("dl",{className:"docker-management__env-list",children:Object.entries(c.envVars).map(([r,S])=>e.jsxs("div",{children:[e.jsx("dt",{children:r}),e.jsx("dd",{children:tt(r,S)})]},r))})]}),e.jsxs("details",{children:[e.jsx("summary",{children:"Volume Mounts"}),e.jsx("ul",{className:"docker-management__mounts-list",children:c.volumeMounts.map(r=>e.jsxs("li",{children:[e.jsxs("span",{children:[r.hostPath," → ",r.containerPath]}),r.readOnly&&e.jsx("span",{className:"node-card__type-badge",children:"Read-only"})]},`${r.hostPath}:${r.containerPath}`))})]})]}),a.type==="remote"&&e.jsxs("section",{className:"node-detail-modal__section",children:[e.jsx("h4",{children:"Settings Sync"}),p&&e.jsxs("div",{className:"node-detail-modal__sync-status",children:[e.jsx("span",{className:`node-detail-modal__sync-dot ${Qs(p.syncState)}`,"aria-hidden":!0}),e.jsxs("span",{children:["Last sync: ",e.jsx("strong",{children:p.lastSyncAt?Ne(p.lastSyncAt):"Never synced"})]}),p.diffCount>0&&e.jsxs("span",{className:"node-detail-modal__sync-diff",children:["Differences: ",e.jsx("strong",{children:p.diffCount})]})]}),e.jsxs("div",{className:"node-detail-modal__sync-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:Ge,disabled:q||!d,children:[e.jsx(Be,{size:14}),q?"Pushing...":"Push Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Qe,disabled:Y||!b,children:[e.jsx(Te,{size:14}),Y?"Pulling...":"Pull Settings"]}),e.jsxs("button",{className:"btn btn-sm",onClick:Ze,disabled:R||!N,children:[e.jsx(Ke,{size:14}),R?"Syncing...":"Sync Auth"]})]}),ee&&e.jsxs("div",{className:"node-detail-modal__sync-error",children:[e.jsx("span",{children:ee}),e.jsx("button",{className:"node-detail-modal__sync-error-dismiss",onClick:es,"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(Xs,{nodeId:a.id,entries:P,singleNode:!0})]})]}),e.jsxs("div",{className:"modal-actions node-detail-modal__actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:We,children:[e.jsx(Fe,{size:14}),"Health Check"]}),e.jsx("button",{className:"btn btn-sm",onClick:n,children:"Close"})]})]}),a.type==="remote"&&e.jsx(Ws,{isOpen:M,onClose:()=>$(!1),onResolve:y??(async()=>{}),conflicts:F,localNodeName:"Local",remoteNodeName:a.name,addToast:o})]})}const nt=15e3,lt=1e3;function rt(){const[t,n]=s.useState([]),[a,i]=s.useState(!0),[u,g]=s.useState(null),o=s.useRef(null),p=s.useRef(0),d=s.useCallback(async()=>{try{g(null);const y=await De();n(y)}catch(y){g(y instanceof Error?y.message:"Failed to fetch managed Docker nodes")}},[]);s.useEffect(()=>{let y=!1;async function c(){i(!0);try{const v=await De();y||(n(v),g(null))}catch(v){y||g(v instanceof Error?v.message:"Failed to fetch managed Docker nodes")}finally{y||i(!1)}}c();const w=()=>{if(document.visibilityState!=="visible")return;const v=Date.now();v-p.current<lt||(p.current=v,d())};return document.addEventListener("visibilitychange",w),()=>{y=!0,document.removeEventListener("visibilitychange",w)}},[d]),s.useEffect(()=>{if(!a)return o.current=setInterval(()=>{d()},nt),()=>{o.current&&(clearInterval(o.current),o.current=null)}},[a,d]);const b=s.useCallback(async y=>js(y),[]),N=s.useCallback(async(y,c)=>(await bs(y,c)).logs,[]),P=s.useCallback(async y=>{const c=await ys(y),w={...c,nodeId:c.nodeId??void 0,containerId:c.containerId??void 0,status:c.status,hostConfig:{type:c.hostConfig.host||c.hostConfig.context?"remote":"local",host:c.hostConfig.host,context:c.hostConfig.context},reachableUrl:c.reachableUrl??void 0,volumeMounts:c.volumeMounts.map(v=>({hostPath:v.hostPath,containerPath:v.containerPath,readOnly:v.mode==="ro"?!0:void 0})),persistentStorage:c.persistentStorage,resourceSizing:{cpuLimit:c.resourceSizing.cpus!==void 0?String(c.resourceSizing.cpus):void 0,memoryLimit:c.resourceSizing.memoryMB!==void 0?`${c.resourceSizing.memoryMB}MB`:void 0},errorMessage:c.errorMessage??void 0};return n(v=>[...v,w]),w},[]);return{dockerNodes:t,loading:a,error:u,refresh:d,getContainerStatus:b,getLogs:N,create:P}}function ut({addToast:t,onClose:n}){const{nodes:a,loading:i,error:u,refresh:g,register:o,update:p,unregister:d,healthCheck:b,patchDockerConfig:N,fetchDockerDiff:P}=vs(),{projects:y}=_s(),{syncStatusMap:c,pushSettings:w,pullSettings:v,syncAuth:l,trackNode:j,getAuthSyncState:_,getAuthProviders:k}=Es(),{dockerNodes:L,loading:E,refresh:V,getContainerStatus:x,getLogs:m,create:C}=rt(),[B,H]=s.useState(!1),[J,W]=s.useState(!1),[D,X]=s.useState(null);s.useEffect(()=>{const M=a.filter($=>$.type==="remote");for(const $ of M)j($.id)},[a,j]),s.useEffect(()=>{if(!D)return;const M=a.find($=>$.id===D.id)??null;X(M)},[a,D]);const q=s.useMemo(()=>{const M=a.length,$=a.filter(U=>U.status==="online").length,F=a.filter(U=>U.status==="offline"||U.status==="error").length,G=a.filter(U=>U.type==="remote").length,re=a.filter(U=>U.type==="remote"&&c[U.id]&&pe(c[U.id]).syncState==="synced").length,ae=L.length;return{total:M,online:$,offline:F,remote:G,synced:re,docker:ae}},[L.length,a,c]),Q=s.useCallback(async M=>{await o(M)},[o]),Y=s.useCallback(async M=>{try{await C(M),t(`Docker node "${M.name}" created`,"success"),W(!1)}catch($){const F=$ instanceof Error?$.message:"Failed to create Docker node";throw t(F,"error"),$}},[t,C]),te=s.useMemo(()=>{const M=new Map;for(const $ of L)$.nodeId&&M.set($.nodeId,$);return M},[L]),R=s.useCallback(async()=>{try{await Promise.all([g(),V()])}catch{t("Failed to refresh nodes","error")}},[t,g,V]),Z=s.useCallback(async M=>{try{await b(M),t("Node health check complete","success")}catch($){const F=$ instanceof Error?$.message:"Health check failed";t(F,"error")}},[t,b]),ee=s.useCallback(async M=>{try{await d(M),t("Node removed","success"),D?.id===M&&X(null)}catch($){const F=$ instanceof Error?$.message:"Failed to remove node";t(F,"error")}},[t,D?.id,d]),O=s.useCallback(async(M,$)=>{await p(M,$)},[p]);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(Ve,{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:n,"aria-label":"Close nodes view",children:e.jsx(ue,{size:16})}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>void R(),disabled:i||E,children:[e.jsx(_e,{size:14,className:i?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>H(!0),children:[e.jsx(me,{size:14}),"Add Node"]}),e.jsxs("button",{className:"btn btn-sm",onClick:()=>W(!0),title:"Add a managed Docker node",children:[e.jsx(ye,{size:14}),"Add 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:q.total})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--online","data-testid":"nodes-stat-online",children:[e.jsxs("span",{children:[e.jsx(ws,{size:14})," Online"]}),e.jsx("strong",{children:q.online})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--offline","data-testid":"nodes-stat-offline",children:[e.jsxs("span",{children:[e.jsx(Cs,{size:14})," Offline"]}),e.jsx("strong",{children:q.offline})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-remote",children:[e.jsxs("span",{children:[e.jsx(Ns,{size:14})," Remote"]}),e.jsx("strong",{children:q.remote})]}),e.jsxs("div",{className:"nodes-view-stat nodes-view-stat--synced","data-testid":"nodes-stat-synced",children:[e.jsxs("span",{children:[e.jsx(_e,{size:14})," Synced"]}),e.jsx("strong",{children:q.synced})]}),e.jsxs("div",{className:"nodes-view-stat","data-testid":"nodes-stat-docker",children:[e.jsxs("span",{children:[e.jsx(ye,{size:14})," Docker"]}),e.jsx("strong",{children:q.docker})]})]}),u&&e.jsx("div",{className:"nodes-view-error",children:u}),!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(Os,{nodes:a})]}),i?e.jsx("div",{className:"nodes-view-grid",children:Array.from({length:4}).map((M,$)=>e.jsx("div",{className:"node-card node-card--loading","aria-hidden":!0},$))}):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:()=>H(!0),children:[e.jsx(me,{size:14}),"Add First Node"]})]}):e.jsx("div",{className:"nodes-view-grid",children:a.map(M=>{const $=M.type==="remote"&&c[M.id]?pe(c[M.id]):void 0;return e.jsx(Is,{node:M,projects:y,onHealthCheck:F=>{Z(F)},onEdit:F=>X(F),onRemove:F=>{ee(F)},isLoading:i,syncStatus:$,authSyncState:M.type==="remote"?_(M.id):void 0,authSyncProviders:M.type==="remote"?k(M.id):void 0,managedDockerNode:te.get(M.id)},M.id)})}),e.jsx(Ts,{isOpen:B,onClose:()=>H(!1),onSubmit:Q,addToast:t}),e.jsx(Ys,{isOpen:J,onClose:()=>W(!1),onSubmit:Y,addToast:t}),e.jsx(at,{isOpen:D!==null,onClose:()=>X(null),node:D,projects:y,onUpdate:O,onHealthCheck:Z,addToast:t,syncStatus:D?.type==="remote"&&D&&c[D.id]?pe(c[D.id]):void 0,onPushSettings:w,onPullSettings:v,onSyncAuth:l,managedDockerNode:D?te.get(D.id):void 0,onFetchContainerStatus:x,onFetchLogs:m,onUpdateDockerConfig:N,onFetchDockerConfigDiff:P})]})}export{ut as NodesView};
@@ -1,6 +1,6 @@
1
- import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{h as Y,dT as Z,dU as H,dV as J,R as $,dK as y,P as Q,C as T,e as ee,a7 as se,dW as L,c1 as D,F as W,dX as ae,dY as te,dZ as A,X as ie}from"./index-NFptaeUQ.js";import"./vendor-xterm-DzcZoU0P.js";/**
1
+ import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{c as H,eb as J,ec as V,ed as Y,R as $,e2 as y,Q as Z,a as T,b as ee,a1 as se,ee as L,cx as D,F as A,ef as ae,eg as te,eh as G,X as ie}from"./index-D__RMku8.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"}]],G=Y("palette",ne);function le(t){return t.replace(/-/g,"-")}function ce(t){return{"fusion-global":"Fusion Global","pi-global":"Pi Global","fusion-project":"Fusion Project","pi-project":"Pi Project",package:"Package"}[t]??t}function re(t){return t.startsWith("npm:")?"npm":t.startsWith("git:")?"git":"local"}function oe(t){return t.replace(/^(npm:|git:)/,"")}function pe({addToast:t,projectId:x}){const[c,I]=l.useState(null),[f,P]=l.useState(!0),[N,S]=l.useState(!1),[E,w]=l.useState(!1),[h,F]=l.useState(""),[K,M]=l.useState(new Set),[p,O]=l.useState([]),[b,C]=l.useState(!0),[U,R]=l.useState(!1),r=l.useCallback(async()=>{try{P(!0);const s=await Z();I(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 H(x);O(s.extensions)}catch(s){t(`Failed to load extensions: ${s instanceof Error?s.message:String(s)}`,"error")}finally{C(!1)}},[t,x]),X=l.useCallback(async s=>{try{R(!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 J(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{R(!1)}},[p,x,m,t]);l.useEffect(()=>{r()},[r]),l.useEffect(()=>{m()},[m]);const _=s=>{M(i=>{const a=new Set(i);return a.has(s)?a.delete(s):a.add(s),a})},z=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)}},q=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)}},B=async s=>{if(!c)return;const i=c.packages.filter(a=>(typeof a=="string"?a:a.source)!==s);try{await A({packages:i}),t("Package removed","success"),await r()}catch(a){t(`Failed to remove package: ${a instanceof Error?a.message:String(a)}`,"error")}},V=async(s,i)=>{if(!c)return;const a=c[s].filter(n=>n!==i);try{await A({[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:()=>V(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(),z())},disabled:N}),e.jsxs("button",{className:"btn btn-primary",onClick:z,disabled:N||!h.trim(),children:[e.jsx(Q,{size:14}),N?"Installing…":"Add"]})]}),e.jsx("div",{className:"pi-ext-add-form-row",children:e.jsx("button",{className:"btn",onClick:q,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=re(a),g=oe(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=K.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:()=>_(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:()=>B(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(L,{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(D,{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(W,{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(G,{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",L,"extensions"),u("Skills",D,"skills"),u("Prompts",W,"prompts"),u("Themes",G,"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--${le(s.source)}`,children:ce(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 X(s),disabled:U,"aria-label":`Toggle ${s.name}`}),e.jsx("span",{className:"toggle-slider"})]})})]},s.id))})]})]})}export{pe as PiExtensionsManager};
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"}]],I=H("palette",ne);function le(t){return t.replace(/-/g,"-")}function ce(t){return{"fusion-global":"Fusion Global","pi-global":"Pi Global","fusion-project":"Fusion Project","pi-project":"Pi Project",package:"Package"}[t]??t}function re(t){return t.startsWith("npm:")?"npm":t.startsWith("git:")?"git":"local"}function oe(t){return t.replace(/^(npm:|git:)/,"")}function pe({addToast:t,projectId:x}){const[c,M]=l.useState(null),[f,P]=l.useState(!0),[N,S]=l.useState(!1),[E,w]=l.useState(!1),[h,F]=l.useState(""),[O,W]=l.useState(new Set),[p,_]=l.useState([]),[b,C]=l.useState(!0),[q,R]=l.useState(!1),r=l.useCallback(async()=>{try{P(!0);const s=await J();M(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 V(x);_(s.extensions)}catch(s){t(`Failed to load extensions: ${s instanceof Error?s.message:String(s)}`,"error")}finally{C(!1)}},[t,x]),B=l.useCallback(async s=>{try{R(!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 Y(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{R(!1)}},[p,x,m,t]);l.useEffect(()=>{r()},[r]),l.useEffect(()=>{m()},[m]);const K=s=>{W(i=>{const a=new Set(i);return a.has(s)?a.delete(s):a.add(s),a})},z=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)}},Q=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 G({packages:i}),t("Package removed","success"),await r()}catch(a){t(`Failed to remove package: ${a instanceof Error?a.message:String(a)}`,"error")}},X=async(s,i)=>{if(!c)return;const a=c[s].filter(n=>n!==i);try{await G({[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:()=>X(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(),z())},disabled:N}),e.jsxs("button",{className:"btn btn-primary",onClick:z,disabled:N||!h.trim(),children:[e.jsx(Z,{size:14}),N?"Installing…":"Add"]})]}),e.jsx("div",{className:"pi-ext-add-form-row",children:e.jsx("button",{className:"btn",onClick:Q,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=re(a),g=oe(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=O.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:()=>K(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(L,{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(D,{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(A,{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(I,{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",L,"extensions"),u("Skills",D,"skills"),u("Prompts",A,"prompts"),u("Themes",I,"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--${le(s.source)}`,children:ce(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 B(s),disabled:q,"aria-label":`Toggle ${s.name}`}),e.jsx("span",{className:"toggle-slider"})]})})]},s.id))})]})]})}export{pe as PiExtensionsManager};
@@ -0,0 +1 @@
1
+ import{r as g,j as e}from"./vendor-react-K0fH_qHe.js";import{D as me,e0 as he,e1 as K,s as be,X,av as xe,Q as Y,aW as Z,a1 as T,R as fe,e2 as Ne,aQ as je,cn as ye,e3 as Se,e4 as ve,e5 as we,e6 as Ce,e7 as ee,ck as Ie,e8 as Pe}from"./index-D__RMku8.js";import{D as Ee}from"./DirectoryPicker-aVdFaV37.js";import"./vendor-xterm-DzcZoU0P.js";import"./folder-open-usZkXdq2.js";const ne="fusion-plugin-agent-browser",te={enabled:{type:"boolean",label:"Enable Agent Browser",group:"General"},installChannel:{type:"enum",label:"Install Channel",enumValues:["stable","beta","nightly"],defaultValue:"stable",group:"General"},commandTimeoutMs:{type:"number",label:"Command Timeout (ms)",defaultValue:12e4,group:"General"},headlessMode:{type:"boolean",label:"Headless Mode",defaultValue:!0,group:"Browser"},allowedDomains:{type:"array",label:"Allowed Domains",itemType:"string",group:"Browser"},promptExecutorSystem:{type:"string",label:"Executor System Prompt",multiline:!0,group:"Prompt Contributions"},promptExecutorTask:{type:"string",label:"Executor Task Prompt",multiline:!0,group:"Prompt Contributions"},promptTriage:{type:"string",label:"Triage Prompt",multiline:!0,group:"Prompt Contributions"},promptReviewer:{type:"string",label:"Reviewer Prompt",multiline:!0,group:"Prompt Contributions"},promptHeartbeat:{type:"string",label:"Heartbeat Prompt",multiline:!0,group:"Prompt Contributions"},skillExposure:{type:"enum",label:"Skill Exposure",enumValues:["none","selected","all"],defaultValue:"selected",group:"Skills"}},se=[{id:"fusion-plugin-hermes-runtime",name:"Hermes Runtime",description:"Runtime provider for Hermes CLI-backed execution.",category:"runtime",path:"./plugins/fusion-plugin-hermes-runtime",experimental:!0},{id:"fusion-plugin-paperclip-runtime",name:"Paperclip Runtime",description:"Runtime provider for Paperclip agent connections.",category:"runtime",path:"./plugins/fusion-plugin-paperclip-runtime"},{id:"fusion-plugin-openclaw-runtime",name:"OpenClaw Runtime",description:"Runtime provider for OpenClaw execution.",category:"runtime",path:"./plugins/fusion-plugin-openclaw-runtime",experimental:!0},{id:"fusion-plugin-droid-runtime",name:"Droid Runtime",description:"Runtime provider for Droid CLI execution.",category:"runtime",path:"./plugins/fusion-plugin-droid-runtime",experimental:!0},{id:"fusion-plugin-dependency-graph",name:"Dependency Graph",description:"Dashboard plugin for task dependency graph visualization.",category:"integration",path:"./plugins/fusion-plugin-dependency-graph"},{id:ne,name:"Agent Browser",description:"Built-in integration metadata. Package install support lands in FN-3101.",category:"integration",hasSetup:!0}],I={started:"var(--color-success)",loaded:"var(--color-warning)",error:"var(--color-error)",stopped:"var(--color-muted)",installed:"var(--color-info)"};function Re(c){const l=c.settingsSchema,h=l&&Object.keys(l).length>0;return c.id!==ne?h?l:void 0:h?{...te,...l}:te}function $e(c){const l=new Map,h=[];for(const[N,f]of Object.entries(c))if(f.group){const j=l.get(f.group)??[];j.push([N,f]),l.set(f.group,j)}else h.push([N,f]);return{grouped:l,ungrouped:h}}function Ae({addToast:c,projectId:l}){const[h,N]=g.useState([]),[f,j]=g.useState(!0),[ie,P]=g.useState(!1),[w,E]=g.useState(""),[k,F]=g.useState(!1),[y,L]=g.useState(null),[u,R]=g.useState(null),[d,b]=g.useState({}),[ae,D]=g.useState(!1),[z,A]=g.useState(null),[le,$]=g.useState({}),[re,O]=g.useState(null),[C,U]=g.useState(null),{confirm:ce}=me(),m=g.useCallback(async()=>{try{j(!0);const t=await he(l);N(t)}catch(t){c(`Failed to load plugins: ${t instanceof Error?t.message:String(t)}`,"error")}finally{j(!1)}},[l,c]);g.useEffect(()=>{m()},[m]),g.useEffect(()=>{const t=se.filter(r=>r.hasSetup&&h.some(a=>a.id===r.id));if(t.length===0)return;let i=!1;return Promise.all(t.map(async r=>{try{const a=await K(r.id,l);if(i)return;$(o=>({...o,[r.id]:a}))}catch{if(i)return;$(a=>({...a,[r.id]:{hasSetup:!0,status:"error",error:"Failed to check setup status"}}))}})),()=>{i=!0}},[h,l]);const oe=g.useRef([]);oe.current=h,g.useEffect(()=>{const t=l?`?projectId=${encodeURIComponent(l)}`:"",i=r=>{try{const a=JSON.parse(r.data);if(l&&a.projectId&&a.projectId!==l)return;switch(a.transition){case"installing":case"enabled":case"disabled":case"settings-updated":N(o=>{const n=o.findIndex(s=>s.id===a.pluginId);if(n>=0){const s=[...o];return s[n]={...s[n],enabled:a.enabled,state:a.state,settings:a.settings,error:a.error},s}else return m(),o});break;case"uninstalled":N(o=>o.filter(n=>n.id!==a.pluginId));break;case"error":N(o=>{const n=o.findIndex(s=>s.id===a.pluginId);if(n>=0){const s=[...o];return s[n]={...s[n],state:a.state,error:a.error},s}return o});break}}catch{}};return be(`/api/events${t}`,{events:{"plugin:lifecycle":i},onReconnect:()=>{m()}})},[l,m]);const q=async()=>{if(!w.trim()){c("Please enter a plugin path","error");return}try{F(!0),await ee({path:w},l),c("Plugin installed successfully","success"),P(!1),E(""),await m()}catch(t){c(`Failed to install plugin: ${t instanceof Error?t.message:String(t)}`,"error")}finally{F(!1)}},ue=async t=>{if(!t.path){c(`${t.name} is built in and does not have an installable package yet`,"warning");return}try{A(t.id),await ee({path:t.path},l),c(`${t.name} installed successfully`,"success"),await m()}catch(i){c(`Failed to install ${t.name}: ${i instanceof Error?i.message:String(i)}`,"error")}finally{A(null)}},_=async t=>{try{U(t.id);const i=await Pe(t.id,l);if(!i.success){c(`Failed to install ${t.name} setup: ${i.error??"unknown error"}`,"error");return}c(`${t.name} setup installed`,"success"),O(t.id);const r=await K(t.id,l);$(a=>({...a,[t.id]:r}))}catch(i){c(`Failed to install ${t.name} setup: ${i instanceof Error?i.message:String(i)}`,"error")}finally{U(null),O(null)}},G=async t=>{try{await we(t.id,l),c(`${t.name} enabled`,"success"),await m()}catch(i){c(`Failed to enable plugin: ${i instanceof Error?i.message:String(i)}`,"error")}},M=async t=>{try{await ve(t.id,l),c(`${t.name} disabled`,"success"),await m()}catch(i){c(`Failed to disable plugin: ${i instanceof Error?i.message:String(i)}`,"error")}},V=async t=>{try{L(t.id),await Se(t.id,l),c(`${t.name} reloaded`,"success"),await m()}catch(i){c(`Failed to reload plugin: ${i instanceof Error?i.message:String(i)}`,"error")}finally{L(null)}},H=async t=>{if(await ce({title:"Uninstall Plugin",message:`Are you sure you want to uninstall "${t.name}"?`,danger:!0}))try{await Ce(t.id,l),c(`${t.name} uninstalled`,"success"),await m(),R(null)}catch(r){c(`Failed to uninstall plugin: ${r instanceof Error?r.message:String(r)}`,"error")}},B=async t=>{R(t);try{D(!0);const i=await Ie(t.id,l);b(i)}catch{b({})}finally{D(!1)}},de=async()=>{if(u)try{await ye(u.id,d,l),c("Settings saved","success")}catch(t){c(`Failed to save settings: ${t instanceof Error?t.message:String(t)}`,"error")}};if(u)return e.jsxs("div",{className:"plugin-manager-detail","data-testid":"plugin-manager-detail",children:[e.jsxs("div",{className:"plugin-manager-detail-header",children:[e.jsx("button",{className:"btn-icon",onClick:()=>R(null),"aria-label":"Back to plugin list",children:e.jsx(X,{size:16})}),e.jsxs("div",{className:"plugin-detail-title",children:[e.jsx("h4",{className:"plugin-detail-name",children:u.name}),e.jsx("span",{className:"plugin-state-badge",style:{color:I[u.state]||I.installed},children:u.state})]})]}),e.jsxs("div",{className:"plugin-detail-content",children:[e.jsxs("div",{className:"plugin-detail-card",children:[u.description&&e.jsx("p",{className:"plugin-description",children:u.description}),u.author&&e.jsxs("p",{className:"plugin-detail-meta-row",children:[e.jsx("span",{className:"text-muted",children:"Author:"}),u.author]}),u.homepage&&e.jsxs("p",{className:"plugin-detail-meta-row plugin-homepage",children:[e.jsx("span",{className:"text-muted",children:"Homepage:"}),e.jsxs("a",{href:u.homepage,target:"_blank",rel:"noopener noreferrer",children:[u.homepage,e.jsx(xe,{size:12})]})]}),e.jsxs("p",{className:"plugin-detail-meta-row",children:[e.jsx("span",{className:"text-muted",children:"Version:"}),u.version]})]}),e.jsxs("div",{className:"plugin-detail-card",children:[e.jsx("h5",{className:"plugin-detail-section-heading",children:"Settings"}),ae?e.jsx("p",{className:"text-muted",children:"Loading..."}):(()=>{const t=Re(u);return t&&Object.keys(t).length>0?e.jsxs("div",{className:"plugin-settings-form",children:[(()=>{const{grouped:i,ungrouped:r}=$e(t),a=[];r.length>0&&a.push({title:null,entries:r});for(const[o,n]of i.entries())a.push({title:o,entries:n});return a.map(o=>e.jsxs("div",{className:o.title?"plugin-settings-group":void 0,children:[o.title&&e.jsx("h6",{className:"plugin-settings-group-heading",children:o.title}),o.entries.map(([n,s])=>{const x=`setting-${n}-help`;return e.jsxs("div",{className:"form-group",children:[e.jsxs("label",{htmlFor:`setting-${n}`,children:[s.label||n,s.required&&" *"]}),s.type==="string"&&!s.multiline&&e.jsx("input",{className:"input",type:"text",id:`setting-${n}`,value:d[n]??"",onChange:p=>b({...d,[n]:p.target.value}),placeholder:s.description,"aria-describedby":s.description&&!s.required?x:void 0}),s.type==="string"&&s.multiline&&e.jsx("textarea",{className:"input",id:`setting-${n}`,rows:4,value:d[n]??"",onChange:p=>b({...d,[n]:p.target.value}),placeholder:s.description,"aria-describedby":s.description&&!s.required?x:void 0}),s.type==="password"&&e.jsx("input",{className:"input",type:"password",id:`setting-${n}`,value:d[n]??"",onChange:p=>b({...d,[n]:p.target.value}),placeholder:s.description,"aria-describedby":s.description&&!s.required?x:void 0}),s.type==="number"&&e.jsx("input",{className:"input",type:"number",id:`setting-${n}`,value:d[n]??"",onChange:p=>b({...d,[n]:Number(p.target.value)}),"aria-describedby":s.description&&!s.required?x:void 0}),s.type==="boolean"&&e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:d[n]??!1,onChange:p=>b({...d,[n]:p.target.checked})}),s.description]}),s.type==="enum"&&e.jsxs("select",{className:"select",id:`setting-${n}`,value:d[n]??"",onChange:p=>b({...d,[n]:p.target.value}),"aria-describedby":s.description&&!s.required?x:void 0,children:[e.jsx("option",{value:"",children:"Select..."}),s.enumValues?.map(p=>e.jsx("option",{value:p,children:p},p))]}),s.type==="array"&&e.jsxs("div",{className:"plugin-settings-array",children:[d[n]?.map((p,S)=>e.jsxs("div",{className:"plugin-settings-array-item",children:[e.jsx("input",{className:"input",type:s.itemType==="number"?"number":"text",value:p??"",onChange:Q=>{const v=Q.target.value,J=[...d[n]||[]];J[S]=s.itemType==="number"?Number(v):v,b({...d,[n]:J})}}),e.jsx("button",{className:"btn-icon",onClick:()=>{const v=[...d[n]||[]];v.splice(S,1),b({...d,[n]:v})},"aria-label":"Remove item",children:e.jsx(X,{size:14})})]},S)),e.jsxs("button",{className:"btn btn-secondary",onClick:()=>{const p=d[n]||[],S=s.itemType==="number"?0:"";b({...d,[n]:[...p,S]})},children:[e.jsx(Y,{size:14})," Add Item"]})]}),s.description&&!s.required&&!s.multiline&&e.jsx("span",{id:x,className:"form-help",children:s.description})]},n)})]},o.title??"ungrouped"))})(),e.jsx("button",{className:"btn btn-primary",onClick:de,children:"Save Settings"})]}):e.jsx("p",{className:"text-muted",children:"No configurable settings."})})()]}),e.jsxs("div",{className:"plugin-detail-actions",children:[u.state==="started"&&e.jsxs("button",{className:"btn btn-secondary",onClick:()=>V(u),disabled:y===u.id,children:[e.jsx(Z,{size:14,className:y===u.id?"spin":""}),y===u.id?"Reloading...":"Reload"]}),u.enabled?e.jsx("button",{className:"btn btn-secondary",onClick:()=>M(u),children:"Disable"}):e.jsx("button",{className:"btn btn-primary",onClick:()=>G(u),children:"Enable"}),e.jsxs("button",{className:"btn btn-danger",onClick:()=>H(u),children:[e.jsx(T,{size:14})," Uninstall"]})]})]})]});const pe=new Map(h.map(t=>[t.id,t])),W=h,ge=()=>e.jsxs("section",{className:"plugin-builtins-section","aria-label":"Built-in Plugins",children:[e.jsxs("div",{className:"plugin-builtins-header",children:[e.jsx("h4",{className:"plugin-builtins-heading",children:"Built-in Plugins"}),e.jsx("p",{className:"plugin-builtins-description",children:"Built-in plugin catalog for runtimes and integrations."})]}),e.jsx("div",{className:"plugin-builtins-list","aria-label":"Built-in plugin recommendations",children:se.map(t=>{const i=pe.get(t.id),r=!!i,a=le[t.id],o=r&&t.hasSetup&&a?.hasSetup&&(a.status==="not-installed"||a.status==="error"),n=r&&a?.hasSetup&&a.status==="installed",s=re===t.id,x=!t.path;return e.jsxs("div",{className:"plugin-builtins-item",children:[e.jsxs("div",{className:"plugin-builtins-meta",children:[e.jsx("span",{className:"plugin-builtins-name",children:t.name}),t.experimental&&e.jsx("span",{className:"plugin-builtins-runtime-badge",children:"Experimental"}),e.jsx("span",{className:"plugin-builtins-runtime-badge",children:t.category}),e.jsx("span",{className:`plugin-builtins-status ${r?"plugin-builtins-status--installed":"plugin-builtins-status--available"}`,children:r?"Installed":x?"Built in":"Not installed"}),o&&e.jsx("span",{className:"plugin-builtins-setup-status plugin-builtins-setup-status--warning",children:"Setup required"}),n&&e.jsx("span",{className:"plugin-builtins-setup-status plugin-builtins-setup-status--ready",children:"Setup ready"}),s&&e.jsx("span",{className:"plugin-builtins-setup-status plugin-builtins-setup-status--pending",children:"Checking setup..."}),e.jsx("span",{className:"plugin-builtins-description-text",children:t.description})]}),x?r&&o?e.jsx("button",{className:"btn btn-primary btn-sm",onClick:()=>void _(t),disabled:C===t.id||s,children:C===t.id?"Setting up...":"Install Setup"}):r&&i?e.jsx("button",{className:"btn btn-secondary btn-sm",onClick:()=>void B(i),children:"Manage"}):e.jsx("span",{className:"plugin-builtins-metadata-only",children:"Built-in metadata only"}):e.jsx("button",{className:`btn ${r&&!o?"btn-secondary":"btn-primary"} btn-sm`,onClick:()=>{if(!r){ue(t);return}if(o){_(t);return}i&&B(i)},disabled:z===t.id||C===t.id||s,children:r?o?C===t.id?"Setting up...":"Install Setup":"Manage":z===t.id?"Installing...":`Install ${t.name}`})]},t.id)})})]});return e.jsxs("div",{className:"plugin-manager","data-testid":"plugin-manager",children:[e.jsxs("div",{className:"plugin-manager-header",children:[e.jsx("span",{className:"plugin-manager-header-title",children:"Installed Plugins"}),e.jsxs("div",{className:"plugin-manager-actions",children:[e.jsxs("button",{className:"btn btn-sm",onClick:m,title:"Refresh","aria-label":"Refresh plugin list",children:[e.jsx(fe,{size:14,className:f?"spin":""}),"Refresh"]}),e.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>P(!0),children:[e.jsx(Y,{size:14})," Install"]})]})]}),ie&&e.jsxs("div",{className:"plugin-install-form",children:[e.jsxs("p",{className:"plugin-install-hint",children:["Browse to a plugin package root (contains ",e.jsx("code",{children:"manifest.json"}),") or a built ",e.jsx("code",{children:"dist"})," directory."]}),e.jsx(Ee,{value:w,onChange:E,placeholder:"Absolute path to plugin directory or dist folder",onInputKeyDown:t=>{t.key==="Enter"&&(t.preventDefault(),q())}}),e.jsxs("div",{className:"plugin-install-actions",children:[e.jsx("button",{className:"btn btn-primary",onClick:q,disabled:k||!w.trim(),children:k?"Installing...":"Install Plugin"}),e.jsx("button",{className:"btn btn-secondary",onClick:()=>{P(!1),E("")},children:"Cancel"})]})]}),f?e.jsx("div",{className:"settings-empty-state",children:"Loading plugins..."}):e.jsxs(e.Fragment,{children:[W.length===0?e.jsxs("div",{className:"settings-empty-state",children:[e.jsx(Ne,{size:32,className:"text-muted"}),e.jsx("p",{children:"No plugins installed."}),e.jsx("p",{className:"text-muted",children:"Install a plugin to get started, or use the built-in catalog below."})]}):e.jsx("div",{className:"plugin-list",children:W.map(t=>e.jsxs("div",{className:"plugin-item",children:[e.jsxs("div",{className:"plugin-info",children:[e.jsx("span",{className:"plugin-name",children:t.name}),e.jsxs("span",{className:"plugin-version text-muted",children:["v",t.version]}),e.jsx("span",{className:"plugin-state-badge",style:{color:I[t.state]||I.installed},children:t.state})]}),e.jsxs("div",{className:"plugin-actions",children:[t.state==="started"&&e.jsx("button",{className:"btn-icon",onClick:()=>V(t),disabled:y===t.id,title:"Reload",children:e.jsx(Z,{size:14,className:y===t.id?"spin":""})}),e.jsxs("label",{className:"toggle-switch",children:[e.jsx("input",{type:"checkbox",checked:t.enabled,onChange:()=>t.enabled?M(t):G(t)}),e.jsx("span",{className:"toggle-slider"})]}),e.jsx("button",{className:"btn-icon",onClick:()=>B(t),title:"Settings",children:e.jsx(je,{size:14})}),e.jsx("button",{className:"btn-icon",onClick:()=>H(t),title:"Uninstall",children:e.jsx(T,{size:14})})]})]},t.id))}),ge()]})]})}export{te as AGENT_BROWSER_SETTINGS_SCHEMA,ne as BUILTIN_AGENT_BROWSER_PLUGIN_ID,Ae as PluginManager,I as STATE_COLORS};
@@ -0,0 +1 @@
1
+ .plugin-manager,.plugin-manager-detail{display:flex;flex-direction:column;gap:var(--space-lg);padding-inline:var(--space-xl);padding-block:var(--space-md)}.plugin-manager-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--space-sm);border-bottom:var(--btn-border-width) solid var(--border);gap:var(--space-sm)}.plugin-manager-header-title{font-size:13px;font-weight:600;color:var(--text);flex:1}.plugin-manager-actions{display:flex;gap:var(--space-sm);align-items:center}.plugin-install-form{display:flex;flex-direction:column;gap:var(--space-sm);padding:var(--space-lg);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-install-hint{margin:0;font-size:.85rem;color:var(--text-secondary, var(--text-muted));line-height:1.45}.plugin-install-hint code{padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-sm);background:color-mix(in srgb,var(--text-muted) 12%,transparent);font-size:.85em}.plugin-install-actions{display:flex;gap:var(--space-sm);justify-content:flex-end}.plugin-list{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-item{display:flex;align-items:center;justify-content:space-between;padding:var(--space-md) var(--space-lg);background:var(--surface);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);transition:border-color var(--transition-fast)}.plugin-item:hover{border-color:var(--text-dim)}.plugin-info{display:flex;align-items:center;gap:var(--space-sm);min-width:0}.plugin-name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.plugin-version{font-size:.85rem}.plugin-state-badge{display:inline-flex;align-items:center;padding:var(--space-xs) var(--space-sm);border-radius:var(--radius-pill);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;background:color-mix(in srgb,currentColor 12%,transparent)}.plugin-actions{display:flex;align-items:center;gap:var(--space-xs);flex-shrink:0}.plugin-manager-detail-header,.plugin-detail-title{display:flex;align-items:center;gap:var(--space-md);flex-wrap:wrap}.plugin-detail-name{margin:0}.plugin-detail-content{display:flex;flex-direction:column;gap:var(--space-lg)}.plugin-detail-card{background:var(--surface);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);padding:var(--space-lg);display:flex;flex-direction:column;gap:var(--space-md)}.plugin-description{font-size:.95rem;color:var(--text-secondary, var(--text-muted));line-height:1.5}.plugin-detail-meta-row{display:flex;align-items:center;gap:var(--space-xs);font-size:.9rem;color:var(--text-muted)}.plugin-homepage{flex-wrap:wrap;align-items:flex-start}.plugin-homepage a{display:inline-flex;align-items:center;gap:var(--space-xs);color:var(--color-info);font-size:.85rem;flex-wrap:wrap;overflow-wrap:anywhere}.plugin-detail-section-heading{margin:0;padding:0;border:0;font-size:.95rem}.plugin-settings-form{display:flex;flex-direction:column;gap:var(--space-lg);margin-top:var(--space-xs)}.plugin-settings-form .form-group{padding:0;margin:0}.plugin-settings-group{display:flex;flex-direction:column;gap:var(--space-md)}.plugin-settings-group-heading{margin:0;font-size:.8rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}.plugin-settings-array{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-settings-array-item{display:flex;align-items:center;gap:var(--space-sm)}.plugin-settings-array-item input{flex:1}.plugin-detail-actions{display:flex;gap:var(--space-sm);padding-top:var(--space-md);border-top:var(--btn-border-width) solid var(--border);justify-content:flex-end}.plugin-manager .empty-state,.plugin-manager .loading-state,.plugin-manager .settings-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-sm);padding:var(--space-2xl);text-align:center;color:var(--text-muted)}.plugin-builtins-section{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-builtins-header{display:flex;flex-direction:column;gap:var(--space-xs)}.plugin-builtins-heading{margin:0;font-size:.95rem}.plugin-builtins-description{margin:0;font-size:.85rem;color:var(--text-muted)}.plugin-builtins-list{width:100%;display:flex;flex-direction:column;gap:var(--space-sm);margin-top:var(--space-sm)}.plugin-builtins-item{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-builtins-meta{display:flex;align-items:center;gap:var(--space-sm);row-gap:var(--space-xs);flex-wrap:wrap;min-width:0}.plugin-builtins-name{color:var(--text);font-size:.9rem;font-weight:500}.plugin-builtins-runtime-badge{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);background:var(--status-in-review-bg);color:var(--in-review);font-size:.75rem;font-weight:600;text-transform:uppercase}.plugin-builtins-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:.75rem;font-weight:600;text-transform:uppercase}.plugin-builtins-status--installed{background:var(--status-done-bg);color:var(--done)}.plugin-builtins-status--available{background:var(--status-todo-bg);color:var(--todo)}.plugin-builtins-setup-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:.75rem;font-weight:600;text-transform:uppercase}.plugin-builtins-setup-status--warning{background:var(--status-in-review-bg);color:var(--color-warning)}.plugin-builtins-setup-status--ready{background:var(--status-done-bg);color:var(--color-success)}.plugin-builtins-setup-status--pending{background:var(--status-todo-bg);color:var(--color-warning)}.plugin-builtins-description-text{flex:1 1 100%;color:var(--text-muted);font-size:.85rem}.plugin-builtins-metadata-only{color:var(--text-muted);font-size:.8rem;font-weight:600;text-transform:uppercase}.plugin-bundled-runtime-list{width:100%;display:flex;flex-direction:column;gap:var(--space-sm);margin-top:var(--space-sm)}.plugin-bundled-runtime-item{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border:var(--btn-border-width) solid var(--border);border-radius:var(--radius-md);background:var(--surface)}.plugin-bundled-runtime-meta{display:flex;align-items:center;gap:var(--space-sm);row-gap:var(--space-xs);flex-wrap:wrap;min-width:0}.plugin-bundled-runtime-name{color:var(--text);font-size:.9rem;font-weight:500}.plugin-bundled-runtime-badge{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);background:var(--status-in-review-bg);color:var(--in-review);font-size:.75rem;font-weight:600;text-transform:uppercase}.plugin-bundled-runtime-section{display:flex;flex-direction:column;gap:var(--space-sm)}.plugin-bundled-runtime-header{display:flex;flex-direction:column;gap:var(--space-xs)}.plugin-bundled-runtime-heading{margin:0;font-size:.95rem}.plugin-bundled-runtime-description{margin:0;font-size:.85rem;color:var(--text-muted)}.plugin-bundled-runtime-status{display:inline-flex;align-items:center;padding:var(--btn-border-width) var(--space-xs);border-radius:var(--radius-pill);font-size:.75rem;font-weight:600;text-transform:uppercase}@media(min-width:769px){.plugin-builtins-status,.plugin-bundled-runtime-status{margin-left:auto}}.plugin-bundled-runtime-status--installed{background:var(--status-done-bg);color:var(--done)}.plugin-bundled-runtime-status--available{background:var(--status-todo-bg);color:var(--todo)}@media(max-width:768px){.plugin-manager,.plugin-manager-detail{padding-inline:var(--space-sm)}.plugin-manager-detail-header{gap:var(--space-sm)}.plugin-detail-title{gap:var(--space-xs)}.plugin-detail-card{padding:var(--space-md);gap:var(--space-sm)}.plugin-list{gap:var(--space-xs)}.plugin-item{padding:var(--space-md);flex-direction:column;align-items:stretch;gap:var(--space-sm)}.plugin-info{width:100%;flex-wrap:wrap;row-gap:var(--space-xs)}.plugin-name{flex:1 1 100%;white-space:normal}.plugin-actions{width:100%;justify-content:flex-end;flex-wrap:wrap;gap:var(--space-sm)}.plugin-actions .btn-icon,.plugin-actions .toggle-switch{min-width:36px;min-height:36px}.plugin-actions .toggle-switch{display:inline-flex;align-items:center;justify-content:center}.plugin-detail-actions{flex-wrap:wrap;justify-content:flex-start}.plugin-detail-actions button{flex:1 1 auto;min-height:36px}.plugin-builtins-item,.plugin-bundled-runtime-item{flex-direction:column;align-items:stretch}.plugin-builtins-item .btn,.plugin-bundled-runtime-item .btn{min-height:36px}}