@mmmbuto/nexuscrew 0.8.52-rc.13 → 0.8.52-rc.15

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.
@@ -90,4 +90,4 @@ Minimum version required to store current data is: `+B+`.
90
90
  `).filter(o=>o!==""),promptMode:e.promptMode};return e.modelFlag&&(s.model={flag:e.modelFlag,value:e.modelValue||""}),e.promptMode==="flag"&&(s.promptFlag=e.promptFlag),t&&(s.env=Object.fromEntries(e.envRows.filter(o=>!o.remove&&o.key).map(o=>[o.key,o.value]))),s}function $s({children:e,onClose:t,label:n,error:s=""}){const o=j.useRef(null),a=j.useRef(null),u=j.useRef(t);return u.current=t,j.useEffect(()=>{const h=document.activeElement,p=o.current,f=()=>Array.from((p==null?void 0:p.querySelectorAll('button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [href], [tabindex]:not([tabindex="-1"])'))||[]).filter(y=>y.offsetParent!==null),v=requestAnimationFrame(()=>{var y;return(y=f()[0]||p)==null?void 0:y.focus({preventScroll:!0})}),S=y=>{var C;if(y.key==="Escape"){y.preventDefault(),(C=u.current)==null||C.call(u);return}if(y.key!=="Tab")return;const b=f();if(!b.length){y.preventDefault(),p==null||p.focus();return}const w=b[0],k=b[b.length-1];y.shiftKey&&document.activeElement===w?(y.preventDefault(),k.focus()):!y.shiftKey&&document.activeElement===k&&(y.preventDefault(),w.focus())};return document.addEventListener("keydown",S),()=>{cancelAnimationFrame(v),document.removeEventListener("keydown",S),h&&h.isConnected&&typeof h.focus=="function"&&h.focus({preventScroll:!0})}},[]),j.useEffect(()=>{s&&requestAnimationFrame(()=>{var h;return(h=a.current)==null?void 0:h.scrollIntoView({block:"nearest"})})},[s]),c.jsx("div",{className:"nc-fleet-modal",role:"presentation",onMouseDown:h=>{h.target===h.currentTarget&&t()},children:c.jsxs("div",{ref:o,className:"nc-fleet-modal-dialog",role:"dialog","aria-modal":"true","aria-label":n||m("settings"),tabIndex:-1,children:[e,s&&c.jsx("div",{ref:a,className:"nc-err nc-fleet-modal-error",role:"alert","aria-live":"assertive",children:s})]})})}function zd({token:e,targets:t=[],readonly:n=!1,onPower:s,onImport:o}){const[a,u]=j.useState([]),[h,p]=j.useState(0);j.useEffect(()=>{let C=!0;async function R(){const F=[{route:[],label:m("local")}].concat((t||[]).map(K=>({route:Array.isArray(K.route)?K.route:[],label:K.label||(K.route||[]).join(" › ")}))),O=await Promise.all(F.map(async K=>{const Y={...K,available:!1,readonly:!1,capabilities:[],provider:null,cells:[],unmanaged:[],err:""};try{const z=await ws(e,K.route);Y.available=!!z.available,Y.capabilities=z.capabilities||[],Y.provider=z.provider||null,Y.cells=(z.cells||[]).map(Z=>({...Z,route:K.route}));try{Y.readonly=!!(await Zh(e,K.route)).readonlyDefault}catch{}try{const Z=await Jh(e,K.route),G=new Set(Y.cells.map(Q=>Q.tmuxSession).filter(Boolean));Y.unmanaged=(Z.sessions||[]).filter(Q=>Q&&!G.has(Q.name))}catch{}}catch(z){Y.err=String(z&&z.message||z)}return Y}));C&&u(O)}R();const E=setInterval(R,6e3);return()=>{C=!1,clearInterval(E)}},[e,t,h]);const f=(C,R)=>Array.isArray(C.capabilities)&&C.capabilities.includes(R)&&!n&&!C.readonly,v=()=>p(C=>C+1),S=(C,R)=>{s&&s({...C,route:R})},y=(C,R)=>{s&&s({...C,route:R})},b=async(C,R)=>{try{await Fo(e,C.cell,R)}catch{}v()},w=async(C,R)=>{if(window.confirm(m("fleet-remove-cell").replace("{id}",C.cell))){try{await J_(e,C.cell,!0,R)}catch{}v()}},k=async(C,R)=>{if(window.confirm(m("terminate-confirm").replace("{name}",C.name))){try{await Xh(e,C.name,R)}catch{}v()}};return c.jsxs("div",{className:"nc-fleet-inventory",children:[c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:m("fleet-inventory")}),c.jsx("small",{children:m("fleet-inventory-help")})]}),a.map(C=>{const R=C.route.length?C.route.join("/"):"local";return c.jsxs("div",{className:"nc-fleet-pos",children:[c.jsxs("div",{className:"nc-fleet-pos-title",children:[c.jsx("span",{className:`dot ${C.available?"on":"warn"}`}),c.jsx("b",{children:C.label}),c.jsx("small",{children:C.available?`${C.cells.length} ${m("fleet-cells")} · ${C.unmanaged.length} ${m("fleet-tmux")}`:C.err||m("fleet-not-available")})]}),C.cells.map(E=>c.jsxs("div",{className:"nc-fleet-item nc-fleet-cell",children:[c.jsxs("span",{children:[c.jsx("b",{children:E.cell}),c.jsx("small",{children:`${E.engine||""}${E.key?`·${E.key}`:""}${E.active?"":` · ${m("cell-off")}`}`})]}),c.jsxs("span",{className:"nc-fleet-cell-actions",children:[E.active&&f(C,"down")&&c.jsx("button",{className:"nc-btn ghost",title:m("power-off"),onClick:()=>y(E,C.route),children:m("stop")}),!E.active&&f(C,"up")&&c.jsx("button",{className:"nc-btn ghost",title:m("power-on"),onClick:()=>S(E,C.route),children:m("start")}),f(C,"restart")&&E.active&&c.jsx("button",{className:"nc-btn ghost",title:m("restart"),onClick:()=>b(E,C.route),children:m("restart")}),f(C,"remove")&&c.jsx("button",{className:"nc-btn danger",title:m("delete"),onClick:()=>w(E,C.route),children:"×"})]})]},`${R}:${E.cell}`)),C.unmanaged.map(E=>c.jsxs("div",{className:"nc-fleet-item nc-fleet-unmanaged",children:[c.jsxs("span",{children:[c.jsx("b",{children:E.name}),c.jsx("small",{children:m("fleet-tmux")})]}),c.jsxs("span",{className:"nc-fleet-cell-actions",children:[f(C,"import")&&o&&c.jsx("button",{className:"nc-btn ghost",title:m("import-as-cell"),onClick:()=>o(E,C.route),children:m("import-as-cell")}),c.jsx("button",{className:"nc-btn danger",title:m("terminate"),onClick:()=>k(E,C.route),children:"×"})]})]},`${R}:u:${E.name}`)),C.available&&C.cells.length===0&&C.unmanaged.length===0&&c.jsx("div",{className:"nc-empty",children:m("fleet-inventory-empty")})]},R)})]})}function J2({cells:e=[],engines:t=[],busy:n,canRestore:s=!1,onRestore:o,onClose:a}){const[u,h]=j.useState("export"),[p,f]=j.useState(()=>new Set(e.map(N=>N.id))),[v,S]=j.useState(()=>new Set(t.map(N=>N.id))),[y,b]=j.useState([]),[w,k]=j.useState([]),[C,R]=j.useState(""),E=new Set(t.map(N=>N.id)),F=new Set(e.map(N=>N.id)),O=(N,T)=>N(L=>{const $=new Set(L);return $.has(T)?$.delete(T):$.add(T),$}),K=()=>{const N=K2(e,p,t,v);if(N.ok===!1){R(m(`fleet-backup-${N.error}`));return}const T=e.filter(I=>p.has(I.id)).length,L=t.filter(I=>v.has(I.id)).length;if(N.cells.length!==T){R(m("fleet-backup-invalid-cell"));return}if(N.engines.length!==L){R(m("fleet-backup-invalid-engine"));return}if(!N.cells.length&&!N.engines.length){R(m("fleet-backup-select-one"));return}const $=new Blob([`${JSON.stringify(N,null,2)}
91
91
  `],{type:"application/json"}),U=URL.createObjectURL($),x=document.createElement("a");x.href=U,x.download=`nexuscrew-fleet-${new Date().toISOString().slice(0,10)}.json`,document.body.appendChild(x),x.click(),x.remove(),setTimeout(()=>URL.revokeObjectURL(U),0),R("")},Y=async N=>{if(R(""),b([]),k([]),!!N){if(N.size>1024*1024){R(m("fleet-backup-too-large"));return}try{const T=q2(await N.text());if(!T.ok){R(m(`fleet-backup-${T.error}`));return}const L=new Set(T.engines.map($=>$.id));k(T.engines.map($=>({engine:$,exists:E.has($.id),selected:!E.has($.id)}))),b(T.cells.map($=>{const U=E.has($.engine)||L.has($.engine),x=F.has($.id);return{cell:$,engine:U?$.engine:"",selected:U&&!x,exists:x}}))}catch(T){R(String(T.message||T))}}},z=(N,T)=>b(L=>L.map(($,U)=>U===N?{...$,...T}:$)),Z=(N,T)=>k(L=>L.map(($,U)=>U===N?{...$,...T}:$)),G=new Set([...E,...w.filter(N=>N.selected).map(N=>N.engine.id)]),Q=y.filter(N=>N.selected&&N.engine&&G.has(N.engine)),B=w.filter(N=>N.selected),M=[...new Map([...t.map(N=>[N.id,N.label||N.id]),...w.filter(N=>N.selected).map(N=>[N.engine.id,N.engine.label||N.engine.id])]).entries()];return c.jsxs("div",{className:"nc-set-form nc-fleet-form nc-backup-dialog",children:[c.jsx("b",{children:m("fleet-backup")}),c.jsx("small",{children:m("fleet-backup-help")}),c.jsxs("div",{className:"nc-set-tabs nc-backup-tabs",children:[c.jsx("button",{type:"button",className:`nc-set-tabbtn${u==="export"?" on":""}`,onClick:()=>{h("export"),R("")},children:m("fleet-backup-export")}),c.jsx("button",{type:"button",className:`nc-set-tabbtn${u==="import"?" on":""}`,disabled:!s,title:s?"":m("fleet-backup-restore-unavailable"),onClick:()=>{h("import"),R("")},children:m("fleet-backup-import")})]}),u==="export"?c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{f(new Set(e.map(N=>N.id))),S(new Set(t.map(N=>N.id)))},children:m("select-all")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{f(new Set),S(new Set)},children:m("select-none")})]}),c.jsxs("div",{className:"nc-backup-list",children:[c.jsx("b",{children:m("fleet-engines")}),t.map(N=>{var T;return c.jsxs("label",{className:"nc-check nc-backup-row",children:[c.jsx("input",{type:"checkbox",checked:v.has(N.id),onChange:()=>O(S,N.id)}),c.jsxs("span",{children:[c.jsx("b",{children:N.label||N.id}),c.jsxs("small",{children:[N.id,(T=N.envKeys)!=null&&T.length?` · ${N.envKeys.length} ${m("fleet-backup-env-names")}`:""]})]})]},N.id)}),c.jsx("b",{children:m("fleet-cells")}),e.map(N=>c.jsxs("label",{className:"nc-check nc-backup-row",children:[c.jsx("input",{type:"checkbox",checked:p.has(N.id),onChange:()=>O(f,N.id)}),c.jsxs("span",{children:[c.jsx("b",{children:N.id}),c.jsxs("small",{children:[N.engine," · ",m("fleet-system-prompt")," ",(N.prompt||"").length," ",m("characters")]})]})]},N.id)),!e.length&&!t.length&&c.jsx("div",{className:"nc-empty",children:m("fleet-backup-empty")})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:m("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:!p.size&&!v.size,onClick:K,children:m("fleet-backup-download")})]})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{type:"file",accept:"application/json,.json",disabled:n,onChange:N=>Y(N.target.files&&N.target.files[0])}),c.jsx("small",{children:m("fleet-backup-import-help")}),!!(y.length||w.length)&&c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{k(N=>N.map(T=>({...T,selected:!0}))),b(N=>N.map(T=>({...T,selected:!!T.engine})))},children:m("select-all")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{k(N=>N.map(T=>({...T,selected:!1}))),b(N=>N.map(T=>({...T,selected:!1})))},children:m("select-none")})]}),c.jsxs("div",{className:"nc-backup-list",children:[!!w.length&&c.jsx("b",{children:m("fleet-engines")}),w.map((N,T)=>{var L;return c.jsx("div",{className:"nc-backup-import-row",children:c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:N.selected,onChange:$=>Z(T,{selected:$.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:N.engine.label||N.engine.id}),c.jsxs("small",{children:[N.engine.id," · ",N.exists?m("fleet-backup-overwrite"):m("fleet-backup-new"),(L=N.engine.envKeys)!=null&&L.length?` · ${m("fleet-backup-env-values-required")}`:""]})]})]})},N.engine.id)}),!!y.length&&c.jsx("b",{children:m("fleet-cells")}),y.map((N,T)=>c.jsxs("div",{className:"nc-backup-import-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:N.selected,disabled:!N.engine||!G.has(N.engine),onChange:L=>z(T,{selected:L.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:N.cell.id}),c.jsxs("small",{children:[N.exists?m("fleet-backup-overwrite"):m("fleet-backup-new")," · ",m("fleet-system-prompt")," ",N.cell.systemPrompt.length," ",m("characters")]})]})]}),c.jsxs("select",{value:N.engine,onChange:L=>z(T,{engine:L.target.value,selected:!!L.target.value}),children:[c.jsx("option",{value:"",children:m("fleet-backup-engine-missing")}),M.map(([L,$])=>c.jsx("option",{value:L,children:$},L))]})]},N.cell.id))]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:m("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||!Q.length&&!B.length,onClick:()=>o({engineRows:w,cellRows:y}),children:m("fleet-backup-restore")})]})]}),C&&c.jsx("div",{className:"nc-err",children:C})]})}function Z2({state:e,setState:t,busy:n,onSave:s,catalog:o}){var R;const a=e.form,u=E=>t({...e,form:{...a,...E}}),h=a.envRows||[],p=[...new Map(o.map(E=>[E.client,E.clientLabel||E.client])).entries()],f=o.filter(E=>E.client===a.client),v=Zo(o,a),S=typeof(v==null?void 0:v.credentialEnv)=="string"?v.credentialEnv:"",y=Array.isArray(v==null?void 0:v.credentialUsedBy)?v.credentialUsedBy:[],b=(v==null?void 0:v.credentialSource)||"missing",w=!!(v!=null&&v.credentialEnv)||a.credentialProfile==="a"||a.credentialProfile==="p",k=!!S&&e.mode==="new"&&(v==null?void 0:v.authConfigured)!==!0&&!a.credentialValue&&!a.allowMissingCredential,C=E=>{E&&u({client:E.client,provider:E.provider,credentialProfile:E.credentialProfile||"",managedModel:E.model||"",protocol:E.protocol||"",permissionPolicy:E.permissionPolicyDefault||"standard",credentialSourcePolicy:"auto",rc:!!E.rc,displayName:E.custom?m("fleet-custom-provider-default"):"",baseUrl:E.custom?"":E.endpoint||"",envKey:E.defaultEnvKey||"",providerId:"nexuscrew-custom",credentialValue:"",credentialReveal:!1,allowMissingCredential:!1,...e.mode==="new"?{id:E.id,label:""}:{}})};return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsx("b",{children:e.mode==="new"?m("fleet-new-engine"):`${m("edit")} ${a.id}`}),c.jsx("input",{value:a.id,disabled:e.mode!=="new",placeholder:"id",onChange:E=>u({id:E.target.value})}),c.jsx("input",{value:a.label,placeholder:m("label"),onChange:E=>u({label:E.target.value})}),c.jsxs("select",{value:a.kind,disabled:e.mode!=="new",onChange:E=>u({kind:E.target.value}),children:[c.jsx("option",{value:"managed",children:m("fleet-managed")}),c.jsx("option",{value:"custom",children:m("fleet-custom")})]}),a.kind==="managed"?c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("select",{value:a.client,disabled:e.mode!=="new",onChange:E=>C(o.find(F=>F.client===E.target.value&&F.default)||o.find(F=>F.client===E.target.value)),children:p.map(([E,F])=>c.jsx("option",{value:E,children:F},E))}),c.jsx("select",{value:((R=Zo(o,a))==null?void 0:R.id)||"",disabled:e.mode!=="new",onChange:E=>C(o.find(F=>F.id===E.target.value)),children:f.map(E=>c.jsx("option",{value:E.id,children:E.label},E.id))})]}),a.client!=="shell"&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.managedModel,list:"nc-managed-models",placeholder:m(v!=null&&v.requiresModel?"fleet-model-required":"fleet-model-default"),onChange:E=>u({managedModel:E.target.value})}),c.jsx("datalist",{id:"nc-managed-models",children:[...(v==null?void 0:v.models)||[],...a.modelOptions||[]].filter((E,F,O)=>E&&O.indexOf(E)===F).map(E=>c.jsx("option",{value:E},E))})]}),v!=null&&v.supportsUnsafe?c.jsxs("select",{value:a.permissionPolicy,onChange:E=>u({permissionPolicy:E.target.value}),children:[c.jsx("option",{value:"standard",children:m("fleet-standard-permissions")}),c.jsx("option",{value:"unsafe",children:m("fleet-unsafe-permissions")})]}):c.jsx("small",{children:m("fleet-standard-permissions")}),(v==null?void 0:v.supportsUnsafe)&&a.permissionPolicy==="unsafe"&&c.jsx("small",{className:"nc-err",children:m("fleet-unsafe-warning")}),w&&c.jsxs("select",{"aria-label":m("fleet-credential-source-policy"),value:a.credentialSourcePolicy||"auto",onChange:E=>u({credentialSourcePolicy:E.target.value}),children:[c.jsx("option",{value:"auto",children:m("fleet-credential-source-policy-auto")}),c.jsx("option",{value:"nexuscrew-store",children:m("fleet-credential-source-policy-nexuscrew-store")}),c.jsx("option",{value:"environment",children:m("fleet-credential-source-policy-environment")})]}),(v==null?void 0:v.credentialEnv)===!0&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.envKey,placeholder:m("fleet-api-key-env"),onChange:E=>u({envKey:E.target.value})}),c.jsx("small",{children:m("fleet-custom-secret-help")})]}),S&&c.jsxs("section",{className:"nc-fleet-key","aria-label":m("fleet-key-section"),children:[c.jsxs("div",{className:"nc-fleet-key-head",children:[c.jsx("b",{children:m("fleet-key-section")}),c.jsx("span",{children:m(`fleet-credential-source-${b}`)})]}),c.jsxs("label",{className:"nc-field",children:[m("fleet-key-variable"),c.jsx("input",{value:S,readOnly:!0,"aria-label":m("fleet-key-variable")})]}),c.jsx("small",{children:y.length?m("fleet-key-used-by").replace("{engines}",y.join(", ")):m("fleet-key-used-by-none")}),y.length>0&&c.jsx("small",{children:m("fleet-key-shared-impact").replace("{key}",S).replace("{engines}",y.join(", "))}),c.jsxs("div",{className:"nc-fleet-key-input",children:[c.jsx("input",{type:a.credentialReveal?"text":"password",value:a.credentialValue||"","aria-label":m("fleet-key-value").replace("{key}",S),autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",placeholder:v!=null&&v.authConfigured||e.mode==="edit"?m("fleet-key-blank-keeps"):m("fleet-key-enter"),onChange:E=>u({credentialValue:E.target.value,allowMissingCredential:E.target.value?!1:a.allowMissingCredential})}),c.jsx("button",{type:"button",className:"nc-btn ghost","aria-label":m(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal"),onClick:()=>u({credentialReveal:!a.credentialReveal}),children:m(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal")})]}),c.jsx("small",{children:m("fleet-key-local-help")}),e.mode==="new"&&(v==null?void 0:v.authConfigured)!==!0&&!a.credentialValue&&c.jsxs("label",{className:"nc-check nc-fleet-key-missing",children:[c.jsx("input",{type:"checkbox",checked:!!a.allowMissingCredential,onChange:E=>u({allowMissingCredential:E.target.checked})}),m("fleet-key-create-missing")]}),(v==null?void 0:v.notice)&&c.jsx("small",{className:"nc-fleet-provider-notice",children:m(`fleet-provider-notice-${v.notice}`)})]}),(v==null?void 0:v.notice)&&!S&&c.jsx("small",{className:"nc-fleet-provider-notice",children:m(`fleet-provider-notice-${v.notice}`)}),a.provider==="custom"&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.displayName,placeholder:m("fleet-provider-display"),onChange:E=>u({displayName:E.target.value})}),c.jsx("input",{value:a.baseUrl,placeholder:"https://api.example.com/v1",onChange:E=>u({baseUrl:E.target.value})}),((v==null?void 0:v.protocols)||[]).length>1&&c.jsx("select",{value:a.protocol,onChange:E=>u({protocol:E.target.value}),children:v.protocols.map(E=>c.jsx("option",{value:E,children:E},E))}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:a.envKey,placeholder:m("fleet-api-key-env"),onChange:E=>u({envKey:E.target.value})}),c.jsx("input",{value:a.providerId,placeholder:m("fleet-provider-id"),onChange:E=>u({providerId:E.target.value})})]}),c.jsxs("small",{children:[a.protocol," · ",m("fleet-custom-secret-help")]})]}),c.jsx("small",{children:m(a.client==="shell"?"fleet-shell-engine-help":"fleet-managed-help")})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.command,placeholder:m("command-path"),onChange:E=>u({command:E.target.value})}),c.jsx("textarea",{value:a.argsText,placeholder:m("args-lines"),onChange:E=>u({argsText:E.target.value})}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:a.rc,onChange:E=>u({rc:E.target.checked})})," remote control"]}),c.jsxs("select",{value:a.promptMode,onChange:E=>u({promptMode:E.target.value}),children:[c.jsx("option",{value:"send-keys",children:"send-keys"}),c.jsx("option",{value:"flag",children:"flag"})]}),a.promptMode==="flag"&&c.jsx("input",{value:a.promptFlag,placeholder:"prompt flag",onChange:E=>u({promptFlag:E.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:a.modelFlag,placeholder:"model flag",onChange:E=>u({modelFlag:E.target.value})}),c.jsx("input",{value:a.modelValue,placeholder:"model default",onChange:E=>u({modelValue:E.target.value})})]}),c.jsx("small",{children:m("env-write-only")}),h.map((E,F)=>c.jsxs("div",{className:"nc-fleet-env",children:[c.jsx("input",{value:E.key,disabled:E.configured,placeholder:"ENV_KEY",onChange:O=>{const K=h.slice();K[F]={...E,key:O.target.value},u({envRows:K})}}),c.jsx("input",{type:"password",value:E.value,placeholder:E.configured?"•••••• (unchanged)":"value",onChange:O=>{const K=h.slice();K[F]={...E,value:O.target.value},u({envRows:K})}}),c.jsx("button",{className:"nc-btn danger",onClick:()=>u({envRows:h.filter((O,K)=>K!==F)}),children:"×"})]},`${E.key}-${F}`)),c.jsx("button",{className:"nc-btn ghost",onClick:()=>u({envRows:[...h,{key:"",value:"",configured:!1}]}),children:"+ env"})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>t(null),children:m("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:n||!a.id||k||a.kind==="custom"&&!a.command||a.kind==="managed"&&(v==null?void 0:v.requiresModel)&&!a.managedModel||a.kind==="managed"&&(v==null?void 0:v.credentialEnv)===!0&&!a.envKey||a.kind==="managed"&&a.provider==="custom"&&(!a.displayName||!a.baseUrl||!a.envKey||!a.providerId),onClick:s,children:m("save")})]})]})}function ej({token:e,route:t,targets:n=[],location:s,setLocation:o,state:a,setState:u,engines:h,busy:p,onSave:f}){var Y;const[v,S]=j.useState(null),[y,b]=j.useState(""),w=a.form,k=z=>u({...a,form:{...w,...z}}),C=h.find(z=>z.id===w.engine),R=((Y=C==null?void 0:C.managed)==null?void 0:Y.client)==="shell",E=z=>{var Q,B,M,N;const Z=h.find(T=>T.id===z),G={...w.commands||{}};((Q=C==null?void 0:C.managed)==null?void 0:Q.client)==="shell"&&(w.command?G[w.engine]=w.command:delete G[w.engine]),k({engine:z,model:((B=w.models)==null?void 0:B[z])||((M=Z==null?void 0:Z.managed)==null?void 0:M.model)||((N=Z==null?void 0:Z.model)==null?void 0:N.value)||"",commands:G,command:G[z]||""})},F=z=>{const Z={...w.commands||{}};z?Z[w.engine]=z:delete Z[w.engine],k({command:z,commands:Z})},O=R&&/[|&;<>()`$*?{}\[\]~]/.test(w.command||""),K=async z=>{try{const Z=await Qh(e,z,t);S(Z),k({cwd:Z.path}),b("")}catch(Z){b(String(Z.message||Z))}};return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsx("b",{children:a.mode==="new"?m("fleet-new-cell"):`${m("edit")} ${w.id}`}),a.mode==="new"&&c.jsxs("label",{className:"nc-field",children:[m("location"),c.jsx("span",{className:"nc-req",children:" *"}),c.jsxs("select",{value:s,onChange:z=>{o(z.target.value),k({engine:""})},children:[c.jsx("option",{value:"",children:m("local")}),n.map(z=>c.jsxs("option",{value:z.route.join("/"),disabled:z.status&&z.status!=="up",children:[z.label,z.status&&z.status!=="up"?` · ${m("node-offline")}`:""]},z.route.join("/")))]})]}),c.jsx("input",{value:w.id,disabled:a.mode!=="new",placeholder:"id",onChange:z=>k({id:z.target.value})}),c.jsx("input",{value:w.label||"",maxLength:64,placeholder:m("fleet-cell-label"),onChange:z=>k({label:z.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:w.cwd,placeholder:m("cwd"),onChange:z=>k({cwd:z.target.value})}),c.jsx("button",{className:"nc-btn ghost",onClick:()=>v?S(null):K(w.cwd),children:m("browse")})]}),v&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:v.path}),c.jsxs("div",{className:"nc-fs-list",children:[v.parent&&c.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>K(v.parent),children:["↑ ",m("fs-parent")]}),(v.dirs||[]).map(z=>c.jsxs("button",{className:"nc-fs-item",onClick:()=>K(`${v.path.replace(/\/$/,"")}/${z}`),children:["📁 ",z]},z))]})]}),y&&c.jsx("div",{className:"nc-err",children:y}),c.jsx("select",{value:w.engine,onChange:z=>E(z.target.value),children:h.map(z=>c.jsx("option",{value:z.id,children:z.label},z.id))}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:!!w.boot,onChange:z=>k({boot:z.target.checked})})," boot"]}),R?c.jsxs(c.Fragment,{children:[c.jsx("input",{value:w.command||"",maxLength:4096,placeholder:m("fleet-shell-command-placeholder"),onChange:z=>F(z.target.value)}),c.jsx("small",{children:w.command?m("fleet-shell-command-help"):m("fleet-shell-interactive")}),O&&c.jsx("small",{className:"nc-note",children:m("fleet-shell-command-metachar")})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{value:w.model||"",list:"nc-cell-models",placeholder:m("fleet-model-override"),onChange:z=>k({model:z.target.value})}),c.jsx("datalist",{id:"nc-cell-models",children:((C==null?void 0:C.availableModels)||[]).map(z=>c.jsx("option",{value:z},z))}),c.jsx("textarea",{value:w.prompt||"",placeholder:"prompt",onChange:z=>k({prompt:z.target.value})})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>u(null),children:m("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:p||!w.id||!w.cwd||!w.engine,onClick:f,children:m("save")})]})]})}function I_({token:e,route:t=[],state:n,setState:s,busy:o,onSave:a}){const[u,h]=j.useState(null),[p,f]=j.useState([]),[v,S]=j.useState(""),y=Array.isArray(t)?t.join("/"):"";j.useEffect(()=>{let R=!0;return Gh(e,y?y.split("/"):[]).then(E=>{R&&(f(E.engines||[]),S(""))}).catch(E=>{R&&(f([]),S(String(E.message||E)))}),()=>{R=!1}},[e,y]);const b=n.form,w=R=>s({...n,form:{...b,...R}}),k=async R=>{try{const E=await Qh(e,R,t);h(E),w({cwd:E.path})}catch{}},C=!b.id&&b.tmuxSession?b.tmuxSession.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,32):b.id;return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsxs("b",{children:[m("import-as-cell")," · ",b.tmuxSession]}),c.jsx("small",{children:m("import-help")}),c.jsx("input",{value:b.tmuxSession,disabled:!0,placeholder:"tmux session",readOnly:!0}),c.jsx("input",{value:b.id,placeholder:m("name"),onChange:R=>w({id:R.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:b.cwd,placeholder:m("cwd"),onChange:R=>w({cwd:R.target.value})}),c.jsx("button",{className:"nc-btn ghost",type:"button",onClick:()=>u?h(null):k(b.cwd),children:m("browse")})]}),u&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:u.path}),c.jsxs("div",{className:"nc-fs-list",children:[u.parent&&c.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>k(u.parent),children:["↑ ",m("fs-parent")]}),(u.dirs||[]).map(R=>c.jsxs("button",{className:"nc-fs-item",onClick:()=>k(`${u.path.replace(/\/$/,"")}/${R}`),children:["📁 ",R]},R))]})]}),c.jsxs("select",{value:b.engine,onChange:R=>w({engine:R.target.value}),children:[c.jsx("option",{value:"",children:m("import-engine-required")}),p.map(R=>c.jsx("option",{value:R.id,children:R.label},R.id))]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:!!b.boot,onChange:R=>w({boot:R.target.checked})})," boot"]}),(n.err||v)&&c.jsx("div",{className:"nc-err",children:n.err||v}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>s(null),children:m("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:o||!b.tmuxSession||!b.engine||!p.length,onClick:a,children:m(C?"import-as-cell":"save")})]})]})}function El(e){const t=Ks(e);return t===null?`~/${e}`:t?`~/${t}`:"~"}function tj(e){if(!e||typeof e.path!="string"||typeof e.home!="string")return"";const{path:t,home:n}=e;return!n||t===n||!t.startsWith(n+"/")?"":t.slice(n.length+1).replace(/\/+$/,"")}function nj({token:e,route:t,cell:n,busy:s=!1,onSaved:o,onClose:a}){const[u,h]=j.useState(""),[p,f]=j.useState(null),[v,S]=j.useState(!1),[y,b]=j.useState(""),w=typeof(n==null?void 0:n.cwdSuggestion)=="string"&&Ks(n.cwdSuggestion)!==null?Ks(n.cwdSuggestion):null,[k,C]=j.useState(w),R=n==null?void 0:n.id,E=s||v,F=async G=>{const Q=Ks(G);if(Q===null){f({rel:"",dirs:[],loading:!1,err:m("fleet-cwd-repair-invalid")});return}f({rel:Q,dirs:[],loading:!0,err:""});try{const B=await Qh(e,Q,t),M=tj(B);f({rel:M,dirs:Array.isArray(B.dirs)?B.dirs:[],loading:!1,err:""})}catch(B){f({rel:Q,dirs:[],loading:!1,err:String(B&&B.message||B)})}},O=G=>{if(!p)return;const Q=p.rel?`${p.rel}/${G}`:G;F(Q)},K=()=>{p!=null&&p.rel&&F(p.rel.split("/").slice(0,-1).join("/"))},Y=()=>{p&&(h(p.rel||""),f(null),b(""),C(null))},z=()=>{k!==null&&(h(k),C(null),b(""))},Z=async()=>{if(E)return;const G=Ks(u);if(G===null){b(m("fleet-cwd-repair-invalid"));return}const Q=El(G);let B=!0;try{B=window.confirm(m("fleet-cwd-repair-confirm").replace("{id}",String(R||"")).replace("{path}",Q))}catch{B=!1}if(B){S(!0),b(""),C(null);try{const M=await Q_(e,R,{cwdRel:G},t);o==null||o(M)}catch(M){const N=M&&M.data||{};if(N.code==="unportable-cwd"){b(m("fleet-cwd-repair-unportable"));const T=Array.isArray(N.cells)?N.cells.find($=>$&&$.id===R):null,L=T&&typeof T.suggestion=="string"?T.suggestion:null;L!==null&&Ks(L)!==null&&C(L)}else b(String(M&&M.message||M))}finally{S(!1)}}};return c.jsxs("div",{className:"nc-set-form nc-fleet-form nc-cwd-repair",children:[c.jsx("b",{children:m("fleet-cwd-repair-title").replace("{id}",String(R||""))}),c.jsx("small",{className:"nc-set-info",children:m("fleet-cwd-repair-help")}),c.jsx("small",{className:"nc-cwd-no-source",children:m("fleet-cwd-repair-no-source-path")}),c.jsxs("label",{className:"nc-field",children:[m("cwd"),c.jsxs("div",{className:"nc-cwd-rel-row",children:[c.jsx("span",{className:"nc-cwd-rel-prefix",children:"~/"}),c.jsx("input",{value:u,placeholder:m("fleet-cwd-repair-placeholder"),autoComplete:"off",spellCheck:"false",autoCapitalize:"none",onChange:G=>h(G.target.value)}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:E,onClick:()=>p?f(null):F(""),children:m(p?"close":"fleet-cwd-repair-browse")})]})]}),c.jsx("div",{className:"nc-cwd-preview",children:m("fleet-cwd-repair-preview").replace("{path}",El(u))}),p&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:El(p.rel)}),c.jsxs("div",{className:"nc-fs-list",children:[p.rel&&c.jsxs("button",{type:"button",className:"nc-fs-item nc-fs-nav",onClick:K,children:["↑ ",m("fs-parent")]}),p.loading&&c.jsx("div",{className:"nc-fs-empty",children:"…"}),!p.loading&&p.dirs.length===0&&c.jsx("div",{className:"nc-fs-empty",children:m("fleet-cwd-repair-fs-empty")}),!p.loading&&p.dirs.map(G=>c.jsxs("button",{type:"button",className:"nc-fs-item",onClick:()=>O(G),children:["📁 ",G]},G))]}),p.err&&c.jsx("div",{className:"nc-err",children:p.err}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{type:"button",className:"nc-btn primary",disabled:E||p.loading||!!p.err,onClick:Y,children:m("fleet-cwd-repair-use-current")})})]}),y&&c.jsx("div",{className:"nc-err",role:"alert",children:y}),k!==null&&c.jsxs("div",{className:"nc-cwd-suggestion",role:"status",children:[c.jsx("span",{children:m("fleet-cwd-repair-suggestion")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:z,children:m("fleet-cwd-repair-use-suggestion").replace("{path}",El(k))})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:v,onClick:a,children:m("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:E,onClick:Z,children:v?"…":m("save")})]})]})}function ij({token:e,readonly:t,targets:n=[],startNewCell:s=!1,initialLocation:o=""}){const[a,u]=j.useState({engines:[],cells:[],managedCatalog:[]}),[h,p]=j.useState({available:!1,capabilities:[]}),[f,v]=j.useState(!1),[S,y]=j.useState(null),[b,w]=j.useState(null),[k,C]=j.useState(!1),[R,E]=j.useState(""),[F,O]=j.useState(""),[K,Y]=j.useState(o),[z,Z]=j.useState(!1),[G,Q]=j.useState(null),[B,M]=j.useState(null),[N,T]=j.useState(null),[L,$]=j.useState(!1),[U,x]=j.useState([]),[I,D]=j.useState(null),[A,W]=j.useState("manage"),X=j.useRef(!1),ie=K?K.split("/"):[],le=j.useCallback(async()=>{try{const re=await ws(e,ie);p(re);try{const xe=await Zh(e,ie);Z(!!xe.readonlyDefault)}catch{Z(!1)}if(re.provider==="builtin"&&(re.capabilities||[]).includes("definitions")){const xe=await Gh(e,ie),Oe=new Map((re.engines||[]).map(Fe=>[Fe.id,Fe]));if(xe.engines=(xe.engines||[]).map(Fe=>{var de;return{...Fe,availableModels:((de=Oe.get(Fe.id))==null?void 0:de.models)||[]}}),u(xe),(re.capabilities||[]).includes("credentials"))try{x((await f0(e,ie)).credentials||[])}catch{x([])}else x([])}E("")}catch(re){E(String(re.message||re))}finally{v(!0)}},[e,K]);j.useEffect(()=>{le();const re=setInterval(le,5e3);return()=>clearInterval(re)},[le]);const fe=new Set((h.cells||[]).filter(re=>re.active).map(re=>re.cell)),we=h.provider==="builtin"&&(h.capabilities||[]).includes("edit"),pe=(h.capabilities||[]).includes("restore");j.useEffect(()=>{!s||X.current||!we||!a.engines.length||(X.current=!0,w({mode:"new",form:M_(a.engines[0].id)}))},[s,we,a.engines]);const _e=async re=>{C(!0),E(""),O("");try{await re(),await le()}catch(xe){E(String(xe.message||xe))}C(!1)},He=()=>_e(async()=>{var me;const re=S.mode==="new",xe=S.form,Oe=a.managedCatalog||[],Fe=xe.kind==="managed"?Zo(Oe,xe):null,de=typeof(Fe==null?void 0:Fe.credentialEnv)=="string"?Fe.credentialEnv:"",ze=de&&xe.credentialValue||"",q=Q2(xe,re,Oe);!re&&S.form.kind==="custom"&&!S.form.modelFlag&&(q.model=null),!re&&S.form.kind==="custom"&&S.form.promptMode!=="flag"&&(q.promptFlag=null);let te;if(re)te=await m0(e,q,ie);else{const Te=S.original,ve=new Set(S.form.envRows.filter(ot=>!ot.remove).map(ot=>ot.key)),Ce=(Te.envKeys||[]).filter(ot=>!ve.has(ot)),nt=Object.fromEntries(S.form.envRows.filter(ot=>!ot.remove&&ot.key&&(!ot.configured||ot.value!=="")).map(ot=>[ot.key,ot.value]));te=await g0(e,Te.id,q,S.form.kind==="custom"?{set:nt,remove:Ce}:void 0,ie)}let H=null;if(de&&ze)try{H=await yg(e,de,ze,ie),x(H.credentials||[])}catch{throw await le(),re&&y({mode:"edit",original:{...q,id:xe.id},form:{...xe,credentialReveal:!1}}),new Error(m(re?"fleet-key-partial-create":"fleet-key-partial-edit"))}await le(),y(null),O(m("fleet-saved"));const ae=((me=((H==null?void 0:H.credentials)||[]).find(Te=>Te.envKey===de))==null?void 0:me.activeCells)||[],ce=[...new Set([...(te==null?void 0:te.activeCells)||[],...ae])];if(ce.length&&window.confirm(m("fleet-restart-confirm").replace("{cells}",ce.join(", "))))for(const Te of ce)await Fo(e,Te,ie)}),ht=()=>_e(async()=>{var H,ae,ce,me,Te,ve;const re=b.mode==="new",xe=b.form,Oe={...re?{id:xe.id}:{},cwd:xe.cwd,engine:xe.engine,boot:!!xe.boot},Fe={...xe.commands||{}},de=a.engines.find(Ce=>Ce.id===xe.engine);((H=de==null?void 0:de.managed)==null?void 0:H.client)==="shell"&&typeof xe.command=="string"&&xe.command.length?Fe[xe.engine]=xe.command:((ae=de==null?void 0:de.managed)==null?void 0:ae.client)==="shell"&&delete Fe[xe.engine],(Object.keys(Fe).length||!re)&&(Oe.commands=Fe);const ze=(xe.label||"").trim();re?ze&&(Oe.label=ze):Oe.label=ze||null,re?(((ce=de==null?void 0:de.managed)==null?void 0:ce.client)!=="shell"&&xe.model&&(Oe.model=xe.model),((me=de==null?void 0:de.managed)==null?void 0:me.client)!=="shell"&&xe.prompt&&(Oe.prompt=xe.prompt)):(Oe.model=((Te=de==null?void 0:de.managed)==null?void 0:Te.client)==="shell"?null:xe.model||null,((ve=de==null?void 0:de.managed)==null?void 0:ve.client)!=="shell"&&(Oe.prompt=xe.prompt||null));const q=re?await _0(e,Oe,ie):await Q_(e,b.original.id,Oe,ie),te=re?xe.id:b.original.id;w(null),O(m("fleet-saved")),!re&&(q!=null&&q.active)&&window.confirm(m("fleet-restart-confirm").replace("{cells}",te))&&await Fo(e,te,ie)}),Ye=t||z,Ae=re=>U.find(xe=>{var Oe;return(Oe=xe.engines)==null?void 0:Oe.includes(re.id)})||null,Ne=()=>_e(async()=>{var Oe;const re=await yg(e,I.envKey,I.value,ie);x(re.credentials||[]),D(null),O(m("fleet-credential-saved"));const xe=((Oe=(re.credentials||[]).find(Fe=>Fe.envKey===I.envKey))==null?void 0:Oe.activeCells)||[];if(xe.length&&window.confirm(m("fleet-restart-confirm").replace("{cells}",xe.join(", "))))for(const Fe of xe)await Fo(e,Fe,ie)}),ut=re=>_e(async()=>{if(!window.confirm(m("fleet-credential-remove-confirm").replace("{key}",re.envKey)))return;const xe=await p0(e,re.envKey,ie);x(xe.credentials||[]),O(m("fleet-credential-removed"))}),Pe=re=>Q({...re,route:Array.isArray(re==null?void 0:re.route)?re.route:ie}),Ut=async re=>{if(!G)return;const xe=G.cell||G.id,Oe=Array.isArray(G.route)?G.route:ie;if(re.action==="up"){const Fe=await Yh(e,{cell:xe,boot:!!re.boot,...re.engine?{engine:re.engine}:{},...re.model!==void 0?{model:re.model}:{},...re.permissionPolicy?{permissionPolicy:re.permissionPolicy}:{}},Oe),de=uf(Fe);de&&O(de.text)}else await Il(e,{cell:xe,boot:!!re.boot},Oe)},Rt=(re,xe)=>{E(""),M({mode:"new",route:Array.isArray(xe)?xe:ie,form:{tmuxSession:re.name,id:"",engine:"",cwd:"",boot:!1},err:""})},Dt=()=>_e(async()=>{const re=B.form;if(!re.engine){M({...B,err:m("import-engine-required")});return}await y0(e,{tmuxSession:re.tmuxSession,id:re.id||void 0,engine:re.engine,cwd:re.cwd||void 0,boot:!!re.boot},re.route||ie),M(null),O(m("fleet-saved"))}),vt=({engineRows:re=[],cellRows:xe=[]})=>_e(async()=>{const Oe=re.filter(me=>me.selected),Fe=Oe.map(me=>ib(me.engine));if(Fe.some(me=>!me))throw new Error(m("fleet-backup-invalid-engine"));const de=Oe.filter(me=>me.exists).map(me=>me.engine.id);if(de.length&&!window.confirm(m("fleet-backup-confirm-engine-overwrite").replace("{engines}",de.join(", "))))return;const ze=[...new Set([...a.engines.map(me=>me.id),...Fe.map(me=>me.id)])],q=[];for(const me of xe.filter(Te=>Te.selected)){const Te=V2(me.cell,me.engine,ze);if(!Te)throw new Error(`${me.cell.id}: ${m("fleet-backup-engine-missing")}`);q.push(Te)}const te=xe.filter(me=>me.selected&&me.exists).map(me=>me.cell.id);if(te.length&&!window.confirm(m("fleet-backup-confirm-overwrite").replace("{cells}",te.join(", "))))return;const H=Fe.length?await b0(e,Fe,de.length>0,ie):{needsRestart:[]},ae=q.length?await w0(e,q,ie):{needsRestart:[]},ce=[...new Set([...H.needsRestart||[],...ae.needsRestart||[]])];$(!1),O(`${m("fleet-backup-restored").replace("{n}",String(q.length))}${Fe.length?` · ${Fe.length} ${m("fleet-engines").toLowerCase()}`:""}${ce.length?` · ${m("fleet-backup-needs-restart").replace("{cells}",ce.join(", "))}`:""}`)}),ft=c.jsxs("label",{className:"nc-field",children:[m("location"),c.jsxs("select",{value:K,onChange:re=>{Y(re.target.value),y(null),w(null),E(""),O(""),Z(!1),v(!1),p({available:!1,capabilities:[]}),u({engines:[],cells:[],managedCatalog:[]}),x([]),D(null)},children:[c.jsx("option",{value:"",children:m("local")}),n.map(re=>c.jsxs("option",{value:re.route.join("/"),disabled:re.status&&re.status!=="up",children:[re.label,re.status&&re.status!=="up"?` · ${m("node-offline")}`:""]},re.route.join("/")))]})]}),dt=c.jsxs("div",{className:"nc-fleet-view-tabs",role:"tablist","aria-label":m("tab-fleet"),children:[c.jsx("button",{type:"button",role:"tab","aria-selected":A==="manage",className:`nc-set-tabbtn${A==="manage"?" on":""}`,onClick:()=>W("manage"),children:m("fleet-manage-location")}),c.jsx("button",{type:"button",role:"tab","aria-selected":A==="overview",className:`nc-set-tabbtn${A==="overview"?" on":""}`,onClick:()=>W("overview"),children:m("fleet-network-overview")})]});return f?we?c.jsxs("div",{className:"nc-set-tab nc-fleet-editor",children:[dt,A==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:m("fleet-overview-help")}),c.jsx(zd,{token:e,targets:n,readonly:t,onPower:Pe,onImport:Rt})]}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:m("fleet-manage-help")}),ft,c.jsxs("div",{className:"nc-set-row nc-fleet-backup-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>{E(""),$(!0)},children:m("fleet-backup")}),c.jsx("small",{children:m("fleet-backup-help")})]}),c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:m("fleet-cells")}),c.jsx("span",{className:"nc-fleet-head-actions",children:c.jsxs("button",{className:"nc-btn primary",disabled:Ye||k||!a.engines.length,onClick:()=>{var re;E(""),w({mode:"new",form:M_((re=a.engines[0])==null?void 0:re.id)})},children:["+ ",m("add")]})})]}),a.cells.map(re=>{const xe=fe.has(re.id),Oe=h.capabilities||[],Fe=re.needsRepair===!0;return c.jsxs("div",{className:"nc-fleet-item",children:[c.jsxs("span",{children:[c.jsx("b",{children:re.id}),re.label?c.jsxs("small",{className:"nc-fleet-label",children:[" ",re.label]}):null,c.jsxs("small",{children:[re.engine," · ",Fe?c.jsx("span",{className:"nc-fleet-tag nc-fleet-tag-warn",children:m("fleet-cwd-needs-repair")}):re.cwd,xe?` · ${m("service-active")}`:` · ${m("cell-off")}`]})]}),c.jsxs("span",{children:[xe&&Oe.includes("down")&&c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>_e(()=>Il(e,{cell:re.id},ie)),children:m("stop")}),!xe&&Oe.includes("up")&&c.jsx("button",{className:"nc-btn primary",disabled:Ye||k,onClick:()=>Pe({cell:re.id,id:re.id,engine:re.engine,model:re.model,models:re.models,permissionPolicies:re.permissionPolicies,active:!1,boot:re.boot}),children:m("start")}),xe&&Oe.includes("restart")&&c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>_e(()=>Fo(e,re.id,ie)),children:m("restart")}),Fe?c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>{E(""),T(re)},children:m("fleet-cwd-repair")}):c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>{var de;E(""),w({mode:"edit",original:re,form:{...re,commands:{...re.commands||{}},command:((de=re.commands)==null?void 0:de[re.engine])||""}})},children:m("edit")}),c.jsx("button",{className:"nc-btn danger",disabled:Ye||k,onClick:()=>_e(async()=>{window.confirm(m("fleet-remove-cell").replace("{id}",re.id))&&await J_(e,re.id,!0,ie)}),children:"×"})]})]},re.id)}),c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:m("fleet-engines")}),c.jsxs("button",{className:"nc-btn primary",disabled:Ye||k,onClick:()=>{E(""),y({mode:"new",form:Y2()})},children:["+ ",m("add")]})]}),a.engines.map(re=>{var Oe,Fe;const xe=Ae(re);return c.jsxs("div",{className:"nc-fleet-item",children:[c.jsxs("span",{children:[c.jsx("b",{children:re.label}),c.jsx("small",{children:re.managed?`${re.id} · ${re.managed.client} / ${re.managed.provider} · ${(Oe=re.managedInfo)!=null&&Oe.configured?m("fleet-ready"):((Fe=re.managedInfo)==null?void 0:Fe.reason)||m("fleet-not-ready")}`:`${re.id} · ${re.command}`}),xe&&c.jsxs("small",{children:[xe.envKey," · ",m(`fleet-credential-source-${xe.source||"missing"}`)]})]}),c.jsxs("span",{children:[xe&&c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>{E(""),D({envKey:xe.envKey,value:""})},children:xe.configured?m("fleet-credential-change"):m("fleet-credential-set")}),(xe==null?void 0:xe.source)==="local"&&c.jsx("button",{className:"nc-btn danger",disabled:Ye||k,onClick:()=>ut(xe),children:m("fleet-credential-forget")}),c.jsx("button",{className:"nc-btn ghost",disabled:Ye||k,onClick:()=>{E(""),y({mode:"edit",original:re,form:X2(re)})},children:m("edit")}),c.jsx("button",{className:"nc-btn danger",disabled:Ye||k,onClick:()=>_e(async()=>{window.confirm(m("fleet-remove-engine").replace("{id}",re.id))&&await v0(e,re.id,ie)}),children:"×"})]})]},re.id)})]}),S&&c.jsx($s,{onClose:()=>y(null),label:m("fleet-new-engine"),error:R,children:c.jsx(Z2,{state:S,setState:y,busy:k,onSave:He,catalog:a.managedCatalog||[]})}),b&&c.jsx($s,{onClose:()=>w(null),label:m("fleet-new-cell"),error:R,children:c.jsx(ej,{token:e,route:ie,targets:n,location:K,setLocation:Y,state:b,setState:w,engines:a.engines,busy:k,onSave:ht})}),N&&c.jsx($s,{onClose:()=>T(null),label:m("fleet-cwd-repair-title").replace("{id}",N.id),error:"",children:c.jsx(nj,{token:e,route:ie,cell:N,busy:k,onSaved:async()=>{T(null),O(m("fleet-cwd-repaired")),await le()},onClose:()=>T(null)})}),F&&c.jsx("div",{className:"nc-set-note",children:F}),R&&c.jsx("div",{className:"nc-err",children:R}),L&&c.jsx($s,{onClose:()=>$(!1),label:m("fleet-backup"),error:R,children:c.jsx(J2,{cells:a.cells,engines:a.engines,busy:k,canRestore:pe,onRestore:vt,onClose:()=>$(!1)})}),I&&c.jsx($s,{onClose:()=>D(null),label:m("fleet-credential-title"),error:R,children:c.jsxs("div",{className:"nc-fleet-form nc-credential-form",children:[c.jsx("b",{children:m("fleet-credential-title")}),c.jsx("small",{children:m("fleet-credential-help")}),c.jsxs("label",{className:"nc-field",children:[I.envKey,c.jsx("input",{type:"password",autoComplete:"off",spellCheck:"false",autoCapitalize:"none",value:I.value,onChange:re=>D({...I,value:re.target.value})})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>D(null),children:m("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:k||!I.value,onClick:Ne,children:m("save")})]})]})}),B&&c.jsx($s,{onClose:()=>M(null),label:m("import-as-cell"),error:R,children:c.jsx(I_,{token:e,route:B.route||ie,state:B,setState:M,busy:k,onSave:Dt})}),G&&c.jsx(Bl,{cell:G,token:e,route:Array.isArray(G.route)?G.route:ie,onConfirm:async re=>{try{await Ut(re)}finally{await le()}},onClose:()=>Q(null)})]}):c.jsxs("div",{className:"nc-set-tab",children:[dt,A==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:m("fleet-overview-help")}),c.jsx(zd,{token:e,targets:n,readonly:t,onPower:Pe,onImport:Rt})]}):c.jsxs(c.Fragment,{children:[ft,c.jsxs("div",{className:"nc-set-info",children:[m(R?"fleet-editor-load-error":"fleet-editor-unavailable"),!R&&h.reason?` ${h.reason}`:""]})]}),R&&c.jsx("div",{className:"nc-err",children:R}),B&&c.jsx($s,{onClose:()=>M(null),label:m("import-as-cell"),error:R,children:c.jsx(I_,{token:e,route:B.route||ie,state:B,setState:M,busy:k,onSave:Dt})}),G&&c.jsx(Bl,{cell:G,token:e,route:Array.isArray(G.route)?G.route:ie,onConfirm:async re=>{try{await Ut(re)}finally{await le()}},onClose:()=>Q(null)})]}):c.jsxs("div",{className:"nc-set-tab",children:[dt,A==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:m("fleet-overview-help")}),c.jsx(zd,{token:e,targets:n,readonly:t,onPower:Pe,onImport:Rt})]}):c.jsxs(c.Fragment,{children:[ft,c.jsx("div",{className:"nc-set-info",children:m("fleet-editor-loading")})]})]})}const Of="nc_notification_speech_v1",Nl="nc-notification-speech",Fh="nc-notification-speech-preview",sj=320,rj=2,oj=3e4,aj=1e4,lj=6e4,cj=64,O_=Object.freeze({enabled:!1}),uj=Object.freeze({it:"it-IT",en:"en-US",es:"es-ES"}),dj=Object.freeze({it:new Set(["che","con","correzione","della","delle","difetto","il","lavoro","nessun","non","per","pronto","pubblicata","pubblicato","rilascio","sono","stato","tutti","verificata","verificato","versione","completata","completato","riepilogo"]),en:new Set(["all","and","completed","fix","for","issue","no","published","ready","release","summary","the","verified","version","with","work"]),es:new Set(["con","correccion","defecto","el","esta","para","publicada","publicado","resumen","sin","todos","trabajo","verificada","verificado","version","y"])}),Bf=new WeakSet;function zf(e){return{enabled:(e&&typeof e=="object"&&!Array.isArray(e)?e:{}).enabled===!0}}function rb(){try{return typeof localStorage<"u"?localStorage:null}catch{return null}}function B_(e=rb()){if(!e)return{...O_};try{return zf(JSON.parse(e.getItem(Of)||"null"))}catch{return{...O_}}}function hj(e,t=rb()){const n=zf(e);if(t)try{t.setItem(Of,JSON.stringify(n))}catch{}return n}function Ur(){try{return typeof window<"u"?window:null}catch{return null}}function ea(e=Ur()){try{return!!(e&&e.speechSynthesis&&typeof e.speechSynthesis.speak=="function"&&typeof e.SpeechSynthesisUtterance=="function")}catch{return!1}}function ob(e=Ur()){return!!(e&&(typeof e=="object"||typeof e=="function")&&Bf.has(e))}function $h(e=Ur()){e&&(typeof e=="object"||typeof e=="function")&&Bf.delete(e)}function Ar(e){const t=String(e||"").trim().toLowerCase().split("-")[0];return uj[t]||""}function cc(e){return String(e||"").normalize("NFC").replace(/[\p{Cc}\p{Cf}]+/gu," ").replace(/\s+/g," ").trim()}function fj(e){const t=cc(e).normalize("NFD").replace(new RegExp("\\p{M}+","gu"),"").toLowerCase(),n=new Set(t.match(new RegExp("\\p{L}+","gu"))||[]);if(n.size<4)return"";const s=Object.entries(dj).map(([o,a])=>({lang:o,score:[...n].reduce((u,h)=>u+(a.has(h)?1:0),0)})).sort((o,a)=>a.score-o.score);return s[0].score>=3&&s[0].score-s[1].score>=2?s[0].lang:""}function pj(e,t){const n=Ar(e&&e.lang);return n||Ar(fj(e&&e.body))||Ar(t)}function ab(e){try{const t=e&&e.speechSynthesis&&e.speechSynthesis.getVoices;return typeof t=="function"?Array.from(t.call(e.speechSynthesis)||[]).filter(Boolean):[]}catch{return[]}}function lb(e,t){const n=Ar(t);if(!n)return null;const s=Array.from(e||[]).filter(h=>h&&typeof h.lang=="string"&&h.lang.trim()),o=s.filter(h=>h.lang.trim().toLowerCase()===n.toLowerCase()),a=n.slice(0,2).toLowerCase(),u=s.filter(h=>h.lang.trim().toLowerCase().split("-")[0]===a);return o.find(h=>h.default===!0)||o[0]||u.find(h=>h.default===!0)||u[0]||null}function z_(e){return cc(e).replace(/\bBearer\s+\S+/gi,"Bearer [redacted]").replace(/\b(?:sk|hf|zai|xox[baprs]|gh[pousr]|npm)[_-][A-Za-z0-9._-]{8,}\b/gi,"[redacted]").replace(/\b(?:token|password|secret|credential|api[_-]?key)\s*[=:]\s*(?:"[^"]*"|'[^']*'|\S+)/gi,"[redacted]").replace(/\b[A-Z][A-Z0-9_]{2,}\s*=\s*(?:"[^"]*"|'[^']*'|\S+)/g,"[redacted]").replace(/(?:\/home\/[^/\s]+|\/Users\/[^/\s]+|\/data\/(?:data|user\/\d+)\/[^/\s]+)(?:\/[^\s]*)?/g,"[private path]").replace(/\b[A-Za-z]:\\Users\\[^\\\s]+(?:\\[^\s]*)?/g,"[private path]")}function mj(e,t=sj){const n=z_(e&&e.title),s=z_(e&&e.body),o=[n,s&&s!==n?s:""].filter(Boolean).join(". "),a=Array.from(o);return a.length<=t?o:`${a.slice(0,Math.max(0,t-1)).join("").trimEnd()}…`}function gj(e){try{return!!(e&&e.visibilityState==="visible"&&typeof e.hasFocus=="function"&&e.hasFocus())}catch{return!1}}function cb(e=Ur()){if(!ea(e))return!1;try{return e.speechSynthesis.cancel(),!0}catch{return!1}}function vj(e){try{e&&typeof e.dispatchEvent=="function"&&typeof e.Event=="function"&&e.dispatchEvent(new e.Event(Fh))}catch{}}function _j(e,t,n=Ur(),s={}){var f;if($h(n),!ea(n))return Promise.resolve(!1);const o=cc(e);if(!o||(f=s.signal)!=null&&f.aborted)return Promise.resolve(!1);const a=Number.isFinite(s.timeoutMs)&&s.timeoutMs>0?s.timeoutMs:aj,u=s.setTimer||((v,S)=>setTimeout(v,S)),h=s.clearTimer||(v=>clearTimeout(v)),p=s.signal;return vj(n),new Promise(v=>{let S=!1,y=!1,b=null,w=null;const k=C=>{if(!S){if(S=!0,p&&w&&p.removeEventListener("abort",w),b!==null&&h(b),C)Bf.add(n);else try{n.speechSynthesis.cancel()}catch{}v(C)}};w=()=>k(!1),p&&p.addEventListener("abort",w,{once:!0});try{n.speechSynthesis.cancel();const C=new n.SpeechSynthesisUtterance(o);C.lang=Ar(t);const R=lb(ab(n),C.lang);R&&(C.voice=R),C.onstart=()=>{y=!0},C.onend=()=>k(y),C.onerror=()=>k(!1),b=u(()=>k(!1),a),n.speechSynthesis.speak(C)}catch{k(!1)}})}function yj(e={}){const t=e.scope||Ur(),n=e.document||t&&t.document,s=Number.isInteger(e.maxPending)&&e.maxPending>=0?e.maxPending:rj,o=Number.isFinite(e.watchdogMs)&&e.watchdogMs>0?e.watchdogMs:oj,a=Number.isFinite(e.dedupWindowMs)&&e.dedupWindowMs>0?e.dedupWindowMs:lj,u=Number.isInteger(e.maxDedup)&&e.maxDedup>0?e.maxDedup:cj,h=e.now||Date.now,p=e.setTimer||((B,M)=>setTimeout(B,M)),f=e.clearTimer||(B=>clearTimeout(B)),v=e.isPrimed||(()=>ob(t)),S=t&&t.speechSynthesis;let y=null,b=null,w=[],k=[],C=()=>{};const R=new Map,E=()=>{k=ab(t)};if(E(),S&&typeof S.addEventListener=="function")S.addEventListener("voiceschanged",E),C=()=>S.removeEventListener("voiceschanged",E);else if(S&&"onvoiceschanged"in S){const B=S.onvoiceschanged,M=(...N)=>{E(),typeof B=="function"&&B.apply(S,N)};S.onvoiceschanged=M,C=()=>{S.onvoiceschanged===M&&(S.onvoiceschanged=B)}}const F=B=>{if(B&&typeof B.id=="string"&&B.id)return`id:${B.id}`;const M=Number(B&&B.ts),N=cc(B&&B.title);return Number.isFinite(M)&&N?`frame:${M}:${N}`:""},O=B=>{if(!B)return!0;const M=h();for(const[N,T]of R)M-T>=a&&R.delete(N);if(R.has(B))return!1;for(R.set(B,M);R.size>u;)R.delete(R.keys().next().value);return!0},K=()=>{b!==null&&f(b),b=null},Y=()=>{K(),y&&(y.onend=null,y.onerror=null,y=null),cb(t)},z=B=>{if(!ea(t))return w=[],"unsupported";let M;try{M=new t.SpeechSynthesisUtterance(B.text),M.lang=Ar(B.lang);const T=lb(k,M.lang);T&&(M.voice=T)}catch{return w=[],"error"}y=M;const N=()=>{if(y!==M)return;K(),y=null,M.onend=null,M.onerror=null;const T=w.shift();T&&z(T)};M.onend=N,M.onerror=N;try{return b=p(()=>{if(y!==M)return;y=null,M.onend=null,M.onerror=null;try{t.speechSynthesis.cancel()}catch{}K();const T=w.shift();T&&z(T)},o),t.speechSynthesis.speak(M),"speaking"}catch{return N(),"error"}},Z=(B,M)=>{if(!ea(t))return"unsupported";const N=mj(B);if(!N)return"empty";if(!O(F(B)))return"duplicate";if(!gj(n))return"inactive";if(!v())return"unprimed";const T={text:N,lang:M};return B&&B.urgency==="high"?(w=[],Y(),z(T)):y?s===0?"dropped":(w.length>=s&&w.shift(),w.push(T),"queued"):z(T)},G=()=>{w=[],Y()};return{enqueue:Z,stop:G,dispose:()=>{G(),C(),C=()=>{}}}}function ub(){const[e,t]=j.useState(B_);j.useEffect(()=>{const s=o=>{if((o==null?void 0:o.type)==="storage"&&o.key!==Of&&o.key!==null)return;const a=(o==null?void 0:o.type)===Nl?zf(o.detail):B_();t(a)};return window.addEventListener(Nl,s),window.addEventListener("storage",s),()=>{window.removeEventListener(Nl,s),window.removeEventListener("storage",s)}},[]);const n=j.useCallback(s=>{const o=hj({enabled:s===!0});return t(o),window.dispatchEvent(new CustomEvent(Nl,{detail:o})),o.enabled},[]);return[e.enabled,n]}function wj({text:e}){const[t,n]=j.useState(!1),s=async()=>{try{await navigator.clipboard.writeText(e),n(!0),setTimeout(()=>n(!1),1500)}catch{}};return c.jsxs("div",{className:"nc-set-copyline",children:[c.jsx("code",{children:e}),c.jsxs("button",{type:"button",className:"nc-btn ghost",onClick:s,children:[c.jsx(Ze,{name:"copy",size:14})," ",m(t?"copied":"copy")]})]})}function bj({value:e}){const[t,n]=j.useState("");return j.useEffect(()=>{let s=!0;return t2.toDataURL(e,{margin:1,width:220}).then(o=>{s&&n(o)}).catch(()=>{}),()=>{s=!1}},[e]),t?c.jsx("img",{src:t,width:"220",height:"220",alt:"NexusCrew pairing QR"}):null}function Sj({token:e,nodes:t,roster:n,settings:s,readonly:o,refresh:a,refreshAliases:u,vlUnavailable:h=[]}){const[p,f]=j.useState(null),[v,S]=j.useState(null),[y,b]=j.useState(null),[w,k]=j.useState({ssh:"",sshPort:"",name:""}),[C,R]=j.useState(""),[E,F]=j.useState(""),[O,K]=j.useState(!1),[Y,z]=j.useState(null),Z=Date.now(),G=s&&s.deviceName||"",Q=(t||[]).filter(x=>x&&x.direction==="outbound"&&x.name&&x.ssh),B=Q.find(x=>x.name===C)||Q[0]||null,M=B?Yd(B.tunnel,Z):null,N=B!=null&&B.shared?M!=null&&M.up?"share-local-active":"share-local-pending":M!=null&&M.up?"share-local-private":"share-local-private-down",T=[{key:"peer-group-hubs",rows:(t||[]).filter(x=>x.kind!=="transitive"&&x.kind!=="vl"&&x.relation!=="client")},{key:"peer-group-clients",rows:(t||[]).filter(x=>x.kind!=="transitive"&&x.kind!=="vl"&&x.relation==="client")},{key:"peer-group-routed",rows:(t||[]).filter(x=>x.kind==="transitive")},{key:"peer-group-vl",rows:(t||[]).filter(x=>x.kind==="vl")}],L=Y&&(t||[]).find(x=>`${x.kind||"direct"}:${x.nodeId||x.name}`===Y)||null,$=async x=>{if(B){f(null),S(`${B.name}:share`);try{const I=await A0(e,B.name,x);await a(),I&&I.reversePoolPending===!0&&f(`${B.name}: ${m("share-reverse-pool-pending")}`)}catch(I){const D=I!=null&&I.data&&I.data.shared===!1?I.data:null;if(D)try{await a()}catch{}const A=I!=null&&I.data&&typeof I.data.hint=="string"?I.data.hint:"",W=(D==null?void 0:D.revoked)===!0&&(D==null?void 0:D.localReconcilePending)===!0?m("share-off-local-reconcile-pending"):(D==null?void 0:D.revoked)===!1&&(D==null?void 0:D.reconcilePending)===!0?m("share-off-hub-reconcile-pending"):"",X=I!=null&&I.data&&I.data.reversePoolPending===!0?m("share-reverse-pool-pending"):"",ie=I!=null&&I.data&&typeof I.data.code=="string"?I.data.code:"",le=I!=null&&I.data&&typeof I.data.detail=="string"?I.data.detail.trim():"",fe=le&&le!==String(I.message||"")?le:"";f(`${B.name}: ${String(I.message||I)}${ie?` [${ie}]`:""}${fe?` — ${fe}`:""}${A?` — ${A}`:""}${W?` — ${W}`:""}${X?` — ${X}`:""}`)}S(null)}},U=async()=>{f(null);const x=vs(w.name||E||G||"NexusCrew"),I=Mx({name:x,ssh:w.ssh,sshPort:w.sshPort});if(!I.ok){f(m(I.error));return}S("invite");try{b(await N0(e,{...E||G?{label:E||G}:{},name:x,ssh:I.value.ssh,...I.value.sshPort?{sshPort:I.value.sshPort}:{}}))}catch(D){f(String(D.message||D))}S(null)};return c.jsxs("div",{className:"nc-set-tab",children:[c.jsx("div",{className:"nc-sheet-label",children:m("peer-inventory-title")}),(t||[]).length===0&&c.jsx("div",{className:"nc-empty",children:m("no-nodes")}),T.map(x=>x.rows.length>0&&c.jsxs("div",{className:"nc-peer-group",children:[c.jsxs("div",{className:"nc-peer-group-title",children:[m(x.key)," ",c.jsx("span",{children:x.rows.length})]}),x.rows.map(I=>{const D=f2(I);if(!D)return null;const A=`${I.kind||"direct"}:${I.nodeId||I.name}`,W=D.routed?{up:D.reach.up,since:null}:Yd(I.tunnel,Z);return c.jsxs("button",{type:"button",className:`nc-set-node nc-node-row${D.routed?" routed":""}`,"aria-label":`${D.title} — ${m("node-detail-open")}`,onClick:()=>z(A),children:[c.jsx("span",{className:`nc-dot ${D.reach.up?"on":""}`}),c.jsxs("span",{className:"nc-node-row-text",children:[c.jsx("b",{children:D.title}),c.jsxs("small",{children:[D.routed?D.routeLabel:`${D.subtitle} · ${m(D.exposure.shortKey)}`,!D.routed&&D.exposure.shortKey==="visibility-selected"?` (${D.exposure.count})`:""]})]}),c.jsxs("span",{className:`nc-set-tunnel${D.reach.up?" up":""}`,children:[m(D.reach.key),W.since?` · ${W.since}`:""]}),c.jsx("span",{className:"nc-node-row-more",children:c.jsx(Ze,{name:"chevronDown",size:14})})]},A)})]},x.key)),h.length>0&&c.jsxs("div",{className:"nc-err",children:[m("vl-owners-unavailable"),": ",h.map(x=>x.label||x.route.join("/")).join(", ")]}),L&&c.jsx(E2,{node:L,nodes:t||[],token:e,readonly:o,refresh:a,onClose:()=>z(null)}),(n||[]).some(x=>!x.direct&&x.instanceId)&&c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:m("routed-node-aliases")}),c.jsx("small",{className:"nc-set-hint",children:m("routed-node-aliases-help")}),(n||[]).filter(x=>!x.direct&&x.instanceId).map(x=>c.jsxs("div",{className:"nc-set-node",children:[c.jsxs("div",{className:"nc-set-node-head",children:[c.jsx("span",{className:`nc-dot ${x.status==="up"?"on":""}`}),c.jsx("b",{children:x.label||x.name}),c.jsx("small",{children:(x.route||[]).join(" › ")})]}),c.jsxs("div",{className:"nc-set-node-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!v,onClick:async()=>{const I=window.prompt(m("node-local-alias"),x.alias||"");if(I!=null){if(I.trim()&&!Mr(I.normalize("NFC"))){f(m("err-label"));return}S(`${x.instanceId}:alias`),f(null);try{I.trim()?await R0(e,x.instanceId,I):await Sg(e,x.instanceId),u()}catch(D){f(String(D.message||D))}S(null)}},children:m("alias-on-device")}),x.alias&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!v,onClick:async()=>{S(`${x.instanceId}:alias-reset`),f(null);try{await Sg(e,x.instanceId),u()}catch(I){f(String(I.message||I))}S(null)},children:m("reset-alias")})]})]},x.instanceId))]}),B&&c.jsxs("div",{className:"nc-set-form nc-local-share",children:[c.jsx("div",{className:"nc-sheet-label",children:m("share-local-heading")}),Q.length>1&&c.jsxs("label",{className:"nc-field",children:[m("share-local-hub"),c.jsx("select",{value:B.name,disabled:o||!!v,onChange:x=>R(x.target.value),children:Q.map(x=>c.jsx("option",{value:x.name,children:x.label||x.name},x.name))})]}),c.jsxs("label",{className:"nc-check nc-node-share",children:[c.jsx("input",{type:"checkbox",checked:B.shared===!0,disabled:o||!!v,onChange:x=>$(x.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:m("share-local-through").replace("{device}",G||m("local")).replace("{hub}",B.label||B.name)}),c.jsx("small",{children:m(B.shared?"share-node-on-desc":"share-node-off-desc")})]})]}),c.jsx("div",{className:`nc-set-test${B.shared&&(M!=null&&M.up)?" ok":M!=null&&M.up?"":" ko"}`,children:m(N)}),!(M!=null&&M.up)&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!v,onClick:()=>$(B.shared===!0),children:m("share-local-reconnect")})]}),c.jsx(Yw,{token:e,deviceDefault:G,localNodeId:s&&s.nodeId||"",localNameDefault:s&&s.localName||"",readonly:o,onSuccess:a}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:m("invite-node")}),c.jsx("small",{className:"nc-set-hint",children:m("invite-v2-hint")}),c.jsxs("div",{className:"nc-set-info nc-invite-endpoint",children:[m("invite-target-local"),": ",c.jsx("b",{children:G||m("invite-target-this")})]}),c.jsxs("label",{className:"nc-field",children:[m("invite-endpoint-label"),c.jsx("input",{placeholder:"user@host",value:w.ssh,disabled:o,onChange:x=>k({...w,ssh:x.target.value})}),c.jsx("small",{className:"nc-set-hint",children:m("invite-endpoint-needed")})]}),Q.length>0&&c.jsx("small",{className:"nc-set-hint nc-invite-hub-hint",children:m("invite-hub-only")}),O&&c.jsxs("div",{className:"nc-invite-advanced",children:[c.jsxs("label",{className:"nc-field",children:[m("node-ssh-port-label"),c.jsx("input",{inputMode:"numeric",placeholder:"22",value:w.sshPort,disabled:o,onChange:x=>k({...w,sshPort:x.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),c.jsxs("label",{className:"nc-field",children:[m("device-name-label"),c.jsx("input",{placeholder:G||"NexusCrew",value:E,disabled:o,onChange:x=>F(x.target.value)}),c.jsx("small",{className:"nc-set-hint",children:m("device-name-hint")})]}),c.jsxs("label",{className:"nc-field",children:[m("node-name-label"),c.jsx("input",{placeholder:vs(E||G||m("node-name-ph")),value:w.name,disabled:o,onChange:x=>k({...w,name:x.target.value?vs(x.target.value):""})})]})]}),c.jsxs("div",{className:"nc-set-row nc-invite-actions",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:o||!!v||!w.ssh.trim(),onClick:U,children:m("create-pairing-link")}),c.jsxs("button",{type:"button",className:"nc-btn ghost",disabled:!!v,onClick:()=>K(x=>!x),children:[O?"▾":"▸"," ",m("pair-advanced")]})]}),y&&c.jsxs(c.Fragment,{children:[c.jsx(bj,{value:y.pairingUrl}),c.jsx(wj,{text:y.pairingUrl}),c.jsx("div",{className:"nc-set-info",children:m("invite-next-steps")})]})]}),p&&c.jsx("div",{className:"nc-err",children:p})]})}function xj({token:e,readonly:t}){const[n,s]=j.useState("idle"),[o,a]=j.useState(!1),[u,h]=j.useState(null);j.useEffect(()=>{let f=!1;return j2().then(v=>{f||s(v)}),()=>{f=!0}},[]);const p=async()=>{h(null),a(!0);try{n==="subscribed"?(await P2(e),s("idle")):(await R2(e),s("subscribed"))}catch(f){const v=String(f.message||f);v==="push-denied"?(s("denied"),h(m("push-denied"))):v==="push-unsupported"?(s("unsupported"),h(m("push-unsupported"))):h(v)}a(!1)};return n==="unsupported"?c.jsx("div",{className:"nc-set-info",children:m("push-unsupported")}):c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||o||n==="denied",title:t?m("settings-readonly"):"",onClick:p,children:m(n==="subscribed"?"push-disable":"push-enable")}),c.jsx("span",{className:"nc-set-info",children:m(n==="subscribed"?"push-on":n==="denied"?"push-denied":"push-off")})]}),u&&c.jsx("div",{className:"nc-err",children:u})]})}function kj(){const[e,t]=ub(),[n,s]=j.useState(""),[o,a]=j.useState(""),[u,h]=j.useState(!1),[p,f]=j.useState(ob),v=j.useRef(0),S=j.useRef(!1),y=j.useRef(null),b=ea();j.useEffect(()=>()=>{var C;v.current+=1,S.current&&((C=y.current)==null||C.abort(),y.current=null,S.current=!1,$h())},[]);const w=async()=>{const C=v.current+=1,R=typeof AbortController=="function"?new AbortController:null;s(""),a(""),h(!0),f(!1),S.current=!0,y.current=R;const E=await _j(m("notification-speech-preview"),Ol(),void 0,R?{signal:R.signal}:{});return C!==v.current||(y.current=null,S.current=!1,h(!1),f(E),E?a(m("notification-speech-preview-ok")):s(m("notification-speech-preview-failed"))),E},k=C=>{var R;if(t(C),s(""),a(""),C)w();else{v.current+=1;const E=S.current;(R=y.current)==null||R.abort(),y.current=null,S.current=!1,h(!1),f(!1),$h(),E||cb()}};return c.jsxs("div",{className:"nc-set-form",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox","aria-label":m("notification-speech-enable"),checked:e,disabled:!b,onChange:C=>k(C.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:m("notification-speech-enable")}),c.jsx("small",{children:m("notification-speech-help")})]})]}),b?c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!e||u,onClick:()=>{w()},children:m(u?"notification-speech-testing":"notification-speech-test")})}):c.jsx("div",{className:"nc-set-info",children:m("notification-speech-unsupported")}),e&&!p&&!u&&!n&&c.jsx("div",{className:"nc-set-info",children:m("notification-speech-needs-test")}),o&&c.jsx("div",{className:"nc-set-info",role:"status",children:o}),n&&c.jsx("div",{className:"nc-err",children:n})]})}function Cj({token:e,readonly:t,nodes:n=[],settings:s=null}){const[o,a]=j.useState(null),[u,h]=j.useState([]),[p,f]=j.useState({name:"",mode:"primary-failover",targets:[]}),[v,S]=j.useState(null),[y,b]=j.useState(!1),[w,k]=j.useState(null),[C,R]=j.useState(""),E=j.useCallback(async()=>{try{const[U,x]=await Promise.all([D0(e),B0(e)]);a(U),h(Array.isArray(x==null?void 0:x.groups)?x.groups:[]),R("")}catch(U){R(String(U.message||U))}},[e]);j.useEffect(()=>{E()},[E]);const F=async U=>{b(!0),k(null),R("");try{a(await M0(e,U))}catch(x){R(String(x.message||x))}b(!1)},O=async()=>{b(!0),k(null),R("");try{k({kind:"test",...await I0(e)})}catch(U){R(String(U.message||U))}b(!1)},K=async()=>{b(!0),k(null),R("");try{k({kind:"stop",...await O0(e)})}catch(U){R(String(U.message||U))}b(!1)},Y=[],z=new Set,Z=(U,x)=>{const I=typeof U=="string"?U.toLowerCase():"";!/^[a-f0-9]{32}$/.test(I)||z.has(I)||(z.add(I),Y.push({id:I,label:x||I}))};Z(s==null?void 0:s.nodeId,m("audio-group-local"));for(const U of n||[])Z((U==null?void 0:U.nodeId)||(U==null?void 0:U.instanceId),(U==null?void 0:U.label)||(U==null?void 0:U.name)||(U==null?void 0:U.nodeId));for(const U of p.targets)Z(U,`${U} (${m("audio-group-unavailable-target")})`);const G=(U=null)=>{R(""),U?(S(U.name),f({name:U.name,mode:U.mode,targets:[...U.targets||[]]})):(S(null),f({name:"",mode:"primary-failover",targets:[]}))},Q=(U,x)=>f(I=>({...I,targets:x?[...new Set([...I.targets,U])]:I.targets.filter(D=>D!==U)})),B=async()=>{const U=p.name.trim().toLowerCase();if(!/^[a-z0-9][a-z0-9-]{0,31}$/.test(U)||p.targets.length<1){R(m("audio-group-invalid"));return}b(!0),R("");try{const x=await z0(e,U,{targets:p.targets,mode:p.mode}),I=x==null?void 0:x.group;if(!I||!I.name)throw new Error(m("audio-group-save-failed"));h(D=>[...D.filter(A=>A.name!==I.name),I].sort((A,W)=>A.name.localeCompare(W.name))),G(null)}catch(x){R(String(x.message||x))}b(!1)},M=async U=>{b(!0),R("");try{await F0(e,U),h(x=>x.filter(I=>I.name!==U)),v===U&&G(null)}catch(x){R(String(x.message||x))}b(!1)},N=(o==null?void 0:o.installed)===!0,T=(o==null?void 0:o.liveness)==="ready",L=(o==null?void 0:o.consent)===!0&&N&&T,$=w?w.kind==="stop"?m(w.stopped?"audio-stop-stopped":"audio-stop-idle"):w.status==="accepted"||w.status==="spoken"?m("audio-test-accepted"):w.status==="unknown"?m("audio-test-unknown"):`${m("audio-test-refused")}${w.reason?` (${w.reason})`:""}`:"";return c.jsxs("div",{className:"nc-set-tab nc-audio-settings",children:[c.jsxs("div",{className:"nc-set-info",children:[c.jsx("b",{children:m("audio-title")}),c.jsx("br",{}),m("audio-help")]}),o?c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox","aria-label":m("audio-consent"),checked:o.consent===!0,disabled:t||y,onChange:U=>{F(U.target.checked)}}),c.jsxs("span",{children:[c.jsx("b",{children:m("audio-consent")}),c.jsx("small",{children:m("audio-consent-help")})]})]}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:m("audio-capability")}),c.jsxs("div",{className:"nc-set-info",children:[m("audio-adapter"),": ",o.adapter||m("audio-adapter-none"),c.jsx("br",{}),m("audio-installed"),": ",m(N?"audio-installed-yes":"audio-installed-no"),c.jsx("br",{}),m("audio-liveness"),": ",m(T?"audio-liveness-ready":"audio-liveness-unavailable"),Array.isArray(o.languages)&&o.languages.length>0&&c.jsxs(c.Fragment,{children:[c.jsx("br",{}),m("audio-languages"),": ",o.languages.join(", ")]})]}),o.limits&&c.jsxs("small",{className:"nc-set-hint",children:[m("audio-limits"),": ",o.limits]})]}),!o.consent&&c.jsx("div",{className:"nc-set-info",children:m("audio-test-needs-consent")}),!N&&c.jsx("div",{className:"nc-set-info",children:m("audio-unavailable")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:t||y||!L,title:t?m("settings-readonly"):"",onClick:()=>{O()},children:m(y?"audio-testing":"audio-test")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:y,onClick:()=>{K()},children:m(y?"audio-stopping":"audio-stop")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:y,onClick:()=>{E()},children:m("refresh")})]}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:m("audio-groups-title")}),c.jsx("small",{className:"nc-set-hint",children:m("audio-groups-help")}),u.length===0&&c.jsx("div",{className:"nc-set-info",children:m("audio-groups-empty")}),u.map(U=>{var x;return c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("span",{children:[c.jsx("b",{children:U.name})," ",c.jsxs("small",{children:["· ",U.mode==="fanout"?m("audio-group-fanout"):m("audio-group-primary")," · ",((x=U.targets)==null?void 0:x.length)||0]})]}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||y,onClick:()=>G(U),children:m("audio-group-edit")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||y,onClick:()=>{M(U.name)},children:m("audio-group-delete")})]},U.name)}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||y,onClick:()=>G(null),children:m("audio-group-new")})}),c.jsxs("label",{className:"nc-field",children:[m("audio-group-name"),c.jsx("input",{"aria-label":m("audio-group-name"),value:p.name,maxLength:"32",disabled:t||y,onChange:U=>f(x=>({...x,name:U.target.value}))})]}),c.jsxs("label",{className:"nc-field",children:[m("audio-group-mode"),c.jsxs("select",{"aria-label":m("audio-group-mode"),value:p.mode,disabled:t||y,onChange:U=>f(x=>({...x,mode:U.target.value})),children:[c.jsx("option",{value:"primary-failover",children:m("audio-group-primary")}),c.jsx("option",{value:"fanout",children:m("audio-group-fanout")})]})]}),c.jsx("div",{className:"nc-set-info",children:m("audio-group-targets")}),Y.length===0?c.jsx("small",{className:"nc-set-hint",children:m("audio-group-no-targets")}):Y.map(U=>c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:p.targets.includes(U.id),disabled:t||y,onChange:x=>Q(U.id,x.target.checked)}),c.jsxs("span",{children:[U.label,c.jsx("small",{children:U.id})]})]},U.id)),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:t||y,onClick:()=>{B()},children:m("audio-group-save")}),v&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:y,onClick:()=>G(null),children:m("audio-group-cancel")})]})]})]}):c.jsx("div",{className:"nc-set-info",children:m("audio-loading")}),$&&c.jsx("div",{className:"nc-set-note",role:"status",children:$}),C&&c.jsx("div",{className:"nc-err",children:C})]})}function Ej(){const[e,t]=kf();return c.jsxs("div",{className:"nc-set-tab nc-input-settings",children:[c.jsx("div",{className:"nc-set-info",children:m("input-settings-local")}),c.jsxs("div",{className:"nc-set-form",children:[c.jsxs("label",{className:"nc-field",children:[m("terminal-keyboard-gesture"),c.jsx("select",{"aria-label":m("terminal-keyboard-gesture"),value:e.terminalKeyboardGesture,onChange:n=>t({terminalKeyboardGesture:n.target.value}),children:Ew.map(n=>c.jsx("option",{value:n,children:m(`terminal-keyboard-${n}`)},n))}),c.jsx("small",{children:m("terminal-keyboard-gesture-help")})]}),c.jsxs("label",{className:"nc-field",children:[m("keybar-layout"),c.jsx("select",{"aria-label":m("keybar-layout"),value:e.keybarLayout,onChange:n=>t({keybarLayout:n.target.value}),children:Nw.map(n=>c.jsx("option",{value:n,children:m(`keybar-layout-${n}`)},n))}),c.jsx("small",{children:m("keybar-layout-help")})]})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:e.keybarKeepsKeyboardClosed,onChange:n=>t({keybarKeepsKeyboardClosed:n.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:m("keybar-ime-lock")}),c.jsx("small",{children:m("keybar-ime-lock-help")})]})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:e.voiceKeepsKeyboardClosed,onChange:n=>t({voiceKeepsKeyboardClosed:n.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:m("voice-ime-lock")}),c.jsx("small",{children:m("voice-ime-lock-help")})]})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:e.showKeybarEnter,onChange:n=>t({showKeybarEnter:n.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:m("keybar-enter-show")}),c.jsx("small",{children:m("keybar-enter-show-help")})]})]}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>t(ps),children:m("input-settings-reset")})})]})}function Nj({token:e,roster:t=[],readonly:n}){const s=[{key:"local",route:[],label:m("diagnostics-local"),status:"up"},...t.filter(L=>Array.isArray(L.route)&&L.route.length).map(L=>({key:L.route.join("/"),route:L.route,label:L.label||L.name,status:L.status}))],[o,a]=j.useState("local"),u=s.find(L=>L.key===o)||s[0],[h,p]=j.useState(null),[f,v]=j.useState([]),[S,y]=j.useState(""),[b,w]=j.useState(!1),[k,C]=j.useState(!0),[R,E]=j.useState(900),[F,O]=j.useState("all"),[K,Y]=j.useState(""),z=j.useRef(0),Z=j.useRef(null),G=j.useCallback(async()=>{try{const L=await W0(e,u.route);if(p(L),y(""),!b){const $=await K0(e,{after:z.current,limit:200},u.route);Array.isArray($.records)&&$.records.length&&(z.current=$.cursor,v(U=>[...U,...$.records].slice(-500)))}}catch(L){y(L&&L.status===404?m("diagnostics-unsupported"):String(L.message||L))}},[e,u.key,b]);j.useEffect(()=>{z.current=0,v([]),p(null),y("")},[u.key]),j.useEffect(()=>{let L=null,$=!0;const U=()=>{!$||document.visibilityState==="hidden"||G()},x=()=>{L&&clearInterval(L),L=document.visibilityState==="hidden"?null:setInterval(U,2e3),document.visibilityState!=="hidden"&&U()};return document.addEventListener("visibilitychange",x),x(),()=>{$=!1,L&&clearInterval(L),document.removeEventListener("visibilitychange",x)}},[G]),j.useEffect(()=>{k&&Z.current&&(Z.current.scrollTop=Z.current.scrollHeight)},[f,k]);const Q=f.filter(L=>(F==="all"||L.level===F)&&(!K||String(L.component||"").toLowerCase().includes(K.toLowerCase()))),B=Q.map(L=>`${L.ts} ${L.level.toUpperCase()} ${L.component} ${L.code} ${L.message}${Object.keys(L.meta||{}).length?` ${JSON.stringify(L.meta)}`:""}`).join(`
92
92
  `),M=async L=>{y("");try{const $=await L();p($),await G()}catch($){y(String($.message||$))}},N=async()=>{try{await navigator.clipboard.writeText(B)}catch{y(m("copy-manual"))}},T=()=>{try{const L=new Blob([`${JSON.stringify(Q,null,2)}
93
- `],{type:"application/json"}),$=URL.createObjectURL(L),U=document.createElement("a");U.href=$,U.download="nexuscrew-diagnostics.json",U.click(),URL.revokeObjectURL($)}catch{y(m("diagnostics-export-failed"))}};return c.jsxs("div",{className:"nc-set-tab nc-diagnostics",children:[c.jsxs("label",{className:"nc-field",children:[m("diagnostics-target"),c.jsx("select",{value:u.key,onChange:L=>a(L.target.value),children:s.map(L=>c.jsxs("option",{value:L.key,children:[L.label,L.status!=="up"?` · ${L.status}`:""]},L.key))})]}),c.jsxs("div",{className:"nc-set-form nc-diag-controls",children:[c.jsx("div",{className:"nc-sheet-label",children:m("diagnostics-verbose")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("select",{"aria-label":m("diagnostics-duration"),value:R,onChange:L=>E(Number(L.target.value)),disabled:n,children:[300,900,1800,3600].map(L=>c.jsxs("option",{value:L,children:[L/60," min"]},L))}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||(h==null?void 0:h.verbose)===!0,onClick:()=>M(()=>xg(e,!0,R,u.route)),children:m("enable")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||(h==null?void 0:h.verbose)!==!0,onClick:()=>M(()=>xg(e,!1,R,u.route)),children:m("stop")})]}),c.jsx("small",{className:"nc-set-hint",children:h!=null&&h.verbose?m("diagnostics-expires").replace("{time}",new Date(h.expiresAt).toLocaleTimeString()):m("diagnostics-off")})]}),c.jsxs("div",{className:"nc-diag-filters",children:[c.jsxs("select",{"aria-label":m("diagnostics-level"),value:F,onChange:L=>O(L.target.value),children:[c.jsx("option",{value:"all",children:m("diagnostics-all-levels")}),["debug","info","warn","error"].map(L=>c.jsx("option",{value:L,children:L},L))]}),c.jsx("input",{"aria-label":m("diagnostics-component"),placeholder:m("diagnostics-component"),value:K,onChange:L=>Y(L.target.value)})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:b,onChange:L=>w(L.target.checked)})," ",m("diagnostics-pause")]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:k,onChange:L=>C(L.target.checked)})," ",m("diagnostics-autoscroll")]})]}),c.jsx("pre",{ref:Z,className:"nc-diag-log","aria-label":m("diagnostics-log"),children:B||m("diagnostics-empty")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:N,children:m("copy")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:T,children:m("diagnostics-export")}),c.jsx("button",{type:"button",className:"nc-btn danger",disabled:n,onClick:()=>{window.confirm(m("diagnostics-clear-confirm"))&&M(async()=>{const L=await q0(e,u.route);return z.current=0,v([]),L})},children:m("diagnostics-clear")})]}),S&&c.jsx("div",{className:"nc-err",children:S})]})}function jj({token:e,settings:t,readonly:n,refresh:s,roster:o,section:a,setSection:u}){const[h,p]=j.useState(null),[f,v]=j.useState(null),[S,y]=j.useState(!1),[b,w]=j.useState(!1),[k,C]=j.useState(null),[R,E]=j.useState(!0),[F,O]=j.useState(!1);j.useEffect(()=>{C(t&&t.update||null),E(!t||t.autoUpdate!==!1),O(!!(t&&t.alternateScreen))},[t]);const K=async()=>{p(null),v(null),w(!0);try{const N=await C0(e);v(N.note||"ok"),y(!1)}catch(N){p(String(N.message||N))}w(!1)},Y=async()=>{p(null),v(null),w(!0);try{const N=await $0(e);v(`${N.note||"ok"}${N.target?` (${N.target})`:""}`)}catch(N){p(String(N.message||N))}w(!1)},z=async N=>{p(null),v(null),w(!0);try{await Ud(e,{autoUpdate:N}),E(N),s&&await s()}catch(T){p(String(T.message||T))}w(!1)},Z=async N=>{p(null),v(null),w(!0);try{await Ud(e,{alternateScreen:N}),O(N),s&&await s()}catch(T){p(String(T.message||T))}w(!1)},G=async()=>{p(null),v(null),w(!0);try{C(await H0(e))}catch(N){p(String(N.message||N))}w(!1)},Q=async()=>{p(null),v(null),w(!0);try{const N=await U0(e);C(N),v(m("npm-update-restarting"))}catch(N){p(String(N.message||N))}w(!1)},B=()=>{window.confirm(m("composer-clear-confirm"))&&(p(null),WE()?(window.dispatchEvent(new Event(Dh)),v(m("composer-clear-done"))):p(m("composer-clear-failed")))},M=t&&t.service;return c.jsxs("div",{className:"nc-set-tab",children:[c.jsx("div",{className:"nc-system-tabs",role:"tablist","aria-label":m("tab-system"),children:["general","diagnostics"].map(N=>c.jsx("button",{type:"button",role:"tab","aria-selected":a===N,className:`nc-set-tabbtn${a===N?" on":""}`,onClick:()=>u(N),children:m(`system-${N}`)},N))}),a==="general"&&c.jsxs(c.Fragment,{children:[c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-this-node")}),t&&c.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,c.jsx("br",{}),M&&M.installed?m("service-installed"):m("service-missing"),M&&M.installed?` · ${M.active?m("service-active"):m("service-inactive")}`:"",M?` · boot ${M.boot?"on":"off"}`:""]}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||b,title:n?m("settings-readonly"):"",onClick:Y,children:m("service-regenerate")})}),c.jsx(xj,{token:e,readonly:n}),c.jsx(kj,{})]}),c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-updates")}),c.jsxs("div",{className:"nc-set-form nc-update-settings",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:R,disabled:n||b||k&&!k.supported,onChange:N=>z(N.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:m("npm-auto-update")}),c.jsx("small",{children:m("npm-auto-update-help")})]})]}),k&&c.jsxs("div",{className:"nc-set-info",children:[m("npm-update-current")," v",k.current,k.latest?` · ${m("npm-update-latest")} v${k.latest}`:""," · ",m(`npm-update-${k.supported?k.phase:"unsupported"}`)]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||b||!k||!k.supported,onClick:G,children:m("npm-update-check")}),k&&k.available&&c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||b||k.phase==="installing",onClick:Q,children:m("npm-update-install")})]}),k&&k.lastError&&c.jsx("div",{className:"nc-err",children:k.lastError})]})]}),c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-security")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||b,title:n?m("settings-readonly"):"",onClick:()=>{p(null),v(null),y(!0)},children:m("token-rotate")})})]}),c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-local-data")}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:m("composer-clear-data")}),c.jsx("small",{className:"nc-set-hint",children:m("composer-clear-data-help")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:B,children:m("composer-clear-data")})})]})]})]}),a==="diagnostics"&&c.jsxs("section",{className:"nc-system-group nc-system-diagnostics",children:[c.jsx("h3",{children:m("system-diagnostics")}),c.jsxs("div",{className:"nc-set-form nc-alternate-screen-settings",children:[c.jsx("div",{className:"nc-sheet-label",children:m("system-terminal")}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox","aria-label":m("alternate-screen"),checked:F,disabled:n||b,onChange:N=>Z(N.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:m("alternate-screen")}),c.jsx("small",{children:m("alternate-screen-help")})]})]})]}),c.jsx(Nj,{token:e,roster:o,readonly:n})]}),S&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("div",{children:m("token-rotate-explain")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:b,onClick:()=>y(!1),children:m("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:b,onClick:K,children:m("confirm")})]})]}),f&&c.jsx("div",{className:"nc-set-note",children:f}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function Rj({token:e,onClose:t,initialTab:n="nodes",initialLocation:s="",startNewCell:o=!1}){kn();const a=n==="diagnostics"?"diagnostics":"general",[u,h]=j.useState(n==="diagnostics"?"system":n),p=j.useRef(null),f=j.useRef(null),[v,S]=j.useState({start:!1,end:!1}),[y,b]=j.useState(a),[w,k]=j.useState(null),[C,R]=j.useState([]),[E,F]=j.useState([]),[O,K]=j.useState(!1),[Y,z]=j.useState(null),[Z,G]=j.useState(0),Q=nf(e,!0,Z),B=j.useCallback(()=>{const N=p.current;if(!N)return;const T=N.scrollWidth>N.clientWidth+1,L={start:T&&N.scrollLeft>1,end:T&&N.scrollLeft+N.clientWidth<N.scrollWidth-1};S($=>$.start===L.start&&$.end===L.end?$:L)},[]);j.useEffect(()=>{const N=p.current;if(!N)return;const T=typeof ResizeObserver=="function"?new ResizeObserver(B):null;return T==null||T.observe(N),window.addEventListener("resize",B),B(),()=>{T==null||T.disconnect(),window.removeEventListener("resize",B)}},[B]),j.useEffect(()=>{var T,L;(L=(T=f.current)==null?void 0:T.scrollIntoView)==null||L.call(T,{behavior:"smooth",block:"nearest",inline:"nearest"});const N=window.requestAnimationFrame?window.requestAnimationFrame(B):setTimeout(B,0);return()=>{window.cancelAnimationFrame?window.cancelAnimationFrame(N):clearTimeout(N)}},[u,B]);const M=j.useCallback(async()=>{var N;try{const T=await ey(e);k(T),z(null)}catch(T){z(String(T.message||T))}try{const L=(await x0(e)).peers||[];let $=[],U=[];try{const[x,I]=await Promise.all([Ht("/api/config",e).then(X=>X.json()).catch(()=>null),ny(e).catch(()=>null)]),D=x&&typeof x.instanceId=="string"?x.instanceId:"",A=[{instanceId:D||null,route:[],label:null},...dy(I,D)],W=await Promise.all(A.map(async X=>{try{return{ok:!0,peers:((await ty(e,X.route)).nodes||[]).map(fe=>uy(fe,X)).filter(Boolean)}}catch(ie){return{ok:!1,owner:X,error:ie}}}));for(const X of W){if(X.ok){$.push(...X.peers);continue}X.owner.route.length!==0&&U.push({instanceId:X.owner.instanceId,label:X.owner.label,route:X.owner.route,failure:/timeout/i.test(String(((N=X.error)==null?void 0:N.message)||X.error))?"timeout":"unreachable"})}}catch{}F(U),R([...L,...$])}catch(T){z(String(T.message||T))}},[e]);return j.useEffect(()=>{M();const N=setInterval(M,5e3);return()=>clearInterval(N)},[M]),j.useEffect(()=>{let N=!1;return Ht("/api/config",e).then(T=>T.json()).then(T=>{N||K(!!T.readonlyDefault)}).catch(()=>{}),()=>{N=!0}},[e]),c.jsx("div",{className:"nc-set-overlay",onClick:t,children:c.jsxs("div",{className:"nc-set-panel",onClick:N=>N.stopPropagation(),children:[c.jsxs("div",{className:"nc-set-head",children:[c.jsx(Ze,{name:"gear",size:18}),c.jsx("b",{children:m("settings")}),c.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:m("close"),children:c.jsx(Ze,{name:"x",size:18})})]}),O&&c.jsx("div",{className:"nc-set-readonly",children:m("settings-readonly")}),Y&&c.jsx("div",{className:"nc-err",children:Y}),c.jsx("div",{className:`nc-set-tabs-wrap${v.start?" has-start-overflow":""}${v.end?" has-end-overflow":""}`,children:c.jsx("div",{className:"nc-set-tabs",ref:p,onScroll:B,children:["nodes","fleet","audio","input","system"].map(N=>c.jsx("button",{ref:u===N?f:null,type:"button",className:`nc-set-tabbtn${u===N?" on":""}`,onClick:()=>h(N),children:m(`tab-${N}`)},N))})}),c.jsxs("div",{className:"nc-set-body",children:[u==="nodes"&&c.jsx(Sj,{token:e,nodes:C,roster:Q,settings:w,readonly:O,vlUnavailable:E,refresh:M,refreshAliases:()=>G(N=>N+1)}),u==="fleet"&&c.jsx(ij,{token:e,readonly:O,startNewCell:o,initialLocation:s,targets:Q.map(N=>({route:N.route,label:N.label||N.name,status:N.status}))}),u==="audio"&&c.jsx(Cj,{token:e,readonly:O,nodes:C,settings:w}),u==="input"&&c.jsx(Ej,{}),u==="system"&&c.jsx(jj,{token:e,settings:w,readonly:O,refresh:M,roster:Q,section:y,setSection:b})]})]})})}function Pj({token:e,initialPair:t,deviceDefault:n="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:u}){kn();const[h,p]=j.useState(t?"pair":"welcome"),[f,v]=j.useState(!1),[S,y]=j.useState(null),b=async()=>{v(!0),y(null);try{await Ud(e,{wizardDone:!0}),a&&a(),u()}catch(w){y(String(w.message||w)),v(!1)}};return c.jsx("div",{className:"nc-wiz-overlay",children:c.jsxs("div",{className:"nc-wiz",children:[c.jsxs("div",{className:"nc-wiz-head",children:[c.jsx("b",{children:m("wizard-title")}),c.jsx("small",{children:m("hydra-simple")})]}),h==="welcome"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:m("local-ready")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:b,children:m("local-only")}),c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:()=>p("pair"),children:m("add-node")})]})]}),h==="pair"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx(Yw,{token:e,initial:t||"",autoStart:!!t,deviceDefault:n,localNodeId:s,localNameDefault:o,onBusyChange:v,onSuccess:async()=>{a&&a(),p("done")}}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:()=>{a&&a(),p("welcome")},children:m("back")})})]}),h==="done"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:m("node-connected")}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:b,children:m("finish")})})]}),S&&c.jsx("div",{className:"nc-err",children:S})]})})}function Lj(e,t){if(typeof EventSource>"u"||!e)return()=>{};const n=new EventSource(`/api/events?token=${encodeURIComponent(e)}`);return n.onmessage=s=>{try{const o=JSON.parse(s.data);o&&typeof o=="object"&&typeof o.type=="string"&&t(o)}catch{}},()=>{try{n.close()}catch{}}}const Aj=6e3,Tj=12e3;function Dj({n:e,onClose:t}){return c.jsxs("div",{className:`nc-ntf-toast${e.urgency==="high"?" high":""}`,role:"status","aria-live":"polite",children:[c.jsxs("div",{className:"nc-ntf-toast-txt",children:[c.jsx("b",{children:e.title}),e.body&&c.jsx("small",{children:e.body}),e.session&&c.jsx("span",{className:"nc-ntf-from",children:e.session})]}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:t,title:m("close"),children:c.jsx(Ze,{name:"x",size:14})})]})}function Mj({ask:e,token:t,onAnswered:n,onDismiss:s}){const[o,a]=j.useState(""),[u,h]=j.useState(!1),[p,f]=j.useState(null),[v,S]=j.useState(!1),y=async w=>{const k=String(w||"").trim();if(!(!k||u)){f(null),h(!0);try{await Y0(t,e.id,k),n(e.id)}catch(C){f(String(C.message||C))}h(!1)}},b=async()=>{if(!v){S(!0);try{await G0(t,e.id),s(e.id)}catch{S(!1)}}};return c.jsxs("div",{className:"nc-ask-card",children:[c.jsxs("div",{className:"nc-ask-head",children:[c.jsx("span",{className:"nc-ntf-from",children:e.session}),c.jsxs("code",{className:"nc-ask-id",children:["#",e.id]}),c.jsx("button",{type:"button",className:"nc-ask-dismiss",title:m("ask-dismiss"),"aria-label":m("ask-dismiss"),disabled:v,onClick:b,children:c.jsx(Ze,{name:"x",size:14})})]}),c.jsx("div",{className:"nc-ask-q",children:e.question}),Array.isArray(e.options)&&e.options.length>0&&c.jsx("div",{className:"nc-ask-opts",children:e.options.map(w=>c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:u,onClick:()=>y(w),children:w},w))}),c.jsxs("div",{className:"nc-ask-reply",children:[c.jsx("textarea",{rows:2,placeholder:m("ask-reply-ph"),value:o,disabled:u,onChange:w=>a(w.target.value)}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:u||!o.trim(),onClick:()=>y(o),children:m("send")})]}),p&&c.jsx("div",{className:"nc-err",children:p})]})}function Ij({token:e}){const[t]=kn(),[n]=ub(),[s,o]=j.useState([]),[a,u]=j.useState([]),[h,p]=j.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),f=j.useRef(0),v=j.useRef(null);v.current||(v.current=yj());const S=j.useRef({enabled:n,lang:t});S.current={enabled:n,lang:t};const y=j.useCallback(C=>{o(R=>R.filter(E=>E.key!==C))},[]),b=j.useCallback(C=>{const R=`t${f.current+=1}`;o(F=>[...F.slice(-3),{...C,key:R}]),setTimeout(()=>y(R),C.urgency==="high"?Tj:Aj);const E=S.current;if(E.enabled)try{v.current.enqueue(C,pj(C,E.lang))}catch{}},[y]);j.useEffect(()=>{n||v.current.stop()},[n]),j.useEffect(()=>{const C=()=>{(document.visibilityState!=="visible"||!document.hasFocus())&&v.current.stop()},R=()=>v.current.stop();return document.addEventListener("visibilitychange",C),window.addEventListener("blur",C),window.addEventListener(Fh,R),()=>{document.removeEventListener("visibilitychange",C),window.removeEventListener("blur",C),window.removeEventListener(Fh,R),typeof v.current.dispose=="function"?v.current.dispose():v.current.stop()}},[]),j.useEffect(()=>{if(!e)return;let C=!1;V0(e).then(E=>{C||u(E.asks||[])}).catch(()=>{});const R=Lj(e,E=>{E.type==="notify"?b(E):E.type==="ask"&&E.ask&&E.ask.id?u(F=>F.some(O=>O.id===E.ask.id)?F:[...F,E.ask]):E.type==="ask-answered"&&E.id?u(F=>F.filter(O=>O.id!==E.id)):E.type==="ask-dismissed"&&E.id&&u(F=>F.filter(O=>O.id!==E.id))});return()=>{C=!0,R()}},[e,b]);const w=C=>u(R=>R.filter(E=>E.id!==C)),k=C=>u(R=>R.filter(E=>E.id!==C));return c.jsxs(c.Fragment,{children:[s.length>0&&c.jsx("div",{className:"nc-ntf-toasts",children:s.map(C=>c.jsx(Dj,{n:C,onClose:()=>y(C.key)},C.key))}),a.length>0&&!h&&c.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>p(!0),title:m("asks-title"),children:["? ",c.jsx("span",{className:"nc-ask-count",children:a.length})]}),a.length>0&&h&&c.jsxs("div",{className:"nc-ask-panel",children:[c.jsxs("div",{className:"nc-ask-panel-head",children:[c.jsx("b",{children:m("asks-title")}),c.jsx("span",{className:"nc-ask-count",children:a.length}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>p(!1),title:m("close"),children:c.jsx(Ze,{name:"x",size:16})})]}),c.jsx("div",{className:"nc-ask-panel-body",children:a.map(C=>c.jsx(Mj,{ask:C,token:e,onAnswered:w,onDismiss:k},C.id))})]})]})}const Oj=4e3;async function Bj(e){const t=await Ht("/api/sessions",e);if((t==null?void 0:t.ok)===!1)throw new Error(`HTTP ${t.status}`);const n=await t.json();if(!Array.isArray(n==null?void 0:n.sessions))throw new Error("invalid sessions payload");return n.sessions}async function Fd(e,t=[]){var h;const[n,s]=await Promise.allSettled([t.length?Jh(e,t):Bj(e),ws(e,t)]),o=n.status==="fulfilled"&&Array.isArray((h=n.value)==null?void 0:h.sessions)?n.value.sessions:Array.isArray(n.value)?n.value:null,a=s.status==="fulfilled"?s.value:null,u=(a==null?void 0:a.available)===!0&&Array.isArray(a.cells)?a.cells:null;return{sessions:o,cells:u,fresh:Array.isArray(o)&&Array.isArray(u)}}function db(e,t,n){return n===!0&&(e==null?void 0:e.degraded)!==!0&&(e==null?void 0:e.active)===!0&&e.tmux!==!1&&!!e.tmuxSession&&(t||[]).some(s=>(s==null?void 0:s.name)===e.tmuxSession)}function zj(e){const t=new Map((e.sessions||[]).map(o=>[o.name,o])),n=[],s=(o,a,u,h=[],p="")=>{const f=new Map((a||[]).map(v=>[v.name,v]));for(const v of o||[]){const S=f.get(v.tmuxSession)||{},y=of(v,S),b=db(v,a,u);n.push({key:Go(h,v.tmuxSession||v.cell),session:v.tmuxSession,route:h,cellName:v.cell,label:v.cell,node:h.length?h.join("/"):"",nodeLabel:p,live:b,selectable:b,verified:u===!0,working:y.working,degraded:!!v.degraded,active:v.active===!0,activity:S.activity||v.activity||0,subtitle:y.subtitle})}};s(e.cells,[...t.values()],e.localFresh===!0);for(const o of e.nodeGroups||[]){const a=Array.isArray(o.route)?o.route:[];o.kind==="vl"||!a.length||s(o.cells,o.sessions,o.switcherFresh===!0,a,o.label||o.name||"")}return n}function Fj(e){const t=new Map,n=Array.isArray(e.sessions)?e.sessions:[],s=Array.isArray(e.cells)?e.cells:[],o=new Map(n.map(u=>[u.name,u])),a=new Set(s.map(u=>u.tmuxSession).filter(Boolean));t.set("local",af(s,n.filter(u=>!a.has(u.name)),o));for(const u of e.nodeGroups||[]){const h=Array.isArray(u.route)?u.route:[];if(u.kind==="vl"||!h.length)continue;const p=Array.isArray(u.cells)?u.cells:[],f=Array.isArray(u.sessions)?u.sessions:[],v=new Set(p.map(S=>S.tmuxSession).filter(Boolean));t.set(h.join("/"),Fl({...u,route:h,cells:p,sessions:f,unmanaged:f.filter(S=>!v.has(S.name))}).rawItems)}return t}function $j(e,t,n,s){return[...new Set(e.map(a=>a.node||"local"))].flatMap(a=>{const u=e.filter(v=>(v.node||"local")===a),h=new Map((t.get(a)||[]).map(v=>[v.key,v])),p=new Map(u.map(v=>[v.key,v])),f=u.map(v=>{const S=h.get(v.key);return{...v,label:(S==null?void 0:S.label)||v.label,live:(S==null?void 0:S.live)??v.live,fresh:(S==null?void 0:S.fresh)===!0,activity:(S==null?void 0:S.activity)??v.activity}});return gs(f,n,"all",Fi(s,a)).map(v=>p.get(v.key))})}function Hj({token:e,current:t,onPick:n,onClose:s}){const[o,a]=j.useState(Ig),[u,h]=j.useState(!1),[p,f]=j.useState(!1),[v,S]=j.useState(""),[y,b]=j.useState(""),[w,k]=j.useState(""),C=j.useRef(null),R=j.useRef(null),E=j.useMemo(()=>zj(o),[o]),F=j.useMemo(()=>Fj(o),[o]),{pins:O,orders:K,canMoveRoster:Y,moveRoster:z,stepRoster:Z}=rf(),G=j.useMemo(()=>$j(E,F,O,K),[E,F,O,K]),Q=j.useMemo(()=>u?G:G.filter(L=>L.selectable||L.degraded&&L.active),[G,u]),B=j.useMemo(()=>E.find(L=>L.key===y&&L.selectable),[E,y]);j.useEffect(()=>{var U;const L=document.activeElement;(U=R.current)==null||U.focus();const $=x=>{x.key==="Escape"&&s()};return document.addEventListener("keydown",$),()=>{var x;document.removeEventListener("keydown",$),(x=L==null?void 0:L.focus)==null||x.call(L)}},[s]),j.useEffect(()=>{let L=!0,$=!1;const U=async()=>{if($)return;$=!0;const I=Ig(),D=Array.isArray(I.nodeGroups)?I.nodeGroups:[],A=Fd(e),W=pe=>pe.kind!=="vl"&&Array.isArray(pe.route)&&pe.route.length>0,X=await Promise.all(D.filter(W).map(async pe=>({group:pe,result:await Fd(e,pe.route)}))),ie=await A;if(!L)return;const le=new Map(X.map(({group:pe,result:_e})=>[JSON.stringify(pe.route||[]),_e])),fe=D.map(pe=>{if(!W(pe))return{...pe,switcherFresh:!1};const _e=le.get(JSON.stringify(pe.route||[]));return _e?{...pe,sessions:_e.sessions||pe.sessions||[],cells:_e.cells||pe.cells||[],switcherFresh:_e.fresh}:{...pe,switcherFresh:!1}}),we=by({...I,sessions:ie.sessions||I.sessions||[],cells:ie.cells||I.cells||[],localFresh:ie.fresh,nodeGroups:fe,refreshedAt:Date.now()});a(we),f(!0),$=!1};U();const x=setInterval(U,Oj);return()=>{L=!1,clearInterval(x)}},[e]);const M=L=>L.degraded?m("cell-degraded"):L.verified?L.selectable?L.working?m("cell-working"):m("cell-idle"):m("cell-off"):m("cell-switcher-not-confirmed"),N=L=>{if(S(""),!L.selectable){S(m("cell-switcher-not-active"));return}b(L.key)},T=async()=>{const L=B;if(!L){S(m("cell-switcher-select"));return}S(""),k(L.key);try{const $=await Fd(e,L.route),U=($.cells||[]).find(x=>(x==null?void 0:x.cell)===L.cellName&&(x==null?void 0:x.tmuxSession)===L.session);if(!db(U,$.sessions,$.fresh)){S(m("cell-switcher-not-active"));return}n({session:L.session,...L.node?{node:L.node}:{},cellName:L.cellName}),s()}catch{S(m("cell-switcher-not-active"))}finally{k("")}};return c.jsx("div",{className:"nc-cell-switcher-backdrop",onClick:s,children:c.jsxs("aside",{ref:C,className:"nc-cell-switcher",role:"dialog","aria-label":m("fleet-cells"),tabIndex:-1,onClick:L=>L.stopPropagation(),children:[c.jsxs("div",{className:"nc-cell-switcher-list",children:[!p&&c.jsx("div",{className:"nc-empty",role:"status",children:m("cell-switcher-refreshing")}),p&&Q.length===0&&c.jsx("div",{className:"nc-empty",role:"status",children:m("cell-switcher-empty-active")}),Q.map(L=>{const $=(t==null?void 0:t.session)===L.session&&((t==null?void 0:t.node)||"")===L.node,U=y===L.key,x=M(L),I=L.node||"local",D=F.get(I)||E.filter(A=>(A.node||"local")===I);return c.jsxs("div",{className:`nc-cell-switcher-row${$?" current":""}${U?" selected":""}${L.selectable?"":" off"}`,"data-roster-key":L.key,"data-position":I,children:[c.jsx(bi,{position:I,itemKey:L.key,label:L.cellName,canMove:Y,onMove:(A,W)=>z(I,A,W,D),onStep:A=>Z(I,L.key,A,D)}),c.jsxs("button",{type:"button",className:"nc-cell-switcher-row-select","aria-current":$?"true":void 0,"aria-pressed":U,"aria-disabled":!L.selectable,disabled:w===L.key,onClick:()=>N(L),children:[c.jsx("span",{className:`nc-cell-switcher-dot${L.degraded?" warn":L.live?` on${L.working?" working":""}`:""}`}),c.jsxs("span",{className:"nc-cell-switcher-copy",children:[c.jsx("b",{children:L.cellName}),c.jsx("small",{className:"nc-cell-switcher-state",children:x}),c.jsx("small",{children:[L.nodeLabel,L.subtitle].filter(Boolean).join(" · ")})]})]})]},L.key)})]}),v&&c.jsx("div",{className:"nc-cell-switcher-notice",role:"status",children:v}),c.jsx("div",{className:"nc-cell-switcher-actions",children:c.jsx("button",{type:"button",className:"nc-cell-switcher-open",disabled:!B||!!w,onClick:T,children:B?`${m("cell-switcher-open")}: ${B.cellName}`:m("cell-switcher-select")})}),c.jsxs("div",{className:"nc-cell-switcher-controls",children:[c.jsx("b",{children:m("fleet-cells")}),c.jsx("button",{type:"button",className:"nc-cell-switcher-filter","aria-pressed":u,onClick:()=>{S(""),h(L=>!L)},children:m(u?"cell-switcher-show-active":"cell-switcher-show-all")}),c.jsx("button",{ref:R,type:"button",className:"nc-cell-switcher-close","aria-label":m("cell-switcher-close"),title:m("cell-switcher-close"),onClick:s,children:"×"})]})]})})}const Uj=2e3;function Wj(e){if(e.kind==="gap")return`${m("vl-events-gap")} ${e.count||"?"}`;if(e.kind==="truncate")return m("vl-events-truncated");if(e.kind==="turn_end")return m("vl-events-turn-end");if(e.kind==="done")return m("vl-events-done");if(e.kind==="writer_epoch")return m("vl-events-writer-epoch");if(e.kind==="tool_start")return`${m("vl-events-tool")} ${e.name||""}`.trim();if(e.kind==="tool_end"){const t=e.isError?m("vl-events-tool-failed"):m("vl-events-tool-ok");return`${m("vl-events-tool")} ${e.name||""} — ${t}`.trim()}return e.kind==="usage"?e.text||m("vl-events-usage"):e.text||""}function Kj({token:e,nodeId:t,route:n=[]}){const[s,o]=j.useState([]),[a,u]=j.useState(!1),h=j.useRef(0),p=j.useRef(!1);return j.useEffect(()=>{let f=!0;h.current=0,p.current=!1,o([]);const v=async()=>{if(!p.current){p.current=!0;try{const b=await k0(e,t,h.current,n);if(!f)return;u(!1);const k=(Array.isArray(b==null?void 0:b.events)?b.events:[]).filter(C=>Number.isSafeInteger(C==null?void 0:C.seq)&&C.seq>h.current);k.length&&(o(C=>C.concat(k).slice(-500)),h.current=k[k.length-1].seq),Number.isSafeInteger(b==null?void 0:b.cursor)&&b.cursor>h.current&&(h.current=b.cursor)}catch{f&&u(!0)}finally{p.current=!1}}},S=()=>{v().catch(()=>{})};S();const y=setInterval(S,Uj);return()=>{f=!1,clearInterval(y)}},[e,t,JSON.stringify(n)]),c.jsxs("div",{className:"nc-vl-events",children:[a&&c.jsx("div",{className:"nc-set-hint nc-vl-events-stale",children:m("vl-events-stale")}),s.length===0&&!a&&c.jsx("small",{className:"nc-set-hint","data-testid":"vl-events-empty",children:m("vl-events-empty")}),s.length>0&&c.jsx("ol",{className:"nc-vl-events-list",children:s.map(f=>c.jsx("li",{"data-testid":`vl-ev-${f.seq}`,className:`nc-vl-ev nc-vl-ev-${f.kind}${f.isError?" error":""}${f.kind==="gap"?" gap":""}`,children:Wj(f)},`${f.seq}-${f.kind}`))})]})}function F_({peer:e,token:t,onBack:n}){var s;return kn(),e?c.jsxs("section",{className:"nc-vl-session-view",children:[c.jsxs("header",{className:"nc-bar nc-bar-single",children:[c.jsxs("button",{onClick:n,title:m("back"),children:[c.jsx(Ze,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:m("back")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:e.label,children:e.label}),((s=e.session)==null?void 0:s.profile)&&c.jsxs("small",{className:"nc-bar-sub",children:[e.session.profile," · ",m("vl-events-title")]})]}),c.jsx("span",{className:"nc-bar-right"})]}),c.jsx("div",{className:"nc-vl-session-body",children:c.jsx(Kj,{token:t,nodeId:e.nodeId,route:e.route||[]})})]}):null}const $d=e=>ki(Ys(e)).length===0,Hd=e=>Array.isArray(e)?e.join("/"):"";function qj(e){const t=new Set,n=[];for(const s of Array.isArray(e)?e:[])!s||!Hn.test(String(s.instanceId||""))||!Array.isArray(s.route)||!s.route.length||t.has(s.instanceId)||(t.add(s.instanceId),n.push({instanceId:s.instanceId,route:[...s.route],label:String(s.label||s.name||s.route.join(" › ")),status:s.status||"offline"}));return n.sort((s,o)=>s.label.localeCompare(o.label))}function zo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function Hs(e,t,n,s=!1,o=!0){const a=t.instanceId;return{...e,id:Lr(s?null:a,e.name),ownerId:a,ownerRoute:s?[]:[...t.route],ownerLabel:s?"Local":t.label,local:s,available:o,ownerTopology:Array.isArray(n)?n:[],layout:Ih(e.layout,a,n)}}function Vj(e,t,n,s,o=[]){const[a,u]=j.useState([]),[h,p]=j.useState(!1),[f,v]=j.useState("idle"),[S,y]=j.useState(""),[b,w]=j.useState(""),k=j.useRef([]),C=j.useRef([]),R=j.useRef(""),E=j.useRef(t),F=j.useRef(n),O=j.useRef(!1),K=j.useRef(!0),Y=j.useRef(""),z=j.useMemo(()=>qj(o),[o]),Z=z.map(D=>`${D.instanceId}:${Hd(D.route)}:${D.status}:${D.label}`).join("|");C.current=z,F.current=n,E.current=t,k.current=a;const G=j.useCallback(D=>Oh(D.layout,R.current,C.current),[]),Q=j.useCallback((D,A=!0,W=E.current)=>{const X=qv(D,xl()),ie=k.current.some(fe=>fe.id===W);k.current=X,u(X);const le=X.find(fe=>fe.id===W);if(A&&le){K.current=!0;const fe=G(le);s(fe),le.local&&pN(le.name,fe)}else ie&&!le&&(K.current=!0,s(Pr()),y("deck non più condiviso dal nodo owner"));uN(X.filter(fe=>fe.local).map(fe=>fe.name))},[s,G]),B=j.useCallback(async D=>{let A=await pl(e);const W=A.decks.find(X=>X.name==="main");if(A.decks.length===1&&W&&W.revision===0&&$d(W.layout)){const X=cN(),ie=Ys(Bh("main")||Pr());if(!$d(ie)){const le=Ih(ie,D,[]);A.decks[0]=await ml(e,"main",le,0,[])}for(const le of X.filter(fe=>fe!=="main"))try{let fe=await kg(e,le,[]);const we=Ys(Bh(le)||Pr());$d(we)||(fe=await ml(e,le,Ih(we,D,[]),fe.revision,[]))}catch{}A=await pl(e)}return A},[e]),M=j.useCallback(async({migrate:D=!1}={})=>{const A=await Zh(e,[]),W=Hn.test(String(A.instanceId||""))?A.instanceId:"";if(!W)throw new Error("instanceId locale non disponibile");R.current=W,w(W);const[X,ie]=await Promise.all([D?B(W):pl(e),bg(e,[]).catch(()=>({nodes:[]}))]),le={instanceId:W,route:[],label:"Local"},fe=X.decks.map(pe=>Hs(pe,le,ie.nodes,!0,!0)),we=k.current;return await Promise.all(C.current.map(async pe=>{if(pe.status!=="up"){fe.push(...we.filter(_e=>!_e.local&&_e.ownerId===pe.instanceId).map(_e=>({..._e,ownerRoute:[...pe.route],ownerLabel:pe.label,available:!1})));return}try{const[_e,He]=await Promise.all([pl(e,pe.route),bg(e,pe.route).catch(()=>({nodes:[]}))]);fe.push(..._e.decks.map(ht=>Hs(ht,pe,He.nodes,!1,!0)))}catch{fe.push(...we.filter(He=>!He.local&&He.ownerId===pe.instanceId).map(He=>({...He,ownerRoute:[...pe.route],ownerLabel:pe.label,available:!1})))}})),fe.sort((pe,_e)=>pe.local===_e.local?pe.ownerLabel.localeCompare(_e.ownerLabel)||(pe.name==="main"?-1:_e.name==="main"?1:pe.name.localeCompare(_e.name)):pe.local?-1:1),fe},[e,B,Z]);j.useEffect(()=>{if(!e)return;let D=!1;const A=Y.current!==e;return Y.current=e,M({migrate:A}).then(W=>{if(D)return;const X=k.current.find(fe=>fe.id===E.current),ie=W.find(fe=>fe.id===E.current),le=ie&&(!X||ie.revision>X.revision||X.available!==ie.available||Hd(X.ownerRoute)!==Hd(ie.ownerRoute));Q(W,A||!!le&&!O.current),p(!0),ie&&y("")}).catch(W=>{D||(p(!0),y(String(W.message||W)))}),()=>{D=!0}},[Q,M,Z,e]);const N=j.useCallback(async(D=E.current)=>{if(!h||!O.current)return!0;const A=k.current.find(W=>W.id===D);if(!A)return y(`deck inesistente: ${D}`),!1;if(A.available===!1)return y(`nodo owner offline: ${A.ownerLabel}`),!1;v("saving");try{const W=Wv(Ys(F.current),A.ownerId,A.ownerTopology),X=await ml(e,A.name,W,A.revision,A.ownerRoute),ie=Hs(X,zo(A),A.ownerTopology,A.local,!0);return Q(k.current.map(le=>le.id===D?ie:le),!1),O.current=!1,v("saved"),y(""),setTimeout(()=>v("idle"),1500),!0}catch(W){if(v("error"),y(String(W.message||W)),W.status===409&&W.data&&W.data.current){const X=k.current.find(le=>le.id===D),ie=Hs(W.data.current,zo(X),X.ownerTopology,X.local,!0);Q(k.current.map(le=>le.id===D?ie:le),!1)}return!1}},[h,e,Q]);return j.useEffect(()=>{if(!h)return;if(K.current){K.current=!1;return}O.current=!0,v("saving");const D=setTimeout(N,650);return()=>clearTimeout(D)},[n,h,N]),j.useEffect(()=>{if(!h)return;const D=setInterval(async()=>{try{const A=await M(),W=k.current.find(le=>le.id===E.current),X=A.find(le=>le.id===E.current),ie=X&&(!W||X.revision>W.revision||W.available!==X.available);Q(A,ie&&!O.current)}catch{}},5e3);return()=>clearInterval(D)},[h,M,Q]),{decks:a,records:a,localNodeId:b,ready:h,saveState:f,error:S,setError:y,saveNow:N,select:async D=>{if(O.current&&!await N(E.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const A=k.current.find(W=>W.id===D);if(!A)throw new Error(`deck inesistente: ${D}`);return O.current=!1,K.current=!0,G(A)},add:async(D,A=null)=>{const W=A===Si?k.current.find(le=>le.local):A?k.current.find(le=>le.ownerId===A):null;if(!W||W.available===!1)throw new Error("nodo owner non disponibile");const X=await kg(e,D,W.ownerRoute),ie=Hs(X,zo(W),W.ownerTopology,W.local,!0);return Q([...k.current,ie],!1),ie},rename:async(D,A)=>{const W=k.current.find(we=>we.id===D);if(!W)throw new Error("deck inesistente");if(D===E.current&&O.current&&!await N(D))throw new Error(`salvataggio di "${W.name}" fallito: rinomina annullata`);const X=k.current.find(we=>we.id===D);if(!X)throw new Error("deck inesistente");const ie=await X0(e,X.name,A,X.revision,X.ownerRoute),le=Hs(ie,zo(X),X.ownerTopology,X.local,!0),fe=X.local?Si:X.ownerId;return pd(hN(xl(),fe,D,le.id)),Q(k.current.map(we=>we.id===D?le:we),!1),le},remove:async D=>{const A=k.current.find(X=>X.id===D);if(!A)throw new Error("deck inesistente");D===E.current&&(O.current=!1),await Q0(e,A.name,A.revision,A.ownerRoute);const W=A.local?Si:A.ownerId;pd(fN(xl(),W,D)),Q(k.current.filter(X=>X.id!==D),!1)},reorder:(D,A)=>{const W=k.current.find(pe=>pe.id===D),X=k.current.find(pe=>pe.id===A);if(!W||!X)return!1;const ie=W.local?Si:W.ownerId,le=X.local?Si:X.ownerId;if(ie!==le)return!1;const fe=k.current.filter(pe=>(pe.local?Si:pe.ownerId)===ie).map(pe=>pe.id),we=pd(dN(xl(),ie,D,A,fe));return Q(qv(k.current,we),!1),!0},addTileTo:async(D,A)=>{const W=k.current.find(pe=>pe.id===D);if(!W)throw new Error("deck inesistente");if(W.available===!1)throw new Error(`nodo owner offline: ${W.ownerLabel}`);const X=Pw(A,R.current,C.current);if(!X)throw new Error("riferimento sessione non valido");const ie=G(W),le=Wv(_y(ie,X),W.ownerId,W.ownerTopology),fe=await ml(e,W.name,le,W.revision,W.ownerRoute),we=Hs(fe,zo(W),W.ownerTopology,W.local,!0);return Q(k.current.map(pe=>pe.id===D?we:pe),!1),we},localMainId:Lr(null,"main"),parseDeckId:sc}}const Yj="/sw.js",Hh="nc-sw-update";let Uh=null,Wh=!1,Kh=null,hb=Object.freeze({needed:!1,kind:null,version:""}),fb=!1;function pb(){typeof window<"u"&&window.dispatchEvent(new Event(Hh))}function mb(){const e=Kh||(Wh?{kind:"reload",version:""}:null);hb=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function $_(){return hb}function Gj(e){return typeof window>"u"?()=>{}:(window.addEventListener(Hh,e),()=>window.removeEventListener(Hh,e))}function Ff(e){Wh!==e&&(Wh=e,mb(),pb())}function Xj(e,t,n){let s=null;e&&t&&e!==t?s={kind:"install",version:e}:t&&n&&t!==n&&(s={kind:"reload",version:t});const o=JSON.stringify(s)===JSON.stringify(Kh);Kh=s,mb(),o||pb()}function Qj(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&Ff(!0)})}function Jj(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{fb&&location.reload()}),navigator.serviceWorker.register(Yj).then(e=>{Uh=e,e.waiting&&Ff(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&Qj(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function Zj(){fb=!0,Ff(!1);const e=Uh&&Uh.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function eR({hash:e="",origin:t="",pathname:n="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${n||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const u=new URLSearchParams(o),h=u.get("token")||"",p=u.get("pair")||"",f=p?`${t||""}${n||""}#pair=${p}`:"";return{token:h,pair:f,nextUrl:a}}const gb=9,vb=24,_b="nc_side_w",H_="nc_side_min",tR=240,U_="(min-width:1024px) and (pointer:fine)";function nR(){const e=Number(localStorage.getItem(_b));return e>=180&&e<=480?e:tR}function iR(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=gb&&e<=vb?e:13}function sR(){const e={token:"",pair:""};try{const{token:t,pair:n,nextUrl:s}=eR({hash:location.hash,origin:location.origin,pathname:location.pathname,search:location.search});if(t){e.token=t;try{localStorage.setItem("nc_token",t)}catch{}}if(n){e.pair=n;try{sessionStorage.setItem("nc_pair",n)}catch{}}if(location.hash)try{history.replaceState(null,"",s)}catch{}}catch{}if(e.token||(e.token=sessionStorage.getItem("nc_token")||localStorage.getItem("nc_token")||""),!e.pair){const t=sessionStorage.getItem("nc_pair");t&&(e.pair=t)}return e}function rR(e){try{return Ys(Bh(e))}catch{return Pr()}}function W_(e){if(!e)return"";const t=Math.floor(Date.now()/1e3)-e;return t<0||t<60?"ora":t<3600?`${Math.floor(t/60)}m`:t<86400?`${Math.floor(t/3600)}h`:`${Math.floor(t/86400)}g`}function oR(){const[e,t]=j.useState(()=>window.matchMedia(U_).matches);return j.useEffect(()=>{const n=window.matchMedia(U_),s=o=>t(o.matches);return n.addEventListener("change",s),()=>n.removeEventListener("change",s)},[]),e}function K_({session:e,node:t,ownerId:n,cellName:s,token:o,readonly:a=!1,onBack:u,onCellSwitcher:h,cellSwitcherOpen:p=!1}){kn();const[f]=kf(),[v,S]=j.useState(!1),[y,b]=j.useState(()=>window.matchMedia("(pointer: coarse)").matches),[w,k]=j.useState(null),[C,R]=j.useState(iR),[E,F]=j.useState(s||e),[O,K]=j.useState(""),Y=$=>R(U=>{const x=Math.max(gb,Math.min(vb,U+$));return localStorage.setItem("nc_fontsize",String(x)),x}),z=j.useRef(()=>{}),Z=j.useRef(()=>!1),G=j.useRef(()=>{}),Q=j.useRef(!1),[B,M]=j.useState(!1),[N,T]=j.useState(!1),L=()=>{Q.current=!Q.current,M(Q.current)};return j.useEffect(()=>{F(s||e)},[s,e]),j.useEffect(()=>{let $=!0;const U=t?t.split("/"):[],x=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function I(){let A=null,W=null;try{const le=await(await Ht(`${x}/sessions`,o)).json();Array.isArray(le.sessions)&&(A=le.sessions.find(fe=>fe.name===e))}catch{}try{const ie=await ws(o,U);ie.available&&Array.isArray(ie.cells)&&(W=ie.cells.find(le=>le.tmuxSession===e))}catch{}if(!$)return;F(Cf({session:e,cell:W||(s?{cell:s}:null)}));let X="";W?X=`${W.engine}${W.key?`·${W.key}`:""}`:A&&(X=A.attached?`attached · ${W_(A.activity)}`:A.activity?W_(A.activity):""),K(X)}I();const D=setInterval(I,4e3);return()=>{$=!1,clearInterval(D)}},[e,t,o]),c.jsxs("div",{className:"nc-app",children:[c.jsxs("header",{className:"nc-bar nc-bar-single",children:[c.jsxs("button",{onClick:u,title:m("sessions"),children:[c.jsx(Ze,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:m("sessions")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:t?`${E} · ${t}`:E,children:E}),O&&c.jsx("small",{className:"nc-bar-sub",children:O})]}),c.jsxs("span",{className:"nc-bar-right",children:[c.jsx("button",{onClick:()=>Y(-1),title:m("zoom-out"),children:c.jsx(Ze,{name:"zoomOut",size:18})}),c.jsx("button",{onClick:()=>Y(1),title:m("zoom-in"),children:c.jsx(Ze,{name:"zoomIn",size:18})}),c.jsx("button",{onClick:()=>b($=>!$),title:m("composer"),children:c.jsx(Ze,{name:"keyboard",size:20})}),c.jsx("button",{onClick:()=>S($=>!$),title:m("files"),children:c.jsx(Ze,{name:"folder",size:20})})]})]}),c.jsx("div",{className:"nc-termwrap",children:c.jsx(mw,{session:e,node:t,token:o,readonly:a,takeSize:!0,sendRef:z,composerRef:Z,actionRef:G,ctrlRef:Q,setCtrlArmed:M,onFiles:k,fontSize:C,selectionMode:N,onSelectionModeChange:T,keyboardGesture:f.terminalKeyboardGesture})}),c.jsx(TE,{onKeyboard:()=>b($=>!$),onCellSwitcher:h,cellSwitcherOpen:p,send:$=>z.current($),action:$=>G.current($),ctrlArmed:B,onCtrl:L,selectionMode:N,onSelectionMode:T,keepKeyboardClosed:f.keybarKeepsKeyboardClosed,showEnter:f.showKeybarEnter,keybarLayout:f.keybarLayout}),y&&c.jsx(Cw,{submitText:$=>Z.current($),token:o,session:e,node:t,ownerId:n,keepKeyboardClosedOnVoice:f.voiceKeepsKeyboardClosed}),v&&c.jsx(gw,{session:e,node:t,token:o,filesEvent:w,onClose:()=>S(!1)})]})}function aR(){kn();const[e]=j.useState(sR),[t,n]=j.useState(e.token),[s,o]=j.useState(e.pair||""),a=j.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[u,h]=j.useState(!1),p=oR(),[f]=j.useState(()=>lN(typeof location<"u"?location.pathname:"/")),[v,S]=j.useState(f.id),y=v===Lr(null,Un),[b,w]=j.useState(null),k=ue=>{const je=ys(ue);w(je?{...je,...typeof(ue==null?void 0:ue.cellName)=="string"&&ue.cellName?{cellName:ue.cellName}:{}}:null)},[C,R]=j.useState([]),[E,F]=j.useState([]),[O,K]=j.useState([]),[Y,z]=j.useState(()=>f.ownerId?Pr():rR(f.name)),[Z,G]=j.useState(null),[Q,B]=j.useState(null),M=ue=>B(ys(ue)),[N,T]=j.useState(null),L=nf(t,p),$=j.useMemo(()=>(L||[]).filter(ue=>ue.instanceId).map(ue=>({instanceId:ue.instanceId,route:ue.route,label:ue.label,status:ue.status})),[L]),U=Vj(t,v,Y,z,$),x=U.decks;j.useEffect(()=>{const ue=new Map;for(const je of L||[]){const qe=(je.route||[je.name]).join("/"),pt=new Set((je.sessions||[]).map(_t=>_t.name));for(const _t of je.cells||[])!_t.cell||!_t.tmuxSession||_t.cell===_t.tmuxSession||pt.has(_t.cell)||ue.set(`${qe}:${_t.cell}`,`${qe}:${_t.tmuxSession}`)}z(je=>Ex(je,ue))},[L]),j.useEffect(()=>{U.localNodeId&&z(ue=>{const je=Oh(ue,U.localNodeId,$);return JSON.stringify(je)===JSON.stringify(ue)?ue:je})},[$,U.localNodeId]);const[I,D]=j.useState(null),[A,W]=j.useState(null),X=j.useRef(0),[ie,le]=j.useState(!1),[fe,we]=j.useState(nR),[pe,_e]=j.useState(!y),[He,ht]=j.useState(()=>y?localStorage.getItem(H_)==="1":!0),[Ye,Ae]=j.useState(!1),[Ne,ut]=j.useState("nodes"),[Pe,Ut]=j.useState(!1),[Rt,Dt]=j.useState(""),[vt,ft]=j.useState(!1),dt=(ue="nodes",je=!1,qe="")=>{ut(ue),Ut(je),Dt(qe),Ae(!0)},[re,xe]=j.useState(!1),[Oe,Fe]=j.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[de,ze]=j.useState(!1);j.useEffect(()=>{try{localStorage.setItem(_b,String(fe))}catch{}},[fe]),j.useEffect(()=>{try{localStorage.setItem(H_,He?"1":"")}catch{}},[He]),j.useEffect(()=>{if(!t)return;let ue=!1;return Promise.all([ey(t),Ht("/api/config",t).then(je=>je.json())]).then(([je,qe])=>{ue||(Fe({deviceDefault:je.deviceName||"",localNodeId:je.nodeId||"",localNameDefault:je.localName||""}),ze(!!qe.readonlyDefault),(je.firstRun===!0&&!qe.readonlyDefault||s)&&xe(!0))}).catch(()=>{}),()=>{ue=!0}},[t,s]);const q=j.useCallback(async()=>{try{const je=await(await Ht("/api/sessions",t)).json();je.error||R(je.sessions||[])}catch{}try{const ue=await ws(t);F(ue.available?ue.cells||[]:[]),K(ue.available?ue.capabilities||[]:[])}catch{F([]),K([])}},[t]);j.useEffect(()=>{if(!p)return;q();const ue=setInterval(q,4e3);return()=>clearInterval(ue)},[p,q]),j.useEffect(()=>{let ue=!1;return Ht("/api/config",t).then(je=>je.json()).then(je=>{ue||Xj(je.version,je.uiVersion,"0.8.52-rc.13")}).catch(()=>{}),()=>{ue=!0}},[t]);const te=new Set([...C.map(ue=>ue.name),...L.flatMap(ue=>ue.sessions.map(je=>je.key))]),H=ki(Y),ae=ue=>z(je=>{const qe=Pw(ue,U.localNodeId,$)||ue,pt=_y(je,qe);return pt===je&&ki(je).length>=9&&U.setError(m("grid-full")),pt}),ce=async(ue,je=[])=>{try{await Xh(t,ue,je)}catch{return}const qe=je.length?`${je.join("/")}:${ue}`:ue;z(pt=>Hl(pt,qe)),q()},me=async(ue,je,qe=[])=>{try{await Z_(t,ue,je,qe)}catch{return}q()},Te=async(ue,je,qe=[])=>{await X_(t,{cell:ue,enabled:!!je},qe),q()},ve=async ue=>{if(!I)return;const{cell:je}=I,qe=Array.isArray(I.route)?I.route:[];if(ue.action==="up"){const _t=await Yh(t,{cell:je,boot:!!ue.boot,...ue.engine?{engine:ue.engine}:{},...ue.model!==void 0?{model:ue.model}:{},...ue.permissionPolicy?{permissionPolicy:ue.permissionPolicy}:{}},qe),bs=uf(_t);bs&&U.setError(bs.text)}else await Il(t,{cell:je,boot:!!ue.boot},qe);const pt=ue.action==="up"?!!ue.boot:ue.boot?!1:!!I.boot;W({id:++X.current,cell:je,route:qe,enabled:pt}),q()},Ce=j.useCallback(ue=>{W(je=>(je==null?void 0:je.id)===ue?null:je)},[]),nt=async ue=>{if(!(!(ue!=null&&ue.direct)||ie)){le(!0);try{await ef(t,ue.name,ue.tunnelStatus==="up"?"down":"up")}finally{le(!1)}}},ot=async(ue,je)=>{const qe=String(je||"").trim();return!(ue!=null&&ue.direct)||!Mr(qe)?!1:(await iy(t,ue.name,qe),!0)},Je=ue=>{const je=x.find(qe=>qe.id===ue);if(!je)return!1;try{const qe=window.open(fd(je,t),"_blank");return qe&&(qe.opener=null),!!qe}catch{return!1}},Mt=async ue=>{if(!ue||ue===v)return;const je=await U.select(ue),qe=U.records.find(pt=>pt.id===ue);S(ue),z(je),G(null),B(null);try{history.replaceState(null,"",fd(qe||ue,null))}catch{}},lt=async(ue,je)=>{const qe=await U.add(ue,je);await Mt(qe.id)},et=async(ue,je)=>{const qe=await U.rename(ue,je);if(ue===v){S(qe.id),z(Oh(qe.layout,U.localNodeId,$)),G(null),B(null);try{history.replaceState(null,"",fd(qe,null))}catch{}}},Zt=async ue=>{await U.remove(ue),ue===v&&await Mt(U.localMainId)},ni=async(ue,je)=>{!je||je===v||(await U.addTileTo(je,ue),z(qe=>Hl(qe,ue)))};if(!t)return c.jsxs("div",{className:"nc-auth",children:[c.jsx("p",{children:m("auth-prompt")}),c.jsx("input",{onChange:ue=>n(ue.target.value.trim()),placeholder:"token"}),c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:u,onChange:ue=>h(ue.target.checked)})," ",m("remember-device")]}),c.jsx("button",{onClick:()=>{(u?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Wn=c.jsxs(c.Fragment,{children:[Ye&&c.jsx(Rj,{token:t,initialTab:Ne,initialLocation:Rt,startNewCell:Pe,onClose:()=>{Ae(!1),Ut(!1),Dt("")}}),re&&c.jsx(Pj,{token:t,initialPair:s,...Oe,onPairDone:a,onDone:()=>xe(!1)}),c.jsx(Ij,{token:t})]});if(!p)return N?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-single-overlay",children:c.jsx(F_,{peer:N,token:t,onBack:()=>T(null)})}),Wn]}):b?c.jsxs(c.Fragment,{children:[c.jsx(K_,{session:b.session,node:b.node,ownerId:b.ownerId,cellName:b.cellName,token:t,readonly:de,onBack:()=>w(null),onCellSwitcher:()=>ft(!0),cellSwitcherOpen:vt}),vt&&c.jsx(Hj,{token:t,current:b,onPick:ue=>{k(ue),ft(!1)},onClose:()=>ft(!1)}),Wn]}):c.jsxs(c.Fragment,{children:[c.jsx(Wx,{onPick:k,token:t,onSettings:dt,onOpenVlSession:T}),Wn]});const ui=y||!pe;return c.jsxs("div",{className:"nc-workspace",children:[ui&&c.jsx(XE,{sessions:C,cells:E,activeSessions:H,nodeGroups:L,fleetCapabilities:O,bootSettlement:A,onBootSettlementApplied:Ce,localNodeId:U.localNodeId,onPick:M,onAddTile:ae,onPower:D,onBoot:Te,onBootError:ue=>U.setError(String((ue==null?void 0:ue.message)||ue)),onNodePower:nt,onNodeRename:ot,onKill:ce,onVisibility:me,onNew:()=>dt("fleet",!0),onSettings:dt,onOpenVlSession:T,width:fe,collapsed:He,onResize:we,onToggleCollapse:()=>ht(ue=>!ue)}),c.jsxs("div",{className:"nc-workspace-main",children:[c.jsx(LN,{decks:x,currentDeck:v,onCreate:lt,onRename:et,onDelete:Zt,onReorder:U.reorder,onOpenWindow:Je,onNavigate:Mt,saveState:U.saveState,error:U.error,sidebarVisible:ui,onToggleSidebar:y?null:()=>_e(ue=>!ue)}),c.jsx(iN,{layout:Y,onLayoutChange:z,token:t,readonly:de,sessionsAlive:te,focusSession:Z,onFocus:G,onOpenSingle:M,decks:x,currentDeck:v,onSendToDeck:ni,cells:E,nodeGroups:L})]}),N&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(F_,{peer:N,token:t,onBack:()=>T(null)})}),Q&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(K_,{session:Q.session,node:Q.node,ownerId:Q.ownerId,cellName:Cf({session:Q.session,node:Q.node,ownerId:Q.ownerId,cells:E,nodeGroups:L}),token:t,readonly:de,onBack:()=>B(null)})}),I&&c.jsx(Bl,{cell:I,token:t,route:Array.isArray(I.route)?I.route:[],onConfirm:ve,onClose:()=>D(null)}),Wn]})}function lR(){kn();const e=j.useSyncExternalStore(Gj,$_,$_);if(!e.needed)return null;const t=e.kind==="install"?m("install-version-mismatch").replace("{v}",e.version):m("update-available").replace("{v}",e.version).replace(" "," ").trim();return c.jsxs("div",{className:"nc-update",role:"status","aria-live":"polite",children:[c.jsx("span",{className:"nc-update-msg",children:t}),e.kind==="reload"&&c.jsx("button",{className:"nc-update-btn",onClick:Zj,children:m("reload")})]})}Jj();h0.createRoot(document.getElementById("root")).render(c.jsxs(r0.StrictMode,{children:[c.jsx(aR,{}),c.jsx(lR,{})]}));
93
+ `],{type:"application/json"}),$=URL.createObjectURL(L),U=document.createElement("a");U.href=$,U.download="nexuscrew-diagnostics.json",U.click(),URL.revokeObjectURL($)}catch{y(m("diagnostics-export-failed"))}};return c.jsxs("div",{className:"nc-set-tab nc-diagnostics",children:[c.jsxs("label",{className:"nc-field",children:[m("diagnostics-target"),c.jsx("select",{value:u.key,onChange:L=>a(L.target.value),children:s.map(L=>c.jsxs("option",{value:L.key,children:[L.label,L.status!=="up"?` · ${L.status}`:""]},L.key))})]}),c.jsxs("div",{className:"nc-set-form nc-diag-controls",children:[c.jsx("div",{className:"nc-sheet-label",children:m("diagnostics-verbose")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("select",{"aria-label":m("diagnostics-duration"),value:R,onChange:L=>E(Number(L.target.value)),disabled:n,children:[300,900,1800,3600].map(L=>c.jsxs("option",{value:L,children:[L/60," min"]},L))}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||(h==null?void 0:h.verbose)===!0,onClick:()=>M(()=>xg(e,!0,R,u.route)),children:m("enable")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||(h==null?void 0:h.verbose)!==!0,onClick:()=>M(()=>xg(e,!1,R,u.route)),children:m("stop")})]}),c.jsx("small",{className:"nc-set-hint",children:h!=null&&h.verbose?m("diagnostics-expires").replace("{time}",new Date(h.expiresAt).toLocaleTimeString()):m("diagnostics-off")})]}),c.jsxs("div",{className:"nc-diag-filters",children:[c.jsxs("select",{"aria-label":m("diagnostics-level"),value:F,onChange:L=>O(L.target.value),children:[c.jsx("option",{value:"all",children:m("diagnostics-all-levels")}),["debug","info","warn","error"].map(L=>c.jsx("option",{value:L,children:L},L))]}),c.jsx("input",{"aria-label":m("diagnostics-component"),placeholder:m("diagnostics-component"),value:K,onChange:L=>Y(L.target.value)})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:b,onChange:L=>w(L.target.checked)})," ",m("diagnostics-pause")]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:k,onChange:L=>C(L.target.checked)})," ",m("diagnostics-autoscroll")]})]}),c.jsx("pre",{ref:Z,className:"nc-diag-log","aria-label":m("diagnostics-log"),children:B||m("diagnostics-empty")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:N,children:m("copy")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:T,children:m("diagnostics-export")}),c.jsx("button",{type:"button",className:"nc-btn danger",disabled:n,onClick:()=>{window.confirm(m("diagnostics-clear-confirm"))&&M(async()=>{const L=await q0(e,u.route);return z.current=0,v([]),L})},children:m("diagnostics-clear")})]}),S&&c.jsx("div",{className:"nc-err",children:S})]})}function jj({token:e,settings:t,readonly:n,refresh:s,roster:o,section:a,setSection:u}){const[h,p]=j.useState(null),[f,v]=j.useState(null),[S,y]=j.useState(!1),[b,w]=j.useState(!1),[k,C]=j.useState(null),[R,E]=j.useState(!0),[F,O]=j.useState(!1);j.useEffect(()=>{C(t&&t.update||null),E(!t||t.autoUpdate!==!1),O(!!(t&&t.alternateScreen))},[t]);const K=async()=>{p(null),v(null),w(!0);try{const N=await C0(e);v(N.note||"ok"),y(!1)}catch(N){p(String(N.message||N))}w(!1)},Y=async()=>{p(null),v(null),w(!0);try{const N=await $0(e);v(`${N.note||"ok"}${N.target?` (${N.target})`:""}`)}catch(N){p(String(N.message||N))}w(!1)},z=async N=>{p(null),v(null),w(!0);try{await Ud(e,{autoUpdate:N}),E(N),s&&await s()}catch(T){p(String(T.message||T))}w(!1)},Z=async N=>{p(null),v(null),w(!0);try{await Ud(e,{alternateScreen:N}),O(N),s&&await s()}catch(T){p(String(T.message||T))}w(!1)},G=async()=>{p(null),v(null),w(!0);try{C(await H0(e))}catch(N){p(String(N.message||N))}w(!1)},Q=async()=>{p(null),v(null),w(!0);try{const N=await U0(e);C(N),v(m("npm-update-restarting"))}catch(N){p(String(N.message||N))}w(!1)},B=()=>{window.confirm(m("composer-clear-confirm"))&&(p(null),WE()?(window.dispatchEvent(new Event(Dh)),v(m("composer-clear-done"))):p(m("composer-clear-failed")))},M=t&&t.service;return c.jsxs("div",{className:"nc-set-tab",children:[c.jsx("div",{className:"nc-system-tabs",role:"tablist","aria-label":m("tab-system"),children:["general","diagnostics"].map(N=>c.jsx("button",{type:"button",role:"tab","aria-selected":a===N,className:`nc-set-tabbtn${a===N?" on":""}`,onClick:()=>u(N),children:m(`system-${N}`)},N))}),a==="general"&&c.jsxs(c.Fragment,{children:[c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-this-node")}),t&&c.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,c.jsx("br",{}),M&&M.installed?m("service-installed"):m("service-missing"),M&&M.installed?` · ${M.active?m("service-active"):m("service-inactive")}`:"",M?` · boot ${M.boot?"on":"off"}`:""]}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||b,title:n?m("settings-readonly"):"",onClick:Y,children:m("service-regenerate")})}),c.jsx(xj,{token:e,readonly:n}),c.jsx(kj,{})]}),c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-updates")}),c.jsxs("div",{className:"nc-set-form nc-update-settings",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:R,disabled:n||b||k&&!k.supported,onChange:N=>z(N.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:m("npm-auto-update")}),c.jsx("small",{children:m("npm-auto-update-help")})]})]}),k&&c.jsxs("div",{className:"nc-set-info",children:[m("npm-update-current")," v",k.current,k.latest?` · ${m("npm-update-latest")} v${k.latest}`:""," · ",m(`npm-update-${k.supported?k.phase:"unsupported"}`)]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||b||!k||!k.supported,onClick:G,children:m("npm-update-check")}),k&&k.available&&c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||b||k.phase==="installing",onClick:Q,children:m("npm-update-install")})]}),k&&k.lastError&&c.jsx("div",{className:"nc-err",children:k.lastError})]})]}),c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-security")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||b,title:n?m("settings-readonly"):"",onClick:()=>{p(null),v(null),y(!0)},children:m("token-rotate")})})]}),c.jsxs("section",{className:"nc-system-group",children:[c.jsx("h3",{children:m("system-local-data")}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:m("composer-clear-data")}),c.jsx("small",{className:"nc-set-hint",children:m("composer-clear-data-help")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:B,children:m("composer-clear-data")})})]})]})]}),a==="diagnostics"&&c.jsxs("section",{className:"nc-system-group nc-system-diagnostics",children:[c.jsx("h3",{children:m("system-diagnostics")}),c.jsxs("div",{className:"nc-set-form nc-alternate-screen-settings",children:[c.jsx("div",{className:"nc-sheet-label",children:m("system-terminal")}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox","aria-label":m("alternate-screen"),checked:F,disabled:n||b,onChange:N=>Z(N.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:m("alternate-screen")}),c.jsx("small",{children:m("alternate-screen-help")})]})]})]}),c.jsx(Nj,{token:e,roster:o,readonly:n})]}),S&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("div",{children:m("token-rotate-explain")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:b,onClick:()=>y(!1),children:m("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:b,onClick:K,children:m("confirm")})]})]}),f&&c.jsx("div",{className:"nc-set-note",children:f}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function Rj({token:e,onClose:t,initialTab:n="nodes",initialLocation:s="",startNewCell:o=!1}){kn();const a=n==="diagnostics"?"diagnostics":"general",[u,h]=j.useState(n==="diagnostics"?"system":n),p=j.useRef(null),f=j.useRef(null),[v,S]=j.useState({start:!1,end:!1}),[y,b]=j.useState(a),[w,k]=j.useState(null),[C,R]=j.useState([]),[E,F]=j.useState([]),[O,K]=j.useState(!1),[Y,z]=j.useState(null),[Z,G]=j.useState(0),Q=nf(e,!0,Z),B=j.useCallback(()=>{const N=p.current;if(!N)return;const T=N.scrollWidth>N.clientWidth+1,L={start:T&&N.scrollLeft>1,end:T&&N.scrollLeft+N.clientWidth<N.scrollWidth-1};S($=>$.start===L.start&&$.end===L.end?$:L)},[]);j.useEffect(()=>{const N=p.current;if(!N)return;const T=typeof ResizeObserver=="function"?new ResizeObserver(B):null;return T==null||T.observe(N),window.addEventListener("resize",B),B(),()=>{T==null||T.disconnect(),window.removeEventListener("resize",B)}},[B]),j.useEffect(()=>{var T,L;(L=(T=f.current)==null?void 0:T.scrollIntoView)==null||L.call(T,{behavior:"smooth",block:"nearest",inline:"nearest"});const N=window.requestAnimationFrame?window.requestAnimationFrame(B):setTimeout(B,0);return()=>{window.cancelAnimationFrame?window.cancelAnimationFrame(N):clearTimeout(N)}},[u,B]);const M=j.useCallback(async()=>{var N;try{const T=await ey(e);k(T),z(null)}catch(T){z(String(T.message||T))}try{const L=(await x0(e)).peers||[];let $=[],U=[];try{const[x,I]=await Promise.all([Ht("/api/config",e).then(X=>X.json()).catch(()=>null),ny(e).catch(()=>null)]),D=x&&typeof x.instanceId=="string"?x.instanceId:"",A=[{instanceId:D||null,route:[],label:null},...dy(I,D)],W=await Promise.all(A.map(async X=>{try{return{ok:!0,peers:((await ty(e,X.route)).nodes||[]).map(fe=>uy(fe,X)).filter(Boolean)}}catch(ie){return{ok:!1,owner:X,error:ie}}}));for(const X of W){if(X.ok){$.push(...X.peers);continue}X.owner.route.length!==0&&U.push({instanceId:X.owner.instanceId,label:X.owner.label,route:X.owner.route,failure:/timeout/i.test(String(((N=X.error)==null?void 0:N.message)||X.error))?"timeout":"unreachable"})}}catch{}F(U),R([...L,...$])}catch(T){z(String(T.message||T))}},[e]);return j.useEffect(()=>{M();const N=setInterval(M,5e3);return()=>clearInterval(N)},[M]),j.useEffect(()=>{let N=!1;return Ht("/api/config",e).then(T=>T.json()).then(T=>{N||K(!!T.readonlyDefault)}).catch(()=>{}),()=>{N=!0}},[e]),c.jsx("div",{className:"nc-set-overlay",onClick:t,children:c.jsxs("div",{className:"nc-set-panel",onClick:N=>N.stopPropagation(),children:[c.jsxs("div",{className:"nc-set-head",children:[c.jsx(Ze,{name:"gear",size:18}),c.jsx("b",{children:m("settings")}),c.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:m("close"),children:c.jsx(Ze,{name:"x",size:18})})]}),O&&c.jsx("div",{className:"nc-set-readonly",children:m("settings-readonly")}),Y&&c.jsx("div",{className:"nc-err",children:Y}),c.jsx("div",{className:`nc-set-tabs-wrap${v.start?" has-start-overflow":""}${v.end?" has-end-overflow":""}`,children:c.jsx("div",{className:"nc-set-tabs",ref:p,onScroll:B,children:["nodes","fleet","audio","input","system"].map(N=>c.jsx("button",{ref:u===N?f:null,type:"button",className:`nc-set-tabbtn${u===N?" on":""}`,onClick:()=>h(N),children:m(`tab-${N}`)},N))})}),c.jsxs("div",{className:"nc-set-body",children:[u==="nodes"&&c.jsx(Sj,{token:e,nodes:C,roster:Q,settings:w,readonly:O,vlUnavailable:E,refresh:M,refreshAliases:()=>G(N=>N+1)}),u==="fleet"&&c.jsx(ij,{token:e,readonly:O,startNewCell:o,initialLocation:s,targets:Q.map(N=>({route:N.route,label:N.label||N.name,status:N.status}))}),u==="audio"&&c.jsx(Cj,{token:e,readonly:O,nodes:C,settings:w}),u==="input"&&c.jsx(Ej,{}),u==="system"&&c.jsx(jj,{token:e,settings:w,readonly:O,refresh:M,roster:Q,section:y,setSection:b})]})]})})}function Pj({token:e,initialPair:t,deviceDefault:n="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:u}){kn();const[h,p]=j.useState(t?"pair":"welcome"),[f,v]=j.useState(!1),[S,y]=j.useState(null),b=async()=>{v(!0),y(null);try{await Ud(e,{wizardDone:!0}),a&&a(),u()}catch(w){y(String(w.message||w)),v(!1)}};return c.jsx("div",{className:"nc-wiz-overlay",children:c.jsxs("div",{className:"nc-wiz",children:[c.jsxs("div",{className:"nc-wiz-head",children:[c.jsx("b",{children:m("wizard-title")}),c.jsx("small",{children:m("hydra-simple")})]}),h==="welcome"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:m("local-ready")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:b,children:m("local-only")}),c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:()=>p("pair"),children:m("add-node")})]})]}),h==="pair"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx(Yw,{token:e,initial:t||"",autoStart:!!t,deviceDefault:n,localNodeId:s,localNameDefault:o,onBusyChange:v,onSuccess:async()=>{a&&a(),p("done")}}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:()=>{a&&a(),p("welcome")},children:m("back")})})]}),h==="done"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:m("node-connected")}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:b,children:m("finish")})})]}),S&&c.jsx("div",{className:"nc-err",children:S})]})})}function Lj(e,t){if(typeof EventSource>"u"||!e)return()=>{};const n=new EventSource(`/api/events?token=${encodeURIComponent(e)}`);return n.onmessage=s=>{try{const o=JSON.parse(s.data);o&&typeof o=="object"&&typeof o.type=="string"&&t(o)}catch{}},()=>{try{n.close()}catch{}}}const Aj=6e3,Tj=12e3;function Dj({n:e,onClose:t}){return c.jsxs("div",{className:`nc-ntf-toast${e.urgency==="high"?" high":""}`,role:"status","aria-live":"polite",children:[c.jsxs("div",{className:"nc-ntf-toast-txt",children:[c.jsx("b",{children:e.title}),e.body&&c.jsx("small",{children:e.body}),e.session&&c.jsx("span",{className:"nc-ntf-from",children:e.session})]}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:t,title:m("close"),children:c.jsx(Ze,{name:"x",size:14})})]})}function Mj({ask:e,token:t,onAnswered:n,onDismiss:s}){const[o,a]=j.useState(""),[u,h]=j.useState(!1),[p,f]=j.useState(null),[v,S]=j.useState(!1),y=async w=>{const k=String(w||"").trim();if(!(!k||u)){f(null),h(!0);try{await Y0(t,e.id,k),n(e.id)}catch(C){f(String(C.message||C))}h(!1)}},b=async()=>{if(!v){S(!0);try{await G0(t,e.id),s(e.id)}catch{S(!1)}}};return c.jsxs("div",{className:"nc-ask-card",children:[c.jsxs("div",{className:"nc-ask-head",children:[c.jsx("span",{className:"nc-ntf-from",children:e.session}),c.jsxs("code",{className:"nc-ask-id",children:["#",e.id]}),c.jsx("button",{type:"button",className:"nc-ask-dismiss",title:m("ask-dismiss"),"aria-label":m("ask-dismiss"),disabled:v,onClick:b,children:c.jsx(Ze,{name:"x",size:14})})]}),c.jsx("div",{className:"nc-ask-q",children:e.question}),Array.isArray(e.options)&&e.options.length>0&&c.jsx("div",{className:"nc-ask-opts",children:e.options.map(w=>c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:u,onClick:()=>y(w),children:w},w))}),c.jsxs("div",{className:"nc-ask-reply",children:[c.jsx("textarea",{rows:2,placeholder:m("ask-reply-ph"),value:o,disabled:u,onChange:w=>a(w.target.value)}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:u||!o.trim(),onClick:()=>y(o),children:m("send")})]}),p&&c.jsx("div",{className:"nc-err",children:p})]})}function Ij({token:e}){const[t]=kn(),[n]=ub(),[s,o]=j.useState([]),[a,u]=j.useState([]),[h,p]=j.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),f=j.useRef(0),v=j.useRef(null);v.current||(v.current=yj());const S=j.useRef({enabled:n,lang:t});S.current={enabled:n,lang:t};const y=j.useCallback(C=>{o(R=>R.filter(E=>E.key!==C))},[]),b=j.useCallback(C=>{const R=`t${f.current+=1}`;o(F=>[...F.slice(-3),{...C,key:R}]),setTimeout(()=>y(R),C.urgency==="high"?Tj:Aj);const E=S.current;if(E.enabled)try{v.current.enqueue(C,pj(C,E.lang))}catch{}},[y]);j.useEffect(()=>{n||v.current.stop()},[n]),j.useEffect(()=>{const C=()=>{(document.visibilityState!=="visible"||!document.hasFocus())&&v.current.stop()},R=()=>v.current.stop();return document.addEventListener("visibilitychange",C),window.addEventListener("blur",C),window.addEventListener(Fh,R),()=>{document.removeEventListener("visibilitychange",C),window.removeEventListener("blur",C),window.removeEventListener(Fh,R),typeof v.current.dispose=="function"?v.current.dispose():v.current.stop()}},[]),j.useEffect(()=>{if(!e)return;let C=!1;V0(e).then(E=>{C||u(E.asks||[])}).catch(()=>{});const R=Lj(e,E=>{E.type==="notify"?b(E):E.type==="ask"&&E.ask&&E.ask.id?u(F=>F.some(O=>O.id===E.ask.id)?F:[...F,E.ask]):E.type==="ask-answered"&&E.id?u(F=>F.filter(O=>O.id!==E.id)):E.type==="ask-dismissed"&&E.id&&u(F=>F.filter(O=>O.id!==E.id))});return()=>{C=!0,R()}},[e,b]);const w=C=>u(R=>R.filter(E=>E.id!==C)),k=C=>u(R=>R.filter(E=>E.id!==C));return c.jsxs(c.Fragment,{children:[s.length>0&&c.jsx("div",{className:"nc-ntf-toasts",children:s.map(C=>c.jsx(Dj,{n:C,onClose:()=>y(C.key)},C.key))}),a.length>0&&!h&&c.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>p(!0),title:m("asks-title"),children:["? ",c.jsx("span",{className:"nc-ask-count",children:a.length})]}),a.length>0&&h&&c.jsxs("div",{className:"nc-ask-panel",children:[c.jsxs("div",{className:"nc-ask-panel-head",children:[c.jsx("b",{children:m("asks-title")}),c.jsx("span",{className:"nc-ask-count",children:a.length}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>p(!1),title:m("close"),children:c.jsx(Ze,{name:"x",size:16})})]}),c.jsx("div",{className:"nc-ask-panel-body",children:a.map(C=>c.jsx(Mj,{ask:C,token:e,onAnswered:w,onDismiss:k},C.id))})]})]})}const Oj=4e3;async function Bj(e){const t=await Ht("/api/sessions",e);if((t==null?void 0:t.ok)===!1)throw new Error(`HTTP ${t.status}`);const n=await t.json();if(!Array.isArray(n==null?void 0:n.sessions))throw new Error("invalid sessions payload");return n.sessions}async function Fd(e,t=[]){var h;const[n,s]=await Promise.allSettled([t.length?Jh(e,t):Bj(e),ws(e,t)]),o=n.status==="fulfilled"&&Array.isArray((h=n.value)==null?void 0:h.sessions)?n.value.sessions:Array.isArray(n.value)?n.value:null,a=s.status==="fulfilled"?s.value:null,u=(a==null?void 0:a.available)===!0&&Array.isArray(a.cells)?a.cells:null;return{sessions:o,cells:u,fresh:Array.isArray(o)&&Array.isArray(u)}}function db(e,t,n){return n===!0&&(e==null?void 0:e.degraded)!==!0&&(e==null?void 0:e.active)===!0&&e.tmux!==!1&&!!e.tmuxSession&&(t||[]).some(s=>(s==null?void 0:s.name)===e.tmuxSession)}function zj(e){const t=new Map((e.sessions||[]).map(o=>[o.name,o])),n=[],s=(o,a,u,h=[],p="")=>{const f=new Map((a||[]).map(v=>[v.name,v]));for(const v of o||[]){const S=f.get(v.tmuxSession)||{},y=of(v,S),b=db(v,a,u);n.push({key:Go(h,v.tmuxSession||v.cell),session:v.tmuxSession,route:h,cellName:v.cell,label:v.cell,node:h.length?h.join("/"):"",nodeLabel:p,live:b,selectable:b,verified:u===!0,working:y.working,degraded:!!v.degraded,active:v.active===!0,activity:S.activity||v.activity||0,subtitle:y.subtitle})}};s(e.cells,[...t.values()],e.localFresh===!0);for(const o of e.nodeGroups||[]){const a=Array.isArray(o.route)?o.route:[];o.kind==="vl"||!a.length||s(o.cells,o.sessions,o.switcherFresh===!0,a,o.label||o.name||"")}return n}function Fj(e){const t=new Map,n=Array.isArray(e.sessions)?e.sessions:[],s=Array.isArray(e.cells)?e.cells:[],o=new Map(n.map(u=>[u.name,u])),a=new Set(s.map(u=>u.tmuxSession).filter(Boolean));t.set("local",af(s,n.filter(u=>!a.has(u.name)),o));for(const u of e.nodeGroups||[]){const h=Array.isArray(u.route)?u.route:[];if(u.kind==="vl"||!h.length)continue;const p=Array.isArray(u.cells)?u.cells:[],f=Array.isArray(u.sessions)?u.sessions:[],v=new Set(p.map(S=>S.tmuxSession).filter(Boolean));t.set(h.join("/"),Fl({...u,route:h,cells:p,sessions:f,unmanaged:f.filter(S=>!v.has(S.name))}).rawItems)}return t}function $j(e,t,n,s){return[...new Set(e.map(a=>a.node||"local"))].flatMap(a=>{const u=e.filter(v=>(v.node||"local")===a),h=new Map((t.get(a)||[]).map(v=>[v.key,v])),p=new Map(u.map(v=>[v.key,v])),f=u.map(v=>{const S=h.get(v.key);return{...v,label:(S==null?void 0:S.label)||v.label,live:(S==null?void 0:S.live)??v.live,fresh:(S==null?void 0:S.fresh)===!0,activity:(S==null?void 0:S.activity)??v.activity}});return gs(f,n,"all",Fi(s,a)).map(v=>p.get(v.key))})}function Hj({token:e,current:t,onPick:n,onClose:s}){const[o,a]=j.useState(Ig),[u,h]=j.useState(!1),[p,f]=j.useState(!1),[v,S]=j.useState(""),[y,b]=j.useState(""),[w,k]=j.useState(""),C=j.useRef(null),R=j.useRef(null),E=j.useMemo(()=>zj(o),[o]),F=j.useMemo(()=>Fj(o),[o]),{pins:O,orders:K,canMoveRoster:Y,moveRoster:z,stepRoster:Z}=rf(),G=j.useMemo(()=>$j(E,F,O,K),[E,F,O,K]),Q=j.useMemo(()=>u?G:G.filter(L=>L.selectable||L.degraded&&L.active),[G,u]),B=j.useMemo(()=>E.find(L=>L.key===y&&L.selectable),[E,y]);j.useEffect(()=>{var U;const L=document.activeElement;(U=R.current)==null||U.focus();const $=x=>{x.key==="Escape"&&s()};return document.addEventListener("keydown",$),()=>{var x;document.removeEventListener("keydown",$),(x=L==null?void 0:L.focus)==null||x.call(L)}},[s]),j.useEffect(()=>{let L=!0,$=!1;const U=async()=>{if($)return;$=!0;const I=Ig(),D=Array.isArray(I.nodeGroups)?I.nodeGroups:[],A=Fd(e),W=pe=>pe.kind!=="vl"&&Array.isArray(pe.route)&&pe.route.length>0,X=await Promise.all(D.filter(W).map(async pe=>({group:pe,result:await Fd(e,pe.route)}))),ie=await A;if(!L)return;const le=new Map(X.map(({group:pe,result:_e})=>[JSON.stringify(pe.route||[]),_e])),fe=D.map(pe=>{if(!W(pe))return{...pe,switcherFresh:!1};const _e=le.get(JSON.stringify(pe.route||[]));return _e?{...pe,sessions:_e.sessions||pe.sessions||[],cells:_e.cells||pe.cells||[],switcherFresh:_e.fresh}:{...pe,switcherFresh:!1}}),we=by({...I,sessions:ie.sessions||I.sessions||[],cells:ie.cells||I.cells||[],localFresh:ie.fresh,nodeGroups:fe,refreshedAt:Date.now()});a(we),f(!0),$=!1};U();const x=setInterval(U,Oj);return()=>{L=!1,clearInterval(x)}},[e]);const M=L=>L.degraded?m("cell-degraded"):L.verified?L.selectable?L.working?m("cell-working"):m("cell-idle"):m("cell-off"):m("cell-switcher-not-confirmed"),N=L=>{if(S(""),!L.selectable){S(m("cell-switcher-not-active"));return}b(L.key)},T=async()=>{const L=B;if(!L){S(m("cell-switcher-select"));return}S(""),k(L.key);try{const $=await Fd(e,L.route),U=($.cells||[]).find(x=>(x==null?void 0:x.cell)===L.cellName&&(x==null?void 0:x.tmuxSession)===L.session);if(!db(U,$.sessions,$.fresh)){S(m("cell-switcher-not-active"));return}n({session:L.session,...L.node?{node:L.node}:{},cellName:L.cellName}),s()}catch{S(m("cell-switcher-not-active"))}finally{k("")}};return c.jsx("div",{className:"nc-cell-switcher-backdrop",onClick:s,children:c.jsxs("aside",{ref:C,className:"nc-cell-switcher",role:"dialog","aria-label":m("fleet-cells"),tabIndex:-1,onClick:L=>L.stopPropagation(),children:[c.jsxs("div",{className:"nc-cell-switcher-list",children:[!p&&c.jsx("div",{className:"nc-empty",role:"status",children:m("cell-switcher-refreshing")}),p&&Q.length===0&&c.jsx("div",{className:"nc-empty",role:"status",children:m("cell-switcher-empty-active")}),Q.map(L=>{const $=(t==null?void 0:t.session)===L.session&&((t==null?void 0:t.node)||"")===L.node,U=y===L.key,x=M(L),I=L.node||"local",D=F.get(I)||E.filter(A=>(A.node||"local")===I);return c.jsxs("div",{className:`nc-cell-switcher-row${$?" current":""}${U?" selected":""}${L.selectable?"":" off"}`,"data-roster-key":L.key,"data-position":I,children:[c.jsx(bi,{position:I,itemKey:L.key,label:L.cellName,canMove:Y,onMove:(A,W)=>z(I,A,W,D),onStep:A=>Z(I,L.key,A,D)}),c.jsxs("button",{type:"button",className:"nc-cell-switcher-row-select","aria-current":$?"true":void 0,"aria-pressed":U,"aria-disabled":!L.selectable,disabled:w===L.key,onClick:()=>N(L),children:[c.jsx("span",{className:`nc-cell-switcher-dot${L.degraded?" warn":L.live?` on${L.working?" working":""}`:""}`}),c.jsxs("span",{className:"nc-cell-switcher-copy",children:[c.jsx("b",{children:L.cellName}),c.jsx("small",{className:"nc-cell-switcher-state",children:x}),c.jsx("small",{children:[L.nodeLabel,L.subtitle].filter(Boolean).join(" · ")})]})]})]},L.key)})]}),v&&c.jsx("div",{className:"nc-cell-switcher-notice",role:"status",children:v}),c.jsx("div",{className:"nc-cell-switcher-actions",children:c.jsx("button",{type:"button",className:"nc-cell-switcher-open",disabled:!B||!!w,onClick:T,children:B?`${m("cell-switcher-open")}: ${B.cellName}`:m("cell-switcher-select")})}),c.jsxs("div",{className:"nc-cell-switcher-controls",children:[c.jsx("b",{children:m("fleet-cells")}),c.jsx("button",{type:"button",className:"nc-cell-switcher-filter","aria-pressed":u,onClick:()=>{S(""),h(L=>!L)},children:m(u?"cell-switcher-show-active":"cell-switcher-show-all")}),c.jsx("button",{ref:R,type:"button",className:"nc-cell-switcher-close","aria-label":m("cell-switcher-close"),title:m("cell-switcher-close"),onClick:s,children:"×"})]})]})})}const Uj=2e3;function Wj(e){if(e.kind==="gap")return`${m("vl-events-gap")} ${e.count||"?"}`;if(e.kind==="truncate")return m("vl-events-truncated");if(e.kind==="turn_end")return m("vl-events-turn-end");if(e.kind==="done")return m("vl-events-done");if(e.kind==="writer_epoch")return m("vl-events-writer-epoch");if(e.kind==="tool_start")return`${m("vl-events-tool")} ${e.name||""}`.trim();if(e.kind==="tool_end"){const t=e.isError?m("vl-events-tool-failed"):m("vl-events-tool-ok");return`${m("vl-events-tool")} ${e.name||""} — ${t}`.trim()}return e.kind==="usage"?e.text||m("vl-events-usage"):e.text||""}function Kj({token:e,nodeId:t,route:n=[]}){const[s,o]=j.useState([]),[a,u]=j.useState(!1),h=j.useRef(0),p=j.useRef(!1);return j.useEffect(()=>{let f=!0;h.current=0,p.current=!1,o([]);const v=async()=>{if(!p.current){p.current=!0;try{const b=await k0(e,t,h.current,n);if(!f)return;u(!1);const k=(Array.isArray(b==null?void 0:b.events)?b.events:[]).filter(C=>Number.isSafeInteger(C==null?void 0:C.seq)&&C.seq>h.current);k.length&&(o(C=>C.concat(k).slice(-500)),h.current=k[k.length-1].seq),Number.isSafeInteger(b==null?void 0:b.cursor)&&b.cursor>h.current&&(h.current=b.cursor)}catch{f&&u(!0)}finally{p.current=!1}}},S=()=>{v().catch(()=>{})};S();const y=setInterval(S,Uj);return()=>{f=!1,clearInterval(y)}},[e,t,JSON.stringify(n)]),c.jsxs("div",{className:"nc-vl-events",children:[a&&c.jsx("div",{className:"nc-set-hint nc-vl-events-stale",children:m("vl-events-stale")}),s.length===0&&!a&&c.jsx("small",{className:"nc-set-hint","data-testid":"vl-events-empty",children:m("vl-events-empty")}),s.length>0&&c.jsx("ol",{className:"nc-vl-events-list",children:s.map(f=>c.jsx("li",{"data-testid":`vl-ev-${f.seq}`,className:`nc-vl-ev nc-vl-ev-${f.kind}${f.isError?" error":""}${f.kind==="gap"?" gap":""}`,children:Wj(f)},`${f.seq}-${f.kind}`))})]})}function F_({peer:e,token:t,onBack:n}){var s;return kn(),e?c.jsxs("section",{className:"nc-vl-session-view",children:[c.jsxs("header",{className:"nc-bar nc-bar-single",children:[c.jsxs("button",{onClick:n,title:m("back"),children:[c.jsx(Ze,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:m("back")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:e.label,children:e.label}),((s=e.session)==null?void 0:s.profile)&&c.jsxs("small",{className:"nc-bar-sub",children:[e.session.profile," · ",m("vl-events-title")]})]}),c.jsx("span",{className:"nc-bar-right"})]}),c.jsx("div",{className:"nc-vl-session-body",children:c.jsx(Kj,{token:t,nodeId:e.nodeId,route:e.route||[]})})]}):null}const $d=e=>ki(Ys(e)).length===0,Hd=e=>Array.isArray(e)?e.join("/"):"";function qj(e){const t=new Set,n=[];for(const s of Array.isArray(e)?e:[])!s||!Hn.test(String(s.instanceId||""))||!Array.isArray(s.route)||!s.route.length||t.has(s.instanceId)||(t.add(s.instanceId),n.push({instanceId:s.instanceId,route:[...s.route],label:String(s.label||s.name||s.route.join(" › ")),status:s.status||"offline"}));return n.sort((s,o)=>s.label.localeCompare(o.label))}function zo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function Hs(e,t,n,s=!1,o=!0){const a=t.instanceId;return{...e,id:Lr(s?null:a,e.name),ownerId:a,ownerRoute:s?[]:[...t.route],ownerLabel:s?"Local":t.label,local:s,available:o,ownerTopology:Array.isArray(n)?n:[],layout:Ih(e.layout,a,n)}}function Vj(e,t,n,s,o=[]){const[a,u]=j.useState([]),[h,p]=j.useState(!1),[f,v]=j.useState("idle"),[S,y]=j.useState(""),[b,w]=j.useState(""),k=j.useRef([]),C=j.useRef([]),R=j.useRef(""),E=j.useRef(t),F=j.useRef(n),O=j.useRef(!1),K=j.useRef(!0),Y=j.useRef(""),z=j.useMemo(()=>qj(o),[o]),Z=z.map(D=>`${D.instanceId}:${Hd(D.route)}:${D.status}:${D.label}`).join("|");C.current=z,F.current=n,E.current=t,k.current=a;const G=j.useCallback(D=>Oh(D.layout,R.current,C.current),[]),Q=j.useCallback((D,A=!0,W=E.current)=>{const X=qv(D,xl()),ie=k.current.some(fe=>fe.id===W);k.current=X,u(X);const le=X.find(fe=>fe.id===W);if(A&&le){K.current=!0;const fe=G(le);s(fe),le.local&&pN(le.name,fe)}else ie&&!le&&(K.current=!0,s(Pr()),y("deck non più condiviso dal nodo owner"));uN(X.filter(fe=>fe.local).map(fe=>fe.name))},[s,G]),B=j.useCallback(async D=>{let A=await pl(e);const W=A.decks.find(X=>X.name==="main");if(A.decks.length===1&&W&&W.revision===0&&$d(W.layout)){const X=cN(),ie=Ys(Bh("main")||Pr());if(!$d(ie)){const le=Ih(ie,D,[]);A.decks[0]=await ml(e,"main",le,0,[])}for(const le of X.filter(fe=>fe!=="main"))try{let fe=await kg(e,le,[]);const we=Ys(Bh(le)||Pr());$d(we)||(fe=await ml(e,le,Ih(we,D,[]),fe.revision,[]))}catch{}A=await pl(e)}return A},[e]),M=j.useCallback(async({migrate:D=!1}={})=>{const A=await Zh(e,[]),W=Hn.test(String(A.instanceId||""))?A.instanceId:"";if(!W)throw new Error("instanceId locale non disponibile");R.current=W,w(W);const[X,ie]=await Promise.all([D?B(W):pl(e),bg(e,[]).catch(()=>({nodes:[]}))]),le={instanceId:W,route:[],label:"Local"},fe=X.decks.map(pe=>Hs(pe,le,ie.nodes,!0,!0)),we=k.current;return await Promise.all(C.current.map(async pe=>{if(pe.status!=="up"){fe.push(...we.filter(_e=>!_e.local&&_e.ownerId===pe.instanceId).map(_e=>({..._e,ownerRoute:[...pe.route],ownerLabel:pe.label,available:!1})));return}try{const[_e,He]=await Promise.all([pl(e,pe.route),bg(e,pe.route).catch(()=>({nodes:[]}))]);fe.push(..._e.decks.map(ht=>Hs(ht,pe,He.nodes,!1,!0)))}catch{fe.push(...we.filter(He=>!He.local&&He.ownerId===pe.instanceId).map(He=>({...He,ownerRoute:[...pe.route],ownerLabel:pe.label,available:!1})))}})),fe.sort((pe,_e)=>pe.local===_e.local?pe.ownerLabel.localeCompare(_e.ownerLabel)||(pe.name==="main"?-1:_e.name==="main"?1:pe.name.localeCompare(_e.name)):pe.local?-1:1),fe},[e,B,Z]);j.useEffect(()=>{if(!e)return;let D=!1;const A=Y.current!==e;return Y.current=e,M({migrate:A}).then(W=>{if(D)return;const X=k.current.find(fe=>fe.id===E.current),ie=W.find(fe=>fe.id===E.current),le=ie&&(!X||ie.revision>X.revision||X.available!==ie.available||Hd(X.ownerRoute)!==Hd(ie.ownerRoute));Q(W,A||!!le&&!O.current),p(!0),ie&&y("")}).catch(W=>{D||(p(!0),y(String(W.message||W)))}),()=>{D=!0}},[Q,M,Z,e]);const N=j.useCallback(async(D=E.current)=>{if(!h||!O.current)return!0;const A=k.current.find(W=>W.id===D);if(!A)return y(`deck inesistente: ${D}`),!1;if(A.available===!1)return y(`nodo owner offline: ${A.ownerLabel}`),!1;v("saving");try{const W=Wv(Ys(F.current),A.ownerId,A.ownerTopology),X=await ml(e,A.name,W,A.revision,A.ownerRoute),ie=Hs(X,zo(A),A.ownerTopology,A.local,!0);return Q(k.current.map(le=>le.id===D?ie:le),!1),O.current=!1,v("saved"),y(""),setTimeout(()=>v("idle"),1500),!0}catch(W){if(v("error"),y(String(W.message||W)),W.status===409&&W.data&&W.data.current){const X=k.current.find(le=>le.id===D),ie=Hs(W.data.current,zo(X),X.ownerTopology,X.local,!0);Q(k.current.map(le=>le.id===D?ie:le),!1)}return!1}},[h,e,Q]);return j.useEffect(()=>{if(!h)return;if(K.current){K.current=!1;return}O.current=!0,v("saving");const D=setTimeout(N,650);return()=>clearTimeout(D)},[n,h,N]),j.useEffect(()=>{if(!h)return;const D=setInterval(async()=>{try{const A=await M(),W=k.current.find(le=>le.id===E.current),X=A.find(le=>le.id===E.current),ie=X&&(!W||X.revision>W.revision||W.available!==X.available);Q(A,ie&&!O.current)}catch{}},5e3);return()=>clearInterval(D)},[h,M,Q]),{decks:a,records:a,localNodeId:b,ready:h,saveState:f,error:S,setError:y,saveNow:N,select:async D=>{if(O.current&&!await N(E.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const A=k.current.find(W=>W.id===D);if(!A)throw new Error(`deck inesistente: ${D}`);return O.current=!1,K.current=!0,G(A)},add:async(D,A=null)=>{const W=A===Si?k.current.find(le=>le.local):A?k.current.find(le=>le.ownerId===A):null;if(!W||W.available===!1)throw new Error("nodo owner non disponibile");const X=await kg(e,D,W.ownerRoute),ie=Hs(X,zo(W),W.ownerTopology,W.local,!0);return Q([...k.current,ie],!1),ie},rename:async(D,A)=>{const W=k.current.find(we=>we.id===D);if(!W)throw new Error("deck inesistente");if(D===E.current&&O.current&&!await N(D))throw new Error(`salvataggio di "${W.name}" fallito: rinomina annullata`);const X=k.current.find(we=>we.id===D);if(!X)throw new Error("deck inesistente");const ie=await X0(e,X.name,A,X.revision,X.ownerRoute),le=Hs(ie,zo(X),X.ownerTopology,X.local,!0),fe=X.local?Si:X.ownerId;return pd(hN(xl(),fe,D,le.id)),Q(k.current.map(we=>we.id===D?le:we),!1),le},remove:async D=>{const A=k.current.find(X=>X.id===D);if(!A)throw new Error("deck inesistente");D===E.current&&(O.current=!1),await Q0(e,A.name,A.revision,A.ownerRoute);const W=A.local?Si:A.ownerId;pd(fN(xl(),W,D)),Q(k.current.filter(X=>X.id!==D),!1)},reorder:(D,A)=>{const W=k.current.find(pe=>pe.id===D),X=k.current.find(pe=>pe.id===A);if(!W||!X)return!1;const ie=W.local?Si:W.ownerId,le=X.local?Si:X.ownerId;if(ie!==le)return!1;const fe=k.current.filter(pe=>(pe.local?Si:pe.ownerId)===ie).map(pe=>pe.id),we=pd(dN(xl(),ie,D,A,fe));return Q(qv(k.current,we),!1),!0},addTileTo:async(D,A)=>{const W=k.current.find(pe=>pe.id===D);if(!W)throw new Error("deck inesistente");if(W.available===!1)throw new Error(`nodo owner offline: ${W.ownerLabel}`);const X=Pw(A,R.current,C.current);if(!X)throw new Error("riferimento sessione non valido");const ie=G(W),le=Wv(_y(ie,X),W.ownerId,W.ownerTopology),fe=await ml(e,W.name,le,W.revision,W.ownerRoute),we=Hs(fe,zo(W),W.ownerTopology,W.local,!0);return Q(k.current.map(pe=>pe.id===D?we:pe),!1),we},localMainId:Lr(null,"main"),parseDeckId:sc}}const Yj="/sw.js",Hh="nc-sw-update";let Uh=null,Wh=!1,Kh=null,hb=Object.freeze({needed:!1,kind:null,version:""}),fb=!1;function pb(){typeof window<"u"&&window.dispatchEvent(new Event(Hh))}function mb(){const e=Kh||(Wh?{kind:"reload",version:""}:null);hb=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function $_(){return hb}function Gj(e){return typeof window>"u"?()=>{}:(window.addEventListener(Hh,e),()=>window.removeEventListener(Hh,e))}function Ff(e){Wh!==e&&(Wh=e,mb(),pb())}function Xj(e,t,n){let s=null;e&&t&&e!==t?s={kind:"install",version:e}:t&&n&&t!==n&&(s={kind:"reload",version:t});const o=JSON.stringify(s)===JSON.stringify(Kh);Kh=s,mb(),o||pb()}function Qj(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&Ff(!0)})}function Jj(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{fb&&location.reload()}),navigator.serviceWorker.register(Yj).then(e=>{Uh=e,e.waiting&&Ff(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&Qj(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function Zj(){fb=!0,Ff(!1);const e=Uh&&Uh.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function eR({hash:e="",origin:t="",pathname:n="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${n||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const u=new URLSearchParams(o),h=u.get("token")||"",p=u.get("pair")||"",f=p?`${t||""}${n||""}#pair=${p}`:"";return{token:h,pair:f,nextUrl:a}}const gb=9,vb=24,_b="nc_side_w",H_="nc_side_min",tR=240,U_="(min-width:1024px) and (pointer:fine)";function nR(){const e=Number(localStorage.getItem(_b));return e>=180&&e<=480?e:tR}function iR(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=gb&&e<=vb?e:13}function sR(){const e={token:"",pair:""};try{const{token:t,pair:n,nextUrl:s}=eR({hash:location.hash,origin:location.origin,pathname:location.pathname,search:location.search});if(t){e.token=t;try{localStorage.setItem("nc_token",t)}catch{}}if(n){e.pair=n;try{sessionStorage.setItem("nc_pair",n)}catch{}}if(location.hash)try{history.replaceState(null,"",s)}catch{}}catch{}if(e.token||(e.token=sessionStorage.getItem("nc_token")||localStorage.getItem("nc_token")||""),!e.pair){const t=sessionStorage.getItem("nc_pair");t&&(e.pair=t)}return e}function rR(e){try{return Ys(Bh(e))}catch{return Pr()}}function W_(e){if(!e)return"";const t=Math.floor(Date.now()/1e3)-e;return t<0||t<60?"ora":t<3600?`${Math.floor(t/60)}m`:t<86400?`${Math.floor(t/3600)}h`:`${Math.floor(t/86400)}g`}function oR(){const[e,t]=j.useState(()=>window.matchMedia(U_).matches);return j.useEffect(()=>{const n=window.matchMedia(U_),s=o=>t(o.matches);return n.addEventListener("change",s),()=>n.removeEventListener("change",s)},[]),e}function K_({session:e,node:t,ownerId:n,cellName:s,token:o,readonly:a=!1,onBack:u,onCellSwitcher:h,cellSwitcherOpen:p=!1}){kn();const[f]=kf(),[v,S]=j.useState(!1),[y,b]=j.useState(()=>window.matchMedia("(pointer: coarse)").matches),[w,k]=j.useState(null),[C,R]=j.useState(iR),[E,F]=j.useState(s||e),[O,K]=j.useState(""),Y=$=>R(U=>{const x=Math.max(gb,Math.min(vb,U+$));return localStorage.setItem("nc_fontsize",String(x)),x}),z=j.useRef(()=>{}),Z=j.useRef(()=>!1),G=j.useRef(()=>{}),Q=j.useRef(!1),[B,M]=j.useState(!1),[N,T]=j.useState(!1),L=()=>{Q.current=!Q.current,M(Q.current)};return j.useEffect(()=>{F(s||e)},[s,e]),j.useEffect(()=>{let $=!0;const U=t?t.split("/"):[],x=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function I(){let A=null,W=null;try{const le=await(await Ht(`${x}/sessions`,o)).json();Array.isArray(le.sessions)&&(A=le.sessions.find(fe=>fe.name===e))}catch{}try{const ie=await ws(o,U);ie.available&&Array.isArray(ie.cells)&&(W=ie.cells.find(le=>le.tmuxSession===e))}catch{}if(!$)return;F(Cf({session:e,cell:W||(s?{cell:s}:null)}));let X="";W?X=`${W.engine}${W.key?`·${W.key}`:""}`:A&&(X=A.attached?`attached · ${W_(A.activity)}`:A.activity?W_(A.activity):""),K(X)}I();const D=setInterval(I,4e3);return()=>{$=!1,clearInterval(D)}},[e,t,o]),c.jsxs("div",{className:"nc-app",children:[c.jsxs("header",{className:"nc-bar nc-bar-single",children:[c.jsxs("button",{onClick:u,title:m("sessions"),children:[c.jsx(Ze,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:m("sessions")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:t?`${E} · ${t}`:E,children:E}),O&&c.jsx("small",{className:"nc-bar-sub",children:O})]}),c.jsxs("span",{className:"nc-bar-right",children:[c.jsx("button",{onClick:()=>Y(-1),title:m("zoom-out"),children:c.jsx(Ze,{name:"zoomOut",size:18})}),c.jsx("button",{onClick:()=>Y(1),title:m("zoom-in"),children:c.jsx(Ze,{name:"zoomIn",size:18})}),c.jsx("button",{onClick:()=>b($=>!$),title:m("composer"),children:c.jsx(Ze,{name:"keyboard",size:20})}),c.jsx("button",{onClick:()=>S($=>!$),title:m("files"),children:c.jsx(Ze,{name:"folder",size:20})})]})]}),c.jsx("div",{className:"nc-termwrap",children:c.jsx(mw,{session:e,node:t,token:o,readonly:a,takeSize:!0,sendRef:z,composerRef:Z,actionRef:G,ctrlRef:Q,setCtrlArmed:M,onFiles:k,fontSize:C,selectionMode:N,onSelectionModeChange:T,keyboardGesture:f.terminalKeyboardGesture})}),c.jsx(TE,{onKeyboard:()=>b($=>!$),onCellSwitcher:h,cellSwitcherOpen:p,send:$=>z.current($),action:$=>G.current($),ctrlArmed:B,onCtrl:L,selectionMode:N,onSelectionMode:T,keepKeyboardClosed:f.keybarKeepsKeyboardClosed,showEnter:f.showKeybarEnter,keybarLayout:f.keybarLayout}),y&&c.jsx(Cw,{submitText:$=>Z.current($),token:o,session:e,node:t,ownerId:n,keepKeyboardClosedOnVoice:f.voiceKeepsKeyboardClosed}),v&&c.jsx(gw,{session:e,node:t,token:o,filesEvent:w,onClose:()=>S(!1)})]})}function aR(){kn();const[e]=j.useState(sR),[t,n]=j.useState(e.token),[s,o]=j.useState(e.pair||""),a=j.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[u,h]=j.useState(!1),p=oR(),[f]=j.useState(()=>lN(typeof location<"u"?location.pathname:"/")),[v,S]=j.useState(f.id),y=v===Lr(null,Un),[b,w]=j.useState(null),k=ue=>{const je=ys(ue);w(je?{...je,...typeof(ue==null?void 0:ue.cellName)=="string"&&ue.cellName?{cellName:ue.cellName}:{}}:null)},[C,R]=j.useState([]),[E,F]=j.useState([]),[O,K]=j.useState([]),[Y,z]=j.useState(()=>f.ownerId?Pr():rR(f.name)),[Z,G]=j.useState(null),[Q,B]=j.useState(null),M=ue=>B(ys(ue)),[N,T]=j.useState(null),L=nf(t,p),$=j.useMemo(()=>(L||[]).filter(ue=>ue.instanceId).map(ue=>({instanceId:ue.instanceId,route:ue.route,label:ue.label,status:ue.status})),[L]),U=Vj(t,v,Y,z,$),x=U.decks;j.useEffect(()=>{const ue=new Map;for(const je of L||[]){const qe=(je.route||[je.name]).join("/"),pt=new Set((je.sessions||[]).map(_t=>_t.name));for(const _t of je.cells||[])!_t.cell||!_t.tmuxSession||_t.cell===_t.tmuxSession||pt.has(_t.cell)||ue.set(`${qe}:${_t.cell}`,`${qe}:${_t.tmuxSession}`)}z(je=>Ex(je,ue))},[L]),j.useEffect(()=>{U.localNodeId&&z(ue=>{const je=Oh(ue,U.localNodeId,$);return JSON.stringify(je)===JSON.stringify(ue)?ue:je})},[$,U.localNodeId]);const[I,D]=j.useState(null),[A,W]=j.useState(null),X=j.useRef(0),[ie,le]=j.useState(!1),[fe,we]=j.useState(nR),[pe,_e]=j.useState(!y),[He,ht]=j.useState(()=>y?localStorage.getItem(H_)==="1":!0),[Ye,Ae]=j.useState(!1),[Ne,ut]=j.useState("nodes"),[Pe,Ut]=j.useState(!1),[Rt,Dt]=j.useState(""),[vt,ft]=j.useState(!1),dt=(ue="nodes",je=!1,qe="")=>{ut(ue),Ut(je),Dt(qe),Ae(!0)},[re,xe]=j.useState(!1),[Oe,Fe]=j.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[de,ze]=j.useState(!1);j.useEffect(()=>{try{localStorage.setItem(_b,String(fe))}catch{}},[fe]),j.useEffect(()=>{try{localStorage.setItem(H_,He?"1":"")}catch{}},[He]),j.useEffect(()=>{if(!t)return;let ue=!1;return Promise.all([ey(t),Ht("/api/config",t).then(je=>je.json())]).then(([je,qe])=>{ue||(Fe({deviceDefault:je.deviceName||"",localNodeId:je.nodeId||"",localNameDefault:je.localName||""}),ze(!!qe.readonlyDefault),(je.firstRun===!0&&!qe.readonlyDefault||s)&&xe(!0))}).catch(()=>{}),()=>{ue=!0}},[t,s]);const q=j.useCallback(async()=>{try{const je=await(await Ht("/api/sessions",t)).json();je.error||R(je.sessions||[])}catch{}try{const ue=await ws(t);F(ue.available?ue.cells||[]:[]),K(ue.available?ue.capabilities||[]:[])}catch{F([]),K([])}},[t]);j.useEffect(()=>{if(!p)return;q();const ue=setInterval(q,4e3);return()=>clearInterval(ue)},[p,q]),j.useEffect(()=>{let ue=!1;return Ht("/api/config",t).then(je=>je.json()).then(je=>{ue||Xj(je.version,je.uiVersion,"0.8.52-rc.15")}).catch(()=>{}),()=>{ue=!0}},[t]);const te=new Set([...C.map(ue=>ue.name),...L.flatMap(ue=>ue.sessions.map(je=>je.key))]),H=ki(Y),ae=ue=>z(je=>{const qe=Pw(ue,U.localNodeId,$)||ue,pt=_y(je,qe);return pt===je&&ki(je).length>=9&&U.setError(m("grid-full")),pt}),ce=async(ue,je=[])=>{try{await Xh(t,ue,je)}catch{return}const qe=je.length?`${je.join("/")}:${ue}`:ue;z(pt=>Hl(pt,qe)),q()},me=async(ue,je,qe=[])=>{try{await Z_(t,ue,je,qe)}catch{return}q()},Te=async(ue,je,qe=[])=>{await X_(t,{cell:ue,enabled:!!je},qe),q()},ve=async ue=>{if(!I)return;const{cell:je}=I,qe=Array.isArray(I.route)?I.route:[];if(ue.action==="up"){const _t=await Yh(t,{cell:je,boot:!!ue.boot,...ue.engine?{engine:ue.engine}:{},...ue.model!==void 0?{model:ue.model}:{},...ue.permissionPolicy?{permissionPolicy:ue.permissionPolicy}:{}},qe),bs=uf(_t);bs&&U.setError(bs.text)}else await Il(t,{cell:je,boot:!!ue.boot},qe);const pt=ue.action==="up"?!!ue.boot:ue.boot?!1:!!I.boot;W({id:++X.current,cell:je,route:qe,enabled:pt}),q()},Ce=j.useCallback(ue=>{W(je=>(je==null?void 0:je.id)===ue?null:je)},[]),nt=async ue=>{if(!(!(ue!=null&&ue.direct)||ie)){le(!0);try{await ef(t,ue.name,ue.tunnelStatus==="up"?"down":"up")}finally{le(!1)}}},ot=async(ue,je)=>{const qe=String(je||"").trim();return!(ue!=null&&ue.direct)||!Mr(qe)?!1:(await iy(t,ue.name,qe),!0)},Je=ue=>{const je=x.find(qe=>qe.id===ue);if(!je)return!1;try{const qe=window.open(fd(je,t),"_blank");return qe&&(qe.opener=null),!!qe}catch{return!1}},Mt=async ue=>{if(!ue||ue===v)return;const je=await U.select(ue),qe=U.records.find(pt=>pt.id===ue);S(ue),z(je),G(null),B(null);try{history.replaceState(null,"",fd(qe||ue,null))}catch{}},lt=async(ue,je)=>{const qe=await U.add(ue,je);await Mt(qe.id)},et=async(ue,je)=>{const qe=await U.rename(ue,je);if(ue===v){S(qe.id),z(Oh(qe.layout,U.localNodeId,$)),G(null),B(null);try{history.replaceState(null,"",fd(qe,null))}catch{}}},Zt=async ue=>{await U.remove(ue),ue===v&&await Mt(U.localMainId)},ni=async(ue,je)=>{!je||je===v||(await U.addTileTo(je,ue),z(qe=>Hl(qe,ue)))};if(!t)return c.jsxs("div",{className:"nc-auth",children:[c.jsx("p",{children:m("auth-prompt")}),c.jsx("input",{onChange:ue=>n(ue.target.value.trim()),placeholder:"token"}),c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:u,onChange:ue=>h(ue.target.checked)})," ",m("remember-device")]}),c.jsx("button",{onClick:()=>{(u?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Wn=c.jsxs(c.Fragment,{children:[Ye&&c.jsx(Rj,{token:t,initialTab:Ne,initialLocation:Rt,startNewCell:Pe,onClose:()=>{Ae(!1),Ut(!1),Dt("")}}),re&&c.jsx(Pj,{token:t,initialPair:s,...Oe,onPairDone:a,onDone:()=>xe(!1)}),c.jsx(Ij,{token:t})]});if(!p)return N?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-single-overlay",children:c.jsx(F_,{peer:N,token:t,onBack:()=>T(null)})}),Wn]}):b?c.jsxs(c.Fragment,{children:[c.jsx(K_,{session:b.session,node:b.node,ownerId:b.ownerId,cellName:b.cellName,token:t,readonly:de,onBack:()=>w(null),onCellSwitcher:()=>ft(!0),cellSwitcherOpen:vt}),vt&&c.jsx(Hj,{token:t,current:b,onPick:ue=>{k(ue),ft(!1)},onClose:()=>ft(!1)}),Wn]}):c.jsxs(c.Fragment,{children:[c.jsx(Wx,{onPick:k,token:t,onSettings:dt,onOpenVlSession:T}),Wn]});const ui=y||!pe;return c.jsxs("div",{className:"nc-workspace",children:[ui&&c.jsx(XE,{sessions:C,cells:E,activeSessions:H,nodeGroups:L,fleetCapabilities:O,bootSettlement:A,onBootSettlementApplied:Ce,localNodeId:U.localNodeId,onPick:M,onAddTile:ae,onPower:D,onBoot:Te,onBootError:ue=>U.setError(String((ue==null?void 0:ue.message)||ue)),onNodePower:nt,onNodeRename:ot,onKill:ce,onVisibility:me,onNew:()=>dt("fleet",!0),onSettings:dt,onOpenVlSession:T,width:fe,collapsed:He,onResize:we,onToggleCollapse:()=>ht(ue=>!ue)}),c.jsxs("div",{className:"nc-workspace-main",children:[c.jsx(LN,{decks:x,currentDeck:v,onCreate:lt,onRename:et,onDelete:Zt,onReorder:U.reorder,onOpenWindow:Je,onNavigate:Mt,saveState:U.saveState,error:U.error,sidebarVisible:ui,onToggleSidebar:y?null:()=>_e(ue=>!ue)}),c.jsx(iN,{layout:Y,onLayoutChange:z,token:t,readonly:de,sessionsAlive:te,focusSession:Z,onFocus:G,onOpenSingle:M,decks:x,currentDeck:v,onSendToDeck:ni,cells:E,nodeGroups:L})]}),N&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(F_,{peer:N,token:t,onBack:()=>T(null)})}),Q&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(K_,{session:Q.session,node:Q.node,ownerId:Q.ownerId,cellName:Cf({session:Q.session,node:Q.node,ownerId:Q.ownerId,cells:E,nodeGroups:L}),token:t,readonly:de,onBack:()=>B(null)})}),I&&c.jsx(Bl,{cell:I,token:t,route:Array.isArray(I.route)?I.route:[],onConfirm:ve,onClose:()=>D(null)}),Wn]})}function lR(){kn();const e=j.useSyncExternalStore(Gj,$_,$_);if(!e.needed)return null;const t=e.kind==="install"?m("install-version-mismatch").replace("{v}",e.version):m("update-available").replace("{v}",e.version).replace(" "," ").trim();return c.jsxs("div",{className:"nc-update",role:"status","aria-live":"polite",children:[c.jsx("span",{className:"nc-update-msg",children:t}),e.kind==="reload"&&c.jsx("button",{className:"nc-update-btn",onClick:Zj,children:m("reload")})]})}Jj();h0.createRoot(document.getElementById("root")).render(c.jsxs(r0.StrictMode,{children:[c.jsx(aR,{}),c.jsx(lR,{})]}));
@@ -11,7 +11,7 @@
11
11
  <meta name="apple-mobile-web-app-title" content="NexusCrew" />
12
12
  <link rel="manifest" href="/manifest.json" />
13
13
  <title>NexusCrew</title>
14
- <script type="module" crossorigin src="/assets/index-Cjq8zHOy.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-CB3o67BZ.js"></script>
15
15
  <link rel="stylesheet" crossorigin href="/assets/index-_kotgT8Z.css">
16
16
  </head>
17
17
  <body>
@@ -2,6 +2,17 @@ self.addEventListener('install', () => self.skipWaiting());
2
2
  self.addEventListener('activate', () => self.clients.claim());
3
3
  self.addEventListener('fetch', (e) => e.respondWith(fetch(e.request)));
4
4
 
5
+ // applyUpdate() (lib/sw-update.js) manda questo messaggio a un worker in
6
+ // waiting e aspetta il `controllerchange`. Senza un listener il messaggio
7
+ // cadeva nel vuoto: scattava il reload di fallback, il worker restava in
8
+ // waiting, e al ricaricamento `reg.waiting` faceva ricomparire il banner —
9
+ // "nuova versione disponibile" per sempre, e il bottone non poteva spegnerlo.
10
+ // Un worker installato da una versione precedente di questo file resta in
11
+ // waiting fino a che non lo si attiva: e' quello il caso che si incastrava.
12
+ self.addEventListener('message', (e) => {
13
+ if (e && e.data && e.data.type === 'SKIP_WAITING') self.skipWaiting();
14
+ });
15
+
5
16
  // Web Push del MCP bridge: payload JSON {title, body?, lang?, url?} dal server.
6
17
  // tag fisso 'nexuscrew': le notifiche si sostituiscono invece di accumularsi.
7
18
  self.addEventListener('push', (e) => {
@@ -1 +1 @@
1
- {"version":"0.8.52-rc.13"}
1
+ {"version":"0.8.52-rc.15"}
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+ // lib/cells/scope-guard.js — il punto in cui lo scope celle diventa effettivo.
3
+ //
4
+ // Sta in testa al router /api perche' i canali da cui una cella puo' trapelare
5
+ // sono tredici, e un permesso che vale su dodici di essi non e' un permesso:
6
+ // e' un'etichetta. Gli elenchi (cosa esce) e le azioni (cosa si puo' toccare)
7
+ // passano dallo stesso predicato di lib/cells/scope.js.
8
+ //
9
+ // Due scelte deliberate:
10
+ //
11
+ // * il bersaglio di ogni route e' DICHIARATO in una tabella, non indovinato.
12
+ // Una regola magica sbaglia in silenzio quando qualcuno aggiunge una route;
13
+ // un elenco esplicito, quando qualcuno lo dimentica, fa scattare la rete di
14
+ // sicurezza qui sotto.
15
+ // * fail-closed sulle route sconosciute che nominano una cella: se un corpo
16
+ // porta `cell`, `to.cell` o `session` e la route non e' dichiarata, si nega.
17
+ // E' l'unico modo perche' una route aggiunta domani non nasca scoperta.
18
+ const { createCellScope } = require('./scope.js');
19
+
20
+
21
+ // Chiavi da cui, in una RISPOSTA, esce un elenco di celle o di sessioni.
22
+ // Coprono /cells, /fleet/status, /fleet/definitions, /sessions.
23
+ const CELL_LIST_KEYS = ['cells'];
24
+ const SESSION_LIST_KEYS = ['sessions', 'unmanaged'];
25
+
26
+ // Route che AGISCONO su una cella o su una sessione, con il punto esatto in cui
27
+ // leggere il bersaglio. `null` = la route e' nota e non ha un bersaglio cella.
28
+ function declaredTarget(req) {
29
+ const p = req.path || '';
30
+ const b = (req.body && typeof req.body === 'object') ? req.body : {};
31
+ const q = req.query || {};
32
+
33
+ // Azioni sul ciclo di vita di una cella: il bersaglio e' `body.cell`.
34
+ if (/^\/fleet\/(up|down|restart|engine|boot)$/.test(p)) return { cell: b.cell };
35
+ // Definire/modificare/rimuovere celle e' una capacita' a se': chi ha uno
36
+ // scope ristretto non deve poter creare la cella che gli manca.
37
+ if (/^\/fleet\/(define-cell|edit-cell|remove-cell|restore-cells|import-cell)$/.test(p)) return { deny: 'scope-cannot-define-cells' };
38
+ // Messaggio a una cella: il bersaglio e' dichiarato due volte, e devono
39
+ // valere entrambe (il codice a valle le confronta gia' fra loro).
40
+ if (p === '/cells/send') return { cell: b.to && b.to.cell, session: b.to && b.to.tmuxSession };
41
+ // Sessioni: creazione, eliminazione, visibilita', file.
42
+ if (p === '/sessions' && req.method === 'POST') return { session: b.name };
43
+ if (/^\/sessions\/[^/]+/.test(p)) return { session: decodeURIComponent(p.split('/')[2] || '') };
44
+ if (p === '/files' || p.startsWith('/files/')) return { session: q.session || b.session };
45
+ return undefined; // route non dichiarata
46
+ }
47
+
48
+ // Rete di sicurezza: un corpo che nomina una cella o una sessione su una route
49
+ // non dichiarata. Meglio un 403 su qualcosa di legittimo — che si nota subito e
50
+ // si aggiunge alla tabella — di un canale aperto che nessuno vede.
51
+ function undeclaredTarget(req) {
52
+ const b = (req.body && typeof req.body === 'object') ? req.body : {};
53
+ const q = req.query || {};
54
+ const cell = b.cell || (b.to && b.to.cell);
55
+ const session = b.session || b.tmuxSession || (b.to && b.to.tmuxSession) || q.session;
56
+ if (cell || session) return { cell, session };
57
+ return null;
58
+ }
59
+
60
+ function filterPayload(payload, scope) {
61
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return payload;
62
+ const out = { ...payload };
63
+ for (const key of CELL_LIST_KEYS) {
64
+ if (Array.isArray(out[key])) out[key] = scope.filterCells(out[key]);
65
+ }
66
+ for (const key of SESSION_LIST_KEYS) {
67
+ if (Array.isArray(out[key])) out[key] = scope.filterSessions(out[key]);
68
+ }
69
+ return out;
70
+ }
71
+
72
+ // Parser JSON usato SOLO sulle richieste federate. Non si puo' montare un
73
+ // parser globale: le route locali catturano `rawBody` con `verify` per la firma
74
+ // del bridge, e `verify` non viene chiamato se il corpo e' gia' stato
75
+ // consumato; inoltre i limiti di dimensione per-route (16kb per notify, 8kb per
76
+ // i comandi VL) verrebbero saltati, perche' express.json esce subito quando
77
+ // `req.body` esiste gia'. Le richieste locali devono arrivare INTATTE ai loro
78
+ // parser.
79
+ function federatedBodyParser(limit) {
80
+ const parse = require('express').json({ limit });
81
+ return (req, res, next) => {
82
+ if (req.body !== undefined) return next();
83
+ const method = (req.method || 'GET').toUpperCase();
84
+ if (method === 'GET' || method === 'HEAD' || method === 'DELETE') return next();
85
+ return parse(req, res, () => next());
86
+ };
87
+ }
88
+
89
+ function createCellScopeGuard({
90
+ nodesPath, loadStoreImpl, cellForSession,
91
+ // resolveOrigin(req) -> {ok, trust, visited}. In produzione e' l'origin
92
+ // resolver basato sulla prova di hop; nei test e' un seam, cosi' questo
93
+ // modulo prova il GUARD e non di nuovo la firma (che ha i suoi test).
94
+ resolveOrigin,
95
+ bodyLimit = '1mb',
96
+ } = {}) {
97
+ const scoper = createCellScope({ nodesPath, loadStoreImpl, cellForSession });
98
+ const parseFederatedBody = federatedBodyParser(bodyLimit);
99
+
100
+ return async function cellScopeGuard(req, res, next) {
101
+ let origin;
102
+ try {
103
+ origin = await resolveOrigin(req);
104
+ } catch (_) {
105
+ origin = null;
106
+ }
107
+ // Un hop che non verifica NON e' una richiesta federata, e questo guard non
108
+ // e' il posto dove deciderne l'esito: le route che sanno rifiutarlo lo
109
+ // fanno gia' con il loro codice (401 su audio, non 403), e sostituirlo qui
110
+ // cambierebbe una risposta di sicurezza esistente senza migliorarla.
111
+ // Non e' nemmeno una scappatoia: `cleanHeaders` cancella gli header di hop
112
+ // in ingresso e il proxy li riscrive lui, quindi un peer non puo' ne'
113
+ // toglierli ne' falsificarli. Chi puo' inventarne uno possiede gia' il
114
+ // Bearer locale — e' il proprietario della macchina, che non si limita da
115
+ // solo.
116
+ if (!origin || origin.ok !== true) return next();
117
+ if (origin.trust !== 'federated') return next();
118
+
119
+ const scope = scoper.resolve({ trust: 'federated', visited: origin.visited });
120
+ req.cellScope = scope;
121
+ if (scope.mode === 'all') return next();
122
+
123
+ // Il bersaglio di molte azioni sta nel corpo: qui, e solo per le federate,
124
+ // va parsato prima di poterlo guardare.
125
+ return parseFederatedBody(req, res, () => applyScope(req, res, next, scope));
126
+ };
127
+
128
+ function applyScope(req, res, next, scope) {
129
+ // --- azioni ---------------------------------------------------------
130
+ const declared = declaredTarget(req);
131
+ if (declared === undefined) {
132
+ const sneaky = undeclaredTarget(req);
133
+ if (sneaky) {
134
+ return res.status(403).json({ error: 'route non dichiarata per lo scope celle', reason: 'undeclared-cell-route' });
135
+ }
136
+ } else if (declared) {
137
+ if (declared.deny) return res.status(403).json({ error: 'fuori dallo scope celle', reason: declared.deny });
138
+ if (declared.cell !== undefined && !scope.allowsCell(declared.cell)) {
139
+ return res.status(403).json({ error: 'fuori dallo scope celle', reason: 'cell-not-granted' });
140
+ }
141
+ if (declared.session !== undefined && !scope.allowsSession(declared.session)) {
142
+ return res.status(403).json({ error: 'fuori dallo scope celle', reason: 'session-not-granted' });
143
+ }
144
+ }
145
+
146
+ // --- letture --------------------------------------------------------
147
+ // Si avvolge res.json invece di riscrivere ogni handler: gli elenchi sono
148
+ // quattro e crescono, e un handler nuovo nascerebbe scoperto.
149
+ const json = res.json.bind(res);
150
+ res.json = (payload) => json(filterPayload(payload, scope));
151
+ return next();
152
+ }
153
+ }
154
+
155
+ module.exports = { createCellScopeGuard, declaredTarget, filterPayload };
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+ // lib/cells/scope.js — quali celle di QUESTO nodo un peer federato puo' vedere.
3
+ //
4
+ // Gemello di lib/audio/acl.js, e per le stesse ragioni:
5
+ // * la sorgente della decisione e' il node store locale, mai il corpo della
6
+ // richiesta. Un peer non dichiara il proprio scope: lo subisce.
7
+ // * l'identita' arriva dalla catena `visited` che costruisce il server
8
+ // (controlledVisited), non da un campo dichiarato.
9
+ //
10
+ // Due invarianti che una versione semplificata romperebbe in silenzio:
11
+ //
12
+ // 1. In multi-hop lo scope e' l'INTERSEZIONE fra chi consegna e chi origina.
13
+ // `canTransit` autorizza il transito, non l'accesso: un peer B puo'
14
+ // instradare una richiesta di C. Guardare solo B lascerebbe C ereditare i
15
+ // permessi di B. lib/audio/acl.js risolve lo stesso caso controllando
16
+ // entrambi, e qui va replicato invece che semplificato.
17
+ // 2. Una sessione tmux e' concessa solo se lo e' la CELLA a cui appartiene, e
18
+ // una sessione che non appartiene a nessuna cella non e' "libera": e'
19
+ // fuori scope. Altrimenti basterebbe una tmux creata a mano per aggirare
20
+ // il permesso — e `/ws` attacca proprio per nome di sessione.
21
+ //
22
+ // Il permesso e' indicizzato per `nodeId`, non per `name`: il name e' uno slug
23
+ // locale rinominabile, il nodeId e' l'identita' stabile provata dal pairing.
24
+ const nodesStore = require('../nodes/store.js');
25
+
26
+ // Scope di un singolo nodo, normalizzato. `all` non elenca nulla di proposito:
27
+ // una lista che significa "tutte" invecchierebbe a ogni cella nuova.
28
+ function scopeOf(node) {
29
+ if (!node) return { mode: 'none', cells: new Set() };
30
+ const mode = node.cellVisibility || 'all';
31
+ if (mode === 'all') return { mode: 'all', cells: null };
32
+ if (mode === 'none') return { mode: 'none', cells: new Set() };
33
+ return { mode: 'selected', cells: new Set(Array.isArray(node.cells) ? node.cells : []) };
34
+ }
35
+
36
+ // Intersezione di due scope. `all` e' l'elemento neutro; `none` assorbe.
37
+ function intersect(a, b) {
38
+ if (a.mode === 'none' || b.mode === 'none') return { mode: 'none', cells: new Set() };
39
+ if (a.mode === 'all') return b;
40
+ if (b.mode === 'all') return a;
41
+ return { mode: 'selected', cells: new Set([...a.cells].filter((c) => b.cells.has(c))) };
42
+ }
43
+
44
+ function findByNodeId(st, nodeId) {
45
+ if (!st || !Array.isArray(st.nodes) || !nodeId) return null;
46
+ return st.nodes.find((n) => n && n.nodeId === nodeId) || null;
47
+ }
48
+
49
+ // createCellScope(): deps esplicite, nessun accesso globale.
50
+ // nodesPath percorso del node store
51
+ // loadStoreImpl iniettabile nei test
52
+ // cellForSession mappa tmuxSession -> id cella (null se non e' di una cella)
53
+ function createCellScope({ nodesPath, loadStoreImpl = nodesStore.loadStore, cellForSession = () => null } = {}) {
54
+ // Scope "tutto", usato dal percorso locale: il proprietario della macchina
55
+ // non si limita da solo, e questo modulo non e' il posto dove decidere
56
+ // altrimenti.
57
+ function openScope() {
58
+ return buildApi({ mode: 'all', cells: null });
59
+ }
60
+
61
+ function buildApi(resolved) {
62
+ const allowsCell = (cell) => {
63
+ if (resolved.mode === 'all') return true;
64
+ if (resolved.mode === 'none') return false;
65
+ return typeof cell === 'string' && resolved.cells.has(cell);
66
+ };
67
+ const allowsSession = (session) => {
68
+ if (resolved.mode === 'all') return true;
69
+ const cell = cellForSession(session);
70
+ // Nessuna cella dietro la sessione => fuori scope. Vedi invariante 2.
71
+ if (!cell) return false;
72
+ return allowsCell(cell);
73
+ };
74
+ return {
75
+ mode: resolved.mode,
76
+ cells: resolved.cells ? [...resolved.cells] : null,
77
+ allowsCell,
78
+ allowsSession,
79
+ // Filtri: gli elenchi si restringono con lo STESSO predicato che decide
80
+ // le azioni. Due implementazioni divergenti sarebbero un bug latente.
81
+ filterCells: (list) => (Array.isArray(list) ? list.filter((c) => allowsCell(c && (c.cell ?? c.id))) : []),
82
+ filterSessions: (list) => (Array.isArray(list) ? list.filter((s) => allowsSession(s && (s.name ?? s.tmuxSession))) : []),
83
+ };
84
+ }
85
+
86
+ // resolve({trust, visited}) -> api dello scope.
87
+ // trust 'local-bridge' (o assente e non federato) : nessuna restrizione
88
+ // trust 'federated' : intersezione consegnante ∩ origine
89
+ function resolve({ trust, visited } = {}) {
90
+ if (trust !== 'federated') return openScope();
91
+ const chain = Array.isArray(visited) ? visited : [];
92
+ // Serve almeno origine + questo nodo. Una catena piu' corta non identifica
93
+ // nessun peer: fail-closed.
94
+ if (chain.length < 2) return buildApi({ mode: 'none', cells: new Set() });
95
+
96
+ let st;
97
+ try { st = loadStoreImpl(nodesPath); } catch (_) { st = null; }
98
+ if (!st) return buildApi({ mode: 'none', cells: new Set() });
99
+
100
+ const deliveringId = chain[chain.length - 2];
101
+ const originId = chain[0];
102
+ const delivering = scopeOf(findByNodeId(st, deliveringId));
103
+ // Quando origine e consegnante coincidono (hop singolo) l'intersezione con
104
+ // se stesso e' identita': nessun caso speciale da mantenere allineato.
105
+ const origin = originId === deliveringId ? delivering : scopeOf(findByNodeId(st, originId));
106
+ return buildApi(intersect(delivering, origin));
107
+ }
108
+
109
+ return { resolve };
110
+ }
111
+
112
+ module.exports = { createCellScope, scopeOf, intersect };
package/lib/mcp/tools.js CHANGED
@@ -273,7 +273,11 @@ const TOOLS = [
273
273
  // Con un target remoto l'esito e' quello del nodo che ha consegnato:
274
274
  // `delivered` da solo direbbe "0 tentativi qui", che e' vero e inutile.
275
275
  // Restituirlo cosi' evita di far leggere un successo dove c'e' un rifiuto.
276
- if (target) return { status: j.status, ...(j.reason ? { reason: j.reason } : {}), ...(j.delivered ? { delivered: j.delivered } : {}) };
276
+ // Nessun conteggio: il dispatcher non lo propaga, e un ramo che non puo'
277
+ // mai essere vero e' codice morto travestito da informazione
278
+ // (rilievo R1 di DevAuditor su rc.14). Se un giorno servira' il dettaglio,
279
+ // va fatto propagare da forward(), non dedotto qui.
280
+ if (target) return { status: j.status, ...(j.reason ? { reason: j.reason } : {}) };
277
281
  return { delivered: j.delivered };
278
282
  },
279
283
  },
@@ -108,8 +108,17 @@ const NODE_KEYS = new Set([
108
108
  'name', 'ssh', 'sshPort', 'remotePort', 'localPort', 'keyPath', 'identityFile',
109
109
  'roles', 'rolesKnown', 'token', 'acceptToken', 'nodeId', 'transport', 'autostart', 'visibility', 'selected',
110
110
  'direction', 'reversePort', 'shared', 'label', 'reversePool',
111
+ // Quali CELLE di questo nodo il peer puo' vedere. Distinto da `visibility`,
112
+ // che governa il TRANSITO (attraverso chi passa il traffico) e non l'accesso.
113
+ 'cellVisibility', 'cells',
111
114
  ]);
112
115
 
116
+ // Nome di cella: stessa forma usata da fleet/definitions.js, cells/routes.js e
117
+ // mcp/cells.js. E' `cell` la chiave del permesso — unica nel nodo e immutabile —
118
+ // non `tmuxSession`, che e' derivata e ammette override non canonici.
119
+ const CELL_ID_RE = /^[A-Za-z0-9._-]{1,32}$/;
120
+ const MAX_CELLS = 128;
121
+
113
122
  const REVERSE_POOL_SLOT_STATES = new Set(['active', 'ready', 'reserved', 'draining', 'quarantined', 'retired']);
114
123
  const REVERSE_POOL_VERIFICATIONS = new Set(['verified', 'unverifiable', 'missing', 'invalidated']);
115
124
  const REVERSE_POOL_PHASES = new Set(['active', 'prepared', 'switched', 'abandoned']);
@@ -265,6 +274,28 @@ function parseNode(n, schemaVersion = SCHEMA_VERSION) {
265
274
  if (selected.some((id) => typeof id !== 'string' || !NODE_ID_RE.test(id))) return null;
266
275
  out.selected = selected;
267
276
  }
277
+ // Scope celle. Il default e' `all` quando il campo e' ASSENTE: un default
278
+ // fail-closed renderebbe muta l'intera flotta al primo aggiornamento, senza
279
+ // che nessuno abbia deciso nulla. Restringere deve restare un atto esplicito.
280
+ // `selected` con lista vuota significa invece "nessuna cella", ed e' proprio
281
+ // per distinguerlo da "campo assente" che la modalita' e' un campo a parte.
282
+ if (n.cellVisibility !== undefined) {
283
+ if (!['all', 'none', 'selected'].includes(n.cellVisibility)) return null;
284
+ out.cellVisibility = n.cellVisibility;
285
+ } else {
286
+ out.cellVisibility = 'all';
287
+ }
288
+ if (n.cells !== undefined) {
289
+ // Elencare celle senza dichiarare la modalita' e' ambiguo: l'elenco
290
+ // sembrerebbe un permesso mentre non lo e'. Meglio rifiutare che indovinare.
291
+ if (out.cellVisibility !== 'selected') return null;
292
+ if (!Array.isArray(n.cells) || n.cells.length > MAX_CELLS) return null;
293
+ const cells = [...new Set(n.cells)];
294
+ if (cells.some((id) => typeof id !== 'string' || !CELL_ID_RE.test(id))) return null;
295
+ out.cells = cells;
296
+ } else if (out.cellVisibility === 'selected') {
297
+ out.cells = [];
298
+ }
268
299
  // Campo opzionale per compatibilita' con gli store 0.8.0: se assente, ssh
269
300
  // continua a usare la porta risolta da ~/.ssh/config o il default OpenSSH.
270
301
  if (n.sshPort !== undefined) out.sshPort = n.sshPort;
@@ -577,6 +608,10 @@ function redactNode(n) {
577
608
  };
578
609
  if (n.identityFile || n.keyPath) out.hasIdentity = true;
579
610
  if (n.visibility === 'selected') out.selected = [...(n.selected || [])];
611
+ // Lo scope celle non e' un segreto: e' una decisione dell'operatore e va
612
+ // mostrata, come `selected`. La Settings UI deve poterla leggere per dirla.
613
+ out.cellVisibility = n.cellVisibility || 'all';
614
+ if (out.cellVisibility === 'selected') out.cells = [...(n.cells || [])];
580
615
  if (n.sshPort !== undefined) out.sshPort = n.sshPort;
581
616
  if (n.nodeId) out.nodeId = n.nodeId;
582
617
  if (n.reversePool) {
@@ -1084,7 +1084,7 @@ function localRouter({ nodesPath, localPort, localCredential, readonly, hopSecre
1084
1084
  return r;
1085
1085
  }
1086
1086
 
1087
- function forwardUpgrade({ req, socket, head, nodesPath, localPort, localCredential, ingress, readonly = () => false, activeSockets = null }) {
1087
+ function forwardUpgrade({ req, socket, head, nodesPath, localPort, localCredential, ingress, readonly = () => false, activeSockets = null, hopSecret = null }) {
1088
1088
  if (readonly()) return reject(socket, 403);
1089
1089
  const parsed = parseRoute(req.url.replace(/^\/(?:api\/route|federation\/route)/, ''));
1090
1090
  if (!parsed || parsed.resource !== '/ws') return reject(socket, 404);
@@ -1103,7 +1103,18 @@ function forwardUpgrade({ req, socket, head, nodesPath, localPort, localCredenti
1103
1103
  }
1104
1104
  const up = net.connect({ host: '127.0.0.1', port });
1105
1105
  up.once('connect', () => {
1106
- const headers = cleanHeaders(req.headers, credential, parsed.route.length ? visited : null);
1106
+ // Ultimo hop: l'upgrade rientra nell'API locale col Bearer locale e da li'
1107
+ // in poi sarebbe indistinguibile da un attach diretto di chi possiede quel
1108
+ // token. La catena PIU' la prova di hop sono cio' che lo distingue —
1109
+ // esattamente come fa routeHandler per il percorso HTTP. Senza, il nodo che
1110
+ // possiede la sessione non sa quale peer stia aprendo il suo PTY, e ogni
1111
+ // permesso per-cella diventa decorativo: `/ws` attacca per NOME di
1112
+ // sessione, quindi basta indovinarlo.
1113
+ const secret = typeof hopSecret === 'function' ? hopSecret() : hopSecret;
1114
+ const hopProof = !parsed.route.length && secret
1115
+ ? signHop(secret, { method: req.method || 'GET', path, visited })
1116
+ : null;
1117
+ const headers = cleanHeaders(req.headers, credential, visited, hopProof);
1107
1118
  const lines = [`GET ${path} HTTP/1.1`, `Host: 127.0.0.1:${port}`];
1108
1119
  for (const [k, v] of Object.entries(headers)) lines.push(`${k}: ${Array.isArray(v) ? v.join(', ') : v}`);
1109
1120
  lines.push('Connection: Upgrade', 'Upgrade: websocket', '', '');
package/lib/server.js CHANGED
@@ -39,6 +39,9 @@ const { isConsent: isAudioConsent } = require('./audio/consent.js');
39
39
  const { createOriginResolver } = require('./audio/origin.js');
40
40
  const { createAudioAcl } = require('./audio/acl.js');
41
41
  const { createDispatcher } = require('./audio/dispatch.js');
42
+ const { createCellScopeGuard } = require('./cells/scope-guard.js');
43
+ const { createCellScope } = require('./cells/scope.js');
44
+ const { cellIdFromTmuxSession } = require('./fleet/definitions.js');
42
45
  // Budget a tre finestre, riusato dalle notifiche federate: e' gia' scritto per
43
46
  // limitare il danno di un nodo che mente sulla propria cella (il tetto per
44
47
  // target si calcola sul NODO, che la catena visited verifica).
@@ -48,7 +51,8 @@ const { createReceiptStore } = require('./audio/receipt.js');
48
51
  const audioAdapters = require('./audio/adapters.js');
49
52
  const audioGroups = require('./audio/groups.js');
50
53
  const { bridgeSecretPath, loadOrCreateBridgeSecret, createNonceCache } = require('./audio/bridge-auth.js');
51
- const { createHopSecret } = require('./proxy/hop-proof.js');
54
+ const { createHopSecret, verifyHop, HOP_HEADER } = require('./proxy/hop-proof.js');
55
+ const { parseVisitedChain } = require('./audio/origin.js');
52
56
  const { settingsRoutes, publicPeeringRoutes } = require('./settings/routes.js');
53
57
  const decksStore = require('./decks/store.js');
54
58
  const { decksRoutes } = require('./decks/routes.js');
@@ -109,6 +113,10 @@ function createServer(opts = {}) {
109
113
  // alla porta sbagliata (bug reale: 401/"unauthorized" dal servizio su cfg.port).
110
114
  let server = null;
111
115
  const previews = createPreviewSampler(cfg.tmuxBin);
116
+ // Seam di test per l'esistenza di una sessione: senza, un test sullo scope
117
+ // dell'attach non discrimina, perche' ogni sessione risulta assente e il
118
+ // rifiuto arriverebbe comunque — un verde che non prova nulla.
119
+ const sessionExistsImpl = cfg.sessionExistsSeam || ((name) => sessionExists(cfg.tmuxBin, name));
112
120
  // MCP bridge (notify/ask/push): lo stato vive accanto al token (dirname del
113
121
  // tokenPath = ~/.nexuscrew di default) cosi' le istanze isolate via opts/env
114
122
  // nei test NON scrivono mai nella home reale. Tutto lazy: vapid.json/asks.json
@@ -545,6 +553,51 @@ function createServer(opts = {}) {
545
553
  // ma il token chiude anche altri processi locali della stessa macchina.
546
554
  const api = express.Router();
547
555
  api.use(requireToken(tokenStore));
556
+ // Origine per lo scope celle: stessa prova di hop usata da audio e notify.
557
+ // requireCell:false perche' qui interessa QUALE NODO parla, non quale cella
558
+ // dichiari — la cella e' solo attestata e non deve decidere un permesso.
559
+ const cellScopeOrigin = createOriginResolver({
560
+ localNodeId: () => (nodesStore.loadStore(nodesPath) || {}).nodeId || null,
561
+ hopSecret: () => hopSecret,
562
+ });
563
+ const cellScoper = createCellScope({
564
+ nodesPath,
565
+ cellForSession: (session) => cellIdFromTmuxSession(session),
566
+ });
567
+ // Variante SINCRONA per l'upgrade WebSocket: verifica la prova di hop e la
568
+ // catena senza passare dal resolver async. Un upgrade non federato — o con
569
+ // una prova che non regge — resta senza restrizioni, esattamente come una
570
+ // richiesta locale: la prova la costruisce il proxy, un peer non puo' ne'
571
+ // toglierla ne' falsificarla.
572
+ const wsCellScope = (req) => {
573
+ try {
574
+ const headers = (req && req.headers) || {};
575
+ const proof = headers[HOP_HEADER];
576
+ if (!proof) return cellScoper.resolve({ trust: 'local-bridge' });
577
+ const self = (nodesStore.loadStore(nodesPath) || {}).nodeId || null;
578
+ const visited = parseVisitedChain(headers['x-nexuscrew-visited'], self);
579
+ if (!visited) return cellScoper.resolve({ trust: 'federated', visited: [] });
580
+ const ok = verifyHop(hopSecret, {
581
+ method: req.method || 'GET', path: '/ws', visited,
582
+ }, proof);
583
+ if (!ok) return cellScoper.resolve({ trust: 'federated', visited: [] });
584
+ return cellScoper.resolve({ trust: 'federated', visited });
585
+ } catch (_) {
586
+ // Fail-closed: se non si riesce a stabilire l'origine, nessuna sessione.
587
+ return cellScoper.resolve({ trust: 'federated', visited: [] });
588
+ }
589
+ };
590
+ // Scope celle (NC-E): un punto solo decide cosa un peer federato vede e puo'
591
+ // toccare. Sta qui, prima di ogni route, perche' i canali da cui una cella
592
+ // trapela sono molti e crescono: filtrarli uno per uno significa dimenticarne
593
+ // uno, e un permesso che vale su quasi tutti i canali non e' un permesso.
594
+ // Le richieste locali non lo attraversano: il proprietario della macchina non
595
+ // si limita da solo.
596
+ api.use(createCellScopeGuard({
597
+ nodesPath,
598
+ cellForSession: (session) => cellIdFromTmuxSession(session),
599
+ resolveOrigin: (req) => cellScopeOrigin.resolve(req, { requireCell: false }),
600
+ }));
548
601
  api.get('/sessions', async (_req, res) => {
549
602
  try {
550
603
  const sessions = await listSessions(cfg.tmuxBin);
@@ -901,13 +954,13 @@ function createServer(opts = {}) {
901
954
  let u; try { u = new URL(req.url, 'http://127.0.0.1'); } catch (_) { return socket.destroy(); }
902
955
  const given = bearerFrom(req) || u.searchParams.get('token') || '';
903
956
  if (!verify(tokenHolder.value, given)) return socket.destroy();
904
- federation.forwardUpgrade({ req, socket, head, nodesPath, localPort: runtimePort, localCredential: () => tokenHolder.value, ingress: null, readonly: proxyReadonly, activeSockets: proxySockets });
957
+ federation.forwardUpgrade({ req, socket, head, nodesPath, localPort: runtimePort, localCredential: () => tokenHolder.value, ingress: null, readonly: proxyReadonly, activeSockets: proxySockets, hopSecret: () => hopSecret });
905
958
  return;
906
959
  }
907
960
  if (pathname.startsWith('/federation/route/')) {
908
961
  const ingress = federation.peerFromToken(nodesPath, bearerFrom(req));
909
962
  if (!ingress) return socket.destroy();
910
- federation.forwardUpgrade({ req, socket, head, nodesPath, localPort: runtimePort, localCredential: () => tokenHolder.value, ingress, readonly: proxyReadonly, activeSockets: proxySockets });
963
+ federation.forwardUpgrade({ req, socket, head, nodesPath, localPort: runtimePort, localCredential: () => tokenHolder.value, ingress, readonly: proxyReadonly, activeSockets: proxySockets, hopSecret: () => hopSecret });
911
964
  return;
912
965
  }
913
966
  if (pathname === '/node' || pathname.startsWith('/node/')) {
@@ -933,10 +986,19 @@ function createServer(opts = {}) {
933
986
  // non possono settare header sui WS -> il flusso locale resta identico
934
987
  // (token nel frame attach, mai in URL).
935
988
  const preauth = req ? verify(tokenHolder.value, bearerFrom(req)) : false;
989
+ // Scope celle sull'ATTACH. Calcolato in modo sincrono di proposito: rendere
990
+ // async questo handler rischierebbe di perdere i frame che arrivano prima
991
+ // di bindWs. `/ws` attacca per NOME DI SESSIONE e senza questo gate ogni
992
+ // filtro sugli elenchi sarebbe decorativo — basterebbe indovinare
993
+ // `cloud-Dev`.
994
+ const wsScope = wsCellScope(req);
936
995
  bindWs(ws, {
937
996
  openAttach,
938
997
  verifyToken: (t) => preauth || verify(tokenHolder.value, t),
939
- isValidSession: (name) => sessionExists(cfg.tmuxBin, name),
998
+ // Una sessione fuori scope viene trattata come inesistente: il client
999
+ // riceve 4404, lo stesso codice di una sessione che non c'e'. Dire "esiste
1000
+ // ma non puoi" rivelerebbe proprio cio' che lo scope nasconde.
1001
+ isValidSession: (name) => sessionExistsImpl(name) && wsScope.allowsSession(name),
940
1002
  runAction: (sess, action) => runAction(cfg.tmuxBin, sess, action),
941
1003
  countClients: (sess) => attachedClients(cfg.tmuxBin, sess),
942
1004
  defaults: { readonlyDefault: cfg.readonlyDefault, tmuxBin: cfg.tmuxBin },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmmbuto/nexuscrew",
3
- "version": "0.8.52-rc.13",
3
+ "version": "0.8.52-rc.15",
4
4
  "description": "Faithful browser tmux client — attach to live sessions over a real PTY, localhost-only, mobile-easy",
5
5
  "main": "lib/server.js",
6
6
  "bin": {