@mmmbuto/nexuscrew 0.8.28 → 0.8.29
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/CHANGELOG.md +18 -0
- package/README.md +1 -1
- package/frontend/dist/assets/{index-DiKMLCvW.js → index-BJdAdNOq.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/cli/commands.js +89 -13
- package/lib/cli/doctor.js +25 -0
- package/lib/cli/fleet-service.js +9 -10
- package/lib/cli/init.js +8 -1
- package/lib/diagnostics/store.js +1 -1
- package/lib/fleet/builtin.js +5 -1
- package/lib/fleet/launch.js +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.8.29 — 2026-07-21 — "Stable Fleet Boot"
|
|
6
|
+
|
|
7
|
+
- Keeps the optional Fleet boot companion rooted at the stable user home on Linux, macOS and
|
|
8
|
+
Termux. It can no longer create the shared tmux server from a replaceable npm package directory
|
|
9
|
+
and leave later cells with an orphaned working directory after an update.
|
|
10
|
+
- Makes smart startup inspect and migrate both the main service and the Fleet companion. Startup
|
|
11
|
+
fails closed unless regeneration, service activation and an explicit runtime restart all succeed,
|
|
12
|
+
so an already-active unit cannot keep the old working directory. A private durable marker retries
|
|
13
|
+
interrupted or failed migrations on the next start; a missing optional companion remains a
|
|
14
|
+
non-blocking doctor warning.
|
|
15
|
+
- Validates imported Fleet working directories before persistence and redacts the active Shell
|
|
16
|
+
command together with the existing prompt and environment secret values.
|
|
17
|
+
- Extends source-side diagnostic redaction to macOS `/Users/<name>/...` paths in addition to Linux
|
|
18
|
+
and Android home paths.
|
|
19
|
+
- Gate: **987 isolated Node tests** (986 pass / 1 platform skip), **93/93 frontend component
|
|
20
|
+
tests**, production PWA build and zero production dependency vulnerabilities in both dependency
|
|
21
|
+
trees.
|
|
22
|
+
|
|
5
23
|
## 0.8.28 — 2026-07-21 — "Portable Workspaces"
|
|
6
24
|
|
|
7
25
|
- Makes Fleet backups portable across devices. Version 3 archives store a validated home-relative
|
package/README.md
CHANGED
|
@@ -90,4 +90,4 @@ Minimum version required to store current data is: `+K+`.
|
|
|
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 gr({children:e,onClose:t,label:i,error:s=""}){const o=R.useRef(null),a=R.useRef(null),u=R.useRef(t);return u.current=t,R.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(S=>S.offsetParent!==null),_=requestAnimationFrame(()=>{var S;return(S=f()[0]||p)==null?void 0:S.focus({preventScroll:!0})}),b=S=>{var k;if(S.key==="Escape"){S.preventDefault(),(k=u.current)==null||k.call(u);return}if(S.key!=="Tab")return;const w=f();if(!w.length){S.preventDefault(),p==null||p.focus();return}const y=w[0],x=w[w.length-1];S.shiftKey&&document.activeElement===y?(S.preventDefault(),x.focus()):!S.shiftKey&&document.activeElement===x&&(S.preventDefault(),y.focus())};return document.addEventListener("keydown",b),()=>{cancelAnimationFrame(_),document.removeEventListener("keydown",b),h&&h.isConnected&&typeof h.focus=="function"&&h.focus({preventScroll:!0})}},[]),R.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":i||g("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 nd({token:e,targets:t=[],readonly:i=!1,onPower:s,onImport:o}){const[a,u]=R.useState([]),[h,p]=R.useState(0);R.useEffect(()=>{let k=!0;async function C(){const A=[{route:[],label:g("local")}].concat((t||[]).map(ee=>({route:Array.isArray(ee.route)?ee.route:[],label:ee.label||(ee.route||[]).join(" › ")}))),M=await Promise.all(A.map(async ee=>{const q={...ee,available:!1,readonly:!1,capabilities:[],provider:null,cells:[],unmanaged:[],err:""};try{const B=await Rr(e,ee.route);q.available=!!B.available,q.capabilities=B.capabilities||[],q.provider=B.provider||null,q.cells=(B.cells||[]).map(Z=>({...Z,route:ee.route}));try{q.readonly=!!(await dh(e,ee.route)).readonlyDefault}catch{}try{const Z=await Xv(e,ee.route),H=new Set(q.cells.map(te=>te.tmuxSession).filter(Boolean));q.unmanaged=(Z.sessions||[]).filter(te=>te&&!H.has(te.name))}catch{}}catch(B){q.err=String(B&&B.message||B)}return q}));k&&u(M)}C();const P=setInterval(C,6e3);return()=>{k=!1,clearInterval(P)}},[e,t,h]);const f=(k,C)=>Array.isArray(k.capabilities)&&k.capabilities.includes(C)&&!i&&!k.readonly,_=()=>p(k=>k+1),b=(k,C)=>{s&&s({...k,route:C})},S=(k,C)=>{s&&s({...k,route:C})},w=async(k,C)=>{try{await ho(e,k.cell,C)}catch{}_()},y=async(k,C)=>{if(window.confirm(g("fleet-remove-cell").replace("{id}",k.cell))){try{await qv(e,k.cell,!0,C)}catch{}_()}},x=async(k,C)=>{if(window.confirm(g("terminate-confirm").replace("{name}",k.name))){try{await ch(e,k.name,C)}catch{}_()}};return c.jsxs("div",{className:"nc-fleet-inventory",children:[c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:g("fleet-inventory")}),c.jsx("small",{children:g("fleet-inventory-help")})]}),a.map(k=>{const C=k.route.length?k.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 ${k.available?"on":"warn"}`}),c.jsx("b",{children:k.label}),c.jsx("small",{children:k.available?`${k.cells.length} ${g("fleet-cells")} · ${k.unmanaged.length} ${g("fleet-tmux")}`:k.err||g("fleet-not-available")})]}),k.cells.map(P=>c.jsxs("div",{className:"nc-fleet-item nc-fleet-cell",children:[c.jsxs("span",{children:[c.jsx("b",{children:P.cell}),c.jsx("small",{children:`${P.engine||""}${P.key?`·${P.key}`:""}${P.active?"":` · ${g("cell-off")}`}`})]}),c.jsxs("span",{className:"nc-fleet-cell-actions",children:[P.active&&f(k,"down")&&c.jsx("button",{className:"nc-btn ghost",title:g("power-off"),onClick:()=>S(P,k.route),children:g("stop")}),!P.active&&f(k,"up")&&c.jsx("button",{className:"nc-btn ghost",title:g("power-on"),onClick:()=>b(P,k.route),children:g("start")}),f(k,"restart")&&P.active&&c.jsx("button",{className:"nc-btn ghost",title:g("restart"),onClick:()=>w(P,k.route),children:g("restart")}),f(k,"remove")&&c.jsx("button",{className:"nc-btn danger",title:g("delete"),onClick:()=>y(P,k.route),children:"×"})]})]},`${C}:${P.cell}`)),k.unmanaged.map(P=>c.jsxs("div",{className:"nc-fleet-item nc-fleet-unmanaged",children:[c.jsxs("span",{children:[c.jsx("b",{children:P.name}),c.jsx("small",{children:g("fleet-tmux")})]}),c.jsxs("span",{className:"nc-fleet-cell-actions",children:[f(k,"import")&&o&&c.jsx("button",{className:"nc-btn ghost",title:g("import-as-cell"),onClick:()=>o(P,k.route),children:g("import-as-cell")}),c.jsx("button",{className:"nc-btn danger",title:g("terminate"),onClick:()=>x(P,k.route),children:"×"})]})]},`${C}:u:${P.name}`)),k.available&&k.cells.length===0&&k.unmanaged.length===0&&c.jsx("div",{className:"nc-empty",children:g("fleet-inventory-empty")})]},C)})]})}function kE({cells:e=[],engines:t=[],busy:i,canRestore:s=!1,onRestore:o,onClose:a}){const[u,h]=R.useState("export"),[p,f]=R.useState(()=>new Set(e.map(E=>E.id))),[_,b]=R.useState(()=>new Set(t.map(E=>E.id))),[S,w]=R.useState([]),[y,x]=R.useState([]),[k,C]=R.useState(""),P=new Set(t.map(E=>E.id)),A=new Set(e.map(E=>E.id)),M=(E,j)=>E(D=>{const U=new Set(D);return U.has(j)?U.delete(j):U.add(j),U}),ee=()=>{const E=_E(e,p,t,_);if(E.ok===!1){C(g(`fleet-backup-${E.error}`));return}const j=e.filter(V=>p.has(V.id)).length,D=t.filter(V=>_.has(V.id)).length;if(E.cells.length!==j){C(g("fleet-backup-invalid-cell"));return}if(E.engines.length!==D){C(g("fleet-backup-invalid-engine"));return}if(!E.cells.length&&!E.engines.length){C(g("fleet-backup-select-one"));return}const U=new Blob([`${JSON.stringify(E,null,2)}
|
|
91
91
|
`],{type:"application/json"}),re=URL.createObjectURL(U),I=document.createElement("a");I.href=re,I.download=`nexuscrew-fleet-${new Date().toISOString().slice(0,10)}.json`,document.body.appendChild(I),I.click(),I.remove(),setTimeout(()=>URL.revokeObjectURL(re),0),C("")},q=async E=>{if(C(""),w([]),x([]),!!E){if(E.size>1024*1024){C(g("fleet-backup-too-large"));return}try{const j=yE(await E.text());if(!j.ok){C(g(`fleet-backup-${j.error}`));return}const D=new Set(j.engines.map(U=>U.id));x(j.engines.map(U=>({engine:U,exists:P.has(U.id),selected:!P.has(U.id)}))),w(j.cells.map(U=>{const re=P.has(U.engine)||D.has(U.engine),I=A.has(U.id);return{cell:U,engine:re?U.engine:"",selected:re&&!I,exists:I}}))}catch(j){C(String(j.message||j))}}},B=(E,j)=>w(D=>D.map((U,re)=>re===E?{...U,...j}:U)),Z=(E,j)=>x(D=>D.map((U,re)=>re===E?{...U,...j}:U)),H=new Set([...P,...y.filter(E=>E.selected).map(E=>E.engine.id)]),te=S.filter(E=>E.selected&&E.engine&&H.has(E.engine)),K=y.filter(E=>E.selected),T=[...new Map([...t.map(E=>[E.id,E.label||E.id]),...y.filter(E=>E.selected).map(E=>[E.engine.id,E.engine.label||E.engine.id])]).entries()];return c.jsxs("div",{className:"nc-set-form nc-fleet-form nc-backup-dialog",children:[c.jsx("b",{children:g("fleet-backup")}),c.jsx("small",{children:g("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"),C("")},children:g("fleet-backup-export")}),c.jsx("button",{type:"button",className:`nc-set-tabbtn${u==="import"?" on":""}`,disabled:!s,title:s?"":g("fleet-backup-restore-unavailable"),onClick:()=>{h("import"),C("")},children:g("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(E=>E.id))),b(new Set(t.map(E=>E.id)))},children:g("select-all")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{f(new Set),b(new Set)},children:g("select-none")})]}),c.jsxs("div",{className:"nc-backup-list",children:[c.jsx("b",{children:g("fleet-engines")}),t.map(E=>{var j;return c.jsxs("label",{className:"nc-check nc-backup-row",children:[c.jsx("input",{type:"checkbox",checked:_.has(E.id),onChange:()=>M(b,E.id)}),c.jsxs("span",{children:[c.jsx("b",{children:E.label||E.id}),c.jsxs("small",{children:[E.id,(j=E.envKeys)!=null&&j.length?` · ${E.envKeys.length} ${g("fleet-backup-env-names")}`:""]})]})]},E.id)}),c.jsx("b",{children:g("fleet-cells")}),e.map(E=>c.jsxs("label",{className:"nc-check nc-backup-row",children:[c.jsx("input",{type:"checkbox",checked:p.has(E.id),onChange:()=>M(f,E.id)}),c.jsxs("span",{children:[c.jsx("b",{children:E.id}),c.jsxs("small",{children:[E.engine," · ",g("fleet-system-prompt")," ",(E.prompt||"").length," ",g("characters")]})]})]},E.id)),!e.length&&!t.length&&c.jsx("div",{className:"nc-empty",children:g("fleet-backup-empty")})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:!p.size&&!_.size,onClick:ee,children:g("fleet-backup-download")})]})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{type:"file",accept:"application/json,.json",disabled:i,onChange:E=>q(E.target.files&&E.target.files[0])}),c.jsx("small",{children:g("fleet-backup-import-help")}),!!(S.length||y.length)&&c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(E=>E.map(j=>({...j,selected:!0}))),w(E=>E.map(j=>({...j,selected:!!j.engine})))},children:g("select-all")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(E=>E.map(j=>({...j,selected:!1}))),w(E=>E.map(j=>({...j,selected:!1})))},children:g("select-none")})]}),c.jsxs("div",{className:"nc-backup-list",children:[!!y.length&&c.jsx("b",{children:g("fleet-engines")}),y.map((E,j)=>{var D;return c.jsx("div",{className:"nc-backup-import-row",children:c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:E.selected,onChange:U=>Z(j,{selected:U.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:E.engine.label||E.engine.id}),c.jsxs("small",{children:[E.engine.id," · ",E.exists?g("fleet-backup-overwrite"):g("fleet-backup-new"),(D=E.engine.envKeys)!=null&&D.length?` · ${g("fleet-backup-env-values-required")}`:""]})]})]})},E.engine.id)}),!!S.length&&c.jsx("b",{children:g("fleet-cells")}),S.map((E,j)=>c.jsxs("div",{className:"nc-backup-import-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:E.selected,disabled:!E.engine||!H.has(E.engine),onChange:D=>B(j,{selected:D.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:E.cell.id}),c.jsxs("small",{children:[E.exists?g("fleet-backup-overwrite"):g("fleet-backup-new")," · ",g("fleet-system-prompt")," ",E.cell.systemPrompt.length," ",g("characters")]})]})]}),c.jsxs("select",{value:E.engine,onChange:D=>B(j,{engine:D.target.value,selected:!!D.target.value}),children:[c.jsx("option",{value:"",children:g("fleet-backup-engine-missing")}),T.map(([D,U])=>c.jsx("option",{value:D,children:U},D))]})]},E.cell.id))]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||!te.length&&!K.length,onClick:()=>o({engineRows:y,cellRows:S}),children:g("fleet-backup-restore")})]})]}),k&&c.jsx("div",{className:"nc-err",children:k})]})}function EE({state:e,setState:t,busy:i,onSave:s,catalog:o}){var k;const a=e.form,u=C=>t({...e,form:{...a,...C}}),h=a.envRows||[],p=[...new Map(o.map(C=>[C.client,C.clientLabel||C.client])).entries()],f=o.filter(C=>C.client===a.client),_=xo(o,a),b=typeof(_==null?void 0:_.credentialEnv)=="string"?_.credentialEnv:"",S=Array.isArray(_==null?void 0:_.credentialUsedBy)?_.credentialUsedBy:[],w=(_==null?void 0:_.credentialSource)||"missing",y=!!b&&e.mode==="new"&&(_==null?void 0:_.authConfigured)!==!0&&!a.credentialValue&&!a.allowMissingCredential,x=C=>{C&&u({client:C.client,provider:C.provider,credentialProfile:C.credentialProfile||"",managedModel:C.model||"",protocol:C.protocol||"",permissionPolicy:C.permissionPolicyDefault||"standard",rc:!!C.rc,displayName:C.custom?g("fleet-custom-provider-default"):"",baseUrl:C.custom?"":C.endpoint||"",envKey:C.defaultEnvKey||"",providerId:"nexuscrew-custom",credentialValue:"",credentialReveal:!1,allowMissingCredential:!1,...e.mode==="new"?{id:C.id,label:""}:{}})};return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsx("b",{children:e.mode==="new"?g("fleet-new-engine"):`${g("edit")} ${a.id}`}),c.jsx("input",{value:a.id,disabled:e.mode!=="new",placeholder:"id",onChange:C=>u({id:C.target.value})}),c.jsx("input",{value:a.label,placeholder:g("label"),onChange:C=>u({label:C.target.value})}),c.jsxs("select",{value:a.kind,disabled:e.mode!=="new",onChange:C=>u({kind:C.target.value}),children:[c.jsx("option",{value:"managed",children:g("fleet-managed")}),c.jsx("option",{value:"custom",children:g("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:C=>x(o.find(P=>P.client===C.target.value&&P.default)||o.find(P=>P.client===C.target.value)),children:p.map(([C,P])=>c.jsx("option",{value:C,children:P},C))}),c.jsx("select",{value:((k=xo(o,a))==null?void 0:k.id)||"",disabled:e.mode!=="new",onChange:C=>x(o.find(P=>P.id===C.target.value)),children:f.map(C=>c.jsx("option",{value:C.id,children:C.label},C.id))})]}),a.client!=="shell"&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.managedModel,list:"nc-managed-models",placeholder:g(_!=null&&_.requiresModel?"fleet-model-required":"fleet-model-default"),onChange:C=>u({managedModel:C.target.value})}),c.jsx("datalist",{id:"nc-managed-models",children:[...(_==null?void 0:_.models)||[],...a.modelOptions||[]].filter((C,P,A)=>C&&A.indexOf(C)===P).map(C=>c.jsx("option",{value:C},C))})]}),_!=null&&_.supportsUnsafe?c.jsxs("select",{value:a.permissionPolicy,onChange:C=>u({permissionPolicy:C.target.value}),children:[c.jsx("option",{value:"standard",children:g("fleet-standard-permissions")}),c.jsx("option",{value:"unsafe",children:g("fleet-unsafe-permissions")})]}):c.jsx("small",{children:g("fleet-standard-permissions")}),(_==null?void 0:_.supportsUnsafe)&&a.permissionPolicy==="unsafe"&&c.jsx("small",{className:"nc-err",children:g("fleet-unsafe-warning")}),(_==null?void 0:_.credentialEnv)===!0&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.envKey,placeholder:g("fleet-api-key-env"),onChange:C=>u({envKey:C.target.value})}),c.jsx("small",{children:g("fleet-custom-secret-help")})]}),b&&c.jsxs("section",{className:"nc-fleet-key","aria-label":g("fleet-key-section"),children:[c.jsxs("div",{className:"nc-fleet-key-head",children:[c.jsx("b",{children:g("fleet-key-section")}),c.jsx("span",{children:g(`fleet-credential-source-${w}`)})]}),c.jsxs("label",{className:"nc-field",children:[g("fleet-key-variable"),c.jsx("input",{value:b,readOnly:!0,"aria-label":g("fleet-key-variable")})]}),c.jsx("small",{children:S.length?g("fleet-key-used-by").replace("{engines}",S.join(", ")):g("fleet-key-used-by-none")}),S.length>0&&c.jsx("small",{children:g("fleet-key-shared-impact").replace("{key}",b).replace("{engines}",S.join(", "))}),c.jsxs("div",{className:"nc-fleet-key-input",children:[c.jsx("input",{type:a.credentialReveal?"text":"password",value:a.credentialValue||"","aria-label":g("fleet-key-value").replace("{key}",b),autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",placeholder:_!=null&&_.authConfigured||e.mode==="edit"?g("fleet-key-blank-keeps"):g("fleet-key-enter"),onChange:C=>u({credentialValue:C.target.value,allowMissingCredential:C.target.value?!1:a.allowMissingCredential})}),c.jsx("button",{type:"button",className:"nc-btn ghost","aria-label":g(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal"),onClick:()=>u({credentialReveal:!a.credentialReveal}),children:g(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal")})]}),c.jsx("small",{children:g("fleet-key-local-help")}),e.mode==="new"&&(_==null?void 0:_.authConfigured)!==!0&&!a.credentialValue&&c.jsxs("label",{className:"nc-check nc-fleet-key-missing",children:[c.jsx("input",{type:"checkbox",checked:!!a.allowMissingCredential,onChange:C=>u({allowMissingCredential:C.target.checked})}),g("fleet-key-create-missing")]}),(_==null?void 0:_.notice)&&c.jsx("small",{className:"nc-fleet-provider-notice",children:g(`fleet-provider-notice-${_.notice}`)})]}),a.provider==="custom"&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.displayName,placeholder:g("fleet-provider-display"),onChange:C=>u({displayName:C.target.value})}),c.jsx("input",{value:a.baseUrl,placeholder:"https://api.example.com/v1",onChange:C=>u({baseUrl:C.target.value})}),((_==null?void 0:_.protocols)||[]).length>1&&c.jsx("select",{value:a.protocol,onChange:C=>u({protocol:C.target.value}),children:_.protocols.map(C=>c.jsx("option",{value:C,children:C},C))}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:a.envKey,placeholder:g("fleet-api-key-env"),onChange:C=>u({envKey:C.target.value})}),c.jsx("input",{value:a.providerId,placeholder:g("fleet-provider-id"),onChange:C=>u({providerId:C.target.value})})]}),c.jsxs("small",{children:[a.protocol," · ",g("fleet-custom-secret-help")]})]}),c.jsx("small",{children:g(a.client==="shell"?"fleet-shell-engine-help":"fleet-managed-help")})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.command,placeholder:g("command-path"),onChange:C=>u({command:C.target.value})}),c.jsx("textarea",{value:a.argsText,placeholder:g("args-lines"),onChange:C=>u({argsText:C.target.value})}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:a.rc,onChange:C=>u({rc:C.target.checked})})," remote control"]}),c.jsxs("select",{value:a.promptMode,onChange:C=>u({promptMode:C.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:C=>u({promptFlag:C.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:a.modelFlag,placeholder:"model flag",onChange:C=>u({modelFlag:C.target.value})}),c.jsx("input",{value:a.modelValue,placeholder:"model default",onChange:C=>u({modelValue:C.target.value})})]}),c.jsx("small",{children:g("env-write-only")}),h.map((C,P)=>c.jsxs("div",{className:"nc-fleet-env",children:[c.jsx("input",{value:C.key,disabled:C.configured,placeholder:"ENV_KEY",onChange:A=>{const M=h.slice();M[P]={...C,key:A.target.value},u({envRows:M})}}),c.jsx("input",{type:"password",value:C.value,placeholder:C.configured?"•••••• (unchanged)":"value",onChange:A=>{const M=h.slice();M[P]={...C,value:A.target.value},u({envRows:M})}}),c.jsx("button",{className:"nc-btn danger",onClick:()=>u({envRows:h.filter((A,M)=>M!==P)}),children:"×"})]},`${C.key}-${P}`)),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:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:i||!a.id||y||a.kind==="custom"&&!a.command||a.kind==="managed"&&(_==null?void 0:_.requiresModel)&&!a.managedModel||a.kind==="managed"&&(_==null?void 0:_.credentialEnv)===!0&&!a.envKey||a.kind==="managed"&&a.provider==="custom"&&(!a.displayName||!a.baseUrl||!a.envKey||!a.providerId),onClick:s,children:g("save")})]})]})}function NE({token:e,route:t,targets:i=[],location:s,setLocation:o,state:a,setState:u,engines:h,busy:p,onSave:f}){var q;const[_,b]=R.useState(null),[S,w]=R.useState(""),y=a.form,x=B=>u({...a,form:{...y,...B}}),k=h.find(B=>B.id===y.engine),C=((q=k==null?void 0:k.managed)==null?void 0:q.client)==="shell",P=B=>{var te,K,T,E;const Z=h.find(j=>j.id===B),H={...y.commands||{}};((te=k==null?void 0:k.managed)==null?void 0:te.client)==="shell"&&(y.command?H[y.engine]=y.command:delete H[y.engine]),x({engine:B,model:((K=y.models)==null?void 0:K[B])||((T=Z==null?void 0:Z.managed)==null?void 0:T.model)||((E=Z==null?void 0:Z.model)==null?void 0:E.value)||"",commands:H,command:H[B]||""})},A=B=>{const Z={...y.commands||{}};B?Z[y.engine]=B:delete Z[y.engine],x({command:B,commands:Z})},M=C&&/[|&;<>()`$*?{}\[\]~]/.test(y.command||""),ee=async B=>{try{const Z=await uh(e,B,t);b(Z),x({cwd:Z.path}),w("")}catch(Z){w(String(Z.message||Z))}};return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsx("b",{children:a.mode==="new"?g("fleet-new-cell"):`${g("edit")} ${y.id}`}),a.mode==="new"&&c.jsxs("label",{className:"nc-field",children:[g("location"),c.jsx("span",{className:"nc-req",children:" *"}),c.jsxs("select",{value:s,onChange:B=>{o(B.target.value),x({engine:""})},children:[c.jsx("option",{value:"",children:g("local")}),i.map(B=>c.jsxs("option",{value:B.route.join("/"),disabled:B.status&&B.status!=="up",children:[B.label,B.status&&B.status!=="up"?` · ${g("node-offline")}`:""]},B.route.join("/")))]})]}),c.jsx("input",{value:y.id,disabled:a.mode!=="new",placeholder:"id",onChange:B=>x({id:B.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:y.cwd,placeholder:g("cwd"),onChange:B=>x({cwd:B.target.value})}),c.jsx("button",{className:"nc-btn ghost",onClick:()=>_?b(null):ee(y.cwd),children:g("browse")})]}),_&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:_.path}),c.jsxs("div",{className:"nc-fs-list",children:[_.parent&&c.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>ee(_.parent),children:["↑ ",g("fs-parent")]}),(_.dirs||[]).map(B=>c.jsxs("button",{className:"nc-fs-item",onClick:()=>ee(`${_.path.replace(/\/$/,"")}/${B}`),children:["📁 ",B]},B))]})]}),S&&c.jsx("div",{className:"nc-err",children:S}),c.jsx("select",{value:y.engine,onChange:B=>P(B.target.value),children:h.map(B=>c.jsx("option",{value:B.id,children:B.label},B.id))}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:!!y.boot,onChange:B=>x({boot:B.target.checked})})," boot"]}),C?c.jsxs(c.Fragment,{children:[c.jsx("input",{value:y.command||"",maxLength:4096,placeholder:g("fleet-shell-command-placeholder"),onChange:B=>A(B.target.value)}),c.jsx("small",{children:y.command?g("fleet-shell-command-help"):g("fleet-shell-interactive")}),M&&c.jsx("small",{className:"nc-note",children:g("fleet-shell-command-metachar")})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{value:y.model||"",list:"nc-cell-models",placeholder:g("fleet-model-override"),onChange:B=>x({model:B.target.value})}),c.jsx("datalist",{id:"nc-cell-models",children:((k==null?void 0:k.availableModels)||[]).map(B=>c.jsx("option",{value:B},B))}),c.jsx("textarea",{value:y.prompt||"",placeholder:"prompt",onChange:B=>x({prompt:B.target.value})})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>u(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:p||!y.id||!y.cwd||!y.engine,onClick:f,children:g("save")})]})]})}function Tv({token:e,route:t=[],state:i,setState:s,busy:o,onSave:a}){const[u,h]=R.useState(null),[p,f]=R.useState([]),[_,b]=R.useState(""),S=Array.isArray(t)?t.join("/"):"";R.useEffect(()=>{let C=!0;return lh(e,S?S.split("/"):[]).then(P=>{C&&(f(P.engines||[]),b(""))}).catch(P=>{C&&(f([]),b(String(P.message||P)))}),()=>{C=!1}},[e,S]);const w=i.form,y=C=>s({...i,form:{...w,...C}}),x=async C=>{try{const P=await uh(e,C,t);h(P),y({cwd:P.path})}catch{}},k=!w.id&&w.tmuxSession?w.tmuxSession.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,32):w.id;return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsxs("b",{children:[g("import-as-cell")," · ",w.tmuxSession]}),c.jsx("small",{children:g("import-help")}),c.jsx("input",{value:w.tmuxSession,disabled:!0,placeholder:"tmux session",readOnly:!0}),c.jsx("input",{value:w.id,placeholder:g("name"),onChange:C=>y({id:C.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:w.cwd,placeholder:g("cwd"),onChange:C=>y({cwd:C.target.value})}),c.jsx("button",{className:"nc-btn ghost",type:"button",onClick:()=>u?h(null):x(w.cwd),children:g("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:()=>x(u.parent),children:["↑ ",g("fs-parent")]}),(u.dirs||[]).map(C=>c.jsxs("button",{className:"nc-fs-item",onClick:()=>x(`${u.path.replace(/\/$/,"")}/${C}`),children:["📁 ",C]},C))]})]}),c.jsxs("select",{value:w.engine,onChange:C=>y({engine:C.target.value}),children:[c.jsx("option",{value:"",children:g("import-engine-required")}),p.map(C=>c.jsx("option",{value:C.id,children:C.label},C.id))]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:!!w.boot,onChange:C=>y({boot:C.target.checked})})," boot"]}),(i.err||_)&&c.jsx("div",{className:"nc-err",children:i.err||_}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>s(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:o||!w.tmuxSession||!w.engine||!p.length,onClick:a,children:g(k?"import-as-cell":"save")})]})]})}function Ga(e){const t=wr(e);return t===null?`~/${e}`:t?`~/${t}`:"~"}function RE(e){if(!e||typeof e.path!="string"||typeof e.home!="string")return"";const{path:t,home:i}=e;return!i||t===i||!t.startsWith(i+"/")?"":t.slice(i.length+1).replace(/\/+$/,"")}function PE({token:e,route:t,cell:i,busy:s=!1,onSaved:o,onClose:a}){const[u,h]=R.useState(""),[p,f]=R.useState(null),[_,b]=R.useState(!1),[S,w]=R.useState(""),y=typeof(i==null?void 0:i.cwdSuggestion)=="string"&&wr(i.cwdSuggestion)!==null?wr(i.cwdSuggestion):null,[x,k]=R.useState(y),C=i==null?void 0:i.id,P=s||_,A=async H=>{const te=wr(H);if(te===null){f({rel:"",dirs:[],loading:!1,err:g("fleet-cwd-repair-invalid")});return}f({rel:te,dirs:[],loading:!0,err:""});try{const K=await uh(e,te,t),T=RE(K);f({rel:T,dirs:Array.isArray(K.dirs)?K.dirs:[],loading:!1,err:""})}catch(K){f({rel:te,dirs:[],loading:!1,err:String(K&&K.message||K)})}},M=H=>{if(!p)return;const te=p.rel?`${p.rel}/${H}`:H;A(te)},ee=()=>{p!=null&&p.rel&&A(p.rel.split("/").slice(0,-1).join("/"))},q=()=>{p&&(h(p.rel||""),f(null),w(""),k(null))},B=()=>{x!==null&&(h(x),k(null),w(""))},Z=async()=>{if(P)return;const H=wr(u);if(H===null){w(g("fleet-cwd-repair-invalid"));return}const te=Ga(H);let K=!0;try{K=window.confirm(g("fleet-cwd-repair-confirm").replace("{id}",String(C||"")).replace("{path}",te))}catch{K=!1}if(K){b(!0),w(""),k(null);try{const T=await Kv(e,C,{cwdRel:H},t);o==null||o(T)}catch(T){const E=T&&T.data||{};if(E.code==="unportable-cwd"){w(g("fleet-cwd-repair-unportable"));const j=Array.isArray(E.cells)?E.cells.find(U=>U&&U.id===C):null,D=j&&typeof j.suggestion=="string"?j.suggestion:null;D!==null&&wr(D)!==null&&k(D)}else w(String(T&&T.message||T))}finally{b(!1)}}};return c.jsxs("div",{className:"nc-set-form nc-fleet-form nc-cwd-repair",children:[c.jsx("b",{children:g("fleet-cwd-repair-title").replace("{id}",String(C||""))}),c.jsx("small",{className:"nc-set-info",children:g("fleet-cwd-repair-help")}),c.jsx("small",{className:"nc-cwd-no-source",children:g("fleet-cwd-repair-no-source-path")}),c.jsxs("label",{className:"nc-field",children:[g("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:g("fleet-cwd-repair-placeholder"),autoComplete:"off",spellCheck:"false",autoCapitalize:"none",onChange:H=>h(H.target.value)}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:P,onClick:()=>p?f(null):A(""),children:g(p?"close":"fleet-cwd-repair-browse")})]})]}),c.jsx("div",{className:"nc-cwd-preview",children:g("fleet-cwd-repair-preview").replace("{path}",Ga(u))}),p&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:Ga(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:ee,children:["↑ ",g("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:g("fleet-cwd-repair-fs-empty")}),!p.loading&&p.dirs.map(H=>c.jsxs("button",{type:"button",className:"nc-fs-item",onClick:()=>M(H),children:["📁 ",H]},H))]}),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:P||p.loading||!!p.err,onClick:q,children:g("fleet-cwd-repair-use-current")})})]}),S&&c.jsx("div",{className:"nc-err",role:"alert",children:S}),x!==null&&c.jsxs("div",{className:"nc-cwd-suggestion",role:"status",children:[c.jsx("span",{children:g("fleet-cwd-repair-suggestion")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:B,children:g("fleet-cwd-repair-use-suggestion").replace("{path}",Ga(x))})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:_,onClick:a,children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:P,onClick:Z,children:_?"…":g("save")})]})]})}function jE({token:e,readonly:t,targets:i=[],startNewCell:s=!1,initialLocation:o=""}){const[a,u]=R.useState({engines:[],cells:[],managedCatalog:[]}),[h,p]=R.useState({available:!1,capabilities:[]}),[f,_]=R.useState(!1),[b,S]=R.useState(null),[w,y]=R.useState(null),[x,k]=R.useState(!1),[C,P]=R.useState(""),[A,M]=R.useState(""),[ee,q]=R.useState(o),[B,Z]=R.useState(!1),[H,te]=R.useState(null),[K,T]=R.useState(null),[E,j]=R.useState(null),[D,U]=R.useState(!1),[re,I]=R.useState([]),[V,$]=R.useState(null),[L,z]=R.useState("manage"),oe=R.useRef(!1),ce=ee?ee.split("/"):[],he=R.useCallback(async()=>{try{const ae=await Rr(e,ce);p(ae);try{const xe=await dh(e,ce);Z(!!xe.readonlyDefault)}catch{Z(!1)}if(ae.provider==="builtin"&&(ae.capabilities||[]).includes("definitions")){const xe=await lh(e,ce),de=new Map((ae.engines||[]).map(Ne=>[Ne.id,Ne]));if(xe.engines=(xe.engines||[]).map(Ne=>{var Ee;return{...Ne,availableModels:((Ee=de.get(Ne.id))==null?void 0:Ee.models)||[]}}),u(xe),(ae.capabilities||[]).includes("credentials"))try{I((await QS(e,ce)).credentials||[])}catch{I([])}else I([])}P("")}catch(ae){P(String(ae.message||ae))}finally{_(!0)}},[e,ee]);R.useEffect(()=>{he();const ae=setInterval(he,5e3);return()=>clearInterval(ae)},[he]);const me=new Set((h.cells||[]).filter(ae=>ae.active).map(ae=>ae.cell)),G=h.provider==="builtin"&&(h.capabilities||[]).includes("edit"),J=(h.capabilities||[]).includes("restore");R.useEffect(()=>{!s||oe.current||!G||!a.engines.length||(oe.current=!0,y({mode:"new",form:Mv(a.engines[0].id)}))},[s,G,a.engines]);const ge=async ae=>{k(!0),P(""),M("");try{await ae(),await he()}catch(xe){P(String(xe.message||xe))}k(!1)},Me=()=>ge(async()=>{var Ce;const ae=b.mode==="new",xe=b.form,de=a.managedCatalog||[],Ne=xe.kind==="managed"?xo(de,xe):null,Ee=typeof(Ne==null?void 0:Ne.credentialEnv)=="string"?Ne.credentialEnv:"",X=Ee&&xe.credentialValue||"",ie=CE(xe,ae,de);!ae&&b.form.kind==="custom"&&!b.form.modelFlag&&(ie.model=null),!ae&&b.form.kind==="custom"&&b.form.promptMode!=="flag"&&(ie.promptFlag=null);let F;if(ae)F=await ZS(e,ie,ce);else{const _e=b.original,Re=new Set(b.form.envRows.filter(ze=>!ze.remove).map(ze=>ze.key)),Qe=(_e.envKeys||[]).filter(ze=>!Re.has(ze)),zt=Object.fromEntries(b.form.envRows.filter(ze=>!ze.remove&&ze.key&&(!ze.configured||ze.value!=="")).map(ze=>[ze.key,ze.value]));F=await e0(e,_e.id,ie,b.form.kind==="custom"?{set:zt,remove:Qe}:void 0,ce)}let W=null;if(Ee&&X)try{W=await xm(e,Ee,X,ce),I(W.credentials||[])}catch{throw await he(),ae&&S({mode:"edit",original:{...ie,id:xe.id},form:{...xe,credentialReveal:!1}}),new Error(g(ae?"fleet-key-partial-create":"fleet-key-partial-edit"))}await he(),S(null),M(g("fleet-saved"));const ue=((Ce=((W==null?void 0:W.credentials)||[]).find(_e=>_e.envKey===Ee))==null?void 0:Ce.activeCells)||[],ve=[...new Set([...(F==null?void 0:F.activeCells)||[],...ue])];if(ve.length&&window.confirm(g("fleet-restart-confirm").replace("{cells}",ve.join(", "))))for(const _e of ve)await ho(e,_e,ce)}),Ze=()=>ge(async()=>{var F,W,ue,ve,Ce,_e;const ae=w.mode==="new",xe=w.form,de={...ae?{id:xe.id}:{},cwd:xe.cwd,engine:xe.engine,boot:!!xe.boot},Ne={...xe.commands||{}},Ee=a.engines.find(Re=>Re.id===xe.engine);((F=Ee==null?void 0:Ee.managed)==null?void 0:F.client)==="shell"&&typeof xe.command=="string"&&xe.command.length?Ne[xe.engine]=xe.command:((W=Ee==null?void 0:Ee.managed)==null?void 0:W.client)==="shell"&&delete Ne[xe.engine],(Object.keys(Ne).length||!ae)&&(de.commands=Ne),ae?(((ue=Ee==null?void 0:Ee.managed)==null?void 0:ue.client)!=="shell"&&xe.model&&(de.model=xe.model),((ve=Ee==null?void 0:Ee.managed)==null?void 0:ve.client)!=="shell"&&xe.prompt&&(de.prompt=xe.prompt)):(de.model=((Ce=Ee==null?void 0:Ee.managed)==null?void 0:Ce.client)==="shell"?null:xe.model||null,((_e=Ee==null?void 0:Ee.managed)==null?void 0:_e.client)!=="shell"&&(de.prompt=xe.prompt||null));const X=ae?await i0(e,de,ce):await Kv(e,w.original.id,de,ce),ie=ae?xe.id:w.original.id;y(null),M(g("fleet-saved")),!ae&&(X!=null&&X.active)&&window.confirm(g("fleet-restart-confirm").replace("{cells}",ie))&&await ho(e,ie,ce)}),Be=t||B,Fe=ae=>re.find(xe=>{var de;return(de=xe.engines)==null?void 0:de.includes(ae.id)})||null,ct=()=>ge(async()=>{var de;const ae=await xm(e,V.envKey,V.value,ce);I(ae.credentials||[]),$(null),M(g("fleet-credential-saved"));const xe=((de=(ae.credentials||[]).find(Ne=>Ne.envKey===V.envKey))==null?void 0:de.activeCells)||[];if(xe.length&&window.confirm(g("fleet-restart-confirm").replace("{cells}",xe.join(", "))))for(const Ne of xe)await ho(e,Ne,ce)}),Ve=ae=>ge(async()=>{if(!window.confirm(g("fleet-credential-remove-confirm").replace("{key}",ae.envKey)))return;const xe=await JS(e,ae.envKey,ce);I(xe.credentials||[]),M(g("fleet-credential-removed"))}),gt=ae=>te({...ae,route:Array.isArray(ae==null?void 0:ae.route)?ae.route:ce}),ut=async ae=>{if(!H)return;const xe=H.cell||H.id,de=Array.isArray(H.route)?H.route:ce;ae.action==="up"?await ah(e,{cell:xe,boot:!!ae.boot,...ae.engine?{engine:ae.engine}:{},...ae.model!==void 0?{model:ae.model}:{},...ae.permissionPolicy?{permissionPolicy:ae.permissionPolicy}:{}},de):await sl(e,{cell:xe,boot:!!ae.boot},de)},lt=(ae,xe)=>{P(""),T({mode:"new",route:Array.isArray(xe)?xe:ce,form:{tmuxSession:ae.name,id:"",engine:"",cwd:"",boot:!1},err:""})},nt=()=>ge(async()=>{const ae=K.form;if(!ae.engine){T({...K,err:g("import-engine-required")});return}await n0(e,{tmuxSession:ae.tmuxSession,id:ae.id||void 0,engine:ae.engine,cwd:ae.cwd||void 0,boot:!!ae.boot},ae.route||ce),T(null),M(g("fleet-saved"))}),dt=({engineRows:ae=[],cellRows:xe=[]})=>ge(async()=>{const de=ae.filter(Ce=>Ce.selected),Ne=de.map(Ce=>By(Ce.engine));if(Ne.some(Ce=>!Ce))throw new Error(g("fleet-backup-invalid-engine"));const Ee=de.filter(Ce=>Ce.exists).map(Ce=>Ce.engine.id);if(Ee.length&&!window.confirm(g("fleet-backup-confirm-engine-overwrite").replace("{engines}",Ee.join(", "))))return;const X=[...new Set([...a.engines.map(Ce=>Ce.id),...Ne.map(Ce=>Ce.id)])],ie=[];for(const Ce of xe.filter(_e=>_e.selected)){const _e=wE(Ce.cell,Ce.engine,X);if(!_e)throw new Error(`${Ce.cell.id}: ${g("fleet-backup-engine-missing")}`);ie.push(_e)}const F=xe.filter(Ce=>Ce.selected&&Ce.exists).map(Ce=>Ce.cell.id);if(F.length&&!window.confirm(g("fleet-backup-confirm-overwrite").replace("{cells}",F.join(", "))))return;const W=Ne.length?await s0(e,Ne,Ee.length>0,ce):{needsRestart:[]},ue=ie.length?await r0(e,ie,ce):{needsRestart:[]},ve=[...new Set([...W.needsRestart||[],...ue.needsRestart||[]])];U(!1),M(`${g("fleet-backup-restored").replace("{n}",String(ie.length))}${Ne.length?` · ${Ne.length} ${g("fleet-engines").toLowerCase()}`:""}${ve.length?` · ${g("fleet-backup-needs-restart").replace("{cells}",ve.join(", "))}`:""}`)}),rt=c.jsxs("label",{className:"nc-field",children:[g("location"),c.jsxs("select",{value:ee,onChange:ae=>{q(ae.target.value),S(null),y(null),P(""),M(""),Z(!1),_(!1),p({available:!1,capabilities:[]}),u({engines:[],cells:[],managedCatalog:[]}),I([]),$(null)},children:[c.jsx("option",{value:"",children:g("local")}),i.map(ae=>c.jsxs("option",{value:ae.route.join("/"),disabled:ae.status&&ae.status!=="up",children:[ae.label,ae.status&&ae.status!=="up"?` · ${g("node-offline")}`:""]},ae.route.join("/")))]})]}),ot=c.jsxs("div",{className:"nc-fleet-view-tabs",role:"tablist","aria-label":g("tab-fleet"),children:[c.jsx("button",{type:"button",role:"tab","aria-selected":L==="manage",className:`nc-set-tabbtn${L==="manage"?" on":""}`,onClick:()=>z("manage"),children:g("fleet-manage-location")}),c.jsx("button",{type:"button",role:"tab","aria-selected":L==="overview",className:`nc-set-tabbtn${L==="overview"?" on":""}`,onClick:()=>z("overview"),children:g("fleet-network-overview")})]});return f?G?c.jsxs("div",{className:"nc-set-tab nc-fleet-editor",children:[ot,L==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-overview-help")}),c.jsx(nd,{token:e,targets:i,readonly:t,onPower:gt,onImport:lt})]}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-manage-help")}),rt,c.jsxs("div",{className:"nc-set-row nc-fleet-backup-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>{P(""),U(!0)},children:g("fleet-backup")}),c.jsx("small",{children:g("fleet-backup-help")})]}),c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:g("fleet-cells")}),c.jsx("span",{className:"nc-fleet-head-actions",children:c.jsxs("button",{className:"nc-btn primary",disabled:Be||x||!a.engines.length,onClick:()=>{var ae;P(""),y({mode:"new",form:Mv((ae=a.engines[0])==null?void 0:ae.id)})},children:["+ ",g("add")]})})]}),a.cells.map(ae=>{const xe=me.has(ae.id),de=h.capabilities||[],Ne=ae.needsRepair===!0;return c.jsxs("div",{className:"nc-fleet-item",children:[c.jsxs("span",{children:[c.jsx("b",{children:ae.id}),c.jsxs("small",{children:[ae.engine," · ",Ne?c.jsx("span",{className:"nc-fleet-tag nc-fleet-tag-warn",children:g("fleet-cwd-needs-repair")}):ae.cwd,xe?` · ${g("service-active")}`:` · ${g("cell-off")}`]})]}),c.jsxs("span",{children:[xe&&de.includes("down")&&c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>ge(()=>sl(e,{cell:ae.id},ce)),children:g("stop")}),!xe&&de.includes("up")&&c.jsx("button",{className:"nc-btn primary",disabled:Be||x,onClick:()=>gt({cell:ae.id,id:ae.id,engine:ae.engine,model:ae.model,models:ae.models,permissionPolicies:ae.permissionPolicies,active:!1,boot:ae.boot}),children:g("start")}),xe&&de.includes("restart")&&c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>ge(()=>ho(e,ae.id,ce)),children:g("restart")}),Ne?c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>{P(""),j(ae)},children:g("fleet-cwd-repair")}):c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>{var Ee;P(""),y({mode:"edit",original:ae,form:{...ae,commands:{...ae.commands||{}},command:((Ee=ae.commands)==null?void 0:Ee[ae.engine])||""}})},children:g("edit")}),c.jsx("button",{className:"nc-btn danger",disabled:Be||x,onClick:()=>ge(async()=>{window.confirm(g("fleet-remove-cell").replace("{id}",ae.id))&&await qv(e,ae.id,!0,ce)}),children:"×"})]})]},ae.id)}),c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:g("fleet-engines")}),c.jsxs("button",{className:"nc-btn primary",disabled:Be||x,onClick:()=>{P(""),S({mode:"new",form:SE()})},children:["+ ",g("add")]})]}),a.engines.map(ae=>{var de,Ne;const xe=Fe(ae);return c.jsxs("div",{className:"nc-fleet-item",children:[c.jsxs("span",{children:[c.jsx("b",{children:ae.label}),c.jsx("small",{children:ae.managed?`${ae.id} · ${ae.managed.client} / ${ae.managed.provider} · ${(de=ae.managedInfo)!=null&&de.configured?g("fleet-ready"):((Ne=ae.managedInfo)==null?void 0:Ne.reason)||g("fleet-not-ready")}`:`${ae.id} · ${ae.command}`}),xe&&c.jsxs("small",{children:[xe.envKey," · ",g(`fleet-credential-source-${xe.source||"missing"}`)]})]}),c.jsxs("span",{children:[xe&&c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>{P(""),$({envKey:xe.envKey,value:""})},children:xe.configured?g("fleet-credential-change"):g("fleet-credential-set")}),(xe==null?void 0:xe.source)==="local"&&c.jsx("button",{className:"nc-btn danger",disabled:Be||x,onClick:()=>Ve(xe),children:g("fleet-credential-forget")}),c.jsx("button",{className:"nc-btn ghost",disabled:Be||x,onClick:()=>{P(""),S({mode:"edit",original:ae,form:xE(ae)})},children:g("edit")}),c.jsx("button",{className:"nc-btn danger",disabled:Be||x,onClick:()=>ge(async()=>{window.confirm(g("fleet-remove-engine").replace("{id}",ae.id))&&await t0(e,ae.id,ce)}),children:"×"})]})]},ae.id)})]}),b&&c.jsx(gr,{onClose:()=>S(null),label:g("fleet-new-engine"),error:C,children:c.jsx(EE,{state:b,setState:S,busy:x,onSave:Me,catalog:a.managedCatalog||[]})}),w&&c.jsx(gr,{onClose:()=>y(null),label:g("fleet-new-cell"),error:C,children:c.jsx(NE,{token:e,route:ce,targets:i,location:ee,setLocation:q,state:w,setState:y,engines:a.engines,busy:x,onSave:Ze})}),E&&c.jsx(gr,{onClose:()=>j(null),label:g("fleet-cwd-repair-title").replace("{id}",E.id),error:"",children:c.jsx(PE,{token:e,route:ce,cell:E,busy:x,onSaved:async()=>{j(null),M(g("fleet-cwd-repaired")),await he()},onClose:()=>j(null)})}),A&&c.jsx("div",{className:"nc-set-note",children:A}),C&&c.jsx("div",{className:"nc-err",children:C}),D&&c.jsx(gr,{onClose:()=>U(!1),label:g("fleet-backup"),error:C,children:c.jsx(kE,{cells:a.cells,engines:a.engines,busy:x,canRestore:J,onRestore:dt,onClose:()=>U(!1)})}),V&&c.jsx(gr,{onClose:()=>$(null),label:g("fleet-credential-title"),error:C,children:c.jsxs("div",{className:"nc-fleet-form nc-credential-form",children:[c.jsx("b",{children:g("fleet-credential-title")}),c.jsx("small",{children:g("fleet-credential-help")}),c.jsxs("label",{className:"nc-field",children:[V.envKey,c.jsx("input",{type:"password",autoComplete:"off",spellCheck:"false",autoCapitalize:"none",value:V.value,onChange:ae=>$({...V,value:ae.target.value})})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>$(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:x||!V.value,onClick:ct,children:g("save")})]})]})}),K&&c.jsx(gr,{onClose:()=>T(null),label:g("import-as-cell"),error:C,children:c.jsx(Tv,{token:e,route:K.route||ce,state:K,setState:T,busy:x,onSave:nt})}),H&&c.jsx(ol,{cell:H,token:e,route:Array.isArray(H.route)?H.route:ce,onConfirm:async ae=>{try{await ut(ae)}finally{await he()}},onClose:()=>te(null)})]}):c.jsxs("div",{className:"nc-set-tab",children:[ot,L==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-overview-help")}),c.jsx(nd,{token:e,targets:i,readonly:t,onPower:gt,onImport:lt})]}):c.jsxs(c.Fragment,{children:[rt,c.jsxs("div",{className:"nc-set-info",children:[g(C?"fleet-editor-load-error":"fleet-editor-unavailable"),!C&&h.reason?` ${h.reason}`:""]})]}),C&&c.jsx("div",{className:"nc-err",children:C}),K&&c.jsx(gr,{onClose:()=>T(null),label:g("import-as-cell"),error:C,children:c.jsx(Tv,{token:e,route:K.route||ce,state:K,setState:T,busy:x,onSave:nt})}),H&&c.jsx(ol,{cell:H,token:e,route:Array.isArray(H.route)?H.route:ce,onConfirm:async ae=>{try{await ut(ae)}finally{await he()}},onClose:()=>te(null)})]}):c.jsxs("div",{className:"nc-set-tab",children:[ot,L==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-overview-help")}),c.jsx(nd,{token:e,targets:i,readonly:t,onPower:gt,onImport:lt})]}):c.jsxs(c.Fragment,{children:[rt,c.jsx("div",{className:"nc-set-info",children:g("fleet-editor-loading")})]})]})}function DE({text:e}){const[t,i]=R.useState(!1),s=async()=>{try{await navigator.clipboard.writeText(e),i(!0),setTimeout(()=>i(!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(et,{name:"copy",size:14})," ",g(t?"copied":"copy")]})]})}function LE({value:e}){const[t,i]=R.useState("");return R.useEffect(()=>{let s=!0;return Uk.toDataURL(e,{margin:1,width:220}).then(o=>{s&&i(o)}).catch(()=>{}),()=>{s=!1}},[e]),t?c.jsx("img",{src:t,width:"220",height:"220",alt:"NexusCrew pairing QR"}):null}function ME({token:e,nodes:t,roster:i,settings:s,readonly:o,refresh:a,refreshAliases:u}){const[h,p]=R.useState(null),[f,_]=R.useState(null),[b,S]=R.useState({}),[w,y]=R.useState(null),[x,k]=R.useState({ssh:"",sshPort:"",name:""}),[C,P]=R.useState(""),[A,M]=R.useState(""),[ee,q]=R.useState(""),[B,Z]=R.useState(!1),[H,te]=R.useState(null),[K,T]=R.useState(null),E=Date.now(),j=s&&s.deviceName||"",D=(t||[]).filter(G=>G&&G.direction==="outbound"&&G.name&&G.ssh),U=D.find(G=>G.name===C)||D[0]||null,re=D.find(G=>G.name===A)||D[0]||null,I=re?Hm(re.tunnel,E):null,V=re!=null&&re.shared?I!=null&&I.up?"share-local-active":"share-local-pending":I!=null&&I.up?"share-local-private":"share-local-private-down",$=[{key:"peer-group-hubs",rows:(t||[]).filter(G=>G.kind!=="transitive"&&G.relation!=="client")},{key:"peer-group-clients",rows:(t||[]).filter(G=>G.kind!=="transitive"&&G.relation==="client")},{key:"peer-group-routed",rows:(t||[]).filter(G=>G.kind==="transitive")}],L=async(G,J)=>{p(null),_(`${G}:${J}`);try{const ge=await hh(e,G,J);J==="test"&&S(Me=>({...Me,[G]:ge})),await a()}catch(ge){p(`${G}: ${String(ge.message||ge)}`)}_(null)},z=async G=>{p(null),_(`${G}:remove`);try{await p0(e,G),T(null),await a()}catch(J){p(`${G}: ${String(J.message||J)}`)}_(null)},oe=G=>te({name:G.name,direction:G.direction,label:G.label||G.name,ssh:G.ssh||"",sshPort:G.sshPort?String(G.sshPort):"",autostart:G.autostart===!0,visibility:G.visibility||"network",selected:[...G.selected||[]]}),ce=async()=>{if(!H||!ds(H.label)){p(g("err-label"));return}const G=H.direction==="inbound"?{label:H.label,visibility:H.visibility,selected:H.visibility==="selected"?H.selected:[]}:{label:H.label,ssh:H.ssh,autostart:H.autostart,...H.sshPort?{sshPort:Number(H.sshPort)}:{}};p(null),_(`${H.name}:edit`);try{await f0(e,H.name,G),te(null),await a()}catch(J){p(`${H.name}: ${String(J.message||J)}`)}_(null)},he=async G=>{if(re){p(null),_(`${re.name}:share`);try{await m0(e,re.name,G),await a()}catch(J){const ge=J!=null&&J.data&&typeof J.data.hint=="string"?J.data.hint:"";p(`${re.name}: ${String(J.message||J)}${ge?` — ${ge}`:""}`)}_(null)}},me=async()=>{if(p(null),U){const ge=$m({name:U.name,ssh:U.ssh,sshPort:U.sshPort||""});if(!ge.ok){p(g(ge.error));return}_("invite");try{y(await km(e,{ssh:ge.value.ssh,...ge.value.sshPort?{sshPort:ge.value.sshPort}:{}},[U.name]))}catch(Me){p(String(Me.message||Me))}_(null);return}const G=Zn(x.name||ee||j||"NexusCrew"),J=$m({name:G,ssh:x.ssh,sshPort:x.sshPort});if(!J.ok){p(g(J.error));return}_("invite");try{y(await km(e,{...ee||j?{label:ee||j}:{},name:G,ssh:J.value.ssh,...J.value.sshPort?{sshPort:J.value.sshPort}:{}}))}catch(ge){p(String(ge.message||ge))}_(null)};return c.jsxs("div",{className:"nc-set-tab",children:[c.jsx("div",{className:"nc-sheet-label",children:g("peer-inventory-title")}),(t||[]).length===0&&c.jsx("div",{className:"nc-empty",children:g("no-nodes")}),$.map(G=>G.rows.length>0&&c.jsxs("div",{className:"nc-peer-group",children:[c.jsxs("div",{className:"nc-peer-group-title",children:[g(G.key)," ",c.jsx("span",{children:G.rows.length})]}),G.rows.map(J=>{var Fe,ct;const ge=J.kind==="transitive",Me=ge?{up:J.stale!==!0,label:J.stale===!0?"peer-routed-stale":"peer-routed"}:Hm(J.tunnel,E),Ze=b[J.name],Be=J.actions||{};return c.jsxs("div",{className:`nc-set-node${ge?" routed":""}`,children:[c.jsxs("div",{className:"nc-set-node-head",children:[c.jsx("span",{className:`nc-dot ${Me.up?"on":""}`}),c.jsx("b",{children:J.label||J.name}),c.jsxs("small",{children:[J.name,ge?` · ${J.route.join(" → ")}`:J.direction==="outbound"?` · SSH ${J.ssh||""}`:` · ${g("node-connected-client")}`,(Fe=J.tunnel)!=null&&Fe.transport?` · ${J.tunnel.transport} ${g("transport-used")}`:""]}),c.jsxs("span",{className:`nc-set-tunnel${Me.up?" up":""}`,children:[g(Me.label),Me.since?` · ${Me.since}`:""]})]}),!ge&&J.direction==="inbound"&&c.jsx("div",{className:"nc-set-info",children:g(J.shared?"peer-shared":"peer-private")}),Be.visibility&&J.shared&&c.jsxs(c.Fragment,{children:[c.jsxs("select",{value:J.visibility||"network",disabled:o||!!f,onChange:async Ve=>{_(`${J.name}:visibility`);try{await Nm(e,J.name,Ve.target.value),await a()}catch(gt){p(String(gt.message||gt))}_(null)},children:[c.jsx("option",{value:"network",children:g("visibility-network")}),c.jsx("option",{value:"relay-only",children:g("visibility-relay")}),c.jsx("option",{value:"selected",children:g("visibility-selected")})]}),J.visibility==="selected"&&c.jsx("div",{className:"nc-set-row",children:(t||[]).filter(Ve=>Ve.name!==J.name&&Ve.nodeId).map(Ve=>{const gt=(J.selected||[]).includes(Ve.nodeId);return c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:gt,disabled:o||!!f,onChange:async ut=>{const lt=ut.target.checked?[...J.selected||[],Ve.nodeId]:(J.selected||[]).filter(nt=>nt!==Ve.nodeId);_(`${J.name}:visibility`);try{await Nm(e,J.name,"selected",lt),await a()}catch(nt){p(String(nt.message||nt))}_(null)}})," ",Ve.label||Ve.name]},Ve.nodeId)})})]}),c.jsxs("div",{className:"nc-set-node-actions",children:[Be.edit&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>oe(J),children:g("edit")}),Be.test&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>L(J.name,"test"),children:g("node-test")}),Be.disconnect&&Me.up&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>L(J.name,"down"),children:g("tunnel-stop")}),Be.connect&&!Me.up&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>L(J.name,"up"),children:g("tunnel-start")}),Be.restart&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>L(J.name,"restart"),children:g("tunnel-restart")}),Be.remove&&c.jsxs("button",{type:"button",className:"nc-btn danger",disabled:o||!!f,title:g(o?"settings-readonly":"delete"),onClick:()=>T(J.name),children:[c.jsx(et,{name:"trash",size:14})," ",g("delete")]})]}),(H==null?void 0:H.name)===J.name&&c.jsxs("div",{className:"nc-set-form nc-node-editor",children:[c.jsxs("label",{className:"nc-field",children:[g("node-display-label"),c.jsx("input",{value:H.label,disabled:!!f,onChange:Ve=>te({...H,label:Ve.target.value})})]}),H.direction==="outbound"?c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"nc-field",children:[g("node-ssh-label"),c.jsx("input",{value:H.ssh,disabled:!!f,onChange:Ve=>te({...H,ssh:Ve.target.value})})]}),c.jsxs("label",{className:"nc-field",children:[g("node-ssh-port-label"),c.jsx("input",{inputMode:"numeric",value:H.sshPort,disabled:!!f,onChange:Ve=>te({...H,sshPort:Ve.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:H.autostart,disabled:!!f,onChange:Ve=>te({...H,autostart:Ve.target.checked})})," ",g("boot-persist")]})]}):c.jsxs("label",{className:"nc-field",children:[g("peer-visibility"),c.jsxs("select",{value:H.visibility,disabled:!!f,onChange:Ve=>te({...H,visibility:Ve.target.value}),children:[c.jsx("option",{value:"network",children:g("visibility-network")}),c.jsx("option",{value:"relay-only",children:g("visibility-relay")}),c.jsx("option",{value:"selected",children:g("visibility-selected")})]})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:!!f,onClick:ce,children:g("save")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>te(null),children:g("cancel")})]})]}),K===J.name&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("b",{children:g("node-remove-confirm").replace("{name}",J.label||J.name)}),c.jsx("small",{children:g("node-remove-warning")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn danger",disabled:!!f,onClick:()=>z(J.name),children:g("delete")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>T(null),children:g("cancel")})]})]}),((ct=J.health)==null?void 0:ct.detail)&&c.jsx("div",{className:`nc-set-test${J.health.status==="healthy"?" ok":J.health.status==="passive"?"":" ko"}`,children:J.health.detail}),Ze&&c.jsxs("div",{className:`nc-set-test${Ze.ok?" ok":" ko"}`,children:[Ze.result,Ze.detail?` — ${Ze.detail}`:""]})]},`${J.kind||"direct"}:${J.nodeId||J.name}`)})]},G.key)),(i||[]).some(G=>!G.direct&&G.instanceId)&&c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("routed-node-aliases")}),c.jsx("small",{className:"nc-set-hint",children:g("routed-node-aliases-help")}),(i||[]).filter(G=>!G.direct&&G.instanceId).map(G=>c.jsxs("div",{className:"nc-set-node",children:[c.jsxs("div",{className:"nc-set-node-head",children:[c.jsx("span",{className:`nc-dot ${G.status==="up"?"on":""}`}),c.jsx("b",{children:G.label||G.name}),c.jsx("small",{children:(G.route||[]).join(" › ")})]}),c.jsxs("div",{className:"nc-set-node-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:async()=>{const J=window.prompt(g("node-local-alias"),G.alias||"");if(J!=null){if(J.trim()&&!ds(J.normalize("NFC"))){p(g("err-label"));return}_(`${G.instanceId}:alias`),p(null);try{J.trim()?await h0(e,G.instanceId,J):await Em(e,G.instanceId),u()}catch(ge){p(String(ge.message||ge))}_(null)}},children:g("alias-on-device")}),G.alias&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:async()=>{_(`${G.instanceId}:alias-reset`),p(null);try{await Em(e,G.instanceId),u()}catch(J){p(String(J.message||J))}_(null)},children:g("reset-alias")})]})]},G.instanceId))]}),re&&c.jsxs("div",{className:"nc-set-form nc-local-share",children:[c.jsx("div",{className:"nc-sheet-label",children:g("share-local-heading")}),D.length>1&&c.jsxs("label",{className:"nc-field",children:[g("share-local-hub"),c.jsx("select",{value:re.name,disabled:o||!!f,onChange:G=>M(G.target.value),children:D.map(G=>c.jsx("option",{value:G.name,children:G.label||G.name},G.name))})]}),c.jsxs("label",{className:"nc-check nc-node-share",children:[c.jsx("input",{type:"checkbox",checked:re.shared===!0,disabled:o||!!f,onChange:G=>he(G.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:g("share-local-through").replace("{device}",j||g("local")).replace("{hub}",re.label||re.name)}),c.jsx("small",{children:g(re.shared?"share-node-on-desc":"share-node-off-desc")})]})]}),c.jsx("div",{className:`nc-set-test${re.shared&&(I!=null&&I.up)?" ok":I!=null&&I.up?"":" ko"}`,children:g(V)}),!(I!=null&&I.up)&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>he(re.shared===!0),children:g("share-local-reconnect")})]}),c.jsx(Py,{token:e,deviceDefault:j,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:g("invite-node")}),c.jsx("small",{className:"nc-set-hint",children:g(U?"invite-network-hint":"invite-v2-hint")}),U?c.jsxs(c.Fragment,{children:[D.length>1&&c.jsxs("label",{className:"nc-field",children:[g("invite-network-label"),c.jsx("select",{value:U.name,disabled:o||!!f,onChange:G=>{P(G.target.value),y(null)},children:D.map(G=>c.jsx("option",{value:G.name,children:G.label||G.name},G.name))})]}),c.jsxs("div",{className:"nc-set-info nc-invite-endpoint",children:[g("invite-network-via"),": ",c.jsx("b",{children:U.label||U.name})," · ",U.ssh,U.sshPort?`:${U.sshPort}`:""]}),c.jsx("small",{className:"nc-set-hint",children:g("invite-network-route")})]}):c.jsxs("label",{className:"nc-field",children:[g("invite-endpoint-label"),c.jsx("input",{placeholder:"user@host",value:x.ssh,disabled:o,onChange:G=>k({...x,ssh:G.target.value})}),c.jsx("small",{className:"nc-set-hint",children:g("invite-endpoint-needed")})]}),!U&&B&&c.jsxs("div",{className:"nc-invite-advanced",children:[c.jsxs("label",{className:"nc-field",children:[g("node-ssh-port-label"),c.jsx("input",{inputMode:"numeric",placeholder:"22",value:x.sshPort,disabled:o,onChange:G=>k({...x,sshPort:G.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),c.jsxs("label",{className:"nc-field",children:[g("device-name-label"),c.jsx("input",{placeholder:j||"NexusCrew",value:ee,disabled:o,onChange:G=>q(G.target.value)}),c.jsx("small",{className:"nc-set-hint",children:g("device-name-hint")})]}),c.jsxs("label",{className:"nc-field",children:[g("node-name-label"),c.jsx("input",{placeholder:Zn(ee||j||g("node-name-ph")),value:x.name,disabled:o,onChange:G=>k({...x,name:G.target.value?Zn(G.target.value):""})})]})]}),c.jsxs("div",{className:"nc-set-row nc-invite-actions",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:o||!!f||!U&&!x.ssh.trim(),onClick:me,children:g("create-pairing-link")}),!U&&c.jsxs("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>Z(G=>!G),children:[B?"▾":"▸"," ",g("pair-advanced")]})]}),w&&c.jsxs(c.Fragment,{children:[c.jsx(LE,{value:w.pairingUrl}),c.jsx(DE,{text:w.pairingUrl}),c.jsx("div",{className:"nc-set-info",children:g("invite-next-steps")})]})]}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function TE({token:e,readonly:t}){const[i,s]=R.useState("idle"),[o,a]=R.useState(!1),[u,h]=R.useState(null);R.useEffect(()=>{let f=!1;return iE().then(_=>{f||s(_)}),()=>{f=!0}},[]);const p=async()=>{h(null),a(!0);try{i==="subscribed"?(await rE(e),s("idle")):(await nE(e),s("subscribed"))}catch(f){const _=String(f.message||f);_==="push-denied"?(s("denied"),h(g("push-denied"))):_==="push-unsupported"?(s("unsupported"),h(g("push-unsupported"))):h(_)}a(!1)};return i==="unsupported"?c.jsx("div",{className:"nc-set-info",children:g("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||i==="denied",title:t?g("settings-readonly"):"",onClick:p,children:g(i==="subscribed"?"push-disable":"push-enable")}),c.jsx("span",{className:"nc-set-info",children:g(i==="subscribed"?"push-on":i==="denied"?"push-denied":"push-off")})]}),u&&c.jsx("div",{className:"nc-err",children:u})]})}function AE({token:e,roster:t=[],readonly:i}){const s=[{key:"local",route:[],label:g("diagnostics-local"),status:"up"},...t.filter(D=>Array.isArray(D.route)&&D.route.length).map(D=>({key:D.route.join("/"),route:D.route,label:D.label||D.name,status:D.status}))],[o,a]=R.useState("local"),u=s.find(D=>D.key===o)||s[0],[h,p]=R.useState(null),[f,_]=R.useState([]),[b,S]=R.useState(""),[w,y]=R.useState(!1),[x,k]=R.useState(!0),[C,P]=R.useState(900),[A,M]=R.useState("all"),[ee,q]=R.useState(""),B=R.useRef(0),Z=R.useRef(null),H=R.useCallback(async()=>{try{const D=await y0(e,u.route);if(p(D),S(""),!w){const U=await w0(e,{after:B.current,limit:200},u.route);Array.isArray(U.records)&&U.records.length&&(B.current=U.cursor,_(re=>[...re,...U.records].slice(-500)))}}catch(D){S(D&&D.status===404?g("diagnostics-unsupported"):String(D.message||D))}},[e,u.key,w]);R.useEffect(()=>{B.current=0,_([]),p(null),S("")},[u.key]),R.useEffect(()=>{let D=null,U=!0;const re=()=>{!U||document.visibilityState==="hidden"||H()},I=()=>{D&&clearInterval(D),D=document.visibilityState==="hidden"?null:setInterval(re,2e3),document.visibilityState!=="hidden"&&re()};return document.addEventListener("visibilitychange",I),I(),()=>{U=!1,D&&clearInterval(D),document.removeEventListener("visibilitychange",I)}},[H]),R.useEffect(()=>{x&&Z.current&&(Z.current.scrollTop=Z.current.scrollHeight)},[f,x]);const te=f.filter(D=>(A==="all"||D.level===A)&&(!ee||String(D.component||"").toLowerCase().includes(ee.toLowerCase()))),K=te.map(D=>`${D.ts} ${D.level.toUpperCase()} ${D.component} ${D.code} ${D.message}${Object.keys(D.meta||{}).length?` ${JSON.stringify(D.meta)}`:""}`).join(`
|
|
92
92
|
`),T=async D=>{S("");try{const U=await D();p(U),await H()}catch(U){S(String(U.message||U))}},E=async()=>{try{await navigator.clipboard.writeText(K)}catch{S(g("copy-manual"))}},j=()=>{try{const D=new Blob([`${JSON.stringify(te,null,2)}
|
|
93
|
-
`],{type:"application/json"}),U=URL.createObjectURL(D),re=document.createElement("a");re.href=U,re.download="nexuscrew-diagnostics.json",re.click(),URL.revokeObjectURL(U)}catch{S(g("diagnostics-export-failed"))}};return c.jsxs("div",{className:"nc-set-tab nc-diagnostics",children:[c.jsxs("label",{className:"nc-field",children:[g("diagnostics-target"),c.jsx("select",{value:u.key,onChange:D=>a(D.target.value),children:s.map(D=>c.jsxs("option",{value:D.key,children:[D.label,D.status!=="up"?` · ${D.status}`:""]},D.key))})]}),c.jsxs("div",{className:"nc-set-form nc-diag-controls",children:[c.jsx("div",{className:"nc-sheet-label",children:g("diagnostics-verbose")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("select",{"aria-label":g("diagnostics-duration"),value:C,onChange:D=>P(Number(D.target.value)),disabled:i,children:[300,900,1800,3600].map(D=>c.jsxs("option",{value:D,children:[D/60," min"]},D))}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||(h==null?void 0:h.verbose)===!0,onClick:()=>T(()=>Rm(e,!0,C,u.route)),children:g("enable")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||(h==null?void 0:h.verbose)!==!0,onClick:()=>T(()=>Rm(e,!1,C,u.route)),children:g("stop")})]}),c.jsx("small",{className:"nc-set-hint",children:h!=null&&h.verbose?g("diagnostics-expires").replace("{time}",new Date(h.expiresAt).toLocaleTimeString()):g("diagnostics-off")})]}),c.jsxs("div",{className:"nc-diag-filters",children:[c.jsxs("select",{"aria-label":g("diagnostics-level"),value:A,onChange:D=>M(D.target.value),children:[c.jsx("option",{value:"all",children:g("diagnostics-all-levels")}),["debug","info","warn","error"].map(D=>c.jsx("option",{value:D,children:D},D))]}),c.jsx("input",{"aria-label":g("diagnostics-component"),placeholder:g("diagnostics-component"),value:ee,onChange:D=>q(D.target.value)})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:w,onChange:D=>y(D.target.checked)})," ",g("diagnostics-pause")]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:x,onChange:D=>k(D.target.checked)})," ",g("diagnostics-autoscroll")]})]}),c.jsx("pre",{ref:Z,className:"nc-diag-log","aria-label":g("diagnostics-log"),children:K||g("diagnostics-empty")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:E,children:g("copy")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:j,children:g("diagnostics-export")}),c.jsx("button",{type:"button",className:"nc-btn danger",disabled:i,onClick:()=>{window.confirm(g("diagnostics-clear-confirm"))&&T(async()=>{const D=await S0(e,u.route);return B.current=0,_([]),D})},children:g("diagnostics-clear")})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})}function IE({token:e,settings:t,readonly:i,refresh:s}){const[o,a]=R.useState(null),[u,h]=R.useState(null),[p,f]=R.useState(!1),[_,b]=R.useState(!1),[S,w]=R.useState(null),[y,x]=R.useState(!0);R.useEffect(()=>{w(t&&t.update||null),x(!t||t.autoUpdate!==!1)},[t]);const k=async()=>{a(null),h(null),b(!0);try{const B=await c0(e);h(B.note||"ok"),f(!1)}catch(B){a(String(B.message||B))}b(!1)},C=async()=>{a(null),h(null),b(!0);try{const B=await g0(e);h(`${B.note||"ok"}${B.target?` (${B.target})`:""}`)}catch(B){a(String(B.message||B))}b(!1)},P=async B=>{a(null),h(null),b(!0);try{await Gv(e,{autoUpdate:B}),x(B),s&&await s()}catch(Z){a(String(Z.message||Z))}b(!1)},A=async()=>{a(null),h(null),b(!0);try{w(await v0(e))}catch(B){a(String(B.message||B))}b(!1)},M=async()=>{a(null),h(null),b(!0);try{const B=await _0(e);w(B),h(g("npm-update-restarting"))}catch(B){a(String(B.message||B))}b(!1)},ee=()=>{window.confirm(g("composer-clear-confirm"))&&(a(null),GC()?(window.dispatchEvent(new Event(Xd)),h(g("composer-clear-done"))):a(g("composer-clear-failed")))},q=t&&t.service;return c.jsxs("div",{className:"nc-set-tab",children:[t&&c.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,c.jsx("br",{}),q&&q.installed?g("service-installed"):g("service-missing"),q&&q.installed?` · ${q.active?g("service-active"):g("service-inactive")}`:"",q?` · boot ${q.boot?"on":"off"}`:""]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||_,title:i?g("settings-readonly"):"",onClick:()=>{a(null),h(null),f(!0)},children:g("token-rotate")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||_,title:i?g("settings-readonly"):"",onClick:C,children:g("service-regenerate")})]}),c.jsxs("div",{className:"nc-set-form nc-update-settings",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:y,disabled:i||_||S&&!S.supported,onChange:B=>P(B.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:g("npm-auto-update")}),c.jsx("small",{children:g("npm-auto-update-help")})]})]}),S&&c.jsxs("div",{className:"nc-set-info",children:[g("npm-update-current")," v",S.current,S.latest?` · ${g("npm-update-latest")} v${S.latest}`:""," · ",g(`npm-update-${S.supported?S.phase:"unsupported"}`)]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||_||!S||!S.supported,onClick:A,children:g("npm-update-check")}),S&&S.available&&c.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||_||S.phase==="installing",onClick:M,children:g("npm-update-install")})]}),S&&S.lastError&&c.jsx("div",{className:"nc-err",children:S.lastError})]}),c.jsx(TE,{token:e,readonly:i}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("composer-clear-data")}),c.jsx("small",{className:"nc-set-hint",children:g("composer-clear-data-help")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:ee,children:g("composer-clear-data")})})]}),p&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("div",{children:g("token-rotate-explain")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:_,onClick:()=>f(!1),children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:_,onClick:k,children:g("confirm")})]})]}),u&&c.jsx("div",{className:"nc-set-note",children:u}),o&&c.jsx("div",{className:"nc-err",children:o})]})}function BE({token:e,onClose:t,initialTab:i="nodes",initialLocation:s="",startNewCell:o=!1}){ji();const[a,u]=R.useState(i),[h,p]=R.useState(null),[f,_]=R.useState([]),[b,S]=R.useState(!1),[w,y]=R.useState(null),[x,k]=R.useState(0),C=ph(e,!0,x),P=R.useCallback(async()=>{try{const A=await Yv(e);p(A),y(null)}catch(A){y(String(A.message||A))}try{const A=await a0(e);_(A.peers||[])}catch(A){y(String(A.message||A))}},[e]);return R.useEffect(()=>{P();const A=setInterval(P,5e3);return()=>clearInterval(A)},[P]),R.useEffect(()=>{let A=!1;return Vt("/api/config",e).then(M=>M.json()).then(M=>{A||S(!!M.readonlyDefault)}).catch(()=>{}),()=>{A=!0}},[e]),c.jsx("div",{className:"nc-set-overlay",onClick:t,children:c.jsxs("div",{className:"nc-set-panel",onClick:A=>A.stopPropagation(),children:[c.jsxs("div",{className:"nc-set-head",children:[c.jsx(et,{name:"gear",size:18}),c.jsx("b",{children:g("settings")}),c.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:g("close"),children:c.jsx(et,{name:"x",size:18})})]}),b&&c.jsx("div",{className:"nc-set-readonly",children:g("settings-readonly")}),w&&c.jsx("div",{className:"nc-err",children:w}),c.jsx("div",{className:"nc-set-tabs",children:["nodes","fleet","diagnostics","system"].map(A=>c.jsx("button",{type:"button",className:`nc-set-tabbtn${a===A?" on":""}`,onClick:()=>u(A),children:g(`tab-${A}`)},A))}),c.jsxs("div",{className:"nc-set-body",children:[a==="nodes"&&c.jsx(ME,{token:e,nodes:f,roster:C,settings:h,readonly:b,refresh:P,refreshAliases:()=>k(A=>A+1)}),a==="fleet"&&c.jsx(jE,{token:e,readonly:b,startNewCell:o,initialLocation:s,targets:C.map(A=>({route:A.route,label:A.label||A.name,status:A.status}))}),a==="diagnostics"&&c.jsx(AE,{token:e,roster:C,readonly:b}),a==="system"&&c.jsx(IE,{token:e,settings:h,readonly:b,refresh:P})]})]})})}function OE({token:e,initialPair:t,deviceDefault:i="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:u}){ji();const[h,p]=R.useState(t?"pair":"welcome"),[f,_]=R.useState(!1),[b,S]=R.useState(null),w=async()=>{_(!0),S(null);try{await Gv(e,{wizardDone:!0}),a&&a(),u()}catch(y){S(String(y.message||y)),_(!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:g("wizard-title")}),c.jsx("small",{children:g("hydra-simple")})]}),h==="welcome"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("local-ready")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:w,children:g("local-only")}),c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:()=>p("pair"),children:g("add-node")})]})]}),h==="pair"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx(Py,{token:e,initial:t||"",autoStart:!!t,deviceDefault:i,localNodeId:s,localNameDefault:o,onBusyChange:_,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:g("back")})})]}),h==="done"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("node-connected")}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:w,children:g("finish")})})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})})}function zE(e,t){if(typeof EventSource>"u"||!e)return()=>{};const i=new EventSource(`/api/events?token=${encodeURIComponent(e)}`);return i.onmessage=s=>{try{const o=JSON.parse(s.data);o&&typeof o=="object"&&typeof o.type=="string"&&t(o)}catch{}},()=>{try{i.close()}catch{}}}const FE=6e3,$E=12e3;function HE({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:g("close"),children:c.jsx(et,{name:"x",size:14})})]})}function WE({ask:e,token:t,onAnswered:i}){const[s,o]=R.useState(""),[a,u]=R.useState(!1),[h,p]=R.useState(null),f=async _=>{const b=String(_||"").trim();if(!(!b||a)){p(null),u(!0);try{await x0(t,e.id,b),i(e.id)}catch(S){p(String(S.message||S))}u(!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("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(_=>c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:a,onClick:()=>f(_),children:_},_))}),c.jsxs("div",{className:"nc-ask-reply",children:[c.jsx("textarea",{rows:2,placeholder:g("ask-reply-ph"),value:s,disabled:a,onChange:_=>o(_.target.value)}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:a||!s.trim(),onClick:()=>f(s),children:g("send")})]}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function UE({token:e}){ji();const[t,i]=R.useState([]),[s,o]=R.useState([]),[a,u]=R.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),h=R.useRef(0),p=R.useCallback(b=>{i(S=>S.filter(w=>w.key!==b))},[]),f=R.useCallback(b=>{const S=`t${h.current+=1}`;i(w=>[...w.slice(-3),{...b,key:S}]),setTimeout(()=>p(S),b.urgency==="high"?$E:FE)},[p]);R.useEffect(()=>{if(!e)return;let b=!1;b0(e).then(w=>{b||o(w.asks||[])}).catch(()=>{});const S=zE(e,w=>{w.type==="notify"?f(w):w.type==="ask"&&w.ask&&w.ask.id?o(y=>y.some(x=>x.id===w.ask.id)?y:[...y,w.ask]):w.type==="ask-answered"&&w.id&&o(y=>y.filter(x=>x.id!==w.id))});return()=>{b=!0,S()}},[e,f]);const _=b=>o(S=>S.filter(w=>w.id!==b));return c.jsxs(c.Fragment,{children:[t.length>0&&c.jsx("div",{className:"nc-ntf-toasts",children:t.map(b=>c.jsx(HE,{n:b,onClose:()=>p(b.key)},b.key))}),s.length>0&&!a&&c.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>u(!0),title:g("asks-title"),children:["? ",c.jsx("span",{className:"nc-ask-count",children:s.length})]}),s.length>0&&a&&c.jsxs("div",{className:"nc-ask-panel",children:[c.jsxs("div",{className:"nc-ask-panel-head",children:[c.jsx("b",{children:g("asks-title")}),c.jsx("span",{className:"nc-ask-count",children:s.length}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>u(!1),title:g("close"),children:c.jsx(et,{name:"x",size:16})})]}),c.jsx("div",{className:"nc-ask-panel-body",children:s.map(b=>c.jsx(WE,{ask:b,token:e,onAnswered:_},b.id))})]})]})}const rd=e=>ln(Cr(e)).length===0,sd=e=>Array.isArray(e)?e.join("/"):"";function KE(e){const t=new Set,i=[];for(const s of Array.isArray(e)?e:[])!s||!Hi.test(String(s.instanceId||""))||!Array.isArray(s.route)||!s.route.length||t.has(s.instanceId)||(t.add(s.instanceId),i.push({instanceId:s.instanceId,route:[...s.route],label:String(s.label||s.name||s.route.join(" › ")),status:s.status||"offline"}));return i.sort((s,o)=>s.label.localeCompare(o.label))}function uo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function vr(e,t,i,s=!1,o=!0){const a=t.instanceId;return{...e,id:ls(s?null:a,e.name),ownerId:a,ownerRoute:s?[]:[...t.route],ownerLabel:s?"Local":t.label,local:s,available:o,ownerTopology:Array.isArray(i)?i:[],layout:Qd(e.layout,a,i)}}function qE(e,t,i,s,o=[]){const[a,u]=R.useState([]),[h,p]=R.useState(!1),[f,_]=R.useState("idle"),[b,S]=R.useState(""),[w,y]=R.useState(""),x=R.useRef([]),k=R.useRef([]),C=R.useRef(""),P=R.useRef(t),A=R.useRef(i),M=R.useRef(!1),ee=R.useRef(!0),q=R.useRef(""),B=R.useMemo(()=>KE(o),[o]),Z=B.map($=>`${$.instanceId}:${sd($.route)}:${$.status}:${$.label}`).join("|");k.current=B,A.current=i,P.current=t,x.current=a;const H=R.useCallback($=>Jd($.layout,C.current,k.current),[]),te=R.useCallback(($,L=!0,z=P.current)=>{const oe=Jg($,Ya()),ce=x.current.some(me=>me.id===z);x.current=oe,u(oe);const he=oe.find(me=>me.id===z);if(L&&he){ee.current=!0;const me=H(he);s(me),he.local&&yk(he.name,me)}else ce&&!he&&(ee.current=!0,s(as()),S("deck non più condiviso dal nodo owner"));mk(oe.filter(me=>me.local).map(me=>me.name))},[s,H]),K=R.useCallback(async $=>{let L=await za(e);const z=L.decks.find(oe=>oe.name==="main");if(L.decks.length===1&&z&&z.revision===0&&rd(z.layout)){const oe=pk(),ce=Cr(Zd("main")||as());if(!rd(ce)){const he=Qd(ce,$,[]);L.decks[0]=await Fa(e,"main",he,0,[])}for(const he of oe.filter(me=>me!=="main"))try{let me=await Pm(e,he,[]);const G=Cr(Zd(he)||as());rd(G)||(me=await Fa(e,he,Qd(G,$,[]),me.revision,[]))}catch{}L=await za(e)}return L},[e]),T=R.useCallback(async({migrate:$=!1}={})=>{const L=await dh(e,[]),z=Hi.test(String(L.instanceId||""))?L.instanceId:"";if(!z)throw new Error("instanceId locale non disponibile");C.current=z,y(z);const[oe,ce]=await Promise.all([$?K(z):za(e),Cm(e,[]).catch(()=>({nodes:[]}))]),he={instanceId:z,route:[],label:"Local"},me=oe.decks.map(J=>vr(J,he,ce.nodes,!0,!0)),G=x.current;return await Promise.all(k.current.map(async J=>{if(J.status!=="up"){me.push(...G.filter(ge=>!ge.local&&ge.ownerId===J.instanceId).map(ge=>({...ge,ownerRoute:[...J.route],ownerLabel:J.label,available:!1})));return}try{const[ge,Me]=await Promise.all([za(e,J.route),Cm(e,J.route).catch(()=>({nodes:[]}))]);me.push(...ge.decks.map(Ze=>vr(Ze,J,Me.nodes,!1,!0)))}catch{me.push(...G.filter(Me=>!Me.local&&Me.ownerId===J.instanceId).map(Me=>({...Me,ownerRoute:[...J.route],ownerLabel:J.label,available:!1})))}})),me.sort((J,ge)=>J.local===ge.local?J.ownerLabel.localeCompare(ge.ownerLabel)||(J.name==="main"?-1:ge.name==="main"?1:J.name.localeCompare(ge.name)):J.local?-1:1),me},[e,K,Z]);R.useEffect(()=>{if(!e)return;let $=!1;const L=q.current!==e;return q.current=e,T({migrate:L}).then(z=>{if($)return;const oe=x.current.find(me=>me.id===P.current),ce=z.find(me=>me.id===P.current),he=ce&&(!oe||ce.revision>oe.revision||oe.available!==ce.available||sd(oe.ownerRoute)!==sd(ce.ownerRoute));te(z,L||!!he&&!M.current),p(!0),ce&&S("")}).catch(z=>{$||(p(!0),S(String(z.message||z)))}),()=>{$=!0}},[te,T,Z,e]);const E=R.useCallback(async($=P.current)=>{if(!h||!M.current)return!0;const L=x.current.find(z=>z.id===$);if(!L)return S(`deck inesistente: ${$}`),!1;if(L.available===!1)return S(`nodo owner offline: ${L.ownerLabel}`),!1;_("saving");try{const z=Gg(Cr(A.current),L.ownerId,L.ownerTopology),oe=await Fa(e,L.name,z,L.revision,L.ownerRoute),ce=vr(oe,uo(L),L.ownerTopology,L.local,!0);return te(x.current.map(he=>he.id===$?ce:he),!1),M.current=!1,_("saved"),S(""),setTimeout(()=>_("idle"),1500),!0}catch(z){if(_("error"),S(String(z.message||z)),z.status===409&&z.data&&z.data.current){const oe=x.current.find(he=>he.id===$),ce=vr(z.data.current,uo(oe),oe.ownerTopology,oe.local,!0);te(x.current.map(he=>he.id===$?ce:he),!1)}return!1}},[h,e,te]);return R.useEffect(()=>{if(!h)return;if(ee.current){ee.current=!1;return}M.current=!0,_("saving");const $=setTimeout(E,650);return()=>clearTimeout($)},[i,h,E]),R.useEffect(()=>{if(!h)return;const $=setInterval(async()=>{try{const L=await T(),z=x.current.find(he=>he.id===P.current),oe=L.find(he=>he.id===P.current),ce=oe&&(!z||oe.revision>z.revision||z.available!==oe.available);te(L,ce&&!M.current)}catch{}},5e3);return()=>clearInterval($)},[h,T,te]),{decks:a,records:a,localNodeId:w,ready:h,saveState:f,error:b,setError:S,saveNow:E,select:async $=>{if(M.current&&!await E(P.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const L=x.current.find(z=>z.id===$);if(!L)throw new Error(`deck inesistente: ${$}`);return M.current=!1,ee.current=!0,H(L)},add:async($,L=null)=>{const z=L===Sn?x.current.find(he=>he.local):L?x.current.find(he=>he.ownerId===L):null;if(!z||z.available===!1)throw new Error("nodo owner non disponibile");const oe=await Pm(e,$,z.ownerRoute),ce=vr(oe,uo(z),z.ownerTopology,z.local,!0);return te([...x.current,ce],!1),ce},rename:async($,L)=>{const z=x.current.find(G=>G.id===$);if(!z)throw new Error("deck inesistente");if($===P.current&&M.current&&!await E($))throw new Error(`salvataggio di "${z.name}" fallito: rinomina annullata`);const oe=x.current.find(G=>G.id===$);if(!oe)throw new Error("deck inesistente");const ce=await C0(e,oe.name,L,oe.revision,oe.ownerRoute),he=vr(ce,uo(oe),oe.ownerTopology,oe.local,!0),me=oe.local?Sn:oe.ownerId;return Mu(vk(Ya(),me,$,he.id)),te(x.current.map(G=>G.id===$?he:G),!1),he},remove:async $=>{const L=x.current.find(oe=>oe.id===$);if(!L)throw new Error("deck inesistente");$===P.current&&(M.current=!1),await k0(e,L.name,L.revision,L.ownerRoute);const z=L.local?Sn:L.ownerId;Mu(_k(Ya(),z,$)),te(x.current.filter(oe=>oe.id!==$),!1)},reorder:($,L)=>{const z=x.current.find(J=>J.id===$),oe=x.current.find(J=>J.id===L);if(!z||!oe)return!1;const ce=z.local?Sn:z.ownerId,he=oe.local?Sn:oe.ownerId;if(ce!==he)return!1;const me=x.current.filter(J=>(J.local?Sn:J.ownerId)===ce).map(J=>J.id),G=Mu(gk(Ya(),ce,$,L,me));return te(Jg(x.current,G),!1),!0},addTileTo:async($,L)=>{const z=x.current.find(J=>J.id===$);if(!z)throw new Error("deck inesistente");if(z.available===!1)throw new Error(`nodo owner offline: ${z.ownerLabel}`);const oe=yy(L,C.current,k.current);if(!oe)throw new Error("riferimento sessione non valido");const ce=H(z),he=Gg(f_(ce,oe),z.ownerId,z.ownerTopology),me=await Fa(e,z.name,he,z.revision,z.ownerRoute),G=vr(me,uo(z),z.ownerTopology,z.local,!0);return te(x.current.map(J=>J.id===$?G:J),!1),G},localMainId:ls(null,"main"),parseDeckId:jh}}const VE="/sw.js",th="nc-sw-update";let ih=null,nh=!1,rh=null,zy=Object.freeze({needed:!1,kind:null,version:""}),Fy=!1;function $y(){typeof window<"u"&&window.dispatchEvent(new Event(th))}function Hy(){const e=rh||(nh?{kind:"reload",version:""}:null);zy=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function Av(){return zy}function YE(e){return typeof window>"u"?()=>{}:(window.addEventListener(th,e),()=>window.removeEventListener(th,e))}function Ah(e){nh!==e&&(nh=e,Hy(),$y())}function XE(e,t,i){let s=null;e&&t&&e!==t?s={kind:"install",version:e}:t&&i&&t!==i&&(s={kind:"reload",version:t});const o=JSON.stringify(s)===JSON.stringify(rh);rh=s,Hy(),o||$y()}function GE(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&Ah(!0)})}function QE(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{Fy&&location.reload()}),navigator.serviceWorker.register(VE).then(e=>{ih=e,e.waiting&&Ah(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&GE(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function JE(){Fy=!0,Ah(!1);const e=ih&&ih.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function ZE({hash:e="",origin:t="",pathname:i="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${i||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const u=new URLSearchParams(o),h=u.get("token")||"",p=u.get("pair")||"",f=p?`${t||""}${i||""}#pair=${p}`:"";return{token:h,pair:f,nextUrl:a}}const Wy=9,Uy=24,Ky="nc_side_w",Iv="nc_side_min",e2=240,Bv="(min-width:1024px) and (pointer:fine)";function t2(){const e=Number(localStorage.getItem(Ky));return e>=180&&e<=480?e:e2}function i2(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=Wy&&e<=Uy?e:13}function n2(){const e={token:"",pair:""};try{const{token:t,pair:i,nextUrl:s}=ZE({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(i){e.pair=i;try{sessionStorage.setItem("nc_pair",i)}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 r2(e){try{return Cr(Zd(e))}catch{return as()}}function Ov(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 s2(){const[e,t]=R.useState(()=>window.matchMedia(Bv).matches);return R.useEffect(()=>{const i=window.matchMedia(Bv),s=o=>t(o.matches);return i.addEventListener("change",s),()=>i.removeEventListener("change",s)},[]),e}function zv({session:e,node:t,ownerId:i,cellName:s,token:o,readonly:a=!1,onBack:u}){ji();const[h,p]=R.useState(!1),[f,_]=R.useState(()=>window.matchMedia("(pointer: coarse)").matches),[b,S]=R.useState(null),[w,y]=R.useState(i2),[x,k]=R.useState(s||e),[C,P]=R.useState(""),A=E=>y(j=>{const D=Math.max(Wy,Math.min(Uy,j+E));return localStorage.setItem("nc_fontsize",String(D)),D}),M=R.useRef(()=>{}),ee=R.useRef(()=>!1),q=R.useRef(()=>{}),B=R.useRef(!1),[Z,H]=R.useState(!1),[te,K]=R.useState(!1),T=()=>{B.current=!B.current,H(B.current)};return R.useEffect(()=>{k(s||e)},[s,e]),R.useEffect(()=>{let E=!0;const j=t?t.split("/"):[],D=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function U(){let I=null,V=null;try{const z=await(await Vt(`${D}/sessions`,o)).json();Array.isArray(z.sessions)&&(I=z.sessions.find(oe=>oe.name===e))}catch{}try{const L=await Rr(o,j);L.available&&Array.isArray(L.cells)&&(V=L.cells.find(z=>z.tmuxSession===e))}catch{}if(!E)return;k(Ph({session:e,cell:V||(s?{cell:s}:null)}));let $="";V?$=`${V.engine}${V.key?`·${V.key}`:""}`:I&&($=I.attached?`attached · ${Ov(I.activity)}`:I.activity?Ov(I.activity):""),P($)}U();const re=setInterval(U,4e3);return()=>{E=!1,clearInterval(re)}},[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:g("sessions"),children:[c.jsx(et,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:g("sessions")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:t?`${x} · ${t}`:x,children:x}),C&&c.jsx("small",{className:"nc-bar-sub",children:C})]}),c.jsxs("span",{className:"nc-bar-right",children:[c.jsx("button",{onClick:()=>A(-1),title:g("zoom-out"),children:c.jsx(et,{name:"zoomOut",size:18})}),c.jsx("button",{onClick:()=>A(1),title:g("zoom-in"),children:c.jsx(et,{name:"zoomIn",size:18})}),c.jsx("button",{onClick:()=>_(E=>!E),title:g("composer"),children:c.jsx(et,{name:"keyboard",size:20})}),c.jsx("button",{onClick:()=>p(E=>!E),title:g("files"),children:c.jsx(et,{name:"folder",size:20})})]})]}),c.jsx("div",{className:"nc-termwrap",children:c.jsx(ay,{session:e,node:t,token:o,readonly:a,takeSize:!0,sendRef:M,composerRef:ee,actionRef:q,ctrlRef:B,setCtrlArmed:H,onFiles:S,fontSize:w,selectionMode:te,onSelectionModeChange:K})}),c.jsx(zC,{onKeyboard:()=>_(E=>!E),send:E=>M.current(E),action:E=>q.current(E),ctrlArmed:Z,onCtrl:T,selectionMode:te,onSelectionMode:K}),f&&c.jsx(vy,{submitText:E=>ee.current(E),token:o,session:e,node:t,ownerId:i}),h&&c.jsx(ly,{session:e,node:t,token:o,filesEvent:b,onClose:()=>p(!1)})]})}function o2(){ji();const[e]=R.useState(n2),[t,i]=R.useState(e.token),[s,o]=R.useState(e.pair||""),a=R.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[u,h]=R.useState(!1),p=s2(),[f]=R.useState(()=>fk(typeof location<"u"?location.pathname:"/")),[_,b]=R.useState(f.id),S=_===ls(null,Pi),[w,y]=R.useState(null),x=fe=>{const Pe=tr(fe);y(Pe?{...Pe,...typeof(fe==null?void 0:fe.cellName)=="string"&&fe.cellName?{cellName:fe.cellName}:{}}:null)},[k,C]=R.useState([]),[P,A]=R.useState([]),[M,ee]=R.useState([]),[q,B]=R.useState(()=>f.ownerId?as():r2(f.name)),[Z,H]=R.useState(null),[te,K]=R.useState(null),T=fe=>K(tr(fe)),E=ph(t,p),j=R.useMemo(()=>(E||[]).filter(fe=>fe.instanceId).map(fe=>({instanceId:fe.instanceId,route:fe.route,label:fe.label,status:fe.status})),[E]),D=qE(t,_,q,B,j),U=D.decks;R.useEffect(()=>{const fe=new Map;for(const Pe of E||[]){const qe=(Pe.route||[Pe.name]).join("/"),xt=new Set((Pe.sessions||[]).map(oi=>oi.name));for(const oi of Pe.cells||[])!oi.cell||!oi.tmuxSession||oi.cell===oi.tmuxSession||xt.has(oi.cell)||fe.set(`${qe}:${oi.cell}`,`${qe}:${oi.tmuxSession}`)}B(Pe=>Z0(Pe,fe))},[E]),R.useEffect(()=>{D.localNodeId&&B(fe=>{const Pe=Jd(fe,D.localNodeId,j);return JSON.stringify(Pe)===JSON.stringify(fe)?fe:Pe})},[j,D.localNodeId]);const[re,I]=R.useState(null),[V,$]=R.useState(null),L=R.useRef(0),[z,oe]=R.useState(!1),[ce,he]=R.useState(t2),[me,G]=R.useState(!S),[J,ge]=R.useState(()=>S?localStorage.getItem(Iv)==="1":!0),[Me,Ze]=R.useState(!1),[Be,Fe]=R.useState("nodes"),[ct,Ve]=R.useState(!1),[gt,ut]=R.useState(""),lt=(fe="nodes",Pe=!1,qe="")=>{Fe(fe),Ve(Pe),ut(qe),Ze(!0)},[nt,dt]=R.useState(!1),[rt,ot]=R.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[ae,xe]=R.useState(!1);R.useEffect(()=>{try{localStorage.setItem(Ky,String(ce))}catch{}},[ce]),R.useEffect(()=>{try{localStorage.setItem(Iv,J?"1":"")}catch{}},[J]),R.useEffect(()=>{if(!t)return;let fe=!1;return Promise.all([Yv(t),Vt("/api/config",t).then(Pe=>Pe.json())]).then(([Pe,qe])=>{fe||(ot({deviceDefault:Pe.deviceName||"",localNodeId:Pe.nodeId||"",localNameDefault:Pe.localName||""}),xe(!!qe.readonlyDefault),(Pe.firstRun===!0&&!qe.readonlyDefault||s)&&dt(!0))}).catch(()=>{}),()=>{fe=!0}},[t,s]);const de=R.useCallback(async()=>{try{const Pe=await(await Vt("/api/sessions",t)).json();Pe.error||C(Pe.sessions||[])}catch{}try{const fe=await Rr(t);A(fe.available?fe.cells||[]:[]),ee(fe.available?fe.capabilities||[]:[])}catch{A([]),ee([])}},[t]);R.useEffect(()=>{if(!p)return;de();const fe=setInterval(de,4e3);return()=>clearInterval(fe)},[p,de]),R.useEffect(()=>{let fe=!1;return Vt("/api/config",t).then(Pe=>Pe.json()).then(Pe=>{fe||XE(Pe.version,Pe.uiVersion,"0.8.28")}).catch(()=>{}),()=>{fe=!0}},[t]);const Ne=new Set([...k.map(fe=>fe.name),...E.flatMap(fe=>fe.sessions.map(Pe=>Pe.key))]),Ee=ln(q),X=fe=>B(Pe=>{const qe=yy(fe,D.localNodeId,j)||fe,xt=f_(Pe,qe);return xt===Pe&&ln(Pe).length>=9&&D.setError(g("grid-full")),xt}),ie=async(fe,Pe=[])=>{try{await ch(t,fe,Pe)}catch{return}const qe=Pe.length?`${Pe.join("/")}:${fe}`:fe;B(xt=>dl(xt,qe)),de()},F=async(fe,Pe,qe=[])=>{try{await Vv(t,fe,Pe,qe)}catch{return}de()},W=async(fe,Pe,qe=[])=>{await Uv(t,{cell:fe,enabled:!!Pe},qe),de()},ue=async fe=>{if(!re)return;const{cell:Pe}=re,qe=Array.isArray(re.route)?re.route:[];fe.action==="up"?await ah(t,{cell:Pe,boot:!!fe.boot,...fe.engine?{engine:fe.engine}:{},...fe.model!==void 0?{model:fe.model}:{},...fe.permissionPolicy?{permissionPolicy:fe.permissionPolicy}:{}},qe):await sl(t,{cell:Pe,boot:!!fe.boot},qe);const xt=fe.action==="up"?!!fe.boot:fe.boot?!1:!!re.boot;$({id:++L.current,cell:Pe,route:qe,enabled:xt}),de()},ve=R.useCallback(fe=>{$(Pe=>(Pe==null?void 0:Pe.id)===fe?null:Pe)},[]),Ce=async fe=>{if(!(!(fe!=null&&fe.direct)||z)){oe(!0);try{await hh(t,fe.name,fe.tunnelStatus==="up"?"down":"up")}finally{oe(!1)}}},_e=async(fe,Pe)=>{const qe=String(Pe||"").trim();return!(fe!=null&&fe.direct)||!ds(qe)?!1:(await Qv(t,fe.name,qe),!0)},Re=fe=>{const Pe=U.find(qe=>qe.id===fe);if(!Pe)return!1;try{const qe=window.open(Lu(Pe,t),"_blank");return qe&&(qe.opener=null),!!qe}catch{return!1}},Qe=async fe=>{if(!fe||fe===_)return;const Pe=await D.select(fe),qe=D.records.find(xt=>xt.id===fe);b(fe),B(Pe),H(null),K(null);try{history.replaceState(null,"",Lu(qe||fe,null))}catch{}},zt=async(fe,Pe)=>{const qe=await D.add(fe,Pe);await Qe(qe.id)},ze=async(fe,Pe)=>{const qe=await D.rename(fe,Pe);if(fe===_){b(qe.id),B(Jd(qe.layout,D.localNodeId,j)),H(null),K(null);try{history.replaceState(null,"",Lu(qe,null))}catch{}}},Di=async fe=>{await D.remove(fe),fe===_&&await Qe(D.localMainId)},Nt=async(fe,Pe)=>{!Pe||Pe===_||(await D.addTileTo(Pe,fe),B(qe=>dl(qe,fe)))};if(!t)return c.jsxs("div",{className:"nc-auth",children:[c.jsx("p",{children:g("auth-prompt")}),c.jsx("input",{onChange:fe=>i(fe.target.value.trim()),placeholder:"token"}),c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:u,onChange:fe=>h(fe.target.checked)})," ",g("remember-device")]}),c.jsx("button",{onClick:()=>{(u?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Ke=c.jsxs(c.Fragment,{children:[Me&&c.jsx(BE,{token:t,initialTab:Be,initialLocation:gt,startNewCell:ct,onClose:()=>{Ze(!1),Ve(!1),ut("")}}),nt&&c.jsx(OE,{token:t,initialPair:s,...rt,onPairDone:a,onDone:()=>dt(!1)}),c.jsx(UE,{token:t})]});if(!p)return w?c.jsxs(c.Fragment,{children:[c.jsx(zv,{session:w.session,node:w.node,ownerId:w.ownerId,cellName:w.cellName,token:t,readonly:ae,onBack:()=>y(null)}),Ke]}):c.jsxs(c.Fragment,{children:[c.jsx(pb,{onPick:x,token:t,onSettings:lt}),Ke]});const ei=S||!me;return c.jsxs("div",{className:"nc-workspace",children:[ei&&c.jsx(ik,{sessions:k,cells:P,activeSessions:Ee,nodeGroups:E,fleetCapabilities:M,bootSettlement:V,onBootSettlementApplied:ve,localNodeId:D.localNodeId,onPick:T,onAddTile:X,onPower:I,onBoot:W,onBootError:fe=>D.setError(String((fe==null?void 0:fe.message)||fe)),onNodePower:Ce,onNodeRename:_e,onKill:ie,onVisibility:F,onNew:()=>lt("fleet",!0),onSettings:lt,width:ce,collapsed:J,onResize:he,onToggleCollapse:()=>ge(fe=>!fe)}),c.jsxs("div",{className:"nc-workspace-main",children:[c.jsx(Sk,{decks:U,currentDeck:_,onCreate:zt,onRename:ze,onDelete:Di,onReorder:D.reorder,onOpenWindow:Re,onNavigate:Qe,saveState:D.saveState,error:D.error,sidebarVisible:ei,onToggleSidebar:S?null:()=>G(fe=>!fe)}),c.jsx(lk,{layout:q,onLayoutChange:B,token:t,readonly:ae,sessionsAlive:Ne,focusSession:Z,onFocus:H,onOpenSingle:T,decks:U,currentDeck:_,onSendToDeck:Nt,cells:P,nodeGroups:E})]}),te&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(zv,{session:te.session,node:te.node,ownerId:te.ownerId,cellName:Ph({session:te.session,node:te.node,ownerId:te.ownerId,cells:P,nodeGroups:E}),token:t,readonly:ae,onBack:()=>K(null)})}),re&&c.jsx(ol,{cell:re,token:t,route:Array.isArray(re.route)?re.route:[],onConfirm:ue,onClose:()=>I(null)}),Ke]})}function a2(){ji();const e=R.useSyncExternalStore(YE,Av,Av);if(!e.needed)return null;const t=e.kind==="install"?g("install-version-mismatch").replace("{v}",e.version):g("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:JE,children:g("reload")})]})}QE();GS.createRoot(document.getElementById("root")).render(c.jsxs(WS.StrictMode,{children:[c.jsx(o2,{}),c.jsx(a2,{})]}));
|
|
93
|
+
`],{type:"application/json"}),U=URL.createObjectURL(D),re=document.createElement("a");re.href=U,re.download="nexuscrew-diagnostics.json",re.click(),URL.revokeObjectURL(U)}catch{S(g("diagnostics-export-failed"))}};return c.jsxs("div",{className:"nc-set-tab nc-diagnostics",children:[c.jsxs("label",{className:"nc-field",children:[g("diagnostics-target"),c.jsx("select",{value:u.key,onChange:D=>a(D.target.value),children:s.map(D=>c.jsxs("option",{value:D.key,children:[D.label,D.status!=="up"?` · ${D.status}`:""]},D.key))})]}),c.jsxs("div",{className:"nc-set-form nc-diag-controls",children:[c.jsx("div",{className:"nc-sheet-label",children:g("diagnostics-verbose")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("select",{"aria-label":g("diagnostics-duration"),value:C,onChange:D=>P(Number(D.target.value)),disabled:i,children:[300,900,1800,3600].map(D=>c.jsxs("option",{value:D,children:[D/60," min"]},D))}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||(h==null?void 0:h.verbose)===!0,onClick:()=>T(()=>Rm(e,!0,C,u.route)),children:g("enable")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||(h==null?void 0:h.verbose)!==!0,onClick:()=>T(()=>Rm(e,!1,C,u.route)),children:g("stop")})]}),c.jsx("small",{className:"nc-set-hint",children:h!=null&&h.verbose?g("diagnostics-expires").replace("{time}",new Date(h.expiresAt).toLocaleTimeString()):g("diagnostics-off")})]}),c.jsxs("div",{className:"nc-diag-filters",children:[c.jsxs("select",{"aria-label":g("diagnostics-level"),value:A,onChange:D=>M(D.target.value),children:[c.jsx("option",{value:"all",children:g("diagnostics-all-levels")}),["debug","info","warn","error"].map(D=>c.jsx("option",{value:D,children:D},D))]}),c.jsx("input",{"aria-label":g("diagnostics-component"),placeholder:g("diagnostics-component"),value:ee,onChange:D=>q(D.target.value)})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:w,onChange:D=>y(D.target.checked)})," ",g("diagnostics-pause")]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:x,onChange:D=>k(D.target.checked)})," ",g("diagnostics-autoscroll")]})]}),c.jsx("pre",{ref:Z,className:"nc-diag-log","aria-label":g("diagnostics-log"),children:K||g("diagnostics-empty")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:E,children:g("copy")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:j,children:g("diagnostics-export")}),c.jsx("button",{type:"button",className:"nc-btn danger",disabled:i,onClick:()=>{window.confirm(g("diagnostics-clear-confirm"))&&T(async()=>{const D=await S0(e,u.route);return B.current=0,_([]),D})},children:g("diagnostics-clear")})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})}function IE({token:e,settings:t,readonly:i,refresh:s}){const[o,a]=R.useState(null),[u,h]=R.useState(null),[p,f]=R.useState(!1),[_,b]=R.useState(!1),[S,w]=R.useState(null),[y,x]=R.useState(!0);R.useEffect(()=>{w(t&&t.update||null),x(!t||t.autoUpdate!==!1)},[t]);const k=async()=>{a(null),h(null),b(!0);try{const B=await c0(e);h(B.note||"ok"),f(!1)}catch(B){a(String(B.message||B))}b(!1)},C=async()=>{a(null),h(null),b(!0);try{const B=await g0(e);h(`${B.note||"ok"}${B.target?` (${B.target})`:""}`)}catch(B){a(String(B.message||B))}b(!1)},P=async B=>{a(null),h(null),b(!0);try{await Gv(e,{autoUpdate:B}),x(B),s&&await s()}catch(Z){a(String(Z.message||Z))}b(!1)},A=async()=>{a(null),h(null),b(!0);try{w(await v0(e))}catch(B){a(String(B.message||B))}b(!1)},M=async()=>{a(null),h(null),b(!0);try{const B=await _0(e);w(B),h(g("npm-update-restarting"))}catch(B){a(String(B.message||B))}b(!1)},ee=()=>{window.confirm(g("composer-clear-confirm"))&&(a(null),GC()?(window.dispatchEvent(new Event(Xd)),h(g("composer-clear-done"))):a(g("composer-clear-failed")))},q=t&&t.service;return c.jsxs("div",{className:"nc-set-tab",children:[t&&c.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,c.jsx("br",{}),q&&q.installed?g("service-installed"):g("service-missing"),q&&q.installed?` · ${q.active?g("service-active"):g("service-inactive")}`:"",q?` · boot ${q.boot?"on":"off"}`:""]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||_,title:i?g("settings-readonly"):"",onClick:()=>{a(null),h(null),f(!0)},children:g("token-rotate")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||_,title:i?g("settings-readonly"):"",onClick:C,children:g("service-regenerate")})]}),c.jsxs("div",{className:"nc-set-form nc-update-settings",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:y,disabled:i||_||S&&!S.supported,onChange:B=>P(B.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:g("npm-auto-update")}),c.jsx("small",{children:g("npm-auto-update-help")})]})]}),S&&c.jsxs("div",{className:"nc-set-info",children:[g("npm-update-current")," v",S.current,S.latest?` · ${g("npm-update-latest")} v${S.latest}`:""," · ",g(`npm-update-${S.supported?S.phase:"unsupported"}`)]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||_||!S||!S.supported,onClick:A,children:g("npm-update-check")}),S&&S.available&&c.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||_||S.phase==="installing",onClick:M,children:g("npm-update-install")})]}),S&&S.lastError&&c.jsx("div",{className:"nc-err",children:S.lastError})]}),c.jsx(TE,{token:e,readonly:i}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("composer-clear-data")}),c.jsx("small",{className:"nc-set-hint",children:g("composer-clear-data-help")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:ee,children:g("composer-clear-data")})})]}),p&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("div",{children:g("token-rotate-explain")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:_,onClick:()=>f(!1),children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:_,onClick:k,children:g("confirm")})]})]}),u&&c.jsx("div",{className:"nc-set-note",children:u}),o&&c.jsx("div",{className:"nc-err",children:o})]})}function BE({token:e,onClose:t,initialTab:i="nodes",initialLocation:s="",startNewCell:o=!1}){ji();const[a,u]=R.useState(i),[h,p]=R.useState(null),[f,_]=R.useState([]),[b,S]=R.useState(!1),[w,y]=R.useState(null),[x,k]=R.useState(0),C=ph(e,!0,x),P=R.useCallback(async()=>{try{const A=await Yv(e);p(A),y(null)}catch(A){y(String(A.message||A))}try{const A=await a0(e);_(A.peers||[])}catch(A){y(String(A.message||A))}},[e]);return R.useEffect(()=>{P();const A=setInterval(P,5e3);return()=>clearInterval(A)},[P]),R.useEffect(()=>{let A=!1;return Vt("/api/config",e).then(M=>M.json()).then(M=>{A||S(!!M.readonlyDefault)}).catch(()=>{}),()=>{A=!0}},[e]),c.jsx("div",{className:"nc-set-overlay",onClick:t,children:c.jsxs("div",{className:"nc-set-panel",onClick:A=>A.stopPropagation(),children:[c.jsxs("div",{className:"nc-set-head",children:[c.jsx(et,{name:"gear",size:18}),c.jsx("b",{children:g("settings")}),c.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:g("close"),children:c.jsx(et,{name:"x",size:18})})]}),b&&c.jsx("div",{className:"nc-set-readonly",children:g("settings-readonly")}),w&&c.jsx("div",{className:"nc-err",children:w}),c.jsx("div",{className:"nc-set-tabs",children:["nodes","fleet","diagnostics","system"].map(A=>c.jsx("button",{type:"button",className:`nc-set-tabbtn${a===A?" on":""}`,onClick:()=>u(A),children:g(`tab-${A}`)},A))}),c.jsxs("div",{className:"nc-set-body",children:[a==="nodes"&&c.jsx(ME,{token:e,nodes:f,roster:C,settings:h,readonly:b,refresh:P,refreshAliases:()=>k(A=>A+1)}),a==="fleet"&&c.jsx(jE,{token:e,readonly:b,startNewCell:o,initialLocation:s,targets:C.map(A=>({route:A.route,label:A.label||A.name,status:A.status}))}),a==="diagnostics"&&c.jsx(AE,{token:e,roster:C,readonly:b}),a==="system"&&c.jsx(IE,{token:e,settings:h,readonly:b,refresh:P})]})]})})}function OE({token:e,initialPair:t,deviceDefault:i="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:u}){ji();const[h,p]=R.useState(t?"pair":"welcome"),[f,_]=R.useState(!1),[b,S]=R.useState(null),w=async()=>{_(!0),S(null);try{await Gv(e,{wizardDone:!0}),a&&a(),u()}catch(y){S(String(y.message||y)),_(!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:g("wizard-title")}),c.jsx("small",{children:g("hydra-simple")})]}),h==="welcome"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("local-ready")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:w,children:g("local-only")}),c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:()=>p("pair"),children:g("add-node")})]})]}),h==="pair"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx(Py,{token:e,initial:t||"",autoStart:!!t,deviceDefault:i,localNodeId:s,localNameDefault:o,onBusyChange:_,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:g("back")})})]}),h==="done"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("node-connected")}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:w,children:g("finish")})})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})})}function zE(e,t){if(typeof EventSource>"u"||!e)return()=>{};const i=new EventSource(`/api/events?token=${encodeURIComponent(e)}`);return i.onmessage=s=>{try{const o=JSON.parse(s.data);o&&typeof o=="object"&&typeof o.type=="string"&&t(o)}catch{}},()=>{try{i.close()}catch{}}}const FE=6e3,$E=12e3;function HE({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:g("close"),children:c.jsx(et,{name:"x",size:14})})]})}function WE({ask:e,token:t,onAnswered:i}){const[s,o]=R.useState(""),[a,u]=R.useState(!1),[h,p]=R.useState(null),f=async _=>{const b=String(_||"").trim();if(!(!b||a)){p(null),u(!0);try{await x0(t,e.id,b),i(e.id)}catch(S){p(String(S.message||S))}u(!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("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(_=>c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:a,onClick:()=>f(_),children:_},_))}),c.jsxs("div",{className:"nc-ask-reply",children:[c.jsx("textarea",{rows:2,placeholder:g("ask-reply-ph"),value:s,disabled:a,onChange:_=>o(_.target.value)}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:a||!s.trim(),onClick:()=>f(s),children:g("send")})]}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function UE({token:e}){ji();const[t,i]=R.useState([]),[s,o]=R.useState([]),[a,u]=R.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),h=R.useRef(0),p=R.useCallback(b=>{i(S=>S.filter(w=>w.key!==b))},[]),f=R.useCallback(b=>{const S=`t${h.current+=1}`;i(w=>[...w.slice(-3),{...b,key:S}]),setTimeout(()=>p(S),b.urgency==="high"?$E:FE)},[p]);R.useEffect(()=>{if(!e)return;let b=!1;b0(e).then(w=>{b||o(w.asks||[])}).catch(()=>{});const S=zE(e,w=>{w.type==="notify"?f(w):w.type==="ask"&&w.ask&&w.ask.id?o(y=>y.some(x=>x.id===w.ask.id)?y:[...y,w.ask]):w.type==="ask-answered"&&w.id&&o(y=>y.filter(x=>x.id!==w.id))});return()=>{b=!0,S()}},[e,f]);const _=b=>o(S=>S.filter(w=>w.id!==b));return c.jsxs(c.Fragment,{children:[t.length>0&&c.jsx("div",{className:"nc-ntf-toasts",children:t.map(b=>c.jsx(HE,{n:b,onClose:()=>p(b.key)},b.key))}),s.length>0&&!a&&c.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>u(!0),title:g("asks-title"),children:["? ",c.jsx("span",{className:"nc-ask-count",children:s.length})]}),s.length>0&&a&&c.jsxs("div",{className:"nc-ask-panel",children:[c.jsxs("div",{className:"nc-ask-panel-head",children:[c.jsx("b",{children:g("asks-title")}),c.jsx("span",{className:"nc-ask-count",children:s.length}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>u(!1),title:g("close"),children:c.jsx(et,{name:"x",size:16})})]}),c.jsx("div",{className:"nc-ask-panel-body",children:s.map(b=>c.jsx(WE,{ask:b,token:e,onAnswered:_},b.id))})]})]})}const rd=e=>ln(Cr(e)).length===0,sd=e=>Array.isArray(e)?e.join("/"):"";function KE(e){const t=new Set,i=[];for(const s of Array.isArray(e)?e:[])!s||!Hi.test(String(s.instanceId||""))||!Array.isArray(s.route)||!s.route.length||t.has(s.instanceId)||(t.add(s.instanceId),i.push({instanceId:s.instanceId,route:[...s.route],label:String(s.label||s.name||s.route.join(" › ")),status:s.status||"offline"}));return i.sort((s,o)=>s.label.localeCompare(o.label))}function uo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function vr(e,t,i,s=!1,o=!0){const a=t.instanceId;return{...e,id:ls(s?null:a,e.name),ownerId:a,ownerRoute:s?[]:[...t.route],ownerLabel:s?"Local":t.label,local:s,available:o,ownerTopology:Array.isArray(i)?i:[],layout:Qd(e.layout,a,i)}}function qE(e,t,i,s,o=[]){const[a,u]=R.useState([]),[h,p]=R.useState(!1),[f,_]=R.useState("idle"),[b,S]=R.useState(""),[w,y]=R.useState(""),x=R.useRef([]),k=R.useRef([]),C=R.useRef(""),P=R.useRef(t),A=R.useRef(i),M=R.useRef(!1),ee=R.useRef(!0),q=R.useRef(""),B=R.useMemo(()=>KE(o),[o]),Z=B.map($=>`${$.instanceId}:${sd($.route)}:${$.status}:${$.label}`).join("|");k.current=B,A.current=i,P.current=t,x.current=a;const H=R.useCallback($=>Jd($.layout,C.current,k.current),[]),te=R.useCallback(($,L=!0,z=P.current)=>{const oe=Jg($,Ya()),ce=x.current.some(me=>me.id===z);x.current=oe,u(oe);const he=oe.find(me=>me.id===z);if(L&&he){ee.current=!0;const me=H(he);s(me),he.local&&yk(he.name,me)}else ce&&!he&&(ee.current=!0,s(as()),S("deck non più condiviso dal nodo owner"));mk(oe.filter(me=>me.local).map(me=>me.name))},[s,H]),K=R.useCallback(async $=>{let L=await za(e);const z=L.decks.find(oe=>oe.name==="main");if(L.decks.length===1&&z&&z.revision===0&&rd(z.layout)){const oe=pk(),ce=Cr(Zd("main")||as());if(!rd(ce)){const he=Qd(ce,$,[]);L.decks[0]=await Fa(e,"main",he,0,[])}for(const he of oe.filter(me=>me!=="main"))try{let me=await Pm(e,he,[]);const G=Cr(Zd(he)||as());rd(G)||(me=await Fa(e,he,Qd(G,$,[]),me.revision,[]))}catch{}L=await za(e)}return L},[e]),T=R.useCallback(async({migrate:$=!1}={})=>{const L=await dh(e,[]),z=Hi.test(String(L.instanceId||""))?L.instanceId:"";if(!z)throw new Error("instanceId locale non disponibile");C.current=z,y(z);const[oe,ce]=await Promise.all([$?K(z):za(e),Cm(e,[]).catch(()=>({nodes:[]}))]),he={instanceId:z,route:[],label:"Local"},me=oe.decks.map(J=>vr(J,he,ce.nodes,!0,!0)),G=x.current;return await Promise.all(k.current.map(async J=>{if(J.status!=="up"){me.push(...G.filter(ge=>!ge.local&&ge.ownerId===J.instanceId).map(ge=>({...ge,ownerRoute:[...J.route],ownerLabel:J.label,available:!1})));return}try{const[ge,Me]=await Promise.all([za(e,J.route),Cm(e,J.route).catch(()=>({nodes:[]}))]);me.push(...ge.decks.map(Ze=>vr(Ze,J,Me.nodes,!1,!0)))}catch{me.push(...G.filter(Me=>!Me.local&&Me.ownerId===J.instanceId).map(Me=>({...Me,ownerRoute:[...J.route],ownerLabel:J.label,available:!1})))}})),me.sort((J,ge)=>J.local===ge.local?J.ownerLabel.localeCompare(ge.ownerLabel)||(J.name==="main"?-1:ge.name==="main"?1:J.name.localeCompare(ge.name)):J.local?-1:1),me},[e,K,Z]);R.useEffect(()=>{if(!e)return;let $=!1;const L=q.current!==e;return q.current=e,T({migrate:L}).then(z=>{if($)return;const oe=x.current.find(me=>me.id===P.current),ce=z.find(me=>me.id===P.current),he=ce&&(!oe||ce.revision>oe.revision||oe.available!==ce.available||sd(oe.ownerRoute)!==sd(ce.ownerRoute));te(z,L||!!he&&!M.current),p(!0),ce&&S("")}).catch(z=>{$||(p(!0),S(String(z.message||z)))}),()=>{$=!0}},[te,T,Z,e]);const E=R.useCallback(async($=P.current)=>{if(!h||!M.current)return!0;const L=x.current.find(z=>z.id===$);if(!L)return S(`deck inesistente: ${$}`),!1;if(L.available===!1)return S(`nodo owner offline: ${L.ownerLabel}`),!1;_("saving");try{const z=Gg(Cr(A.current),L.ownerId,L.ownerTopology),oe=await Fa(e,L.name,z,L.revision,L.ownerRoute),ce=vr(oe,uo(L),L.ownerTopology,L.local,!0);return te(x.current.map(he=>he.id===$?ce:he),!1),M.current=!1,_("saved"),S(""),setTimeout(()=>_("idle"),1500),!0}catch(z){if(_("error"),S(String(z.message||z)),z.status===409&&z.data&&z.data.current){const oe=x.current.find(he=>he.id===$),ce=vr(z.data.current,uo(oe),oe.ownerTopology,oe.local,!0);te(x.current.map(he=>he.id===$?ce:he),!1)}return!1}},[h,e,te]);return R.useEffect(()=>{if(!h)return;if(ee.current){ee.current=!1;return}M.current=!0,_("saving");const $=setTimeout(E,650);return()=>clearTimeout($)},[i,h,E]),R.useEffect(()=>{if(!h)return;const $=setInterval(async()=>{try{const L=await T(),z=x.current.find(he=>he.id===P.current),oe=L.find(he=>he.id===P.current),ce=oe&&(!z||oe.revision>z.revision||z.available!==oe.available);te(L,ce&&!M.current)}catch{}},5e3);return()=>clearInterval($)},[h,T,te]),{decks:a,records:a,localNodeId:w,ready:h,saveState:f,error:b,setError:S,saveNow:E,select:async $=>{if(M.current&&!await E(P.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const L=x.current.find(z=>z.id===$);if(!L)throw new Error(`deck inesistente: ${$}`);return M.current=!1,ee.current=!0,H(L)},add:async($,L=null)=>{const z=L===Sn?x.current.find(he=>he.local):L?x.current.find(he=>he.ownerId===L):null;if(!z||z.available===!1)throw new Error("nodo owner non disponibile");const oe=await Pm(e,$,z.ownerRoute),ce=vr(oe,uo(z),z.ownerTopology,z.local,!0);return te([...x.current,ce],!1),ce},rename:async($,L)=>{const z=x.current.find(G=>G.id===$);if(!z)throw new Error("deck inesistente");if($===P.current&&M.current&&!await E($))throw new Error(`salvataggio di "${z.name}" fallito: rinomina annullata`);const oe=x.current.find(G=>G.id===$);if(!oe)throw new Error("deck inesistente");const ce=await C0(e,oe.name,L,oe.revision,oe.ownerRoute),he=vr(ce,uo(oe),oe.ownerTopology,oe.local,!0),me=oe.local?Sn:oe.ownerId;return Mu(vk(Ya(),me,$,he.id)),te(x.current.map(G=>G.id===$?he:G),!1),he},remove:async $=>{const L=x.current.find(oe=>oe.id===$);if(!L)throw new Error("deck inesistente");$===P.current&&(M.current=!1),await k0(e,L.name,L.revision,L.ownerRoute);const z=L.local?Sn:L.ownerId;Mu(_k(Ya(),z,$)),te(x.current.filter(oe=>oe.id!==$),!1)},reorder:($,L)=>{const z=x.current.find(J=>J.id===$),oe=x.current.find(J=>J.id===L);if(!z||!oe)return!1;const ce=z.local?Sn:z.ownerId,he=oe.local?Sn:oe.ownerId;if(ce!==he)return!1;const me=x.current.filter(J=>(J.local?Sn:J.ownerId)===ce).map(J=>J.id),G=Mu(gk(Ya(),ce,$,L,me));return te(Jg(x.current,G),!1),!0},addTileTo:async($,L)=>{const z=x.current.find(J=>J.id===$);if(!z)throw new Error("deck inesistente");if(z.available===!1)throw new Error(`nodo owner offline: ${z.ownerLabel}`);const oe=yy(L,C.current,k.current);if(!oe)throw new Error("riferimento sessione non valido");const ce=H(z),he=Gg(f_(ce,oe),z.ownerId,z.ownerTopology),me=await Fa(e,z.name,he,z.revision,z.ownerRoute),G=vr(me,uo(z),z.ownerTopology,z.local,!0);return te(x.current.map(J=>J.id===$?G:J),!1),G},localMainId:ls(null,"main"),parseDeckId:jh}}const VE="/sw.js",th="nc-sw-update";let ih=null,nh=!1,rh=null,zy=Object.freeze({needed:!1,kind:null,version:""}),Fy=!1;function $y(){typeof window<"u"&&window.dispatchEvent(new Event(th))}function Hy(){const e=rh||(nh?{kind:"reload",version:""}:null);zy=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function Av(){return zy}function YE(e){return typeof window>"u"?()=>{}:(window.addEventListener(th,e),()=>window.removeEventListener(th,e))}function Ah(e){nh!==e&&(nh=e,Hy(),$y())}function XE(e,t,i){let s=null;e&&t&&e!==t?s={kind:"install",version:e}:t&&i&&t!==i&&(s={kind:"reload",version:t});const o=JSON.stringify(s)===JSON.stringify(rh);rh=s,Hy(),o||$y()}function GE(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&Ah(!0)})}function QE(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{Fy&&location.reload()}),navigator.serviceWorker.register(VE).then(e=>{ih=e,e.waiting&&Ah(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&GE(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function JE(){Fy=!0,Ah(!1);const e=ih&&ih.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function ZE({hash:e="",origin:t="",pathname:i="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${i||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const u=new URLSearchParams(o),h=u.get("token")||"",p=u.get("pair")||"",f=p?`${t||""}${i||""}#pair=${p}`:"";return{token:h,pair:f,nextUrl:a}}const Wy=9,Uy=24,Ky="nc_side_w",Iv="nc_side_min",e2=240,Bv="(min-width:1024px) and (pointer:fine)";function t2(){const e=Number(localStorage.getItem(Ky));return e>=180&&e<=480?e:e2}function i2(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=Wy&&e<=Uy?e:13}function n2(){const e={token:"",pair:""};try{const{token:t,pair:i,nextUrl:s}=ZE({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(i){e.pair=i;try{sessionStorage.setItem("nc_pair",i)}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 r2(e){try{return Cr(Zd(e))}catch{return as()}}function Ov(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 s2(){const[e,t]=R.useState(()=>window.matchMedia(Bv).matches);return R.useEffect(()=>{const i=window.matchMedia(Bv),s=o=>t(o.matches);return i.addEventListener("change",s),()=>i.removeEventListener("change",s)},[]),e}function zv({session:e,node:t,ownerId:i,cellName:s,token:o,readonly:a=!1,onBack:u}){ji();const[h,p]=R.useState(!1),[f,_]=R.useState(()=>window.matchMedia("(pointer: coarse)").matches),[b,S]=R.useState(null),[w,y]=R.useState(i2),[x,k]=R.useState(s||e),[C,P]=R.useState(""),A=E=>y(j=>{const D=Math.max(Wy,Math.min(Uy,j+E));return localStorage.setItem("nc_fontsize",String(D)),D}),M=R.useRef(()=>{}),ee=R.useRef(()=>!1),q=R.useRef(()=>{}),B=R.useRef(!1),[Z,H]=R.useState(!1),[te,K]=R.useState(!1),T=()=>{B.current=!B.current,H(B.current)};return R.useEffect(()=>{k(s||e)},[s,e]),R.useEffect(()=>{let E=!0;const j=t?t.split("/"):[],D=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function U(){let I=null,V=null;try{const z=await(await Vt(`${D}/sessions`,o)).json();Array.isArray(z.sessions)&&(I=z.sessions.find(oe=>oe.name===e))}catch{}try{const L=await Rr(o,j);L.available&&Array.isArray(L.cells)&&(V=L.cells.find(z=>z.tmuxSession===e))}catch{}if(!E)return;k(Ph({session:e,cell:V||(s?{cell:s}:null)}));let $="";V?$=`${V.engine}${V.key?`·${V.key}`:""}`:I&&($=I.attached?`attached · ${Ov(I.activity)}`:I.activity?Ov(I.activity):""),P($)}U();const re=setInterval(U,4e3);return()=>{E=!1,clearInterval(re)}},[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:g("sessions"),children:[c.jsx(et,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:g("sessions")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:t?`${x} · ${t}`:x,children:x}),C&&c.jsx("small",{className:"nc-bar-sub",children:C})]}),c.jsxs("span",{className:"nc-bar-right",children:[c.jsx("button",{onClick:()=>A(-1),title:g("zoom-out"),children:c.jsx(et,{name:"zoomOut",size:18})}),c.jsx("button",{onClick:()=>A(1),title:g("zoom-in"),children:c.jsx(et,{name:"zoomIn",size:18})}),c.jsx("button",{onClick:()=>_(E=>!E),title:g("composer"),children:c.jsx(et,{name:"keyboard",size:20})}),c.jsx("button",{onClick:()=>p(E=>!E),title:g("files"),children:c.jsx(et,{name:"folder",size:20})})]})]}),c.jsx("div",{className:"nc-termwrap",children:c.jsx(ay,{session:e,node:t,token:o,readonly:a,takeSize:!0,sendRef:M,composerRef:ee,actionRef:q,ctrlRef:B,setCtrlArmed:H,onFiles:S,fontSize:w,selectionMode:te,onSelectionModeChange:K})}),c.jsx(zC,{onKeyboard:()=>_(E=>!E),send:E=>M.current(E),action:E=>q.current(E),ctrlArmed:Z,onCtrl:T,selectionMode:te,onSelectionMode:K}),f&&c.jsx(vy,{submitText:E=>ee.current(E),token:o,session:e,node:t,ownerId:i}),h&&c.jsx(ly,{session:e,node:t,token:o,filesEvent:b,onClose:()=>p(!1)})]})}function o2(){ji();const[e]=R.useState(n2),[t,i]=R.useState(e.token),[s,o]=R.useState(e.pair||""),a=R.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[u,h]=R.useState(!1),p=s2(),[f]=R.useState(()=>fk(typeof location<"u"?location.pathname:"/")),[_,b]=R.useState(f.id),S=_===ls(null,Pi),[w,y]=R.useState(null),x=fe=>{const Pe=tr(fe);y(Pe?{...Pe,...typeof(fe==null?void 0:fe.cellName)=="string"&&fe.cellName?{cellName:fe.cellName}:{}}:null)},[k,C]=R.useState([]),[P,A]=R.useState([]),[M,ee]=R.useState([]),[q,B]=R.useState(()=>f.ownerId?as():r2(f.name)),[Z,H]=R.useState(null),[te,K]=R.useState(null),T=fe=>K(tr(fe)),E=ph(t,p),j=R.useMemo(()=>(E||[]).filter(fe=>fe.instanceId).map(fe=>({instanceId:fe.instanceId,route:fe.route,label:fe.label,status:fe.status})),[E]),D=qE(t,_,q,B,j),U=D.decks;R.useEffect(()=>{const fe=new Map;for(const Pe of E||[]){const qe=(Pe.route||[Pe.name]).join("/"),xt=new Set((Pe.sessions||[]).map(oi=>oi.name));for(const oi of Pe.cells||[])!oi.cell||!oi.tmuxSession||oi.cell===oi.tmuxSession||xt.has(oi.cell)||fe.set(`${qe}:${oi.cell}`,`${qe}:${oi.tmuxSession}`)}B(Pe=>Z0(Pe,fe))},[E]),R.useEffect(()=>{D.localNodeId&&B(fe=>{const Pe=Jd(fe,D.localNodeId,j);return JSON.stringify(Pe)===JSON.stringify(fe)?fe:Pe})},[j,D.localNodeId]);const[re,I]=R.useState(null),[V,$]=R.useState(null),L=R.useRef(0),[z,oe]=R.useState(!1),[ce,he]=R.useState(t2),[me,G]=R.useState(!S),[J,ge]=R.useState(()=>S?localStorage.getItem(Iv)==="1":!0),[Me,Ze]=R.useState(!1),[Be,Fe]=R.useState("nodes"),[ct,Ve]=R.useState(!1),[gt,ut]=R.useState(""),lt=(fe="nodes",Pe=!1,qe="")=>{Fe(fe),Ve(Pe),ut(qe),Ze(!0)},[nt,dt]=R.useState(!1),[rt,ot]=R.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[ae,xe]=R.useState(!1);R.useEffect(()=>{try{localStorage.setItem(Ky,String(ce))}catch{}},[ce]),R.useEffect(()=>{try{localStorage.setItem(Iv,J?"1":"")}catch{}},[J]),R.useEffect(()=>{if(!t)return;let fe=!1;return Promise.all([Yv(t),Vt("/api/config",t).then(Pe=>Pe.json())]).then(([Pe,qe])=>{fe||(ot({deviceDefault:Pe.deviceName||"",localNodeId:Pe.nodeId||"",localNameDefault:Pe.localName||""}),xe(!!qe.readonlyDefault),(Pe.firstRun===!0&&!qe.readonlyDefault||s)&&dt(!0))}).catch(()=>{}),()=>{fe=!0}},[t,s]);const de=R.useCallback(async()=>{try{const Pe=await(await Vt("/api/sessions",t)).json();Pe.error||C(Pe.sessions||[])}catch{}try{const fe=await Rr(t);A(fe.available?fe.cells||[]:[]),ee(fe.available?fe.capabilities||[]:[])}catch{A([]),ee([])}},[t]);R.useEffect(()=>{if(!p)return;de();const fe=setInterval(de,4e3);return()=>clearInterval(fe)},[p,de]),R.useEffect(()=>{let fe=!1;return Vt("/api/config",t).then(Pe=>Pe.json()).then(Pe=>{fe||XE(Pe.version,Pe.uiVersion,"0.8.29")}).catch(()=>{}),()=>{fe=!0}},[t]);const Ne=new Set([...k.map(fe=>fe.name),...E.flatMap(fe=>fe.sessions.map(Pe=>Pe.key))]),Ee=ln(q),X=fe=>B(Pe=>{const qe=yy(fe,D.localNodeId,j)||fe,xt=f_(Pe,qe);return xt===Pe&&ln(Pe).length>=9&&D.setError(g("grid-full")),xt}),ie=async(fe,Pe=[])=>{try{await ch(t,fe,Pe)}catch{return}const qe=Pe.length?`${Pe.join("/")}:${fe}`:fe;B(xt=>dl(xt,qe)),de()},F=async(fe,Pe,qe=[])=>{try{await Vv(t,fe,Pe,qe)}catch{return}de()},W=async(fe,Pe,qe=[])=>{await Uv(t,{cell:fe,enabled:!!Pe},qe),de()},ue=async fe=>{if(!re)return;const{cell:Pe}=re,qe=Array.isArray(re.route)?re.route:[];fe.action==="up"?await ah(t,{cell:Pe,boot:!!fe.boot,...fe.engine?{engine:fe.engine}:{},...fe.model!==void 0?{model:fe.model}:{},...fe.permissionPolicy?{permissionPolicy:fe.permissionPolicy}:{}},qe):await sl(t,{cell:Pe,boot:!!fe.boot},qe);const xt=fe.action==="up"?!!fe.boot:fe.boot?!1:!!re.boot;$({id:++L.current,cell:Pe,route:qe,enabled:xt}),de()},ve=R.useCallback(fe=>{$(Pe=>(Pe==null?void 0:Pe.id)===fe?null:Pe)},[]),Ce=async fe=>{if(!(!(fe!=null&&fe.direct)||z)){oe(!0);try{await hh(t,fe.name,fe.tunnelStatus==="up"?"down":"up")}finally{oe(!1)}}},_e=async(fe,Pe)=>{const qe=String(Pe||"").trim();return!(fe!=null&&fe.direct)||!ds(qe)?!1:(await Qv(t,fe.name,qe),!0)},Re=fe=>{const Pe=U.find(qe=>qe.id===fe);if(!Pe)return!1;try{const qe=window.open(Lu(Pe,t),"_blank");return qe&&(qe.opener=null),!!qe}catch{return!1}},Qe=async fe=>{if(!fe||fe===_)return;const Pe=await D.select(fe),qe=D.records.find(xt=>xt.id===fe);b(fe),B(Pe),H(null),K(null);try{history.replaceState(null,"",Lu(qe||fe,null))}catch{}},zt=async(fe,Pe)=>{const qe=await D.add(fe,Pe);await Qe(qe.id)},ze=async(fe,Pe)=>{const qe=await D.rename(fe,Pe);if(fe===_){b(qe.id),B(Jd(qe.layout,D.localNodeId,j)),H(null),K(null);try{history.replaceState(null,"",Lu(qe,null))}catch{}}},Di=async fe=>{await D.remove(fe),fe===_&&await Qe(D.localMainId)},Nt=async(fe,Pe)=>{!Pe||Pe===_||(await D.addTileTo(Pe,fe),B(qe=>dl(qe,fe)))};if(!t)return c.jsxs("div",{className:"nc-auth",children:[c.jsx("p",{children:g("auth-prompt")}),c.jsx("input",{onChange:fe=>i(fe.target.value.trim()),placeholder:"token"}),c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:u,onChange:fe=>h(fe.target.checked)})," ",g("remember-device")]}),c.jsx("button",{onClick:()=>{(u?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Ke=c.jsxs(c.Fragment,{children:[Me&&c.jsx(BE,{token:t,initialTab:Be,initialLocation:gt,startNewCell:ct,onClose:()=>{Ze(!1),Ve(!1),ut("")}}),nt&&c.jsx(OE,{token:t,initialPair:s,...rt,onPairDone:a,onDone:()=>dt(!1)}),c.jsx(UE,{token:t})]});if(!p)return w?c.jsxs(c.Fragment,{children:[c.jsx(zv,{session:w.session,node:w.node,ownerId:w.ownerId,cellName:w.cellName,token:t,readonly:ae,onBack:()=>y(null)}),Ke]}):c.jsxs(c.Fragment,{children:[c.jsx(pb,{onPick:x,token:t,onSettings:lt}),Ke]});const ei=S||!me;return c.jsxs("div",{className:"nc-workspace",children:[ei&&c.jsx(ik,{sessions:k,cells:P,activeSessions:Ee,nodeGroups:E,fleetCapabilities:M,bootSettlement:V,onBootSettlementApplied:ve,localNodeId:D.localNodeId,onPick:T,onAddTile:X,onPower:I,onBoot:W,onBootError:fe=>D.setError(String((fe==null?void 0:fe.message)||fe)),onNodePower:Ce,onNodeRename:_e,onKill:ie,onVisibility:F,onNew:()=>lt("fleet",!0),onSettings:lt,width:ce,collapsed:J,onResize:he,onToggleCollapse:()=>ge(fe=>!fe)}),c.jsxs("div",{className:"nc-workspace-main",children:[c.jsx(Sk,{decks:U,currentDeck:_,onCreate:zt,onRename:ze,onDelete:Di,onReorder:D.reorder,onOpenWindow:Re,onNavigate:Qe,saveState:D.saveState,error:D.error,sidebarVisible:ei,onToggleSidebar:S?null:()=>G(fe=>!fe)}),c.jsx(lk,{layout:q,onLayoutChange:B,token:t,readonly:ae,sessionsAlive:Ne,focusSession:Z,onFocus:H,onOpenSingle:T,decks:U,currentDeck:_,onSendToDeck:Nt,cells:P,nodeGroups:E})]}),te&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(zv,{session:te.session,node:te.node,ownerId:te.ownerId,cellName:Ph({session:te.session,node:te.node,ownerId:te.ownerId,cells:P,nodeGroups:E}),token:t,readonly:ae,onBack:()=>K(null)})}),re&&c.jsx(ol,{cell:re,token:t,route:Array.isArray(re.route)?re.route:[],onConfirm:ue,onClose:()=>I(null)}),Ke]})}function a2(){ji();const e=R.useSyncExternalStore(YE,Av,Av);if(!e.needed)return null;const t=e.kind==="install"?g("install-version-mismatch").replace("{v}",e.version):g("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:JE,children:g("reload")})]})}QE();GS.createRoot(document.getElementById("root")).render(c.jsxs(WS.StrictMode,{children:[c.jsx(o2,{}),c.jsx(a2,{})]}));
|
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-BJdAdNOq.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-BAzlX2nP.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.8.
|
|
1
|
+
{"version":"0.8.29"}
|
package/lib/cli/commands.js
CHANGED
|
@@ -346,9 +346,64 @@ function servicePinsLegacyPort(platform, home, installPath) {
|
|
|
346
346
|
try { return /NEXUSCREW_PORT/.test(fs.readFileSync(target, 'utf8')); } catch (_) { return false; }
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
function
|
|
349
|
+
function fleetServiceDefinitionNeedsRefresh(platform, home, installPath) {
|
|
350
|
+
let target;
|
|
351
|
+
try { target = installPath || fleetInstallPath(platform, home); } catch (_) { return false; }
|
|
352
|
+
try { fs.lstatSync(target); }
|
|
353
|
+
catch (error) { return error.code !== 'ENOENT'; }
|
|
354
|
+
return !checkServiceWorkingDirectory(platform, home, target).ok;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function serviceDefinitionNeedsRefresh(platform, home, installPath, fleetInstallPathOverride) {
|
|
350
358
|
if (servicePinsLegacyPort(platform, home, installPath)) return true;
|
|
351
|
-
|
|
359
|
+
if (!checkServiceWorkingDirectory(platform, home, installPath).ok) return true;
|
|
360
|
+
return fleetServiceDefinitionNeedsRefresh(platform, home, fleetInstallPathOverride);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function serviceMigrationMarkerPath(home, override) {
|
|
364
|
+
return override || path.join(home, '.nexuscrew', 'service-cwd-migration.pending');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function serviceMigrationPending(home, override) {
|
|
368
|
+
const target = serviceMigrationMarkerPath(home, override);
|
|
369
|
+
try {
|
|
370
|
+
const st = fs.lstatSync(target);
|
|
371
|
+
if (st.isSymbolicLink() || !st.isFile()) throw new Error(`unsafe service migration marker: ${target}`);
|
|
372
|
+
return true;
|
|
373
|
+
} catch (error) {
|
|
374
|
+
if (error.code === 'ENOENT') return false;
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function ensureServiceMigrationMarker(home, override) {
|
|
380
|
+
const target = serviceMigrationMarkerPath(home, override);
|
|
381
|
+
if (serviceMigrationPending(home, override)) return target;
|
|
382
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
383
|
+
let fd;
|
|
384
|
+
try {
|
|
385
|
+
fd = fs.openSync(target, 'wx', 0o600);
|
|
386
|
+
fs.writeFileSync(fd, 'pending\n');
|
|
387
|
+
fs.fsyncSync(fd);
|
|
388
|
+
fs.fchmodSync(fd, 0o600);
|
|
389
|
+
} catch (error) {
|
|
390
|
+
if (error.code === 'EEXIST' && serviceMigrationPending(home, override)) return target;
|
|
391
|
+
throw error;
|
|
392
|
+
} finally {
|
|
393
|
+
if (fd !== undefined) fs.closeSync(fd);
|
|
394
|
+
}
|
|
395
|
+
return target;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function clearServiceMigrationMarker(home, override) {
|
|
399
|
+
const target = serviceMigrationMarkerPath(home, override);
|
|
400
|
+
try {
|
|
401
|
+
const st = fs.lstatSync(target);
|
|
402
|
+
if (st.isSymbolicLink() || !st.isFile()) throw new Error(`unsafe service migration marker: ${target}`);
|
|
403
|
+
fs.unlinkSync(target);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (error.code !== 'ENOENT') throw error;
|
|
406
|
+
}
|
|
352
407
|
}
|
|
353
408
|
|
|
354
409
|
function portAvailable(port, host = '127.0.0.1') {
|
|
@@ -471,18 +526,37 @@ async function smartUp(opts = {}) {
|
|
|
471
526
|
const home = opts.home || require('node:os').homedir();
|
|
472
527
|
const persistent = bootState({ ...opts, platform }).enabled;
|
|
473
528
|
let serviceRefreshed = false;
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
529
|
+
const migrationPending = persistent
|
|
530
|
+
? serviceMigrationPending(home, opts.serviceMigrationMarkerPath) : false;
|
|
531
|
+
if (persistent && (migrationPending || serviceDefinitionNeedsRefresh(
|
|
532
|
+
platform, home, opts.installPath, opts.fleetInstallPath,
|
|
533
|
+
))) {
|
|
534
|
+
ensureServiceMigrationMarker(home, opts.serviceMigrationMarkerPath);
|
|
535
|
+
const initResult = runInitImpl({ ...opts, log: quiet, platform, installBoot: true, printUrl: false });
|
|
536
|
+
serviceRefreshed = !serviceDefinitionNeedsRefresh(
|
|
537
|
+
platform, home, opts.installPath, opts.fleetInstallPath,
|
|
538
|
+
);
|
|
539
|
+
if (!serviceRefreshed) {
|
|
540
|
+
throw new Error('service cwd migration failed verification; refusing to continue with a replaceable working directory');
|
|
541
|
+
}
|
|
542
|
+
const installFailures = Array.isArray(initResult?.installFailures) ? initResult.installFailures : [];
|
|
543
|
+
if (installFailures.length) {
|
|
544
|
+
const components = [...new Set(installFailures.map((failure) => failure?.component)
|
|
545
|
+
.filter((component) => component === 'service' || component === 'fleet-companion'))];
|
|
546
|
+
throw new Error(`service cwd migration activation failed verification${components.length ? ` (${components.join(', ')})` : ''}; refusing to continue with an unverified service runtime`);
|
|
547
|
+
}
|
|
548
|
+
// Writing and activating a definition does not prove that an already-active
|
|
549
|
+
// Linux unit adopted it (`systemctl enable --now` is a no-op for that
|
|
550
|
+
// process). Restart every persistent runtime exactly once after the files
|
|
551
|
+
// and activation outcomes are verified. The lifecycle helper preserves the
|
|
552
|
+
// shared tmux server on Linux and uses PID-pinned portable restart on Termux.
|
|
553
|
+
const migrated = restartImpl({ ...opts, platform, log: quiet });
|
|
554
|
+
if (!migrated || migrated.restarted !== true) {
|
|
555
|
+
throw new Error(`service cwd migration restart failed verification (${platform}); refusing to continue with an unverified service runtime`);
|
|
485
556
|
}
|
|
557
|
+
// Only a verified file migration plus successful service activation (and,
|
|
558
|
+
// on Termux, a verified runtime restart) clears the durable retry marker.
|
|
559
|
+
clearServiceMigrationMarker(home, opts.serviceMigrationMarkerPath);
|
|
486
560
|
}
|
|
487
561
|
|
|
488
562
|
if (!port) port = urlmod.loadPort(opts);
|
|
@@ -989,6 +1063,8 @@ module.exports = {
|
|
|
989
1063
|
dispatch, dispatchNodes, serve, start, stop, status, parseFlags, HELP, NODES_HELP,
|
|
990
1064
|
smartUp, url, tokenRotate, logs, doctor, update, restart,
|
|
991
1065
|
serviceDefinitionNeedsRefresh,
|
|
1066
|
+
fleetServiceDefinitionNeedsRefresh,
|
|
1067
|
+
serviceMigrationPending,
|
|
992
1068
|
portAvailable, findAvailablePort, probeNexusCrew, waitForNexusCrew, openPwa, startPortable, wizardComplete,
|
|
993
1069
|
servicePinsLegacyPort,
|
|
994
1070
|
isServiceRunning, readRoles,
|
package/lib/cli/doctor.js
CHANGED
|
@@ -11,6 +11,7 @@ const path = require('node:path');
|
|
|
11
11
|
const { execFileSync } = require('node:child_process');
|
|
12
12
|
const { detectPlatform, uid } = require('./platform.js');
|
|
13
13
|
const { installPath } = require('./service.js');
|
|
14
|
+
const { fleetInstallPath } = require('./fleet-service.js');
|
|
14
15
|
const { resolvePaths } = require('./url.js');
|
|
15
16
|
const { commandExists } = require('./path.js');
|
|
16
17
|
const { loadDefinitions } = require('../fleet/definitions.js');
|
|
@@ -126,6 +127,26 @@ function checkServiceWorkingDirectory(platform, home, installPathOverride) {
|
|
|
126
127
|
};
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
// The Fleet boot companion can win the boot race and create the shared tmux
|
|
131
|
+
// server before the HTTP service. Apply the same stable-HOME invariant to it.
|
|
132
|
+
// A missing companion is non-fatal because Fleet boot is optional; an installed
|
|
133
|
+
// but stale/unsafe definition is a real failure.
|
|
134
|
+
function checkFleetServiceWorkingDirectory(platform, home, installPathOverride) {
|
|
135
|
+
if (!['linux', 'mac', 'termux'].includes(platform)) {
|
|
136
|
+
return { name: 'fleet service cwd stabile', ok: true, detail: `${platform}: non applicabile` };
|
|
137
|
+
}
|
|
138
|
+
const target = installPathOverride || fleetInstallPath(platform, home);
|
|
139
|
+
try {
|
|
140
|
+
fs.lstatSync(target);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (error.code === 'ENOENT') {
|
|
143
|
+
return { name: 'fleet service cwd stabile', ok: true, warn: true, detail: `companion non installato (${target})` };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const result = checkServiceWorkingDirectory(platform, home, target);
|
|
147
|
+
return { ...result, name: 'fleet service cwd stabile' };
|
|
148
|
+
}
|
|
149
|
+
|
|
129
150
|
function checkBoot(platform, home, execImpl) {
|
|
130
151
|
if (platform === 'termux') {
|
|
131
152
|
const p = path.join(home, '.termux', 'boot', 'nexuscrew.sh');
|
|
@@ -425,6 +446,9 @@ function doctor(opts = {}) {
|
|
|
425
446
|
checkPty(ptyLoad),
|
|
426
447
|
checkService(platform, home, execImpl, uidVal, opts.installPath),
|
|
427
448
|
checkServiceWorkingDirectory(platform, home, opts.installPath),
|
|
449
|
+
fleetEnabled
|
|
450
|
+
? checkFleetServiceWorkingDirectory(platform, home, opts.fleetInstallPath)
|
|
451
|
+
: { name: 'fleet service cwd stabile', ok: true, warn: true, detail: 'Fleet disabilitata' },
|
|
428
452
|
checkBoot(platform, home, execImpl),
|
|
429
453
|
checkUserLinger(platform, execImpl, uidVal),
|
|
430
454
|
checkTmuxSurvival(platform, execImpl),
|
|
@@ -454,6 +478,7 @@ module.exports = {
|
|
|
454
478
|
checkMacServiceWorkingDirectory,
|
|
455
479
|
checkFleetDefinitions,
|
|
456
480
|
checkServiceWorkingDirectory,
|
|
481
|
+
checkFleetServiceWorkingDirectory,
|
|
457
482
|
checkTmuxSurvival, checkUserLinger,
|
|
458
483
|
checkSshClient, checkAutossh, checkSshPermitlisten,
|
|
459
484
|
checkMcpIdentity, checkTermuxExec,
|
package/lib/cli/fleet-service.js
CHANGED
|
@@ -41,13 +41,16 @@ function generateFleetService(opts) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function generateFleetLinux(opts) {
|
|
44
|
-
const
|
|
44
|
+
const home = opts.home || os.homedir();
|
|
45
45
|
const { nodeBin, entryPath } = opts;
|
|
46
46
|
// Parita' di hardening con service.js (M3): reject char non gestibili in systemd.
|
|
47
|
-
assertSystemdSafe('
|
|
47
|
+
assertSystemdSafe('home', home);
|
|
48
48
|
assertSystemdSafe('nodeBin', nodeBin);
|
|
49
49
|
assertSystemdSafe('entryPath', entryPath);
|
|
50
|
-
|
|
50
|
+
// fleet-boot can create the shared tmux server before the HTTP service. Its
|
|
51
|
+
// cwd must therefore be the stable HOME, never the replaceable npm package
|
|
52
|
+
// directory containing entryPath.
|
|
53
|
+
const stableCwd = escapeSystemdPath(home);
|
|
51
54
|
const node = escapeSystemdExec(nodeBin);
|
|
52
55
|
const entry = escapeSystemdExec(entryPath);
|
|
53
56
|
const nodeDir = escapeSystemdPath(path.dirname(nodeBin));
|
|
@@ -61,7 +64,7 @@ Type=oneshot
|
|
|
61
64
|
# fleet-boot may be the process that creates the shared tmux server. Keep
|
|
62
65
|
# systemd from reaping that server and its cells when this oneshot exits.
|
|
63
66
|
KillMode=process
|
|
64
|
-
WorkingDirectory=${
|
|
67
|
+
WorkingDirectory=${stableCwd}
|
|
65
68
|
Environment=PATH=${nodeDir}:/usr/local/bin:/usr/bin:/bin
|
|
66
69
|
ExecStart=${node} ${entry} fleet-boot
|
|
67
70
|
|
|
@@ -71,11 +74,9 @@ WantedBy=default.target
|
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
function generateFleetMac(opts) {
|
|
74
|
-
const repoRoot = opts.repoRoot || deriveRepoRoot(opts.entryPath);
|
|
75
77
|
const home = opts.home || os.homedir();
|
|
76
78
|
const nodeXml = escapeXml(opts.nodeBin);
|
|
77
79
|
const entryXml = escapeXml(opts.entryPath);
|
|
78
|
-
const repoXml = escapeXml(repoRoot);
|
|
79
80
|
const homeXml = escapeXml(home);
|
|
80
81
|
const launchPath = [...new Set([
|
|
81
82
|
path.dirname(opts.nodeBin), '/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', '/bin',
|
|
@@ -94,7 +95,7 @@ function generateFleetMac(opts) {
|
|
|
94
95
|
<string>fleet-boot</string>
|
|
95
96
|
</array>
|
|
96
97
|
<key>WorkingDirectory</key>
|
|
97
|
-
<string>${
|
|
98
|
+
<string>${homeXml}</string>
|
|
98
99
|
<key>EnvironmentVariables</key>
|
|
99
100
|
<dict>
|
|
100
101
|
<key>PATH</key>
|
|
@@ -112,10 +113,8 @@ function generateFleetMac(opts) {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
function generateFleetTermux(opts) {
|
|
115
|
-
const repoRoot = opts.repoRoot || deriveRepoRoot(opts.entryPath);
|
|
116
116
|
const nodeQ = shellQuote(opts.nodeBin);
|
|
117
117
|
const entryQ = shellQuote(opts.entryPath);
|
|
118
|
-
const repoQ = shellQuote(repoRoot);
|
|
119
118
|
return `#!/data/data/com.termux/files/usr/bin/sh
|
|
120
119
|
# NexusCrew fleet boot companion (Termux) - avvia le celle boot:true
|
|
121
120
|
export PATH=/data/data/com.termux/files/usr/bin:$PATH
|
|
@@ -124,7 +123,7 @@ export PREFIX=/data/data/com.termux/files/usr
|
|
|
124
123
|
export TMPDIR=$PREFIX/tmp
|
|
125
124
|
export TMUX_TMPDIR=$PREFIX/var/run
|
|
126
125
|
mkdir -p "$TMPDIR" "$TMUX_TMPDIR"
|
|
127
|
-
cd -- $
|
|
126
|
+
cd -- "$HOME"
|
|
128
127
|
exec ${nodeQ} ${entryQ} fleet-boot >> "$HOME/.nexuscrew/fleet-boot.log" 2>&1
|
|
129
128
|
`;
|
|
130
129
|
}
|
package/lib/cli/init.js
CHANGED
|
@@ -109,6 +109,9 @@ function runInit(opts = {}) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
const actions = [];
|
|
112
|
+
// Structured, value-free install outcomes for callers that must fail closed
|
|
113
|
+
// (notably smart-up migrations). Human-readable actions remain compatible.
|
|
114
|
+
const installFailures = [];
|
|
112
115
|
|
|
113
116
|
// porta: opts.port > migration rule > config esistente > default 41820
|
|
114
117
|
let port = opts.port;
|
|
@@ -214,12 +217,14 @@ function runInit(opts = {}) {
|
|
|
214
217
|
const r = installService(platform, content, svcCtx, { execImpl: opts.execImpl });
|
|
215
218
|
if (r.failures && r.failures.length) {
|
|
216
219
|
// file installato MA activation (systemctl/launchctl) fallita [M1]
|
|
220
|
+
installFailures.push({ component: 'service', phase: 'activation' });
|
|
217
221
|
actions.push(`WARN: service file installato ${r.target} MA activation fallita: ${r.failures.map((f) => f.cmd).join('; ')} (file preservato, diagnosi)`);
|
|
218
222
|
} else {
|
|
219
223
|
actions.push(`service installed ${r.target} (mode 0${fileMode(platform).toString(8)})`);
|
|
220
224
|
}
|
|
221
225
|
} catch (e) {
|
|
222
226
|
// failure: preserve file + diagnosi (no rollback) [M8]
|
|
227
|
+
installFailures.push({ component: 'service', phase: 'install' });
|
|
223
228
|
actions.push(`WARN: service install fallito: ${e.message} (file generati preservati)`);
|
|
224
229
|
}
|
|
225
230
|
}
|
|
@@ -273,6 +278,7 @@ function runInit(opts = {}) {
|
|
|
273
278
|
);
|
|
274
279
|
if (fr.failures && fr.failures.length) {
|
|
275
280
|
// file installato MA activation (systemctl/launchctl) fallita [M1]
|
|
281
|
+
installFailures.push({ component: 'fleet-companion', phase: 'activation' });
|
|
276
282
|
actions.push(`WARN: fleet companion file installato ${fr.target} MA activation fallita: ${fr.failures.map((f) => f.cmd).join('; ')} (file preservato, diagnosi)`);
|
|
277
283
|
} else {
|
|
278
284
|
actions.push(`fleet companion installed ${fr.target} (mode 0${fleetFileMode(platform).toString(8)})`);
|
|
@@ -282,6 +288,7 @@ function runInit(opts = {}) {
|
|
|
282
288
|
}
|
|
283
289
|
} catch (e) {
|
|
284
290
|
// qualunque errore del companion: WARN + init principale prosegue intatto.
|
|
291
|
+
installFailures.push({ component: 'fleet-companion', phase: 'install' });
|
|
285
292
|
actions.push(`WARN: fleet companion fallito: ${e.message} (init principale prosegue)`);
|
|
286
293
|
}
|
|
287
294
|
|
|
@@ -303,7 +310,7 @@ function runInit(opts = {}) {
|
|
|
303
310
|
|
|
304
311
|
for (const a of actions) log(a);
|
|
305
312
|
|
|
306
|
-
return { platform, port, token, url: urlWithToken, actions, tmuxOk, dryRun };
|
|
313
|
+
return { platform, port, token, url: urlWithToken, actions, installFailures, tmuxOk, dryRun };
|
|
307
314
|
}
|
|
308
315
|
|
|
309
316
|
module.exports = { runInit, readExistingPort, haveTmux, nodeMajor, writeConfigAtomic, ensureFleetDefaults };
|
package/lib/diagnostics/store.js
CHANGED
|
@@ -18,7 +18,7 @@ function redactText(value, max = 160) {
|
|
|
18
18
|
.replace(/\bBearer\s+\S+/gi, 'Bearer [redacted]')
|
|
19
19
|
.replace(/\b(?:sk|xox[baprs]|gh[pousr]|npm)_[A-Za-z0-9_-]{8,}\b/g, '[redacted]')
|
|
20
20
|
.replace(/\b(?:token|password|secret|api[_-]?key)\s*[=:]\s*\S+/gi, '[redacted]')
|
|
21
|
-
.replace(/(?:\/home\/[^/\s]+|\/data\/(?:data|user\/\d+)\/[^/\s]+)(?:\/[^\s]*)?/g, '[path]')
|
|
21
|
+
.replace(/(?:\/home\/[^/\s]+|\/Users\/[^/\s]+|\/data\/(?:data|user\/\d+)\/[^/\s]+)(?:\/[^\s]*)?/g, '[path]')
|
|
22
22
|
.replace(/\b[A-Z][A-Z0-9_]{2,}\s*=\s*\S+/g, '[env]');
|
|
23
23
|
return text.slice(0, max);
|
|
24
24
|
}
|
package/lib/fleet/builtin.js
CHANGED
|
@@ -620,8 +620,12 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
620
620
|
if (defs.cells.some((c) => c.id === id)) throw httpError(409, `id cella già usato: ${id}`);
|
|
621
621
|
const cwd = typeof b.cwd === 'string' && b.cwd.trim() ? b.cwd.trim() : home;
|
|
622
622
|
const cellDef = { id, cwd, engine: engineId, boot: b.boot === true, tmuxSession };
|
|
623
|
+
// Import is a write boundary just like define/edit/restore. Resolve and
|
|
624
|
+
// confine the cwd before mutate so a foreign or missing path is never
|
|
625
|
+
// persisted as a cell that can only fail later at up().
|
|
626
|
+
const [resolvedCell] = resolveCellsOrFail([cellDef], home);
|
|
623
627
|
try {
|
|
624
|
-
await mutate(defs, (d) => { d.cells.push(
|
|
628
|
+
await mutate(defs, (d) => { d.cells.push(resolvedCell); });
|
|
625
629
|
} catch (e) { throw httpError(400, `import non valido: ${e.message}`); }
|
|
626
630
|
return { ok: true, id, tmuxSession, imported: true };
|
|
627
631
|
}
|
package/lib/fleet/launch.js
CHANGED
|
@@ -63,6 +63,7 @@ const REDACTED = '‹redacted›';
|
|
|
63
63
|
// - valori di engine.env (le CHIAVI restano, i VALUES vengono redatti)
|
|
64
64
|
// - testo del prompt della cella (cell.prompt)
|
|
65
65
|
// - testo del prompt dell'engine (engine.prompt) se presente
|
|
66
|
+
// - comando Shell attivo per cella (cell.commands[cell.engine])
|
|
66
67
|
// Applicato a OGNI messaggio d'errore che incorpora stderr/stdout dei comandi
|
|
67
68
|
// tmux falliti (up / down / injectPrompt): tmux puo' ecoare argv/env del comando
|
|
68
69
|
// lanciato nei suoi log di errore. Pura + senza dipendenze: testabile direttamente.
|
|
@@ -76,6 +77,11 @@ function redactSecrets(text, engine, cell) {
|
|
|
76
77
|
}
|
|
77
78
|
if (engine && typeof engine.prompt === 'string' && engine.prompt) secrets.push(engine.prompt);
|
|
78
79
|
if (cell && typeof cell.prompt === 'string' && cell.prompt) secrets.push(cell.prompt);
|
|
80
|
+
if (cell && typeof cell.engine === 'string'
|
|
81
|
+
&& cell.commands && typeof cell.commands === 'object' && !Array.isArray(cell.commands)) {
|
|
82
|
+
const activeCommand = cell.commands[cell.engine];
|
|
83
|
+
if (typeof activeCommand === 'string' && activeCommand) secrets.push(activeCommand);
|
|
84
|
+
}
|
|
79
85
|
// Ordina per lunghezza DECRESCENTE: i segreti piu' lunghi prima, cosi' un segreto
|
|
80
86
|
// che e' prefisso/sottostringa di un altro non ne maschera il rimpiazzo completo.
|
|
81
87
|
secrets.sort((a, b) => b.length - a.length);
|