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