@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 +20 -0
- package/README.md +27 -4
- package/frontend/dist/assets/{index-Dov6E_p3.js → index-DkIPrmX6.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/cli/doctor.js +35 -0
- package/lib/mcp/server.js +80 -19
- package/lib/mcp/tools.js +57 -8
- package/package.json +1 -1
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
|
|
361
|
-
|
|
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.
|
|
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,{})]}));
|
package/frontend/dist/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-title" content="NexusCrew" />
|
|
12
12
|
<link rel="manifest" href="/manifest.json" />
|
|
13
13
|
<title>NexusCrew</title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-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.
|
|
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'`
|
|
43
|
-
// sessione reale); fallback env NEXUSCREW_MCP_SESSION;
|
|
44
|
-
// che RICHIEDONO la sessione
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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)
|
|
56
|
-
|
|
57
|
-
|
|
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 (_) {
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 };
|