@mmmbuto/nexuscrew 0.8.52-rc.14 → 0.8.52-rc.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/frontend/dist/assets/{index-BeB9TSrW.js → index-BtioMzYQ.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/cells/scope-guard.js +155 -0
- package/lib/cells/scope.js +112 -0
- package/lib/cli/commands.js +16 -0
- package/lib/mcp/tools.js +5 -1
- package/lib/nodes/commands.js +12 -2
- package/lib/nodes/store.js +35 -0
- package/lib/proxy/federation.js +13 -2
- package/lib/server.js +66 -4
- package/lib/settings/routes.js +1 -1
- package/package.json +1 -1
|
@@ -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.14")}).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.16")}).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,{})]}));
|
package/frontend/dist/index.html
CHANGED
|
@@ -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-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-BtioMzYQ.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-_kotgT8Z.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.8.52-rc.
|
|
1
|
+
{"version":"0.8.52-rc.16"}
|
|
@@ -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/cli/commands.js
CHANGED
|
@@ -61,6 +61,7 @@ Usage:
|
|
|
61
61
|
[--persist]
|
|
62
62
|
nexuscrew nodes rename <name|nodeId> --label TEXT
|
|
63
63
|
nexuscrew nodes visibility <name|nodeId> network|relay-only|selected
|
|
64
|
+
nexuscrew nodes cells <name|nodeId> all|none|Cella1,Cella2
|
|
64
65
|
[--selected NODE_ID,...]
|
|
65
66
|
nexuscrew nodes share <name|nodeId> on|off [--json]
|
|
66
67
|
nexuscrew nodes invite --ssh TARGET [--ssh-port PORT] [--name SLUG]
|
|
@@ -985,6 +986,21 @@ async function dispatchNodes(rest, flags, opts = {}) {
|
|
|
985
986
|
: String(flags.selected).split(',').map((value) => value.trim()).filter(Boolean);
|
|
986
987
|
return { code: nodesCmds.nodesEdit({ ...opts, log, ref, patch: { visibility, selected } }).code };
|
|
987
988
|
}
|
|
989
|
+
// Scope celle (NC-E): quali celle di QUESTO nodo il peer puo' vedere.
|
|
990
|
+
// Diverso da `visibility`, che governa il transito e non l'accesso.
|
|
991
|
+
if (sub === 'cells') {
|
|
992
|
+
const arg = rest[3];
|
|
993
|
+
if (!arg) {
|
|
994
|
+
log('nodes cells: uso `nodes cells <nodo> all|none|Cella1,Cella2`');
|
|
995
|
+
return { code: 1 };
|
|
996
|
+
}
|
|
997
|
+
if (arg === 'all' || arg === 'none') {
|
|
998
|
+
return { code: nodesCmds.nodesEdit({ ...opts, log, ref, patch: { cellVisibility: arg, cells: undefined } }).code };
|
|
999
|
+
}
|
|
1000
|
+
const cells = String(arg).split(',').map((v) => v.trim()).filter(Boolean);
|
|
1001
|
+
if (!cells.length) { log('nodes cells: elenco vuoto — usa `none` se intendi nessuna cella'); return { code: 1 }; }
|
|
1002
|
+
return { code: nodesCmds.nodesEdit({ ...opts, log, ref, patch: { cellVisibility: 'selected', cells } }).code };
|
|
1003
|
+
}
|
|
988
1004
|
if (sub === 'remove') {
|
|
989
1005
|
if (flags.yes !== true) { log('nodes remove: conferma richiesta con --yes'); return { code: 1 }; }
|
|
990
1006
|
return { code: nodesCmds.nodesRemove({ ...opts, log, ref }).code };
|
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
|
-
|
|
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
|
},
|
package/lib/nodes/commands.js
CHANGED
|
@@ -267,9 +267,12 @@ function nodesEdit(opts) {
|
|
|
267
267
|
const node = resolveStoredNode(st, opts.ref || opts.name);
|
|
268
268
|
if (!node) { log(`nodes edit: nodo sconosciuto "${opts.ref || opts.name || ''}"`); return { code: 1, reason: 'unknown-node' }; }
|
|
269
269
|
const supplied = opts.patch && typeof opts.patch === 'object' ? opts.patch : {};
|
|
270
|
+
// Lo scope celle vale per QUALUNQUE peer accoppiato, in entrambe le
|
|
271
|
+
// direzioni: riguarda chi accede alle celle di questo nodo, non chi ha
|
|
272
|
+
// aperto il tunnel.
|
|
270
273
|
const allowed = node.direction === 'inbound'
|
|
271
|
-
? new Set(['label', 'visibility', 'selected'])
|
|
272
|
-
: new Set(['label', 'ssh', 'sshPort', 'autostart']);
|
|
274
|
+
? new Set(['label', 'visibility', 'selected', 'cellVisibility', 'cells'])
|
|
275
|
+
: new Set(['label', 'ssh', 'sshPort', 'autostart', 'cellVisibility', 'cells']);
|
|
273
276
|
const keys = Object.keys(supplied);
|
|
274
277
|
const invalid = keys.find((key) => !allowed.has(key));
|
|
275
278
|
if (invalid) {
|
|
@@ -298,6 +301,13 @@ function nodesEdit(opts) {
|
|
|
298
301
|
log('nodes edit: visibility non valida'); return { code: 1, reason: 'invalid-visibility' };
|
|
299
302
|
}
|
|
300
303
|
if (patch.visibility !== 'selected') delete patch.selected;
|
|
304
|
+
// Stessa simmetria per lo scope celle. `delete` non basterebbe: updateNode
|
|
305
|
+
// fonde patch e nodo esistente, quindi le celle concesse prima
|
|
306
|
+
// sopravviverebbero alla modalita' che le revoca — e tornerebbero buone al
|
|
307
|
+
// primo ritorno a `selected`, concedendo in silenzio cio' che l'operatore
|
|
308
|
+
// credeva di aver tolto. `undefined` invece le fa sparire, perche' parseNode
|
|
309
|
+
// ricostruisce il nodo da zero.
|
|
310
|
+
if (Object.hasOwn(patch, 'cellVisibility') && patch.cellVisibility !== 'selected') patch.cells = undefined;
|
|
301
311
|
|
|
302
312
|
let next;
|
|
303
313
|
try { next = store.updateNode(st, node.name, patch); }
|
package/lib/nodes/store.js
CHANGED
|
@@ -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) {
|
package/lib/proxy/federation.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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/lib/settings/routes.js
CHANGED
|
@@ -72,7 +72,7 @@ const { publicPeeringRoutes } = require('./public-peering-routes.js');
|
|
|
72
72
|
const CONFIG_KEYS = new Set(['roles', 'port', 'wizardDone', 'autoUpdate', 'alternateScreen']);
|
|
73
73
|
const ROLE_KEYS = new Set(['client', 'node']);
|
|
74
74
|
const ADD_KEYS = new Set(['name', 'ssh', 'sshPort', 'remotePort', 'localPort', 'keyPath', 'label']);
|
|
75
|
-
const EDIT_KEYS = new Set(['label', 'ssh', 'sshPort', 'autostart', 'visibility', 'selected']);
|
|
75
|
+
const EDIT_KEYS = new Set(['label', 'ssh', 'sshPort', 'autostart', 'visibility', 'selected', 'cellVisibility', 'cells']);
|
|
76
76
|
const LOCAL_AUDIO_TEST_TEXT = 'NexusCrew audio test.';
|
|
77
77
|
|
|
78
78
|
// Default sensato per il "nome dispositivo" proposto nei form (pairing/invite).
|
package/package.json
CHANGED