@mmmbuto/nexuscrew 0.8.25 → 0.8.26

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 CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to NexusCrew are tracked here.
4
4
 
5
+ ## 0.8.26 — 2026-07-20 — "MCP Identity"
6
+
7
+ - Makes MCP caller resolution directly diagnosable through the read-only `nc_identity` tool.
8
+ The diagnostic works without a token or resolved session, reports only non-sensitive source
9
+ and presence metadata, and returns stable missing/invalid identity codes with remediation.
10
+ - Keeps identity-gated tools fail-closed while improving their error contract. A missing or
11
+ invalid tmux identity can no longer be mistaken for a transport failure, and `nc_notify`
12
+ continues to degrade safely to an unknown sender.
13
+ - Documents the explicit `env_vars` name allowlist required by clients that clear the MCP stdio
14
+ environment, including the matching repeatable `codex-vl mcp add --env-var NAME` form. Values
15
+ remain outside command arguments and configuration files.
16
+ - Extends `nexuscrew doctor` with a non-failing MCP identity check: PWA-only users receive at
17
+ most an informational warning, while MCP users can see whether `TMUX` or
18
+ `NEXUSCREW_MCP_SESSION` is observable in the current process.
19
+ - Stabilizes the writable-provider-file security fixture across differing CI umasks without
20
+ weakening the production permission check.
21
+ - Gate: **888 isolated Node tests** (887 pass / 1 platform skip), **39/39 frontend component
22
+ tests**, production PWA build and zero production dependency vulnerabilities in both the root
23
+ and frontend dependency trees.
24
+
5
25
  ## 0.8.25 — 2026-07-19 — "Token Plan"
6
26
 
7
27
  - Adds Alibaba Token Plan Personal as a first-class managed provider for Claude Code,
package/README.md CHANGED
@@ -331,11 +331,18 @@ It is intended for AI sessions running inside managed tmux cells.
331
331
  | `nc_deck` | Discover owner-qualified decks containing the calling tmux session |
332
332
  | `nc_cells` | List authorized active and inactive Fleet cells across visible nodes |
333
333
  | `nc_send_cell` | Submit bounded text to one exact active cell returned by `nc_cells` |
334
+ | `nc_identity` | Read-only identity diagnostics; callable with no session and no token |
334
335
 
335
336
  Cell delivery uses bracketed paste followed by a separate Enter. A `submitted` receipt confirms
336
337
  delivery to the target TUI, not acceptance or completion by its model. There is no silent
337
338
  offline queue.
338
339
 
340
+ `nc_identity` returns only non-sensitive data: the `source` the caller was resolved from
341
+ (`tmux`, `NEXUSCREW_MCP_SESSION`, or `missing`), boolean presence of the identity env vars,
342
+ a stable `code` (`OK`, `NEXUSCREW_MCP_IDENTITY_MISSING`, `NEXUSCREW_MCP_IDENTITY_INVALID`) and
343
+ a remediation hint. It never calls an HTTP API or reads the token, so it works even when the
344
+ identity is missing — use it to diagnose why the identity-gated tools fail closed.
345
+
339
346
  Register the bridge in Claude Code:
340
347
 
341
348
  ```json
@@ -349,16 +356,32 @@ Register the bridge in Claude Code:
349
356
  }
350
357
  ```
351
358
 
352
- Or in Codex / Codex-VL:
359
+ Or in Codex / Codex-VL (`env_vars` allowlists variable **names** only — no values are copied
360
+ into the CLI or config file):
353
361
 
354
362
  ```toml
355
363
  [mcp_servers.nexuscrew]
356
364
  command = "nexuscrew"
357
365
  args = ["mcp"]
366
+ env_vars = ["NEXUSCREW_MCP_SESSION", "TMUX", "TMUX_PANE"]
367
+ ```
368
+
369
+ The equivalent CLI form on a Codex-VL build that supports `--env-var` (allowlist by name, repeated):
370
+
371
+ ```text
372
+ codex-vl mcp add nexuscrew \
373
+ --env-var NEXUSCREW_MCP_SESSION \
374
+ --env-var TMUX \
375
+ --env-var TMUX_PANE \
376
+ -- nexuscrew mcp
358
377
  ```
359
378
 
360
- The caller is resolved from its tmux session. `NEXUSCREW_MCP_SESSION` is available only as an
361
- explicit fallback for non-tmux contexts.
379
+ The caller is resolved, in order, from its tmux session (`tmux display-message -p '#S'`),
380
+ then from the `NEXUSCREW_MCP_SESSION` fallback, then not at all. Codex/Codex-VL launch MCP stdio
381
+ processes with a cleared environment, so those clients must explicitly allowlist the identity
382
+ env vars for the server to observe them; otherwise the identity-gated tools (`nc_ask`, `nc_send_file`,
383
+ `nc_deck`, `nc_send_cell`, `nc_inbox`) stay fail-closed with a stable
384
+ `NEXUSCREW_MCP_IDENTITY_*` code, while `nc_notify` degrades to an unknown sender.
362
385
 
363
386
  ## Configuration
364
387
 
@@ -411,7 +434,7 @@ See [CHANGELOG.md](CHANGELOG.md) for released changes.
411
434
 
412
435
  ## Status
413
436
 
414
- The current stable release is **v0.8.25** on npm and GitHub.
437
+ The current stable release is **v0.8.26** on npm and GitHub.
415
438
 
416
439
  ## License
417
440
 
@@ -88,4 +88,4 @@ Minimum version required to store current data is: `+Q+`.
88
88
  `;return typeof h=="function"&&h(null,k),k},Gu}var vv;function Nx(){if(vv)return is;vv=1;const e=ax(),t=Cx(),i=xx(),s=Ex();function o(a,c,h,m,p){const y=[].slice.call(arguments,1),S=y.length,_=typeof y[S-1]=="function";if(!_&&!e())throw new Error("Callback required as last argument");if(_){if(S<2)throw new Error("Too few arguments provided");S===2?(p=h,h=c,c=m=void 0):S===3&&(c.getContext&&typeof p>"u"?(p=m,m=void 0):(p=m,m=h,h=c,c=void 0))}else{if(S<1)throw new Error("Too few arguments provided");return S===1?(h=c,c=m=void 0):S===2&&!c.getContext&&(m=h,h=c,c=void 0),new Promise(function(C,w){try{const x=t.create(h,m);C(a(x,c,m))}catch(x){w(x)}})}try{const C=t.create(h,m);p(null,a(C,c,m))}catch(C){p(C)}}return is.create=t.create,is.toCanvas=o.bind(null,i.render),is.toDataURL=o.bind(null,i.renderToDataURL),is.toString=o.bind(null,function(a,c,h){return s.render(a,h)}),is}var Rx=Nx();const Px=Jd(Rx);function _v(e){return typeof e=="string"?e.trim():e&&typeof e=="object"&&typeof e.data=="string"?e.data.trim():""}function Dx(e){const t=String(e||"").trim();if(!t)return{kind:"empty"};const i=J0(t);if(!i||!i.ok)return{kind:"invalid"};const s=[];return i.ssh||s.push("ssh"),i.name||s.push("name"),s.length?{kind:"partial",decoded:i,missing:s}:{kind:"complete",decoded:i}}function jx(e,t,i=new Set,s=!1){if(!t||!t.ok)return e;const o=Z0(e,t,i);return t.version===2&&t.name&&!i.has("name")&&!s&&(o.name=t.name),!i.has("label")&&!o.label&&t.label&&(o.label=t.label),o}function yv(e,t,i=new Set,s=!1){const o=String(t||"").trim(),a=Dx(o);let c={...e,pairingUrl:o};return(a.kind==="complete"||a.kind==="partial")&&(c={...jx(c,a.decoded,i,s),pairingUrl:o}),{classification:a,form:c}}function tl(e,t){const i=String(t||"").toLowerCase();if(!/^[a-f0-9]{16,64}$/.test(i))return"";const s=String(e||"NexusCrew").replace(/([a-z0-9])([A-Z])/g,"$1 $2");let o=Jn(s);(o==="localhost"||o==="node")&&(o="nexuscrew");const a=i.slice(0,4),c=Math.max(1,32-a.length-1);return`${o.slice(0,c).replace(/-+$/g,"")||"n"}-${a}`}function Mx(e,{deviceDefault:t="",localNodeId:i="",localNameDefault:s=""}={}){const o={name:e.name,ssh:e.ssh,pairingUrl:e.pairingUrl,...e.label?{label:e.label}:{},...e.sshPort?{sshPort:Number(e.sshPort)}:{}},a=e.localLabel||t;a&&(o.localLabel=a);const c=String(e.localName||tl(a,i)||s||"").trim();return c&&(o.localName=c),o}function Lx(){const e=new Set;let t=!1;return{canAuto(i){return!t&&!e.has(String(i||""))},start(i){return t?!1:(t=!0,e.add(String(i||"")),!0)},finish(){t=!1},reset(i){e.delete(String(i||""))},isBusy(){return t}}}const Tx=["validation","conflict","ssh-start","ssh-ready","join","tunnel-final","confirm","health"];function Ax(e){const t=e&&e.data||{},i=typeof t.stage=="string"&&t.stage?t.stage:"",s=typeof t.detail=="string"&&t.detail?t.detail:"";return{stage:i,code:typeof t.code=="string"?t.code:"",detail:s,hint:typeof t.hint=="string"?t.hint:"",suggestedName:typeof t.suggestedName=="string"?t.suggestedName:"",retryable:typeof t.retryable=="boolean"?t.retryable:!i,message:s||String(e&&e.message||e)}}const Ix="modulepreload",Bx=function(e){return"/"+e},Sv={},Ox=function(t,i,s){let o=Promise.resolve();if(i&&i.length>0){let c=function(p){return Promise.all(p.map(y=>Promise.resolve(y).then(S=>({status:"fulfilled",value:S}),S=>({status:"rejected",reason:S}))))};document.getElementsByTagName("link");const h=document.querySelector("meta[property=csp-nonce]"),m=(h==null?void 0:h.nonce)||(h==null?void 0:h.getAttribute("nonce"));o=c(i.map(p=>{if(p=Bx(p),p in Sv)return;Sv[p]=!0;const y=p.endsWith(".css"),S=y?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${p}"]${S}`))return;const _=document.createElement("link");if(_.rel=y?"stylesheet":Ix,y||(_.as="script"),_.crossOrigin="",_.href=p,m&&_.setAttribute("nonce",m),document.head.appendChild(_),y)return new Promise((C,w)=>{_.addEventListener("load",C),_.addEventListener("error",()=>w(new Error(`Unable to preload CSS for ${p}`)))})}))}function a(c){const h=new Event("vite:preloadError",{cancelable:!0});if(h.payload=c,window.dispatchEvent(h),!h.defaultPrevented)throw c}return o.then(c=>{for(const h of c||[])h.status==="rejected"&&a(h.reason);return t().catch(a)})};class Ye{constructor(t,i,s,o,a){this._legacyCanvasSize=Ye.DEFAULT_CANVAS_SIZE,this._preferredCamera="environment",this._maxScansPerSecond=25,this._lastScanTimestamp=-1,this._destroyed=this._flashOn=this._paused=this._active=!1,this.$video=t,this.$canvas=document.createElement("canvas"),s&&typeof s=="object"?this._onDecode=i:(console.warn(s||o||a?"You're using a deprecated version of the QrScanner constructor which will be removed in the future":"Note that the type of the scan result passed to onDecode will change in the future. To already switch to the new api today, you can pass returnDetailedScanResult: true."),this._legacyOnDecode=i),i=typeof s=="object"?s:{},this._onDecodeError=i.onDecodeError||(typeof s=="function"?s:this._onDecodeError),this._calculateScanRegion=i.calculateScanRegion||(typeof o=="function"?o:this._calculateScanRegion),this._preferredCamera=i.preferredCamera||a||this._preferredCamera,this._legacyCanvasSize=typeof s=="number"?s:typeof o=="number"?o:this._legacyCanvasSize,this._maxScansPerSecond=i.maxScansPerSecond||this._maxScansPerSecond,this._onPlay=this._onPlay.bind(this),this._onLoadedMetaData=this._onLoadedMetaData.bind(this),this._onVisibilityChange=this._onVisibilityChange.bind(this),this._updateOverlay=this._updateOverlay.bind(this),t.disablePictureInPicture=!0,t.playsInline=!0,t.muted=!0;let c=!1;if(t.hidden&&(t.hidden=!1,c=!0),document.body.contains(t)||(document.body.appendChild(t),c=!0),s=t.parentElement,i.highlightScanRegion||i.highlightCodeOutline){if(o=!!i.overlay,this.$overlay=i.overlay||document.createElement("div"),a=this.$overlay.style,a.position="absolute",a.display="none",a.pointerEvents="none",this.$overlay.classList.add("scan-region-highlight"),!o&&i.highlightScanRegion){this.$overlay.innerHTML='<svg class="scan-region-highlight-svg" viewBox="0 0 238 238" preserveAspectRatio="none" style="position:absolute;width:100%;height:100%;left:0;top:0;fill:none;stroke:#e9b213;stroke-width:4;stroke-linecap:round;stroke-linejoin:round"><path d="M31 2H10a8 8 0 0 0-8 8v21M207 2h21a8 8 0 0 1 8 8v21m0 176v21a8 8 0 0 1-8 8h-21m-176 0H10a8 8 0 0 1-8-8v-21"/></svg>';try{this.$overlay.firstElementChild.animate({transform:["scale(.98)","scale(1.01)"]},{duration:400,iterations:1/0,direction:"alternate",easing:"ease-in-out"})}catch{}s.insertBefore(this.$overlay,this.$video.nextSibling)}i.highlightCodeOutline&&(this.$overlay.insertAdjacentHTML("beforeend",'<svg class="code-outline-highlight" preserveAspectRatio="none" style="display:none;width:100%;height:100%;fill:none;stroke:#e9b213;stroke-width:5;stroke-dasharray:25;stroke-linecap:round;stroke-linejoin:round"><polygon/></svg>'),this.$codeOutlineHighlight=this.$overlay.lastElementChild)}this._scanRegion=this._calculateScanRegion(t),requestAnimationFrame(()=>{let h=window.getComputedStyle(t);h.display==="none"&&(t.style.setProperty("display","block","important"),c=!0),h.visibility!=="visible"&&(t.style.setProperty("visibility","visible","important"),c=!0),c&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),t.style.opacity="0",t.style.width="0",t.style.height="0",this.$overlay&&this.$overlay.parentElement&&this.$overlay.parentElement.removeChild(this.$overlay),delete this.$overlay,delete this.$codeOutlineHighlight),this.$overlay&&this._updateOverlay()}),t.addEventListener("play",this._onPlay),t.addEventListener("loadedmetadata",this._onLoadedMetaData),document.addEventListener("visibilitychange",this._onVisibilityChange),window.addEventListener("resize",this._updateOverlay),this._qrEnginePromise=Ye.createQrEngine()}static set WORKER_PATH(t){console.warn("Setting QrScanner.WORKER_PATH is not required and not supported anymore. Have a look at the README for new setup instructions.")}static async hasCamera(){try{return!!(await Ye.listCameras(!1)).length}catch{return!1}}static async listCameras(t=!1){if(!navigator.mediaDevices)return[];let i=async()=>(await navigator.mediaDevices.enumerateDevices()).filter(o=>o.kind==="videoinput"),s;try{t&&(await i()).every(o=>!o.label)&&(s=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch{}try{return(await i()).map((o,a)=>({id:o.deviceId,label:o.label||(a===0?"Default Camera":`Camera ${a+1}`)}))}finally{s&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),Ye._stopVideoStream(s))}}async hasFlash(){let t;try{if(this.$video.srcObject){if(!(this.$video.srcObject instanceof MediaStream))return!1;t=this.$video.srcObject}else t=(await this._getCameraStream()).stream;return"torch"in t.getVideoTracks()[0].getSettings()}catch{return!1}finally{t&&t!==this.$video.srcObject&&(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),Ye._stopVideoStream(t))}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&!this._destroyed&&(this._flashOn=!0,this._active&&!this._paused))try{if(!await this.hasFlash())throw"No flash available";await this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(t){throw this._flashOn=!1,t}}async turnFlashOff(){this._flashOn&&(this._flashOn=!1,await this._restartVideoStream())}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData),this.$video.removeEventListener("play",this._onPlay),document.removeEventListener("visibilitychange",this._onVisibilityChange),window.removeEventListener("resize",this._updateOverlay),this._destroyed=!0,this._flashOn=!1,this.stop(),Ye._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(this._destroyed)throw Error("The QR scanner can not be started as it had been destroyed.");if((!this._active||this._paused)&&(window.location.protocol!=="https:"&&console.warn("The camera stream is only accessible if the page is transferred via https."),this._active=!0,!document.hidden))if(this._paused=!1,this.$video.srcObject)await this.$video.play();else try{let{stream:t,facingMode:i}=await this._getCameraStream();!this._active||this._paused?Ye._stopVideoStream(t):(this._setVideoMirror(i),this.$video.srcObject=t,await this.$video.play(),this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{})))}catch(t){if(!this._paused)throw this._active=!1,t}}stop(){this.pause(),this._active=!1}async pause(t=!1){if(this._paused=!0,!this._active)return!0;this.$video.pause(),this.$overlay&&(this.$overlay.style.display="none");let i=()=>{this.$video.srcObject instanceof MediaStream&&(Ye._stopVideoStream(this.$video.srcObject),this.$video.srcObject=null)};return t?(i(),!0):(await new Promise(s=>setTimeout(s,300)),this._paused?(i(),!0):!1)}async setCamera(t){t!==this._preferredCamera&&(this._preferredCamera=t,await this._restartVideoStream())}static async scanImage(t,i,s,o,a=!1,c=!1){let h,m=!1;i&&("scanRegion"in i||"qrEngine"in i||"canvas"in i||"disallowCanvasResizing"in i||"alsoTryWithoutScanRegion"in i||"returnDetailedScanResult"in i)?(h=i.scanRegion,s=i.qrEngine,o=i.canvas,a=i.disallowCanvasResizing||!1,c=i.alsoTryWithoutScanRegion||!1,m=!0):console.warn(i||s||o||a||c?"You're using a deprecated api for scanImage which will be removed in the future.":"Note that the return type of scanImage will change in the future. To already switch to the new api today, you can pass returnDetailedScanResult: true."),i=!!s;try{let p,y;[s,p]=await Promise.all([s||Ye.createQrEngine(),Ye._loadImage(t)]),[o,y]=Ye._drawToCanvas(p,h,o,a);let S;if(s instanceof Worker){let _=s;i||Ye._postWorkerMessageSync(_,"inversionMode","both"),S=await new Promise((C,w)=>{let x,k,b,D=-1;k=M=>{M.data.id===D&&(_.removeEventListener("message",k),_.removeEventListener("error",b),clearTimeout(x),M.data.data!==null?C({data:M.data.data,cornerPoints:Ye._convertPoints(M.data.cornerPoints,h)}):w(Ye.NO_QR_CODE_FOUND))},b=M=>{_.removeEventListener("message",k),_.removeEventListener("error",b),clearTimeout(x),w("Scanner error: "+(M?M.message||M:"Unknown Error"))},_.addEventListener("message",k),_.addEventListener("error",b),x=setTimeout(()=>b("timeout"),1e4);let L=y.getImageData(0,0,o.width,o.height);D=Ye._postWorkerMessageSync(_,"decode",L,[L.data.buffer])})}else S=await Promise.race([new Promise((_,C)=>window.setTimeout(()=>C("Scanner error: timeout"),1e4)),(async()=>{try{var[_]=await s.detect(o);if(!_)throw Ye.NO_QR_CODE_FOUND;return{data:_.rawValue,cornerPoints:Ye._convertPoints(_.cornerPoints,h)}}catch(C){if(_=C.message||C,/not implemented|service unavailable/.test(_))return Ye._disableBarcodeDetector=!0,Ye.scanImage(t,{scanRegion:h,canvas:o,disallowCanvasResizing:a,alsoTryWithoutScanRegion:c});throw`Scanner error: ${_}`}})()]);return m?S:S.data}catch(p){if(!h||!c)throw p;let y=await Ye.scanImage(t,{qrEngine:s,canvas:o,disallowCanvasResizing:a});return m?y:y.data}finally{i||Ye._postWorkerMessage(s,"close")}}setGrayscaleWeights(t,i,s,o=!0){Ye._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:t,green:i,blue:s,useIntegerApproximation:o})}setInversionMode(t){Ye._postWorkerMessage(this._qrEnginePromise,"inversionMode",t)}static async createQrEngine(t){if(t&&console.warn("Specifying a worker path is not required and not supported anymore."),t=()=>Ox(()=>import("./qr-scanner-worker.min-D85Z9gVD.js"),[]).then(s=>s.createWorker()),!(!Ye._disableBarcodeDetector&&"BarcodeDetector"in window&&BarcodeDetector.getSupportedFormats&&(await BarcodeDetector.getSupportedFormats()).includes("qr_code")))return t();let i=navigator.userAgentData;return i&&i.brands.some(({brand:s})=>/Chromium/i.test(s))&&/mac ?OS/i.test(i.platform)&&await i.getHighEntropyValues(["architecture","platformVersion"]).then(({architecture:s,platformVersion:o})=>/arm/i.test(s||"arm")&&13<=parseInt(o||"13")).catch(()=>!0)?t():new BarcodeDetector({formats:["qr_code"]})}_onPlay(){this._scanRegion=this._calculateScanRegion(this.$video),this._updateOverlay(),this.$overlay&&(this.$overlay.style.display=""),this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video),this._updateOverlay()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_calculateScanRegion(t){let i=Math.round(.6666666666666666*Math.min(t.videoWidth,t.videoHeight));return{x:Math.round((t.videoWidth-i)/2),y:Math.round((t.videoHeight-i)/2),width:i,height:i,downScaledWidth:this._legacyCanvasSize,downScaledHeight:this._legacyCanvasSize}}_updateOverlay(){requestAnimationFrame(()=>{if(this.$overlay){var t=this.$video,i=t.videoWidth,s=t.videoHeight,o=t.offsetWidth,a=t.offsetHeight,c=t.offsetLeft,h=t.offsetTop,m=window.getComputedStyle(t),p=m.objectFit,y=i/s,S=o/a;switch(p){case"none":var _=i,C=s;break;case"fill":_=o,C=a;break;default:(p==="cover"?y>S:y<S)?(C=a,_=C*y):(_=o,C=_/y),p==="scale-down"&&(_=Math.min(_,i),C=Math.min(C,s))}var[w,x]=m.objectPosition.split(" ").map((b,D)=>{const L=parseFloat(b);return b.endsWith("%")?(D?a-C:o-_)*L/100:L});m=this._scanRegion.width||i,S=this._scanRegion.height||s,p=this._scanRegion.x||0;var k=this._scanRegion.y||0;y=this.$overlay.style,y.width=`${m/i*_}px`,y.height=`${S/s*C}px`,y.top=`${h+x+k/s*C}px`,s=/scaleX\(-1\)/.test(t.style.transform),y.left=`${c+(s?o-w-_:w)+(s?i-p-m:p)/i*_}px`,y.transform=t.style.transform}})}static _convertPoints(t,i){if(!i)return t;let s=i.x||0,o=i.y||0,a=i.width&&i.downScaledWidth?i.width/i.downScaledWidth:1;i=i.height&&i.downScaledHeight?i.height/i.downScaledHeight:1;for(let c of t)c.x=c.x*a+s,c.y=c.y*i+o;return t}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||("requestVideoFrameCallback"in this.$video?this.$video.requestVideoFrameCallback.bind(this.$video):requestAnimationFrame)(async()=>{if(!(1>=this.$video.readyState)){var t=Date.now()-this._lastScanTimestamp,i=1e3/this._maxScansPerSecond;t<i&&await new Promise(o=>setTimeout(o,i-t)),this._lastScanTimestamp=Date.now();try{var s=await Ye.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(o){if(!this._active)return;this._onDecodeError(o)}!Ye._disableBarcodeDetector||await this._qrEnginePromise instanceof Worker||(this._qrEnginePromise=Ye.createQrEngine()),s?(this._onDecode?this._onDecode(s):this._legacyOnDecode&&this._legacyOnDecode(s.data),this.$codeOutlineHighlight&&(clearTimeout(this._codeOutlineHighlightRemovalTimeout),this._codeOutlineHighlightRemovalTimeout=void 0,this.$codeOutlineHighlight.setAttribute("viewBox",`${this._scanRegion.x||0} ${this._scanRegion.y||0} ${this._scanRegion.width||this.$video.videoWidth} ${this._scanRegion.height||this.$video.videoHeight}`),this.$codeOutlineHighlight.firstElementChild.setAttribute("points",s.cornerPoints.map(({x:o,y:a})=>`${o},${a}`).join(" ")),this.$codeOutlineHighlight.style.display="")):this.$codeOutlineHighlight&&!this._codeOutlineHighlightRemovalTimeout&&(this._codeOutlineHighlightRemovalTimeout=setTimeout(()=>this.$codeOutlineHighlight.style.display="none",100))}this._scanFrame()})}_onDecodeError(t){t!==Ye.NO_QR_CODE_FOUND&&console.log(t)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";let t=/^(environment|user)$/.test(this._preferredCamera)?"facingMode":"deviceId",i=[{width:{min:1024}},{width:{min:768}},{}],s=i.map(o=>Object.assign({},o,{[t]:{exact:this._preferredCamera}}));for(let o of[...s,...i])try{let a=await navigator.mediaDevices.getUserMedia({video:o,audio:!1}),c=this._getFacingMode(a)||(o.facingMode?this._preferredCamera:this._preferredCamera==="environment"?"user":"environment");return{stream:a,facingMode:c}}catch{}throw"Camera not found."}async _restartVideoStream(){let t=this._paused;await this.pause(!0)&&!t&&this._active&&await this.start()}static _stopVideoStream(t){for(let i of t.getTracks())i.stop(),t.removeTrack(i)}_setVideoMirror(t){this.$video.style.transform="scaleX("+(t==="user"?-1:1)+")"}_getFacingMode(t){return(t=t.getVideoTracks()[0])?/rear|back|environment/i.test(t.label)?"environment":/front|user|face/i.test(t.label)?"user":null:null}static _drawToCanvas(t,i,s,o=!1){s=s||document.createElement("canvas");let a=i&&i.x?i.x:0,c=i&&i.y?i.y:0,h=i&&i.width?i.width:t.videoWidth||t.width,m=i&&i.height?i.height:t.videoHeight||t.height;return o||(o=i&&i.downScaledWidth?i.downScaledWidth:h,i=i&&i.downScaledHeight?i.downScaledHeight:m,s.width!==o&&(s.width=o),s.height!==i&&(s.height=i)),i=s.getContext("2d",{alpha:!1}),i.imageSmoothingEnabled=!1,i.drawImage(t,a,c,h,m,0,0,s.width,s.height),[s,i]}static async _loadImage(t){if(t instanceof Image)return await Ye._awaitImageLoad(t),t;if(t instanceof HTMLVideoElement||t instanceof HTMLCanvasElement||t instanceof SVGImageElement||"OffscreenCanvas"in window&&t instanceof OffscreenCanvas||"ImageBitmap"in window&&t instanceof ImageBitmap)return t;if(t instanceof File||t instanceof Blob||t instanceof URL||typeof t=="string"){let i=new Image;i.src=t instanceof File||t instanceof Blob?URL.createObjectURL(t):t.toString();try{return await Ye._awaitImageLoad(i),i}finally{(t instanceof File||t instanceof Blob)&&URL.revokeObjectURL(i.src)}}else throw"Unsupported image type."}static async _awaitImageLoad(t){t.complete&&t.naturalWidth!==0||await new Promise((i,s)=>{let o=a=>{t.removeEventListener("load",o),t.removeEventListener("error",o),a instanceof ErrorEvent?s("Image load error"):i()};t.addEventListener("load",o),t.addEventListener("error",o)})}static async _postWorkerMessage(t,i,s,o){return Ye._postWorkerMessageSync(await t,i,s,o)}static _postWorkerMessageSync(t,i,s,o){if(!(t instanceof Worker))return-1;let a=Ye._workerMessageId++;return t.postMessage({id:a,type:i,data:s},o),a}}Ye.DEFAULT_CANVAS_SIZE=400;Ye.NO_QR_CODE_FOUND="No QR code found";Ye._disableBarcodeDetector=!1;Ye._workerMessageId=0;function zx({onResult:e,onClose:t}){const i=R.useRef(null),s=R.useRef(null),o=R.useRef(!1),[a,c]=R.useState("");R.useEffect(()=>{let m=!1;const p=i.current,y=S=>{m||o.current||(o.current=!0,e(S))};return(async()=>{if(typeof navigator>"u"||!navigator.mediaDevices||typeof navigator.mediaDevices.getUserMedia!="function"){c(v("qr-unsupported"));return}let S=!1;try{S=await Ye.hasCamera()}catch{S=!1}if(!S){c(v("qr-no-camera"));return}if(m||!p)return;const _=new Ye(p,C=>{const w=_v(C);w&&y(w)},{returnDetailedScanResult:!0,preferredCamera:"environment",calculateScanRegion:C=>{const w=C.videoWidth||640,x=C.videoHeight||480,k=Math.max(1,Math.round(Math.min(w,x)*.92));return{x:Math.round((w-k)/2),y:Math.round((x-k)/2),width:k,height:k,downScaledWidth:600,downScaledHeight:600}},highlightScanRegion:!0,highlightCodeOutline:!0});s.current=_;try{await _.start()}catch(C){if(m)return;const w=C&&C.name||"";c(v(w==="NotAllowedError"||w==="SecurityError"?"qr-permission":w==="NotFoundError"||w==="OverconstrainedError"||w==="NotReadableError"?"qr-no-camera":"qr-unsupported"))}})(),()=>{m=!0;const S=s.current;if(S){try{S.stop()}catch{}try{S.destroy()}catch{}s.current=null}const _=p,C=_&&_.srcObject;if(C&&typeof C.getTracks=="function"){for(const w of C.getTracks())try{w.stop()}catch{}try{_.srcObject=null}catch{}}}},[]);const h=async m=>{const p=m.target.files&&m.target.files[0];if(m.target.value="",!!p)try{const y=await Ye.scanImage(p,{returnDetailedScanResult:!0}),S=_v(y);if(!S)throw new Error("empty");o.current||(o.current=!0,e(S))}catch{c(v("pairing-qr-invalid"))}};return u.jsx("div",{className:"nc-sheet-overlay nc-qr-overlay",onClick:t,children:u.jsxs("div",{className:"nc-sheet nc-qr-modal",onClick:m=>m.stopPropagation(),children:[u.jsx("div",{className:"nc-sheet-head",children:u.jsx("b",{children:v("scan-qr")})}),u.jsx("video",{ref:i,className:"nc-qr-video",playsInline:!0,muted:!0}),a&&u.jsx("div",{className:"nc-err",role:"alert",children:a}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsxs("label",{className:"nc-btn ghost nc-qr-upload",children:[v("qr-upload"),u.jsx("input",{type:"file",accept:"image/*",hidden:!0,onChange:h})]}),u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:t,children:v("cancel")})]})]})})}const wv=()=>({name:"",label:"",ssh:"",sshPort:"",pairingUrl:"",localLabel:"",localName:""});function Sy({token:e,initial:t="",autoStart:i=!1,deviceDefault:s="",localNodeId:o="",localNameDefault:a="",readonly:c=!1,onSuccess:h,onBusyChange:m}){const[p,y]=R.useState(wv),[S,_]=R.useState(!1),[C,w]=R.useState(!1),[x,k]=R.useState("idle"),[b,D]=R.useState(null),[L,M]=R.useState(""),[te,U]=R.useState(!1),[O,ee]=R.useState(!1),q=R.useRef(Lx()),J=R.useRef(new Set),Q=x==="busy",T=tl(p.localLabel||s,o)||a,N=O?p.localName:T;R.useEffect(()=>(m&&m(Q),()=>{m&&m(!1)}),[Q,m]);const P=async W=>{const j=String(W.pairingUrl||"").trim();if(q.current.start(j)){k("busy"),D(null),M("");try{await t0(e,Mx({...W,localName:O?W.localName:tl(W.localLabel||s,o)||a},{deviceDefault:s,localNodeId:o,localNameDefault:a})),k("ok"),_(!1),y(wv()),J.current=new Set,U(!1),ee(!1),q.current.finish(),h&&await h()}catch(A){const se=Ax(A);D(se),k("idle"),q.current.finish(),se.code==="peer-name-conflict"&&sd(se.suggestedName)&&(ee(!0),J.current.add("localName"),y(ge=>({...ge,localName:se.suggestedName}))),(se.stage==="validation"||se.stage==="conflict"||se.stage==="ssh-start"||se.stage==="ssh-ready"||String(se.code||"").startsWith("ssh-"))&&_(!0)}}},I=(W,j=!0)=>{const{classification:A,form:se}=yv(p,W,J.current,te),ge=se.pairingUrl;if(D(null),M(""),x==="ok"&&k("idle"),y(se),A.kind==="invalid"){D({stage:"validation",code:"bad-link",message:v("pair-invalid-link"),hint:v("pair-invalid-hint"),retryable:!0});return}if(A.kind==="partial"){_(!0),M(A.missing.includes("ssh")?v("pair-missing-ssh"):v("pair-missing-name"));return}A.kind==="complete"&&j&&!c&&q.current.canAuto(ge)&&P(se)};R.useEffect(()=>{t&&I(t,i)},[]);const $=()=>{const W=String(p.pairingUrl||"").trim(),{classification:j,form:A}=yv(p,W,J.current,te);if(y(A),M(""),D(null),j.kind==="empty"){D({stage:"validation",message:v("pairing-required"),retryable:!0});return}if(j.kind==="invalid"){D({stage:"validation",code:"bad-link",message:v("pair-invalid-link"),hint:v("pair-invalid-hint"),retryable:!0});return}if(!A.ssh||!A.name){_(!0),M(A.ssh?v("pair-missing-name"):v("pair-missing-ssh"));return}if(A.label&&!vo(A.label)){D({stage:"validation",message:v("err-label"),retryable:!0});return}if(A.localLabel&&!vo(A.localLabel)){D({stage:"validation",message:v("err-label"),retryable:!0});return}const se=O?A.localName:tl(A.localLabel||s,o)||a;if(!sd(se)||se==="localhost"){_(!0),D({stage:"validation",code:"bad-local-name",message:v("err-local-name"),retryable:!0});return}q.current.reset(W),P(A)},K=async()=>{try{if(!navigator.clipboard||typeof navigator.clipboard.readText!="function")throw new Error("unsupported");const W=await navigator.clipboard.readText();W&&W.trim()?I(W):M(v("pair-clipboard-empty"))}catch{M(v("pair-clipboard-denied"))}},F=(W,j)=>{J.current.add(W),y(A=>({...A,[W]:j}))},ae=b&&b.stage&&(Tx.includes(b.stage)||b.stage==="internal")?v(`pair-stage-${b.stage}`):v("pair-stage-generic");return u.jsxs("div",{className:"nc-set-form nc-pair-card",children:[u.jsx("div",{className:"nc-sheet-label",children:v("pair-card-title")}),u.jsx("div",{className:"nc-set-info",children:v("pair-card-help")}),u.jsx("input",{className:"nc-pair-input",placeholder:v("pair-paste-ph"),value:p.pairingUrl,disabled:c||Q,onChange:W=>{y({...p,pairingUrl:W.target.value}),D(null),M("")},onPaste:W=>{const j=W.clipboardData&&W.clipboardData.getData("text")||"";j.trim()&&(W.preventDefault(),I(j))},onKeyDown:W=>{W.key==="Enter"&&(W.preventDefault(),$())}}),u.jsxs("div",{className:"nc-pair-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:c||Q,onClick:K,children:v("pair-paste-btn")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:c||Q,onClick:()=>w(!0),children:v("scan-qr")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:c||Q,title:c?v("settings-readonly"):"",onClick:$,children:v("test-and-connect")})]}),u.jsxs("div",{className:"nc-pair-status","aria-live":"polite",children:[Q&&u.jsx("div",{className:"nc-pair-progress",children:v("pair-progress")}),x==="ok"&&u.jsx("div",{className:"nc-set-test ok",children:v("node-connected")}),L&&u.jsx("div",{className:"nc-set-hint nc-pair-notice",children:L}),b&&u.jsxs("div",{className:"nc-err",role:"alert",children:[u.jsx("b",{children:ae})," — ",b.message,b.hint&&u.jsx("div",{className:"nc-pair-hint",children:b.hint}),b.retryable&&u.jsx("div",{children:u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:c||Q,onClick:$,children:v("pair-retry")})})]})]}),u.jsxs("button",{type:"button",className:"nc-btn ghost nc-pair-advanced-toggle",disabled:Q,onClick:()=>_(W=>!W),children:[S?"▾":"▸"," ",v("pair-advanced")]}),S&&u.jsxs("div",{className:"nc-pair-advanced",children:[u.jsxs("label",{className:"nc-field",children:[v("node-display-label"),u.jsx("input",{placeholder:"Home Relay",value:p.label,disabled:c||Q,onChange:W=>{J.current.add("label"),y(j=>({...j,label:W.target.value,name:te?j.name:Jn(W.target.value)}))}})]}),u.jsxs("label",{className:"nc-field",children:[v("node-name-label"),u.jsx("input",{placeholder:v("node-name-ph"),value:p.name,disabled:c||Q,onChange:W=>{U(!0),J.current.add("name"),y(j=>({...j,name:W.target.value?Jn(W.target.value):""}))}})]}),u.jsx("small",{className:"nc-set-hint",children:v("node-slug-hint").replace("{slug}",p.name||"home-relay")}),u.jsxs("label",{className:"nc-field",children:[v("pair-ssh-local-label"),u.jsx("input",{placeholder:"my-relay",value:p.ssh,disabled:c||Q,onChange:W=>{J.current.add("ssh"),F("ssh",W.target.value)}}),u.jsx("small",{className:"nc-set-hint",children:v("node-ssh-local-help")})]}),u.jsxs("label",{className:"nc-field",children:[v("node-ssh-port-label"),u.jsx("input",{inputMode:"numeric",placeholder:v("node-ssh-port-help"),value:p.sshPort,disabled:c||Q,onChange:W=>F("sshPort",W.target.value.replace(/[^0-9]/g,"").slice(0,5))})]}),u.jsxs("label",{className:"nc-field",children:[v("device-name-label"),u.jsx("input",{placeholder:s||"NexusCrew",value:p.localLabel,disabled:c||Q,onChange:W=>F("localLabel",W.target.value)}),u.jsx("small",{className:"nc-set-hint",children:v("device-name-hint")})]}),u.jsxs("label",{className:"nc-field",children:[v("local-route-name-label"),u.jsx("input",{placeholder:T||"nexuscrew-5bd6",value:N,disabled:c||Q,onChange:W=>{ee(!0),J.current.add("localName"),y(j=>({...j,localName:W.target.value?Jn(W.target.value):""}))}}),u.jsx("small",{className:"nc-set-hint",children:v("local-route-name-hint")})]})]}),C&&u.jsx(zx,{onResult:W=>{w(!1),I(W)},onClose:()=>w(!1)})]})}function Ch(){return typeof navigator<"u"&&"serviceWorker"in navigator&&typeof window<"u"&&"PushManager"in window&&typeof Notification<"u"}function Fx(e){const t="=".repeat((4-e.length%4)%4),i=(e+t).replace(/-/g,"+").replace(/_/g,"/"),s=atob(i),o=new Uint8Array(s.length);for(let a=0;a<s.length;a+=1)o[a]=s.charCodeAt(a);return o}async function Hx(){if(!Ch())return"unsupported";if(Notification.permission==="denied")return"denied";try{return await(await navigator.serviceWorker.ready).pushManager.getSubscription()?"subscribed":"idle"}catch{return"idle"}}async function $x(e){if(!Ch())throw new Error("push-unsupported");if(await Notification.requestPermission()!=="granted")throw new Error("push-denied");const i=await qt("/api/push/vapid",e),{publicKey:s}=await i.json();if(!s)throw new Error("vapid non disponibile");const a=await(await navigator.serviceWorker.ready).pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:Fx(s)}),c=await qt("/api/push/subscribe",e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({subscription:a.toJSON()})});if(!c.ok){try{await a.unsubscribe()}catch{}const h=await c.json().catch(()=>({}));throw new Error(h.error||`HTTP ${c.status}`)}return!0}async function Wx(e){if(!Ch())return!1;const i=await(await navigator.serviceWorker.ready).pushManager.getSubscription();if(!i)return!1;const s=i.endpoint;try{await i.unsubscribe()}catch{}try{await qt("/api/push/subscribe",e,{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({endpoint:s})})}catch{}return!0}const wy="nexuscrew.fleet",by=2,Ux="nexuscrew.cells",Kx=/^[A-Za-z0-9._-]{1,32}$/,xh=/^[a-z0-9._-]{1,32}$/,ky=/^[A-Za-z_][A-Za-z0-9_]{0,63}$/,qx=new Set(["standard","unsafe"]),Vx=4096,bv=256,Yx=8192,Xx=32,Gx=24,Qx=new Set(["format","version","exportedAt","cells","engines"]),Jx=new Set(["id","cwd","engine","boot","model","models","permissionPolicies","systemPrompt","prompt"]),Zx=new Set(["id","label","rc","managed","command","args","envKeys","model","promptMode","promptFlag"]),eE=new Set(["client","provider","credentialProfile","model","permissionPolicy","displayName","protocol","baseUrl","envKey","providerId"]);function _r(e,t){return typeof e=="string"&&e.length<=t&&!/[\x00-\x1f\x7f]/.test(e)}function Cy(e){const t=String(e||"");return/(?:bearer\s+|authorization\s*[:=]|(?:api[_-]?key|secret|token)\s*[:=])/i.test(t)||/^-{1,2}(?:api[-_]?key|access[-_]?key|auth(?:orization)?[-_]?token|token|secret|password|credential)(?:$|=)/i.test(t)||/\b(?:sk|fw|fpk|hf|zai)[-_][A-Za-z0-9._-]{8,}\b/i.test(t)||/https?:\/\/[^\s/@:]+:[^\s/@]+@/i.test(t)}function kv(e,t){if(e===void 0)return{};if(!e||typeof e!="object"||Array.isArray(e))return null;const i=Object.entries(e);if(i.length>128)return null;const s={};for(const[o,a]of i){if(!xh.test(o)||!t(a))return null;s[o]=a}return s}function xy(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(a=>!Jx.has(a))||!Kx.test(String(e.id||""))||typeof e.cwd!="string"||!e.cwd||e.cwd.length>Vx||!xh.test(String(e.engine||""))||e.boot!==void 0&&typeof e.boot!="boolean"||e.model!==void 0&&(typeof e.model!="string"||e.model.length>bv)||e.systemPrompt!==void 0&&e.prompt!==void 0&&e.systemPrompt!==e.prompt)return null;const t=e.systemPrompt===void 0?e.prompt===void 0?"":e.prompt:e.systemPrompt;if(typeof t!="string"||t.length>Yx)return null;const i=kv(e.models,a=>typeof a=="string"&&!!a&&a.length<=bv),s=kv(e.permissionPolicies,a=>qx.has(a));if(i===null||s===null)return null;const o={id:e.id,cwd:e.cwd,engine:e.engine,boot:e.boot===!0,systemPrompt:t};return e.model&&(o.model=e.model),Object.keys(i).length&&(o.models=i),Object.keys(s).length&&(o.permissionPolicies=s),o}function tE(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(i=>!eE.has(i)))return null;const t={};for(const[i,s]of Object.entries(e)){if(typeof s!="string"||!_r(s,i==="baseUrl"?512:128)||Cy(s))return null;t[i]=s}return!t.client||!t.provider||t.envKey&&!ky.test(t.envKey)?null:t}function Ey(e){if(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(s=>!Zx.has(s))||!xh.test(String(e.id||""))||!_r(e.label||e.id,64)||e.rc!==void 0&&typeof e.rc!="boolean")return null;const t={id:e.id,label:e.label||e.id,rc:e.rc===!0};if(e.managed!==void 0){const s=tE(e.managed);return!s||["command","args","envKeys","model","promptMode","promptFlag"].some(o=>e[o]!==void 0)?null:(t.managed=s,t)}if(!_r(e.command,512)||!e.command.startsWith("/")||!Array.isArray(e.args)||e.args.length>32||e.args.some(s=>!_r(s,1024)||Cy(s))||e.promptMode!=="flag"&&e.promptMode!=="send-keys"||e.promptMode==="flag"&&(!_r(e.promptFlag,32)||/\s/.test(e.promptFlag)))return null;if(e.model!==void 0){if(!e.model||typeof e.model!="object"||Array.isArray(e.model)||Object.keys(e.model).some(s=>s!=="flag"&&s!=="value")||!_r(e.model.flag,32)||/\s/.test(e.model.flag)||!_r(e.model.value||"",128))return null;t.model={flag:e.model.flag,value:e.model.value||""}}const i=e.envKeys===void 0?[]:e.envKeys;return!Array.isArray(i)||i.length>32||i.some(s=>!ky.test(s))?null:(Object.assign(t,{command:e.command,args:[...e.args],envKeys:[...new Set(i)].sort(),promptMode:e.promptMode}),e.promptMode==="flag"&&(t.promptFlag=e.promptFlag),t)}function Ny(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e.managed?{id:e.id,label:e.label,rc:e.rc,managed:e.managed}:{id:e.id,label:e.label,rc:e.rc,command:e.command,args:e.args,envKeys:e.envKeys,model:e.model,promptMode:e.promptMode,promptFlag:e.promptFlag},i=Ey(t);return i||null}function iE(e,t,i=[],s=[],o=new Date){i instanceof Date&&(o=i,i=[],s=[]);const a=t instanceof Set?t:new Set(t||[]),c=s instanceof Set?s:new Set(s||[]),h=[];for(const p of Array.isArray(e)?e:[]){if(!a.has(p.id))continue;const y=xy({id:p.id,cwd:p.cwd,engine:p.engine,boot:p.boot===!0,...p.model?{model:p.model}:{},...p.models?{models:p.models}:{},...p.permissionPolicies?{permissionPolicies:p.permissionPolicies}:{},systemPrompt:p.prompt||""});y&&h.push(y)}const m=[];for(const p of Array.isArray(i)?i:[]){if(!c.has(p.id))continue;const y=Ny(p);y&&m.push(y)}return{format:wy,version:by,exportedAt:o.toISOString(),cells:h,engines:m}}function nE(e){let t;try{t=JSON.parse(String(e||""))}catch{return{ok:!1,error:"invalid-json",cells:[],engines:[]}}const i=(t==null?void 0:t.format)===Ux&&(t==null?void 0:t.version)===1;if(!t||typeof t!="object"||Array.isArray(t)||Object.keys(t).some(h=>!Qx.has(h))||!i&&(t.format!==wy||t.version!==by)||!Array.isArray(t.cells)||t.cells.length>Xx||!i&&(!Array.isArray(t.engines)||t.engines.length>Gx))return{ok:!1,error:"invalid-format",cells:[],engines:[]};const s=[],o=new Set;for(const h of t.cells){const m=xy(h);if(!m)return{ok:!1,error:"invalid-cell",cells:[],engines:[]};if(o.has(m.id))return{ok:!1,error:"duplicate-cell",cells:[],engines:[]};o.add(m.id),s.push(m)}const a=[],c=new Set;for(const h of i?[]:t.engines){const m=Ey(h);if(!m)return{ok:!1,error:"invalid-engine",cells:[],engines:[]};if(c.has(m.id))return{ok:!1,error:"duplicate-engine",cells:[],engines:[]};c.add(m.id),a.push(m)}return{ok:!0,cells:s,engines:a,legacy:i,exportedAt:typeof t.exportedAt=="string"?t.exportedAt:""}}function rE(e,t,i){const s=new Set(i||[]);if(!s.has(t))return null;const o=m=>Object.fromEntries(Object.entries(m||{}).filter(([p])=>s.has(p))),a={id:e.id,cwd:e.cwd,engine:t,boot:e.boot===!0,prompt:e.systemPrompt||""};t===e.engine&&e.model?a.model=e.model:e.models&&e.models[t]&&(a.model=e.models[t]);const c=o(e.models),h=o(e.permissionPolicies);return Object.keys(c).length&&(a.models=c),Object.keys(h).length&&(a.permissionPolicies=h),a}const sE=()=>({kind:"managed",id:"claude.native",label:"",client:"claude",provider:"native",credentialProfile:"",managedModel:"",permissionPolicy:"unsafe",displayName:"",protocol:"anthropic_messages",baseUrl:"",envKey:"",providerId:"nexuscrew-custom",command:"",argsText:"",rc:!0,promptMode:"send-keys",promptFlag:"",modelFlag:"",modelValue:"",envRows:[],credentialValue:"",credentialReveal:!1,allowMissingCredential:!1}),Cv=(e="")=>({id:"",cwd:"",engine:e,boot:!1,model:"",prompt:""}),Ry=e=>e==="claude"?"unsafe":"standard",wo=(e,t)=>e.find(i=>i.client===t.client&&i.provider===t.provider&&(i.credentialProfile||"")===(t.credentialProfile||"")),oE=(e,t)=>{var i;return((i=wo(e,t))==null?void 0:i.label)||`${t.client} · ${t.provider}`};function aE(e){var t,i,s,o,a,c,h,m,p,y,S,_,C,w;return{kind:e.managed?"managed":"custom",id:e.id,label:e.label||"",command:e.command||"",argsText:(e.args||[]).join(`
89
89
  `),rc:!!e.rc,client:((t=e.managed)==null?void 0:t.client)||"claude",provider:((i=e.managed)==null?void 0:i.provider)||"native",credentialProfile:((s=e.managed)==null?void 0:s.credentialProfile)||"",managedModel:((o=e.managed)==null?void 0:o.model)||"",permissionPolicy:((a=e.managed)==null?void 0:a.permissionPolicy)||Ry((c=e.managed)==null?void 0:c.client),displayName:((h=e.managed)==null?void 0:h.displayName)||"",protocol:((m=e.managed)==null?void 0:m.protocol)||"",baseUrl:((p=e.managed)==null?void 0:p.baseUrl)||"",envKey:((y=e.managed)==null?void 0:y.envKey)||"",providerId:((S=e.managed)==null?void 0:S.providerId)||"nexuscrew-custom",modelOptions:e.availableModels||((_=e.managedInfo)==null?void 0:_.models)||[],promptMode:e.promptMode||"send-keys",promptFlag:e.promptFlag||"",modelFlag:((C=e.model)==null?void 0:C.flag)||"",modelValue:((w=e.model)==null?void 0:w.value)||"",envRows:(e.envKeys||[]).map(x=>({key:x,value:"",configured:!0,remove:!1})),credentialValue:"",credentialReveal:!1,allowMissingCredential:!1}}function lE(e,t,i=[]){if(e.kind==="managed"){const o={client:e.client,provider:e.provider,model:e.managedModel||"",permissionPolicy:e.permissionPolicy||Ry(e.client)};e.credentialProfile&&(o.credentialProfile=e.credentialProfile);const a=wo(i,e);return(a==null?void 0:a.credentialEnv)===!0&&(o.envKey=e.envKey),e.provider==="custom"&&Object.assign(o,{displayName:e.displayName,protocol:e.protocol,baseUrl:e.baseUrl,envKey:e.envKey,providerId:e.providerId}),{...t?{id:e.id}:{},label:e.label||oE(i,e),rc:!!e.rc,managed:o}}const s={...t?{id:e.id}:{},label:e.label||e.id,rc:!!e.rc,command:e.command,args:e.argsText.split(`
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 ns({children:e,onClose:t,label:i,error:s=""}){const o=R.useRef(null),a=R.useRef(null),c=R.useRef(t);return c.current=t,R.useEffect(()=>{const h=document.activeElement,m=o.current,p=()=>Array.from((m==null?void 0:m.querySelectorAll('button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [href], [tabindex]:not([tabindex="-1"])'))||[]).filter(_=>_.offsetParent!==null),y=requestAnimationFrame(()=>{var _;return(_=p()[0]||m)==null?void 0:_.focus({preventScroll:!0})}),S=_=>{var k;if(_.key==="Escape"){_.preventDefault(),(k=c.current)==null||k.call(c);return}if(_.key!=="Tab")return;const C=p();if(!C.length){_.preventDefault(),m==null||m.focus();return}const w=C[0],x=C[C.length-1];_.shiftKey&&document.activeElement===w?(_.preventDefault(),x.focus()):!_.shiftKey&&document.activeElement===x&&(_.preventDefault(),w.focus())};return document.addEventListener("keydown",S),()=>{cancelAnimationFrame(y),document.removeEventListener("keydown",S),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]),u.jsx("div",{className:"nc-fleet-modal",role:"presentation",onMouseDown:h=>{h.target===h.currentTarget&&t()},children:u.jsxs("div",{ref:o,className:"nc-fleet-modal-dialog",role:"dialog","aria-modal":"true","aria-label":i||v("settings"),tabIndex:-1,children:[e,s&&u.jsx("div",{ref:a,className:"nc-err nc-fleet-modal-error",role:"alert","aria-live":"assertive",children:s})]})})}function Qu({token:e,targets:t=[],readonly:i=!1,onPower:s,onImport:o}){const[a,c]=R.useState([]),[h,m]=R.useState(0);R.useEffect(()=>{let k=!0;async function b(){const L=[{route:[],label:v("local")}].concat((t||[]).map(te=>({route:Array.isArray(te.route)?te.route:[],label:te.label||(te.route||[]).join(" › ")}))),M=await Promise.all(L.map(async te=>{const U={...te,available:!1,readonly:!1,capabilities:[],provider:null,cells:[],unmanaged:[],err:""};try{const O=await Er(e,te.route);U.available=!!O.available,U.capabilities=O.capabilities||[],U.provider=O.provider||null,U.cells=(O.cells||[]).map(ee=>({...ee,route:te.route}));try{U.readonly=!!(await nh(e,te.route)).readonlyDefault}catch{}try{const ee=await Fv(e,te.route),q=new Set(U.cells.map(J=>J.tmuxSession).filter(Boolean));U.unmanaged=(ee.sessions||[]).filter(J=>J&&!q.has(J.name))}catch{}}catch(O){U.err=String(O&&O.message||O)}return U}));k&&c(M)}b();const D=setInterval(b,6e3);return()=>{k=!1,clearInterval(D)}},[e,t,h]);const p=(k,b)=>Array.isArray(k.capabilities)&&k.capabilities.includes(b)&&!i&&!k.readonly,y=()=>m(k=>k+1),S=(k,b)=>{s&&s({...k,route:b})},_=(k,b)=>{s&&s({...k,route:b})},C=async(k,b)=>{try{await co(e,k.cell,b)}catch{}y()},w=async(k,b)=>{if(window.confirm(v("fleet-remove-cell").replace("{id}",k.cell))){try{await Iv(e,k.cell,!0,b)}catch{}y()}},x=async(k,b)=>{if(window.confirm(v("terminate-confirm").replace("{name}",k.name))){try{await ih(e,k.name,b)}catch{}y()}};return u.jsxs("div",{className:"nc-fleet-inventory",children:[u.jsxs("div",{className:"nc-fleet-section-head",children:[u.jsx("b",{children:v("fleet-inventory")}),u.jsx("small",{children:v("fleet-inventory-help")})]}),a.map(k=>{const b=k.route.length?k.route.join("/"):"local";return u.jsxs("div",{className:"nc-fleet-pos",children:[u.jsxs("div",{className:"nc-fleet-pos-title",children:[u.jsx("span",{className:`dot ${k.available?"on":"warn"}`}),u.jsx("b",{children:k.label}),u.jsx("small",{children:k.available?`${k.cells.length} ${v("fleet-cells")} · ${k.unmanaged.length} ${v("fleet-tmux")}`:k.err||v("fleet-not-available")})]}),k.cells.map(D=>u.jsxs("div",{className:"nc-fleet-item nc-fleet-cell",children:[u.jsxs("span",{children:[u.jsx("b",{children:D.cell}),u.jsx("small",{children:`${D.engine||""}${D.key?`·${D.key}`:""}${D.active?"":` · ${v("cell-off")}`}`})]}),u.jsxs("span",{className:"nc-fleet-cell-actions",children:[D.active&&p(k,"down")&&u.jsx("button",{className:"nc-btn ghost",title:v("power-off"),onClick:()=>_(D,k.route),children:v("stop")}),!D.active&&p(k,"up")&&u.jsx("button",{className:"nc-btn ghost",title:v("power-on"),onClick:()=>S(D,k.route),children:v("start")}),p(k,"restart")&&D.active&&u.jsx("button",{className:"nc-btn ghost",title:v("restart"),onClick:()=>C(D,k.route),children:v("restart")}),p(k,"remove")&&u.jsx("button",{className:"nc-btn danger",title:v("delete"),onClick:()=>w(D,k.route),children:"×"})]})]},`${b}:${D.cell}`)),k.unmanaged.map(D=>u.jsxs("div",{className:"nc-fleet-item nc-fleet-unmanaged",children:[u.jsxs("span",{children:[u.jsx("b",{children:D.name}),u.jsx("small",{children:v("fleet-tmux")})]}),u.jsxs("span",{className:"nc-fleet-cell-actions",children:[p(k,"import")&&o&&u.jsx("button",{className:"nc-btn ghost",title:v("import-as-cell"),onClick:()=>o(D,k.route),children:v("import-as-cell")}),u.jsx("button",{className:"nc-btn danger",title:v("terminate"),onClick:()=>x(D,k.route),children:"×"})]})]},`${b}:u:${D.name}`)),k.available&&k.cells.length===0&&k.unmanaged.length===0&&u.jsx("div",{className:"nc-empty",children:v("fleet-inventory-empty")})]},b)})]})}function cE({cells:e=[],engines:t=[],busy:i,canRestore:s=!1,onRestore:o,onClose:a}){const[c,h]=R.useState("export"),[m,p]=R.useState(()=>new Set(e.map(N=>N.id))),[y,S]=R.useState(()=>new Set(t.map(N=>N.id))),[_,C]=R.useState([]),[w,x]=R.useState([]),[k,b]=R.useState(""),D=new Set(t.map(N=>N.id)),L=new Set(e.map(N=>N.id)),M=(N,P)=>N(I=>{const $=new Set(I);return $.has(P)?$.delete(P):$.add(P),$}),te=()=>{const N=iE(e,m,t,y),P=t.filter(F=>y.has(F.id)).length;if(N.engines.length!==P){b(v("fleet-backup-invalid-engine"));return}if(!N.cells.length&&!N.engines.length){b(v("fleet-backup-select-one"));return}const I=new Blob([`${JSON.stringify(N,null,2)}
91
- `],{type:"application/json"}),$=URL.createObjectURL(I),K=document.createElement("a");K.href=$,K.download=`nexuscrew-fleet-${new Date().toISOString().slice(0,10)}.json`,document.body.appendChild(K),K.click(),K.remove(),setTimeout(()=>URL.revokeObjectURL($),0),b("")},U=async N=>{if(b(""),C([]),x([]),!!N){if(N.size>1024*1024){b(v("fleet-backup-too-large"));return}try{const P=nE(await N.text());if(!P.ok){b(v(`fleet-backup-${P.error}`));return}const I=new Set(P.engines.map($=>$.id));x(P.engines.map($=>({engine:$,exists:D.has($.id),selected:!D.has($.id)}))),C(P.cells.map($=>{const K=D.has($.engine)||I.has($.engine),F=L.has($.id);return{cell:$,engine:K?$.engine:"",selected:K&&!F,exists:F}}))}catch(P){b(String(P.message||P))}}},O=(N,P)=>C(I=>I.map(($,K)=>K===N?{...$,...P}:$)),ee=(N,P)=>x(I=>I.map(($,K)=>K===N?{...$,...P}:$)),q=new Set([...D,...w.filter(N=>N.selected).map(N=>N.engine.id)]),J=_.filter(N=>N.selected&&N.engine&&q.has(N.engine)),Q=w.filter(N=>N.selected),T=[...new Map([...t.map(N=>[N.id,N.label||N.id]),...w.filter(N=>N.selected).map(N=>[N.engine.id,N.engine.label||N.engine.id])]).entries()];return u.jsxs("div",{className:"nc-set-form nc-fleet-form nc-backup-dialog",children:[u.jsx("b",{children:v("fleet-backup")}),u.jsx("small",{children:v("fleet-backup-help")}),u.jsxs("div",{className:"nc-set-tabs nc-backup-tabs",children:[u.jsx("button",{type:"button",className:`nc-set-tabbtn${c==="export"?" on":""}`,onClick:()=>{h("export"),b("")},children:v("fleet-backup-export")}),u.jsx("button",{type:"button",className:`nc-set-tabbtn${c==="import"?" on":""}`,disabled:!s,title:s?"":v("fleet-backup-restore-unavailable"),onClick:()=>{h("import"),b("")},children:v("fleet-backup-import")})]}),c==="export"?u.jsxs(u.Fragment,{children:[u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{p(new Set(e.map(N=>N.id))),S(new Set(t.map(N=>N.id)))},children:v("select-all")}),u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{p(new Set),S(new Set)},children:v("select-none")})]}),u.jsxs("div",{className:"nc-backup-list",children:[u.jsx("b",{children:v("fleet-engines")}),t.map(N=>{var P;return u.jsxs("label",{className:"nc-check nc-backup-row",children:[u.jsx("input",{type:"checkbox",checked:y.has(N.id),onChange:()=>M(S,N.id)}),u.jsxs("span",{children:[u.jsx("b",{children:N.label||N.id}),u.jsxs("small",{children:[N.id,(P=N.envKeys)!=null&&P.length?` · ${N.envKeys.length} ${v("fleet-backup-env-names")}`:""]})]})]},N.id)}),u.jsx("b",{children:v("fleet-cells")}),e.map(N=>u.jsxs("label",{className:"nc-check nc-backup-row",children:[u.jsx("input",{type:"checkbox",checked:m.has(N.id),onChange:()=>M(p,N.id)}),u.jsxs("span",{children:[u.jsx("b",{children:N.id}),u.jsxs("small",{children:[N.engine," · ",v("fleet-system-prompt")," ",(N.prompt||"").length," ",v("characters")]})]})]},N.id)),!e.length&&!t.length&&u.jsx("div",{className:"nc-empty",children:v("fleet-backup-empty")})]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:v("cancel")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:!m.size&&!y.size,onClick:te,children:v("fleet-backup-download")})]})]}):u.jsxs(u.Fragment,{children:[u.jsx("input",{type:"file",accept:"application/json,.json",disabled:i,onChange:N=>U(N.target.files&&N.target.files[0])}),u.jsx("small",{children:v("fleet-backup-import-help")}),!!(_.length||w.length)&&u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(N=>N.map(P=>({...P,selected:!0}))),C(N=>N.map(P=>({...P,selected:!!P.engine})))},children:v("select-all")}),u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(N=>N.map(P=>({...P,selected:!1}))),C(N=>N.map(P=>({...P,selected:!1})))},children:v("select-none")})]}),u.jsxs("div",{className:"nc-backup-list",children:[!!w.length&&u.jsx("b",{children:v("fleet-engines")}),w.map((N,P)=>{var I;return u.jsx("div",{className:"nc-backup-import-row",children:u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:N.selected,onChange:$=>ee(P,{selected:$.target.checked})}),u.jsxs("span",{children:[u.jsx("b",{children:N.engine.label||N.engine.id}),u.jsxs("small",{children:[N.engine.id," · ",N.exists?v("fleet-backup-overwrite"):v("fleet-backup-new"),(I=N.engine.envKeys)!=null&&I.length?` · ${v("fleet-backup-env-values-required")}`:""]})]})]})},N.engine.id)}),!!_.length&&u.jsx("b",{children:v("fleet-cells")}),_.map((N,P)=>u.jsxs("div",{className:"nc-backup-import-row",children:[u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:N.selected,disabled:!N.engine||!q.has(N.engine),onChange:I=>O(P,{selected:I.target.checked})}),u.jsxs("span",{children:[u.jsx("b",{children:N.cell.id}),u.jsxs("small",{children:[N.exists?v("fleet-backup-overwrite"):v("fleet-backup-new")," · ",v("fleet-system-prompt")," ",N.cell.systemPrompt.length," ",v("characters")]})]})]}),u.jsxs("select",{value:N.engine,onChange:I=>O(P,{engine:I.target.value,selected:!!I.target.value}),children:[u.jsx("option",{value:"",children:v("fleet-backup-engine-missing")}),T.map(([I,$])=>u.jsx("option",{value:I,children:$},I))]})]},N.cell.id))]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:v("cancel")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||!J.length&&!Q.length,onClick:()=>o({engineRows:w,cellRows:_}),children:v("fleet-backup-restore")})]})]}),k&&u.jsx("div",{className:"nc-err",children:k})]})}function uE({state:e,setState:t,busy:i,onSave:s,catalog:o}){var k;const a=e.form,c=b=>t({...e,form:{...a,...b}}),h=a.envRows||[],m=[...new Map(o.map(b=>[b.client,b.clientLabel||b.client])).entries()],p=o.filter(b=>b.client===a.client),y=wo(o,a),S=typeof(y==null?void 0:y.credentialEnv)=="string"?y.credentialEnv:"",_=Array.isArray(y==null?void 0:y.credentialUsedBy)?y.credentialUsedBy:[],C=(y==null?void 0:y.credentialSource)||"missing",w=!!S&&e.mode==="new"&&(y==null?void 0:y.authConfigured)!==!0&&!a.credentialValue&&!a.allowMissingCredential,x=b=>{b&&c({client:b.client,provider:b.provider,credentialProfile:b.credentialProfile||"",managedModel:b.model||"",protocol:b.protocol||"",permissionPolicy:b.permissionPolicyDefault||"standard",rc:!!b.rc,displayName:b.custom?v("fleet-custom-provider-default"):"",baseUrl:b.custom?"":b.endpoint||"",envKey:b.defaultEnvKey||"",providerId:"nexuscrew-custom",credentialValue:"",credentialReveal:!1,allowMissingCredential:!1,...e.mode==="new"?{id:b.id,label:""}:{}})};return u.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[u.jsx("b",{children:e.mode==="new"?v("fleet-new-engine"):`${v("edit")} ${a.id}`}),u.jsx("input",{value:a.id,disabled:e.mode!=="new",placeholder:"id",onChange:b=>c({id:b.target.value})}),u.jsx("input",{value:a.label,placeholder:v("label"),onChange:b=>c({label:b.target.value})}),u.jsxs("select",{value:a.kind,disabled:e.mode!=="new",onChange:b=>c({kind:b.target.value}),children:[u.jsx("option",{value:"managed",children:v("fleet-managed")}),u.jsx("option",{value:"custom",children:v("fleet-custom")})]}),a.kind==="managed"?u.jsxs(u.Fragment,{children:[u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("select",{value:a.client,disabled:e.mode!=="new",onChange:b=>x(o.find(D=>D.client===b.target.value&&D.default)||o.find(D=>D.client===b.target.value)),children:m.map(([b,D])=>u.jsx("option",{value:b,children:D},b))}),u.jsx("select",{value:((k=wo(o,a))==null?void 0:k.id)||"",disabled:e.mode!=="new",onChange:b=>x(o.find(D=>D.id===b.target.value)),children:p.map(b=>u.jsx("option",{value:b.id,children:b.label},b.id))})]}),u.jsx("input",{value:a.managedModel,list:"nc-managed-models",placeholder:v(y!=null&&y.requiresModel?"fleet-model-required":"fleet-model-default"),onChange:b=>c({managedModel:b.target.value})}),u.jsx("datalist",{id:"nc-managed-models",children:[...(y==null?void 0:y.models)||[],...a.modelOptions||[]].filter((b,D,L)=>b&&L.indexOf(b)===D).map(b=>u.jsx("option",{value:b},b))}),y!=null&&y.supportsUnsafe?u.jsxs("select",{value:a.permissionPolicy,onChange:b=>c({permissionPolicy:b.target.value}),children:[u.jsx("option",{value:"standard",children:v("fleet-standard-permissions")}),u.jsx("option",{value:"unsafe",children:v("fleet-unsafe-permissions")})]}):u.jsx("small",{children:v("fleet-standard-permissions")}),(y==null?void 0:y.supportsUnsafe)&&a.permissionPolicy==="unsafe"&&u.jsx("small",{className:"nc-err",children:v("fleet-unsafe-warning")}),(y==null?void 0:y.credentialEnv)===!0&&u.jsxs(u.Fragment,{children:[u.jsx("input",{value:a.envKey,placeholder:v("fleet-api-key-env"),onChange:b=>c({envKey:b.target.value})}),u.jsx("small",{children:v("fleet-custom-secret-help")})]}),S&&u.jsxs("section",{className:"nc-fleet-key","aria-label":v("fleet-key-section"),children:[u.jsxs("div",{className:"nc-fleet-key-head",children:[u.jsx("b",{children:v("fleet-key-section")}),u.jsx("span",{children:v(`fleet-credential-source-${C}`)})]}),u.jsxs("label",{className:"nc-field",children:[v("fleet-key-variable"),u.jsx("input",{value:S,readOnly:!0,"aria-label":v("fleet-key-variable")})]}),u.jsx("small",{children:_.length?v("fleet-key-used-by").replace("{engines}",_.join(", ")):v("fleet-key-used-by-none")}),_.length>0&&u.jsx("small",{children:v("fleet-key-shared-impact").replace("{key}",S).replace("{engines}",_.join(", "))}),u.jsxs("div",{className:"nc-fleet-key-input",children:[u.jsx("input",{type:a.credentialReveal?"text":"password",value:a.credentialValue||"","aria-label":v("fleet-key-value").replace("{key}",S),autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",placeholder:y!=null&&y.authConfigured||e.mode==="edit"?v("fleet-key-blank-keeps"):v("fleet-key-enter"),onChange:b=>c({credentialValue:b.target.value,allowMissingCredential:b.target.value?!1:a.allowMissingCredential})}),u.jsx("button",{type:"button",className:"nc-btn ghost","aria-label":v(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal"),onClick:()=>c({credentialReveal:!a.credentialReveal}),children:v(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal")})]}),u.jsx("small",{children:v("fleet-key-local-help")}),e.mode==="new"&&(y==null?void 0:y.authConfigured)!==!0&&!a.credentialValue&&u.jsxs("label",{className:"nc-check nc-fleet-key-missing",children:[u.jsx("input",{type:"checkbox",checked:!!a.allowMissingCredential,onChange:b=>c({allowMissingCredential:b.target.checked})}),v("fleet-key-create-missing")]}),(y==null?void 0:y.notice)&&u.jsx("small",{className:"nc-fleet-provider-notice",children:v(`fleet-provider-notice-${y.notice}`)})]}),a.provider==="custom"&&u.jsxs(u.Fragment,{children:[u.jsx("input",{value:a.displayName,placeholder:v("fleet-provider-display"),onChange:b=>c({displayName:b.target.value})}),u.jsx("input",{value:a.baseUrl,placeholder:"https://api.example.com/v1",onChange:b=>c({baseUrl:b.target.value})}),((y==null?void 0:y.protocols)||[]).length>1&&u.jsx("select",{value:a.protocol,onChange:b=>c({protocol:b.target.value}),children:y.protocols.map(b=>u.jsx("option",{value:b,children:b},b))}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:a.envKey,placeholder:v("fleet-api-key-env"),onChange:b=>c({envKey:b.target.value})}),u.jsx("input",{value:a.providerId,placeholder:v("fleet-provider-id"),onChange:b=>c({providerId:b.target.value})})]}),u.jsxs("small",{children:[a.protocol," · ",v("fleet-custom-secret-help")]})]}),u.jsx("small",{children:v("fleet-managed-help")})]}):u.jsxs(u.Fragment,{children:[u.jsx("input",{value:a.command,placeholder:v("command-path"),onChange:b=>c({command:b.target.value})}),u.jsx("textarea",{value:a.argsText,placeholder:v("args-lines"),onChange:b=>c({argsText:b.target.value})}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:a.rc,onChange:b=>c({rc:b.target.checked})})," remote control"]}),u.jsxs("select",{value:a.promptMode,onChange:b=>c({promptMode:b.target.value}),children:[u.jsx("option",{value:"send-keys",children:"send-keys"}),u.jsx("option",{value:"flag",children:"flag"})]}),a.promptMode==="flag"&&u.jsx("input",{value:a.promptFlag,placeholder:"prompt flag",onChange:b=>c({promptFlag:b.target.value})}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:a.modelFlag,placeholder:"model flag",onChange:b=>c({modelFlag:b.target.value})}),u.jsx("input",{value:a.modelValue,placeholder:"model default",onChange:b=>c({modelValue:b.target.value})})]}),u.jsx("small",{children:v("env-write-only")}),h.map((b,D)=>u.jsxs("div",{className:"nc-fleet-env",children:[u.jsx("input",{value:b.key,disabled:b.configured,placeholder:"ENV_KEY",onChange:L=>{const M=h.slice();M[D]={...b,key:L.target.value},c({envRows:M})}}),u.jsx("input",{type:"password",value:b.value,placeholder:b.configured?"•••••• (unchanged)":"value",onChange:L=>{const M=h.slice();M[D]={...b,value:L.target.value},c({envRows:M})}}),u.jsx("button",{className:"nc-btn danger",onClick:()=>c({envRows:h.filter((L,M)=>M!==D)}),children:"×"})]},`${b.key}-${D}`)),u.jsx("button",{className:"nc-btn ghost",onClick:()=>c({envRows:[...h,{key:"",value:"",configured:!1}]}),children:"+ env"})]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>t(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:i||!a.id||w||a.kind==="custom"&&!a.command||a.kind==="managed"&&(y==null?void 0:y.requiresModel)&&!a.managedModel||a.kind==="managed"&&(y==null?void 0:y.credentialEnv)===!0&&!a.envKey||a.kind==="managed"&&a.provider==="custom"&&(!a.displayName||!a.baseUrl||!a.envKey||!a.providerId),onClick:s,children:v("save")})]})]})}function dE({token:e,route:t,targets:i=[],location:s,setLocation:o,state:a,setState:c,engines:h,busy:m,onSave:p}){const[y,S]=R.useState(null),[_,C]=R.useState(""),w=a.form,x=L=>c({...a,form:{...w,...L}}),k=h.find(L=>L.id===w.engine),b=L=>{var te,U,O;const M=h.find(ee=>ee.id===L);x({engine:L,model:((te=w.models)==null?void 0:te[L])||((U=M==null?void 0:M.managed)==null?void 0:U.model)||((O=M==null?void 0:M.model)==null?void 0:O.value)||""})},D=async L=>{try{const M=await Ov(e,L,t);S(M),x({cwd:M.path}),C("")}catch(M){C(String(M.message||M))}};return u.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[u.jsx("b",{children:a.mode==="new"?v("fleet-new-cell"):`${v("edit")} ${w.id}`}),a.mode==="new"&&u.jsxs("label",{className:"nc-field",children:[v("location"),u.jsx("span",{className:"nc-req",children:" *"}),u.jsxs("select",{value:s,onChange:L=>{o(L.target.value),x({engine:""})},children:[u.jsx("option",{value:"",children:v("local")}),i.map(L=>u.jsxs("option",{value:L.route.join("/"),disabled:L.status&&L.status!=="up",children:[L.label,L.status&&L.status!=="up"?` · ${v("node-offline")}`:""]},L.route.join("/")))]})]}),u.jsx("input",{value:w.id,disabled:a.mode!=="new",placeholder:"id",onChange:L=>x({id:L.target.value})}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:w.cwd,placeholder:v("cwd"),onChange:L=>x({cwd:L.target.value})}),u.jsx("button",{className:"nc-btn ghost",onClick:()=>y?S(null):D(w.cwd),children:v("browse")})]}),y&&u.jsxs("div",{className:"nc-fs",children:[u.jsx("div",{className:"nc-fs-path",children:y.path}),u.jsxs("div",{className:"nc-fs-list",children:[y.parent&&u.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>D(y.parent),children:["↑ ",v("fs-parent")]}),(y.dirs||[]).map(L=>u.jsxs("button",{className:"nc-fs-item",onClick:()=>D(`${y.path.replace(/\/$/,"")}/${L}`),children:["📁 ",L]},L))]})]}),_&&u.jsx("div",{className:"nc-err",children:_}),u.jsx("select",{value:w.engine,onChange:L=>b(L.target.value),children:h.map(L=>u.jsx("option",{value:L.id,children:L.label},L.id))}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:!!w.boot,onChange:L=>x({boot:L.target.checked})})," boot"]}),u.jsx("input",{value:w.model||"",list:"nc-cell-models",placeholder:v("fleet-model-override"),onChange:L=>x({model:L.target.value})}),u.jsx("datalist",{id:"nc-cell-models",children:((k==null?void 0:k.availableModels)||[]).map(L=>u.jsx("option",{value:L},L))}),u.jsx("textarea",{value:w.prompt||"",placeholder:"prompt",onChange:L=>x({prompt:L.target.value})}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>c(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:m||!w.id||!w.cwd||!w.engine,onClick:p,children:v("save")})]})]})}function xv({token:e,route:t=[],state:i,setState:s,busy:o,onSave:a}){const[c,h]=R.useState(null),[m,p]=R.useState([]),[y,S]=R.useState(""),_=Array.isArray(t)?t.join("/"):"";R.useEffect(()=>{let b=!0;return th(e,_?_.split("/"):[]).then(D=>{b&&(p(D.engines||[]),S(""))}).catch(D=>{b&&(p([]),S(String(D.message||D)))}),()=>{b=!1}},[e,_]);const C=i.form,w=b=>s({...i,form:{...C,...b}}),x=async b=>{try{const D=await Ov(e,b,t);h(D),w({cwd:D.path})}catch{}},k=!C.id&&C.tmuxSession?C.tmuxSession.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,32):C.id;return u.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[u.jsxs("b",{children:[v("import-as-cell")," · ",C.tmuxSession]}),u.jsx("small",{children:v("import-help")}),u.jsx("input",{value:C.tmuxSession,disabled:!0,placeholder:"tmux session",readOnly:!0}),u.jsx("input",{value:C.id,placeholder:v("name"),onChange:b=>w({id:b.target.value})}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:C.cwd,placeholder:v("cwd"),onChange:b=>w({cwd:b.target.value})}),u.jsx("button",{className:"nc-btn ghost",type:"button",onClick:()=>c?h(null):x(C.cwd),children:v("browse")})]}),c&&u.jsxs("div",{className:"nc-fs",children:[u.jsx("div",{className:"nc-fs-path",children:c.path}),u.jsxs("div",{className:"nc-fs-list",children:[c.parent&&u.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>x(c.parent),children:["↑ ",v("fs-parent")]}),(c.dirs||[]).map(b=>u.jsxs("button",{className:"nc-fs-item",onClick:()=>x(`${c.path.replace(/\/$/,"")}/${b}`),children:["📁 ",b]},b))]})]}),u.jsxs("select",{value:C.engine,onChange:b=>w({engine:b.target.value}),children:[u.jsx("option",{value:"",children:v("import-engine-required")}),m.map(b=>u.jsx("option",{value:b.id,children:b.label},b.id))]}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:!!C.boot,onChange:b=>w({boot:b.target.checked})})," boot"]}),(i.err||y)&&u.jsx("div",{className:"nc-err",children:i.err||y}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>s(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:o||!C.tmuxSession||!C.engine||!m.length,onClick:a,children:v(k?"import-as-cell":"save")})]})]})}function hE({token:e,readonly:t,targets:i=[],startNewCell:s=!1,initialLocation:o=""}){const[a,c]=R.useState({engines:[],cells:[],managedCatalog:[]}),[h,m]=R.useState({available:!1,capabilities:[]}),[p,y]=R.useState(!1),[S,_]=R.useState(null),[C,w]=R.useState(null),[x,k]=R.useState(!1),[b,D]=R.useState(""),[L,M]=R.useState(""),[te,U]=R.useState(o),[O,ee]=R.useState(!1),[q,J]=R.useState(null),[Q,T]=R.useState(null),[N,P]=R.useState(!1),[I,$]=R.useState([]),[K,F]=R.useState(null),[ae,W]=R.useState("manage"),j=R.useRef(!1),A=te?te.split("/"):[],se=R.useCallback(async()=>{try{const le=await Er(e,A);m(le);try{const be=await nh(e,A);ee(!!be.readonlyDefault)}catch{ee(!1)}if(le.provider==="builtin"&&(le.capabilities||[]).includes("definitions")){const be=await th(e,A),Fe=new Map((le.engines||[]).map($e=>[$e.id,$e]));if(be.engines=(be.engines||[]).map($e=>{var pe;return{...$e,availableModels:((pe=Fe.get($e.id))==null?void 0:pe.models)||[]}}),c(be),(le.capabilities||[]).includes("credentials"))try{$((await Hw(e,A)).credentials||[])}catch{$([])}else $([])}D("")}catch(le){D(String(le.message||le))}finally{y(!0)}},[e,te]);R.useEffect(()=>{se();const le=setInterval(se,5e3);return()=>clearInterval(le)},[se]);const ge=new Set((h.cells||[]).filter(le=>le.active).map(le=>le.cell)),G=h.provider==="builtin"&&(h.capabilities||[]).includes("edit"),Z=(h.capabilities||[]).includes("restore");R.useEffect(()=>{!s||j.current||!G||!a.engines.length||(j.current=!0,w({mode:"new",form:Cv(a.engines[0].id)}))},[s,G,a.engines]);const ue=async le=>{k(!0),D(""),M("");try{await le(),await se()}catch(be){D(String(be.message||be))}k(!1)},de=()=>ue(async()=>{var re;const le=S.mode==="new",be=S.form,Fe=a.managedCatalog||[],$e=be.kind==="managed"?wo(Fe,be):null,pe=typeof($e==null?void 0:$e.credentialEnv)=="string"?$e.credentialEnv:"",Be=pe&&be.credentialValue||"",Te=lE(be,le,Fe);!le&&S.form.kind==="custom"&&!S.form.modelFlag&&(Te.model=null),!le&&S.form.kind==="custom"&&S.form.promptMode!=="flag"&&(Te.promptFlag=null);let Y;if(le)Y=await Ww(e,Te,A);else{const me=S.original,Ae=new Set(S.form.envRows.filter(He=>!He.remove).map(He=>He.key)),Se=(me.envKeys||[]).filter(He=>!Ae.has(He)),xe=Object.fromEntries(S.form.envRows.filter(He=>!He.remove&&He.key&&(!He.configured||He.value!=="")).map(He=>[He.key,He.value]));Y=await Uw(e,me.id,Te,S.form.kind==="custom"?{set:xe,remove:Se}:void 0,A)}let ne=null;if(pe&&Be)try{ne=await pm(e,pe,Be,A),$(ne.credentials||[])}catch{throw await se(),le&&_({mode:"edit",original:{...Te,id:be.id},form:{...be,credentialReveal:!1}}),new Error(v(le?"fleet-key-partial-create":"fleet-key-partial-edit"))}await se(),_(null),M(v("fleet-saved"));const z=((re=((ne==null?void 0:ne.credentials)||[]).find(me=>me.envKey===pe))==null?void 0:re.activeCells)||[],H=[...new Set([...(Y==null?void 0:Y.activeCells)||[],...z])];if(H.length&&window.confirm(v("fleet-restart-confirm").replace("{cells}",H.join(", "))))for(const me of H)await co(e,me,A)}),Ce=()=>ue(async()=>{const le=C.mode==="new",be=C.form,Fe={...le?{id:be.id}:{},cwd:be.cwd,engine:be.engine,boot:!!be.boot};le?(be.model&&(Fe.model=be.model),be.prompt&&(Fe.prompt=be.prompt)):(Fe.model=be.model||null,Fe.prompt=be.prompt||null);const $e=le?await qw(e,Fe,A):await Vw(e,C.original.id,Fe,A),pe=le?be.id:C.original.id;w(null),M(v("fleet-saved")),!le&&($e!=null&&$e.active)&&window.confirm(v("fleet-restart-confirm").replace("{cells}",pe))&&await co(e,pe,A)}),Re=t||O,tt=le=>I.find(be=>{var Fe;return(Fe=be.engines)==null?void 0:Fe.includes(le.id)})||null,Je=()=>ue(async()=>{var Fe;const le=await pm(e,K.envKey,K.value,A);$(le.credentials||[]),F(null),M(v("fleet-credential-saved"));const be=((Fe=(le.credentials||[]).find($e=>$e.envKey===K.envKey))==null?void 0:Fe.activeCells)||[];if(be.length&&window.confirm(v("fleet-restart-confirm").replace("{cells}",be.join(", "))))for(const $e of be)await co(e,$e,A)}),Ee=le=>ue(async()=>{if(!window.confirm(v("fleet-credential-remove-confirm").replace("{key}",le.envKey)))return;const be=await $w(e,le.envKey,A);$(be.credentials||[]),M(v("fleet-credential-removed"))}),rt=le=>J({...le,route:Array.isArray(le==null?void 0:le.route)?le.route:A}),wt=async le=>{if(!q)return;const be=q.cell||q.id,Fe=Array.isArray(q.route)?q.route:A;le.action==="up"?await eh(e,{cell:be,boot:!!le.boot,...le.engine?{engine:le.engine}:{},...le.model!==void 0?{model:le.model}:{},...le.permissionPolicy?{permissionPolicy:le.permissionPolicy}:{}},Fe):await il(e,{cell:be,boot:!!le.boot},Fe)},Dt=(le,be)=>{D(""),T({mode:"new",route:Array.isArray(be)?be:A,form:{tmuxSession:le.name,id:"",engine:"",cwd:"",boot:!1},err:""})},st=()=>ue(async()=>{const le=Q.form;if(!le.engine){T({...Q,err:v("import-engine-required")});return}await Yw(e,{tmuxSession:le.tmuxSession,id:le.id||void 0,engine:le.engine,cwd:le.cwd||void 0,boot:!!le.boot},le.route||A),T(null),M(v("fleet-saved"))}),gt=({engineRows:le=[],cellRows:be=[]})=>ue(async()=>{const Fe=le.filter(re=>re.selected),$e=Fe.map(re=>Ny(re.engine));if($e.some(re=>!re))throw new Error(v("fleet-backup-invalid-engine"));const pe=Fe.filter(re=>re.exists).map(re=>re.engine.id);if(pe.length&&!window.confirm(v("fleet-backup-confirm-engine-overwrite").replace("{engines}",pe.join(", "))))return;const Be=[...new Set([...a.engines.map(re=>re.id),...$e.map(re=>re.id)])],Te=[];for(const re of be.filter(me=>me.selected)){const me=rE(re.cell,re.engine,Be);if(!me)throw new Error(`${re.cell.id}: ${v("fleet-backup-engine-missing")}`);Te.push(me)}const Y=be.filter(re=>re.selected&&re.exists).map(re=>re.cell.id);if(Y.length&&!window.confirm(v("fleet-backup-confirm-overwrite").replace("{cells}",Y.join(", "))))return;const ne=$e.length?await Gw(e,$e,pe.length>0,A):{needsRestart:[]},z=Te.length?await Xw(e,Te,A):{needsRestart:[]},H=[...new Set([...ne.needsRestart||[],...z.needsRestart||[]])];P(!1),M(`${v("fleet-backup-restored").replace("{n}",String(Te.length))}${$e.length?` · ${$e.length} ${v("fleet-engines").toLowerCase()}`:""}${H.length?` · ${v("fleet-backup-needs-restart").replace("{cells}",H.join(", "))}`:""}`)}),ct=u.jsxs("label",{className:"nc-field",children:[v("location"),u.jsxs("select",{value:te,onChange:le=>{U(le.target.value),_(null),w(null),D(""),M(""),ee(!1),y(!1),m({available:!1,capabilities:[]}),c({engines:[],cells:[],managedCatalog:[]}),$([]),F(null)},children:[u.jsx("option",{value:"",children:v("local")}),i.map(le=>u.jsxs("option",{value:le.route.join("/"),disabled:le.status&&le.status!=="up",children:[le.label,le.status&&le.status!=="up"?` · ${v("node-offline")}`:""]},le.route.join("/")))]})]}),at=u.jsxs("div",{className:"nc-fleet-view-tabs",role:"tablist","aria-label":v("tab-fleet"),children:[u.jsx("button",{type:"button",role:"tab","aria-selected":ae==="manage",className:`nc-set-tabbtn${ae==="manage"?" on":""}`,onClick:()=>W("manage"),children:v("fleet-manage-location")}),u.jsx("button",{type:"button",role:"tab","aria-selected":ae==="overview",className:`nc-set-tabbtn${ae==="overview"?" on":""}`,onClick:()=>W("overview"),children:v("fleet-network-overview")})]});return p?G?u.jsxs("div",{className:"nc-set-tab nc-fleet-editor",children:[at,ae==="overview"?u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-overview-help")}),u.jsx(Qu,{token:e,targets:i,readonly:t,onPower:rt,onImport:Dt})]}):u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-manage-help")}),ct,u.jsxs("div",{className:"nc-set-row nc-fleet-backup-actions",children:[u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),P(!0)},children:v("fleet-backup")}),u.jsx("small",{children:v("fleet-backup-help")})]}),u.jsxs("div",{className:"nc-fleet-section-head",children:[u.jsx("b",{children:v("fleet-cells")}),u.jsx("span",{className:"nc-fleet-head-actions",children:u.jsxs("button",{className:"nc-btn primary",disabled:Re||x||!a.engines.length,onClick:()=>{var le;D(""),w({mode:"new",form:Cv((le=a.engines[0])==null?void 0:le.id)})},children:["+ ",v("add")]})})]}),a.cells.map(le=>{const be=ge.has(le.id),Fe=h.capabilities||[];return u.jsxs("div",{className:"nc-fleet-item",children:[u.jsxs("span",{children:[u.jsx("b",{children:le.id}),u.jsxs("small",{children:[le.engine," · ",le.cwd,be?` · ${v("service-active")}`:` · ${v("cell-off")}`]})]}),u.jsxs("span",{children:[be&&Fe.includes("down")&&u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>ue(()=>il(e,{cell:le.id},A)),children:v("stop")}),!be&&Fe.includes("up")&&u.jsx("button",{className:"nc-btn primary",disabled:Re||x,onClick:()=>rt({cell:le.id,id:le.id,engine:le.engine,model:le.model,models:le.models,permissionPolicies:le.permissionPolicies,active:!1,boot:le.boot}),children:v("start")}),be&&Fe.includes("restart")&&u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>ue(()=>co(e,le.id,A)),children:v("restart")}),u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),w({mode:"edit",original:le,form:{...le}})},children:v("edit")}),u.jsx("button",{className:"nc-btn danger",disabled:Re||x,onClick:()=>ue(async()=>{window.confirm(v("fleet-remove-cell").replace("{id}",le.id))&&await Iv(e,le.id,!0,A)}),children:"×"})]})]},le.id)}),u.jsxs("div",{className:"nc-fleet-section-head",children:[u.jsx("b",{children:v("fleet-engines")}),u.jsxs("button",{className:"nc-btn primary",disabled:Re||x,onClick:()=>{D(""),_({mode:"new",form:sE()})},children:["+ ",v("add")]})]}),a.engines.map(le=>{var Fe,$e;const be=tt(le);return u.jsxs("div",{className:"nc-fleet-item",children:[u.jsxs("span",{children:[u.jsx("b",{children:le.label}),u.jsx("small",{children:le.managed?`${le.id} · ${le.managed.client} / ${le.managed.provider} · ${(Fe=le.managedInfo)!=null&&Fe.configured?v("fleet-ready"):(($e=le.managedInfo)==null?void 0:$e.reason)||v("fleet-not-ready")}`:`${le.id} · ${le.command}`}),be&&u.jsxs("small",{children:[be.envKey," · ",v(`fleet-credential-source-${be.source||"missing"}`)]})]}),u.jsxs("span",{children:[be&&u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),F({envKey:be.envKey,value:""})},children:be.configured?v("fleet-credential-change"):v("fleet-credential-set")}),(be==null?void 0:be.source)==="local"&&u.jsx("button",{className:"nc-btn danger",disabled:Re||x,onClick:()=>Ee(be),children:v("fleet-credential-forget")}),u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),_({mode:"edit",original:le,form:aE(le)})},children:v("edit")}),u.jsx("button",{className:"nc-btn danger",disabled:Re||x,onClick:()=>ue(async()=>{window.confirm(v("fleet-remove-engine").replace("{id}",le.id))&&await Kw(e,le.id,A)}),children:"×"})]})]},le.id)})]}),S&&u.jsx(ns,{onClose:()=>_(null),label:v("fleet-new-engine"),error:b,children:u.jsx(uE,{state:S,setState:_,busy:x,onSave:de,catalog:a.managedCatalog||[]})}),C&&u.jsx(ns,{onClose:()=>w(null),label:v("fleet-new-cell"),error:b,children:u.jsx(dE,{token:e,route:A,targets:i,location:te,setLocation:U,state:C,setState:w,engines:a.engines,busy:x,onSave:Ce})}),L&&u.jsx("div",{className:"nc-set-note",children:L}),b&&u.jsx("div",{className:"nc-err",children:b}),N&&u.jsx(ns,{onClose:()=>P(!1),label:v("fleet-backup"),error:b,children:u.jsx(cE,{cells:a.cells,engines:a.engines,busy:x,canRestore:Z,onRestore:gt,onClose:()=>P(!1)})}),K&&u.jsx(ns,{onClose:()=>F(null),label:v("fleet-credential-title"),error:b,children:u.jsxs("div",{className:"nc-fleet-form nc-credential-form",children:[u.jsx("b",{children:v("fleet-credential-title")}),u.jsx("small",{children:v("fleet-credential-help")}),u.jsxs("label",{className:"nc-field",children:[K.envKey,u.jsx("input",{type:"password",autoComplete:"off",spellCheck:"false",autoCapitalize:"none",value:K.value,onChange:le=>F({...K,value:le.target.value})})]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>F(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:x||!K.value,onClick:Je,children:v("save")})]})]})}),Q&&u.jsx(ns,{onClose:()=>T(null),label:v("import-as-cell"),error:b,children:u.jsx(xv,{token:e,route:Q.route||A,state:Q,setState:T,busy:x,onSave:st})}),q&&u.jsx(nl,{cell:q,token:e,route:Array.isArray(q.route)?q.route:A,onConfirm:async le=>{try{await wt(le)}finally{await se()}},onClose:()=>J(null)})]}):u.jsxs("div",{className:"nc-set-tab",children:[at,ae==="overview"?u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-overview-help")}),u.jsx(Qu,{token:e,targets:i,readonly:t,onPower:rt,onImport:Dt})]}):u.jsxs(u.Fragment,{children:[ct,u.jsxs("div",{className:"nc-set-info",children:[v(b?"fleet-editor-load-error":"fleet-editor-unavailable"),!b&&h.reason?` ${h.reason}`:""]})]}),b&&u.jsx("div",{className:"nc-err",children:b}),Q&&u.jsx(ns,{onClose:()=>T(null),label:v("import-as-cell"),error:b,children:u.jsx(xv,{token:e,route:Q.route||A,state:Q,setState:T,busy:x,onSave:st})}),q&&u.jsx(nl,{cell:q,token:e,route:Array.isArray(q.route)?q.route:A,onConfirm:async le=>{try{await wt(le)}finally{await se()}},onClose:()=>J(null)})]}):u.jsxs("div",{className:"nc-set-tab",children:[at,ae==="overview"?u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-overview-help")}),u.jsx(Qu,{token:e,targets:i,readonly:t,onPower:rt,onImport:Dt})]}):u.jsxs(u.Fragment,{children:[ct,u.jsx("div",{className:"nc-set-info",children:v("fleet-editor-loading")})]})]})}function fE({text:e}){const[t,i]=R.useState(!1),s=async()=>{try{await navigator.clipboard.writeText(e),i(!0),setTimeout(()=>i(!1),1500)}catch{}};return u.jsxs("div",{className:"nc-set-copyline",children:[u.jsx("code",{children:e}),u.jsxs("button",{type:"button",className:"nc-btn ghost",onClick:s,children:[u.jsx(et,{name:"copy",size:14})," ",v(t?"copied":"copy")]})]})}function pE({value:e}){const[t,i]=R.useState("");return R.useEffect(()=>{let s=!0;return Px.toDataURL(e,{margin:1,width:220}).then(o=>{s&&i(o)}).catch(()=>{}),()=>{s=!1}},[e]),t?u.jsx("img",{src:t,width:"220",height:"220",alt:"NexusCrew pairing QR"}):null}function mE({token:e,nodes:t,settings:i,readonly:s,refresh:o}){const[a,c]=R.useState(null),[h,m]=R.useState(null),[p,y]=R.useState({}),[S,_]=R.useState(null),[C,w]=R.useState({ssh:"",sshPort:"",name:""}),[x,k]=R.useState(""),[b,D]=R.useState(""),[L,M]=R.useState(""),[te,U]=R.useState(!1),[O,ee]=R.useState(null),[q,J]=R.useState(null),Q=Date.now(),T=i&&i.deviceName||"",N=(t||[]).filter(G=>G&&G.direction==="outbound"&&G.name&&G.ssh),P=N.find(G=>G.name===x)||N[0]||null,I=N.find(G=>G.name===b)||N[0]||null,$=I?jm(I.tunnel,Q):null,K=I!=null&&I.shared?$!=null&&$.up?"share-local-active":"share-local-pending":$!=null&&$.up?"share-local-private":"share-local-private-down",F=[{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")}],ae=async(G,Z)=>{c(null),m(`${G}:${Z}`);try{const ue=await rh(e,G,Z);Z==="test"&&y(de=>({...de,[G]:ue})),await o()}catch(ue){c(`${G}: ${String(ue.message||ue)}`)}m(null)},W=async G=>{c(null),m(`${G}:remove`);try{await n0(e,G),J(null),await o()}catch(Z){c(`${G}: ${String(Z.message||Z)}`)}m(null)},j=G=>ee({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||[]]}),A=async()=>{if(!O||!vo(O.label)){c(v("err-label"));return}const G=O.direction==="inbound"?{label:O.label,visibility:O.visibility,selected:O.visibility==="selected"?O.selected:[]}:{label:O.label,ssh:O.ssh,autostart:O.autostart,...O.sshPort?{sshPort:Number(O.sshPort)}:{}};c(null),m(`${O.name}:edit`);try{await i0(e,O.name,G),ee(null),await o()}catch(Z){c(`${O.name}: ${String(Z.message||Z)}`)}m(null)},se=async G=>{if(I){c(null),m(`${I.name}:share`);try{await r0(e,I.name,G),await o()}catch(Z){const ue=Z!=null&&Z.data&&typeof Z.data.hint=="string"?Z.data.hint:"";c(`${I.name}: ${String(Z.message||Z)}${ue?` — ${ue}`:""}`)}m(null)}},ge=async()=>{if(c(null),P){const ue=Dm({name:P.name,ssh:P.ssh,sshPort:P.sshPort||""});if(!ue.ok){c(v(ue.error));return}m("invite");try{_(await gm(e,{ssh:ue.value.ssh,...ue.value.sshPort?{sshPort:ue.value.sshPort}:{}},[P.name]))}catch(de){c(String(de.message||de))}m(null);return}const G=Jn(C.name||L||T||"NexusCrew"),Z=Dm({name:G,ssh:C.ssh,sshPort:C.sshPort});if(!Z.ok){c(v(Z.error));return}m("invite");try{_(await gm(e,{...L||T?{label:L||T}:{},name:G,ssh:Z.value.ssh,...Z.value.sshPort?{sshPort:Z.value.sshPort}:{}}))}catch(ue){c(String(ue.message||ue))}m(null)};return u.jsxs("div",{className:"nc-set-tab",children:[u.jsx("div",{className:"nc-sheet-label",children:v("peer-inventory-title")}),(t||[]).length===0&&u.jsx("div",{className:"nc-empty",children:v("no-nodes")}),F.map(G=>G.rows.length>0&&u.jsxs("div",{className:"nc-peer-group",children:[u.jsxs("div",{className:"nc-peer-group-title",children:[v(G.key)," ",u.jsx("span",{children:G.rows.length})]}),G.rows.map(Z=>{var tt,Je;const ue=Z.kind==="transitive",de=ue?{up:Z.stale!==!0,label:Z.stale===!0?"peer-routed-stale":"peer-routed"}:jm(Z.tunnel,Q),Ce=p[Z.name],Re=Z.actions||{};return u.jsxs("div",{className:`nc-set-node${ue?" routed":""}`,children:[u.jsxs("div",{className:"nc-set-node-head",children:[u.jsx("span",{className:`nc-dot ${de.up?"on":""}`}),u.jsx("b",{children:Z.label||Z.name}),u.jsxs("small",{children:[Z.name,ue?` · ${Z.route.join(" → ")}`:Z.direction==="outbound"?` · SSH ${Z.ssh||""}`:` · ${v("node-connected-client")}`,(tt=Z.tunnel)!=null&&tt.transport?` · ${Z.tunnel.transport} ${v("transport-used")}`:""]}),u.jsxs("span",{className:`nc-set-tunnel${de.up?" up":""}`,children:[v(de.label),de.since?` · ${de.since}`:""]})]}),!ue&&Z.direction==="inbound"&&u.jsx("div",{className:"nc-set-info",children:v(Z.shared?"peer-shared":"peer-private")}),Re.visibility&&Z.shared&&u.jsxs(u.Fragment,{children:[u.jsxs("select",{value:Z.visibility||"network",disabled:s||!!h,onChange:async Ee=>{m(`${Z.name}:visibility`);try{await vm(e,Z.name,Ee.target.value),await o()}catch(rt){c(String(rt.message||rt))}m(null)},children:[u.jsx("option",{value:"network",children:v("visibility-network")}),u.jsx("option",{value:"relay-only",children:v("visibility-relay")}),u.jsx("option",{value:"selected",children:v("visibility-selected")})]}),Z.visibility==="selected"&&u.jsx("div",{className:"nc-set-row",children:(t||[]).filter(Ee=>Ee.name!==Z.name&&Ee.nodeId).map(Ee=>{const rt=(Z.selected||[]).includes(Ee.nodeId);return u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:rt,disabled:s||!!h,onChange:async wt=>{const Dt=wt.target.checked?[...Z.selected||[],Ee.nodeId]:(Z.selected||[]).filter(st=>st!==Ee.nodeId);m(`${Z.name}:visibility`);try{await vm(e,Z.name,"selected",Dt),await o()}catch(st){c(String(st.message||st))}m(null)}})," ",Ee.label||Ee.name]},Ee.nodeId)})})]}),u.jsxs("div",{className:"nc-set-node-actions",children:[Re.edit&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>j(Z),children:v("edit")}),Re.test&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>ae(Z.name,"test"),children:v("node-test")}),Re.disconnect&&de.up&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>ae(Z.name,"down"),children:v("tunnel-stop")}),Re.connect&&!de.up&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>ae(Z.name,"up"),children:v("tunnel-start")}),Re.restart&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>ae(Z.name,"restart"),children:v("tunnel-restart")}),Re.remove&&u.jsxs("button",{type:"button",className:"nc-btn danger",disabled:s||!!h,title:v(s?"settings-readonly":"delete"),onClick:()=>J(Z.name),children:[u.jsx(et,{name:"trash",size:14})," ",v("delete")]})]}),(O==null?void 0:O.name)===Z.name&&u.jsxs("div",{className:"nc-set-form nc-node-editor",children:[u.jsxs("label",{className:"nc-field",children:[v("node-display-label"),u.jsx("input",{value:O.label,disabled:!!h,onChange:Ee=>ee({...O,label:Ee.target.value})})]}),O.direction==="outbound"?u.jsxs(u.Fragment,{children:[u.jsxs("label",{className:"nc-field",children:[v("node-ssh-label"),u.jsx("input",{value:O.ssh,disabled:!!h,onChange:Ee=>ee({...O,ssh:Ee.target.value})})]}),u.jsxs("label",{className:"nc-field",children:[v("node-ssh-port-label"),u.jsx("input",{inputMode:"numeric",value:O.sshPort,disabled:!!h,onChange:Ee=>ee({...O,sshPort:Ee.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:O.autostart,disabled:!!h,onChange:Ee=>ee({...O,autostart:Ee.target.checked})})," ",v("boot-persist")]})]}):u.jsxs("label",{className:"nc-field",children:[v("peer-visibility"),u.jsxs("select",{value:O.visibility,disabled:!!h,onChange:Ee=>ee({...O,visibility:Ee.target.value}),children:[u.jsx("option",{value:"network",children:v("visibility-network")}),u.jsx("option",{value:"relay-only",children:v("visibility-relay")}),u.jsx("option",{value:"selected",children:v("visibility-selected")})]})]}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn primary",disabled:!!h,onClick:A,children:v("save")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>ee(null),children:v("cancel")})]})]}),q===Z.name&&u.jsxs("div",{className:"nc-set-confirm",children:[u.jsx("b",{children:v("node-remove-confirm").replace("{name}",Z.label||Z.name)}),u.jsx("small",{children:v("node-remove-warning")}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn danger",disabled:!!h,onClick:()=>W(Z.name),children:v("delete")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>J(null),children:v("cancel")})]})]}),((Je=Z.health)==null?void 0:Je.detail)&&u.jsx("div",{className:`nc-set-test${Z.health.status==="healthy"?" ok":Z.health.status==="passive"?"":" ko"}`,children:Z.health.detail}),Ce&&u.jsxs("div",{className:`nc-set-test${Ce.ok?" ok":" ko"}`,children:[Ce.result,Ce.detail?` — ${Ce.detail}`:""]})]},`${Z.kind||"direct"}:${Z.nodeId||Z.name}`)})]},G.key)),I&&u.jsxs("div",{className:"nc-set-form nc-local-share",children:[u.jsx("div",{className:"nc-sheet-label",children:v("share-local-heading")}),N.length>1&&u.jsxs("label",{className:"nc-field",children:[v("share-local-hub"),u.jsx("select",{value:I.name,disabled:s||!!h,onChange:G=>D(G.target.value),children:N.map(G=>u.jsx("option",{value:G.name,children:G.label||G.name},G.name))})]}),u.jsxs("label",{className:"nc-check nc-node-share",children:[u.jsx("input",{type:"checkbox",checked:I.shared===!0,disabled:s||!!h,onChange:G=>se(G.target.checked)}),u.jsxs("span",{children:[u.jsx("b",{children:v("share-local-through").replace("{device}",T||v("local")).replace("{hub}",I.label||I.name)}),u.jsx("small",{children:v(I.shared?"share-node-on-desc":"share-node-off-desc")})]})]}),u.jsx("div",{className:`nc-set-test${I.shared&&($!=null&&$.up)?" ok":$!=null&&$.up?"":" ko"}`,children:v(K)}),!($!=null&&$.up)&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>se(I.shared===!0),children:v("share-local-reconnect")})]}),u.jsx(Sy,{token:e,deviceDefault:T,localNodeId:i&&i.nodeId||"",localNameDefault:i&&i.localName||"",readonly:s,onSuccess:o}),u.jsxs("div",{className:"nc-set-form",children:[u.jsx("div",{className:"nc-sheet-label",children:v("invite-node")}),u.jsx("small",{className:"nc-set-hint",children:v(P?"invite-network-hint":"invite-v2-hint")}),P?u.jsxs(u.Fragment,{children:[N.length>1&&u.jsxs("label",{className:"nc-field",children:[v("invite-network-label"),u.jsx("select",{value:P.name,disabled:s||!!h,onChange:G=>{k(G.target.value),_(null)},children:N.map(G=>u.jsx("option",{value:G.name,children:G.label||G.name},G.name))})]}),u.jsxs("div",{className:"nc-set-info nc-invite-endpoint",children:[v("invite-network-via"),": ",u.jsx("b",{children:P.label||P.name})," · ",P.ssh,P.sshPort?`:${P.sshPort}`:""]}),u.jsx("small",{className:"nc-set-hint",children:v("invite-network-route")})]}):u.jsxs("label",{className:"nc-field",children:[v("invite-endpoint-label"),u.jsx("input",{placeholder:"user@host",value:C.ssh,disabled:s,onChange:G=>w({...C,ssh:G.target.value})}),u.jsx("small",{className:"nc-set-hint",children:v("invite-endpoint-needed")})]}),!P&&te&&u.jsxs("div",{className:"nc-invite-advanced",children:[u.jsxs("label",{className:"nc-field",children:[v("node-ssh-port-label"),u.jsx("input",{inputMode:"numeric",placeholder:"22",value:C.sshPort,disabled:s,onChange:G=>w({...C,sshPort:G.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),u.jsxs("label",{className:"nc-field",children:[v("device-name-label"),u.jsx("input",{placeholder:T||"NexusCrew",value:L,disabled:s,onChange:G=>M(G.target.value)}),u.jsx("small",{className:"nc-set-hint",children:v("device-name-hint")})]}),u.jsxs("label",{className:"nc-field",children:[v("node-name-label"),u.jsx("input",{placeholder:Jn(L||T||v("node-name-ph")),value:C.name,disabled:s,onChange:G=>w({...C,name:G.target.value?Jn(G.target.value):""})})]})]}),u.jsxs("div",{className:"nc-set-row nc-invite-actions",children:[u.jsx("button",{type:"button",className:"nc-btn primary",disabled:s||!!h||!P&&!C.ssh.trim(),onClick:ge,children:v("create-pairing-link")}),!P&&u.jsxs("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>U(G=>!G),children:[te?"▾":"▸"," ",v("pair-advanced")]})]}),S&&u.jsxs(u.Fragment,{children:[u.jsx(pE,{value:S.pairingUrl}),u.jsx(fE,{text:S.pairingUrl}),u.jsx("div",{className:"nc-set-info",children:v("invite-next-steps")})]})]}),a&&u.jsx("div",{className:"nc-err",children:a})]})}function gE({token:e,readonly:t}){const[i,s]=R.useState("idle"),[o,a]=R.useState(!1),[c,h]=R.useState(null);R.useEffect(()=>{let p=!1;return Hx().then(y=>{p||s(y)}),()=>{p=!0}},[]);const m=async()=>{h(null),a(!0);try{i==="subscribed"?(await Wx(e),s("idle")):(await $x(e),s("subscribed"))}catch(p){const y=String(p.message||p);y==="push-denied"?(s("denied"),h(v("push-denied"))):y==="push-unsupported"?(s("unsupported"),h(v("push-unsupported"))):h(y)}a(!1)};return i==="unsupported"?u.jsx("div",{className:"nc-set-info",children:v("push-unsupported")}):u.jsxs(u.Fragment,{children:[u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||o||i==="denied",title:t?v("settings-readonly"):"",onClick:m,children:v(i==="subscribed"?"push-disable":"push-enable")}),u.jsx("span",{className:"nc-set-info",children:v(i==="subscribed"?"push-on":i==="denied"?"push-denied":"push-off")})]}),c&&u.jsx("div",{className:"nc-err",children:c})]})}function vE({token:e,settings:t,readonly:i,refresh:s}){const[o,a]=R.useState(null),[c,h]=R.useState(null),[m,p]=R.useState(!1),[y,S]=R.useState(!1),[_,C]=R.useState(null),[w,x]=R.useState(!0);R.useEffect(()=>{C(t&&t.update||null),x(!t||t.autoUpdate!==!1)},[t]);const k=async()=>{a(null),h(null),S(!0);try{const O=await e0(e);h(O.note||"ok"),p(!1)}catch(O){a(String(O.message||O))}S(!1)},b=async()=>{a(null),h(null),S(!0);try{const O=await s0(e);h(`${O.note||"ok"}${O.target?` (${O.target})`:""}`)}catch(O){a(String(O.message||O))}S(!1)},D=async O=>{a(null),h(null),S(!0);try{await Hv(e,{autoUpdate:O}),x(O),s&&await s()}catch(ee){a(String(ee.message||ee))}S(!1)},L=async()=>{a(null),h(null),S(!0);try{C(await o0(e))}catch(O){a(String(O.message||O))}S(!1)},M=async()=>{a(null),h(null),S(!0);try{const O=await a0(e);C(O),h(v("npm-update-restarting"))}catch(O){a(String(O.message||O))}S(!1)},te=()=>{window.confirm(v("composer-clear-confirm"))&&(a(null),IC()?(window.dispatchEvent(new Event(Wd)),h(v("composer-clear-done"))):a(v("composer-clear-failed")))},U=t&&t.service;return u.jsxs("div",{className:"nc-set-tab",children:[t&&u.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,u.jsx("br",{}),U&&U.installed?v("service-installed"):v("service-missing"),U&&U.installed?` · ${U.active?v("service-active"):v("service-inactive")}`:"",U?` · boot ${U.boot?"on":"off"}`:""]}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||y,title:i?v("settings-readonly"):"",onClick:()=>{a(null),h(null),p(!0)},children:v("token-rotate")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||y,title:i?v("settings-readonly"):"",onClick:b,children:v("service-regenerate")})]}),u.jsxs("div",{className:"nc-set-form nc-update-settings",children:[u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:w,disabled:i||y||_&&!_.supported,onChange:O=>D(O.target.checked)}),u.jsxs("span",{children:[u.jsx("b",{children:v("npm-auto-update")}),u.jsx("small",{children:v("npm-auto-update-help")})]})]}),_&&u.jsxs("div",{className:"nc-set-info",children:[v("npm-update-current")," v",_.current,_.latest?` · ${v("npm-update-latest")} v${_.latest}`:""," · ",v(`npm-update-${_.supported?_.phase:"unsupported"}`)]}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||y||!_||!_.supported,onClick:L,children:v("npm-update-check")}),_&&_.available&&u.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||y||_.phase==="installing",onClick:M,children:v("npm-update-install")})]}),_&&_.lastError&&u.jsx("div",{className:"nc-err",children:_.lastError})]}),u.jsx(gE,{token:e,readonly:i}),u.jsxs("div",{className:"nc-set-form",children:[u.jsx("div",{className:"nc-sheet-label",children:v("composer-clear-data")}),u.jsx("small",{className:"nc-set-hint",children:v("composer-clear-data-help")}),u.jsx("div",{className:"nc-set-row",children:u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:te,children:v("composer-clear-data")})})]}),m&&u.jsxs("div",{className:"nc-set-confirm",children:[u.jsx("div",{children:v("token-rotate-explain")}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:y,onClick:()=>p(!1),children:v("cancel")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:y,onClick:k,children:v("confirm")})]})]}),c&&u.jsx("div",{className:"nc-set-note",children:c}),o&&u.jsx("div",{className:"nc-err",children:o})]})}function _E({token:e,onClose:t,initialTab:i="nodes",initialLocation:s="",startNewCell:o=!1}){Pi();const[a,c]=R.useState(i),[h,m]=R.useState(null),[p,y]=R.useState([]),[S,_]=R.useState(!1),[C,w]=R.useState(null),x=oh(e),k=R.useCallback(async()=>{try{const b=await zv(e);m(b),w(null)}catch(b){w(String(b.message||b))}try{const b=await Jw(e);y(b.peers||[])}catch(b){w(String(b.message||b))}},[e]);return R.useEffect(()=>{k();const b=setInterval(k,5e3);return()=>clearInterval(b)},[k]),R.useEffect(()=>{let b=!1;return qt("/api/config",e).then(D=>D.json()).then(D=>{b||_(!!D.readonlyDefault)}).catch(()=>{}),()=>{b=!0}},[e]),u.jsx("div",{className:"nc-set-overlay",onClick:t,children:u.jsxs("div",{className:"nc-set-panel",onClick:b=>b.stopPropagation(),children:[u.jsxs("div",{className:"nc-set-head",children:[u.jsx(et,{name:"gear",size:18}),u.jsx("b",{children:v("settings")}),u.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:v("close"),children:u.jsx(et,{name:"x",size:18})})]}),S&&u.jsx("div",{className:"nc-set-readonly",children:v("settings-readonly")}),C&&u.jsx("div",{className:"nc-err",children:C}),u.jsx("div",{className:"nc-set-tabs",children:["nodes","fleet","system"].map(b=>u.jsx("button",{type:"button",className:`nc-set-tabbtn${a===b?" on":""}`,onClick:()=>c(b),children:v(`tab-${b}`)},b))}),u.jsxs("div",{className:"nc-set-body",children:[a==="nodes"&&u.jsx(mE,{token:e,nodes:p,settings:h,readonly:S,refresh:k}),a==="fleet"&&u.jsx(hE,{token:e,readonly:S,startNewCell:o,initialLocation:s,targets:x.map(b=>({route:b.route,label:b.label||b.name,status:b.status}))}),a==="system"&&u.jsx(vE,{token:e,settings:h,readonly:S,refresh:k})]})]})})}function yE({token:e,initialPair:t,deviceDefault:i="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:c}){Pi();const[h,m]=R.useState(t?"pair":"welcome"),[p,y]=R.useState(!1),[S,_]=R.useState(null),C=async()=>{y(!0),_(null);try{await Hv(e,{wizardDone:!0}),a&&a(),c()}catch(w){_(String(w.message||w)),y(!1)}};return u.jsx("div",{className:"nc-wiz-overlay",children:u.jsxs("div",{className:"nc-wiz",children:[u.jsxs("div",{className:"nc-wiz-head",children:[u.jsx("b",{children:v("wizard-title")}),u.jsx("small",{children:v("hydra-simple")})]}),h==="welcome"&&u.jsxs("div",{className:"nc-wiz-body",children:[u.jsx("div",{className:"nc-wiz-done",children:v("local-ready")}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",disabled:p,onClick:C,children:v("local-only")}),u.jsx("button",{className:"nc-btn primary",disabled:p,onClick:()=>m("pair"),children:v("add-node")})]})]}),h==="pair"&&u.jsxs("div",{className:"nc-wiz-body",children:[u.jsx(Sy,{token:e,initial:t||"",autoStart:!!t,deviceDefault:i,localNodeId:s,localNameDefault:o,onBusyChange:y,onSuccess:async()=>{a&&a(),m("done")}}),u.jsx("div",{className:"nc-sheet-actions",children:u.jsx("button",{className:"nc-btn ghost",disabled:p,onClick:()=>{a&&a(),m("welcome")},children:v("back")})})]}),h==="done"&&u.jsxs("div",{className:"nc-wiz-body",children:[u.jsx("div",{className:"nc-wiz-done",children:v("node-connected")}),u.jsx("div",{className:"nc-sheet-actions",children:u.jsx("button",{className:"nc-btn primary",disabled:p,onClick:C,children:v("finish")})})]}),S&&u.jsx("div",{className:"nc-err",children:S})]})})}function SE(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 wE=6e3,bE=12e3;function kE({n:e,onClose:t}){return u.jsxs("div",{className:`nc-ntf-toast${e.urgency==="high"?" high":""}`,role:"status","aria-live":"polite",children:[u.jsxs("div",{className:"nc-ntf-toast-txt",children:[u.jsx("b",{children:e.title}),e.body&&u.jsx("small",{children:e.body}),e.session&&u.jsx("span",{className:"nc-ntf-from",children:e.session})]}),u.jsx("button",{type:"button",className:"nc-ntf-x",onClick:t,title:v("close"),children:u.jsx(et,{name:"x",size:14})})]})}function CE({ask:e,token:t,onAnswered:i}){const[s,o]=R.useState(""),[a,c]=R.useState(!1),[h,m]=R.useState(null),p=async y=>{const S=String(y||"").trim();if(!(!S||a)){m(null),c(!0);try{await c0(t,e.id,S),i(e.id)}catch(_){m(String(_.message||_))}c(!1)}};return u.jsxs("div",{className:"nc-ask-card",children:[u.jsxs("div",{className:"nc-ask-head",children:[u.jsx("span",{className:"nc-ntf-from",children:e.session}),u.jsxs("code",{className:"nc-ask-id",children:["#",e.id]})]}),u.jsx("div",{className:"nc-ask-q",children:e.question}),Array.isArray(e.options)&&e.options.length>0&&u.jsx("div",{className:"nc-ask-opts",children:e.options.map(y=>u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:a,onClick:()=>p(y),children:y},y))}),u.jsxs("div",{className:"nc-ask-reply",children:[u.jsx("textarea",{rows:2,placeholder:v("ask-reply-ph"),value:s,disabled:a,onChange:y=>o(y.target.value)}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:a||!s.trim(),onClick:()=>p(s),children:v("send")})]}),h&&u.jsx("div",{className:"nc-err",children:h})]})}function xE({token:e}){Pi();const[t,i]=R.useState([]),[s,o]=R.useState([]),[a,c]=R.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),h=R.useRef(0),m=R.useCallback(S=>{i(_=>_.filter(C=>C.key!==S))},[]),p=R.useCallback(S=>{const _=`t${h.current+=1}`;i(C=>[...C.slice(-3),{...S,key:_}]),setTimeout(()=>m(_),S.urgency==="high"?bE:wE)},[m]);R.useEffect(()=>{if(!e)return;let S=!1;l0(e).then(C=>{S||o(C.asks||[])}).catch(()=>{});const _=SE(e,C=>{C.type==="notify"?p(C):C.type==="ask"&&C.ask&&C.ask.id?o(w=>w.some(x=>x.id===C.ask.id)?w:[...w,C.ask]):C.type==="ask-answered"&&C.id&&o(w=>w.filter(x=>x.id!==C.id))});return()=>{S=!0,_()}},[e,p]);const y=S=>o(_=>_.filter(C=>C.id!==S));return u.jsxs(u.Fragment,{children:[t.length>0&&u.jsx("div",{className:"nc-ntf-toasts",children:t.map(S=>u.jsx(kE,{n:S,onClose:()=>m(S.key)},S.key))}),s.length>0&&!a&&u.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>c(!0),title:v("asks-title"),children:["? ",u.jsx("span",{className:"nc-ask-count",children:s.length})]}),s.length>0&&a&&u.jsxs("div",{className:"nc-ask-panel",children:[u.jsxs("div",{className:"nc-ask-panel-head",children:[u.jsx("b",{children:v("asks-title")}),u.jsx("span",{className:"nc-ask-count",children:s.length}),u.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>c(!1),title:v("close"),children:u.jsx(et,{name:"x",size:16})})]}),u.jsx("div",{className:"nc-ask-panel-body",children:s.map(S=>u.jsx(CE,{ask:S,token:e,onAnswered:y},S.id))})]})]})}const Ju=e=>ln(br(e)).length===0,Zu=e=>Array.isArray(e)?e.join("/"):"";function EE(e){const t=new Set,i=[];for(const s of Array.isArray(e)?e:[])!s||!$i.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 lo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function mr(e,t,i,s=!1,o=!0){const a=t.instanceId;return{...e,id:as(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:Kd(e.layout,a,i)}}function NE(e,t,i,s,o=[]){const[a,c]=R.useState([]),[h,m]=R.useState(!1),[p,y]=R.useState("idle"),[S,_]=R.useState(""),[C,w]=R.useState(""),x=R.useRef([]),k=R.useRef([]),b=R.useRef(""),D=R.useRef(t),L=R.useRef(i),M=R.useRef(!1),te=R.useRef(!0),U=R.useRef(""),O=R.useMemo(()=>EE(o),[o]),ee=O.map(W=>`${W.instanceId}:${Zu(W.route)}:${W.status}:${W.label}`).join("|");k.current=O,L.current=i,D.current=t,x.current=a;const q=R.useCallback(W=>qd(W.layout,b.current,k.current),[]),J=R.useCallback((W,j=!0,A=D.current)=>{const se=Hg(W,qa()),ge=x.current.some(Z=>Z.id===A);x.current=se,c(se);const G=se.find(Z=>Z.id===A);if(j&&G){te.current=!0;const Z=q(G);s(Z),G.local&&rx(G.name,Z)}else ge&&!G&&(te.current=!0,s(os()),_("deck non più condiviso dal nodo owner"));ex(se.filter(Z=>Z.local).map(Z=>Z.name))},[s,q]),Q=R.useCallback(async W=>{let j=await Ba(e);const A=j.decks.find(se=>se.name==="main");if(j.decks.length===1&&A&&A.revision===0&&Ju(A.layout)){const se=ZC(),ge=br(Vd("main")||os());if(!Ju(ge)){const G=Kd(ge,W,[]);j.decks[0]=await Oa(e,"main",G,0,[])}for(const G of se.filter(Z=>Z!=="main"))try{let Z=await _m(e,G,[]);const ue=br(Vd(G)||os());Ju(ue)||(Z=await Oa(e,G,Kd(ue,W,[]),Z.revision,[]))}catch{}j=await Ba(e)}return j},[e]),T=R.useCallback(async({migrate:W=!1}={})=>{const j=await nh(e,[]),A=$i.test(String(j.instanceId||""))?j.instanceId:"";if(!A)throw new Error("instanceId locale non disponibile");b.current=A,w(A);const[se,ge]=await Promise.all([W?Q(A):Ba(e),mm(e,[]).catch(()=>({nodes:[]}))]),G={instanceId:A,route:[],label:"Local"},Z=se.decks.map(de=>mr(de,G,ge.nodes,!0,!0)),ue=x.current;return await Promise.all(k.current.map(async de=>{if(de.status!=="up"){Z.push(...ue.filter(Ce=>!Ce.local&&Ce.ownerId===de.instanceId).map(Ce=>({...Ce,ownerRoute:[...de.route],ownerLabel:de.label,available:!1})));return}try{const[Ce,Re]=await Promise.all([Ba(e,de.route),mm(e,de.route).catch(()=>({nodes:[]}))]);Z.push(...Ce.decks.map(tt=>mr(tt,de,Re.nodes,!1,!0)))}catch{Z.push(...ue.filter(Re=>!Re.local&&Re.ownerId===de.instanceId).map(Re=>({...Re,ownerRoute:[...de.route],ownerLabel:de.label,available:!1})))}})),Z.sort((de,Ce)=>de.local===Ce.local?de.ownerLabel.localeCompare(Ce.ownerLabel)||(de.name==="main"?-1:Ce.name==="main"?1:de.name.localeCompare(Ce.name)):de.local?-1:1),Z},[e,Q,ee]);R.useEffect(()=>{if(!e)return;let W=!1;const j=U.current!==e;return U.current=e,T({migrate:j}).then(A=>{if(W)return;const se=x.current.find(Z=>Z.id===D.current),ge=A.find(Z=>Z.id===D.current),G=ge&&(!se||ge.revision>se.revision||se.available!==ge.available||Zu(se.ownerRoute)!==Zu(ge.ownerRoute));J(A,j||!!G&&!M.current),m(!0),ge&&_("")}).catch(A=>{W||(m(!0),_(String(A.message||A)))}),()=>{W=!0}},[J,T,ee,e]);const N=R.useCallback(async(W=D.current)=>{if(!h||!M.current)return!0;const j=x.current.find(A=>A.id===W);if(!j)return _(`deck inesistente: ${W}`),!1;if(j.available===!1)return _(`nodo owner offline: ${j.ownerLabel}`),!1;y("saving");try{const A=zg(br(L.current),j.ownerId,j.ownerTopology),se=await Oa(e,j.name,A,j.revision,j.ownerRoute),ge=mr(se,lo(j),j.ownerTopology,j.local,!0);return J(x.current.map(G=>G.id===W?ge:G),!1),M.current=!1,y("saved"),_(""),setTimeout(()=>y("idle"),1500),!0}catch(A){if(y("error"),_(String(A.message||A)),A.status===409&&A.data&&A.data.current){const se=x.current.find(G=>G.id===W),ge=mr(A.data.current,lo(se),se.ownerTopology,se.local,!0);J(x.current.map(G=>G.id===W?ge:G),!1)}return!1}},[h,e,J]);return R.useEffect(()=>{if(!h)return;if(te.current){te.current=!1;return}M.current=!0,y("saving");const W=setTimeout(N,650);return()=>clearTimeout(W)},[i,h,N]),R.useEffect(()=>{if(!h)return;const W=setInterval(async()=>{try{const j=await T(),A=x.current.find(G=>G.id===D.current),se=j.find(G=>G.id===D.current),ge=se&&(!A||se.revision>A.revision||A.available!==se.available);J(j,ge&&!M.current)}catch{}},5e3);return()=>clearInterval(W)},[h,T,J]),{decks:a,records:a,localNodeId:C,ready:h,saveState:p,error:S,setError:_,saveNow:N,select:async W=>{if(M.current&&!await N(D.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const j=x.current.find(A=>A.id===W);if(!j)throw new Error(`deck inesistente: ${W}`);return M.current=!1,te.current=!0,q(j)},add:async(W,j=null)=>{const A=j===wn?x.current.find(G=>G.local):j?x.current.find(G=>G.ownerId===j):null;if(!A||A.available===!1)throw new Error("nodo owner non disponibile");const se=await _m(e,W,A.ownerRoute),ge=mr(se,lo(A),A.ownerTopology,A.local,!0);return J([...x.current,ge],!1),ge},rename:async(W,j)=>{const A=x.current.find(ue=>ue.id===W);if(!A)throw new Error("deck inesistente");if(W===D.current&&M.current&&!await N(W))throw new Error(`salvataggio di "${A.name}" fallito: rinomina annullata`);const se=x.current.find(ue=>ue.id===W);if(!se)throw new Error("deck inesistente");const ge=await u0(e,se.name,j,se.revision,se.ownerRoute),G=mr(ge,lo(se),se.ownerTopology,se.local,!0),Z=se.local?wn:se.ownerId;return Nu(ix(qa(),Z,W,G.id)),J(x.current.map(ue=>ue.id===W?G:ue),!1),G},remove:async W=>{const j=x.current.find(se=>se.id===W);if(!j)throw new Error("deck inesistente");W===D.current&&(M.current=!1),await d0(e,j.name,j.revision,j.ownerRoute);const A=j.local?wn:j.ownerId;Nu(nx(qa(),A,W)),J(x.current.filter(se=>se.id!==W),!1)},reorder:(W,j)=>{const A=x.current.find(de=>de.id===W),se=x.current.find(de=>de.id===j);if(!A||!se)return!1;const ge=A.local?wn:A.ownerId,G=se.local?wn:se.ownerId;if(ge!==G)return!1;const Z=x.current.filter(de=>(de.local?wn:de.ownerId)===ge).map(de=>de.id),ue=Nu(tx(qa(),ge,W,j,Z));return J(Hg(x.current,ue),!1),!0},addTileTo:async(W,j)=>{const A=x.current.find(de=>de.id===W);if(!A)throw new Error("deck inesistente");if(A.available===!1)throw new Error(`nodo owner offline: ${A.ownerLabel}`);const se=uy(j,b.current,k.current);if(!se)throw new Error("riferimento sessione non valido");const ge=q(A),G=zg(r_(ge,se),A.ownerId,A.ownerTopology),Z=await Oa(e,A.name,G,A.revision,A.ownerRoute),ue=mr(Z,lo(A),A.ownerTopology,A.local,!0);return J(x.current.map(de=>de.id===W?ue:de),!1),ue},localMainId:as(null,"main"),parseDeckId:wh}}const RE="/sw.js",Yd="nc-sw-update";let Xd=null,Gd=!1,Qd=null,Py=Object.freeze({needed:!1,kind:null,version:""}),Dy=!1;function jy(){typeof window<"u"&&window.dispatchEvent(new Event(Yd))}function My(){const e=Qd||(Gd?{kind:"reload",version:""}:null);Py=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function Ev(){return Py}function PE(e){return typeof window>"u"?()=>{}:(window.addEventListener(Yd,e),()=>window.removeEventListener(Yd,e))}function Eh(e){Gd!==e&&(Gd=e,My(),jy())}function DE(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(Qd);Qd=s,My(),o||jy()}function jE(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&Eh(!0)})}function ME(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{Dy&&location.reload()}),navigator.serviceWorker.register(RE).then(e=>{Xd=e,e.waiting&&Eh(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&jE(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function LE(){Dy=!0,Eh(!1);const e=Xd&&Xd.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function TE({hash:e="",origin:t="",pathname:i="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${i||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const c=new URLSearchParams(o),h=c.get("token")||"",m=c.get("pair")||"",p=m?`${t||""}${i||""}#pair=${m}`:"";return{token:h,pair:p,nextUrl:a}}const Ly=9,Ty=24,Ay="nc_side_w",Nv="nc_side_min",AE=240,Rv="(min-width:1024px) and (pointer:fine)";function IE(){const e=Number(localStorage.getItem(Ay));return e>=180&&e<=480?e:AE}function BE(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=Ly&&e<=Ty?e:13}function OE(){const e={token:"",pair:""};try{const{token:t,pair:i,nextUrl:s}=TE({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 zE(e){try{return br(Vd(e))}catch{return os()}}function Pv(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 FE(){const[e,t]=R.useState(()=>window.matchMedia(Rv).matches);return R.useEffect(()=>{const i=window.matchMedia(Rv),s=o=>t(o.matches);return i.addEventListener("change",s),()=>i.removeEventListener("change",s)},[]),e}function Dv({session:e,node:t,ownerId:i,token:s,readonly:o=!1,onBack:a}){Pi();const[c,h]=R.useState(!1),[m,p]=R.useState(()=>window.matchMedia("(pointer: coarse)").matches),[y,S]=R.useState(null),[_,C]=R.useState(BE),[w,x]=R.useState(""),k=J=>C(Q=>{const T=Math.max(Ly,Math.min(Ty,Q+J));return localStorage.setItem("nc_fontsize",String(T)),T}),b=R.useRef(()=>{}),D=R.useRef(()=>!1),L=R.useRef(()=>{}),M=R.useRef(!1),[te,U]=R.useState(!1),[O,ee]=R.useState(!1),q=()=>{M.current=!M.current,U(M.current)};return R.useEffect(()=>{let J=!0;const Q=t?t.split("/"):[],T=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function N(){let I=null,$=null;try{const ae=await(await qt(`${T}/sessions`,s)).json();Array.isArray(ae.sessions)&&(I=ae.sessions.find(W=>W.name===e))}catch{}try{const F=await Er(s,Q);F.available&&Array.isArray(F.cells)&&($=F.cells.find(ae=>ae.tmuxSession===e))}catch{}if(!J)return;let K="";$?K=`${$.engine}${$.key?`·${$.key}`:""}`:I&&(K=I.attached?`attached · ${Pv(I.activity)}`:I.activity?Pv(I.activity):""),x(K)}N();const P=setInterval(N,4e3);return()=>{J=!1,clearInterval(P)}},[e,t,s]),u.jsxs("div",{className:"nc-app",children:[u.jsxs("header",{className:"nc-bar nc-bar-single",children:[u.jsxs("button",{onClick:a,title:v("sessions"),children:[u.jsx(et,{name:"chevronLeft",size:18}),u.jsx("span",{className:"nc-bar-label",children:v("sessions")})]}),u.jsxs("span",{className:"nc-bar-center",children:[u.jsx("b",{children:t?`${t}:${e}`:e}),w&&u.jsx("small",{className:"nc-bar-sub",children:w})]}),u.jsxs("span",{className:"nc-bar-right",children:[u.jsx("button",{onClick:()=>k(-1),title:v("zoom-out"),children:u.jsx(et,{name:"zoomOut",size:18})}),u.jsx("button",{onClick:()=>k(1),title:v("zoom-in"),children:u.jsx(et,{name:"zoomIn",size:18})}),u.jsx("button",{onClick:()=>p(J=>!J),title:v("composer"),children:u.jsx(et,{name:"keyboard",size:20})}),u.jsx("button",{onClick:()=>h(J=>!J),title:v("files"),children:u.jsx(et,{name:"folder",size:20})})]})]}),u.jsx("div",{className:"nc-termwrap",children:u.jsx(J_,{session:e,node:t,token:s,readonly:o,takeSize:!0,sendRef:b,composerRef:D,actionRef:L,ctrlRef:M,setCtrlArmed:U,onFiles:S,fontSize:_,selectionMode:O,onSelectionModeChange:ee})}),u.jsx(xC,{onKeyboard:()=>p(J=>!J),send:J=>b.current(J),action:J=>L.current(J),ctrlArmed:te,onCtrl:q,selectionMode:O,onSelectionMode:ee}),m&&u.jsx(ly,{submitText:J=>D.current(J),token:s,session:e,node:t,ownerId:i}),c&&u.jsx(Z_,{session:e,node:t,token:s,filesEvent:y,onClose:()=>h(!1)})]})}function HE(){Pi();const[e]=R.useState(OE),[t,i]=R.useState(e.token),[s,o]=R.useState(e.pair||""),a=R.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[c,h]=R.useState(!1),m=FE(),[p]=R.useState(()=>JC(typeof location<"u"?location.pathname:"/")),[y,S]=R.useState(p.id),_=y===as(null,Ri),[C,w]=R.useState(null),x=he=>w(er(he)),[k,b]=R.useState([]),[D,L]=R.useState([]),[M,te]=R.useState([]),[U,O]=R.useState(()=>p.ownerId?os():zE(p.name)),[ee,q]=R.useState(null),[J,Q]=R.useState(null),T=he=>Q(er(he)),N=oh(t,m),P=R.useMemo(()=>(N||[]).filter(he=>he.instanceId).map(he=>({instanceId:he.instanceId,route:he.route,label:he.label,status:he.status})),[N]),I=NE(t,y,U,O,P),$=I.decks;R.useEffect(()=>{const he=new Map;for(const De of N||[]){const qe=(De.route||[De.name]).join("/"),bt=new Set((De.sessions||[]).map(si=>si.name));for(const si of De.cells||[])!si.cell||!si.tmuxSession||si.cell===si.tmuxSession||bt.has(si.cell)||he.set(`${qe}:${si.cell}`,`${qe}:${si.tmuxSession}`)}O(De=>z0(De,he))},[N]),R.useEffect(()=>{I.localNodeId&&O(he=>{const De=qd(he,I.localNodeId,P);return JSON.stringify(De)===JSON.stringify(he)?he:De})},[P,I.localNodeId]);const[K,F]=R.useState(null),[ae,W]=R.useState(null),j=R.useRef(0),[A,se]=R.useState(!1),[ge,G]=R.useState(IE),[Z,ue]=R.useState(!_),[de,Ce]=R.useState(()=>_?localStorage.getItem(Nv)==="1":!0),[Re,tt]=R.useState(!1),[Je,Ee]=R.useState("nodes"),[rt,wt]=R.useState(!1),[Dt,st]=R.useState(""),gt=(he="nodes",De=!1,qe="")=>{Ee(he),wt(De),st(qe),tt(!0)},[ct,at]=R.useState(!1),[le,be]=R.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[Fe,$e]=R.useState(!1);R.useEffect(()=>{try{localStorage.setItem(Ay,String(ge))}catch{}},[ge]),R.useEffect(()=>{try{localStorage.setItem(Nv,de?"1":"")}catch{}},[de]),R.useEffect(()=>{if(!t)return;let he=!1;return Promise.all([zv(t),qt("/api/config",t).then(De=>De.json())]).then(([De,qe])=>{he||(be({deviceDefault:De.deviceName||"",localNodeId:De.nodeId||"",localNameDefault:De.localName||""}),$e(!!qe.readonlyDefault),(De.firstRun===!0&&!qe.readonlyDefault||s)&&at(!0))}).catch(()=>{}),()=>{he=!0}},[t,s]);const pe=R.useCallback(async()=>{try{const De=await(await qt("/api/sessions",t)).json();De.error||b(De.sessions||[])}catch{}try{const he=await Er(t);L(he.available?he.cells||[]:[]),te(he.available?he.capabilities||[]:[])}catch{L([]),te([])}},[t]);R.useEffect(()=>{if(!m)return;pe();const he=setInterval(pe,4e3);return()=>clearInterval(he)},[m,pe]),R.useEffect(()=>{let he=!1;return qt("/api/config",t).then(De=>De.json()).then(De=>{he||DE(De.version,De.uiVersion,"0.8.25")}).catch(()=>{}),()=>{he=!0}},[t]);const Be=new Set([...k.map(he=>he.name),...N.flatMap(he=>he.sessions.map(De=>De.key))]),Te=ln(U),Y=he=>O(De=>{const qe=uy(he,I.localNodeId,P)||he,bt=r_(De,qe);return bt===De&&ln(De).length>=9&&I.setError(v("grid-full")),bt}),ne=async(he,De=[])=>{try{await ih(t,he,De)}catch{return}const qe=De.length?`${De.join("/")}:${he}`:he;O(bt=>ll(bt,qe)),pe()},z=async(he,De,qe=[])=>{try{await Bv(t,he,De,qe)}catch{return}pe()},H=async(he,De,qe=[])=>{await Av(t,{cell:he,enabled:!!De},qe),pe()},re=async he=>{if(!K)return;const{cell:De}=K,qe=Array.isArray(K.route)?K.route:[];he.action==="up"?await eh(t,{cell:De,boot:!!he.boot,...he.engine?{engine:he.engine}:{},...he.model!==void 0?{model:he.model}:{},...he.permissionPolicy?{permissionPolicy:he.permissionPolicy}:{}},qe):await il(t,{cell:De,boot:!!he.boot},qe);const bt=he.action==="up"?!!he.boot:he.boot?!1:!!K.boot;W({id:++j.current,cell:De,route:qe,enabled:bt}),pe()},me=R.useCallback(he=>{W(De=>(De==null?void 0:De.id)===he?null:De)},[]),Ae=async he=>{if(!(!(he!=null&&he.direct)||A)){se(!0);try{await rh(t,he.name,he.tunnelStatus==="up"?"down":"up")}finally{se(!1)}}},Se=async(he,De)=>{const qe=String(De||"").trim();return!(he!=null&&he.direct)||!vo(qe)?!1:(await $v(t,he.name,qe),!0)},xe=he=>{const De=$.find(qe=>qe.id===he);if(!De)return!1;try{const qe=window.open(Eu(De,t),"_blank");return qe&&(qe.opener=null),!!qe}catch{return!1}},He=async he=>{if(!he||he===y)return;const De=await I.select(he),qe=I.records.find(bt=>bt.id===he);S(he),O(De),q(null),Q(null);try{history.replaceState(null,"",Eu(qe||he,null))}catch{}},Zt=async(he,De)=>{const qe=await I.add(he,De);await He(qe.id)},Xe=async(he,De)=>{const qe=await I.rename(he,De);if(he===y){S(qe.id),O(qd(qe.layout,I.localNodeId,P)),q(null),Q(null);try{history.replaceState(null,"",Eu(qe,null))}catch{}}},Di=async he=>{await I.remove(he),he===y&&await He(I.localMainId)},Et=async(he,De)=>{!De||De===y||(await I.addTileTo(De,he),O(qe=>ll(qe,he)))};if(!t)return u.jsxs("div",{className:"nc-auth",children:[u.jsx("p",{children:v("auth-prompt")}),u.jsx("input",{onChange:he=>i(he.target.value.trim()),placeholder:"token"}),u.jsxs("label",{children:[u.jsx("input",{type:"checkbox",checked:c,onChange:he=>h(he.target.checked)})," ",v("remember-device")]}),u.jsx("button",{onClick:()=>{(c?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Ve=u.jsxs(u.Fragment,{children:[Re&&u.jsx(_E,{token:t,initialTab:Je,initialLocation:Dt,startNewCell:rt,onClose:()=>{tt(!1),wt(!1),st("")}}),ct&&u.jsx(yE,{token:t,initialPair:s,...le,onPairDone:a,onDone:()=>at(!1)}),u.jsx(xE,{token:t})]});if(!m)return C?u.jsxs(u.Fragment,{children:[u.jsx(Dv,{session:C.session,node:C.node,ownerId:C.ownerId,token:t,readonly:Fe,onBack:()=>w(null)}),Ve]}):u.jsxs(u.Fragment,{children:[u.jsx(eb,{onPick:x,token:t,onSettings:gt}),Ve]});const ji=_||!Z;return u.jsxs("div",{className:"nc-workspace",children:[ji&&u.jsx($C,{sessions:k,cells:D,activeSessions:Te,nodeGroups:N,fleetCapabilities:M,bootSettlement:ae,onBootSettlementApplied:me,localNodeId:I.localNodeId,onPick:T,onAddTile:Y,onPower:F,onBoot:H,onBootError:he=>I.setError(String((he==null?void 0:he.message)||he)),onNodePower:Ae,onNodeRename:Se,onKill:ne,onVisibility:z,onNew:()=>gt("fleet",!0),onSettings:gt,width:ge,collapsed:de,onResize:G,onToggleCollapse:()=>Ce(he=>!he)}),u.jsxs("div",{className:"nc-workspace-main",children:[u.jsx(ox,{decks:$,currentDeck:y,onCreate:Zt,onRename:Xe,onDelete:Di,onReorder:I.reorder,onOpenWindow:xe,onNavigate:He,saveState:I.saveState,error:I.error,sidebarVisible:ji,onToggleSidebar:_?null:()=>ue(he=>!he)}),u.jsx(VC,{layout:U,onLayoutChange:O,token:t,readonly:Fe,sessionsAlive:Be,focusSession:ee,onFocus:q,onOpenSingle:T,decks:$,currentDeck:y,onSendToDeck:Et})]}),J&&u.jsx("div",{className:"nc-single-overlay",children:u.jsx(Dv,{session:J.session,node:J.node,ownerId:J.ownerId,token:t,readonly:Fe,onBack:()=>Q(null)})}),K&&u.jsx(nl,{cell:K,token:t,route:Array.isArray(K.route)?K.route:[],onConfirm:re,onClose:()=>F(null)}),Ve]})}function $E(){Pi();const e=R.useSyncExternalStore(PE,Ev,Ev);if(!e.needed)return null;const t=e.kind==="install"?v("install-version-mismatch").replace("{v}",e.version):v("update-available").replace("{v}",e.version).replace(" "," ").trim();return u.jsxs("div",{className:"nc-update",role:"status","aria-live":"polite",children:[u.jsx("span",{className:"nc-update-msg",children:t}),e.kind==="reload"&&u.jsx("button",{className:"nc-update-btn",onClick:LE,children:v("reload")})]})}ME();Fw.createRoot(document.getElementById("root")).render(u.jsxs(Lw.StrictMode,{children:[u.jsx(HE,{}),u.jsx($E,{})]}));
91
+ `],{type:"application/json"}),$=URL.createObjectURL(I),K=document.createElement("a");K.href=$,K.download=`nexuscrew-fleet-${new Date().toISOString().slice(0,10)}.json`,document.body.appendChild(K),K.click(),K.remove(),setTimeout(()=>URL.revokeObjectURL($),0),b("")},U=async N=>{if(b(""),C([]),x([]),!!N){if(N.size>1024*1024){b(v("fleet-backup-too-large"));return}try{const P=nE(await N.text());if(!P.ok){b(v(`fleet-backup-${P.error}`));return}const I=new Set(P.engines.map($=>$.id));x(P.engines.map($=>({engine:$,exists:D.has($.id),selected:!D.has($.id)}))),C(P.cells.map($=>{const K=D.has($.engine)||I.has($.engine),F=L.has($.id);return{cell:$,engine:K?$.engine:"",selected:K&&!F,exists:F}}))}catch(P){b(String(P.message||P))}}},O=(N,P)=>C(I=>I.map(($,K)=>K===N?{...$,...P}:$)),ee=(N,P)=>x(I=>I.map(($,K)=>K===N?{...$,...P}:$)),q=new Set([...D,...w.filter(N=>N.selected).map(N=>N.engine.id)]),J=_.filter(N=>N.selected&&N.engine&&q.has(N.engine)),Q=w.filter(N=>N.selected),T=[...new Map([...t.map(N=>[N.id,N.label||N.id]),...w.filter(N=>N.selected).map(N=>[N.engine.id,N.engine.label||N.engine.id])]).entries()];return u.jsxs("div",{className:"nc-set-form nc-fleet-form nc-backup-dialog",children:[u.jsx("b",{children:v("fleet-backup")}),u.jsx("small",{children:v("fleet-backup-help")}),u.jsxs("div",{className:"nc-set-tabs nc-backup-tabs",children:[u.jsx("button",{type:"button",className:`nc-set-tabbtn${c==="export"?" on":""}`,onClick:()=>{h("export"),b("")},children:v("fleet-backup-export")}),u.jsx("button",{type:"button",className:`nc-set-tabbtn${c==="import"?" on":""}`,disabled:!s,title:s?"":v("fleet-backup-restore-unavailable"),onClick:()=>{h("import"),b("")},children:v("fleet-backup-import")})]}),c==="export"?u.jsxs(u.Fragment,{children:[u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{p(new Set(e.map(N=>N.id))),S(new Set(t.map(N=>N.id)))},children:v("select-all")}),u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{p(new Set),S(new Set)},children:v("select-none")})]}),u.jsxs("div",{className:"nc-backup-list",children:[u.jsx("b",{children:v("fleet-engines")}),t.map(N=>{var P;return u.jsxs("label",{className:"nc-check nc-backup-row",children:[u.jsx("input",{type:"checkbox",checked:y.has(N.id),onChange:()=>M(S,N.id)}),u.jsxs("span",{children:[u.jsx("b",{children:N.label||N.id}),u.jsxs("small",{children:[N.id,(P=N.envKeys)!=null&&P.length?` · ${N.envKeys.length} ${v("fleet-backup-env-names")}`:""]})]})]},N.id)}),u.jsx("b",{children:v("fleet-cells")}),e.map(N=>u.jsxs("label",{className:"nc-check nc-backup-row",children:[u.jsx("input",{type:"checkbox",checked:m.has(N.id),onChange:()=>M(p,N.id)}),u.jsxs("span",{children:[u.jsx("b",{children:N.id}),u.jsxs("small",{children:[N.engine," · ",v("fleet-system-prompt")," ",(N.prompt||"").length," ",v("characters")]})]})]},N.id)),!e.length&&!t.length&&u.jsx("div",{className:"nc-empty",children:v("fleet-backup-empty")})]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:v("cancel")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:!m.size&&!y.size,onClick:te,children:v("fleet-backup-download")})]})]}):u.jsxs(u.Fragment,{children:[u.jsx("input",{type:"file",accept:"application/json,.json",disabled:i,onChange:N=>U(N.target.files&&N.target.files[0])}),u.jsx("small",{children:v("fleet-backup-import-help")}),!!(_.length||w.length)&&u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(N=>N.map(P=>({...P,selected:!0}))),C(N=>N.map(P=>({...P,selected:!!P.engine})))},children:v("select-all")}),u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(N=>N.map(P=>({...P,selected:!1}))),C(N=>N.map(P=>({...P,selected:!1})))},children:v("select-none")})]}),u.jsxs("div",{className:"nc-backup-list",children:[!!w.length&&u.jsx("b",{children:v("fleet-engines")}),w.map((N,P)=>{var I;return u.jsx("div",{className:"nc-backup-import-row",children:u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:N.selected,onChange:$=>ee(P,{selected:$.target.checked})}),u.jsxs("span",{children:[u.jsx("b",{children:N.engine.label||N.engine.id}),u.jsxs("small",{children:[N.engine.id," · ",N.exists?v("fleet-backup-overwrite"):v("fleet-backup-new"),(I=N.engine.envKeys)!=null&&I.length?` · ${v("fleet-backup-env-values-required")}`:""]})]})]})},N.engine.id)}),!!_.length&&u.jsx("b",{children:v("fleet-cells")}),_.map((N,P)=>u.jsxs("div",{className:"nc-backup-import-row",children:[u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:N.selected,disabled:!N.engine||!q.has(N.engine),onChange:I=>O(P,{selected:I.target.checked})}),u.jsxs("span",{children:[u.jsx("b",{children:N.cell.id}),u.jsxs("small",{children:[N.exists?v("fleet-backup-overwrite"):v("fleet-backup-new")," · ",v("fleet-system-prompt")," ",N.cell.systemPrompt.length," ",v("characters")]})]})]}),u.jsxs("select",{value:N.engine,onChange:I=>O(P,{engine:I.target.value,selected:!!I.target.value}),children:[u.jsx("option",{value:"",children:v("fleet-backup-engine-missing")}),T.map(([I,$])=>u.jsx("option",{value:I,children:$},I))]})]},N.cell.id))]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:v("cancel")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||!J.length&&!Q.length,onClick:()=>o({engineRows:w,cellRows:_}),children:v("fleet-backup-restore")})]})]}),k&&u.jsx("div",{className:"nc-err",children:k})]})}function uE({state:e,setState:t,busy:i,onSave:s,catalog:o}){var k;const a=e.form,c=b=>t({...e,form:{...a,...b}}),h=a.envRows||[],m=[...new Map(o.map(b=>[b.client,b.clientLabel||b.client])).entries()],p=o.filter(b=>b.client===a.client),y=wo(o,a),S=typeof(y==null?void 0:y.credentialEnv)=="string"?y.credentialEnv:"",_=Array.isArray(y==null?void 0:y.credentialUsedBy)?y.credentialUsedBy:[],C=(y==null?void 0:y.credentialSource)||"missing",w=!!S&&e.mode==="new"&&(y==null?void 0:y.authConfigured)!==!0&&!a.credentialValue&&!a.allowMissingCredential,x=b=>{b&&c({client:b.client,provider:b.provider,credentialProfile:b.credentialProfile||"",managedModel:b.model||"",protocol:b.protocol||"",permissionPolicy:b.permissionPolicyDefault||"standard",rc:!!b.rc,displayName:b.custom?v("fleet-custom-provider-default"):"",baseUrl:b.custom?"":b.endpoint||"",envKey:b.defaultEnvKey||"",providerId:"nexuscrew-custom",credentialValue:"",credentialReveal:!1,allowMissingCredential:!1,...e.mode==="new"?{id:b.id,label:""}:{}})};return u.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[u.jsx("b",{children:e.mode==="new"?v("fleet-new-engine"):`${v("edit")} ${a.id}`}),u.jsx("input",{value:a.id,disabled:e.mode!=="new",placeholder:"id",onChange:b=>c({id:b.target.value})}),u.jsx("input",{value:a.label,placeholder:v("label"),onChange:b=>c({label:b.target.value})}),u.jsxs("select",{value:a.kind,disabled:e.mode!=="new",onChange:b=>c({kind:b.target.value}),children:[u.jsx("option",{value:"managed",children:v("fleet-managed")}),u.jsx("option",{value:"custom",children:v("fleet-custom")})]}),a.kind==="managed"?u.jsxs(u.Fragment,{children:[u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("select",{value:a.client,disabled:e.mode!=="new",onChange:b=>x(o.find(D=>D.client===b.target.value&&D.default)||o.find(D=>D.client===b.target.value)),children:m.map(([b,D])=>u.jsx("option",{value:b,children:D},b))}),u.jsx("select",{value:((k=wo(o,a))==null?void 0:k.id)||"",disabled:e.mode!=="new",onChange:b=>x(o.find(D=>D.id===b.target.value)),children:p.map(b=>u.jsx("option",{value:b.id,children:b.label},b.id))})]}),u.jsx("input",{value:a.managedModel,list:"nc-managed-models",placeholder:v(y!=null&&y.requiresModel?"fleet-model-required":"fleet-model-default"),onChange:b=>c({managedModel:b.target.value})}),u.jsx("datalist",{id:"nc-managed-models",children:[...(y==null?void 0:y.models)||[],...a.modelOptions||[]].filter((b,D,L)=>b&&L.indexOf(b)===D).map(b=>u.jsx("option",{value:b},b))}),y!=null&&y.supportsUnsafe?u.jsxs("select",{value:a.permissionPolicy,onChange:b=>c({permissionPolicy:b.target.value}),children:[u.jsx("option",{value:"standard",children:v("fleet-standard-permissions")}),u.jsx("option",{value:"unsafe",children:v("fleet-unsafe-permissions")})]}):u.jsx("small",{children:v("fleet-standard-permissions")}),(y==null?void 0:y.supportsUnsafe)&&a.permissionPolicy==="unsafe"&&u.jsx("small",{className:"nc-err",children:v("fleet-unsafe-warning")}),(y==null?void 0:y.credentialEnv)===!0&&u.jsxs(u.Fragment,{children:[u.jsx("input",{value:a.envKey,placeholder:v("fleet-api-key-env"),onChange:b=>c({envKey:b.target.value})}),u.jsx("small",{children:v("fleet-custom-secret-help")})]}),S&&u.jsxs("section",{className:"nc-fleet-key","aria-label":v("fleet-key-section"),children:[u.jsxs("div",{className:"nc-fleet-key-head",children:[u.jsx("b",{children:v("fleet-key-section")}),u.jsx("span",{children:v(`fleet-credential-source-${C}`)})]}),u.jsxs("label",{className:"nc-field",children:[v("fleet-key-variable"),u.jsx("input",{value:S,readOnly:!0,"aria-label":v("fleet-key-variable")})]}),u.jsx("small",{children:_.length?v("fleet-key-used-by").replace("{engines}",_.join(", ")):v("fleet-key-used-by-none")}),_.length>0&&u.jsx("small",{children:v("fleet-key-shared-impact").replace("{key}",S).replace("{engines}",_.join(", "))}),u.jsxs("div",{className:"nc-fleet-key-input",children:[u.jsx("input",{type:a.credentialReveal?"text":"password",value:a.credentialValue||"","aria-label":v("fleet-key-value").replace("{key}",S),autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",placeholder:y!=null&&y.authConfigured||e.mode==="edit"?v("fleet-key-blank-keeps"):v("fleet-key-enter"),onChange:b=>c({credentialValue:b.target.value,allowMissingCredential:b.target.value?!1:a.allowMissingCredential})}),u.jsx("button",{type:"button",className:"nc-btn ghost","aria-label":v(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal"),onClick:()=>c({credentialReveal:!a.credentialReveal}),children:v(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal")})]}),u.jsx("small",{children:v("fleet-key-local-help")}),e.mode==="new"&&(y==null?void 0:y.authConfigured)!==!0&&!a.credentialValue&&u.jsxs("label",{className:"nc-check nc-fleet-key-missing",children:[u.jsx("input",{type:"checkbox",checked:!!a.allowMissingCredential,onChange:b=>c({allowMissingCredential:b.target.checked})}),v("fleet-key-create-missing")]}),(y==null?void 0:y.notice)&&u.jsx("small",{className:"nc-fleet-provider-notice",children:v(`fleet-provider-notice-${y.notice}`)})]}),a.provider==="custom"&&u.jsxs(u.Fragment,{children:[u.jsx("input",{value:a.displayName,placeholder:v("fleet-provider-display"),onChange:b=>c({displayName:b.target.value})}),u.jsx("input",{value:a.baseUrl,placeholder:"https://api.example.com/v1",onChange:b=>c({baseUrl:b.target.value})}),((y==null?void 0:y.protocols)||[]).length>1&&u.jsx("select",{value:a.protocol,onChange:b=>c({protocol:b.target.value}),children:y.protocols.map(b=>u.jsx("option",{value:b,children:b},b))}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:a.envKey,placeholder:v("fleet-api-key-env"),onChange:b=>c({envKey:b.target.value})}),u.jsx("input",{value:a.providerId,placeholder:v("fleet-provider-id"),onChange:b=>c({providerId:b.target.value})})]}),u.jsxs("small",{children:[a.protocol," · ",v("fleet-custom-secret-help")]})]}),u.jsx("small",{children:v("fleet-managed-help")})]}):u.jsxs(u.Fragment,{children:[u.jsx("input",{value:a.command,placeholder:v("command-path"),onChange:b=>c({command:b.target.value})}),u.jsx("textarea",{value:a.argsText,placeholder:v("args-lines"),onChange:b=>c({argsText:b.target.value})}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:a.rc,onChange:b=>c({rc:b.target.checked})})," remote control"]}),u.jsxs("select",{value:a.promptMode,onChange:b=>c({promptMode:b.target.value}),children:[u.jsx("option",{value:"send-keys",children:"send-keys"}),u.jsx("option",{value:"flag",children:"flag"})]}),a.promptMode==="flag"&&u.jsx("input",{value:a.promptFlag,placeholder:"prompt flag",onChange:b=>c({promptFlag:b.target.value})}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:a.modelFlag,placeholder:"model flag",onChange:b=>c({modelFlag:b.target.value})}),u.jsx("input",{value:a.modelValue,placeholder:"model default",onChange:b=>c({modelValue:b.target.value})})]}),u.jsx("small",{children:v("env-write-only")}),h.map((b,D)=>u.jsxs("div",{className:"nc-fleet-env",children:[u.jsx("input",{value:b.key,disabled:b.configured,placeholder:"ENV_KEY",onChange:L=>{const M=h.slice();M[D]={...b,key:L.target.value},c({envRows:M})}}),u.jsx("input",{type:"password",value:b.value,placeholder:b.configured?"•••••• (unchanged)":"value",onChange:L=>{const M=h.slice();M[D]={...b,value:L.target.value},c({envRows:M})}}),u.jsx("button",{className:"nc-btn danger",onClick:()=>c({envRows:h.filter((L,M)=>M!==D)}),children:"×"})]},`${b.key}-${D}`)),u.jsx("button",{className:"nc-btn ghost",onClick:()=>c({envRows:[...h,{key:"",value:"",configured:!1}]}),children:"+ env"})]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>t(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:i||!a.id||w||a.kind==="custom"&&!a.command||a.kind==="managed"&&(y==null?void 0:y.requiresModel)&&!a.managedModel||a.kind==="managed"&&(y==null?void 0:y.credentialEnv)===!0&&!a.envKey||a.kind==="managed"&&a.provider==="custom"&&(!a.displayName||!a.baseUrl||!a.envKey||!a.providerId),onClick:s,children:v("save")})]})]})}function dE({token:e,route:t,targets:i=[],location:s,setLocation:o,state:a,setState:c,engines:h,busy:m,onSave:p}){const[y,S]=R.useState(null),[_,C]=R.useState(""),w=a.form,x=L=>c({...a,form:{...w,...L}}),k=h.find(L=>L.id===w.engine),b=L=>{var te,U,O;const M=h.find(ee=>ee.id===L);x({engine:L,model:((te=w.models)==null?void 0:te[L])||((U=M==null?void 0:M.managed)==null?void 0:U.model)||((O=M==null?void 0:M.model)==null?void 0:O.value)||""})},D=async L=>{try{const M=await Ov(e,L,t);S(M),x({cwd:M.path}),C("")}catch(M){C(String(M.message||M))}};return u.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[u.jsx("b",{children:a.mode==="new"?v("fleet-new-cell"):`${v("edit")} ${w.id}`}),a.mode==="new"&&u.jsxs("label",{className:"nc-field",children:[v("location"),u.jsx("span",{className:"nc-req",children:" *"}),u.jsxs("select",{value:s,onChange:L=>{o(L.target.value),x({engine:""})},children:[u.jsx("option",{value:"",children:v("local")}),i.map(L=>u.jsxs("option",{value:L.route.join("/"),disabled:L.status&&L.status!=="up",children:[L.label,L.status&&L.status!=="up"?` · ${v("node-offline")}`:""]},L.route.join("/")))]})]}),u.jsx("input",{value:w.id,disabled:a.mode!=="new",placeholder:"id",onChange:L=>x({id:L.target.value})}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:w.cwd,placeholder:v("cwd"),onChange:L=>x({cwd:L.target.value})}),u.jsx("button",{className:"nc-btn ghost",onClick:()=>y?S(null):D(w.cwd),children:v("browse")})]}),y&&u.jsxs("div",{className:"nc-fs",children:[u.jsx("div",{className:"nc-fs-path",children:y.path}),u.jsxs("div",{className:"nc-fs-list",children:[y.parent&&u.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>D(y.parent),children:["↑ ",v("fs-parent")]}),(y.dirs||[]).map(L=>u.jsxs("button",{className:"nc-fs-item",onClick:()=>D(`${y.path.replace(/\/$/,"")}/${L}`),children:["📁 ",L]},L))]})]}),_&&u.jsx("div",{className:"nc-err",children:_}),u.jsx("select",{value:w.engine,onChange:L=>b(L.target.value),children:h.map(L=>u.jsx("option",{value:L.id,children:L.label},L.id))}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:!!w.boot,onChange:L=>x({boot:L.target.checked})})," boot"]}),u.jsx("input",{value:w.model||"",list:"nc-cell-models",placeholder:v("fleet-model-override"),onChange:L=>x({model:L.target.value})}),u.jsx("datalist",{id:"nc-cell-models",children:((k==null?void 0:k.availableModels)||[]).map(L=>u.jsx("option",{value:L},L))}),u.jsx("textarea",{value:w.prompt||"",placeholder:"prompt",onChange:L=>x({prompt:L.target.value})}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>c(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:m||!w.id||!w.cwd||!w.engine,onClick:p,children:v("save")})]})]})}function xv({token:e,route:t=[],state:i,setState:s,busy:o,onSave:a}){const[c,h]=R.useState(null),[m,p]=R.useState([]),[y,S]=R.useState(""),_=Array.isArray(t)?t.join("/"):"";R.useEffect(()=>{let b=!0;return th(e,_?_.split("/"):[]).then(D=>{b&&(p(D.engines||[]),S(""))}).catch(D=>{b&&(p([]),S(String(D.message||D)))}),()=>{b=!1}},[e,_]);const C=i.form,w=b=>s({...i,form:{...C,...b}}),x=async b=>{try{const D=await Ov(e,b,t);h(D),w({cwd:D.path})}catch{}},k=!C.id&&C.tmuxSession?C.tmuxSession.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,32):C.id;return u.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[u.jsxs("b",{children:[v("import-as-cell")," · ",C.tmuxSession]}),u.jsx("small",{children:v("import-help")}),u.jsx("input",{value:C.tmuxSession,disabled:!0,placeholder:"tmux session",readOnly:!0}),u.jsx("input",{value:C.id,placeholder:v("name"),onChange:b=>w({id:b.target.value})}),u.jsxs("div",{className:"nc-fleet-pair",children:[u.jsx("input",{value:C.cwd,placeholder:v("cwd"),onChange:b=>w({cwd:b.target.value})}),u.jsx("button",{className:"nc-btn ghost",type:"button",onClick:()=>c?h(null):x(C.cwd),children:v("browse")})]}),c&&u.jsxs("div",{className:"nc-fs",children:[u.jsx("div",{className:"nc-fs-path",children:c.path}),u.jsxs("div",{className:"nc-fs-list",children:[c.parent&&u.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>x(c.parent),children:["↑ ",v("fs-parent")]}),(c.dirs||[]).map(b=>u.jsxs("button",{className:"nc-fs-item",onClick:()=>x(`${c.path.replace(/\/$/,"")}/${b}`),children:["📁 ",b]},b))]})]}),u.jsxs("select",{value:C.engine,onChange:b=>w({engine:b.target.value}),children:[u.jsx("option",{value:"",children:v("import-engine-required")}),m.map(b=>u.jsx("option",{value:b.id,children:b.label},b.id))]}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:!!C.boot,onChange:b=>w({boot:b.target.checked})})," boot"]}),(i.err||y)&&u.jsx("div",{className:"nc-err",children:i.err||y}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>s(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:o||!C.tmuxSession||!C.engine||!m.length,onClick:a,children:v(k?"import-as-cell":"save")})]})]})}function hE({token:e,readonly:t,targets:i=[],startNewCell:s=!1,initialLocation:o=""}){const[a,c]=R.useState({engines:[],cells:[],managedCatalog:[]}),[h,m]=R.useState({available:!1,capabilities:[]}),[p,y]=R.useState(!1),[S,_]=R.useState(null),[C,w]=R.useState(null),[x,k]=R.useState(!1),[b,D]=R.useState(""),[L,M]=R.useState(""),[te,U]=R.useState(o),[O,ee]=R.useState(!1),[q,J]=R.useState(null),[Q,T]=R.useState(null),[N,P]=R.useState(!1),[I,$]=R.useState([]),[K,F]=R.useState(null),[ae,W]=R.useState("manage"),j=R.useRef(!1),A=te?te.split("/"):[],se=R.useCallback(async()=>{try{const le=await Er(e,A);m(le);try{const be=await nh(e,A);ee(!!be.readonlyDefault)}catch{ee(!1)}if(le.provider==="builtin"&&(le.capabilities||[]).includes("definitions")){const be=await th(e,A),Fe=new Map((le.engines||[]).map($e=>[$e.id,$e]));if(be.engines=(be.engines||[]).map($e=>{var pe;return{...$e,availableModels:((pe=Fe.get($e.id))==null?void 0:pe.models)||[]}}),c(be),(le.capabilities||[]).includes("credentials"))try{$((await Hw(e,A)).credentials||[])}catch{$([])}else $([])}D("")}catch(le){D(String(le.message||le))}finally{y(!0)}},[e,te]);R.useEffect(()=>{se();const le=setInterval(se,5e3);return()=>clearInterval(le)},[se]);const ge=new Set((h.cells||[]).filter(le=>le.active).map(le=>le.cell)),G=h.provider==="builtin"&&(h.capabilities||[]).includes("edit"),Z=(h.capabilities||[]).includes("restore");R.useEffect(()=>{!s||j.current||!G||!a.engines.length||(j.current=!0,w({mode:"new",form:Cv(a.engines[0].id)}))},[s,G,a.engines]);const ue=async le=>{k(!0),D(""),M("");try{await le(),await se()}catch(be){D(String(be.message||be))}k(!1)},de=()=>ue(async()=>{var re;const le=S.mode==="new",be=S.form,Fe=a.managedCatalog||[],$e=be.kind==="managed"?wo(Fe,be):null,pe=typeof($e==null?void 0:$e.credentialEnv)=="string"?$e.credentialEnv:"",Be=pe&&be.credentialValue||"",Te=lE(be,le,Fe);!le&&S.form.kind==="custom"&&!S.form.modelFlag&&(Te.model=null),!le&&S.form.kind==="custom"&&S.form.promptMode!=="flag"&&(Te.promptFlag=null);let Y;if(le)Y=await Ww(e,Te,A);else{const me=S.original,Ae=new Set(S.form.envRows.filter(He=>!He.remove).map(He=>He.key)),Se=(me.envKeys||[]).filter(He=>!Ae.has(He)),xe=Object.fromEntries(S.form.envRows.filter(He=>!He.remove&&He.key&&(!He.configured||He.value!=="")).map(He=>[He.key,He.value]));Y=await Uw(e,me.id,Te,S.form.kind==="custom"?{set:xe,remove:Se}:void 0,A)}let ne=null;if(pe&&Be)try{ne=await pm(e,pe,Be,A),$(ne.credentials||[])}catch{throw await se(),le&&_({mode:"edit",original:{...Te,id:be.id},form:{...be,credentialReveal:!1}}),new Error(v(le?"fleet-key-partial-create":"fleet-key-partial-edit"))}await se(),_(null),M(v("fleet-saved"));const z=((re=((ne==null?void 0:ne.credentials)||[]).find(me=>me.envKey===pe))==null?void 0:re.activeCells)||[],H=[...new Set([...(Y==null?void 0:Y.activeCells)||[],...z])];if(H.length&&window.confirm(v("fleet-restart-confirm").replace("{cells}",H.join(", "))))for(const me of H)await co(e,me,A)}),Ce=()=>ue(async()=>{const le=C.mode==="new",be=C.form,Fe={...le?{id:be.id}:{},cwd:be.cwd,engine:be.engine,boot:!!be.boot};le?(be.model&&(Fe.model=be.model),be.prompt&&(Fe.prompt=be.prompt)):(Fe.model=be.model||null,Fe.prompt=be.prompt||null);const $e=le?await qw(e,Fe,A):await Vw(e,C.original.id,Fe,A),pe=le?be.id:C.original.id;w(null),M(v("fleet-saved")),!le&&($e!=null&&$e.active)&&window.confirm(v("fleet-restart-confirm").replace("{cells}",pe))&&await co(e,pe,A)}),Re=t||O,tt=le=>I.find(be=>{var Fe;return(Fe=be.engines)==null?void 0:Fe.includes(le.id)})||null,Je=()=>ue(async()=>{var Fe;const le=await pm(e,K.envKey,K.value,A);$(le.credentials||[]),F(null),M(v("fleet-credential-saved"));const be=((Fe=(le.credentials||[]).find($e=>$e.envKey===K.envKey))==null?void 0:Fe.activeCells)||[];if(be.length&&window.confirm(v("fleet-restart-confirm").replace("{cells}",be.join(", "))))for(const $e of be)await co(e,$e,A)}),Ee=le=>ue(async()=>{if(!window.confirm(v("fleet-credential-remove-confirm").replace("{key}",le.envKey)))return;const be=await $w(e,le.envKey,A);$(be.credentials||[]),M(v("fleet-credential-removed"))}),rt=le=>J({...le,route:Array.isArray(le==null?void 0:le.route)?le.route:A}),wt=async le=>{if(!q)return;const be=q.cell||q.id,Fe=Array.isArray(q.route)?q.route:A;le.action==="up"?await eh(e,{cell:be,boot:!!le.boot,...le.engine?{engine:le.engine}:{},...le.model!==void 0?{model:le.model}:{},...le.permissionPolicy?{permissionPolicy:le.permissionPolicy}:{}},Fe):await il(e,{cell:be,boot:!!le.boot},Fe)},Dt=(le,be)=>{D(""),T({mode:"new",route:Array.isArray(be)?be:A,form:{tmuxSession:le.name,id:"",engine:"",cwd:"",boot:!1},err:""})},st=()=>ue(async()=>{const le=Q.form;if(!le.engine){T({...Q,err:v("import-engine-required")});return}await Yw(e,{tmuxSession:le.tmuxSession,id:le.id||void 0,engine:le.engine,cwd:le.cwd||void 0,boot:!!le.boot},le.route||A),T(null),M(v("fleet-saved"))}),gt=({engineRows:le=[],cellRows:be=[]})=>ue(async()=>{const Fe=le.filter(re=>re.selected),$e=Fe.map(re=>Ny(re.engine));if($e.some(re=>!re))throw new Error(v("fleet-backup-invalid-engine"));const pe=Fe.filter(re=>re.exists).map(re=>re.engine.id);if(pe.length&&!window.confirm(v("fleet-backup-confirm-engine-overwrite").replace("{engines}",pe.join(", "))))return;const Be=[...new Set([...a.engines.map(re=>re.id),...$e.map(re=>re.id)])],Te=[];for(const re of be.filter(me=>me.selected)){const me=rE(re.cell,re.engine,Be);if(!me)throw new Error(`${re.cell.id}: ${v("fleet-backup-engine-missing")}`);Te.push(me)}const Y=be.filter(re=>re.selected&&re.exists).map(re=>re.cell.id);if(Y.length&&!window.confirm(v("fleet-backup-confirm-overwrite").replace("{cells}",Y.join(", "))))return;const ne=$e.length?await Gw(e,$e,pe.length>0,A):{needsRestart:[]},z=Te.length?await Xw(e,Te,A):{needsRestart:[]},H=[...new Set([...ne.needsRestart||[],...z.needsRestart||[]])];P(!1),M(`${v("fleet-backup-restored").replace("{n}",String(Te.length))}${$e.length?` · ${$e.length} ${v("fleet-engines").toLowerCase()}`:""}${H.length?` · ${v("fleet-backup-needs-restart").replace("{cells}",H.join(", "))}`:""}`)}),ct=u.jsxs("label",{className:"nc-field",children:[v("location"),u.jsxs("select",{value:te,onChange:le=>{U(le.target.value),_(null),w(null),D(""),M(""),ee(!1),y(!1),m({available:!1,capabilities:[]}),c({engines:[],cells:[],managedCatalog:[]}),$([]),F(null)},children:[u.jsx("option",{value:"",children:v("local")}),i.map(le=>u.jsxs("option",{value:le.route.join("/"),disabled:le.status&&le.status!=="up",children:[le.label,le.status&&le.status!=="up"?` · ${v("node-offline")}`:""]},le.route.join("/")))]})]}),at=u.jsxs("div",{className:"nc-fleet-view-tabs",role:"tablist","aria-label":v("tab-fleet"),children:[u.jsx("button",{type:"button",role:"tab","aria-selected":ae==="manage",className:`nc-set-tabbtn${ae==="manage"?" on":""}`,onClick:()=>W("manage"),children:v("fleet-manage-location")}),u.jsx("button",{type:"button",role:"tab","aria-selected":ae==="overview",className:`nc-set-tabbtn${ae==="overview"?" on":""}`,onClick:()=>W("overview"),children:v("fleet-network-overview")})]});return p?G?u.jsxs("div",{className:"nc-set-tab nc-fleet-editor",children:[at,ae==="overview"?u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-overview-help")}),u.jsx(Qu,{token:e,targets:i,readonly:t,onPower:rt,onImport:Dt})]}):u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-manage-help")}),ct,u.jsxs("div",{className:"nc-set-row nc-fleet-backup-actions",children:[u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),P(!0)},children:v("fleet-backup")}),u.jsx("small",{children:v("fleet-backup-help")})]}),u.jsxs("div",{className:"nc-fleet-section-head",children:[u.jsx("b",{children:v("fleet-cells")}),u.jsx("span",{className:"nc-fleet-head-actions",children:u.jsxs("button",{className:"nc-btn primary",disabled:Re||x||!a.engines.length,onClick:()=>{var le;D(""),w({mode:"new",form:Cv((le=a.engines[0])==null?void 0:le.id)})},children:["+ ",v("add")]})})]}),a.cells.map(le=>{const be=ge.has(le.id),Fe=h.capabilities||[];return u.jsxs("div",{className:"nc-fleet-item",children:[u.jsxs("span",{children:[u.jsx("b",{children:le.id}),u.jsxs("small",{children:[le.engine," · ",le.cwd,be?` · ${v("service-active")}`:` · ${v("cell-off")}`]})]}),u.jsxs("span",{children:[be&&Fe.includes("down")&&u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>ue(()=>il(e,{cell:le.id},A)),children:v("stop")}),!be&&Fe.includes("up")&&u.jsx("button",{className:"nc-btn primary",disabled:Re||x,onClick:()=>rt({cell:le.id,id:le.id,engine:le.engine,model:le.model,models:le.models,permissionPolicies:le.permissionPolicies,active:!1,boot:le.boot}),children:v("start")}),be&&Fe.includes("restart")&&u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>ue(()=>co(e,le.id,A)),children:v("restart")}),u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),w({mode:"edit",original:le,form:{...le}})},children:v("edit")}),u.jsx("button",{className:"nc-btn danger",disabled:Re||x,onClick:()=>ue(async()=>{window.confirm(v("fleet-remove-cell").replace("{id}",le.id))&&await Iv(e,le.id,!0,A)}),children:"×"})]})]},le.id)}),u.jsxs("div",{className:"nc-fleet-section-head",children:[u.jsx("b",{children:v("fleet-engines")}),u.jsxs("button",{className:"nc-btn primary",disabled:Re||x,onClick:()=>{D(""),_({mode:"new",form:sE()})},children:["+ ",v("add")]})]}),a.engines.map(le=>{var Fe,$e;const be=tt(le);return u.jsxs("div",{className:"nc-fleet-item",children:[u.jsxs("span",{children:[u.jsx("b",{children:le.label}),u.jsx("small",{children:le.managed?`${le.id} · ${le.managed.client} / ${le.managed.provider} · ${(Fe=le.managedInfo)!=null&&Fe.configured?v("fleet-ready"):(($e=le.managedInfo)==null?void 0:$e.reason)||v("fleet-not-ready")}`:`${le.id} · ${le.command}`}),be&&u.jsxs("small",{children:[be.envKey," · ",v(`fleet-credential-source-${be.source||"missing"}`)]})]}),u.jsxs("span",{children:[be&&u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),F({envKey:be.envKey,value:""})},children:be.configured?v("fleet-credential-change"):v("fleet-credential-set")}),(be==null?void 0:be.source)==="local"&&u.jsx("button",{className:"nc-btn danger",disabled:Re||x,onClick:()=>Ee(be),children:v("fleet-credential-forget")}),u.jsx("button",{className:"nc-btn ghost",disabled:Re||x,onClick:()=>{D(""),_({mode:"edit",original:le,form:aE(le)})},children:v("edit")}),u.jsx("button",{className:"nc-btn danger",disabled:Re||x,onClick:()=>ue(async()=>{window.confirm(v("fleet-remove-engine").replace("{id}",le.id))&&await Kw(e,le.id,A)}),children:"×"})]})]},le.id)})]}),S&&u.jsx(ns,{onClose:()=>_(null),label:v("fleet-new-engine"),error:b,children:u.jsx(uE,{state:S,setState:_,busy:x,onSave:de,catalog:a.managedCatalog||[]})}),C&&u.jsx(ns,{onClose:()=>w(null),label:v("fleet-new-cell"),error:b,children:u.jsx(dE,{token:e,route:A,targets:i,location:te,setLocation:U,state:C,setState:w,engines:a.engines,busy:x,onSave:Ce})}),L&&u.jsx("div",{className:"nc-set-note",children:L}),b&&u.jsx("div",{className:"nc-err",children:b}),N&&u.jsx(ns,{onClose:()=>P(!1),label:v("fleet-backup"),error:b,children:u.jsx(cE,{cells:a.cells,engines:a.engines,busy:x,canRestore:Z,onRestore:gt,onClose:()=>P(!1)})}),K&&u.jsx(ns,{onClose:()=>F(null),label:v("fleet-credential-title"),error:b,children:u.jsxs("div",{className:"nc-fleet-form nc-credential-form",children:[u.jsx("b",{children:v("fleet-credential-title")}),u.jsx("small",{children:v("fleet-credential-help")}),u.jsxs("label",{className:"nc-field",children:[K.envKey,u.jsx("input",{type:"password",autoComplete:"off",spellCheck:"false",autoCapitalize:"none",value:K.value,onChange:le=>F({...K,value:le.target.value})})]}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",onClick:()=>F(null),children:v("cancel")}),u.jsx("button",{className:"nc-btn primary",disabled:x||!K.value,onClick:Je,children:v("save")})]})]})}),Q&&u.jsx(ns,{onClose:()=>T(null),label:v("import-as-cell"),error:b,children:u.jsx(xv,{token:e,route:Q.route||A,state:Q,setState:T,busy:x,onSave:st})}),q&&u.jsx(nl,{cell:q,token:e,route:Array.isArray(q.route)?q.route:A,onConfirm:async le=>{try{await wt(le)}finally{await se()}},onClose:()=>J(null)})]}):u.jsxs("div",{className:"nc-set-tab",children:[at,ae==="overview"?u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-overview-help")}),u.jsx(Qu,{token:e,targets:i,readonly:t,onPower:rt,onImport:Dt})]}):u.jsxs(u.Fragment,{children:[ct,u.jsxs("div",{className:"nc-set-info",children:[v(b?"fleet-editor-load-error":"fleet-editor-unavailable"),!b&&h.reason?` ${h.reason}`:""]})]}),b&&u.jsx("div",{className:"nc-err",children:b}),Q&&u.jsx(ns,{onClose:()=>T(null),label:v("import-as-cell"),error:b,children:u.jsx(xv,{token:e,route:Q.route||A,state:Q,setState:T,busy:x,onSave:st})}),q&&u.jsx(nl,{cell:q,token:e,route:Array.isArray(q.route)?q.route:A,onConfirm:async le=>{try{await wt(le)}finally{await se()}},onClose:()=>J(null)})]}):u.jsxs("div",{className:"nc-set-tab",children:[at,ae==="overview"?u.jsxs(u.Fragment,{children:[u.jsx("div",{className:"nc-set-info",children:v("fleet-overview-help")}),u.jsx(Qu,{token:e,targets:i,readonly:t,onPower:rt,onImport:Dt})]}):u.jsxs(u.Fragment,{children:[ct,u.jsx("div",{className:"nc-set-info",children:v("fleet-editor-loading")})]})]})}function fE({text:e}){const[t,i]=R.useState(!1),s=async()=>{try{await navigator.clipboard.writeText(e),i(!0),setTimeout(()=>i(!1),1500)}catch{}};return u.jsxs("div",{className:"nc-set-copyline",children:[u.jsx("code",{children:e}),u.jsxs("button",{type:"button",className:"nc-btn ghost",onClick:s,children:[u.jsx(et,{name:"copy",size:14})," ",v(t?"copied":"copy")]})]})}function pE({value:e}){const[t,i]=R.useState("");return R.useEffect(()=>{let s=!0;return Px.toDataURL(e,{margin:1,width:220}).then(o=>{s&&i(o)}).catch(()=>{}),()=>{s=!1}},[e]),t?u.jsx("img",{src:t,width:"220",height:"220",alt:"NexusCrew pairing QR"}):null}function mE({token:e,nodes:t,settings:i,readonly:s,refresh:o}){const[a,c]=R.useState(null),[h,m]=R.useState(null),[p,y]=R.useState({}),[S,_]=R.useState(null),[C,w]=R.useState({ssh:"",sshPort:"",name:""}),[x,k]=R.useState(""),[b,D]=R.useState(""),[L,M]=R.useState(""),[te,U]=R.useState(!1),[O,ee]=R.useState(null),[q,J]=R.useState(null),Q=Date.now(),T=i&&i.deviceName||"",N=(t||[]).filter(G=>G&&G.direction==="outbound"&&G.name&&G.ssh),P=N.find(G=>G.name===x)||N[0]||null,I=N.find(G=>G.name===b)||N[0]||null,$=I?jm(I.tunnel,Q):null,K=I!=null&&I.shared?$!=null&&$.up?"share-local-active":"share-local-pending":$!=null&&$.up?"share-local-private":"share-local-private-down",F=[{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")}],ae=async(G,Z)=>{c(null),m(`${G}:${Z}`);try{const ue=await rh(e,G,Z);Z==="test"&&y(de=>({...de,[G]:ue})),await o()}catch(ue){c(`${G}: ${String(ue.message||ue)}`)}m(null)},W=async G=>{c(null),m(`${G}:remove`);try{await n0(e,G),J(null),await o()}catch(Z){c(`${G}: ${String(Z.message||Z)}`)}m(null)},j=G=>ee({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||[]]}),A=async()=>{if(!O||!vo(O.label)){c(v("err-label"));return}const G=O.direction==="inbound"?{label:O.label,visibility:O.visibility,selected:O.visibility==="selected"?O.selected:[]}:{label:O.label,ssh:O.ssh,autostart:O.autostart,...O.sshPort?{sshPort:Number(O.sshPort)}:{}};c(null),m(`${O.name}:edit`);try{await i0(e,O.name,G),ee(null),await o()}catch(Z){c(`${O.name}: ${String(Z.message||Z)}`)}m(null)},se=async G=>{if(I){c(null),m(`${I.name}:share`);try{await r0(e,I.name,G),await o()}catch(Z){const ue=Z!=null&&Z.data&&typeof Z.data.hint=="string"?Z.data.hint:"";c(`${I.name}: ${String(Z.message||Z)}${ue?` — ${ue}`:""}`)}m(null)}},ge=async()=>{if(c(null),P){const ue=Dm({name:P.name,ssh:P.ssh,sshPort:P.sshPort||""});if(!ue.ok){c(v(ue.error));return}m("invite");try{_(await gm(e,{ssh:ue.value.ssh,...ue.value.sshPort?{sshPort:ue.value.sshPort}:{}},[P.name]))}catch(de){c(String(de.message||de))}m(null);return}const G=Jn(C.name||L||T||"NexusCrew"),Z=Dm({name:G,ssh:C.ssh,sshPort:C.sshPort});if(!Z.ok){c(v(Z.error));return}m("invite");try{_(await gm(e,{...L||T?{label:L||T}:{},name:G,ssh:Z.value.ssh,...Z.value.sshPort?{sshPort:Z.value.sshPort}:{}}))}catch(ue){c(String(ue.message||ue))}m(null)};return u.jsxs("div",{className:"nc-set-tab",children:[u.jsx("div",{className:"nc-sheet-label",children:v("peer-inventory-title")}),(t||[]).length===0&&u.jsx("div",{className:"nc-empty",children:v("no-nodes")}),F.map(G=>G.rows.length>0&&u.jsxs("div",{className:"nc-peer-group",children:[u.jsxs("div",{className:"nc-peer-group-title",children:[v(G.key)," ",u.jsx("span",{children:G.rows.length})]}),G.rows.map(Z=>{var tt,Je;const ue=Z.kind==="transitive",de=ue?{up:Z.stale!==!0,label:Z.stale===!0?"peer-routed-stale":"peer-routed"}:jm(Z.tunnel,Q),Ce=p[Z.name],Re=Z.actions||{};return u.jsxs("div",{className:`nc-set-node${ue?" routed":""}`,children:[u.jsxs("div",{className:"nc-set-node-head",children:[u.jsx("span",{className:`nc-dot ${de.up?"on":""}`}),u.jsx("b",{children:Z.label||Z.name}),u.jsxs("small",{children:[Z.name,ue?` · ${Z.route.join(" → ")}`:Z.direction==="outbound"?` · SSH ${Z.ssh||""}`:` · ${v("node-connected-client")}`,(tt=Z.tunnel)!=null&&tt.transport?` · ${Z.tunnel.transport} ${v("transport-used")}`:""]}),u.jsxs("span",{className:`nc-set-tunnel${de.up?" up":""}`,children:[v(de.label),de.since?` · ${de.since}`:""]})]}),!ue&&Z.direction==="inbound"&&u.jsx("div",{className:"nc-set-info",children:v(Z.shared?"peer-shared":"peer-private")}),Re.visibility&&Z.shared&&u.jsxs(u.Fragment,{children:[u.jsxs("select",{value:Z.visibility||"network",disabled:s||!!h,onChange:async Ee=>{m(`${Z.name}:visibility`);try{await vm(e,Z.name,Ee.target.value),await o()}catch(rt){c(String(rt.message||rt))}m(null)},children:[u.jsx("option",{value:"network",children:v("visibility-network")}),u.jsx("option",{value:"relay-only",children:v("visibility-relay")}),u.jsx("option",{value:"selected",children:v("visibility-selected")})]}),Z.visibility==="selected"&&u.jsx("div",{className:"nc-set-row",children:(t||[]).filter(Ee=>Ee.name!==Z.name&&Ee.nodeId).map(Ee=>{const rt=(Z.selected||[]).includes(Ee.nodeId);return u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:rt,disabled:s||!!h,onChange:async wt=>{const Dt=wt.target.checked?[...Z.selected||[],Ee.nodeId]:(Z.selected||[]).filter(st=>st!==Ee.nodeId);m(`${Z.name}:visibility`);try{await vm(e,Z.name,"selected",Dt),await o()}catch(st){c(String(st.message||st))}m(null)}})," ",Ee.label||Ee.name]},Ee.nodeId)})})]}),u.jsxs("div",{className:"nc-set-node-actions",children:[Re.edit&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>j(Z),children:v("edit")}),Re.test&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>ae(Z.name,"test"),children:v("node-test")}),Re.disconnect&&de.up&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>ae(Z.name,"down"),children:v("tunnel-stop")}),Re.connect&&!de.up&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>ae(Z.name,"up"),children:v("tunnel-start")}),Re.restart&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>ae(Z.name,"restart"),children:v("tunnel-restart")}),Re.remove&&u.jsxs("button",{type:"button",className:"nc-btn danger",disabled:s||!!h,title:v(s?"settings-readonly":"delete"),onClick:()=>J(Z.name),children:[u.jsx(et,{name:"trash",size:14})," ",v("delete")]})]}),(O==null?void 0:O.name)===Z.name&&u.jsxs("div",{className:"nc-set-form nc-node-editor",children:[u.jsxs("label",{className:"nc-field",children:[v("node-display-label"),u.jsx("input",{value:O.label,disabled:!!h,onChange:Ee=>ee({...O,label:Ee.target.value})})]}),O.direction==="outbound"?u.jsxs(u.Fragment,{children:[u.jsxs("label",{className:"nc-field",children:[v("node-ssh-label"),u.jsx("input",{value:O.ssh,disabled:!!h,onChange:Ee=>ee({...O,ssh:Ee.target.value})})]}),u.jsxs("label",{className:"nc-field",children:[v("node-ssh-port-label"),u.jsx("input",{inputMode:"numeric",value:O.sshPort,disabled:!!h,onChange:Ee=>ee({...O,sshPort:Ee.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:O.autostart,disabled:!!h,onChange:Ee=>ee({...O,autostart:Ee.target.checked})})," ",v("boot-persist")]})]}):u.jsxs("label",{className:"nc-field",children:[v("peer-visibility"),u.jsxs("select",{value:O.visibility,disabled:!!h,onChange:Ee=>ee({...O,visibility:Ee.target.value}),children:[u.jsx("option",{value:"network",children:v("visibility-network")}),u.jsx("option",{value:"relay-only",children:v("visibility-relay")}),u.jsx("option",{value:"selected",children:v("visibility-selected")})]})]}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn primary",disabled:!!h,onClick:A,children:v("save")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>ee(null),children:v("cancel")})]})]}),q===Z.name&&u.jsxs("div",{className:"nc-set-confirm",children:[u.jsx("b",{children:v("node-remove-confirm").replace("{name}",Z.label||Z.name)}),u.jsx("small",{children:v("node-remove-warning")}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn danger",disabled:!!h,onClick:()=>W(Z.name),children:v("delete")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>J(null),children:v("cancel")})]})]}),((Je=Z.health)==null?void 0:Je.detail)&&u.jsx("div",{className:`nc-set-test${Z.health.status==="healthy"?" ok":Z.health.status==="passive"?"":" ko"}`,children:Z.health.detail}),Ce&&u.jsxs("div",{className:`nc-set-test${Ce.ok?" ok":" ko"}`,children:[Ce.result,Ce.detail?` — ${Ce.detail}`:""]})]},`${Z.kind||"direct"}:${Z.nodeId||Z.name}`)})]},G.key)),I&&u.jsxs("div",{className:"nc-set-form nc-local-share",children:[u.jsx("div",{className:"nc-sheet-label",children:v("share-local-heading")}),N.length>1&&u.jsxs("label",{className:"nc-field",children:[v("share-local-hub"),u.jsx("select",{value:I.name,disabled:s||!!h,onChange:G=>D(G.target.value),children:N.map(G=>u.jsx("option",{value:G.name,children:G.label||G.name},G.name))})]}),u.jsxs("label",{className:"nc-check nc-node-share",children:[u.jsx("input",{type:"checkbox",checked:I.shared===!0,disabled:s||!!h,onChange:G=>se(G.target.checked)}),u.jsxs("span",{children:[u.jsx("b",{children:v("share-local-through").replace("{device}",T||v("local")).replace("{hub}",I.label||I.name)}),u.jsx("small",{children:v(I.shared?"share-node-on-desc":"share-node-off-desc")})]})]}),u.jsx("div",{className:`nc-set-test${I.shared&&($!=null&&$.up)?" ok":$!=null&&$.up?"":" ko"}`,children:v(K)}),!($!=null&&$.up)&&u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:s||!!h,onClick:()=>se(I.shared===!0),children:v("share-local-reconnect")})]}),u.jsx(Sy,{token:e,deviceDefault:T,localNodeId:i&&i.nodeId||"",localNameDefault:i&&i.localName||"",readonly:s,onSuccess:o}),u.jsxs("div",{className:"nc-set-form",children:[u.jsx("div",{className:"nc-sheet-label",children:v("invite-node")}),u.jsx("small",{className:"nc-set-hint",children:v(P?"invite-network-hint":"invite-v2-hint")}),P?u.jsxs(u.Fragment,{children:[N.length>1&&u.jsxs("label",{className:"nc-field",children:[v("invite-network-label"),u.jsx("select",{value:P.name,disabled:s||!!h,onChange:G=>{k(G.target.value),_(null)},children:N.map(G=>u.jsx("option",{value:G.name,children:G.label||G.name},G.name))})]}),u.jsxs("div",{className:"nc-set-info nc-invite-endpoint",children:[v("invite-network-via"),": ",u.jsx("b",{children:P.label||P.name})," · ",P.ssh,P.sshPort?`:${P.sshPort}`:""]}),u.jsx("small",{className:"nc-set-hint",children:v("invite-network-route")})]}):u.jsxs("label",{className:"nc-field",children:[v("invite-endpoint-label"),u.jsx("input",{placeholder:"user@host",value:C.ssh,disabled:s,onChange:G=>w({...C,ssh:G.target.value})}),u.jsx("small",{className:"nc-set-hint",children:v("invite-endpoint-needed")})]}),!P&&te&&u.jsxs("div",{className:"nc-invite-advanced",children:[u.jsxs("label",{className:"nc-field",children:[v("node-ssh-port-label"),u.jsx("input",{inputMode:"numeric",placeholder:"22",value:C.sshPort,disabled:s,onChange:G=>w({...C,sshPort:G.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),u.jsxs("label",{className:"nc-field",children:[v("device-name-label"),u.jsx("input",{placeholder:T||"NexusCrew",value:L,disabled:s,onChange:G=>M(G.target.value)}),u.jsx("small",{className:"nc-set-hint",children:v("device-name-hint")})]}),u.jsxs("label",{className:"nc-field",children:[v("node-name-label"),u.jsx("input",{placeholder:Jn(L||T||v("node-name-ph")),value:C.name,disabled:s,onChange:G=>w({...C,name:G.target.value?Jn(G.target.value):""})})]})]}),u.jsxs("div",{className:"nc-set-row nc-invite-actions",children:[u.jsx("button",{type:"button",className:"nc-btn primary",disabled:s||!!h||!P&&!C.ssh.trim(),onClick:ge,children:v("create-pairing-link")}),!P&&u.jsxs("button",{type:"button",className:"nc-btn ghost",disabled:!!h,onClick:()=>U(G=>!G),children:[te?"▾":"▸"," ",v("pair-advanced")]})]}),S&&u.jsxs(u.Fragment,{children:[u.jsx(pE,{value:S.pairingUrl}),u.jsx(fE,{text:S.pairingUrl}),u.jsx("div",{className:"nc-set-info",children:v("invite-next-steps")})]})]}),a&&u.jsx("div",{className:"nc-err",children:a})]})}function gE({token:e,readonly:t}){const[i,s]=R.useState("idle"),[o,a]=R.useState(!1),[c,h]=R.useState(null);R.useEffect(()=>{let p=!1;return Hx().then(y=>{p||s(y)}),()=>{p=!0}},[]);const m=async()=>{h(null),a(!0);try{i==="subscribed"?(await Wx(e),s("idle")):(await $x(e),s("subscribed"))}catch(p){const y=String(p.message||p);y==="push-denied"?(s("denied"),h(v("push-denied"))):y==="push-unsupported"?(s("unsupported"),h(v("push-unsupported"))):h(y)}a(!1)};return i==="unsupported"?u.jsx("div",{className:"nc-set-info",children:v("push-unsupported")}):u.jsxs(u.Fragment,{children:[u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||o||i==="denied",title:t?v("settings-readonly"):"",onClick:m,children:v(i==="subscribed"?"push-disable":"push-enable")}),u.jsx("span",{className:"nc-set-info",children:v(i==="subscribed"?"push-on":i==="denied"?"push-denied":"push-off")})]}),c&&u.jsx("div",{className:"nc-err",children:c})]})}function vE({token:e,settings:t,readonly:i,refresh:s}){const[o,a]=R.useState(null),[c,h]=R.useState(null),[m,p]=R.useState(!1),[y,S]=R.useState(!1),[_,C]=R.useState(null),[w,x]=R.useState(!0);R.useEffect(()=>{C(t&&t.update||null),x(!t||t.autoUpdate!==!1)},[t]);const k=async()=>{a(null),h(null),S(!0);try{const O=await e0(e);h(O.note||"ok"),p(!1)}catch(O){a(String(O.message||O))}S(!1)},b=async()=>{a(null),h(null),S(!0);try{const O=await s0(e);h(`${O.note||"ok"}${O.target?` (${O.target})`:""}`)}catch(O){a(String(O.message||O))}S(!1)},D=async O=>{a(null),h(null),S(!0);try{await Hv(e,{autoUpdate:O}),x(O),s&&await s()}catch(ee){a(String(ee.message||ee))}S(!1)},L=async()=>{a(null),h(null),S(!0);try{C(await o0(e))}catch(O){a(String(O.message||O))}S(!1)},M=async()=>{a(null),h(null),S(!0);try{const O=await a0(e);C(O),h(v("npm-update-restarting"))}catch(O){a(String(O.message||O))}S(!1)},te=()=>{window.confirm(v("composer-clear-confirm"))&&(a(null),IC()?(window.dispatchEvent(new Event(Wd)),h(v("composer-clear-done"))):a(v("composer-clear-failed")))},U=t&&t.service;return u.jsxs("div",{className:"nc-set-tab",children:[t&&u.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,u.jsx("br",{}),U&&U.installed?v("service-installed"):v("service-missing"),U&&U.installed?` · ${U.active?v("service-active"):v("service-inactive")}`:"",U?` · boot ${U.boot?"on":"off"}`:""]}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||y,title:i?v("settings-readonly"):"",onClick:()=>{a(null),h(null),p(!0)},children:v("token-rotate")}),u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||y,title:i?v("settings-readonly"):"",onClick:b,children:v("service-regenerate")})]}),u.jsxs("div",{className:"nc-set-form nc-update-settings",children:[u.jsxs("label",{className:"nc-check",children:[u.jsx("input",{type:"checkbox",checked:w,disabled:i||y||_&&!_.supported,onChange:O=>D(O.target.checked)}),u.jsxs("span",{children:[u.jsx("b",{children:v("npm-auto-update")}),u.jsx("small",{children:v("npm-auto-update-help")})]})]}),_&&u.jsxs("div",{className:"nc-set-info",children:[v("npm-update-current")," v",_.current,_.latest?` · ${v("npm-update-latest")} v${_.latest}`:""," · ",v(`npm-update-${_.supported?_.phase:"unsupported"}`)]}),u.jsxs("div",{className:"nc-set-row",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:i||y||!_||!_.supported,onClick:L,children:v("npm-update-check")}),_&&_.available&&u.jsx("button",{type:"button",className:"nc-btn primary",disabled:i||y||_.phase==="installing",onClick:M,children:v("npm-update-install")})]}),_&&_.lastError&&u.jsx("div",{className:"nc-err",children:_.lastError})]}),u.jsx(gE,{token:e,readonly:i}),u.jsxs("div",{className:"nc-set-form",children:[u.jsx("div",{className:"nc-sheet-label",children:v("composer-clear-data")}),u.jsx("small",{className:"nc-set-hint",children:v("composer-clear-data-help")}),u.jsx("div",{className:"nc-set-row",children:u.jsx("button",{type:"button",className:"nc-btn ghost",onClick:te,children:v("composer-clear-data")})})]}),m&&u.jsxs("div",{className:"nc-set-confirm",children:[u.jsx("div",{children:v("token-rotate-explain")}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:y,onClick:()=>p(!1),children:v("cancel")}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:y,onClick:k,children:v("confirm")})]})]}),c&&u.jsx("div",{className:"nc-set-note",children:c}),o&&u.jsx("div",{className:"nc-err",children:o})]})}function _E({token:e,onClose:t,initialTab:i="nodes",initialLocation:s="",startNewCell:o=!1}){Pi();const[a,c]=R.useState(i),[h,m]=R.useState(null),[p,y]=R.useState([]),[S,_]=R.useState(!1),[C,w]=R.useState(null),x=oh(e),k=R.useCallback(async()=>{try{const b=await zv(e);m(b),w(null)}catch(b){w(String(b.message||b))}try{const b=await Jw(e);y(b.peers||[])}catch(b){w(String(b.message||b))}},[e]);return R.useEffect(()=>{k();const b=setInterval(k,5e3);return()=>clearInterval(b)},[k]),R.useEffect(()=>{let b=!1;return qt("/api/config",e).then(D=>D.json()).then(D=>{b||_(!!D.readonlyDefault)}).catch(()=>{}),()=>{b=!0}},[e]),u.jsx("div",{className:"nc-set-overlay",onClick:t,children:u.jsxs("div",{className:"nc-set-panel",onClick:b=>b.stopPropagation(),children:[u.jsxs("div",{className:"nc-set-head",children:[u.jsx(et,{name:"gear",size:18}),u.jsx("b",{children:v("settings")}),u.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:v("close"),children:u.jsx(et,{name:"x",size:18})})]}),S&&u.jsx("div",{className:"nc-set-readonly",children:v("settings-readonly")}),C&&u.jsx("div",{className:"nc-err",children:C}),u.jsx("div",{className:"nc-set-tabs",children:["nodes","fleet","system"].map(b=>u.jsx("button",{type:"button",className:`nc-set-tabbtn${a===b?" on":""}`,onClick:()=>c(b),children:v(`tab-${b}`)},b))}),u.jsxs("div",{className:"nc-set-body",children:[a==="nodes"&&u.jsx(mE,{token:e,nodes:p,settings:h,readonly:S,refresh:k}),a==="fleet"&&u.jsx(hE,{token:e,readonly:S,startNewCell:o,initialLocation:s,targets:x.map(b=>({route:b.route,label:b.label||b.name,status:b.status}))}),a==="system"&&u.jsx(vE,{token:e,settings:h,readonly:S,refresh:k})]})]})})}function yE({token:e,initialPair:t,deviceDefault:i="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:c}){Pi();const[h,m]=R.useState(t?"pair":"welcome"),[p,y]=R.useState(!1),[S,_]=R.useState(null),C=async()=>{y(!0),_(null);try{await Hv(e,{wizardDone:!0}),a&&a(),c()}catch(w){_(String(w.message||w)),y(!1)}};return u.jsx("div",{className:"nc-wiz-overlay",children:u.jsxs("div",{className:"nc-wiz",children:[u.jsxs("div",{className:"nc-wiz-head",children:[u.jsx("b",{children:v("wizard-title")}),u.jsx("small",{children:v("hydra-simple")})]}),h==="welcome"&&u.jsxs("div",{className:"nc-wiz-body",children:[u.jsx("div",{className:"nc-wiz-done",children:v("local-ready")}),u.jsxs("div",{className:"nc-sheet-actions",children:[u.jsx("button",{className:"nc-btn ghost",disabled:p,onClick:C,children:v("local-only")}),u.jsx("button",{className:"nc-btn primary",disabled:p,onClick:()=>m("pair"),children:v("add-node")})]})]}),h==="pair"&&u.jsxs("div",{className:"nc-wiz-body",children:[u.jsx(Sy,{token:e,initial:t||"",autoStart:!!t,deviceDefault:i,localNodeId:s,localNameDefault:o,onBusyChange:y,onSuccess:async()=>{a&&a(),m("done")}}),u.jsx("div",{className:"nc-sheet-actions",children:u.jsx("button",{className:"nc-btn ghost",disabled:p,onClick:()=>{a&&a(),m("welcome")},children:v("back")})})]}),h==="done"&&u.jsxs("div",{className:"nc-wiz-body",children:[u.jsx("div",{className:"nc-wiz-done",children:v("node-connected")}),u.jsx("div",{className:"nc-sheet-actions",children:u.jsx("button",{className:"nc-btn primary",disabled:p,onClick:C,children:v("finish")})})]}),S&&u.jsx("div",{className:"nc-err",children:S})]})})}function SE(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 wE=6e3,bE=12e3;function kE({n:e,onClose:t}){return u.jsxs("div",{className:`nc-ntf-toast${e.urgency==="high"?" high":""}`,role:"status","aria-live":"polite",children:[u.jsxs("div",{className:"nc-ntf-toast-txt",children:[u.jsx("b",{children:e.title}),e.body&&u.jsx("small",{children:e.body}),e.session&&u.jsx("span",{className:"nc-ntf-from",children:e.session})]}),u.jsx("button",{type:"button",className:"nc-ntf-x",onClick:t,title:v("close"),children:u.jsx(et,{name:"x",size:14})})]})}function CE({ask:e,token:t,onAnswered:i}){const[s,o]=R.useState(""),[a,c]=R.useState(!1),[h,m]=R.useState(null),p=async y=>{const S=String(y||"").trim();if(!(!S||a)){m(null),c(!0);try{await c0(t,e.id,S),i(e.id)}catch(_){m(String(_.message||_))}c(!1)}};return u.jsxs("div",{className:"nc-ask-card",children:[u.jsxs("div",{className:"nc-ask-head",children:[u.jsx("span",{className:"nc-ntf-from",children:e.session}),u.jsxs("code",{className:"nc-ask-id",children:["#",e.id]})]}),u.jsx("div",{className:"nc-ask-q",children:e.question}),Array.isArray(e.options)&&e.options.length>0&&u.jsx("div",{className:"nc-ask-opts",children:e.options.map(y=>u.jsx("button",{type:"button",className:"nc-btn ghost",disabled:a,onClick:()=>p(y),children:y},y))}),u.jsxs("div",{className:"nc-ask-reply",children:[u.jsx("textarea",{rows:2,placeholder:v("ask-reply-ph"),value:s,disabled:a,onChange:y=>o(y.target.value)}),u.jsx("button",{type:"button",className:"nc-btn primary",disabled:a||!s.trim(),onClick:()=>p(s),children:v("send")})]}),h&&u.jsx("div",{className:"nc-err",children:h})]})}function xE({token:e}){Pi();const[t,i]=R.useState([]),[s,o]=R.useState([]),[a,c]=R.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),h=R.useRef(0),m=R.useCallback(S=>{i(_=>_.filter(C=>C.key!==S))},[]),p=R.useCallback(S=>{const _=`t${h.current+=1}`;i(C=>[...C.slice(-3),{...S,key:_}]),setTimeout(()=>m(_),S.urgency==="high"?bE:wE)},[m]);R.useEffect(()=>{if(!e)return;let S=!1;l0(e).then(C=>{S||o(C.asks||[])}).catch(()=>{});const _=SE(e,C=>{C.type==="notify"?p(C):C.type==="ask"&&C.ask&&C.ask.id?o(w=>w.some(x=>x.id===C.ask.id)?w:[...w,C.ask]):C.type==="ask-answered"&&C.id&&o(w=>w.filter(x=>x.id!==C.id))});return()=>{S=!0,_()}},[e,p]);const y=S=>o(_=>_.filter(C=>C.id!==S));return u.jsxs(u.Fragment,{children:[t.length>0&&u.jsx("div",{className:"nc-ntf-toasts",children:t.map(S=>u.jsx(kE,{n:S,onClose:()=>m(S.key)},S.key))}),s.length>0&&!a&&u.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>c(!0),title:v("asks-title"),children:["? ",u.jsx("span",{className:"nc-ask-count",children:s.length})]}),s.length>0&&a&&u.jsxs("div",{className:"nc-ask-panel",children:[u.jsxs("div",{className:"nc-ask-panel-head",children:[u.jsx("b",{children:v("asks-title")}),u.jsx("span",{className:"nc-ask-count",children:s.length}),u.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>c(!1),title:v("close"),children:u.jsx(et,{name:"x",size:16})})]}),u.jsx("div",{className:"nc-ask-panel-body",children:s.map(S=>u.jsx(CE,{ask:S,token:e,onAnswered:y},S.id))})]})]})}const Ju=e=>ln(br(e)).length===0,Zu=e=>Array.isArray(e)?e.join("/"):"";function EE(e){const t=new Set,i=[];for(const s of Array.isArray(e)?e:[])!s||!$i.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 lo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function mr(e,t,i,s=!1,o=!0){const a=t.instanceId;return{...e,id:as(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:Kd(e.layout,a,i)}}function NE(e,t,i,s,o=[]){const[a,c]=R.useState([]),[h,m]=R.useState(!1),[p,y]=R.useState("idle"),[S,_]=R.useState(""),[C,w]=R.useState(""),x=R.useRef([]),k=R.useRef([]),b=R.useRef(""),D=R.useRef(t),L=R.useRef(i),M=R.useRef(!1),te=R.useRef(!0),U=R.useRef(""),O=R.useMemo(()=>EE(o),[o]),ee=O.map(W=>`${W.instanceId}:${Zu(W.route)}:${W.status}:${W.label}`).join("|");k.current=O,L.current=i,D.current=t,x.current=a;const q=R.useCallback(W=>qd(W.layout,b.current,k.current),[]),J=R.useCallback((W,j=!0,A=D.current)=>{const se=Hg(W,qa()),ge=x.current.some(Z=>Z.id===A);x.current=se,c(se);const G=se.find(Z=>Z.id===A);if(j&&G){te.current=!0;const Z=q(G);s(Z),G.local&&rx(G.name,Z)}else ge&&!G&&(te.current=!0,s(os()),_("deck non più condiviso dal nodo owner"));ex(se.filter(Z=>Z.local).map(Z=>Z.name))},[s,q]),Q=R.useCallback(async W=>{let j=await Ba(e);const A=j.decks.find(se=>se.name==="main");if(j.decks.length===1&&A&&A.revision===0&&Ju(A.layout)){const se=ZC(),ge=br(Vd("main")||os());if(!Ju(ge)){const G=Kd(ge,W,[]);j.decks[0]=await Oa(e,"main",G,0,[])}for(const G of se.filter(Z=>Z!=="main"))try{let Z=await _m(e,G,[]);const ue=br(Vd(G)||os());Ju(ue)||(Z=await Oa(e,G,Kd(ue,W,[]),Z.revision,[]))}catch{}j=await Ba(e)}return j},[e]),T=R.useCallback(async({migrate:W=!1}={})=>{const j=await nh(e,[]),A=$i.test(String(j.instanceId||""))?j.instanceId:"";if(!A)throw new Error("instanceId locale non disponibile");b.current=A,w(A);const[se,ge]=await Promise.all([W?Q(A):Ba(e),mm(e,[]).catch(()=>({nodes:[]}))]),G={instanceId:A,route:[],label:"Local"},Z=se.decks.map(de=>mr(de,G,ge.nodes,!0,!0)),ue=x.current;return await Promise.all(k.current.map(async de=>{if(de.status!=="up"){Z.push(...ue.filter(Ce=>!Ce.local&&Ce.ownerId===de.instanceId).map(Ce=>({...Ce,ownerRoute:[...de.route],ownerLabel:de.label,available:!1})));return}try{const[Ce,Re]=await Promise.all([Ba(e,de.route),mm(e,de.route).catch(()=>({nodes:[]}))]);Z.push(...Ce.decks.map(tt=>mr(tt,de,Re.nodes,!1,!0)))}catch{Z.push(...ue.filter(Re=>!Re.local&&Re.ownerId===de.instanceId).map(Re=>({...Re,ownerRoute:[...de.route],ownerLabel:de.label,available:!1})))}})),Z.sort((de,Ce)=>de.local===Ce.local?de.ownerLabel.localeCompare(Ce.ownerLabel)||(de.name==="main"?-1:Ce.name==="main"?1:de.name.localeCompare(Ce.name)):de.local?-1:1),Z},[e,Q,ee]);R.useEffect(()=>{if(!e)return;let W=!1;const j=U.current!==e;return U.current=e,T({migrate:j}).then(A=>{if(W)return;const se=x.current.find(Z=>Z.id===D.current),ge=A.find(Z=>Z.id===D.current),G=ge&&(!se||ge.revision>se.revision||se.available!==ge.available||Zu(se.ownerRoute)!==Zu(ge.ownerRoute));J(A,j||!!G&&!M.current),m(!0),ge&&_("")}).catch(A=>{W||(m(!0),_(String(A.message||A)))}),()=>{W=!0}},[J,T,ee,e]);const N=R.useCallback(async(W=D.current)=>{if(!h||!M.current)return!0;const j=x.current.find(A=>A.id===W);if(!j)return _(`deck inesistente: ${W}`),!1;if(j.available===!1)return _(`nodo owner offline: ${j.ownerLabel}`),!1;y("saving");try{const A=zg(br(L.current),j.ownerId,j.ownerTopology),se=await Oa(e,j.name,A,j.revision,j.ownerRoute),ge=mr(se,lo(j),j.ownerTopology,j.local,!0);return J(x.current.map(G=>G.id===W?ge:G),!1),M.current=!1,y("saved"),_(""),setTimeout(()=>y("idle"),1500),!0}catch(A){if(y("error"),_(String(A.message||A)),A.status===409&&A.data&&A.data.current){const se=x.current.find(G=>G.id===W),ge=mr(A.data.current,lo(se),se.ownerTopology,se.local,!0);J(x.current.map(G=>G.id===W?ge:G),!1)}return!1}},[h,e,J]);return R.useEffect(()=>{if(!h)return;if(te.current){te.current=!1;return}M.current=!0,y("saving");const W=setTimeout(N,650);return()=>clearTimeout(W)},[i,h,N]),R.useEffect(()=>{if(!h)return;const W=setInterval(async()=>{try{const j=await T(),A=x.current.find(G=>G.id===D.current),se=j.find(G=>G.id===D.current),ge=se&&(!A||se.revision>A.revision||A.available!==se.available);J(j,ge&&!M.current)}catch{}},5e3);return()=>clearInterval(W)},[h,T,J]),{decks:a,records:a,localNodeId:C,ready:h,saveState:p,error:S,setError:_,saveNow:N,select:async W=>{if(M.current&&!await N(D.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const j=x.current.find(A=>A.id===W);if(!j)throw new Error(`deck inesistente: ${W}`);return M.current=!1,te.current=!0,q(j)},add:async(W,j=null)=>{const A=j===wn?x.current.find(G=>G.local):j?x.current.find(G=>G.ownerId===j):null;if(!A||A.available===!1)throw new Error("nodo owner non disponibile");const se=await _m(e,W,A.ownerRoute),ge=mr(se,lo(A),A.ownerTopology,A.local,!0);return J([...x.current,ge],!1),ge},rename:async(W,j)=>{const A=x.current.find(ue=>ue.id===W);if(!A)throw new Error("deck inesistente");if(W===D.current&&M.current&&!await N(W))throw new Error(`salvataggio di "${A.name}" fallito: rinomina annullata`);const se=x.current.find(ue=>ue.id===W);if(!se)throw new Error("deck inesistente");const ge=await u0(e,se.name,j,se.revision,se.ownerRoute),G=mr(ge,lo(se),se.ownerTopology,se.local,!0),Z=se.local?wn:se.ownerId;return Nu(ix(qa(),Z,W,G.id)),J(x.current.map(ue=>ue.id===W?G:ue),!1),G},remove:async W=>{const j=x.current.find(se=>se.id===W);if(!j)throw new Error("deck inesistente");W===D.current&&(M.current=!1),await d0(e,j.name,j.revision,j.ownerRoute);const A=j.local?wn:j.ownerId;Nu(nx(qa(),A,W)),J(x.current.filter(se=>se.id!==W),!1)},reorder:(W,j)=>{const A=x.current.find(de=>de.id===W),se=x.current.find(de=>de.id===j);if(!A||!se)return!1;const ge=A.local?wn:A.ownerId,G=se.local?wn:se.ownerId;if(ge!==G)return!1;const Z=x.current.filter(de=>(de.local?wn:de.ownerId)===ge).map(de=>de.id),ue=Nu(tx(qa(),ge,W,j,Z));return J(Hg(x.current,ue),!1),!0},addTileTo:async(W,j)=>{const A=x.current.find(de=>de.id===W);if(!A)throw new Error("deck inesistente");if(A.available===!1)throw new Error(`nodo owner offline: ${A.ownerLabel}`);const se=uy(j,b.current,k.current);if(!se)throw new Error("riferimento sessione non valido");const ge=q(A),G=zg(r_(ge,se),A.ownerId,A.ownerTopology),Z=await Oa(e,A.name,G,A.revision,A.ownerRoute),ue=mr(Z,lo(A),A.ownerTopology,A.local,!0);return J(x.current.map(de=>de.id===W?ue:de),!1),ue},localMainId:as(null,"main"),parseDeckId:wh}}const RE="/sw.js",Yd="nc-sw-update";let Xd=null,Gd=!1,Qd=null,Py=Object.freeze({needed:!1,kind:null,version:""}),Dy=!1;function jy(){typeof window<"u"&&window.dispatchEvent(new Event(Yd))}function My(){const e=Qd||(Gd?{kind:"reload",version:""}:null);Py=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function Ev(){return Py}function PE(e){return typeof window>"u"?()=>{}:(window.addEventListener(Yd,e),()=>window.removeEventListener(Yd,e))}function Eh(e){Gd!==e&&(Gd=e,My(),jy())}function DE(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(Qd);Qd=s,My(),o||jy()}function jE(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&Eh(!0)})}function ME(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{Dy&&location.reload()}),navigator.serviceWorker.register(RE).then(e=>{Xd=e,e.waiting&&Eh(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&jE(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function LE(){Dy=!0,Eh(!1);const e=Xd&&Xd.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function TE({hash:e="",origin:t="",pathname:i="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${i||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const c=new URLSearchParams(o),h=c.get("token")||"",m=c.get("pair")||"",p=m?`${t||""}${i||""}#pair=${m}`:"";return{token:h,pair:p,nextUrl:a}}const Ly=9,Ty=24,Ay="nc_side_w",Nv="nc_side_min",AE=240,Rv="(min-width:1024px) and (pointer:fine)";function IE(){const e=Number(localStorage.getItem(Ay));return e>=180&&e<=480?e:AE}function BE(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=Ly&&e<=Ty?e:13}function OE(){const e={token:"",pair:""};try{const{token:t,pair:i,nextUrl:s}=TE({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 zE(e){try{return br(Vd(e))}catch{return os()}}function Pv(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 FE(){const[e,t]=R.useState(()=>window.matchMedia(Rv).matches);return R.useEffect(()=>{const i=window.matchMedia(Rv),s=o=>t(o.matches);return i.addEventListener("change",s),()=>i.removeEventListener("change",s)},[]),e}function Dv({session:e,node:t,ownerId:i,token:s,readonly:o=!1,onBack:a}){Pi();const[c,h]=R.useState(!1),[m,p]=R.useState(()=>window.matchMedia("(pointer: coarse)").matches),[y,S]=R.useState(null),[_,C]=R.useState(BE),[w,x]=R.useState(""),k=J=>C(Q=>{const T=Math.max(Ly,Math.min(Ty,Q+J));return localStorage.setItem("nc_fontsize",String(T)),T}),b=R.useRef(()=>{}),D=R.useRef(()=>!1),L=R.useRef(()=>{}),M=R.useRef(!1),[te,U]=R.useState(!1),[O,ee]=R.useState(!1),q=()=>{M.current=!M.current,U(M.current)};return R.useEffect(()=>{let J=!0;const Q=t?t.split("/"):[],T=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function N(){let I=null,$=null;try{const ae=await(await qt(`${T}/sessions`,s)).json();Array.isArray(ae.sessions)&&(I=ae.sessions.find(W=>W.name===e))}catch{}try{const F=await Er(s,Q);F.available&&Array.isArray(F.cells)&&($=F.cells.find(ae=>ae.tmuxSession===e))}catch{}if(!J)return;let K="";$?K=`${$.engine}${$.key?`·${$.key}`:""}`:I&&(K=I.attached?`attached · ${Pv(I.activity)}`:I.activity?Pv(I.activity):""),x(K)}N();const P=setInterval(N,4e3);return()=>{J=!1,clearInterval(P)}},[e,t,s]),u.jsxs("div",{className:"nc-app",children:[u.jsxs("header",{className:"nc-bar nc-bar-single",children:[u.jsxs("button",{onClick:a,title:v("sessions"),children:[u.jsx(et,{name:"chevronLeft",size:18}),u.jsx("span",{className:"nc-bar-label",children:v("sessions")})]}),u.jsxs("span",{className:"nc-bar-center",children:[u.jsx("b",{children:t?`${t}:${e}`:e}),w&&u.jsx("small",{className:"nc-bar-sub",children:w})]}),u.jsxs("span",{className:"nc-bar-right",children:[u.jsx("button",{onClick:()=>k(-1),title:v("zoom-out"),children:u.jsx(et,{name:"zoomOut",size:18})}),u.jsx("button",{onClick:()=>k(1),title:v("zoom-in"),children:u.jsx(et,{name:"zoomIn",size:18})}),u.jsx("button",{onClick:()=>p(J=>!J),title:v("composer"),children:u.jsx(et,{name:"keyboard",size:20})}),u.jsx("button",{onClick:()=>h(J=>!J),title:v("files"),children:u.jsx(et,{name:"folder",size:20})})]})]}),u.jsx("div",{className:"nc-termwrap",children:u.jsx(J_,{session:e,node:t,token:s,readonly:o,takeSize:!0,sendRef:b,composerRef:D,actionRef:L,ctrlRef:M,setCtrlArmed:U,onFiles:S,fontSize:_,selectionMode:O,onSelectionModeChange:ee})}),u.jsx(xC,{onKeyboard:()=>p(J=>!J),send:J=>b.current(J),action:J=>L.current(J),ctrlArmed:te,onCtrl:q,selectionMode:O,onSelectionMode:ee}),m&&u.jsx(ly,{submitText:J=>D.current(J),token:s,session:e,node:t,ownerId:i}),c&&u.jsx(Z_,{session:e,node:t,token:s,filesEvent:y,onClose:()=>h(!1)})]})}function HE(){Pi();const[e]=R.useState(OE),[t,i]=R.useState(e.token),[s,o]=R.useState(e.pair||""),a=R.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[c,h]=R.useState(!1),m=FE(),[p]=R.useState(()=>JC(typeof location<"u"?location.pathname:"/")),[y,S]=R.useState(p.id),_=y===as(null,Ri),[C,w]=R.useState(null),x=he=>w(er(he)),[k,b]=R.useState([]),[D,L]=R.useState([]),[M,te]=R.useState([]),[U,O]=R.useState(()=>p.ownerId?os():zE(p.name)),[ee,q]=R.useState(null),[J,Q]=R.useState(null),T=he=>Q(er(he)),N=oh(t,m),P=R.useMemo(()=>(N||[]).filter(he=>he.instanceId).map(he=>({instanceId:he.instanceId,route:he.route,label:he.label,status:he.status})),[N]),I=NE(t,y,U,O,P),$=I.decks;R.useEffect(()=>{const he=new Map;for(const De of N||[]){const qe=(De.route||[De.name]).join("/"),bt=new Set((De.sessions||[]).map(si=>si.name));for(const si of De.cells||[])!si.cell||!si.tmuxSession||si.cell===si.tmuxSession||bt.has(si.cell)||he.set(`${qe}:${si.cell}`,`${qe}:${si.tmuxSession}`)}O(De=>z0(De,he))},[N]),R.useEffect(()=>{I.localNodeId&&O(he=>{const De=qd(he,I.localNodeId,P);return JSON.stringify(De)===JSON.stringify(he)?he:De})},[P,I.localNodeId]);const[K,F]=R.useState(null),[ae,W]=R.useState(null),j=R.useRef(0),[A,se]=R.useState(!1),[ge,G]=R.useState(IE),[Z,ue]=R.useState(!_),[de,Ce]=R.useState(()=>_?localStorage.getItem(Nv)==="1":!0),[Re,tt]=R.useState(!1),[Je,Ee]=R.useState("nodes"),[rt,wt]=R.useState(!1),[Dt,st]=R.useState(""),gt=(he="nodes",De=!1,qe="")=>{Ee(he),wt(De),st(qe),tt(!0)},[ct,at]=R.useState(!1),[le,be]=R.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[Fe,$e]=R.useState(!1);R.useEffect(()=>{try{localStorage.setItem(Ay,String(ge))}catch{}},[ge]),R.useEffect(()=>{try{localStorage.setItem(Nv,de?"1":"")}catch{}},[de]),R.useEffect(()=>{if(!t)return;let he=!1;return Promise.all([zv(t),qt("/api/config",t).then(De=>De.json())]).then(([De,qe])=>{he||(be({deviceDefault:De.deviceName||"",localNodeId:De.nodeId||"",localNameDefault:De.localName||""}),$e(!!qe.readonlyDefault),(De.firstRun===!0&&!qe.readonlyDefault||s)&&at(!0))}).catch(()=>{}),()=>{he=!0}},[t,s]);const pe=R.useCallback(async()=>{try{const De=await(await qt("/api/sessions",t)).json();De.error||b(De.sessions||[])}catch{}try{const he=await Er(t);L(he.available?he.cells||[]:[]),te(he.available?he.capabilities||[]:[])}catch{L([]),te([])}},[t]);R.useEffect(()=>{if(!m)return;pe();const he=setInterval(pe,4e3);return()=>clearInterval(he)},[m,pe]),R.useEffect(()=>{let he=!1;return qt("/api/config",t).then(De=>De.json()).then(De=>{he||DE(De.version,De.uiVersion,"0.8.26")}).catch(()=>{}),()=>{he=!0}},[t]);const Be=new Set([...k.map(he=>he.name),...N.flatMap(he=>he.sessions.map(De=>De.key))]),Te=ln(U),Y=he=>O(De=>{const qe=uy(he,I.localNodeId,P)||he,bt=r_(De,qe);return bt===De&&ln(De).length>=9&&I.setError(v("grid-full")),bt}),ne=async(he,De=[])=>{try{await ih(t,he,De)}catch{return}const qe=De.length?`${De.join("/")}:${he}`:he;O(bt=>ll(bt,qe)),pe()},z=async(he,De,qe=[])=>{try{await Bv(t,he,De,qe)}catch{return}pe()},H=async(he,De,qe=[])=>{await Av(t,{cell:he,enabled:!!De},qe),pe()},re=async he=>{if(!K)return;const{cell:De}=K,qe=Array.isArray(K.route)?K.route:[];he.action==="up"?await eh(t,{cell:De,boot:!!he.boot,...he.engine?{engine:he.engine}:{},...he.model!==void 0?{model:he.model}:{},...he.permissionPolicy?{permissionPolicy:he.permissionPolicy}:{}},qe):await il(t,{cell:De,boot:!!he.boot},qe);const bt=he.action==="up"?!!he.boot:he.boot?!1:!!K.boot;W({id:++j.current,cell:De,route:qe,enabled:bt}),pe()},me=R.useCallback(he=>{W(De=>(De==null?void 0:De.id)===he?null:De)},[]),Ae=async he=>{if(!(!(he!=null&&he.direct)||A)){se(!0);try{await rh(t,he.name,he.tunnelStatus==="up"?"down":"up")}finally{se(!1)}}},Se=async(he,De)=>{const qe=String(De||"").trim();return!(he!=null&&he.direct)||!vo(qe)?!1:(await $v(t,he.name,qe),!0)},xe=he=>{const De=$.find(qe=>qe.id===he);if(!De)return!1;try{const qe=window.open(Eu(De,t),"_blank");return qe&&(qe.opener=null),!!qe}catch{return!1}},He=async he=>{if(!he||he===y)return;const De=await I.select(he),qe=I.records.find(bt=>bt.id===he);S(he),O(De),q(null),Q(null);try{history.replaceState(null,"",Eu(qe||he,null))}catch{}},Zt=async(he,De)=>{const qe=await I.add(he,De);await He(qe.id)},Xe=async(he,De)=>{const qe=await I.rename(he,De);if(he===y){S(qe.id),O(qd(qe.layout,I.localNodeId,P)),q(null),Q(null);try{history.replaceState(null,"",Eu(qe,null))}catch{}}},Di=async he=>{await I.remove(he),he===y&&await He(I.localMainId)},Et=async(he,De)=>{!De||De===y||(await I.addTileTo(De,he),O(qe=>ll(qe,he)))};if(!t)return u.jsxs("div",{className:"nc-auth",children:[u.jsx("p",{children:v("auth-prompt")}),u.jsx("input",{onChange:he=>i(he.target.value.trim()),placeholder:"token"}),u.jsxs("label",{children:[u.jsx("input",{type:"checkbox",checked:c,onChange:he=>h(he.target.checked)})," ",v("remember-device")]}),u.jsx("button",{onClick:()=>{(c?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Ve=u.jsxs(u.Fragment,{children:[Re&&u.jsx(_E,{token:t,initialTab:Je,initialLocation:Dt,startNewCell:rt,onClose:()=>{tt(!1),wt(!1),st("")}}),ct&&u.jsx(yE,{token:t,initialPair:s,...le,onPairDone:a,onDone:()=>at(!1)}),u.jsx(xE,{token:t})]});if(!m)return C?u.jsxs(u.Fragment,{children:[u.jsx(Dv,{session:C.session,node:C.node,ownerId:C.ownerId,token:t,readonly:Fe,onBack:()=>w(null)}),Ve]}):u.jsxs(u.Fragment,{children:[u.jsx(eb,{onPick:x,token:t,onSettings:gt}),Ve]});const ji=_||!Z;return u.jsxs("div",{className:"nc-workspace",children:[ji&&u.jsx($C,{sessions:k,cells:D,activeSessions:Te,nodeGroups:N,fleetCapabilities:M,bootSettlement:ae,onBootSettlementApplied:me,localNodeId:I.localNodeId,onPick:T,onAddTile:Y,onPower:F,onBoot:H,onBootError:he=>I.setError(String((he==null?void 0:he.message)||he)),onNodePower:Ae,onNodeRename:Se,onKill:ne,onVisibility:z,onNew:()=>gt("fleet",!0),onSettings:gt,width:ge,collapsed:de,onResize:G,onToggleCollapse:()=>Ce(he=>!he)}),u.jsxs("div",{className:"nc-workspace-main",children:[u.jsx(ox,{decks:$,currentDeck:y,onCreate:Zt,onRename:Xe,onDelete:Di,onReorder:I.reorder,onOpenWindow:xe,onNavigate:He,saveState:I.saveState,error:I.error,sidebarVisible:ji,onToggleSidebar:_?null:()=>ue(he=>!he)}),u.jsx(VC,{layout:U,onLayoutChange:O,token:t,readonly:Fe,sessionsAlive:Be,focusSession:ee,onFocus:q,onOpenSingle:T,decks:$,currentDeck:y,onSendToDeck:Et})]}),J&&u.jsx("div",{className:"nc-single-overlay",children:u.jsx(Dv,{session:J.session,node:J.node,ownerId:J.ownerId,token:t,readonly:Fe,onBack:()=>Q(null)})}),K&&u.jsx(nl,{cell:K,token:t,route:Array.isArray(K.route)?K.route:[],onConfirm:re,onClose:()=>F(null)}),Ve]})}function $E(){Pi();const e=R.useSyncExternalStore(PE,Ev,Ev);if(!e.needed)return null;const t=e.kind==="install"?v("install-version-mismatch").replace("{v}",e.version):v("update-available").replace("{v}",e.version).replace(" "," ").trim();return u.jsxs("div",{className:"nc-update",role:"status","aria-live":"polite",children:[u.jsx("span",{className:"nc-update-msg",children:t}),e.kind==="reload"&&u.jsx("button",{className:"nc-update-btn",onClick:LE,children:v("reload")})]})}ME();Fw.createRoot(document.getElementById("root")).render(u.jsxs(Lw.StrictMode,{children:[u.jsx(HE,{}),u.jsx($E,{})]}));
@@ -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-Dov6E_p3.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-DkIPrmX6.js"></script>
15
15
  <link rel="stylesheet" crossorigin href="/assets/index-DqG-mDnV.css">
16
16
  </head>
17
17
  <body>
@@ -1 +1 @@
1
- {"version":"0.8.25"}
1
+ {"version":"0.8.26"}
package/lib/cli/doctor.js CHANGED
@@ -186,6 +186,39 @@ function checkTokenPerms(tokenPath) {
186
186
  }
187
187
  }
188
188
 
189
+ // Check MCP identity (P0). NON-FAILING per costrutto: `ok` è sempre true, così
190
+ // chi usa NexusCrew solo come PWA (nessuna integrazione MCP) non vede mai il
191
+ // doctor andare in FAIL solo per env MCP assente. Il check osserva SOLO la
192
+ // presence delle env var di identità nel processo che lancia `doctor` (nessuna
193
+ // lettura di ~/.codex/config.toml, cache private o config MCP).
194
+ //
195
+ // LIMITE DOCUMENTATO (P0): `doctor` non gira dentro il server MCP stdio e non può
196
+ // distinguere in modo portatile "MCP configurato" da "PWA-only". Il WARN è quindi
197
+ // conservativo e informativo: segnala l'assenza di identità osservabile a chi sta
198
+ // configurando l'MCP, senza rompere l'utente PWA-only (per il quale ok resta true).
199
+ function checkMcpIdentity(env) {
200
+ const e = env || process.env;
201
+ const hasTmux = !!(typeof e.TMUX === 'string' && e.TMUX);
202
+ const hasSession = !!(typeof e.NEXUSCREW_MCP_SESSION === 'string' && e.NEXUSCREW_MCP_SESSION.trim());
203
+ if (hasTmux || hasSession) {
204
+ const src = [];
205
+ if (hasTmux) src.push('TMUX');
206
+ if (hasSession) src.push('NEXUSCREW_MCP_SESSION');
207
+ return {
208
+ name: 'MCP identity env',
209
+ ok: true,
210
+ detail: `identita osservabile (${src.join('+')})`,
211
+ };
212
+ }
213
+ return {
214
+ name: 'MCP identity env',
215
+ ok: true, // mai FAIL: PWA-only senza MCP configurato non deve rompere il doctor
216
+ warn: true,
217
+ detail: 'nessuna identita MCP osservabile (TMUX/NEXUSCREW_MCP_SESSION assenti nel processo doctor); '
218
+ + 'se integri l\'MCP stdio allowlista i nomi (codex-vl mcp add --env-var) — PWA-only: ignorabile',
219
+ };
220
+ }
221
+
189
222
  function checkFleetDefinitions(home, fleetDefsPath, enabled = true) {
190
223
  const target = fleetDefsPath || path.join(home, '.nexuscrew', 'fleet.json');
191
224
  if (!enabled) {
@@ -241,6 +274,7 @@ function doctor(opts = {}) {
241
274
  checkSshClient(existsImpl),
242
275
  checkAutossh(existsImpl),
243
276
  checkSshPermitlisten(opts.sshVersion),
277
+ checkMcpIdentity(opts.env),
244
278
  ];
245
279
 
246
280
  for (const c of checks) {
@@ -258,4 +292,5 @@ module.exports = {
258
292
  checkFleetDefinitions,
259
293
  checkTmuxSurvival, checkUserLinger,
260
294
  checkSshClient, checkAutossh, checkSshPermitlisten,
295
+ checkMcpIdentity,
261
296
  };
package/lib/mcp/server.js CHANGED
@@ -25,7 +25,7 @@ const { loadConfig } = require('../config.js');
25
25
  const { readTokenSafe } = require('../auth/token.js');
26
26
  const { isValidSession } = require('../files/store.js');
27
27
  const VERSION = require('../../package.json').version;
28
- const { TOOLS } = require('./tools.js');
28
+ const { TOOLS, IDENTITY_CODE, IDENTITY_REMEDIATION } = require('./tools.js');
29
29
  const cells = require('./cells.js');
30
30
 
31
31
  // Versione protocollo di fallback se il client non ne dichiara una valida.
@@ -39,27 +39,83 @@ const METHOD_NOT_FOUND = -32601;
39
39
  const INVALID_PARAMS = -32602;
40
40
 
41
41
  // --- identita' cella mittente ------------------------------------------------
42
- // Ordine (design §1): $TMUX presente -> `tmux display-message -p '#S'` (nome
43
- // sessione reale); fallback env NEXUSCREW_MCP_SESSION; altrimenti null (i tool
44
- // che RICHIEDONO la sessione falliscono con errore chiaro, nc_notify degrada
45
- // a sender sconosciuto). execFile argv diretto: mai shell.
46
- function resolveSession({ env, tmuxBin, execFileImpl }) {
47
- const fallback = () => {
48
- const s = env.NEXUSCREW_MCP_SESSION;
49
- return (typeof s === 'string' && isValidSession(s.trim())) ? s.trim() : null;
42
+ // Ordine (design §1, INVARIATO): $TMUX presente -> `tmux display-message -p '#S'`
43
+ // (nome sessione reale); se fallisce/invalida -> fallback env NEXUSCREW_MCP_SESSION;
44
+ // altrimenti null. I tool che RICHIEDONO la sessione restano fail-closed.
45
+ // execFile argv diretto: mai shell.
46
+ //
47
+ // `resolveIdentity` rende OSSERVABILE la sorgente della risoluzione (P0):
48
+ // ritorna { session, source, code, envPresence, requiredEnvVars, remediation }
49
+ // senza cambiare la precedenza e senza esporre valori/segreti. `resolveSession`
50
+ // resta il wrapper pubblico Promise<string|null> invariato (compatibilita').
51
+ const IDENTITY_REQUIRED_ENV_VARS = Object.freeze(['TMUX', 'TMUX_PANE', 'NEXUSCREW_MCP_SESSION']);
52
+
53
+ function envPresenceOf(env) {
54
+ return {
55
+ TMUX: !!env.TMUX,
56
+ TMUX_PANE: !!env.TMUX_PANE,
57
+ NEXUSCREW_MCP_SESSION: !!(typeof env.NEXUSCREW_MCP_SESSION === 'string' && env.NEXUSCREW_MCP_SESSION.trim()),
58
+ };
59
+ }
60
+
61
+ function resolveIdentity({ env, tmuxBin, execFileImpl }) {
62
+ const e = env || {};
63
+ const envPresence = envPresenceOf(e);
64
+ const fallbackRaw = e.NEXUSCREW_MCP_SESSION;
65
+ const fallbackPresent = typeof fallbackRaw === 'string' && fallbackRaw.trim().length > 0;
66
+ const tmuxPresent = !!e.TMUX;
67
+
68
+ // Prova il fallback NEXUSCREW_MCP_SESSION: ritorna la sessione normalizzata se
69
+ // valida, `false` se presente ma invalida, `null` se assente.
70
+ const tryFallback = () => {
71
+ if (!fallbackPresent) return null;
72
+ const s = fallbackRaw.trim();
73
+ return isValidSession(s) ? s : false;
50
74
  };
51
- if (!env.TMUX) return Promise.resolve(fallback());
75
+
76
+ // code quando NON identificati: INVALID se c'e' un segnale di identita'
77
+ // (TMUX o NEXUSCREW_MCP_SESSION presente), MISSING altrimenti.
78
+ const codeWhenMissing = () => ((tmuxPresent || fallbackPresent)
79
+ ? IDENTITY_CODE.INVALID : IDENTITY_CODE.MISSING);
80
+
81
+ const ok = (session, source) => ({
82
+ session, source, code: IDENTITY_CODE.OK,
83
+ envPresence, requiredEnvVars: IDENTITY_REQUIRED_ENV_VARS, remediation: IDENTITY_REMEDIATION,
84
+ });
85
+ const missing = () => ({
86
+ session: null, source: 'missing', code: codeWhenMissing(),
87
+ envPresence, requiredEnvVars: IDENTITY_REQUIRED_ENV_VARS, remediation: IDENTITY_REMEDIATION,
88
+ });
89
+
52
90
  return new Promise((resolve) => {
91
+ if (!tmuxPresent) {
92
+ const fb = tryFallback();
93
+ return resolve(typeof fb === 'string' ? ok(fb, 'NEXUSCREW_MCP_SESSION') : missing());
94
+ }
53
95
  try {
54
96
  execFileImpl(tmuxBin, ['display-message', '-p', '#S'], { timeout: 3000 }, (err, stdout) => {
55
- if (err) return resolve(fallback());
56
- const name = String(stdout || '').trim();
57
- resolve(isValidSession(name) ? name : fallback());
97
+ if (!err) {
98
+ const name = String(stdout || '').trim();
99
+ if (isValidSession(name)) return resolve(ok(name, 'tmux'));
100
+ }
101
+ // tmux fallito/invalido -> precedenza preservata: fallback env.
102
+ const fb = tryFallback();
103
+ resolve(typeof fb === 'string' ? ok(fb, 'NEXUSCREW_MCP_SESSION') : missing());
58
104
  });
59
- } catch (_) { resolve(fallback()); }
105
+ } catch (_) {
106
+ const fb = tryFallback();
107
+ resolve(typeof fb === 'string' ? ok(fb, 'NEXUSCREW_MCP_SESSION') : missing());
108
+ }
60
109
  });
61
110
  }
62
111
 
112
+ // Wrapper pubblico STORICO: stessi parametri, stesso return Promise<string|null>.
113
+ // Mantiene i test esistenti e ogni chiamante esterno che dipende solo dal nome
114
+ // della sessione (o null). La diagnostica source/code vive in resolveIdentity.
115
+ function resolveSession(opts) {
116
+ return resolveIdentity(opts).then((i) => i.session);
117
+ }
118
+
63
119
  // --- server --------------------------------------------------------------------
64
120
  function createMcpServer(opts = {}) {
65
121
  const input = opts.input || process.stdin;
@@ -75,11 +131,15 @@ function createMcpServer(opts = {}) {
75
131
  const baseUrl = `http://127.0.0.1:${cfg.port}`;
76
132
 
77
133
  // Identita' risolta una volta e cacheata (la sessione tmux non cambia a runtime).
78
- let sessionP = null;
79
- const session = () => {
80
- if (!sessionP) sessionP = resolveSession({ env, tmuxBin: cfg.tmuxBin || 'tmux', execFileImpl });
81
- return sessionP;
134
+ // Una sola risoluzione condivisa: `identity()` per la diagnostica completa
135
+ // (source/code/presence), `session()` estrae solo il nome per gli handler
136
+ // storici (compatibilita'). Nessuna API/token coinvolta qui.
137
+ let identityP = null;
138
+ const identity = () => {
139
+ if (!identityP) identityP = resolveIdentity({ env, tmuxBin: cfg.tmuxBin || 'tmux', execFileImpl });
140
+ return identityP;
82
141
  };
142
+ const session = () => identity().then((i) => i.session);
83
143
 
84
144
  // Token letto ad OGNI chiamata (rotazione-friendly), MAI incluso negli errori.
85
145
  function readToken() {
@@ -113,6 +173,7 @@ function createMcpServer(opts = {}) {
113
173
 
114
174
  const ctx = {
115
175
  session,
176
+ identity,
116
177
  api,
117
178
  home: () => env.HOME || os.homedir(),
118
179
  fileExists: (p) => { try { return require('node:fs').statSync(p).isFile(); } catch (_) { return false; } },
@@ -252,7 +313,7 @@ function startMcp(opts = {}) {
252
313
  }
253
314
 
254
315
  module.exports = {
255
- createMcpServer, startMcp, resolveSession, TOOLS,
316
+ createMcpServer, startMcp, resolveSession, resolveIdentity, TOOLS,
256
317
  parseCellTarget: cells.parseCellTarget,
257
318
  normalizeCellPayload: cells.normalizeCellPayload,
258
319
  readCellDirectory: cells.readCellDirectory,
package/lib/mcp/tools.js CHANGED
@@ -27,10 +27,33 @@ function argString(args, key, { required = false, max = 4096 } = {}) {
27
27
  return v;
28
28
  }
29
29
 
30
- function requireSession(session, tool) {
30
+ // Codici stabili di identita' MCP (contratto P0). Valori EXACT, non sensibili,
31
+ // usati sia nella diagnostica read-only (`nc_identity`) sia nel messaggio umano
32
+ // dei tool identity-gated (isError=true preservato dal server). Non espongono
33
+ // valori/env/sessioni: solo la categoria del problema.
34
+ const IDENTITY_CODE = Object.freeze({
35
+ OK: 'OK',
36
+ MISSING: 'NEXUSCREW_MCP_IDENTITY_MISSING',
37
+ INVALID: 'NEXUSCREW_MCP_IDENTITY_INVALID',
38
+ });
39
+
40
+ // Remediation senza segreti/valori: nomi soltanto, compatibile con
41
+ // `codex-vl mcp add --env-var` (allowlist di nomi, nessun valore persistito).
42
+ const IDENTITY_REMEDIATION =
43
+ 'Nei client che ripuliscono l\'ambiente, allowlista i nomi delle variabili di identita nel server MCP stdio '
44
+ + '(codex-vl mcp add ... --env-var NEXUSCREW_MCP_SESSION --env-var TMUX --env-var TMUX_PANE) '
45
+ + 'oppure assicurati che il client MCP inoltri il contesto tmux al processo child: nessun valore '
46
+ + 'viene copiato nella CLI o nel file di configurazione.';
47
+
48
+ function requireSession(session, tool, code = IDENTITY_CODE.MISSING) {
31
49
  if (session) return session;
50
+ const stableCode = code === IDENTITY_CODE.INVALID ? IDENTITY_CODE.INVALID : IDENTITY_CODE.MISSING;
51
+ // Errore umano per il modello: messaggio chiaro + codice stabile fra parentesi
52
+ // quadre. isError=true e' impostato dal server (toolsCall); qui si propaga
53
+ // solo il testo. La regex storica /NEXUSCREW_MCP_SESSION/ resta soddisfatta.
32
54
  throw new Error(
33
- `${tool}: sessione tmux non identificata — serve $TMUX (dentro tmux) o NEXUSCREW_MCP_SESSION`,
55
+ `${tool}: sessione tmux non identificata — serve $TMUX (dentro tmux) o `
56
+ + `NEXUSCREW_MCP_SESSION [${stableCode}]`,
34
57
  );
35
58
  }
36
59
 
@@ -79,7 +102,8 @@ const TOOLS = [
79
102
  if (args.options !== undefined && !Array.isArray(args.options)) {
80
103
  throw new Error('options deve essere un array di stringhe');
81
104
  }
82
- const session = requireSession(await ctx.session(), 'nc_ask');
105
+ const identity = await ctx.identity();
106
+ const session = requireSession(identity.session, 'nc_ask', identity.code);
83
107
  const j = await ctx.api('POST', '/api/asks', {
84
108
  question, ...(args.options ? { options: args.options } : {}), session,
85
109
  });
@@ -109,7 +133,8 @@ const TOOLS = [
109
133
  if (!ctx.fileExists(p)) throw new Error(`file inesistente: ${p}`);
110
134
  const home = ctx.home();
111
135
  if (p !== home && !p.startsWith(home + path.sep)) throw new Error('path fuori da HOME');
112
- const session = requireSession(await ctx.session(), 'nc_send_file');
136
+ const identity = await ctx.identity();
137
+ const session = requireSession(identity.session, 'nc_send_file', identity.code);
113
138
  const j = await ctx.api('POST', '/api/files/outbox', {
114
139
  session, path: p, ...(caption ? { caption } : {}),
115
140
  });
@@ -145,7 +170,8 @@ const TOOLS = [
145
170
  inputSchema: { type: 'object', properties: {} },
146
171
  annotations: { readOnlyHint: true },
147
172
  async handler(_args, ctx) {
148
- const tmuxSession = requireSession(await ctx.session(), 'nc_deck');
173
+ const identity = await ctx.identity();
174
+ const tmuxSession = requireSession(identity.session, 'nc_deck', identity.code);
149
175
  const [config, topology, localDecks] = await Promise.all([
150
176
  ctx.api('GET', '/api/config'), ctx.api('GET', '/api/topology'), ctx.api('GET', '/api/decks'),
151
177
  ]);
@@ -257,7 +283,8 @@ const TOOLS = [
257
283
  if (code === 9 || code === 10 || code === 13) continue;
258
284
  if (code < 32 || code === 127) throw new Error('message contiene caratteri di controllo non ammessi');
259
285
  }
260
- const callerSession = requireSession(await ctx.session(), 'nc_send_cell');
286
+ const identity = await ctx.identity();
287
+ const callerSession = requireSession(identity.session, 'nc_send_cell', identity.code);
261
288
  const directory = await readCellDirectory(ctx, callerSession);
262
289
  const sender = directory.cells.find((cell) => cell.self && cell.active);
263
290
  if (!sender) throw new Error('nc_send_cell: la sessione chiamante non e\' una cella Fleet attiva locale');
@@ -290,11 +317,33 @@ const TOOLS = [
290
317
  inputSchema: { type: 'object', properties: {} },
291
318
  annotations: { readOnlyHint: true },
292
319
  async handler(_args, ctx) {
293
- const session = requireSession(await ctx.session(), 'nc_inbox');
320
+ const identity = await ctx.identity();
321
+ const session = requireSession(identity.session, 'nc_inbox', identity.code);
294
322
  const j = await ctx.api('GET', `/api/files?session=${encodeURIComponent(session)}`);
295
323
  return { inbox: Array.isArray(j.inbox) ? j.inbox : [] };
296
324
  },
297
325
  },
326
+ {
327
+ name: 'nc_identity',
328
+ description: 'Diagnostica read-only dell\'identita\' del chiamante MCP. Utilizzabile anche senza sessione tmux e senza token: restituisce SOLO dati non sensibili (presence delle env var, sorgente della risoluzione, codice stabile). Non chiama API HTTP e non legge il token. Usa questo tool quando gli altri tool nc_* falliscono con NEXUSCREW_MCP_IDENTITY_* per capire cosa manca.',
329
+ inputSchema: { type: 'object', properties: {} },
330
+ annotations: { readOnlyHint: true },
331
+ async handler(_args, ctx) {
332
+ const id = await ctx.identity();
333
+ // Output bounded e non sensibile: nessun valore/env, solo presence e codice.
334
+ // `session` solo se validata; `source` sempre fra i tre valori ammessi.
335
+ const out = {
336
+ identified: !!id.session,
337
+ source: id.source,
338
+ envPresence: id.envPresence,
339
+ requiredEnvVars: id.requiredEnvVars,
340
+ code: id.code,
341
+ remediation: id.remediation,
342
+ };
343
+ if (id.session) out.session = id.session;
344
+ return out;
345
+ },
346
+ },
298
347
  ];
299
348
 
300
- module.exports = { TOOLS, argString, requireSession };
349
+ module.exports = { TOOLS, argString, requireSession, IDENTITY_CODE, IDENTITY_REMEDIATION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmmbuto/nexuscrew",
3
- "version": "0.8.25",
3
+ "version": "0.8.26",
4
4
  "description": "Faithful browser tmux client — attach to live sessions over a real PTY, localhost-only, mobile-easy",
5
5
  "main": "lib/server.js",
6
6
  "bin": {