@runfusion/fusion 0.21.0 → 0.22.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.
- package/dist/bin.js +1991 -993
- package/dist/client/assets/AgentDetailView-BKKpbp1S.js +18 -0
- package/dist/client/assets/AgentDetailView-CeO_1MK7.css +1 -0
- package/dist/client/assets/AgentsView-BRXFmrcJ.js +527 -0
- package/dist/client/assets/AgentsView-Bs03ptrd.css +1 -0
- package/dist/client/assets/ChatView-D7L2e_qu.js +1 -0
- package/dist/client/assets/DevServerView-l8RCyL2k.js +1 -0
- package/dist/client/assets/DirectoryPicker-CS1dwqcC.js +1 -0
- package/dist/client/assets/DocumentsView-DmthQWDZ.js +1 -0
- package/dist/client/assets/{InsightsView-CqDethVs.js → InsightsView-DvXpMKmH.js} +2 -2
- package/dist/client/assets/{MemoryView-BLIm9Vr7.js → MemoryView-CPwlKnUI.js} +2 -2
- package/dist/client/assets/{NodesView-DEXvp3WT.js → NodesView-BLlfUfsy.js} +3 -3
- package/dist/client/assets/{PiExtensionsManager-C2YjI9o2.js → PiExtensionsManager-j8rPXqmB.js} +2 -2
- package/dist/client/assets/PluginManager-pW6RMz5z.js +1 -0
- package/dist/client/assets/ResearchView-D9DNJYDq.js +1 -0
- package/dist/client/assets/{RoadmapsView-DPcfX5MS.js → RoadmapsView-Djc_X35v.js} +2 -2
- package/dist/client/assets/SettingsModal-WGCF_pk8.js +31 -0
- package/dist/client/assets/{SettingsModal-BRNAPR1u.js → SettingsModal-fxvTFLtR.js} +1 -1
- package/dist/client/assets/SetupWizardModal-tG_MF_nA.js +1 -0
- package/dist/client/assets/SkillsView-Ddf0YL8z.js +1 -0
- package/dist/client/assets/agentSkills-DDHJnrkn.css +1 -0
- package/dist/client/assets/agentSkills-EwIwBlG8.js +1 -0
- package/dist/client/assets/folder-open-BiJpmnaT.js +6 -0
- package/dist/client/assets/index-D6ebxTPF.css +1 -0
- package/dist/client/assets/index-DYDLmOcK.js +694 -0
- package/dist/client/assets/{star-B314SwLA.js → star-BwRZmiuZ.js} +2 -2
- package/dist/client/assets/upload-D4NwZhPp.js +6 -0
- package/dist/client/assets/{users-Bu_ltePs.js → users-DNISDtI1.js} +2 -2
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +1154 -401
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +480 -0
- package/dist/plugins/fusion-plugin-hermes-runtime/manifest.json +14 -0
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +11 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +369 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/manifest.json +14 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +11 -0
- package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +966 -0
- package/dist/plugins/fusion-plugin-paperclip-runtime/manifest.json +15 -0
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +11 -0
- package/package.json +2 -1
- package/skill/fusion/references/engine-tools.md +1 -1
- package/dist/client/assets/AgentDetailView-CUtWvXBn.css +0 -1
- package/dist/client/assets/AgentDetailView-Dg7Qa1rG.js +0 -18
- package/dist/client/assets/ChatView-ODq-kBk6.js +0 -1
- package/dist/client/assets/DevServerView-6PS9Lvl7.js +0 -1
- package/dist/client/assets/DirectoryPicker-B3dza2Dq.js +0 -1
- package/dist/client/assets/DocumentsView-Bu9YYlki.js +0 -1
- package/dist/client/assets/PluginManager-Dnf-LhYw.js +0 -1
- package/dist/client/assets/ResearchView-Z0TZ7WGo.js +0 -1
- package/dist/client/assets/SettingsModal-B6RN9VYe.js +0 -31
- package/dist/client/assets/SetupWizardModal-BFc3xID2.js +0 -1
- package/dist/client/assets/SkillsView-CipGahOR.js +0 -1
- package/dist/client/assets/index-Df1bHDY4.css +0 -1
- 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{
|
|
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-DYDLmOcK.js";import{U as Be}from"./upload-D4NwZhPp.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};
|
package/dist/client/assets/{PiExtensionsManager-C2YjI9o2.js → PiExtensionsManager-j8rPXqmB.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{
|
|
1
|
+
import{r as l,j as e}from"./vendor-react-K0fH_qHe.js";import{c as H,e9 as J,ea as V,eb as Y,R as $,e1 as y,Q as Z,a as T,b as ee,a1 as se,ec as L,cx as D,F as A,ed as ae,ee as te,ef as G,X as ie}from"./index-DYDLmOcK.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"}]],
|
|
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 o,j as n}from"./vendor-react-K0fH_qHe.js";import{D as T,e0 as ee,s as ne,X as A,av as se,Q as O,aW as M,a1 as V,R as te,e1 as ie,aQ as ae,cn as le,e2 as re,e3 as ce,e4 as de,e5 as ue,e6 as _,ck as oe}from"./index-DYDLmOcK.js";import{D as ge}from"./DirectoryPicker-CS1dwqcC.js";import"./vendor-xterm-DzcZoU0P.js";import"./folder-open-BiJpmnaT.js";const me=[{id:"fusion-plugin-agent-browser-runtime",name:"Agent Browser Runtime",path:"./plugins/fusion-plugin-agent-browser-runtime",experimental:!0},{id:"fusion-plugin-hermes-runtime",name:"Hermes Runtime",path:"./plugins/fusion-plugin-hermes-runtime",experimental:!0},{id:"fusion-plugin-paperclip-runtime",name:"Paperclip Runtime",path:"./plugins/fusion-plugin-paperclip-runtime"},{id:"fusion-plugin-openclaw-runtime",name:"OpenClaw Runtime",path:"./plugins/fusion-plugin-openclaw-runtime",experimental:!0},{id:"fusion-plugin-droid-runtime",name:"Droid Runtime",path:"./plugins/fusion-plugin-droid-runtime",experimental:!0},{id:"fusion-plugin-dependency-graph",name:"Dependency Graph",path:"./plugins/fusion-plugin-dependency-graph"}],N={started:"var(--color-success)",loaded:"var(--color-warning)",error:"var(--color-error)",stopped:"var(--color-muted)",installed:"var(--color-info)"};function fe({addToast:l,projectId:c}){const[x,b]=o.useState([]),[S,w]=o.useState(!0),[G,f]=o.useState(!1),[j,v]=o.useState(""),[C,P]=o.useState(!1),[h,$]=o.useState(null),[i,y]=o.useState(null),[a,p]=o.useState({}),[H,I]=o.useState(!1),[R,E]=o.useState(null),{confirm:Q}=T(),g=o.useCallback(async()=>{try{w(!0);const e=await ee(c);b(e)}catch(e){l(`Failed to load plugins: ${e instanceof Error?e.message:String(e)}`,"error")}finally{w(!1)}},[c,l]);o.useEffect(()=>{g()},[g]);const J=o.useRef([]);J.current=x,o.useEffect(()=>{const e=c?`?projectId=${encodeURIComponent(c)}`:"",s=d=>{try{const t=JSON.parse(d.data);if(c&&t.projectId&&t.projectId!==c)return;switch(t.transition){case"installing":case"enabled":case"disabled":case"settings-updated":b(u=>{const m=u.findIndex(r=>r.id===t.pluginId);if(m>=0){const r=[...u];return r[m]={...r[m],enabled:t.enabled,state:t.state,settings:t.settings,error:t.error},r}else return g(),u});break;case"uninstalled":b(u=>u.filter(m=>m.id!==t.pluginId));break;case"error":b(u=>{const m=u.findIndex(r=>r.id===t.pluginId);if(m>=0){const r=[...u];return r[m]={...r[m],state:t.state,error:t.error},r}return u});break}}catch{}};return ne(`/api/events${e}`,{events:{"plugin:lifecycle":s},onReconnect:()=>{g()}})},[c,g]);const z=async()=>{if(!j.trim()){l("Please enter a plugin path","error");return}try{P(!0),await _({path:j},c),l("Plugin installed successfully","success"),f(!1),v(""),await g()}catch(e){l(`Failed to install plugin: ${e instanceof Error?e.message:String(e)}`,"error")}finally{P(!1)}},K=async e=>{try{E(e.id),await _({path:e.path},c),l(`${e.name} installed successfully`,"success"),await g()}catch(s){l(`Failed to install ${e.name}: ${s instanceof Error?s.message:String(s)}`,"error")}finally{E(null)}},B=async e=>{try{await de(e.id,c),l(`${e.name} enabled`,"success"),await g()}catch(s){l(`Failed to enable plugin: ${s instanceof Error?s.message:String(s)}`,"error")}},D=async e=>{try{await ce(e.id,c),l(`${e.name} disabled`,"success"),await g()}catch(s){l(`Failed to disable plugin: ${s instanceof Error?s.message:String(s)}`,"error")}},F=async e=>{try{$(e.id),await re(e.id,c),l(`${e.name} reloaded`,"success"),await g()}catch(s){l(`Failed to reload plugin: ${s instanceof Error?s.message:String(s)}`,"error")}finally{$(null)}},L=async e=>{if(await Q({title:"Uninstall Plugin",message:`Are you sure you want to uninstall "${e.name}"?`,danger:!0}))try{await ue(e.id,c),l(`${e.name} uninstalled`,"success"),await g(),y(null)}catch(d){l(`Failed to uninstall plugin: ${d instanceof Error?d.message:String(d)}`,"error")}},U=async e=>{y(e);try{I(!0);const s=await oe(e.id,c);p(s)}catch{p({})}finally{I(!1)}},W=async()=>{if(i)try{await le(i.id,a,c),l("Settings saved","success")}catch(e){l(`Failed to save settings: ${e instanceof Error?e.message:String(e)}`,"error")}};if(i)return n.jsxs("div",{className:"plugin-manager-detail","data-testid":"plugin-manager-detail",children:[n.jsxs("div",{className:"plugin-manager-detail-header",children:[n.jsx("button",{className:"btn-icon",onClick:()=>y(null),"aria-label":"Back to plugin list",children:n.jsx(A,{size:16})}),n.jsxs("div",{className:"plugin-detail-title",children:[n.jsx("h4",{className:"plugin-detail-name",children:i.name}),n.jsx("span",{className:"plugin-state-badge",style:{color:N[i.state]||N.installed},children:i.state})]})]}),n.jsxs("div",{className:"plugin-detail-content",children:[n.jsxs("div",{className:"plugin-detail-card",children:[i.description&&n.jsx("p",{className:"plugin-description",children:i.description}),i.author&&n.jsxs("p",{className:"plugin-detail-meta-row",children:[n.jsx("span",{className:"text-muted",children:"Author:"}),i.author]}),i.homepage&&n.jsxs("p",{className:"plugin-detail-meta-row plugin-homepage",children:[n.jsx("span",{className:"text-muted",children:"Homepage:"}),n.jsxs("a",{href:i.homepage,target:"_blank",rel:"noopener noreferrer",children:[i.homepage,n.jsx(se,{size:12})]})]}),n.jsxs("p",{className:"plugin-detail-meta-row",children:[n.jsx("span",{className:"text-muted",children:"Version:"}),i.version]})]}),n.jsxs("div",{className:"plugin-detail-card",children:[n.jsx("h5",{className:"plugin-detail-section-heading",children:"Settings"}),H?n.jsx("p",{className:"text-muted",children:"Loading..."}):i.settingsSchema&&Object.keys(i.settingsSchema).length>0?n.jsxs("div",{className:"plugin-settings-form",children:[Object.entries(i.settingsSchema).map(([e,s])=>{const d=`setting-${e}-help`;return n.jsxs("div",{className:"form-group",children:[n.jsxs("label",{htmlFor:`setting-${e}`,children:[s.label||e,s.required&&" *"]}),s.type==="string"&&!s.multiline&&n.jsx("input",{className:"input",type:"text",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:s.description,"aria-describedby":s.description&&!s.required?d:void 0}),s.type==="string"&&s.multiline&&n.jsx("textarea",{className:"input",id:`setting-${e}`,rows:4,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:s.description,"aria-describedby":s.description&&!s.required?d:void 0}),s.type==="password"&&n.jsx("input",{className:"input",type:"password",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),placeholder:s.description,"aria-describedby":s.description&&!s.required?d:void 0}),s.type==="number"&&n.jsx("input",{className:"input",type:"number",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:Number(t.target.value)}),"aria-describedby":s.description&&!s.required?d:void 0}),s.type==="boolean"&&n.jsxs("label",{className:"checkbox-label",children:[n.jsx("input",{type:"checkbox",checked:a[e]??!1,onChange:t=>p({...a,[e]:t.target.checked})}),s.description]}),s.type==="enum"&&n.jsxs("select",{className:"select",id:`setting-${e}`,value:a[e]??"",onChange:t=>p({...a,[e]:t.target.value}),"aria-describedby":s.description&&!s.required?d:void 0,children:[n.jsx("option",{value:"",children:"Select..."}),s.enumValues?.map(t=>n.jsx("option",{value:t,children:t},t))]}),s.type==="array"&&n.jsxs("div",{className:"plugin-settings-array",children:[a[e]?.map((t,u)=>n.jsxs("div",{className:"plugin-settings-array-item",children:[n.jsx("input",{className:"input",type:s.itemType==="number"?"number":"text",value:t??"",onChange:m=>{const r=m.target.value,q=[...a[e]||[]];q[u]=s.itemType==="number"?Number(r):r,p({...a,[e]:q})}}),n.jsx("button",{className:"btn-icon",onClick:()=>{const r=[...a[e]||[]];r.splice(u,1),p({...a,[e]:r})},"aria-label":"Remove item",children:n.jsx(A,{size:14})})]},u)),n.jsxs("button",{className:"btn btn-secondary",onClick:()=>{const t=a[e]||[],u=s.itemType==="number"?0:"";p({...a,[e]:[...t,u]})},children:[n.jsx(O,{size:14})," Add Item"]})]}),s.description&&!s.required&&!s.multiline&&n.jsx("span",{id:d,className:"form-help",children:s.description})]},e)}),n.jsx("button",{className:"btn btn-primary",onClick:W,children:"Save Settings"})]}):n.jsx("p",{className:"text-muted",children:"No configurable settings."})]}),n.jsxs("div",{className:"plugin-detail-actions",children:[i.state==="started"&&n.jsxs("button",{className:"btn btn-secondary",onClick:()=>F(i),disabled:h===i.id,children:[n.jsx(M,{size:14,className:h===i.id?"spin":""}),h===i.id?"Reloading...":"Reload"]}),i.enabled?n.jsx("button",{className:"btn btn-secondary",onClick:()=>D(i),children:"Disable"}):n.jsx("button",{className:"btn btn-primary",onClick:()=>B(i),children:"Enable"}),n.jsxs("button",{className:"btn btn-danger",onClick:()=>L(i),children:[n.jsx(V,{size:14})," Uninstall"]})]})]})]});const X=new Set(x.map(e=>e.id)),Y=new Map(x.map(e=>[e.id,e])),k=x,Z=()=>n.jsxs("section",{className:"plugin-bundled-runtime-section","aria-label":"Bundled Plugins",children:[n.jsxs("div",{className:"plugin-bundled-runtime-header",children:[n.jsx("h4",{className:"plugin-bundled-runtime-heading",children:"Bundled Plugins"}),n.jsx("p",{className:"plugin-bundled-runtime-description",children:"Install Fusion's bundled plugins directly from this screen."})]}),n.jsx("div",{className:"plugin-bundled-runtime-list","aria-label":"Bundled plugin recommendations",children:me.map(e=>{const s=X.has(e.id);return n.jsxs("div",{className:"plugin-bundled-runtime-item",children:[n.jsxs("div",{className:"plugin-bundled-runtime-meta",children:[n.jsx("span",{className:"plugin-bundled-runtime-name",children:e.name}),e.experimental&&n.jsx("span",{className:"plugin-bundled-runtime-badge",children:"Experimental"}),n.jsx("span",{className:`plugin-bundled-runtime-status ${s?"plugin-bundled-runtime-status--installed":"plugin-bundled-runtime-status--available"}`,children:s?"Installed":"Not installed"})]}),n.jsx("button",{className:`btn ${s?"btn-secondary":"btn-primary"} btn-sm`,onClick:()=>{if(s){const d=Y.get(e.id);d&&U(d);return}K(e)},disabled:R===e.id,children:s?"Manage":R===e.id?"Installing...":`Install ${e.name}`})]},e.id)})})]});return n.jsxs("div",{className:"plugin-manager","data-testid":"plugin-manager",children:[n.jsxs("div",{className:"plugin-manager-header",children:[n.jsx("span",{className:"plugin-manager-header-title",children:"Installed Plugins"}),n.jsxs("div",{className:"plugin-manager-actions",children:[n.jsxs("button",{className:"btn btn-sm",onClick:g,title:"Refresh","aria-label":"Refresh plugin list",children:[n.jsx(te,{size:14,className:S?"spin":""}),"Refresh"]}),n.jsxs("button",{className:"btn btn-primary btn-sm",onClick:()=>f(!0),children:[n.jsx(O,{size:14})," Install"]})]})]}),G&&n.jsxs("div",{className:"plugin-install-form",children:[n.jsxs("p",{className:"plugin-install-hint",children:["Browse to a plugin package root (contains ",n.jsx("code",{children:"manifest.json"}),") or a built ",n.jsx("code",{children:"dist"})," directory."]}),n.jsx(ge,{value:j,onChange:v,placeholder:"Absolute path to plugin directory or dist folder",onInputKeyDown:e=>{e.key==="Enter"&&(e.preventDefault(),z())}}),n.jsxs("div",{className:"plugin-install-actions",children:[n.jsx("button",{className:"btn btn-primary",onClick:z,disabled:C||!j.trim(),children:C?"Installing...":"Install Plugin"}),n.jsx("button",{className:"btn btn-secondary",onClick:()=>{f(!1),v("")},children:"Cancel"})]})]}),S?n.jsx("div",{className:"settings-empty-state",children:"Loading plugins..."}):n.jsxs(n.Fragment,{children:[k.length===0?n.jsxs("div",{className:"settings-empty-state",children:[n.jsx(ie,{size:32,className:"text-muted"}),n.jsx("p",{children:"No plugins installed."}),n.jsx("p",{className:"text-muted",children:"Install a plugin to get started, or use a bundled plugin below."})]}):n.jsx("div",{className:"plugin-list",children:k.map(e=>n.jsxs("div",{className:"plugin-item",children:[n.jsxs("div",{className:"plugin-info",children:[n.jsx("span",{className:"plugin-name",children:e.name}),n.jsxs("span",{className:"plugin-version text-muted",children:["v",e.version]}),n.jsx("span",{className:"plugin-state-badge",style:{color:N[e.state]||N.installed},children:e.state})]}),n.jsxs("div",{className:"plugin-actions",children:[e.state==="started"&&n.jsx("button",{className:"btn-icon",onClick:()=>F(e),disabled:h===e.id,title:"Reload",children:n.jsx(M,{size:14,className:h===e.id?"spin":""})}),n.jsxs("label",{className:"toggle-switch",children:[n.jsx("input",{type:"checkbox",checked:e.enabled,onChange:()=>e.enabled?D(e):B(e)}),n.jsx("span",{className:"toggle-slider"})]}),n.jsx("button",{className:"btn-icon",onClick:()=>U(e),title:"Settings",children:n.jsx(ae,{size:14})}),n.jsx("button",{className:"btn-icon",onClick:()=>L(e),title:"Uninstall",children:n.jsx(V,{size:14})})]})]},e.id))}),Z()]})]})}export{fe as PluginManager,N as STATE_COLORS};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as i,j as e}from"./vendor-react-K0fH_qHe.js";import{ay as oe,az as de,s as ue,aA as he,aB as me,aC as be,aD as fe,aE as ve,aF as pe,aG as xe,u as ye,aH as ge,H as je,aI as Re,L as Se,S as _e}from"./index-DYDLmOcK.js";import"./vendor-xterm-DzcZoU0P.js";const O={webSearch:!0,pageFetch:!0,github:!1,localDocs:!0,llmSynthesis:!0};function X(n){const t=n?.researchGlobalDefaults,c=n?.researchSettings;return{enabled:c?.enabled??n?.researchEnabled??n?.researchGlobalEnabled??!0,searchProvider:c?.searchProvider??t?.searchProvider,synthesisProvider:c?.synthesisProvider??t?.synthesisProvider,synthesisModelId:c?.synthesisModelId??t?.synthesisModelId,enabledSources:{webSearch:c?.enabledSources?.webSearch??t?.enabledSources?.webSearch??O.webSearch,pageFetch:c?.enabledSources?.pageFetch??t?.enabledSources?.pageFetch??O.pageFetch,github:c?.enabledSources?.github??t?.enabledSources?.github??O.github,localDocs:c?.enabledSources?.localDocs??t?.enabledSources?.localDocs??O.localDocs,llmSynthesis:c?.enabledSources?.llmSynthesis??t?.enabledSources?.llmSynthesis??O.llmSynthesis},limits:{maxConcurrentRuns:c?.limits?.maxConcurrentRuns??n?.researchMaxConcurrentRuns??n?.researchGlobalMaxConcurrentRuns??3,maxSourcesPerRun:c?.limits?.maxSourcesPerRun??t?.maxSourcesPerRun??n?.researchMaxSourcesPerRun??n?.researchGlobalMaxSourcesPerRun??20,maxDurationMs:c?.limits?.maxDurationMs??n?.researchDefaultTimeout??n?.researchGlobalDefaultTimeout??3e5,requestTimeoutMs:c?.limits?.requestTimeoutMs??n?.researchGlobalFetchTimeoutMs??3e4},defaultExportFormat:t?.defaultExportFormat??"markdown"}}const we=300,Ne=4e3,ke=["queued","running","cancelling","retry_waiting"];function Z(n,t){if(n instanceof xe){const c=n;return{message:n.message,status:n.status,code:c.researchCode??"INTERNAL_ERROR",setupHint:c.setupHint,retryable:c.retryable}}return n instanceof Error?{message:n.message,code:"INTERNAL_ERROR"}:{message:t,code:"INTERNAL_ERROR"}}function Ee(n){if(!n)return{cancelable:!1,retryable:!1,isTransitioning:!1,blockingReason:"No run selected"};const t=ke.includes(n.status),c=n.status==="queued"||n.status==="running",f=n.lifecycle?.retryable,u=n.status==="failed"||n.status==="timed_out",r=!!(u&&f);let x;return!c&&t?x="Run is already transitioning":!c&&n.status==="completed"?x="Completed runs cannot be cancelled":!c&&n.status==="cancelled"?x="Run is already cancelled":!c&&n.status==="retry_exhausted"?x="Retry attempts exhausted":!c&&n.status==="failed"?x="Failed runs cannot be cancelled":!c&&n.status==="timed_out"&&(x="Timed out runs cannot be cancelled"),!r&&u&&f===!1&&(x=n.lifecycle?.errorCode==="RETRY_EXHAUSTED"?"Retry attempts exhausted":"Run is not retryable"),{cancelable:c,retryable:r,isTransitioning:t,blockingReason:x}}function Ce(n){const t=n?.projectId,[c,f]=i.useState([]),[u,r]=i.useState(null),[x,R]=i.useState(null),[g,w]=i.useState({available:!0}),[k,P]=i.useState(!0),[L,T]=i.useState(null),[M,j]=i.useState(null),[S,F]=i.useState(""),E=i.useRef(0),N=i.useRef(0),_=i.useRef(t);i.useEffect(()=>{_.current!==t&&(_.current=t,N.current++)},[t]);const v=i.useCallback(async(o=S)=>{const a=++E.current,l=t;T(null),j(null);try{const y=await oe({q:o||void 0,limit:100},l);if(a!==E.current||l!==t)return;f(y.runs),w(y.availability),u&&!y.runs.some(b=>b.id===u)&&(r(null),R(null))}catch(y){if(a!==E.current||l!==t)return;const b=Z(y,"Failed to load research runs");T(b.message),j(b)}finally{a===E.current&&P(!1)}},[t,S,u]),p=i.useCallback(async o=>{const a=await de(o,t);return R(a.run),w(a.availability),a.run},[t]);return i.useEffect(()=>{P(!0);const o=window.setTimeout(()=>{v(S)},we);return()=>window.clearTimeout(o)},[v,S]),i.useEffect(()=>{if(!u){R(null);return}p(u)},[p,u]),i.useEffect(()=>{const o=N.current,a=()=>N.current!==o,l=t?`?projectId=${encodeURIComponent(t)}`:"";let y=!0;const b=()=>{!y||a()||(v(),u&&p(u))},D=ue(`/api/events${l}`,{events:{"research:run:created":b,"research:run:updated":b,"research:run:completed":b,"research:run:failed":b,"research:run:cancelled":b},onReconnect:b}),U=window.setInterval(b,Ne);return()=>{y=!1,D(),window.clearInterval(U)}},[t,v,u,p]),{runs:c,selectedRun:x,selectedRunId:u,setSelectedRunId:r,availability:g,loading:k,error:L,searchQuery:S,setSearchQuery:F,refresh:v,createRun:o=>pe(o,t),cancelRun:async o=>{try{j(null);const a=await ve(o,t);return u===o&&R(a.run),await v(),a}catch(a){const l=Z(a,"Failed to cancel run");throw j(l),l}},retryRun:async o=>{try{j(null);const a=await fe(o,t);return u===o&&R(a.run),await v(),a}catch(a){const l=Z(a,"Failed to retry run");throw j(l),l}},exportRun:(o,a)=>be(o,a,t),createTaskFromRun:(o,a,l,y,b,D)=>me(o,{title:a,findingId:l,description:y,priority:b,attachExport:D},t),attachRunToTask:(o,a,l,y)=>he(o,{taskId:a,findingId:l,attachExport:y},t),uiError:M,runActionState:Ee(x),statusCounts:c.reduce((o,a)=>(o[a.status]+=1,o),{queued:0,running:0,cancelling:0,retry_waiting:0,completed:0,failed:0,cancelled:0,timed_out:0,retry_exhausted:0})}}function Pe({open:n,mode:t,run:c,finding:f,projectId:u,onClose:r,onConfirm:x}){ye(n);const[R,g]=i.useState(!1),[w,k]=i.useState(""),[P,L]=i.useState(""),[T,M]=i.useState("normal"),[j,S]=i.useState(""),[F,E]=i.useState([]),[N,_]=i.useState(!1),[v,p]=i.useState(!1),o=i.useMemo(()=>{const a=(f.content??"").split(/(?<=[.!?])\s+/)[0]??"";return`${f.heading||"Research finding"} — ${a}`.trim()},[f.content,f.heading]);return i.useEffect(()=>{n&&(g(!1),k(`Research: ${f.heading||c.title}`),L(o),M("normal"),S(""),t==="enrich"&&(_(!0),ge(50,0,u).then(a=>E(a.filter(l=>l.column!=="archived"))).finally(()=>_(!1))))},[n,t,u,f.heading,o,c.title]),n?e.jsx("div",{className:"modal-overlay open",role:"presentation",onClick:r,children:e.jsxs("div",{className:"modal modal-lg research-task-action-modal",role:"dialog","aria-modal":"true",onClick:a=>a.stopPropagation(),children:[e.jsxs("div",{className:"modal-header",children:[e.jsx("h3",{children:t==="create"?"Create task from finding":"Enrich existing task"}),e.jsx("button",{className:"modal-close",type:"button","aria-label":"Close",onClick:r,children:"×"})]}),e.jsxs("div",{className:"research-task-action-modal__body",children:[e.jsxs("div",{className:"card research-task-action-modal__preview",children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Run:"})," ",c.id]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Finding:"})," ",f.id,f.heading?` — ${f.heading}`:""]}),e.jsx("p",{children:o||"No preview available."})]}),t==="create"?e.jsxs(e.Fragment,{children:[e.jsxs("label",{className:"research-task-action-modal__field",children:["Title",e.jsx("input",{className:"input",value:w,onChange:a=>k(a.target.value)})]}),e.jsxs("label",{className:"research-task-action-modal__field",children:["Description",e.jsx("textarea",{className:"input research-task-action-modal__textarea",value:P,onChange:a=>L(a.target.value)})]}),e.jsxs("label",{className:"research-task-action-modal__field",children:["Priority",e.jsxs("select",{className:"select",value:T,onChange:a=>M(a.target.value),children:[e.jsx("option",{value:"low",children:"Low"}),e.jsx("option",{value:"normal",children:"Normal"}),e.jsx("option",{value:"high",children:"High"}),e.jsx("option",{value:"urgent",children:"Urgent"})]})]})]}):e.jsxs("label",{className:"research-task-action-modal__field",children:["Target task",e.jsx("input",{className:"input",list:"research-task-action-task-list",value:j,placeholder:N?"Loading tasks…":"Enter task ID",onChange:a=>S(a.target.value)}),e.jsx("datalist",{id:"research-task-action-task-list",children:F.map(a=>e.jsx("option",{value:a.id,children:a.title},a.id))})]}),e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:R,onChange:a=>g(a.target.checked)}),e.jsx("span",{children:"Attach markdown export artifact"})]})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"btn",type:"button",onClick:r,children:"Cancel"}),e.jsx("button",{className:"btn btn-primary",type:"button",disabled:v||t==="enrich"&&!j,onClick:()=>{p(!0),x({taskId:t==="enrich"?j:void 0,title:t==="create"?w.trim():void 0,description:t==="create"?P.trim():void 0,priority:t==="create"?T:void 0,attachExport:R}).finally(()=>p(!1))},children:t==="create"?"Create Task":"Enrich Task"})]})]})}):null}const Te=["web-search","page-fetch","github","local-docs","llm-synthesis"],Ae={"web-search":"webSearch","page-fetch":"pageFetch",github:"github","local-docs":"localDocs","llm-synthesis":"llmSynthesis"},Ie={"web-search":"Web Search","page-fetch":"Page Fetch",github:"GitHub","local-docs":"Local Docs","llm-synthesis":"LLM Synthesis"};function qe({projectId:n,addToast:t,onOpenSettings:c,readinessVersion:f=0}){const{runs:u,selectedRun:r,selectedRunId:x,setSelectedRunId:R,availability:g,loading:w,error:k,searchQuery:P,setSearchQuery:L,createRun:T,cancelRun:M,retryRun:j,exportRun:S,createTaskFromRun:F,attachRunToTask:E,statusCounts:N,refresh:_,uiError:v,runActionState:p}=Ce({projectId:n}),[o,a]=i.useState(""),[l,y]=i.useState(()=>X(void 0)),[b,D]=i.useState([]),[U,z]=i.useState(!1),[ee,se]=i.useState([]),[A,H]=i.useState(null),[I,$]=i.useState(null),Q=g.supportedProviders??Te,G=s=>l.enabledSources[Ae[s]];i.useEffect(()=>{const s=Q.filter(d=>G(d));se(d=>{const m=d.filter(h=>s.includes(h));return m.length>0?m:s})},[l.enabledSources,Q]),i.useEffect(()=>{let s=!1;return Promise.all([je(n),Re().catch(()=>({providers:[]}))]).then(([d,m])=>{s||(y(X(d)),D(m.providers.filter(h=>h.type==="api_key").map(h=>({id:h.id,authenticated:h.authenticated}))))}).catch(()=>{s||y(X(void 0))}),()=>{s=!0}},[n,f]);const ie=i.useMemo(()=>r?r.status:"No run selected",[r]),ce=i.useMemo(()=>r?r.status==="queued"||r.status==="retry_waiting"?"status-dot status-dot--pending":r.status==="running"?"status-dot status-dot--connecting":r.status==="completed"?"status-dot status-dot--online":r.status==="failed"||r.status==="cancelled"?"status-dot status-dot--error":"status-dot":"status-dot",[r]),K=g.supportedExportFormats??["markdown","json","html"],V=l.searchProvider,te=l.enabledSources.webSearch&&!V,ae=l.enabledSources.llmSynthesis&&(!l.synthesisProvider||!l.synthesisModelId),Y=i.useMemo(()=>new Map(b.map(s=>[s.id,s.authenticated])),[b]),J=i.useMemo(()=>{const s=new Set;return l.enabledSources.webSearch&&V&&s.add(V),l.enabledSources.llmSynthesis&&l.synthesisProvider&&s.add(l.synthesisProvider),[...s].filter(d=>Y.has(d))},[l.enabledSources.llmSynthesis,l.enabledSources.webSearch,l.synthesisProvider,V,Y]).find(s=>Y.get(s)!==!0),q=i.useMemo(()=>g.available?l.enabled?te||ae?{reason:"Research defaults are incomplete.",details:"Select the required provider/model defaults in Research settings.",settingsSection:"research-global"}:J?{reason:`Missing API key for ${J}.`,details:"Add provider credentials in Authentication settings.",settingsSection:"authentication"}:null:{reason:"Research is disabled for this project.",details:"Enable project research settings to create runs.",settingsSection:"research-project"}:{reason:g.reason??"Research is unavailable for this project.",details:g.setupInstructions,settingsSection:"research-project"},[g.available,g.reason,g.setupInstructions,l.enabled,J,te,ae]),B=async(s,d,m)=>{H(s);try{await d(),t?.(m,"success"),await _()}catch(h){t?.(h instanceof Error?h.message:"Action failed","error")}finally{H(null)}},W=async s=>{if(r){H(`export-${s}`);try{const d=await S(r.id,s),m=new Blob([d.content],{type:"text/plain;charset=utf-8"}),h=URL.createObjectURL(m),C=document.createElement("a");C.href=h,C.download=d.filename,document.body.appendChild(C),C.click(),C.remove(),URL.revokeObjectURL(h),t?.(`Exported ${d.filename}`,"success")}catch(d){t?.(d instanceof Error?d.message:"Export failed","error")}finally{H(null)}}},le=async()=>{if(o.trim()){z(!0);try{const s=ee.filter(m=>G(m));if(s.length===0){z(!1),t?.("No enabled research sources are available for this project.","error");return}const d=await T({query:o.trim(),providers:s});R(d.run.id),a(""),t?.("Research run created","success"),await _()}catch(s){t?.(s instanceof Error?s.message:"Failed to create run","error")}finally{z(!1)}}};return e.jsxs("section",{className:"research-view","aria-label":"Research view",children:[e.jsxs("header",{className:"research-view__header",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"research-view__title",children:"Research"}),e.jsx("p",{className:"research-view__subtitle",children:"Create and track research runs with cited findings."})]}),e.jsx("button",{className:"btn",type:"button",onClick:()=>void _(),children:"Refresh"})]}),q?e.jsxs("div",{className:"research-view__state research-view__state--error card","data-testid":"research-state-unavailable",children:[e.jsx("p",{children:q.reason}),q.details&&e.jsx("p",{children:q.details}),e.jsxs("p",{children:["Current defaults: provider ",l.searchProvider??"(not set)",", max sources ",l.limits.maxSourcesPerRun]}),e.jsxs("div",{className:"research-view__actions",children:[e.jsx("button",{className:"btn",type:"button",onClick:()=>void _(),children:"Refresh"}),e.jsx("button",{className:"btn btn-primary",type:"button",onClick:()=>c?.(q.settingsSection),children:"Open Settings"})]})]}):e.jsxs("div",{className:"research-view__layout",children:[e.jsxs("aside",{className:"research-view__sidebar card",children:[e.jsxs("div",{className:"research-view__form",children:[e.jsxs("div",{className:"form-group",children:[e.jsx("label",{htmlFor:"research-query",children:"Query"}),e.jsx("textarea",{id:"research-query",className:"input research-view__textarea",value:o,onChange:s=>a(s.target.value)})]}),e.jsxs("div",{className:"form-group",children:[e.jsx("label",{children:"Providers"}),e.jsx("div",{className:"research-view__providers",children:Q.map(s=>e.jsxs("label",{className:"checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:ee.includes(s),disabled:!G(s),onChange:()=>{G(s)&&se(d=>d.includes(s)?d.filter(m=>m!==s):[...d,s])}}),e.jsx("span",{children:Ie[s]??s})]},s))})]}),e.jsxs("button",{className:"btn btn-primary",type:"button",disabled:!o.trim()||U,onClick:()=>void le(),children:[U?e.jsx(Se,{className:"animate-spin",size:14}):null,"Create Run"]})]}),e.jsxs("div",{className:"research-view__history-header form-group",children:[e.jsx("label",{htmlFor:"research-run-search",children:"Search"}),e.jsxs("div",{className:"research-view__history-search-row",children:[e.jsx(_e,{size:14}),e.jsx("input",{id:"research-run-search",className:"input",placeholder:"Search runs",value:P,onChange:s=>L(s.target.value)})]})]}),e.jsx("div",{className:"research-view__history","data-testid":"research-state-running",children:u.map(s=>e.jsxs("button",{type:"button",className:`research-view__history-item card${x===s.id?" research-view__history-item--active":""}`,onClick:()=>R(s.id),children:[e.jsx("span",{className:"card-id",children:s.id}),e.jsx("span",{children:s.title})]},s.id))})]}),e.jsxs("div",{className:"research-view__reader card",children:[w&&e.jsx("p",{"data-testid":"research-state-loading",children:"Loading research runs…"}),!w&&k&&e.jsx("p",{"data-testid":"research-state-error",children:k}),!w&&!k&&u.length===0&&e.jsx("p",{"data-testid":"research-state-empty",children:"No research runs yet"}),r&&e.jsxs("div",{children:[e.jsxs("div",{className:"research-view__status-row",children:[e.jsx("span",{className:ce}),e.jsx("strong",{children:ie})]}),e.jsx("h3",{className:"research-view__run-title",children:r.title}),e.jsx("p",{className:"research-view__run-query",children:r.query}),e.jsx("p",{className:"research-view__run-summary","data-testid":"research-state-results",children:r.results?.summary??"No summary yet."}),e.jsxs("div",{className:"research-view__actions",children:[e.jsx("button",{className:"btn",type:"button",title:p.cancelable?void 0:p.blockingReason,disabled:A==="cancel"||A==="retry"||!p.cancelable,onClick:()=>void B("cancel",()=>M(r.id),"Run cancelled"),children:"Cancel"}),e.jsx("button",{className:"btn",type:"button",title:p.retryable?void 0:p.blockingReason,disabled:A==="cancel"||A==="retry"||!p.retryable,onClick:()=>void B("retry",()=>j(r.id),"Run retried"),children:"Retry"}),K.includes("markdown")&&e.jsx("button",{className:"btn",type:"button",disabled:A==="export-markdown",onClick:()=>void W("markdown"),children:"Export MD"}),K.includes("json")&&e.jsx("button",{className:"btn",type:"button",disabled:A==="export-json",onClick:()=>void W("json"),children:"Export JSON"}),K.includes("html")&&e.jsx("button",{className:"btn",type:"button",disabled:A==="export-html",onClick:()=>void W("html"),children:"Export HTML"})]}),r.error&&e.jsx("p",{className:"research-view__error",children:r.error}),v&&e.jsxs("div",{className:"form-error",role:"alert",children:[e.jsx("p",{children:v.message}),v.setupHint&&e.jsx("p",{children:v.setupHint}),v.code==="MISSING_CREDENTIALS"&&e.jsx("button",{className:"btn btn-sm",type:"button",onClick:()=>c?.("authentication"),children:"Open Authentication Settings"}),v.code==="FEATURE_DISABLED"&&e.jsx("button",{className:"btn btn-sm",type:"button",onClick:()=>c?.("research-project"),children:"Open Research Settings"})]}),p.blockingReason&&e.jsx("p",{className:"research-view__run-query",children:p.blockingReason}),Array.isArray(r.results?.findings)&&r.results.findings.length>0&&e.jsx("div",{className:"research-view__findings",children:r.results.findings.map((s,d)=>{const h=s.id?.trim()||`finding-${d+1}`;return e.jsxs("article",{className:"research-view__finding card",children:[e.jsx("h4",{children:s.heading}),e.jsx("p",{children:s.content}),e.jsxs("div",{className:"research-view__actions research-view__finding-actions",children:[e.jsx("button",{className:"btn btn-primary btn-sm",type:"button",onClick:()=>$({mode:"create",findingId:h}),children:"Create Task"}),e.jsx("button",{className:"btn btn-sm",type:"button",onClick:()=>$({mode:"enrich",findingId:h}),children:"Enrich Task"})]})]},h)})}),Array.isArray(r.results?.citations)&&r.results.citations.length>0&&e.jsx("ul",{className:"research-view__citations",children:r.results.citations.map(s=>e.jsx("li",{children:e.jsx("a",{href:s,target:"_blank",rel:"noreferrer",children:s})},s))}),r.events.length>0&&e.jsxs("details",{children:[e.jsx("summary",{children:"Run history"}),e.jsx("ul",{className:"research-view__events",children:r.events.map(s=>e.jsx("li",{children:s.message},s.id))})]})]}),!r&&u.length>0&&e.jsx("p",{children:"Select a run to view details."}),e.jsxs("div",{className:"research-view__stats",children:[e.jsxs("div",{className:"research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Running"}),e.jsx("div",{className:"research-view__stat-value",children:N.running})]}),e.jsxs("div",{className:"research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Completed"}),e.jsx("div",{className:"research-view__stat-value",children:N.completed})]}),e.jsxs("div",{className:"research-view__stat-card",children:[e.jsx("div",{className:"research-view__stat-label",children:"Failed"}),e.jsx("div",{className:"research-view__stat-value",children:N.failed})]})]})]})]}),r&&I&&(()=>{const s=r.results?.findings?.findIndex((m,h)=>(m.id?.trim()||`finding-${h+1}`)===I.findingId)??-1,d=s>=0?r.results.findings[s]:null;return d?e.jsx(Pe,{open:!0,mode:I.mode,run:r,finding:{id:I.findingId,heading:d.heading,content:d.content},projectId:n,onClose:()=>$(null),onConfirm:async({taskId:m,title:h,description:C,priority:re,attachExport:ne})=>{I.mode==="create"?await B("create-task",()=>F(r.id,h,I.findingId,C,re,ne),"Task created from research"):m&&await B("attach-task",()=>E(r.id,m,I.findingId,ne),"Task enriched from research"),$(null)}}):null})()]})}export{qe as ResearchView};
|