@mmmbuto/nexuscrew 0.8.32 → 0.8.34
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 +49 -0
- package/MCP_COMPANIONS.md +67 -0
- package/README.md +42 -5
- package/frontend/dist/assets/{index-Dw90VJRY.js → index-cNTOIj7e.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/cli/commands.js +109 -4
- package/lib/cli/pidfile.js +39 -14
- package/lib/fleet/launch.js +16 -2
- package/lib/mcp/server.js +13 -0
- package/lib/nodes/tunnel.js +6 -1
- package/mcp-companions.json +103 -0
- package/package.json +3 -1
- package/skills/crew/SKILL.md +89 -0
- package/skills/crew/agents/openai.yaml +4 -0
- package/skills/fill-forms/SKILL.md +154 -0
- package/skills/fill-forms/agents/openai.yaml +4 -0
- package/skills/fill-forms/references/overlay-technique.md +99 -0
- package/skills/fill-forms/requirements.txt +4 -0
- package/skills/fill-forms/scripts/dump_docx.py +70 -0
- package/skills/fill-forms/scripts/fill_docx.py +207 -0
- package/skills/fill-forms/scripts/fill_pdf.py +424 -0
- package/skills/fill-forms/scripts/inspect_pdf.py +188 -0
- package/skills/fill-forms/scripts/prepare_signature.py +171 -0
- package/skills/mail-assistant/SKILL.md +71 -0
- package/skills/mail-assistant/agents/openai.yaml +4 -0
- package/skills/memory/SKILL.md +81 -0
- package/skills/memory/agents/openai.yaml +4 -0
- package/skills/nexuscrew-agent/SKILL.md +20 -0
- package/skills/vl-msa/SKILL.md +68 -0
- package/skills/vl-msa/agents/openai.yaml +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.8.34 — 2026-07-23 — "Local Forms"
|
|
6
|
+
|
|
7
|
+
- Adds the portable `fill-forms` skill for locally inspecting, filling and
|
|
8
|
+
visually validating PDF and DOCX forms. It supports named AcroForm fields,
|
|
9
|
+
coordinate overlays, checkboxes, character boxes, optional user-provided
|
|
10
|
+
fonts and explicitly authorised signature images.
|
|
11
|
+
- Ships bounded Python helpers for PDF inspection, atomic PDF output, DOCX
|
|
12
|
+
placeholder replacement and signature-background cleanup. They preserve the
|
|
13
|
+
blank source, refuse silent field omissions and existing-output replacement,
|
|
14
|
+
install no dependency automatically and make no network request.
|
|
15
|
+
- Keeps canonical instructions in English while selecting the user-facing
|
|
16
|
+
language from the explicit preference, current request or reliable locale.
|
|
17
|
+
Document wording and field values remain unchanged unless translation is
|
|
18
|
+
explicitly requested. Filling never implies authorisation to sign, send or
|
|
19
|
+
submit.
|
|
20
|
+
- Self-heals stale tunnel pidfiles after Android reuses a PID under another
|
|
21
|
+
app UID. An `EPERM` ownership probe is no longer accepted as a live
|
|
22
|
+
NexusCrew supervisor: the foreign process is never signalled, the stale
|
|
23
|
+
pidfile is removed and the configured OpenSSH tunnel starts normally.
|
|
24
|
+
- Repairs legacy Termux:Boot definitions automatically on service start and
|
|
25
|
+
restart. Both the HTTP runtime and Fleet companion enter the stable user
|
|
26
|
+
home before they can create tmux, then installed scripts are regenerated
|
|
27
|
+
atomically without changing the configured port, token, pairing state or
|
|
28
|
+
the user's boot opt-in.
|
|
29
|
+
|
|
30
|
+
## 0.8.33 — 2026-07-23 — "Cold Boot"
|
|
31
|
+
|
|
32
|
+
- Restores Fleet on a true cold boot where no tmux server or default socket
|
|
33
|
+
exists yet. The expected `error connecting to … (No such file or directory)`
|
|
34
|
+
response is now treated as an empty tmux inventory, allowing the boot
|
|
35
|
+
companion to create the shared server and start every `boot:true` cell. The
|
|
36
|
+
migration inventory command uses the POSIX `C` message locale while
|
|
37
|
+
preserving UTF-8 character handling, so this classification remains
|
|
38
|
+
deterministic on localized Linux, macOS and Termux hosts.
|
|
39
|
+
- Keeps migration fail-closed for unexpected tmux failures such as permission
|
|
40
|
+
errors, malformed output, ambiguous legacy sessions and rename collisions.
|
|
41
|
+
The regression reproduces the exact stderr observed after reboot and verifies
|
|
42
|
+
both provider availability and safe persistence of dotted cell identities.
|
|
43
|
+
- Adds an optional MCP companion guide and machine-readable catalog for
|
|
44
|
+
structured Memory, searchable MSA knowledge, bounded Crew delegation and
|
|
45
|
+
Mail. Agents discover existing tools first, recommend a companion only for a
|
|
46
|
+
requested missing capability and never install or configure it without
|
|
47
|
+
consent.
|
|
48
|
+
- Ships generic public `memory`, `vl-msa`, `crew` and `mail-assistant` skills.
|
|
49
|
+
Their canonical instructions are in English and user-facing output follows
|
|
50
|
+
the user's explicit preference or request language; Mail reply drafts follow
|
|
51
|
+
the thread language unless overridden. No account, identity, folder,
|
|
52
|
+
credential or machine-specific path is bundled.
|
|
53
|
+
|
|
5
54
|
## 0.8.32 — 2026-07-22 — "Touch History"
|
|
6
55
|
|
|
7
56
|
- Restores continuous mobile finger-drag navigation of tmux history in every
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Optional MCP Companions
|
|
2
|
+
|
|
3
|
+
NexusCrew provides the terminal, Fleet, node-routing and operator bridge. It
|
|
4
|
+
does not bundle unrelated data services or copy their credentials. When an AI
|
|
5
|
+
session needs a capability that its current tool set does not expose, the
|
|
6
|
+
following local-first MCP servers can be installed separately.
|
|
7
|
+
|
|
8
|
+
| Need | Companion | Packaged skill | What it adds |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| Durable agent state | [mcp-memory-rs](https://github.com/DioNanos/mcp-memory-rs) | [`memory`](skills/memory/SKILL.md) | Versioned JSON categories, bounded append-only logs, search, history and explicit per-device writes |
|
|
11
|
+
| Searchable long-term knowledge | [mcp-vl-msa-rs](https://github.com/DioNanos/mcp-vl-msa-rs) | [`vl-msa`](skills/vl-msa/SKILL.md) | Persistent document collections, BM25 retrieval, full-source grounding and bounded multi-hop retrieval |
|
|
12
|
+
| Bounded worker delegation | [mcp-crewd-rs](https://github.com/DioNanos/mcp-crewd-rs) | [`crew`](skills/crew/SKILL.md) | Spawned AI cells, task status/results and a controlled inter-cell message bus |
|
|
13
|
+
| Mail access | [mcp-email-rs](https://github.com/DioNanos/mcp-email-rs) | [`mail-assistant`](skills/mail-assistant/SKILL.md) | Direct IMAP search and reading, folders, attachments, drafts and optional SMTP sending |
|
|
14
|
+
|
|
15
|
+
These projects are companions, not mandatory NexusCrew dependencies. Follow
|
|
16
|
+
the installation and client-registration instructions in each repository.
|
|
17
|
+
Installing NexusCrew never installs, starts or configures them automatically.
|
|
18
|
+
|
|
19
|
+
## Choosing the right companion
|
|
20
|
+
|
|
21
|
+
Use **mcp-memory-rs** for compact structured state that an agent updates over
|
|
22
|
+
time: preferences, project status, checkpoints and bounded journals.
|
|
23
|
+
|
|
24
|
+
Use **mcp-vl-msa-rs** for larger source material that must be located and then
|
|
25
|
+
grounded in its original text: documents, notes, research and past
|
|
26
|
+
conversations. Memory and MSA are complementary; neither is a transparent
|
|
27
|
+
replacement for the other.
|
|
28
|
+
|
|
29
|
+
Use **mcp-crewd-rs** when a task needs temporary worker processes, structured
|
|
30
|
+
results or a dedicated agent bus. NexusCrew Fleet manages persistent tmux
|
|
31
|
+
cells and their UI; Crew manages bounded delegated work. They can be used
|
|
32
|
+
together without making either service a hidden dependency of the other.
|
|
33
|
+
|
|
34
|
+
Use **mcp-email-rs** when direct mailbox access is appropriate. Reading and
|
|
35
|
+
searching can be read-only; moving, drafting, deleting and sending are explicit
|
|
36
|
+
mutations.
|
|
37
|
+
|
|
38
|
+
All four packaged skills contain English canonical instructions and select
|
|
39
|
+
their user-facing response language from the user's explicit preference,
|
|
40
|
+
current request or reliable locale, falling back to English. The Mail skill
|
|
41
|
+
uses the email thread's language for reply drafts unless the user overrides it.
|
|
42
|
+
None contains account details, credentials or machine-specific paths.
|
|
43
|
+
|
|
44
|
+
## Guidance for AI clients
|
|
45
|
+
|
|
46
|
+
When handling a request:
|
|
47
|
+
|
|
48
|
+
1. Discover the tools already exposed by the current client.
|
|
49
|
+
2. Use an available tool that covers the request before suggesting another
|
|
50
|
+
installation.
|
|
51
|
+
3. If the required capability is missing, mention the matching optional
|
|
52
|
+
companion once and explain the specific capability it would add.
|
|
53
|
+
4. Ask before installing software, changing MCP client configuration, starting
|
|
54
|
+
a service or requesting credentials.
|
|
55
|
+
5. Never copy credentials into prompts, NexusCrew Fleet definitions, tmux
|
|
56
|
+
commands, logs or the companion catalog.
|
|
57
|
+
6. If installation is declined or unavailable, continue with a safe supported
|
|
58
|
+
fallback or state the limitation plainly.
|
|
59
|
+
|
|
60
|
+
Do not repeatedly advertise companions during unrelated work. A recommendation
|
|
61
|
+
must be driven by a capability the user actually requested.
|
|
62
|
+
|
|
63
|
+
## Machine-readable catalog
|
|
64
|
+
|
|
65
|
+
[`mcp-companions.json`](mcp-companions.json) contains the same mapping in a
|
|
66
|
+
bounded machine-readable form. It is discovery metadata only: it grants no
|
|
67
|
+
permission, executes no installer and contains no credential fields.
|
package/README.md
CHANGED
|
@@ -87,6 +87,12 @@ On Android, `nexuscrew doctor` also verifies the Termux execution bridge used by
|
|
|
87
87
|
builds. NexusCrew carries forward only a validated `libtermux-exec` preload from the active
|
|
88
88
|
Termux prefix; arbitrary loader injection remains excluded from Fleet environments.
|
|
89
89
|
|
|
90
|
+
Package upgrades self-repair installed Termux:Boot scripts before the runtime or Fleet can
|
|
91
|
+
create tmux from a replaceable npm directory. NexusCrew also discards a tunnel pidfile when
|
|
92
|
+
Android has reused its PID under another app UID; it never signals that foreign process and
|
|
93
|
+
restarts only the configured OpenSSH supervisor. These repairs preserve the local port, token,
|
|
94
|
+
pairing data and existing boot on/off choice.
|
|
95
|
+
|
|
90
96
|
The preferred port is `41820`. If another process owns it, NexusCrew selects the next free
|
|
91
97
|
loopback port and records the result.
|
|
92
98
|
|
|
@@ -183,6 +189,15 @@ and Pi can invoke its dependency-free Python CLI directly; Pi is not assumed to
|
|
|
183
189
|
natively. The media skill requires Python 3. Media generation always requires explicit Credit
|
|
184
190
|
consent and never runs during installation, tests, or startup.
|
|
185
191
|
|
|
192
|
+
The package also includes the portable [`fill-forms`](skills/fill-forms/SKILL.md) skill for
|
|
193
|
+
inspecting, filling and visually validating local PDF and DOCX forms. It supports named
|
|
194
|
+
AcroForm fields, coordinate overlays, checkboxes, character boxes and explicitly authorised
|
|
195
|
+
signature images. Its scripts never install dependencies automatically, never overwrite the
|
|
196
|
+
blank source and do not send documents anywhere. Canonical instructions are in English while
|
|
197
|
+
user-facing guidance follows the user's language; field values and document wording are not
|
|
198
|
+
translated without an explicit request. Optional Python dependencies are not bundled and remain
|
|
199
|
+
subject to their own licences.
|
|
200
|
+
|
|
186
201
|
Permission handling is explicit per cell and engine:
|
|
187
202
|
|
|
188
203
|
- Claude engines can use standard permissions or `--dangerously-skip-permissions`.
|
|
@@ -454,6 +469,28 @@ query remote nodes or add commands to the federated directory. The returned comm
|
|
|
454
469
|
and credential-redacted; the failure is a closed `{status, code, phase}` cause rather than raw
|
|
455
470
|
stderr, paths, environment values, prompts or tokens.
|
|
456
471
|
|
|
472
|
+
## Optional MCP companions
|
|
473
|
+
|
|
474
|
+
NexusCrew can work alongside separate local-first MCP servers for durable
|
|
475
|
+
structured memory, searchable document memory, bounded worker delegation and
|
|
476
|
+
mail access. They are optional projects rather than hidden NexusCrew
|
|
477
|
+
dependencies: the AI should discover its existing tools first, recommend a
|
|
478
|
+
companion only for a capability the user actually requested, and ask before
|
|
479
|
+
installing or changing client configuration.
|
|
480
|
+
|
|
481
|
+
See [MCP_COMPANIONS.md](MCP_COMPANIONS.md) for the capability guide and
|
|
482
|
+
[`mcp-companions.json`](mcp-companions.json) for the machine-readable catalog.
|
|
483
|
+
The npm package also includes generic [`memory`](skills/memory/SKILL.md),
|
|
484
|
+
[`vl-msa`](skills/vl-msa/SKILL.md), [`crew`](skills/crew/SKILL.md) and
|
|
485
|
+
[`mail-assistant`](skills/mail-assistant/SKILL.md) skills. Their instructions
|
|
486
|
+
are written in English, while user-facing output follows the user's explicit
|
|
487
|
+
preference or request language; Mail reply drafts follow the email thread by
|
|
488
|
+
default.
|
|
489
|
+
|
|
490
|
+
`fill-forms` is a separate bundled local document skill, not an MCP companion.
|
|
491
|
+
It is documented in [Fleet: cells, engines and providers](#fleet-cells-engines-and-providers)
|
|
492
|
+
and carries no automatic installation or external-service behaviour.
|
|
493
|
+
|
|
457
494
|
## Configuration
|
|
458
495
|
|
|
459
496
|
Runtime state is local to the current user:
|
|
@@ -499,16 +536,16 @@ operator's tmux server.
|
|
|
499
536
|
|
|
500
537
|
## Roadmap
|
|
501
538
|
|
|
502
|
-
The
|
|
503
|
-
|
|
504
|
-
Credentials and execution would remain on the owner node, with owner-qualified tool
|
|
505
|
-
and read/mutate ACLs.
|
|
539
|
+
The current companion catalog is documentation and agent guidance only. A future optional MCP
|
|
540
|
+
gateway could expose one NexusCrew endpoint with explicit, per-tool federation through shared
|
|
541
|
+
nodes. Credentials and execution would remain on the owner node, with owner-qualified tool
|
|
542
|
+
identities and read/mutate ACLs. The gateway is planned work and is **not part of this release**.
|
|
506
543
|
|
|
507
544
|
See [CHANGELOG.md](CHANGELOG.md) for released changes.
|
|
508
545
|
|
|
509
546
|
## Status
|
|
510
547
|
|
|
511
|
-
The current stable release is **v0.8.
|
|
548
|
+
The current stable release is **v0.8.34** on npm and GitHub.
|
|
512
549
|
|
|
513
550
|
## License
|
|
514
551
|
|
|
@@ -90,4 +90,4 @@ Minimum version required to store current data is: `+K+`.
|
|
|
90
90
|
`).filter(o=>o!==""),promptMode:e.promptMode};return e.modelFlag&&(s.model={flag:e.modelFlag,value:e.modelValue||""}),e.promptMode==="flag"&&(s.promptFlag=e.promptFlag),t&&(s.env=Object.fromEntries(e.envRows.filter(o=>!o.remove&&o.key).map(o=>[o.key,o.value]))),s}function Nr({children:e,onClose:t,label:n,error:s=""}){const o=N.useRef(null),a=N.useRef(null),u=N.useRef(t);return u.current=t,N.useEffect(()=>{const h=document.activeElement,p=o.current,f=()=>Array.from((p==null?void 0:p.querySelectorAll('button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [href], [tabindex]:not([tabindex="-1"])'))||[]).filter(w=>w.offsetParent!==null),_=requestAnimationFrame(()=>{var w;return(w=f()[0]||p)==null?void 0:w.focus({preventScroll:!0})}),b=w=>{var C;if(w.key==="Escape"){w.preventDefault(),(C=u.current)==null||C.call(u);return}if(w.key!=="Tab")return;const y=f();if(!y.length){w.preventDefault(),p==null||p.focus();return}const S=y[0],x=y[y.length-1];w.shiftKey&&document.activeElement===S?(w.preventDefault(),x.focus()):!w.shiftKey&&document.activeElement===x&&(w.preventDefault(),S.focus())};return document.addEventListener("keydown",b),()=>{cancelAnimationFrame(_),document.removeEventListener("keydown",b),h&&h.isConnected&&typeof h.focus=="function"&&h.focus({preventScroll:!0})}},[]),N.useEffect(()=>{s&&requestAnimationFrame(()=>{var h;return(h=a.current)==null?void 0:h.scrollIntoView({block:"nearest"})})},[s]),c.jsx("div",{className:"nc-fleet-modal",role:"presentation",onMouseDown:h=>{h.target===h.currentTarget&&t()},children:c.jsxs("div",{ref:o,className:"nc-fleet-modal-dialog",role:"dialog","aria-modal":"true","aria-label":n||g("settings"),tabIndex:-1,children:[e,s&&c.jsx("div",{ref:a,className:"nc-err nc-fleet-modal-error",role:"alert","aria-live":"assertive",children:s})]})})}function fd({token:e,targets:t=[],readonly:n=!1,onPower:s,onImport:o}){const[a,u]=N.useState([]),[h,p]=N.useState(0);N.useEffect(()=>{let C=!0;async function k(){const I=[{route:[],label:g("local")}].concat((t||[]).map(G=>({route:Array.isArray(G.route)?G.route:[],label:G.label||(G.route||[]).join(" › ")}))),T=await Promise.all(I.map(async G=>{const U={...G,available:!1,readonly:!1,capabilities:[],provider:null,cells:[],unmanaged:[],err:""};try{const A=await zr(e,G.route);U.available=!!A.available,U.capabilities=A.capabilities||[],U.provider=A.provider||null,U.cells=(A.cells||[]).map(ee=>({...ee,route:G.route}));try{U.readonly=!!(await bh(e,G.route)).readonlyDefault}catch{}try{const ee=await S_(e,G.route),W=new Set(U.cells.map(Z=>Z.tmuxSession).filter(Boolean));U.unmanaged=(ee.sessions||[]).filter(Z=>Z&&!W.has(Z.name))}catch{}}catch(A){U.err=String(A&&A.message||A)}return U}));C&&u(T)}k();const j=setInterval(k,6e3);return()=>{C=!1,clearInterval(j)}},[e,t,h]);const f=(C,k)=>Array.isArray(C.capabilities)&&C.capabilities.includes(k)&&!n&&!C.readonly,_=()=>p(C=>C+1),b=(C,k)=>{s&&s({...C,route:k})},w=(C,k)=>{s&&s({...C,route:k})},y=async(C,k)=>{try{await wo(e,C.cell,k)}catch{}_()},S=async(C,k)=>{if(window.confirm(g("fleet-remove-cell").replace("{id}",C.cell))){try{await __(e,C.cell,!0,k)}catch{}_()}},x=async(C,k)=>{if(window.confirm(g("terminate-confirm").replace("{name}",C.name))){try{await wh(e,C.name,k)}catch{}_()}};return c.jsxs("div",{className:"nc-fleet-inventory",children:[c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:g("fleet-inventory")}),c.jsx("small",{children:g("fleet-inventory-help")})]}),a.map(C=>{const k=C.route.length?C.route.join("/"):"local";return c.jsxs("div",{className:"nc-fleet-pos",children:[c.jsxs("div",{className:"nc-fleet-pos-title",children:[c.jsx("span",{className:`dot ${C.available?"on":"warn"}`}),c.jsx("b",{children:C.label}),c.jsx("small",{children:C.available?`${C.cells.length} ${g("fleet-cells")} · ${C.unmanaged.length} ${g("fleet-tmux")}`:C.err||g("fleet-not-available")})]}),C.cells.map(j=>c.jsxs("div",{className:"nc-fleet-item nc-fleet-cell",children:[c.jsxs("span",{children:[c.jsx("b",{children:j.cell}),c.jsx("small",{children:`${j.engine||""}${j.key?`·${j.key}`:""}${j.active?"":` · ${g("cell-off")}`}`})]}),c.jsxs("span",{className:"nc-fleet-cell-actions",children:[j.active&&f(C,"down")&&c.jsx("button",{className:"nc-btn ghost",title:g("power-off"),onClick:()=>w(j,C.route),children:g("stop")}),!j.active&&f(C,"up")&&c.jsx("button",{className:"nc-btn ghost",title:g("power-on"),onClick:()=>b(j,C.route),children:g("start")}),f(C,"restart")&&j.active&&c.jsx("button",{className:"nc-btn ghost",title:g("restart"),onClick:()=>y(j,C.route),children:g("restart")}),f(C,"remove")&&c.jsx("button",{className:"nc-btn danger",title:g("delete"),onClick:()=>S(j,C.route),children:"×"})]})]},`${k}:${j.cell}`)),C.unmanaged.map(j=>c.jsxs("div",{className:"nc-fleet-item nc-fleet-unmanaged",children:[c.jsxs("span",{children:[c.jsx("b",{children:j.name}),c.jsx("small",{children:g("fleet-tmux")})]}),c.jsxs("span",{className:"nc-fleet-cell-actions",children:[f(C,"import")&&o&&c.jsx("button",{className:"nc-btn ghost",title:g("import-as-cell"),onClick:()=>o(j,C.route),children:g("import-as-cell")}),c.jsx("button",{className:"nc-btn danger",title:g("terminate"),onClick:()=>x(j,C.route),children:"×"})]})]},`${k}:u:${j.name}`)),C.available&&C.cells.length===0&&C.unmanaged.length===0&&c.jsx("div",{className:"nc-empty",children:g("fleet-inventory-empty")})]},k)})]})}function A2({cells:e=[],engines:t=[],busy:n,canRestore:s=!1,onRestore:o,onClose:a}){const[u,h]=N.useState("export"),[p,f]=N.useState(()=>new Set(e.map(P=>P.id))),[_,b]=N.useState(()=>new Set(t.map(P=>P.id))),[w,y]=N.useState([]),[S,x]=N.useState([]),[C,k]=N.useState(""),j=new Set(t.map(P=>P.id)),I=new Set(e.map(P=>P.id)),T=(P,R)=>P(M=>{const $=new Set(M);return $.has(R)?$.delete(R):$.add(R),$}),G=()=>{const P=R2(e,p,t,_);if(P.ok===!1){k(g(`fleet-backup-${P.error}`));return}const R=e.filter(ie=>p.has(ie.id)).length,M=t.filter(ie=>_.has(ie.id)).length;if(P.cells.length!==R){k(g("fleet-backup-invalid-cell"));return}if(P.engines.length!==M){k(g("fleet-backup-invalid-engine"));return}if(!P.cells.length&&!P.engines.length){k(g("fleet-backup-select-one"));return}const $=new Blob([`${JSON.stringify(P,null,2)}
|
|
91
91
|
`],{type:"application/json"}),te=URL.createObjectURL($),D=document.createElement("a");D.href=te,D.download=`nexuscrew-fleet-${new Date().toISOString().slice(0,10)}.json`,document.body.appendChild(D),D.click(),D.remove(),setTimeout(()=>URL.revokeObjectURL(te),0),k("")},U=async P=>{if(k(""),y([]),x([]),!!P){if(P.size>1024*1024){k(g("fleet-backup-too-large"));return}try{const R=P2(await P.text());if(!R.ok){k(g(`fleet-backup-${R.error}`));return}const M=new Set(R.engines.map($=>$.id));x(R.engines.map($=>({engine:$,exists:j.has($.id),selected:!j.has($.id)}))),y(R.cells.map($=>{const te=j.has($.engine)||M.has($.engine),D=I.has($.id);return{cell:$,engine:te?$.engine:"",selected:te&&!D,exists:D}}))}catch(R){k(String(R.message||R))}}},A=(P,R)=>y(M=>M.map(($,te)=>te===P?{...$,...R}:$)),ee=(P,R)=>x(M=>M.map(($,te)=>te===P?{...$,...R}:$)),W=new Set([...j,...S.filter(P=>P.selected).map(P=>P.engine.id)]),Z=w.filter(P=>P.selected&&P.engine&&W.has(P.engine)),K=S.filter(P=>P.selected),O=[...new Map([...t.map(P=>[P.id,P.label||P.id]),...S.filter(P=>P.selected).map(P=>[P.engine.id,P.engine.label||P.engine.id])]).entries()];return c.jsxs("div",{className:"nc-set-form nc-fleet-form nc-backup-dialog",children:[c.jsx("b",{children:g("fleet-backup")}),c.jsx("small",{children:g("fleet-backup-help")}),c.jsxs("div",{className:"nc-set-tabs nc-backup-tabs",children:[c.jsx("button",{type:"button",className:`nc-set-tabbtn${u==="export"?" on":""}`,onClick:()=>{h("export"),k("")},children:g("fleet-backup-export")}),c.jsx("button",{type:"button",className:`nc-set-tabbtn${u==="import"?" on":""}`,disabled:!s,title:s?"":g("fleet-backup-restore-unavailable"),onClick:()=>{h("import"),k("")},children:g("fleet-backup-import")})]}),u==="export"?c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{f(new Set(e.map(P=>P.id))),b(new Set(t.map(P=>P.id)))},children:g("select-all")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{f(new Set),b(new Set)},children:g("select-none")})]}),c.jsxs("div",{className:"nc-backup-list",children:[c.jsx("b",{children:g("fleet-engines")}),t.map(P=>{var R;return c.jsxs("label",{className:"nc-check nc-backup-row",children:[c.jsx("input",{type:"checkbox",checked:_.has(P.id),onChange:()=>T(b,P.id)}),c.jsxs("span",{children:[c.jsx("b",{children:P.label||P.id}),c.jsxs("small",{children:[P.id,(R=P.envKeys)!=null&&R.length?` · ${P.envKeys.length} ${g("fleet-backup-env-names")}`:""]})]})]},P.id)}),c.jsx("b",{children:g("fleet-cells")}),e.map(P=>c.jsxs("label",{className:"nc-check nc-backup-row",children:[c.jsx("input",{type:"checkbox",checked:p.has(P.id),onChange:()=>T(f,P.id)}),c.jsxs("span",{children:[c.jsx("b",{children:P.id}),c.jsxs("small",{children:[P.engine," · ",g("fleet-system-prompt")," ",(P.prompt||"").length," ",g("characters")]})]})]},P.id)),!e.length&&!t.length&&c.jsx("div",{className:"nc-empty",children:g("fleet-backup-empty")})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:!p.size&&!_.size,onClick:G,children:g("fleet-backup-download")})]})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{type:"file",accept:"application/json,.json",disabled:n,onChange:P=>U(P.target.files&&P.target.files[0])}),c.jsx("small",{children:g("fleet-backup-import-help")}),!!(w.length||S.length)&&c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(P=>P.map(R=>({...R,selected:!0}))),y(P=>P.map(R=>({...R,selected:!!R.engine})))},children:g("select-all")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>{x(P=>P.map(R=>({...R,selected:!1}))),y(P=>P.map(R=>({...R,selected:!1})))},children:g("select-none")})]}),c.jsxs("div",{className:"nc-backup-list",children:[!!S.length&&c.jsx("b",{children:g("fleet-engines")}),S.map((P,R)=>{var M;return c.jsx("div",{className:"nc-backup-import-row",children:c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:P.selected,onChange:$=>ee(R,{selected:$.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:P.engine.label||P.engine.id}),c.jsxs("small",{children:[P.engine.id," · ",P.exists?g("fleet-backup-overwrite"):g("fleet-backup-new"),(M=P.engine.envKeys)!=null&&M.length?` · ${g("fleet-backup-env-values-required")}`:""]})]})]})},P.engine.id)}),!!w.length&&c.jsx("b",{children:g("fleet-cells")}),w.map((P,R)=>c.jsxs("div",{className:"nc-backup-import-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:P.selected,disabled:!P.engine||!W.has(P.engine),onChange:M=>A(R,{selected:M.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:P.cell.id}),c.jsxs("small",{children:[P.exists?g("fleet-backup-overwrite"):g("fleet-backup-new")," · ",g("fleet-system-prompt")," ",P.cell.systemPrompt.length," ",g("characters")]})]})]}),c.jsxs("select",{value:P.engine,onChange:M=>A(R,{engine:M.target.value,selected:!!M.target.value}),children:[c.jsx("option",{value:"",children:g("fleet-backup-engine-missing")}),O.map(([M,$])=>c.jsx("option",{value:M,children:$},M))]})]},P.cell.id))]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:a,children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||!Z.length&&!K.length,onClick:()=>o({engineRows:S,cellRows:w}),children:g("fleet-backup-restore")})]})]}),C&&c.jsx("div",{className:"nc-err",children:C})]})}function I2({state:e,setState:t,busy:n,onSave:s,catalog:o}){var C;const a=e.form,u=k=>t({...e,form:{...a,...k}}),h=a.envRows||[],p=[...new Map(o.map(k=>[k.client,k.clientLabel||k.client])).entries()],f=o.filter(k=>k.client===a.client),_=Lo(o,a),b=typeof(_==null?void 0:_.credentialEnv)=="string"?_.credentialEnv:"",w=Array.isArray(_==null?void 0:_.credentialUsedBy)?_.credentialUsedBy:[],y=(_==null?void 0:_.credentialSource)||"missing",S=!!b&&e.mode==="new"&&(_==null?void 0:_.authConfigured)!==!0&&!a.credentialValue&&!a.allowMissingCredential,x=k=>{k&&u({client:k.client,provider:k.provider,credentialProfile:k.credentialProfile||"",managedModel:k.model||"",protocol:k.protocol||"",permissionPolicy:k.permissionPolicyDefault||"standard",rc:!!k.rc,displayName:k.custom?g("fleet-custom-provider-default"):"",baseUrl:k.custom?"":k.endpoint||"",envKey:k.defaultEnvKey||"",providerId:"nexuscrew-custom",credentialValue:"",credentialReveal:!1,allowMissingCredential:!1,...e.mode==="new"?{id:k.id,label:""}:{}})};return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsx("b",{children:e.mode==="new"?g("fleet-new-engine"):`${g("edit")} ${a.id}`}),c.jsx("input",{value:a.id,disabled:e.mode!=="new",placeholder:"id",onChange:k=>u({id:k.target.value})}),c.jsx("input",{value:a.label,placeholder:g("label"),onChange:k=>u({label:k.target.value})}),c.jsxs("select",{value:a.kind,disabled:e.mode!=="new",onChange:k=>u({kind:k.target.value}),children:[c.jsx("option",{value:"managed",children:g("fleet-managed")}),c.jsx("option",{value:"custom",children:g("fleet-custom")})]}),a.kind==="managed"?c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("select",{value:a.client,disabled:e.mode!=="new",onChange:k=>x(o.find(j=>j.client===k.target.value&&j.default)||o.find(j=>j.client===k.target.value)),children:p.map(([k,j])=>c.jsx("option",{value:k,children:j},k))}),c.jsx("select",{value:((C=Lo(o,a))==null?void 0:C.id)||"",disabled:e.mode!=="new",onChange:k=>x(o.find(j=>j.id===k.target.value)),children:f.map(k=>c.jsx("option",{value:k.id,children:k.label},k.id))})]}),a.client!=="shell"&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.managedModel,list:"nc-managed-models",placeholder:g(_!=null&&_.requiresModel?"fleet-model-required":"fleet-model-default"),onChange:k=>u({managedModel:k.target.value})}),c.jsx("datalist",{id:"nc-managed-models",children:[...(_==null?void 0:_.models)||[],...a.modelOptions||[]].filter((k,j,I)=>k&&I.indexOf(k)===j).map(k=>c.jsx("option",{value:k},k))})]}),_!=null&&_.supportsUnsafe?c.jsxs("select",{value:a.permissionPolicy,onChange:k=>u({permissionPolicy:k.target.value}),children:[c.jsx("option",{value:"standard",children:g("fleet-standard-permissions")}),c.jsx("option",{value:"unsafe",children:g("fleet-unsafe-permissions")})]}):c.jsx("small",{children:g("fleet-standard-permissions")}),(_==null?void 0:_.supportsUnsafe)&&a.permissionPolicy==="unsafe"&&c.jsx("small",{className:"nc-err",children:g("fleet-unsafe-warning")}),(_==null?void 0:_.credentialEnv)===!0&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.envKey,placeholder:g("fleet-api-key-env"),onChange:k=>u({envKey:k.target.value})}),c.jsx("small",{children:g("fleet-custom-secret-help")})]}),b&&c.jsxs("section",{className:"nc-fleet-key","aria-label":g("fleet-key-section"),children:[c.jsxs("div",{className:"nc-fleet-key-head",children:[c.jsx("b",{children:g("fleet-key-section")}),c.jsx("span",{children:g(`fleet-credential-source-${y}`)})]}),c.jsxs("label",{className:"nc-field",children:[g("fleet-key-variable"),c.jsx("input",{value:b,readOnly:!0,"aria-label":g("fleet-key-variable")})]}),c.jsx("small",{children:w.length?g("fleet-key-used-by").replace("{engines}",w.join(", ")):g("fleet-key-used-by-none")}),w.length>0&&c.jsx("small",{children:g("fleet-key-shared-impact").replace("{key}",b).replace("{engines}",w.join(", "))}),c.jsxs("div",{className:"nc-fleet-key-input",children:[c.jsx("input",{type:a.credentialReveal?"text":"password",value:a.credentialValue||"","aria-label":g("fleet-key-value").replace("{key}",b),autoComplete:"new-password",spellCheck:!1,autoCapitalize:"none",autoCorrect:"off",placeholder:_!=null&&_.authConfigured||e.mode==="edit"?g("fleet-key-blank-keeps"):g("fleet-key-enter"),onChange:k=>u({credentialValue:k.target.value,allowMissingCredential:k.target.value?!1:a.allowMissingCredential})}),c.jsx("button",{type:"button",className:"nc-btn ghost","aria-label":g(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal"),onClick:()=>u({credentialReveal:!a.credentialReveal}),children:g(a.credentialReveal?"fleet-key-hide":"fleet-key-reveal")})]}),c.jsx("small",{children:g("fleet-key-local-help")}),e.mode==="new"&&(_==null?void 0:_.authConfigured)!==!0&&!a.credentialValue&&c.jsxs("label",{className:"nc-check nc-fleet-key-missing",children:[c.jsx("input",{type:"checkbox",checked:!!a.allowMissingCredential,onChange:k=>u({allowMissingCredential:k.target.checked})}),g("fleet-key-create-missing")]}),(_==null?void 0:_.notice)&&c.jsx("small",{className:"nc-fleet-provider-notice",children:g(`fleet-provider-notice-${_.notice}`)})]}),a.provider==="custom"&&c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.displayName,placeholder:g("fleet-provider-display"),onChange:k=>u({displayName:k.target.value})}),c.jsx("input",{value:a.baseUrl,placeholder:"https://api.example.com/v1",onChange:k=>u({baseUrl:k.target.value})}),((_==null?void 0:_.protocols)||[]).length>1&&c.jsx("select",{value:a.protocol,onChange:k=>u({protocol:k.target.value}),children:_.protocols.map(k=>c.jsx("option",{value:k,children:k},k))}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:a.envKey,placeholder:g("fleet-api-key-env"),onChange:k=>u({envKey:k.target.value})}),c.jsx("input",{value:a.providerId,placeholder:g("fleet-provider-id"),onChange:k=>u({providerId:k.target.value})})]}),c.jsxs("small",{children:[a.protocol," · ",g("fleet-custom-secret-help")]})]}),c.jsx("small",{children:g(a.client==="shell"?"fleet-shell-engine-help":"fleet-managed-help")})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{value:a.command,placeholder:g("command-path"),onChange:k=>u({command:k.target.value})}),c.jsx("textarea",{value:a.argsText,placeholder:g("args-lines"),onChange:k=>u({argsText:k.target.value})}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:a.rc,onChange:k=>u({rc:k.target.checked})})," remote control"]}),c.jsxs("select",{value:a.promptMode,onChange:k=>u({promptMode:k.target.value}),children:[c.jsx("option",{value:"send-keys",children:"send-keys"}),c.jsx("option",{value:"flag",children:"flag"})]}),a.promptMode==="flag"&&c.jsx("input",{value:a.promptFlag,placeholder:"prompt flag",onChange:k=>u({promptFlag:k.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:a.modelFlag,placeholder:"model flag",onChange:k=>u({modelFlag:k.target.value})}),c.jsx("input",{value:a.modelValue,placeholder:"model default",onChange:k=>u({modelValue:k.target.value})})]}),c.jsx("small",{children:g("env-write-only")}),h.map((k,j)=>c.jsxs("div",{className:"nc-fleet-env",children:[c.jsx("input",{value:k.key,disabled:k.configured,placeholder:"ENV_KEY",onChange:I=>{const T=h.slice();T[j]={...k,key:I.target.value},u({envRows:T})}}),c.jsx("input",{type:"password",value:k.value,placeholder:k.configured?"•••••• (unchanged)":"value",onChange:I=>{const T=h.slice();T[j]={...k,value:I.target.value},u({envRows:T})}}),c.jsx("button",{className:"nc-btn danger",onClick:()=>u({envRows:h.filter((I,T)=>T!==j)}),children:"×"})]},`${k.key}-${j}`)),c.jsx("button",{className:"nc-btn ghost",onClick:()=>u({envRows:[...h,{key:"",value:"",configured:!1}]}),children:"+ env"})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>t(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:n||!a.id||S||a.kind==="custom"&&!a.command||a.kind==="managed"&&(_==null?void 0:_.requiresModel)&&!a.managedModel||a.kind==="managed"&&(_==null?void 0:_.credentialEnv)===!0&&!a.envKey||a.kind==="managed"&&a.provider==="custom"&&(!a.displayName||!a.baseUrl||!a.envKey||!a.providerId),onClick:s,children:g("save")})]})]})}function B2({token:e,route:t,targets:n=[],location:s,setLocation:o,state:a,setState:u,engines:h,busy:p,onSave:f}){var U;const[_,b]=N.useState(null),[w,y]=N.useState(""),S=a.form,x=A=>u({...a,form:{...S,...A}}),C=h.find(A=>A.id===S.engine),k=((U=C==null?void 0:C.managed)==null?void 0:U.client)==="shell",j=A=>{var Z,K,O,P;const ee=h.find(R=>R.id===A),W={...S.commands||{}};((Z=C==null?void 0:C.managed)==null?void 0:Z.client)==="shell"&&(S.command?W[S.engine]=S.command:delete W[S.engine]),x({engine:A,model:((K=S.models)==null?void 0:K[A])||((O=ee==null?void 0:ee.managed)==null?void 0:O.model)||((P=ee==null?void 0:ee.model)==null?void 0:P.value)||"",commands:W,command:W[A]||""})},I=A=>{const ee={...S.commands||{}};A?ee[S.engine]=A:delete ee[S.engine],x({command:A,commands:ee})},T=k&&/[|&;<>()`$*?{}\[\]~]/.test(S.command||""),G=async A=>{try{const ee=await Sh(e,A,t);b(ee),x({cwd:ee.path}),y("")}catch(ee){y(String(ee.message||ee))}};return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsx("b",{children:a.mode==="new"?g("fleet-new-cell"):`${g("edit")} ${S.id}`}),a.mode==="new"&&c.jsxs("label",{className:"nc-field",children:[g("location"),c.jsx("span",{className:"nc-req",children:" *"}),c.jsxs("select",{value:s,onChange:A=>{o(A.target.value),x({engine:""})},children:[c.jsx("option",{value:"",children:g("local")}),n.map(A=>c.jsxs("option",{value:A.route.join("/"),disabled:A.status&&A.status!=="up",children:[A.label,A.status&&A.status!=="up"?` · ${g("node-offline")}`:""]},A.route.join("/")))]})]}),c.jsx("input",{value:S.id,disabled:a.mode!=="new",placeholder:"id",onChange:A=>x({id:A.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:S.cwd,placeholder:g("cwd"),onChange:A=>x({cwd:A.target.value})}),c.jsx("button",{className:"nc-btn ghost",onClick:()=>_?b(null):G(S.cwd),children:g("browse")})]}),_&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:_.path}),c.jsxs("div",{className:"nc-fs-list",children:[_.parent&&c.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>G(_.parent),children:["↑ ",g("fs-parent")]}),(_.dirs||[]).map(A=>c.jsxs("button",{className:"nc-fs-item",onClick:()=>G(`${_.path.replace(/\/$/,"")}/${A}`),children:["📁 ",A]},A))]})]}),w&&c.jsx("div",{className:"nc-err",children:w}),c.jsx("select",{value:S.engine,onChange:A=>j(A.target.value),children:h.map(A=>c.jsx("option",{value:A.id,children:A.label},A.id))}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:!!S.boot,onChange:A=>x({boot:A.target.checked})})," boot"]}),k?c.jsxs(c.Fragment,{children:[c.jsx("input",{value:S.command||"",maxLength:4096,placeholder:g("fleet-shell-command-placeholder"),onChange:A=>I(A.target.value)}),c.jsx("small",{children:S.command?g("fleet-shell-command-help"):g("fleet-shell-interactive")}),T&&c.jsx("small",{className:"nc-note",children:g("fleet-shell-command-metachar")})]}):c.jsxs(c.Fragment,{children:[c.jsx("input",{value:S.model||"",list:"nc-cell-models",placeholder:g("fleet-model-override"),onChange:A=>x({model:A.target.value})}),c.jsx("datalist",{id:"nc-cell-models",children:((C==null?void 0:C.availableModels)||[]).map(A=>c.jsx("option",{value:A},A))}),c.jsx("textarea",{value:S.prompt||"",placeholder:"prompt",onChange:A=>x({prompt:A.target.value})})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>u(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:p||!S.id||!S.cwd||!S.engine,onClick:f,children:g("save")})]})]})}function o_({token:e,route:t=[],state:n,setState:s,busy:o,onSave:a}){const[u,h]=N.useState(null),[p,f]=N.useState([]),[_,b]=N.useState(""),w=Array.isArray(t)?t.join("/"):"";N.useEffect(()=>{let k=!0;return yh(e,w?w.split("/"):[]).then(j=>{k&&(f(j.engines||[]),b(""))}).catch(j=>{k&&(f([]),b(String(j.message||j)))}),()=>{k=!1}},[e,w]);const y=n.form,S=k=>s({...n,form:{...y,...k}}),x=async k=>{try{const j=await Sh(e,k,t);h(j),S({cwd:j.path})}catch{}},C=!y.id&&y.tmuxSession?y.tmuxSession.replace(/[^A-Za-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,32):y.id;return c.jsxs("div",{className:"nc-set-form nc-fleet-form",children:[c.jsxs("b",{children:[g("import-as-cell")," · ",y.tmuxSession]}),c.jsx("small",{children:g("import-help")}),c.jsx("input",{value:y.tmuxSession,disabled:!0,placeholder:"tmux session",readOnly:!0}),c.jsx("input",{value:y.id,placeholder:g("name"),onChange:k=>S({id:k.target.value})}),c.jsxs("div",{className:"nc-fleet-pair",children:[c.jsx("input",{value:y.cwd,placeholder:g("cwd"),onChange:k=>S({cwd:k.target.value})}),c.jsx("button",{className:"nc-btn ghost",type:"button",onClick:()=>u?h(null):x(y.cwd),children:g("browse")})]}),u&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:u.path}),c.jsxs("div",{className:"nc-fs-list",children:[u.parent&&c.jsxs("button",{className:"nc-fs-item nc-fs-nav",onClick:()=>x(u.parent),children:["↑ ",g("fs-parent")]}),(u.dirs||[]).map(k=>c.jsxs("button",{className:"nc-fs-item",onClick:()=>x(`${u.path.replace(/\/$/,"")}/${k}`),children:["📁 ",k]},k))]})]}),c.jsxs("select",{value:y.engine,onChange:k=>S({engine:k.target.value}),children:[c.jsx("option",{value:"",children:g("import-engine-required")}),p.map(k=>c.jsx("option",{value:k.id,children:k.label},k.id))]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:!!y.boot,onChange:k=>S({boot:k.target.checked})})," boot"]}),(n.err||_)&&c.jsx("div",{className:"nc-err",children:n.err||_}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>s(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:o||!y.tmuxSession||!y.engine||!p.length,onClick:a,children:g(C?"import-as-cell":"save")})]})]})}function rl(e){const t=Lr(e);return t===null?`~/${e}`:t?`~/${t}`:"~"}function O2(e){if(!e||typeof e.path!="string"||typeof e.home!="string")return"";const{path:t,home:n}=e;return!n||t===n||!t.startsWith(n+"/")?"":t.slice(n.length+1).replace(/\/+$/,"")}function z2({token:e,route:t,cell:n,busy:s=!1,onSaved:o,onClose:a}){const[u,h]=N.useState(""),[p,f]=N.useState(null),[_,b]=N.useState(!1),[w,y]=N.useState(""),S=typeof(n==null?void 0:n.cwdSuggestion)=="string"&&Lr(n.cwdSuggestion)!==null?Lr(n.cwdSuggestion):null,[x,C]=N.useState(S),k=n==null?void 0:n.id,j=s||_,I=async W=>{const Z=Lr(W);if(Z===null){f({rel:"",dirs:[],loading:!1,err:g("fleet-cwd-repair-invalid")});return}f({rel:Z,dirs:[],loading:!0,err:""});try{const K=await Sh(e,Z,t),O=O2(K);f({rel:O,dirs:Array.isArray(K.dirs)?K.dirs:[],loading:!1,err:""})}catch(K){f({rel:Z,dirs:[],loading:!1,err:String(K&&K.message||K)})}},T=W=>{if(!p)return;const Z=p.rel?`${p.rel}/${W}`:W;I(Z)},G=()=>{p!=null&&p.rel&&I(p.rel.split("/").slice(0,-1).join("/"))},U=()=>{p&&(h(p.rel||""),f(null),y(""),C(null))},A=()=>{x!==null&&(h(x),C(null),y(""))},ee=async()=>{if(j)return;const W=Lr(u);if(W===null){y(g("fleet-cwd-repair-invalid"));return}const Z=rl(W);let K=!0;try{K=window.confirm(g("fleet-cwd-repair-confirm").replace("{id}",String(k||"")).replace("{path}",Z))}catch{K=!1}if(K){b(!0),y(""),C(null);try{const O=await v_(e,k,{cwdRel:W},t);o==null||o(O)}catch(O){const P=O&&O.data||{};if(P.code==="unportable-cwd"){y(g("fleet-cwd-repair-unportable"));const R=Array.isArray(P.cells)?P.cells.find($=>$&&$.id===k):null,M=R&&typeof R.suggestion=="string"?R.suggestion:null;M!==null&&Lr(M)!==null&&C(M)}else y(String(O&&O.message||O))}finally{b(!1)}}};return c.jsxs("div",{className:"nc-set-form nc-fleet-form nc-cwd-repair",children:[c.jsx("b",{children:g("fleet-cwd-repair-title").replace("{id}",String(k||""))}),c.jsx("small",{className:"nc-set-info",children:g("fleet-cwd-repair-help")}),c.jsx("small",{className:"nc-cwd-no-source",children:g("fleet-cwd-repair-no-source-path")}),c.jsxs("label",{className:"nc-field",children:[g("cwd"),c.jsxs("div",{className:"nc-cwd-rel-row",children:[c.jsx("span",{className:"nc-cwd-rel-prefix",children:"~/"}),c.jsx("input",{value:u,placeholder:g("fleet-cwd-repair-placeholder"),autoComplete:"off",spellCheck:"false",autoCapitalize:"none",onChange:W=>h(W.target.value)}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:j,onClick:()=>p?f(null):I(""),children:g(p?"close":"fleet-cwd-repair-browse")})]})]}),c.jsx("div",{className:"nc-cwd-preview",children:g("fleet-cwd-repair-preview").replace("{path}",rl(u))}),p&&c.jsxs("div",{className:"nc-fs",children:[c.jsx("div",{className:"nc-fs-path",children:rl(p.rel)}),c.jsxs("div",{className:"nc-fs-list",children:[p.rel&&c.jsxs("button",{type:"button",className:"nc-fs-item nc-fs-nav",onClick:G,children:["↑ ",g("fs-parent")]}),p.loading&&c.jsx("div",{className:"nc-fs-empty",children:"…"}),!p.loading&&p.dirs.length===0&&c.jsx("div",{className:"nc-fs-empty",children:g("fleet-cwd-repair-fs-empty")}),!p.loading&&p.dirs.map(W=>c.jsxs("button",{type:"button",className:"nc-fs-item",onClick:()=>T(W),children:["📁 ",W]},W))]}),p.err&&c.jsx("div",{className:"nc-err",children:p.err}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{type:"button",className:"nc-btn primary",disabled:j||p.loading||!!p.err,onClick:U,children:g("fleet-cwd-repair-use-current")})})]}),w&&c.jsx("div",{className:"nc-err",role:"alert",children:w}),x!==null&&c.jsxs("div",{className:"nc-cwd-suggestion",role:"status",children:[c.jsx("span",{children:g("fleet-cwd-repair-suggestion")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:A,children:g("fleet-cwd-repair-use-suggestion").replace("{path}",rl(x))})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:_,onClick:a,children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:j,onClick:ee,children:_?"…":g("save")})]})]})}function F2({token:e,readonly:t,targets:n=[],startNewCell:s=!1,initialLocation:o=""}){const[a,u]=N.useState({engines:[],cells:[],managedCatalog:[]}),[h,p]=N.useState({available:!1,capabilities:[]}),[f,_]=N.useState(!1),[b,w]=N.useState(null),[y,S]=N.useState(null),[x,C]=N.useState(!1),[k,j]=N.useState(""),[I,T]=N.useState(""),[G,U]=N.useState(o),[A,ee]=N.useState(!1),[W,Z]=N.useState(null),[K,O]=N.useState(null),[P,R]=N.useState(null),[M,$]=N.useState(!1),[te,D]=N.useState([]),[ie,B]=N.useState(null),[L,F]=N.useState("manage"),X=N.useRef(!1),ae=G?G.split("/"):[],ue=N.useCallback(async()=>{try{const le=await zr(e,ae);p(le);try{const be=await bh(e,ae);ee(!!be.readonlyDefault)}catch{ee(!1)}if(le.provider==="builtin"&&(le.capabilities||[]).includes("definitions")){const be=await yh(e,ae),Be=new Map((le.engines||[]).map($e=>[$e.id,$e]));if(be.engines=(be.engines||[]).map($e=>{var he;return{...$e,availableModels:((he=Be.get($e.id))==null?void 0:he.models)||[]}}),u(be),(le.capabilities||[]).includes("credentials"))try{D((await Db(e,ae)).credentials||[])}catch{D([])}else D([])}j("")}catch(le){j(String(le.message||le))}finally{_(!0)}},[e,G]);N.useEffect(()=>{ue();const le=setInterval(ue,5e3);return()=>clearInterval(le)},[ue]);const ge=new Set((h.cells||[]).filter(le=>le.active).map(le=>le.cell)),Q=h.provider==="builtin"&&(h.capabilities||[]).includes("edit"),q=(h.capabilities||[]).includes("restore");N.useEffect(()=>{!s||X.current||!Q||!a.engines.length||(X.current=!0,S({mode:"new",form:s_(a.engines[0].id)}))},[s,Q,a.engines]);const fe=async le=>{C(!0),j(""),T("");try{await le(),await ue()}catch(be){j(String(be.message||be))}C(!1)},Ie=()=>fe(async()=>{var Ne;const le=b.mode==="new",be=b.form,Be=a.managedCatalog||[],$e=be.kind==="managed"?Lo(Be,be):null,he=typeof($e==null?void 0:$e.credentialEnv)=="string"?$e.credentialEnv:"",V=he&&be.credentialValue||"",ne=T2(be,le,Be);!le&&b.form.kind==="custom"&&!b.form.modelFlag&&(ne.model=null),!le&&b.form.kind==="custom"&&b.form.promptMode!=="flag"&&(ne.promptFlag=null);let z;if(le)z=await Tb(e,ne,ae);else{const we=b.original,Re=new Set(b.form.envRows.filter(Te=>!Te.remove).map(Te=>Te.key)),Je=(we.envKeys||[]).filter(Te=>!Re.has(Te)),Mt=Object.fromEntries(b.form.envRows.filter(Te=>!Te.remove&&Te.key&&(!Te.configured||Te.value!=="")).map(Te=>[Te.key,Te.value]));z=await Ab(e,we.id,ne,b.form.kind==="custom"?{set:Mt,remove:Je}:void 0,ae)}let se=null;if(he&&V)try{se=await Hm(e,he,V,ae),D(se.credentials||[])}catch{throw await ue(),le&&w({mode:"edit",original:{...ne,id:be.id},form:{...be,credentialReveal:!1}}),new Error(g(le?"fleet-key-partial-create":"fleet-key-partial-edit"))}await ue(),w(null),T(g("fleet-saved"));const me=((Ne=((se==null?void 0:se.credentials)||[]).find(we=>we.envKey===he))==null?void 0:Ne.activeCells)||[],ke=[...new Set([...(z==null?void 0:z.activeCells)||[],...me])];if(ke.length&&window.confirm(g("fleet-restart-confirm").replace("{cells}",ke.join(", "))))for(const we of ke)await wo(e,we,ae)}),et=()=>fe(async()=>{var z,se,me,ke,Ne,we;const le=y.mode==="new",be=y.form,Be={...le?{id:be.id}:{},cwd:be.cwd,engine:be.engine,boot:!!be.boot},$e={...be.commands||{}},he=a.engines.find(Re=>Re.id===be.engine);((z=he==null?void 0:he.managed)==null?void 0:z.client)==="shell"&&typeof be.command=="string"&&be.command.length?$e[be.engine]=be.command:((se=he==null?void 0:he.managed)==null?void 0:se.client)==="shell"&&delete $e[be.engine],(Object.keys($e).length||!le)&&(Be.commands=$e),le?(((me=he==null?void 0:he.managed)==null?void 0:me.client)!=="shell"&&be.model&&(Be.model=be.model),((ke=he==null?void 0:he.managed)==null?void 0:ke.client)!=="shell"&&be.prompt&&(Be.prompt=be.prompt)):(Be.model=((Ne=he==null?void 0:he.managed)==null?void 0:Ne.client)==="shell"?null:be.model||null,((we=he==null?void 0:he.managed)==null?void 0:we.client)!=="shell"&&(Be.prompt=be.prompt||null));const V=le?await Bb(e,Be,ae):await v_(e,y.original.id,Be,ae),ne=le?be.id:y.original.id;S(null),T(g("fleet-saved")),!le&&(V!=null&&V.active)&&window.confirm(g("fleet-restart-confirm").replace("{cells}",ne))&&await wo(e,ne,ae)}),ze=t||A,Fe=le=>te.find(be=>{var Be;return(Be=be.engines)==null?void 0:Be.includes(le.id)})||null,Ct=()=>fe(async()=>{var Be;const le=await Hm(e,ie.envKey,ie.value,ae);D(le.credentials||[]),B(null),T(g("fleet-credential-saved"));const be=((Be=(le.credentials||[]).find($e=>$e.envKey===ie.envKey))==null?void 0:Be.activeCells)||[];if(be.length&&window.confirm(g("fleet-restart-confirm").replace("{cells}",be.join(", "))))for(const $e of be)await wo(e,$e,ae)}),Ge=le=>fe(async()=>{if(!window.confirm(g("fleet-credential-remove-confirm").replace("{key}",le.envKey)))return;const be=await Mb(e,le.envKey,ae);D(be.credentials||[]),T(g("fleet-credential-removed"))}),rt=le=>Z({...le,route:Array.isArray(le==null?void 0:le.route)?le.route:ae}),ut=async le=>{if(!W)return;const be=W.cell||W.id,Be=Array.isArray(W.route)?W.route:ae;le.action==="up"?await _h(e,{cell:be,boot:!!le.boot,...le.engine?{engine:le.engine}:{},...le.model!==void 0?{model:le.model}:{},...le.permissionPolicy?{permissionPolicy:le.permissionPolicy}:{}},Be):await fl(e,{cell:be,boot:!!le.boot},Be)},ct=(le,be)=>{j(""),O({mode:"new",route:Array.isArray(be)?be:ae,form:{tmuxSession:le.name,id:"",engine:"",cwd:"",boot:!1},err:""})},vt=()=>fe(async()=>{const le=K.form;if(!le.engine){O({...K,err:g("import-engine-required")});return}await Ob(e,{tmuxSession:le.tmuxSession,id:le.id||void 0,engine:le.engine,cwd:le.cwd||void 0,boot:!!le.boot},le.route||ae),O(null),T(g("fleet-saved"))}),it=({engineRows:le=[],cellRows:be=[]})=>fe(async()=>{const Be=le.filter(Ne=>Ne.selected),$e=Be.map(Ne=>bw(Ne.engine));if($e.some(Ne=>!Ne))throw new Error(g("fleet-backup-invalid-engine"));const he=Be.filter(Ne=>Ne.exists).map(Ne=>Ne.engine.id);if(he.length&&!window.confirm(g("fleet-backup-confirm-engine-overwrite").replace("{engines}",he.join(", "))))return;const V=[...new Set([...a.engines.map(Ne=>Ne.id),...$e.map(Ne=>Ne.id)])],ne=[];for(const Ne of be.filter(we=>we.selected)){const we=j2(Ne.cell,Ne.engine,V);if(!we)throw new Error(`${Ne.cell.id}: ${g("fleet-backup-engine-missing")}`);ne.push(we)}const z=be.filter(Ne=>Ne.selected&&Ne.exists).map(Ne=>Ne.cell.id);if(z.length&&!window.confirm(g("fleet-backup-confirm-overwrite").replace("{cells}",z.join(", "))))return;const se=$e.length?await Fb(e,$e,he.length>0,ae):{needsRestart:[]},me=ne.length?await zb(e,ne,ae):{needsRestart:[]},ke=[...new Set([...se.needsRestart||[],...me.needsRestart||[]])];$(!1),T(`${g("fleet-backup-restored").replace("{n}",String(ne.length))}${$e.length?` · ${$e.length} ${g("fleet-engines").toLowerCase()}`:""}${ke.length?` · ${g("fleet-backup-needs-restart").replace("{cells}",ke.join(", "))}`:""}`)}),lt=c.jsxs("label",{className:"nc-field",children:[g("location"),c.jsxs("select",{value:G,onChange:le=>{U(le.target.value),w(null),S(null),j(""),T(""),ee(!1),_(!1),p({available:!1,capabilities:[]}),u({engines:[],cells:[],managedCatalog:[]}),D([]),B(null)},children:[c.jsx("option",{value:"",children:g("local")}),n.map(le=>c.jsxs("option",{value:le.route.join("/"),disabled:le.status&&le.status!=="up",children:[le.label,le.status&&le.status!=="up"?` · ${g("node-offline")}`:""]},le.route.join("/")))]})]}),mt=c.jsxs("div",{className:"nc-fleet-view-tabs",role:"tablist","aria-label":g("tab-fleet"),children:[c.jsx("button",{type:"button",role:"tab","aria-selected":L==="manage",className:`nc-set-tabbtn${L==="manage"?" on":""}`,onClick:()=>F("manage"),children:g("fleet-manage-location")}),c.jsx("button",{type:"button",role:"tab","aria-selected":L==="overview",className:`nc-set-tabbtn${L==="overview"?" on":""}`,onClick:()=>F("overview"),children:g("fleet-network-overview")})]});return f?Q?c.jsxs("div",{className:"nc-set-tab nc-fleet-editor",children:[mt,L==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-overview-help")}),c.jsx(fd,{token:e,targets:n,readonly:t,onPower:rt,onImport:ct})]}):c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-manage-help")}),lt,c.jsxs("div",{className:"nc-set-row nc-fleet-backup-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>{j(""),$(!0)},children:g("fleet-backup")}),c.jsx("small",{children:g("fleet-backup-help")})]}),c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:g("fleet-cells")}),c.jsx("span",{className:"nc-fleet-head-actions",children:c.jsxs("button",{className:"nc-btn primary",disabled:ze||x||!a.engines.length,onClick:()=>{var le;j(""),S({mode:"new",form:s_((le=a.engines[0])==null?void 0:le.id)})},children:["+ ",g("add")]})})]}),a.cells.map(le=>{const be=ge.has(le.id),Be=h.capabilities||[],$e=le.needsRepair===!0;return c.jsxs("div",{className:"nc-fleet-item",children:[c.jsxs("span",{children:[c.jsx("b",{children:le.id}),c.jsxs("small",{children:[le.engine," · ",$e?c.jsx("span",{className:"nc-fleet-tag nc-fleet-tag-warn",children:g("fleet-cwd-needs-repair")}):le.cwd,be?` · ${g("service-active")}`:` · ${g("cell-off")}`]})]}),c.jsxs("span",{children:[be&&Be.includes("down")&&c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>fe(()=>fl(e,{cell:le.id},ae)),children:g("stop")}),!be&&Be.includes("up")&&c.jsx("button",{className:"nc-btn primary",disabled:ze||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:g("start")}),be&&Be.includes("restart")&&c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>fe(()=>wo(e,le.id,ae)),children:g("restart")}),$e?c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>{j(""),R(le)},children:g("fleet-cwd-repair")}):c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>{var he;j(""),S({mode:"edit",original:le,form:{...le,commands:{...le.commands||{}},command:((he=le.commands)==null?void 0:he[le.engine])||""}})},children:g("edit")}),c.jsx("button",{className:"nc-btn danger",disabled:ze||x,onClick:()=>fe(async()=>{window.confirm(g("fleet-remove-cell").replace("{id}",le.id))&&await __(e,le.id,!0,ae)}),children:"×"})]})]},le.id)}),c.jsxs("div",{className:"nc-fleet-section-head",children:[c.jsx("b",{children:g("fleet-engines")}),c.jsxs("button",{className:"nc-btn primary",disabled:ze||x,onClick:()=>{j(""),w({mode:"new",form:L2()})},children:["+ ",g("add")]})]}),a.engines.map(le=>{var Be,$e;const be=Fe(le);return c.jsxs("div",{className:"nc-fleet-item",children:[c.jsxs("span",{children:[c.jsx("b",{children:le.label}),c.jsx("small",{children:le.managed?`${le.id} · ${le.managed.client} / ${le.managed.provider} · ${(Be=le.managedInfo)!=null&&Be.configured?g("fleet-ready"):(($e=le.managedInfo)==null?void 0:$e.reason)||g("fleet-not-ready")}`:`${le.id} · ${le.command}`}),be&&c.jsxs("small",{children:[be.envKey," · ",g(`fleet-credential-source-${be.source||"missing"}`)]})]}),c.jsxs("span",{children:[be&&c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>{j(""),B({envKey:be.envKey,value:""})},children:be.configured?g("fleet-credential-change"):g("fleet-credential-set")}),(be==null?void 0:be.source)==="local"&&c.jsx("button",{className:"nc-btn danger",disabled:ze||x,onClick:()=>Ge(be),children:g("fleet-credential-forget")}),c.jsx("button",{className:"nc-btn ghost",disabled:ze||x,onClick:()=>{j(""),w({mode:"edit",original:le,form:M2(le)})},children:g("edit")}),c.jsx("button",{className:"nc-btn danger",disabled:ze||x,onClick:()=>fe(async()=>{window.confirm(g("fleet-remove-engine").replace("{id}",le.id))&&await Ib(e,le.id,ae)}),children:"×"})]})]},le.id)})]}),b&&c.jsx(Nr,{onClose:()=>w(null),label:g("fleet-new-engine"),error:k,children:c.jsx(I2,{state:b,setState:w,busy:x,onSave:Ie,catalog:a.managedCatalog||[]})}),y&&c.jsx(Nr,{onClose:()=>S(null),label:g("fleet-new-cell"),error:k,children:c.jsx(B2,{token:e,route:ae,targets:n,location:G,setLocation:U,state:y,setState:S,engines:a.engines,busy:x,onSave:et})}),P&&c.jsx(Nr,{onClose:()=>R(null),label:g("fleet-cwd-repair-title").replace("{id}",P.id),error:"",children:c.jsx(z2,{token:e,route:ae,cell:P,busy:x,onSaved:async()=>{R(null),T(g("fleet-cwd-repaired")),await ue()},onClose:()=>R(null)})}),I&&c.jsx("div",{className:"nc-set-note",children:I}),k&&c.jsx("div",{className:"nc-err",children:k}),M&&c.jsx(Nr,{onClose:()=>$(!1),label:g("fleet-backup"),error:k,children:c.jsx(A2,{cells:a.cells,engines:a.engines,busy:x,canRestore:q,onRestore:it,onClose:()=>$(!1)})}),ie&&c.jsx(Nr,{onClose:()=>B(null),label:g("fleet-credential-title"),error:k,children:c.jsxs("div",{className:"nc-fleet-form nc-credential-form",children:[c.jsx("b",{children:g("fleet-credential-title")}),c.jsx("small",{children:g("fleet-credential-help")}),c.jsxs("label",{className:"nc-field",children:[ie.envKey,c.jsx("input",{type:"password",autoComplete:"off",spellCheck:"false",autoCapitalize:"none",value:ie.value,onChange:le=>B({...ie,value:le.target.value})})]}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",onClick:()=>B(null),children:g("cancel")}),c.jsx("button",{className:"nc-btn primary",disabled:x||!ie.value,onClick:Ct,children:g("save")})]})]})}),K&&c.jsx(Nr,{onClose:()=>O(null),label:g("import-as-cell"),error:k,children:c.jsx(o_,{token:e,route:K.route||ae,state:K,setState:O,busy:x,onSave:vt})}),W&&c.jsx(pl,{cell:W,token:e,route:Array.isArray(W.route)?W.route:ae,onConfirm:async le=>{try{await ut(le)}finally{await ue()}},onClose:()=>Z(null)})]}):c.jsxs("div",{className:"nc-set-tab",children:[mt,L==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-overview-help")}),c.jsx(fd,{token:e,targets:n,readonly:t,onPower:rt,onImport:ct})]}):c.jsxs(c.Fragment,{children:[lt,c.jsxs("div",{className:"nc-set-info",children:[g(k?"fleet-editor-load-error":"fleet-editor-unavailable"),!k&&h.reason?` ${h.reason}`:""]})]}),k&&c.jsx("div",{className:"nc-err",children:k}),K&&c.jsx(Nr,{onClose:()=>O(null),label:g("import-as-cell"),error:k,children:c.jsx(o_,{token:e,route:K.route||ae,state:K,setState:O,busy:x,onSave:vt})}),W&&c.jsx(pl,{cell:W,token:e,route:Array.isArray(W.route)?W.route:ae,onConfirm:async le=>{try{await ut(le)}finally{await ue()}},onClose:()=>Z(null)})]}):c.jsxs("div",{className:"nc-set-tab",children:[mt,L==="overview"?c.jsxs(c.Fragment,{children:[c.jsx("div",{className:"nc-set-info",children:g("fleet-overview-help")}),c.jsx(fd,{token:e,targets:n,readonly:t,onPower:rt,onImport:ct})]}):c.jsxs(c.Fragment,{children:[lt,c.jsx("div",{className:"nc-set-info",children:g("fleet-editor-loading")})]})]})}function $2({text:e}){const[t,n]=N.useState(!1),s=async()=>{try{await navigator.clipboard.writeText(e),n(!0),setTimeout(()=>n(!1),1500)}catch{}};return c.jsxs("div",{className:"nc-set-copyline",children:[c.jsx("code",{children:e}),c.jsxs("button",{type:"button",className:"nc-btn ghost",onClick:s,children:[c.jsx(nt,{name:"copy",size:14})," ",g(t?"copied":"copy")]})]})}function H2({value:e}){const[t,n]=N.useState("");return N.useEffect(()=>{let s=!0;return e2.toDataURL(e,{margin:1,width:220}).then(o=>{s&&n(o)}).catch(()=>{}),()=>{s=!1}},[e]),t?c.jsx("img",{src:t,width:"220",height:"220",alt:"NexusCrew pairing QR"}):null}function W2({token:e,nodes:t,roster:n,settings:s,readonly:o,refresh:a,refreshAliases:u}){const[h,p]=N.useState(null),[f,_]=N.useState(null),[b,w]=N.useState({}),[y,S]=N.useState(null),[x,C]=N.useState({ssh:"",sshPort:"",name:""}),[k,j]=N.useState(""),[I,T]=N.useState(""),[G,U]=N.useState(""),[A,ee]=N.useState(!1),[W,Z]=N.useState(null),[K,O]=N.useState(null),P=Date.now(),R=s&&s.deviceName||"",M=(t||[]).filter(Q=>Q&&Q.direction==="outbound"&&Q.name&&Q.ssh),$=M.find(Q=>Q.name===k)||M[0]||null,te=M.find(Q=>Q.name===I)||M[0]||null,D=te?ag(te.tunnel,P):null,ie=te!=null&&te.shared?D!=null&&D.up?"share-local-active":"share-local-pending":D!=null&&D.up?"share-local-private":"share-local-private-down",B=[{key:"peer-group-hubs",rows:(t||[]).filter(Q=>Q.kind!=="transitive"&&Q.relation!=="client")},{key:"peer-group-clients",rows:(t||[]).filter(Q=>Q.kind!=="transitive"&&Q.relation==="client")},{key:"peer-group-routed",rows:(t||[]).filter(Q=>Q.kind==="transitive")}],L=async(Q,q)=>{p(null),_(`${Q}:${q}`);try{const fe=await kh(e,Q,q);q==="test"&&w(Ie=>({...Ie,[Q]:fe})),await a()}catch(fe){p(`${Q}: ${String(fe.message||fe)}`)}_(null)},F=async Q=>{p(null),_(`${Q}:remove`);try{await Gb(e,Q),O(null),await a()}catch(q){p(`${Q}: ${String(q.message||q)}`)}_(null)},X=Q=>Z({name:Q.name,direction:Q.direction,label:Q.label||Q.name,ssh:Q.ssh||"",sshPort:Q.sshPort?String(Q.sshPort):"",autostart:Q.autostart===!0,visibility:Q.visibility||"network",selected:[...Q.selected||[]]}),ae=async()=>{if(!W||!bs(W.label)){p(g("err-label"));return}const Q=W.direction==="inbound"?{label:W.label,visibility:W.visibility,selected:W.visibility==="selected"?W.selected:[]}:{label:W.label,ssh:W.ssh,autostart:W.autostart,...W.sshPort?{sshPort:Number(W.sshPort)}:{}};p(null),_(`${W.name}:edit`);try{await Yb(e,W.name,Q),Z(null),await a()}catch(q){p(`${W.name}: ${String(q.message||q)}`)}_(null)},ue=async Q=>{if(te){p(null),_(`${te.name}:share`);try{await Xb(e,te.name,Q),await a()}catch(q){const fe=q!=null&&q.data&&q.data.shared===!1?q.data:null;if(fe)try{await a()}catch{}const Ie=q!=null&&q.data&&typeof q.data.hint=="string"?q.data.hint:"",et=(fe==null?void 0:fe.revoked)===!0&&(fe==null?void 0:fe.localReconcilePending)===!0?g("share-off-local-reconcile-pending"):(fe==null?void 0:fe.revoked)===!1&&(fe==null?void 0:fe.reconcilePending)===!0?g("share-off-hub-reconcile-pending"):"";p(`${te.name}: ${String(q.message||q)}${Ie?` — ${Ie}`:""}${et?` — ${et}`:""}`)}_(null)}},ge=async()=>{if(p(null),$){const fe=og({name:$.name,ssh:$.ssh,sshPort:$.sshPort||""});if(!fe.ok){p(g(fe.error));return}_("invite");try{S(await Um(e,{ssh:fe.value.ssh,...fe.value.sshPort?{sshPort:fe.value.sshPort}:{}},[$.name]))}catch(Ie){p(String(Ie.message||Ie))}_(null);return}const Q=dr(x.name||G||R||"NexusCrew"),q=og({name:Q,ssh:x.ssh,sshPort:x.sshPort});if(!q.ok){p(g(q.error));return}_("invite");try{S(await Um(e,{...G||R?{label:G||R}:{},name:Q,ssh:q.value.ssh,...q.value.sshPort?{sshPort:q.value.sshPort}:{}}))}catch(fe){p(String(fe.message||fe))}_(null)};return c.jsxs("div",{className:"nc-set-tab",children:[c.jsx("div",{className:"nc-sheet-label",children:g("peer-inventory-title")}),(t||[]).length===0&&c.jsx("div",{className:"nc-empty",children:g("no-nodes")}),B.map(Q=>Q.rows.length>0&&c.jsxs("div",{className:"nc-peer-group",children:[c.jsxs("div",{className:"nc-peer-group-title",children:[g(Q.key)," ",c.jsx("span",{children:Q.rows.length})]}),Q.rows.map(q=>{var Fe,Ct;const fe=q.kind==="transitive",Ie=fe?{up:q.stale!==!0,label:q.stale===!0?"peer-routed-stale":"peer-routed"}:ag(q.tunnel,P),et=b[q.name],ze=q.actions||{};return c.jsxs("div",{className:`nc-set-node${fe?" routed":""}`,children:[c.jsxs("div",{className:"nc-set-node-head",children:[c.jsx("span",{className:`nc-dot ${Ie.up?"on":""}`}),c.jsx("b",{children:q.label||q.name}),c.jsxs("small",{children:[q.name,fe?` · ${q.route.join(" → ")}`:q.direction==="outbound"?` · SSH ${q.ssh||""}`:` · ${g("node-connected-client")}`,(Fe=q.tunnel)!=null&&Fe.transport?` · ${q.tunnel.transport} ${g("transport-used")}`:""]}),c.jsxs("span",{className:`nc-set-tunnel${Ie.up?" up":""}`,children:[g(Ie.label),Ie.since?` · ${Ie.since}`:""]})]}),!fe&&q.direction==="inbound"&&c.jsx("div",{className:"nc-set-info",children:g(q.shared?"peer-shared":"peer-private")}),ze.visibility&&q.shared&&c.jsxs(c.Fragment,{children:[c.jsxs("select",{value:q.visibility||"network",disabled:o||!!f,onChange:async Ge=>{_(`${q.name}:visibility`);try{await qm(e,q.name,Ge.target.value),await a()}catch(rt){p(String(rt.message||rt))}_(null)},children:[c.jsx("option",{value:"network",children:g("visibility-network")}),c.jsx("option",{value:"relay-only",children:g("visibility-relay")}),c.jsx("option",{value:"selected",children:g("visibility-selected")})]}),q.visibility==="selected"&&c.jsx("div",{className:"nc-set-row",children:(t||[]).filter(Ge=>Ge.name!==q.name&&Ge.nodeId).map(Ge=>{const rt=(q.selected||[]).includes(Ge.nodeId);return c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:rt,disabled:o||!!f,onChange:async ut=>{const ct=ut.target.checked?[...q.selected||[],Ge.nodeId]:(q.selected||[]).filter(vt=>vt!==Ge.nodeId);_(`${q.name}:visibility`);try{await qm(e,q.name,"selected",ct),await a()}catch(vt){p(String(vt.message||vt))}_(null)}})," ",Ge.label||Ge.name]},Ge.nodeId)})})]}),c.jsxs("div",{className:"nc-set-node-actions",children:[ze.edit&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>X(q),children:g("edit")}),ze.test&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>L(q.name,"test"),children:g("node-test")}),ze.disconnect&&Ie.up&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>L(q.name,"down"),children:g("tunnel-stop")}),ze.connect&&!Ie.up&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>L(q.name,"up"),children:g("tunnel-start")}),ze.restart&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>L(q.name,"restart"),children:g("tunnel-restart")}),ze.remove&&c.jsxs("button",{type:"button",className:"nc-btn danger",disabled:o||!!f,title:g(o?"settings-readonly":"delete"),onClick:()=>O(q.name),children:[c.jsx(nt,{name:"trash",size:14})," ",g("delete")]})]}),(W==null?void 0:W.name)===q.name&&c.jsxs("div",{className:"nc-set-form nc-node-editor",children:[c.jsxs("label",{className:"nc-field",children:[g("node-display-label"),c.jsx("input",{value:W.label,disabled:!!f,onChange:Ge=>Z({...W,label:Ge.target.value})})]}),W.direction==="outbound"?c.jsxs(c.Fragment,{children:[c.jsxs("label",{className:"nc-field",children:[g("node-ssh-label"),c.jsx("input",{value:W.ssh,disabled:!!f,onChange:Ge=>Z({...W,ssh:Ge.target.value})})]}),c.jsxs("label",{className:"nc-field",children:[g("node-ssh-port-label"),c.jsx("input",{inputMode:"numeric",value:W.sshPort,disabled:!!f,onChange:Ge=>Z({...W,sshPort:Ge.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:W.autostart,disabled:!!f,onChange:Ge=>Z({...W,autostart:Ge.target.checked})})," ",g("boot-persist")]})]}):c.jsxs("label",{className:"nc-field",children:[g("peer-visibility"),c.jsxs("select",{value:W.visibility,disabled:!!f,onChange:Ge=>Z({...W,visibility:Ge.target.value}),children:[c.jsx("option",{value:"network",children:g("visibility-network")}),c.jsx("option",{value:"relay-only",children:g("visibility-relay")}),c.jsx("option",{value:"selected",children:g("visibility-selected")})]})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:!!f,onClick:ae,children:g("save")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>Z(null),children:g("cancel")})]})]}),K===q.name&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("b",{children:g("node-remove-confirm").replace("{name}",q.label||q.name)}),c.jsx("small",{children:g("node-remove-warning")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn danger",disabled:!!f,onClick:()=>F(q.name),children:g("delete")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>O(null),children:g("cancel")})]})]}),((Ct=q.health)==null?void 0:Ct.detail)&&c.jsx("div",{className:`nc-set-test${q.health.status==="healthy"?" ok":q.health.status==="passive"?"":" ko"}`,children:q.health.detail}),et&&c.jsxs("div",{className:`nc-set-test${et.ok?" ok":" ko"}`,children:[et.result,et.detail?` — ${et.detail}`:""]})]},`${q.kind||"direct"}:${q.nodeId||q.name}`)})]},Q.key)),(n||[]).some(Q=>!Q.direct&&Q.instanceId)&&c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("routed-node-aliases")}),c.jsx("small",{className:"nc-set-hint",children:g("routed-node-aliases-help")}),(n||[]).filter(Q=>!Q.direct&&Q.instanceId).map(Q=>c.jsxs("div",{className:"nc-set-node",children:[c.jsxs("div",{className:"nc-set-node-head",children:[c.jsx("span",{className:`nc-dot ${Q.status==="up"?"on":""}`}),c.jsx("b",{children:Q.label||Q.name}),c.jsx("small",{children:(Q.route||[]).join(" › ")})]}),c.jsxs("div",{className:"nc-set-node-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:async()=>{const q=window.prompt(g("node-local-alias"),Q.alias||"");if(q!=null){if(q.trim()&&!bs(q.normalize("NFC"))){p(g("err-label"));return}_(`${Q.instanceId}:alias`),p(null);try{q.trim()?await Vb(e,Q.instanceId,q):await Km(e,Q.instanceId),u()}catch(fe){p(String(fe.message||fe))}_(null)}},children:g("alias-on-device")}),Q.alias&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:async()=>{_(`${Q.instanceId}:alias-reset`),p(null);try{await Km(e,Q.instanceId),u()}catch(q){p(String(q.message||q))}_(null)},children:g("reset-alias")})]})]},Q.instanceId))]}),te&&c.jsxs("div",{className:"nc-set-form nc-local-share",children:[c.jsx("div",{className:"nc-sheet-label",children:g("share-local-heading")}),M.length>1&&c.jsxs("label",{className:"nc-field",children:[g("share-local-hub"),c.jsx("select",{value:te.name,disabled:o||!!f,onChange:Q=>T(Q.target.value),children:M.map(Q=>c.jsx("option",{value:Q.name,children:Q.label||Q.name},Q.name))})]}),c.jsxs("label",{className:"nc-check nc-node-share",children:[c.jsx("input",{type:"checkbox",checked:te.shared===!0,disabled:o||!!f,onChange:Q=>ue(Q.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:g("share-local-through").replace("{device}",R||g("local")).replace("{hub}",te.label||te.name)}),c.jsx("small",{children:g(te.shared?"share-node-on-desc":"share-node-off-desc")})]})]}),c.jsx("div",{className:`nc-set-test${te.shared&&(D!=null&&D.up)?" ok":D!=null&&D.up?"":" ko"}`,children:g(ie)}),!(D!=null&&D.up)&&c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:o||!!f,onClick:()=>ue(te.shared===!0),children:g("share-local-reconnect")})]}),c.jsx(pw,{token:e,deviceDefault:R,localNodeId:s&&s.nodeId||"",localNameDefault:s&&s.localName||"",readonly:o,onSuccess:a}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("invite-node")}),c.jsx("small",{className:"nc-set-hint",children:g($?"invite-network-hint":"invite-v2-hint")}),$?c.jsxs(c.Fragment,{children:[M.length>1&&c.jsxs("label",{className:"nc-field",children:[g("invite-network-label"),c.jsx("select",{value:$.name,disabled:o||!!f,onChange:Q=>{j(Q.target.value),S(null)},children:M.map(Q=>c.jsx("option",{value:Q.name,children:Q.label||Q.name},Q.name))})]}),c.jsxs("div",{className:"nc-set-info nc-invite-endpoint",children:[g("invite-network-via"),": ",c.jsx("b",{children:$.label||$.name})," · ",$.ssh,$.sshPort?`:${$.sshPort}`:""]}),c.jsx("small",{className:"nc-set-hint",children:g("invite-network-route")})]}):c.jsxs("label",{className:"nc-field",children:[g("invite-endpoint-label"),c.jsx("input",{placeholder:"user@host",value:x.ssh,disabled:o,onChange:Q=>C({...x,ssh:Q.target.value})}),c.jsx("small",{className:"nc-set-hint",children:g("invite-endpoint-needed")})]}),!$&&A&&c.jsxs("div",{className:"nc-invite-advanced",children:[c.jsxs("label",{className:"nc-field",children:[g("node-ssh-port-label"),c.jsx("input",{inputMode:"numeric",placeholder:"22",value:x.sshPort,disabled:o,onChange:Q=>C({...x,sshPort:Q.target.value.replace(/[^0-9]/g,"").slice(0,5)})})]}),c.jsxs("label",{className:"nc-field",children:[g("device-name-label"),c.jsx("input",{placeholder:R||"NexusCrew",value:G,disabled:o,onChange:Q=>U(Q.target.value)}),c.jsx("small",{className:"nc-set-hint",children:g("device-name-hint")})]}),c.jsxs("label",{className:"nc-field",children:[g("node-name-label"),c.jsx("input",{placeholder:dr(G||R||g("node-name-ph")),value:x.name,disabled:o,onChange:Q=>C({...x,name:Q.target.value?dr(Q.target.value):""})})]})]}),c.jsxs("div",{className:"nc-set-row nc-invite-actions",children:[c.jsx("button",{type:"button",className:"nc-btn primary",disabled:o||!!f||!$&&!x.ssh.trim(),onClick:ge,children:g("create-pairing-link")}),!$&&c.jsxs("button",{type:"button",className:"nc-btn ghost",disabled:!!f,onClick:()=>ee(Q=>!Q),children:[A?"▾":"▸"," ",g("pair-advanced")]})]}),y&&c.jsxs(c.Fragment,{children:[c.jsx(H2,{value:y.pairingUrl}),c.jsx($2,{text:y.pairingUrl}),c.jsx("div",{className:"nc-set-info",children:g("invite-next-steps")})]})]}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function U2({token:e,readonly:t}){const[n,s]=N.useState("idle"),[o,a]=N.useState(!1),[u,h]=N.useState(null);N.useEffect(()=>{let f=!1;return h2().then(_=>{f||s(_)}),()=>{f=!0}},[]);const p=async()=>{h(null),a(!0);try{n==="subscribed"?(await p2(e),s("idle")):(await f2(e),s("subscribed"))}catch(f){const _=String(f.message||f);_==="push-denied"?(s("denied"),h(g("push-denied"))):_==="push-unsupported"?(s("unsupported"),h(g("push-unsupported"))):h(_)}a(!1)};return n==="unsupported"?c.jsx("div",{className:"nc-set-info",children:g("push-unsupported")}):c.jsxs(c.Fragment,{children:[c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:t||o||n==="denied",title:t?g("settings-readonly"):"",onClick:p,children:g(n==="subscribed"?"push-disable":"push-enable")}),c.jsx("span",{className:"nc-set-info",children:g(n==="subscribed"?"push-on":n==="denied"?"push-denied":"push-off")})]}),u&&c.jsx("div",{className:"nc-err",children:u})]})}function K2(){const[e,t]=Hh();return c.jsxs("div",{className:"nc-set-tab nc-input-settings",children:[c.jsx("div",{className:"nc-set-info",children:g("input-settings-local")}),c.jsxs("div",{className:"nc-set-form",children:[c.jsxs("label",{className:"nc-field",children:[g("terminal-keyboard-gesture"),c.jsx("select",{"aria-label":g("terminal-keyboard-gesture"),value:e.terminalKeyboardGesture,onChange:n=>t({terminalKeyboardGesture:n.target.value}),children:qy.map(n=>c.jsx("option",{value:n,children:g(`terminal-keyboard-${n}`)},n))}),c.jsx("small",{children:g("terminal-keyboard-gesture-help")})]}),c.jsxs("label",{className:"nc-field",children:[g("keybar-layout"),c.jsx("select",{"aria-label":g("keybar-layout"),value:e.keybarLayout,onChange:n=>t({keybarLayout:n.target.value}),children:Vy.map(n=>c.jsx("option",{value:n,children:g(`keybar-layout-${n}`)},n))}),c.jsx("small",{children:g("keybar-layout-help")})]})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:e.keybarKeepsKeyboardClosed,onChange:n=>t({keybarKeepsKeyboardClosed:n.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:g("keybar-ime-lock")}),c.jsx("small",{children:g("keybar-ime-lock-help")})]})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:e.voiceKeepsKeyboardClosed,onChange:n=>t({voiceKeepsKeyboardClosed:n.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:g("voice-ime-lock")}),c.jsx("small",{children:g("voice-ime-lock-help")})]})]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:e.showKeybarEnter,onChange:n=>t({showKeybarEnter:n.target.checked})}),c.jsxs("span",{children:[c.jsx("b",{children:g("keybar-enter-show")}),c.jsx("small",{children:g("keybar-enter-show-help")})]})]}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:()=>t(lr),children:g("input-settings-reset")})})]})}function q2({token:e,roster:t=[],readonly:n}){const s=[{key:"local",route:[],label:g("diagnostics-local"),status:"up"},...t.filter(M=>Array.isArray(M.route)&&M.route.length).map(M=>({key:M.route.join("/"),route:M.route,label:M.label||M.name,status:M.status}))],[o,a]=N.useState("local"),u=s.find(M=>M.key===o)||s[0],[h,p]=N.useState(null),[f,_]=N.useState([]),[b,w]=N.useState(""),[y,S]=N.useState(!1),[x,C]=N.useState(!0),[k,j]=N.useState(900),[I,T]=N.useState("all"),[G,U]=N.useState(""),A=N.useRef(0),ee=N.useRef(null),W=N.useCallback(async()=>{try{const M=await e0(e,u.route);if(p(M),w(""),!y){const $=await t0(e,{after:A.current,limit:200},u.route);Array.isArray($.records)&&$.records.length&&(A.current=$.cursor,_(te=>[...te,...$.records].slice(-500)))}}catch(M){w(M&&M.status===404?g("diagnostics-unsupported"):String(M.message||M))}},[e,u.key,y]);N.useEffect(()=>{A.current=0,_([]),p(null),w("")},[u.key]),N.useEffect(()=>{let M=null,$=!0;const te=()=>{!$||document.visibilityState==="hidden"||W()},D=()=>{M&&clearInterval(M),M=document.visibilityState==="hidden"?null:setInterval(te,2e3),document.visibilityState!=="hidden"&&te()};return document.addEventListener("visibilitychange",D),D(),()=>{$=!1,M&&clearInterval(M),document.removeEventListener("visibilitychange",D)}},[W]),N.useEffect(()=>{x&&ee.current&&(ee.current.scrollTop=ee.current.scrollHeight)},[f,x]);const Z=f.filter(M=>(I==="all"||M.level===I)&&(!G||String(M.component||"").toLowerCase().includes(G.toLowerCase()))),K=Z.map(M=>`${M.ts} ${M.level.toUpperCase()} ${M.component} ${M.code} ${M.message}${Object.keys(M.meta||{}).length?` ${JSON.stringify(M.meta)}`:""}`).join(`
|
|
92
92
|
`),O=async M=>{w("");try{const $=await M();p($),await W()}catch($){w(String($.message||$))}},P=async()=>{try{await navigator.clipboard.writeText(K)}catch{w(g("copy-manual"))}},R=()=>{try{const M=new Blob([`${JSON.stringify(Z,null,2)}
|
|
93
|
-
`],{type:"application/json"}),$=URL.createObjectURL(M),te=document.createElement("a");te.href=$,te.download="nexuscrew-diagnostics.json",te.click(),URL.revokeObjectURL($)}catch{w(g("diagnostics-export-failed"))}};return c.jsxs("div",{className:"nc-set-tab nc-diagnostics",children:[c.jsxs("label",{className:"nc-field",children:[g("diagnostics-target"),c.jsx("select",{value:u.key,onChange:M=>a(M.target.value),children:s.map(M=>c.jsxs("option",{value:M.key,children:[M.label,M.status!=="up"?` · ${M.status}`:""]},M.key))})]}),c.jsxs("div",{className:"nc-set-form nc-diag-controls",children:[c.jsx("div",{className:"nc-sheet-label",children:g("diagnostics-verbose")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("select",{"aria-label":g("diagnostics-duration"),value:k,onChange:M=>j(Number(M.target.value)),disabled:n,children:[300,900,1800,3600].map(M=>c.jsxs("option",{value:M,children:[M/60," min"]},M))}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||(h==null?void 0:h.verbose)===!0,onClick:()=>O(()=>Vm(e,!0,k,u.route)),children:g("enable")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||(h==null?void 0:h.verbose)!==!0,onClick:()=>O(()=>Vm(e,!1,k,u.route)),children:g("stop")})]}),c.jsx("small",{className:"nc-set-hint",children:h!=null&&h.verbose?g("diagnostics-expires").replace("{time}",new Date(h.expiresAt).toLocaleTimeString()):g("diagnostics-off")})]}),c.jsxs("div",{className:"nc-diag-filters",children:[c.jsxs("select",{"aria-label":g("diagnostics-level"),value:I,onChange:M=>T(M.target.value),children:[c.jsx("option",{value:"all",children:g("diagnostics-all-levels")}),["debug","info","warn","error"].map(M=>c.jsx("option",{value:M,children:M},M))]}),c.jsx("input",{"aria-label":g("diagnostics-component"),placeholder:g("diagnostics-component"),value:G,onChange:M=>U(M.target.value)})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:y,onChange:M=>S(M.target.checked)})," ",g("diagnostics-pause")]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:x,onChange:M=>C(M.target.checked)})," ",g("diagnostics-autoscroll")]})]}),c.jsx("pre",{ref:ee,className:"nc-diag-log","aria-label":g("diagnostics-log"),children:K||g("diagnostics-empty")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:P,children:g("copy")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:R,children:g("diagnostics-export")}),c.jsx("button",{type:"button",className:"nc-btn danger",disabled:n,onClick:()=>{window.confirm(g("diagnostics-clear-confirm"))&&O(async()=>{const M=await n0(e,u.route);return A.current=0,_([]),M})},children:g("diagnostics-clear")})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})}function V2({token:e,settings:t,readonly:n,refresh:s}){const[o,a]=N.useState(null),[u,h]=N.useState(null),[p,f]=N.useState(!1),[_,b]=N.useState(!1),[w,y]=N.useState(null),[S,x]=N.useState(!0);N.useEffect(()=>{y(t&&t.update||null),x(!t||t.autoUpdate!==!1)},[t]);const C=async()=>{a(null),h(null),b(!0);try{const A=await Ub(e);h(A.note||"ok"),f(!1)}catch(A){a(String(A.message||A))}b(!1)},k=async()=>{a(null),h(null),b(!0);try{const A=await Jb(e);h(`${A.note||"ok"}${A.target?` (${A.target})`:""}`)}catch(A){a(String(A.message||A))}b(!1)},j=async A=>{a(null),h(null),b(!0);try{await b_(e,{autoUpdate:A}),x(A),s&&await s()}catch(ee){a(String(ee.message||ee))}b(!1)},I=async()=>{a(null),h(null),b(!0);try{y(await Qb(e))}catch(A){a(String(A.message||A))}b(!1)},T=async()=>{a(null),h(null),b(!0);try{const A=await Zb(e);y(A),h(g("npm-update-restarting"))}catch(A){a(String(A.message||A))}b(!1)},G=()=>{window.confirm(g("composer-clear-confirm"))&&(a(null),WC()?(window.dispatchEvent(new Event(oh)),h(g("composer-clear-done"))):a(g("composer-clear-failed")))},U=t&&t.service;return c.jsxs("div",{className:"nc-set-tab",children:[t&&c.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,c.jsx("br",{}),U&&U.installed?g("service-installed"):g("service-missing"),U&&U.installed?` · ${U.active?g("service-active"):g("service-inactive")}`:"",U?` · boot ${U.boot?"on":"off"}`:""]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||_,title:n?g("settings-readonly"):"",onClick:()=>{a(null),h(null),f(!0)},children:g("token-rotate")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||_,title:n?g("settings-readonly"):"",onClick:k,children:g("service-regenerate")})]}),c.jsxs("div",{className:"nc-set-form nc-update-settings",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:S,disabled:n||_||w&&!w.supported,onChange:A=>j(A.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:g("npm-auto-update")}),c.jsx("small",{children:g("npm-auto-update-help")})]})]}),w&&c.jsxs("div",{className:"nc-set-info",children:[g("npm-update-current")," v",w.current,w.latest?` · ${g("npm-update-latest")} v${w.latest}`:""," · ",g(`npm-update-${w.supported?w.phase:"unsupported"}`)]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||_||!w||!w.supported,onClick:I,children:g("npm-update-check")}),w&&w.available&&c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||_||w.phase==="installing",onClick:T,children:g("npm-update-install")})]}),w&&w.lastError&&c.jsx("div",{className:"nc-err",children:w.lastError})]}),c.jsx(U2,{token:e,readonly:n}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("composer-clear-data")}),c.jsx("small",{className:"nc-set-hint",children:g("composer-clear-data-help")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:G,children:g("composer-clear-data")})})]}),p&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("div",{children:g("token-rotate-explain")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:_,onClick:()=>f(!1),children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:_,onClick:C,children:g("confirm")})]})]}),u&&c.jsx("div",{className:"nc-set-note",children:u}),o&&c.jsx("div",{className:"nc-err",children:o})]})}function Y2({token:e,onClose:t,initialTab:n="nodes",initialLocation:s="",startNewCell:o=!1}){zn();const[a,u]=N.useState(n),[h,p]=N.useState(null),[f,_]=N.useState([]),[b,w]=N.useState(!1),[y,S]=N.useState(null),[x,C]=N.useState(0),k=Ch(e,!0,x),j=N.useCallback(async()=>{try{const I=await w_(e);p(I),S(null)}catch(I){S(String(I.message||I))}try{const I=await Hb(e);_(I.peers||[])}catch(I){S(String(I.message||I))}},[e]);return N.useEffect(()=>{j();const I=setInterval(j,5e3);return()=>clearInterval(I)},[j]),N.useEffect(()=>{let I=!1;return tn("/api/config",e).then(T=>T.json()).then(T=>{I||w(!!T.readonlyDefault)}).catch(()=>{}),()=>{I=!0}},[e]),c.jsx("div",{className:"nc-set-overlay",onClick:t,children:c.jsxs("div",{className:"nc-set-panel",onClick:I=>I.stopPropagation(),children:[c.jsxs("div",{className:"nc-set-head",children:[c.jsx(nt,{name:"gear",size:18}),c.jsx("b",{children:g("settings")}),c.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:g("close"),children:c.jsx(nt,{name:"x",size:18})})]}),b&&c.jsx("div",{className:"nc-set-readonly",children:g("settings-readonly")}),y&&c.jsx("div",{className:"nc-err",children:y}),c.jsx("div",{className:"nc-set-tabs",children:["nodes","fleet","input","diagnostics","system"].map(I=>c.jsx("button",{type:"button",className:`nc-set-tabbtn${a===I?" on":""}`,onClick:()=>u(I),children:g(`tab-${I}`)},I))}),c.jsxs("div",{className:"nc-set-body",children:[a==="nodes"&&c.jsx(W2,{token:e,nodes:f,roster:k,settings:h,readonly:b,refresh:j,refreshAliases:()=>C(I=>I+1)}),a==="fleet"&&c.jsx(F2,{token:e,readonly:b,startNewCell:o,initialLocation:s,targets:k.map(I=>({route:I.route,label:I.label||I.name,status:I.status}))}),a==="input"&&c.jsx(K2,{}),a==="diagnostics"&&c.jsx(q2,{token:e,roster:k,readonly:b}),a==="system"&&c.jsx(V2,{token:e,settings:h,readonly:b,refresh:j})]})]})})}function G2({token:e,initialPair:t,deviceDefault:n="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:u}){zn();const[h,p]=N.useState(t?"pair":"welcome"),[f,_]=N.useState(!1),[b,w]=N.useState(null),y=async()=>{_(!0),w(null);try{await b_(e,{wizardDone:!0}),a&&a(),u()}catch(S){w(String(S.message||S)),_(!1)}};return c.jsx("div",{className:"nc-wiz-overlay",children:c.jsxs("div",{className:"nc-wiz",children:[c.jsxs("div",{className:"nc-wiz-head",children:[c.jsx("b",{children:g("wizard-title")}),c.jsx("small",{children:g("hydra-simple")})]}),h==="welcome"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("local-ready")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:y,children:g("local-only")}),c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:()=>p("pair"),children:g("add-node")})]})]}),h==="pair"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx(pw,{token:e,initial:t||"",autoStart:!!t,deviceDefault:n,localNodeId:s,localNameDefault:o,onBusyChange:_,onSuccess:async()=>{a&&a(),p("done")}}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:()=>{a&&a(),p("welcome")},children:g("back")})})]}),h==="done"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("node-connected")}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:y,children:g("finish")})})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})})}function X2(e,t){if(typeof EventSource>"u"||!e)return()=>{};const n=new EventSource(`/api/events?token=${encodeURIComponent(e)}`);return n.onmessage=s=>{try{const o=JSON.parse(s.data);o&&typeof o=="object"&&typeof o.type=="string"&&t(o)}catch{}},()=>{try{n.close()}catch{}}}const J2=6e3,Q2=12e3;function Z2({n:e,onClose:t}){return c.jsxs("div",{className:`nc-ntf-toast${e.urgency==="high"?" high":""}`,role:"status","aria-live":"polite",children:[c.jsxs("div",{className:"nc-ntf-toast-txt",children:[c.jsx("b",{children:e.title}),e.body&&c.jsx("small",{children:e.body}),e.session&&c.jsx("span",{className:"nc-ntf-from",children:e.session})]}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:t,title:g("close"),children:c.jsx(nt,{name:"x",size:14})})]})}function eN({ask:e,token:t,onAnswered:n}){const[s,o]=N.useState(""),[a,u]=N.useState(!1),[h,p]=N.useState(null),f=async _=>{const b=String(_||"").trim();if(!(!b||a)){p(null),u(!0);try{await r0(t,e.id,b),n(e.id)}catch(w){p(String(w.message||w))}u(!1)}};return c.jsxs("div",{className:"nc-ask-card",children:[c.jsxs("div",{className:"nc-ask-head",children:[c.jsx("span",{className:"nc-ntf-from",children:e.session}),c.jsxs("code",{className:"nc-ask-id",children:["#",e.id]})]}),c.jsx("div",{className:"nc-ask-q",children:e.question}),Array.isArray(e.options)&&e.options.length>0&&c.jsx("div",{className:"nc-ask-opts",children:e.options.map(_=>c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:a,onClick:()=>f(_),children:_},_))}),c.jsxs("div",{className:"nc-ask-reply",children:[c.jsx("textarea",{rows:2,placeholder:g("ask-reply-ph"),value:s,disabled:a,onChange:_=>o(_.target.value)}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:a||!s.trim(),onClick:()=>f(s),children:g("send")})]}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function tN({token:e}){zn();const[t,n]=N.useState([]),[s,o]=N.useState([]),[a,u]=N.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),h=N.useRef(0),p=N.useCallback(b=>{n(w=>w.filter(y=>y.key!==b))},[]),f=N.useCallback(b=>{const w=`t${h.current+=1}`;n(y=>[...y.slice(-3),{...b,key:w}]),setTimeout(()=>p(w),b.urgency==="high"?Q2:J2)},[p]);N.useEffect(()=>{if(!e)return;let b=!1;i0(e).then(y=>{b||o(y.asks||[])}).catch(()=>{});const w=X2(e,y=>{y.type==="notify"?f(y):y.type==="ask"&&y.ask&&y.ask.id?o(S=>S.some(x=>x.id===y.ask.id)?S:[...S,y.ask]):y.type==="ask-answered"&&y.id&&o(S=>S.filter(x=>x.id!==y.id))});return()=>{b=!0,w()}},[e,f]);const _=b=>o(w=>w.filter(y=>y.id!==b));return c.jsxs(c.Fragment,{children:[t.length>0&&c.jsx("div",{className:"nc-ntf-toasts",children:t.map(b=>c.jsx(Z2,{n:b,onClose:()=>p(b.key)},b.key))}),s.length>0&&!a&&c.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>u(!0),title:g("asks-title"),children:["? ",c.jsx("span",{className:"nc-ask-count",children:s.length})]}),s.length>0&&a&&c.jsxs("div",{className:"nc-ask-panel",children:[c.jsxs("div",{className:"nc-ask-panel-head",children:[c.jsx("b",{children:g("asks-title")}),c.jsx("span",{className:"nc-ask-count",children:s.length}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>u(!1),title:g("close"),children:c.jsx(nt,{name:"x",size:16})})]}),c.jsx("div",{className:"nc-ask-panel-body",children:s.map(b=>c.jsx(eN,{ask:b,token:e,onAnswered:_},b.id))})]})]})}const pd=e=>vi(Ar(e)).length===0,md=e=>Array.isArray(e)?e.join("/"):"";function nN(e){const t=new Set,n=[];for(const s of Array.isArray(e)?e:[])!s||!Bn.test(String(s.instanceId||""))||!Array.isArray(s.route)||!s.route.length||t.has(s.instanceId)||(t.add(s.instanceId),n.push({instanceId:s.instanceId,route:[...s.route],label:String(s.label||s.name||s.route.join(" › ")),status:s.status||"offline"}));return n.sort((s,o)=>s.label.localeCompare(o.label))}function yo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function Rr(e,t,n,s=!1,o=!0){const a=t.instanceId;return{...e,id:ys(s?null:a,e.name),ownerId:a,ownerRoute:s?[]:[...t.route],ownerLabel:s?"Local":t.label,local:s,available:o,ownerTopology:Array.isArray(n)?n:[],layout:lh(e.layout,a,n)}}function iN(e,t,n,s,o=[]){const[a,u]=N.useState([]),[h,p]=N.useState(!1),[f,_]=N.useState("idle"),[b,w]=N.useState(""),[y,S]=N.useState(""),x=N.useRef([]),C=N.useRef([]),k=N.useRef(""),j=N.useRef(t),I=N.useRef(n),T=N.useRef(!1),G=N.useRef(!0),U=N.useRef(""),A=N.useMemo(()=>nN(o),[o]),ee=A.map(B=>`${B.instanceId}:${md(B.route)}:${B.status}:${B.label}`).join("|");C.current=A,I.current=n,j.current=t,x.current=a;const W=N.useCallback(B=>ch(B.layout,k.current,C.current),[]),Z=N.useCallback((B,L=!0,F=j.current)=>{const X=yv(B,tl()),ae=x.current.some(ge=>ge.id===F);x.current=X,u(X);const ue=X.find(ge=>ge.id===F);if(L&&ue){G.current=!0;const ge=W(ue);s(ge),ue.local&&fE(ue.name,ge)}else ae&&!ue&&(G.current=!0,s(_s()),w("deck non più condiviso dal nodo owner"));cE(X.filter(ge=>ge.local).map(ge=>ge.name))},[s,W]),K=N.useCallback(async B=>{let L=await qa(e);const F=L.decks.find(X=>X.name==="main");if(L.decks.length===1&&F&&F.revision===0&&pd(F.layout)){const X=lE(),ae=Ar(uh("main")||_s());if(!pd(ae)){const ue=lh(ae,B,[]);L.decks[0]=await Va(e,"main",ue,0,[])}for(const ue of X.filter(ge=>ge!=="main"))try{let ge=await Ym(e,ue,[]);const Q=Ar(uh(ue)||_s());pd(Q)||(ge=await Va(e,ue,lh(Q,B,[]),ge.revision,[]))}catch{}L=await qa(e)}return L},[e]),O=N.useCallback(async({migrate:B=!1}={})=>{const L=await bh(e,[]),F=Bn.test(String(L.instanceId||""))?L.instanceId:"";if(!F)throw new Error("instanceId locale non disponibile");k.current=F,S(F);const[X,ae]=await Promise.all([B?K(F):qa(e),Wm(e,[]).catch(()=>({nodes:[]}))]),ue={instanceId:F,route:[],label:"Local"},ge=X.decks.map(q=>Rr(q,ue,ae.nodes,!0,!0)),Q=x.current;return await Promise.all(C.current.map(async q=>{if(q.status!=="up"){ge.push(...Q.filter(fe=>!fe.local&&fe.ownerId===q.instanceId).map(fe=>({...fe,ownerRoute:[...q.route],ownerLabel:q.label,available:!1})));return}try{const[fe,Ie]=await Promise.all([qa(e,q.route),Wm(e,q.route).catch(()=>({nodes:[]}))]);ge.push(...fe.decks.map(et=>Rr(et,q,Ie.nodes,!1,!0)))}catch{ge.push(...Q.filter(Ie=>!Ie.local&&Ie.ownerId===q.instanceId).map(Ie=>({...Ie,ownerRoute:[...q.route],ownerLabel:q.label,available:!1})))}})),ge.sort((q,fe)=>q.local===fe.local?q.ownerLabel.localeCompare(fe.ownerLabel)||(q.name==="main"?-1:fe.name==="main"?1:q.name.localeCompare(fe.name)):q.local?-1:1),ge},[e,K,ee]);N.useEffect(()=>{if(!e)return;let B=!1;const L=U.current!==e;return U.current=e,O({migrate:L}).then(F=>{if(B)return;const X=x.current.find(ge=>ge.id===j.current),ae=F.find(ge=>ge.id===j.current),ue=ae&&(!X||ae.revision>X.revision||X.available!==ae.available||md(X.ownerRoute)!==md(ae.ownerRoute));Z(F,L||!!ue&&!T.current),p(!0),ae&&w("")}).catch(F=>{B||(p(!0),w(String(F.message||F)))}),()=>{B=!0}},[Z,O,ee,e]);const P=N.useCallback(async(B=j.current)=>{if(!h||!T.current)return!0;const L=x.current.find(F=>F.id===B);if(!L)return w(`deck inesistente: ${B}`),!1;if(L.available===!1)return w(`nodo owner offline: ${L.ownerLabel}`),!1;_("saving");try{const F=vv(Ar(I.current),L.ownerId,L.ownerTopology),X=await Va(e,L.name,F,L.revision,L.ownerRoute),ae=Rr(X,yo(L),L.ownerTopology,L.local,!0);return Z(x.current.map(ue=>ue.id===B?ae:ue),!1),T.current=!1,_("saved"),w(""),setTimeout(()=>_("idle"),1500),!0}catch(F){if(_("error"),w(String(F.message||F)),F.status===409&&F.data&&F.data.current){const X=x.current.find(ue=>ue.id===B),ae=Rr(F.data.current,yo(X),X.ownerTopology,X.local,!0);Z(x.current.map(ue=>ue.id===B?ae:ue),!1)}return!1}},[h,e,Z]);return N.useEffect(()=>{if(!h)return;if(G.current){G.current=!1;return}T.current=!0,_("saving");const B=setTimeout(P,650);return()=>clearTimeout(B)},[n,h,P]),N.useEffect(()=>{if(!h)return;const B=setInterval(async()=>{try{const L=await O(),F=x.current.find(ue=>ue.id===j.current),X=L.find(ue=>ue.id===j.current),ae=X&&(!F||X.revision>F.revision||F.available!==X.available);Z(L,ae&&!T.current)}catch{}},5e3);return()=>clearInterval(B)},[h,O,Z]),{decks:a,records:a,localNodeId:y,ready:h,saveState:f,error:b,setError:w,saveNow:P,select:async B=>{if(T.current&&!await P(j.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const L=x.current.find(F=>F.id===B);if(!L)throw new Error(`deck inesistente: ${B}`);return T.current=!1,G.current=!0,W(L)},add:async(B,L=null)=>{const F=L===mi?x.current.find(ue=>ue.local):L?x.current.find(ue=>ue.ownerId===L):null;if(!F||F.available===!1)throw new Error("nodo owner non disponibile");const X=await Ym(e,B,F.ownerRoute),ae=Rr(X,yo(F),F.ownerTopology,F.local,!0);return Z([...x.current,ae],!1),ae},rename:async(B,L)=>{const F=x.current.find(Q=>Q.id===B);if(!F)throw new Error("deck inesistente");if(B===j.current&&T.current&&!await P(B))throw new Error(`salvataggio di "${F.name}" fallito: rinomina annullata`);const X=x.current.find(Q=>Q.id===B);if(!X)throw new Error("deck inesistente");const ae=await s0(e,X.name,L,X.revision,X.ownerRoute),ue=Rr(ae,yo(X),X.ownerTopology,X.local,!0),ge=X.local?mi:X.ownerId;return Hu(dE(tl(),ge,B,ue.id)),Z(x.current.map(Q=>Q.id===B?ue:Q),!1),ue},remove:async B=>{const L=x.current.find(X=>X.id===B);if(!L)throw new Error("deck inesistente");B===j.current&&(T.current=!1),await o0(e,L.name,L.revision,L.ownerRoute);const F=L.local?mi:L.ownerId;Hu(hE(tl(),F,B)),Z(x.current.filter(X=>X.id!==B),!1)},reorder:(B,L)=>{const F=x.current.find(q=>q.id===B),X=x.current.find(q=>q.id===L);if(!F||!X)return!1;const ae=F.local?mi:F.ownerId,ue=X.local?mi:X.ownerId;if(ae!==ue)return!1;const ge=x.current.filter(q=>(q.local?mi:q.ownerId)===ae).map(q=>q.id),Q=Hu(uE(tl(),ae,B,L,ge));return Z(yv(x.current,Q),!1),!0},addTileTo:async(B,L)=>{const F=x.current.find(q=>q.id===B);if(!F)throw new Error("deck inesistente");if(F.available===!1)throw new Error(`nodo owner offline: ${F.ownerLabel}`);const X=Xy(L,k.current,C.current);if(!X)throw new Error("riferimento sessione non valido");const ae=W(F),ue=vv(z_(ae,X),F.ownerId,F.ownerTopology),ge=await Va(e,F.name,ue,F.revision,F.ownerRoute),Q=Rr(ge,yo(F),F.ownerTopology,F.local,!0);return Z(x.current.map(q=>q.id===B?Q:q),!1),Q},localMainId:ys(null,"main"),parseDeckId:Al}}const rN="/sw.js",hh="nc-sw-update";let fh=null,ph=!1,mh=null,xw=Object.freeze({needed:!1,kind:null,version:""}),Cw=!1;function Ew(){typeof window<"u"&&window.dispatchEvent(new Event(hh))}function Nw(){const e=mh||(ph?{kind:"reload",version:""}:null);xw=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function a_(){return xw}function sN(e){return typeof window>"u"?()=>{}:(window.addEventListener(hh,e),()=>window.removeEventListener(hh,e))}function ef(e){ph!==e&&(ph=e,Nw(),Ew())}function oN(e,t,n){let s=null;e&&t&&e!==t?s={kind:"install",version:e}:t&&n&&t!==n&&(s={kind:"reload",version:t});const o=JSON.stringify(s)===JSON.stringify(mh);mh=s,Nw(),o||Ew()}function aN(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&ef(!0)})}function lN(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{Cw&&location.reload()}),navigator.serviceWorker.register(rN).then(e=>{fh=e,e.waiting&&ef(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&aN(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function cN(){Cw=!0,ef(!1);const e=fh&&fh.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function uN({hash:e="",origin:t="",pathname:n="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${n||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const u=new URLSearchParams(o),h=u.get("token")||"",p=u.get("pair")||"",f=p?`${t||""}${n||""}#pair=${p}`:"";return{token:h,pair:f,nextUrl:a}}const Rw=9,Pw=24,jw="nc_side_w",l_="nc_side_min",dN=240,c_="(min-width:1024px) and (pointer:fine)";function hN(){const e=Number(localStorage.getItem(jw));return e>=180&&e<=480?e:dN}function fN(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=Rw&&e<=Pw?e:13}function pN(){const e={token:"",pair:""};try{const{token:t,pair:n,nextUrl:s}=uN({hash:location.hash,origin:location.origin,pathname:location.pathname,search:location.search});if(t){e.token=t;try{localStorage.setItem("nc_token",t)}catch{}}if(n){e.pair=n;try{sessionStorage.setItem("nc_pair",n)}catch{}}if(location.hash)try{history.replaceState(null,"",s)}catch{}}catch{}if(e.token||(e.token=sessionStorage.getItem("nc_token")||localStorage.getItem("nc_token")||""),!e.pair){const t=sessionStorage.getItem("nc_pair");t&&(e.pair=t)}return e}function mN(e){try{return Ar(uh(e))}catch{return _s()}}function u_(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 gN(){const[e,t]=N.useState(()=>window.matchMedia(c_).matches);return N.useEffect(()=>{const n=window.matchMedia(c_),s=o=>t(o.matches);return n.addEventListener("change",s),()=>n.removeEventListener("change",s)},[]),e}function d_({session:e,node:t,ownerId:n,cellName:s,token:o,readonly:a=!1,onBack:u}){zn();const[h]=Hh(),[p,f]=N.useState(!1),[_,b]=N.useState(()=>window.matchMedia("(pointer: coarse)").matches),[w,y]=N.useState(null),[S,x]=N.useState(fN),[C,k]=N.useState(s||e),[j,I]=N.useState(""),T=R=>x(M=>{const $=Math.max(Rw,Math.min(Pw,M+R));return localStorage.setItem("nc_fontsize",String($)),$}),G=N.useRef(()=>{}),U=N.useRef(()=>!1),A=N.useRef(()=>{}),ee=N.useRef(!1),[W,Z]=N.useState(!1),[K,O]=N.useState(!1),P=()=>{ee.current=!ee.current,Z(ee.current)};return N.useEffect(()=>{k(s||e)},[s,e]),N.useEffect(()=>{let R=!0;const M=t?t.split("/"):[],$=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function te(){let ie=null,B=null;try{const X=await(await tn(`${$}/sessions`,o)).json();Array.isArray(X.sessions)&&(ie=X.sessions.find(ae=>ae.name===e))}catch{}try{const F=await zr(o,M);F.available&&Array.isArray(F.cells)&&(B=F.cells.find(X=>X.tmuxSession===e))}catch{}if(!R)return;k(Wh({session:e,cell:B||(s?{cell:s}:null)}));let L="";B?L=`${B.engine}${B.key?`·${B.key}`:""}`:ie&&(L=ie.attached?`attached · ${u_(ie.activity)}`:ie.activity?u_(ie.activity):""),I(L)}te();const D=setInterval(te,4e3);return()=>{R=!1,clearInterval(D)}},[e,t,o]),c.jsxs("div",{className:"nc-app",children:[c.jsxs("header",{className:"nc-bar nc-bar-single",children:[c.jsxs("button",{onClick:u,title:g("sessions"),children:[c.jsx(nt,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:g("sessions")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:t?`${C} · ${t}`:C,children:C}),j&&c.jsx("small",{className:"nc-bar-sub",children:j})]}),c.jsxs("span",{className:"nc-bar-right",children:[c.jsx("button",{onClick:()=>T(-1),title:g("zoom-out"),children:c.jsx(nt,{name:"zoomOut",size:18})}),c.jsx("button",{onClick:()=>T(1),title:g("zoom-in"),children:c.jsx(nt,{name:"zoomIn",size:18})}),c.jsx("button",{onClick:()=>b(R=>!R),title:g("composer"),children:c.jsx(nt,{name:"keyboard",size:20})}),c.jsx("button",{onClick:()=>f(R=>!R),title:g("files"),children:c.jsx(nt,{name:"folder",size:20})})]})]}),c.jsx("div",{className:"nc-termwrap",children:c.jsx(Ay,{session:e,node:t,token:o,readonly:a,takeSize:!0,sendRef:G,composerRef:U,actionRef:A,ctrlRef:ee,setCtrlArmed:Z,onFiles:y,fontSize:S,selectionMode:K,onSelectionModeChange:O,keyboardGesture:h.terminalKeyboardGesture})}),c.jsx(DC,{onKeyboard:()=>b(R=>!R),send:R=>G.current(R),action:R=>A.current(R),ctrlArmed:W,onCtrl:P,selectionMode:K,onSelectionMode:O,keepKeyboardClosed:h.keybarKeepsKeyboardClosed,showEnter:h.showKeybarEnter,keybarLayout:h.keybarLayout}),_&&c.jsx(Ky,{submitText:R=>U.current(R),token:o,session:e,node:t,ownerId:n,keepKeyboardClosedOnVoice:h.voiceKeepsKeyboardClosed}),p&&c.jsx(Iy,{session:e,node:t,token:o,filesEvent:w,onClose:()=>f(!1)})]})}function vN(){zn();const[e]=N.useState(pN),[t,n]=N.useState(e.token),[s,o]=N.useState(e.pair||""),a=N.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[u,h]=N.useState(!1),p=gN(),[f]=N.useState(()=>aE(typeof location<"u"?location.pathname:"/")),[_,b]=N.useState(f.id),w=_===ys(null,On),[y,S]=N.useState(null),x=de=>{const Ee=fr(de);S(Ee?{...Ee,...typeof(de==null?void 0:de.cellName)=="string"&&de.cellName?{cellName:de.cellName}:{}}:null)},[C,k]=N.useState([]),[j,I]=N.useState([]),[T,G]=N.useState([]),[U,A]=N.useState(()=>f.ownerId?_s():mN(f.name)),[ee,W]=N.useState(null),[Z,K]=N.useState(null),O=de=>K(fr(de)),P=Ch(t,p),R=N.useMemo(()=>(P||[]).filter(de=>de.instanceId).map(de=>({instanceId:de.instanceId,route:de.route,label:de.label,status:de.status})),[P]),M=iN(t,_,U,A,R),$=M.decks;N.useEffect(()=>{const de=new Map;for(const Ee of P||[]){const Ue=(Ee.route||[Ee.name]).join("/"),_t=new Set((Ee.sessions||[]).map(dt=>dt.name));for(const dt of Ee.cells||[])!dt.cell||!dt.tmuxSession||dt.cell===dt.tmuxSession||_t.has(dt.cell)||de.set(`${Ue}:${dt.cell}`,`${Ue}:${dt.tmuxSession}`)}A(Ee=>T0(Ee,de))},[P]),N.useEffect(()=>{M.localNodeId&&A(de=>{const Ee=ch(de,M.localNodeId,R);return JSON.stringify(Ee)===JSON.stringify(de)?de:Ee})},[R,M.localNodeId]);const[te,D]=N.useState(null),[ie,B]=N.useState(null),L=N.useRef(0),[F,X]=N.useState(!1),[ae,ue]=N.useState(hN),[ge,Q]=N.useState(!w),[q,fe]=N.useState(()=>w?localStorage.getItem(l_)==="1":!0),[Ie,et]=N.useState(!1),[ze,Fe]=N.useState("nodes"),[Ct,Ge]=N.useState(!1),[rt,ut]=N.useState(""),ct=(de="nodes",Ee=!1,Ue="")=>{Fe(de),Ge(Ee),ut(Ue),et(!0)},[vt,it]=N.useState(!1),[lt,mt]=N.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[le,be]=N.useState(!1);N.useEffect(()=>{try{localStorage.setItem(jw,String(ae))}catch{}},[ae]),N.useEffect(()=>{try{localStorage.setItem(l_,q?"1":"")}catch{}},[q]),N.useEffect(()=>{if(!t)return;let de=!1;return Promise.all([w_(t),tn("/api/config",t).then(Ee=>Ee.json())]).then(([Ee,Ue])=>{de||(mt({deviceDefault:Ee.deviceName||"",localNodeId:Ee.nodeId||"",localNameDefault:Ee.localName||""}),be(!!Ue.readonlyDefault),(Ee.firstRun===!0&&!Ue.readonlyDefault||s)&&it(!0))}).catch(()=>{}),()=>{de=!0}},[t,s]);const Be=N.useCallback(async()=>{try{const Ee=await(await tn("/api/sessions",t)).json();Ee.error||k(Ee.sessions||[])}catch{}try{const de=await zr(t);I(de.available?de.cells||[]:[]),G(de.available?de.capabilities||[]:[])}catch{I([]),G([])}},[t]);N.useEffect(()=>{if(!p)return;Be();const de=setInterval(Be,4e3);return()=>clearInterval(de)},[p,Be]),N.useEffect(()=>{let de=!1;return tn("/api/config",t).then(Ee=>Ee.json()).then(Ee=>{de||oN(Ee.version,Ee.uiVersion,"0.8.32")}).catch(()=>{}),()=>{de=!0}},[t]);const $e=new Set([...C.map(de=>de.name),...P.flatMap(de=>de.sessions.map(Ee=>Ee.key))]),he=vi(U),V=de=>A(Ee=>{const Ue=Xy(de,M.localNodeId,R)||de,_t=z_(Ee,Ue);return _t===Ee&&vi(Ee).length>=9&&M.setError(g("grid-full")),_t}),ne=async(de,Ee=[])=>{try{await wh(t,de,Ee)}catch{return}const Ue=Ee.length?`${Ee.join("/")}:${de}`:de;A(_t=>yl(_t,Ue)),Be()},z=async(de,Ee,Ue=[])=>{try{await y_(t,de,Ee,Ue)}catch{return}Be()},se=async(de,Ee,Ue=[])=>{await g_(t,{cell:de,enabled:!!Ee},Ue),Be()},me=async de=>{if(!te)return;const{cell:Ee}=te,Ue=Array.isArray(te.route)?te.route:[];de.action==="up"?await _h(t,{cell:Ee,boot:!!de.boot,...de.engine?{engine:de.engine}:{},...de.model!==void 0?{model:de.model}:{},...de.permissionPolicy?{permissionPolicy:de.permissionPolicy}:{}},Ue):await fl(t,{cell:Ee,boot:!!de.boot},Ue);const _t=de.action==="up"?!!de.boot:de.boot?!1:!!te.boot;B({id:++L.current,cell:Ee,route:Ue,enabled:_t}),Be()},ke=N.useCallback(de=>{B(Ee=>(Ee==null?void 0:Ee.id)===de?null:Ee)},[]),Ne=async de=>{if(!(!(de!=null&&de.direct)||F)){X(!0);try{await kh(t,de.name,de.tunnelStatus==="up"?"down":"up")}finally{X(!1)}}},we=async(de,Ee)=>{const Ue=String(Ee||"").trim();return!(de!=null&&de.direct)||!bs(Ue)?!1:(await k_(t,de.name,Ue),!0)},Re=de=>{const Ee=$.find(Ue=>Ue.id===de);if(!Ee)return!1;try{const Ue=window.open($u(Ee,t),"_blank");return Ue&&(Ue.opener=null),!!Ue}catch{return!1}},Je=async de=>{if(!de||de===_)return;const Ee=await M.select(de),Ue=M.records.find(_t=>_t.id===de);b(de),A(Ee),W(null),K(null);try{history.replaceState(null,"",$u(Ue||de,null))}catch{}},Mt=async(de,Ee)=>{const Ue=await M.add(de,Ee);await Je(Ue.id)},Te=async(de,Ee)=>{const Ue=await M.rename(de,Ee);if(de===_){b(Ue.id),A(ch(Ue.layout,M.localNodeId,R)),W(null),K(null);try{history.replaceState(null,"",$u(Ue,null))}catch{}}},pn=async de=>{await M.remove(de),de===_&&await Je(M.localMainId)},bt=async(de,Ee)=>{!Ee||Ee===_||(await M.addTileTo(Ee,de),A(Ue=>yl(Ue,de)))};if(!t)return c.jsxs("div",{className:"nc-auth",children:[c.jsx("p",{children:g("auth-prompt")}),c.jsx("input",{onChange:de=>n(de.target.value.trim()),placeholder:"token"}),c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:u,onChange:de=>h(de.target.checked)})," ",g("remember-device")]}),c.jsx("button",{onClick:()=>{(u?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Ke=c.jsxs(c.Fragment,{children:[Ie&&c.jsx(Y2,{token:t,initialTab:ze,initialLocation:rt,startNewCell:Ct,onClose:()=>{et(!1),Ge(!1),ut("")}}),vt&&c.jsx(G2,{token:t,initialPair:s,...lt,onPairDone:a,onDone:()=>it(!1)}),c.jsx(tN,{token:t})]});if(!p)return y?c.jsxs(c.Fragment,{children:[c.jsx(d_,{session:y.session,node:y.node,ownerId:y.ownerId,cellName:y.cellName,token:t,readonly:le,onBack:()=>S(null)}),Ke]}):c.jsxs(c.Fragment,{children:[c.jsx(G0,{onPick:x,token:t,onSettings:ct}),Ke]});const $t=w||!ge;return c.jsxs("div",{className:"nc-workspace",children:[$t&&c.jsx(GC,{sessions:C,cells:j,activeSessions:he,nodeGroups:P,fleetCapabilities:T,bootSettlement:ie,onBootSettlementApplied:ke,localNodeId:M.localNodeId,onPick:O,onAddTile:V,onPower:D,onBoot:se,onBootError:de=>M.setError(String((de==null?void 0:de.message)||de)),onNodePower:Ne,onNodeRename:we,onKill:ne,onVisibility:z,onNew:()=>ct("fleet",!0),onSettings:ct,width:ae,collapsed:q,onResize:ue,onToggleCollapse:()=>fe(de=>!de)}),c.jsxs("div",{className:"nc-workspace-main",children:[c.jsx(jE,{decks:$,currentDeck:_,onCreate:Mt,onRename:Te,onDelete:pn,onReorder:M.reorder,onOpenWindow:Re,onNavigate:Je,saveState:M.saveState,error:M.error,sidebarVisible:$t,onToggleSidebar:w?null:()=>Q(de=>!de)}),c.jsx(nE,{layout:U,onLayoutChange:A,token:t,readonly:le,sessionsAlive:$e,focusSession:ee,onFocus:W,onOpenSingle:O,decks:$,currentDeck:_,onSendToDeck:bt,cells:j,nodeGroups:P})]}),Z&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(d_,{session:Z.session,node:Z.node,ownerId:Z.ownerId,cellName:Wh({session:Z.session,node:Z.node,ownerId:Z.ownerId,cells:j,nodeGroups:P}),token:t,readonly:le,onBack:()=>K(null)})}),te&&c.jsx(pl,{cell:te,token:t,route:Array.isArray(te.route)?te.route:[],onConfirm:me,onClose:()=>D(null)}),Ke]})}function _N(){zn();const e=N.useSyncExternalStore(sN,a_,a_);if(!e.needed)return null;const t=e.kind==="install"?g("install-version-mismatch").replace("{v}",e.version):g("update-available").replace("{v}",e.version).replace(" "," ").trim();return c.jsxs("div",{className:"nc-update",role:"status","aria-live":"polite",children:[c.jsx("span",{className:"nc-update-msg",children:t}),e.kind==="reload"&&c.jsx("button",{className:"nc-update-btn",onClick:cN,children:g("reload")})]})}lN();Lb.createRoot(document.getElementById("root")).render(c.jsxs(xb.StrictMode,{children:[c.jsx(vN,{}),c.jsx(_N,{})]}));
|
|
93
|
+
`],{type:"application/json"}),$=URL.createObjectURL(M),te=document.createElement("a");te.href=$,te.download="nexuscrew-diagnostics.json",te.click(),URL.revokeObjectURL($)}catch{w(g("diagnostics-export-failed"))}};return c.jsxs("div",{className:"nc-set-tab nc-diagnostics",children:[c.jsxs("label",{className:"nc-field",children:[g("diagnostics-target"),c.jsx("select",{value:u.key,onChange:M=>a(M.target.value),children:s.map(M=>c.jsxs("option",{value:M.key,children:[M.label,M.status!=="up"?` · ${M.status}`:""]},M.key))})]}),c.jsxs("div",{className:"nc-set-form nc-diag-controls",children:[c.jsx("div",{className:"nc-sheet-label",children:g("diagnostics-verbose")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("select",{"aria-label":g("diagnostics-duration"),value:k,onChange:M=>j(Number(M.target.value)),disabled:n,children:[300,900,1800,3600].map(M=>c.jsxs("option",{value:M,children:[M/60," min"]},M))}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||(h==null?void 0:h.verbose)===!0,onClick:()=>O(()=>Vm(e,!0,k,u.route)),children:g("enable")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||(h==null?void 0:h.verbose)!==!0,onClick:()=>O(()=>Vm(e,!1,k,u.route)),children:g("stop")})]}),c.jsx("small",{className:"nc-set-hint",children:h!=null&&h.verbose?g("diagnostics-expires").replace("{time}",new Date(h.expiresAt).toLocaleTimeString()):g("diagnostics-off")})]}),c.jsxs("div",{className:"nc-diag-filters",children:[c.jsxs("select",{"aria-label":g("diagnostics-level"),value:I,onChange:M=>T(M.target.value),children:[c.jsx("option",{value:"all",children:g("diagnostics-all-levels")}),["debug","info","warn","error"].map(M=>c.jsx("option",{value:M,children:M},M))]}),c.jsx("input",{"aria-label":g("diagnostics-component"),placeholder:g("diagnostics-component"),value:G,onChange:M=>U(M.target.value)})]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:y,onChange:M=>S(M.target.checked)})," ",g("diagnostics-pause")]}),c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:x,onChange:M=>C(M.target.checked)})," ",g("diagnostics-autoscroll")]})]}),c.jsx("pre",{ref:ee,className:"nc-diag-log","aria-label":g("diagnostics-log"),children:K||g("diagnostics-empty")}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:P,children:g("copy")}),c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:R,children:g("diagnostics-export")}),c.jsx("button",{type:"button",className:"nc-btn danger",disabled:n,onClick:()=>{window.confirm(g("diagnostics-clear-confirm"))&&O(async()=>{const M=await n0(e,u.route);return A.current=0,_([]),M})},children:g("diagnostics-clear")})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})}function V2({token:e,settings:t,readonly:n,refresh:s}){const[o,a]=N.useState(null),[u,h]=N.useState(null),[p,f]=N.useState(!1),[_,b]=N.useState(!1),[w,y]=N.useState(null),[S,x]=N.useState(!0);N.useEffect(()=>{y(t&&t.update||null),x(!t||t.autoUpdate!==!1)},[t]);const C=async()=>{a(null),h(null),b(!0);try{const A=await Ub(e);h(A.note||"ok"),f(!1)}catch(A){a(String(A.message||A))}b(!1)},k=async()=>{a(null),h(null),b(!0);try{const A=await Jb(e);h(`${A.note||"ok"}${A.target?` (${A.target})`:""}`)}catch(A){a(String(A.message||A))}b(!1)},j=async A=>{a(null),h(null),b(!0);try{await b_(e,{autoUpdate:A}),x(A),s&&await s()}catch(ee){a(String(ee.message||ee))}b(!1)},I=async()=>{a(null),h(null),b(!0);try{y(await Qb(e))}catch(A){a(String(A.message||A))}b(!1)},T=async()=>{a(null),h(null),b(!0);try{const A=await Zb(e);y(A),h(g("npm-update-restarting"))}catch(A){a(String(A.message||A))}b(!1)},G=()=>{window.confirm(g("composer-clear-confirm"))&&(a(null),WC()?(window.dispatchEvent(new Event(oh)),h(g("composer-clear-done"))):a(g("composer-clear-failed")))},U=t&&t.service;return c.jsxs("div",{className:"nc-set-tab",children:[t&&c.jsxs("div",{className:"nc-set-info",children:["v",t.version," · ",t.platform," · :",t.port,c.jsx("br",{}),U&&U.installed?g("service-installed"):g("service-missing"),U&&U.installed?` · ${U.active?g("service-active"):g("service-inactive")}`:"",U?` · boot ${U.boot?"on":"off"}`:""]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||_,title:n?g("settings-readonly"):"",onClick:()=>{a(null),h(null),f(!0)},children:g("token-rotate")}),c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||_,title:n?g("settings-readonly"):"",onClick:k,children:g("service-regenerate")})]}),c.jsxs("div",{className:"nc-set-form nc-update-settings",children:[c.jsxs("label",{className:"nc-check",children:[c.jsx("input",{type:"checkbox",checked:S,disabled:n||_||w&&!w.supported,onChange:A=>j(A.target.checked)}),c.jsxs("span",{children:[c.jsx("b",{children:g("npm-auto-update")}),c.jsx("small",{children:g("npm-auto-update-help")})]})]}),w&&c.jsxs("div",{className:"nc-set-info",children:[g("npm-update-current")," v",w.current,w.latest?` · ${g("npm-update-latest")} v${w.latest}`:""," · ",g(`npm-update-${w.supported?w.phase:"unsupported"}`)]}),c.jsxs("div",{className:"nc-set-row",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:n||_||!w||!w.supported,onClick:I,children:g("npm-update-check")}),w&&w.available&&c.jsx("button",{type:"button",className:"nc-btn primary",disabled:n||_||w.phase==="installing",onClick:T,children:g("npm-update-install")})]}),w&&w.lastError&&c.jsx("div",{className:"nc-err",children:w.lastError})]}),c.jsx(U2,{token:e,readonly:n}),c.jsxs("div",{className:"nc-set-form",children:[c.jsx("div",{className:"nc-sheet-label",children:g("composer-clear-data")}),c.jsx("small",{className:"nc-set-hint",children:g("composer-clear-data-help")}),c.jsx("div",{className:"nc-set-row",children:c.jsx("button",{type:"button",className:"nc-btn ghost",onClick:G,children:g("composer-clear-data")})})]}),p&&c.jsxs("div",{className:"nc-set-confirm",children:[c.jsx("div",{children:g("token-rotate-explain")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:_,onClick:()=>f(!1),children:g("cancel")}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:_,onClick:C,children:g("confirm")})]})]}),u&&c.jsx("div",{className:"nc-set-note",children:u}),o&&c.jsx("div",{className:"nc-err",children:o})]})}function Y2({token:e,onClose:t,initialTab:n="nodes",initialLocation:s="",startNewCell:o=!1}){zn();const[a,u]=N.useState(n),[h,p]=N.useState(null),[f,_]=N.useState([]),[b,w]=N.useState(!1),[y,S]=N.useState(null),[x,C]=N.useState(0),k=Ch(e,!0,x),j=N.useCallback(async()=>{try{const I=await w_(e);p(I),S(null)}catch(I){S(String(I.message||I))}try{const I=await Hb(e);_(I.peers||[])}catch(I){S(String(I.message||I))}},[e]);return N.useEffect(()=>{j();const I=setInterval(j,5e3);return()=>clearInterval(I)},[j]),N.useEffect(()=>{let I=!1;return tn("/api/config",e).then(T=>T.json()).then(T=>{I||w(!!T.readonlyDefault)}).catch(()=>{}),()=>{I=!0}},[e]),c.jsx("div",{className:"nc-set-overlay",onClick:t,children:c.jsxs("div",{className:"nc-set-panel",onClick:I=>I.stopPropagation(),children:[c.jsxs("div",{className:"nc-set-head",children:[c.jsx(nt,{name:"gear",size:18}),c.jsx("b",{children:g("settings")}),c.jsx("button",{type:"button",className:"nc-set-close",onClick:t,title:g("close"),children:c.jsx(nt,{name:"x",size:18})})]}),b&&c.jsx("div",{className:"nc-set-readonly",children:g("settings-readonly")}),y&&c.jsx("div",{className:"nc-err",children:y}),c.jsx("div",{className:"nc-set-tabs",children:["nodes","fleet","input","diagnostics","system"].map(I=>c.jsx("button",{type:"button",className:`nc-set-tabbtn${a===I?" on":""}`,onClick:()=>u(I),children:g(`tab-${I}`)},I))}),c.jsxs("div",{className:"nc-set-body",children:[a==="nodes"&&c.jsx(W2,{token:e,nodes:f,roster:k,settings:h,readonly:b,refresh:j,refreshAliases:()=>C(I=>I+1)}),a==="fleet"&&c.jsx(F2,{token:e,readonly:b,startNewCell:o,initialLocation:s,targets:k.map(I=>({route:I.route,label:I.label||I.name,status:I.status}))}),a==="input"&&c.jsx(K2,{}),a==="diagnostics"&&c.jsx(q2,{token:e,roster:k,readonly:b}),a==="system"&&c.jsx(V2,{token:e,settings:h,readonly:b,refresh:j})]})]})})}function G2({token:e,initialPair:t,deviceDefault:n="",localNodeId:s="",localNameDefault:o="",onPairDone:a,onDone:u}){zn();const[h,p]=N.useState(t?"pair":"welcome"),[f,_]=N.useState(!1),[b,w]=N.useState(null),y=async()=>{_(!0),w(null);try{await b_(e,{wizardDone:!0}),a&&a(),u()}catch(S){w(String(S.message||S)),_(!1)}};return c.jsx("div",{className:"nc-wiz-overlay",children:c.jsxs("div",{className:"nc-wiz",children:[c.jsxs("div",{className:"nc-wiz-head",children:[c.jsx("b",{children:g("wizard-title")}),c.jsx("small",{children:g("hydra-simple")})]}),h==="welcome"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("local-ready")}),c.jsxs("div",{className:"nc-sheet-actions",children:[c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:y,children:g("local-only")}),c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:()=>p("pair"),children:g("add-node")})]})]}),h==="pair"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx(pw,{token:e,initial:t||"",autoStart:!!t,deviceDefault:n,localNodeId:s,localNameDefault:o,onBusyChange:_,onSuccess:async()=>{a&&a(),p("done")}}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn ghost",disabled:f,onClick:()=>{a&&a(),p("welcome")},children:g("back")})})]}),h==="done"&&c.jsxs("div",{className:"nc-wiz-body",children:[c.jsx("div",{className:"nc-wiz-done",children:g("node-connected")}),c.jsx("div",{className:"nc-sheet-actions",children:c.jsx("button",{className:"nc-btn primary",disabled:f,onClick:y,children:g("finish")})})]}),b&&c.jsx("div",{className:"nc-err",children:b})]})})}function X2(e,t){if(typeof EventSource>"u"||!e)return()=>{};const n=new EventSource(`/api/events?token=${encodeURIComponent(e)}`);return n.onmessage=s=>{try{const o=JSON.parse(s.data);o&&typeof o=="object"&&typeof o.type=="string"&&t(o)}catch{}},()=>{try{n.close()}catch{}}}const J2=6e3,Q2=12e3;function Z2({n:e,onClose:t}){return c.jsxs("div",{className:`nc-ntf-toast${e.urgency==="high"?" high":""}`,role:"status","aria-live":"polite",children:[c.jsxs("div",{className:"nc-ntf-toast-txt",children:[c.jsx("b",{children:e.title}),e.body&&c.jsx("small",{children:e.body}),e.session&&c.jsx("span",{className:"nc-ntf-from",children:e.session})]}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:t,title:g("close"),children:c.jsx(nt,{name:"x",size:14})})]})}function eN({ask:e,token:t,onAnswered:n}){const[s,o]=N.useState(""),[a,u]=N.useState(!1),[h,p]=N.useState(null),f=async _=>{const b=String(_||"").trim();if(!(!b||a)){p(null),u(!0);try{await r0(t,e.id,b),n(e.id)}catch(w){p(String(w.message||w))}u(!1)}};return c.jsxs("div",{className:"nc-ask-card",children:[c.jsxs("div",{className:"nc-ask-head",children:[c.jsx("span",{className:"nc-ntf-from",children:e.session}),c.jsxs("code",{className:"nc-ask-id",children:["#",e.id]})]}),c.jsx("div",{className:"nc-ask-q",children:e.question}),Array.isArray(e.options)&&e.options.length>0&&c.jsx("div",{className:"nc-ask-opts",children:e.options.map(_=>c.jsx("button",{type:"button",className:"nc-btn ghost",disabled:a,onClick:()=>f(_),children:_},_))}),c.jsxs("div",{className:"nc-ask-reply",children:[c.jsx("textarea",{rows:2,placeholder:g("ask-reply-ph"),value:s,disabled:a,onChange:_=>o(_.target.value)}),c.jsx("button",{type:"button",className:"nc-btn primary",disabled:a||!s.trim(),onClick:()=>f(s),children:g("send")})]}),h&&c.jsx("div",{className:"nc-err",children:h})]})}function tN({token:e}){zn();const[t,n]=N.useState([]),[s,o]=N.useState([]),[a,u]=N.useState(()=>{try{return/(?:^|[#&])ask=/.test(location.hash)}catch{return!1}}),h=N.useRef(0),p=N.useCallback(b=>{n(w=>w.filter(y=>y.key!==b))},[]),f=N.useCallback(b=>{const w=`t${h.current+=1}`;n(y=>[...y.slice(-3),{...b,key:w}]),setTimeout(()=>p(w),b.urgency==="high"?Q2:J2)},[p]);N.useEffect(()=>{if(!e)return;let b=!1;i0(e).then(y=>{b||o(y.asks||[])}).catch(()=>{});const w=X2(e,y=>{y.type==="notify"?f(y):y.type==="ask"&&y.ask&&y.ask.id?o(S=>S.some(x=>x.id===y.ask.id)?S:[...S,y.ask]):y.type==="ask-answered"&&y.id&&o(S=>S.filter(x=>x.id!==y.id))});return()=>{b=!0,w()}},[e,f]);const _=b=>o(w=>w.filter(y=>y.id!==b));return c.jsxs(c.Fragment,{children:[t.length>0&&c.jsx("div",{className:"nc-ntf-toasts",children:t.map(b=>c.jsx(Z2,{n:b,onClose:()=>p(b.key)},b.key))}),s.length>0&&!a&&c.jsxs("button",{type:"button",className:"nc-ask-badge",onClick:()=>u(!0),title:g("asks-title"),children:["? ",c.jsx("span",{className:"nc-ask-count",children:s.length})]}),s.length>0&&a&&c.jsxs("div",{className:"nc-ask-panel",children:[c.jsxs("div",{className:"nc-ask-panel-head",children:[c.jsx("b",{children:g("asks-title")}),c.jsx("span",{className:"nc-ask-count",children:s.length}),c.jsx("button",{type:"button",className:"nc-ntf-x",onClick:()=>u(!1),title:g("close"),children:c.jsx(nt,{name:"x",size:16})})]}),c.jsx("div",{className:"nc-ask-panel-body",children:s.map(b=>c.jsx(eN,{ask:b,token:e,onAnswered:_},b.id))})]})]})}const pd=e=>vi(Ar(e)).length===0,md=e=>Array.isArray(e)?e.join("/"):"";function nN(e){const t=new Set,n=[];for(const s of Array.isArray(e)?e:[])!s||!Bn.test(String(s.instanceId||""))||!Array.isArray(s.route)||!s.route.length||t.has(s.instanceId)||(t.add(s.instanceId),n.push({instanceId:s.instanceId,route:[...s.route],label:String(s.label||s.name||s.route.join(" › ")),status:s.status||"offline"}));return n.sort((s,o)=>s.label.localeCompare(o.label))}function yo(e){return{instanceId:e.ownerId,route:e.ownerRoute||[],label:e.ownerLabel,status:e.available===!1?"offline":"up"}}function Rr(e,t,n,s=!1,o=!0){const a=t.instanceId;return{...e,id:ys(s?null:a,e.name),ownerId:a,ownerRoute:s?[]:[...t.route],ownerLabel:s?"Local":t.label,local:s,available:o,ownerTopology:Array.isArray(n)?n:[],layout:lh(e.layout,a,n)}}function iN(e,t,n,s,o=[]){const[a,u]=N.useState([]),[h,p]=N.useState(!1),[f,_]=N.useState("idle"),[b,w]=N.useState(""),[y,S]=N.useState(""),x=N.useRef([]),C=N.useRef([]),k=N.useRef(""),j=N.useRef(t),I=N.useRef(n),T=N.useRef(!1),G=N.useRef(!0),U=N.useRef(""),A=N.useMemo(()=>nN(o),[o]),ee=A.map(B=>`${B.instanceId}:${md(B.route)}:${B.status}:${B.label}`).join("|");C.current=A,I.current=n,j.current=t,x.current=a;const W=N.useCallback(B=>ch(B.layout,k.current,C.current),[]),Z=N.useCallback((B,L=!0,F=j.current)=>{const X=yv(B,tl()),ae=x.current.some(ge=>ge.id===F);x.current=X,u(X);const ue=X.find(ge=>ge.id===F);if(L&&ue){G.current=!0;const ge=W(ue);s(ge),ue.local&&fE(ue.name,ge)}else ae&&!ue&&(G.current=!0,s(_s()),w("deck non più condiviso dal nodo owner"));cE(X.filter(ge=>ge.local).map(ge=>ge.name))},[s,W]),K=N.useCallback(async B=>{let L=await qa(e);const F=L.decks.find(X=>X.name==="main");if(L.decks.length===1&&F&&F.revision===0&&pd(F.layout)){const X=lE(),ae=Ar(uh("main")||_s());if(!pd(ae)){const ue=lh(ae,B,[]);L.decks[0]=await Va(e,"main",ue,0,[])}for(const ue of X.filter(ge=>ge!=="main"))try{let ge=await Ym(e,ue,[]);const Q=Ar(uh(ue)||_s());pd(Q)||(ge=await Va(e,ue,lh(Q,B,[]),ge.revision,[]))}catch{}L=await qa(e)}return L},[e]),O=N.useCallback(async({migrate:B=!1}={})=>{const L=await bh(e,[]),F=Bn.test(String(L.instanceId||""))?L.instanceId:"";if(!F)throw new Error("instanceId locale non disponibile");k.current=F,S(F);const[X,ae]=await Promise.all([B?K(F):qa(e),Wm(e,[]).catch(()=>({nodes:[]}))]),ue={instanceId:F,route:[],label:"Local"},ge=X.decks.map(q=>Rr(q,ue,ae.nodes,!0,!0)),Q=x.current;return await Promise.all(C.current.map(async q=>{if(q.status!=="up"){ge.push(...Q.filter(fe=>!fe.local&&fe.ownerId===q.instanceId).map(fe=>({...fe,ownerRoute:[...q.route],ownerLabel:q.label,available:!1})));return}try{const[fe,Ie]=await Promise.all([qa(e,q.route),Wm(e,q.route).catch(()=>({nodes:[]}))]);ge.push(...fe.decks.map(et=>Rr(et,q,Ie.nodes,!1,!0)))}catch{ge.push(...Q.filter(Ie=>!Ie.local&&Ie.ownerId===q.instanceId).map(Ie=>({...Ie,ownerRoute:[...q.route],ownerLabel:q.label,available:!1})))}})),ge.sort((q,fe)=>q.local===fe.local?q.ownerLabel.localeCompare(fe.ownerLabel)||(q.name==="main"?-1:fe.name==="main"?1:q.name.localeCompare(fe.name)):q.local?-1:1),ge},[e,K,ee]);N.useEffect(()=>{if(!e)return;let B=!1;const L=U.current!==e;return U.current=e,O({migrate:L}).then(F=>{if(B)return;const X=x.current.find(ge=>ge.id===j.current),ae=F.find(ge=>ge.id===j.current),ue=ae&&(!X||ae.revision>X.revision||X.available!==ae.available||md(X.ownerRoute)!==md(ae.ownerRoute));Z(F,L||!!ue&&!T.current),p(!0),ae&&w("")}).catch(F=>{B||(p(!0),w(String(F.message||F)))}),()=>{B=!0}},[Z,O,ee,e]);const P=N.useCallback(async(B=j.current)=>{if(!h||!T.current)return!0;const L=x.current.find(F=>F.id===B);if(!L)return w(`deck inesistente: ${B}`),!1;if(L.available===!1)return w(`nodo owner offline: ${L.ownerLabel}`),!1;_("saving");try{const F=vv(Ar(I.current),L.ownerId,L.ownerTopology),X=await Va(e,L.name,F,L.revision,L.ownerRoute),ae=Rr(X,yo(L),L.ownerTopology,L.local,!0);return Z(x.current.map(ue=>ue.id===B?ae:ue),!1),T.current=!1,_("saved"),w(""),setTimeout(()=>_("idle"),1500),!0}catch(F){if(_("error"),w(String(F.message||F)),F.status===409&&F.data&&F.data.current){const X=x.current.find(ue=>ue.id===B),ae=Rr(F.data.current,yo(X),X.ownerTopology,X.local,!0);Z(x.current.map(ue=>ue.id===B?ae:ue),!1)}return!1}},[h,e,Z]);return N.useEffect(()=>{if(!h)return;if(G.current){G.current=!1;return}T.current=!0,_("saving");const B=setTimeout(P,650);return()=>clearTimeout(B)},[n,h,P]),N.useEffect(()=>{if(!h)return;const B=setInterval(async()=>{try{const L=await O(),F=x.current.find(ue=>ue.id===j.current),X=L.find(ue=>ue.id===j.current),ae=X&&(!F||X.revision>F.revision||F.available!==X.available);Z(L,ae&&!T.current)}catch{}},5e3);return()=>clearInterval(B)},[h,O,Z]),{decks:a,records:a,localNodeId:y,ready:h,saveState:f,error:b,setError:w,saveNow:P,select:async B=>{if(T.current&&!await P(j.current))throw new Error("salvataggio del deck corrente fallito: cambio annullato");const L=x.current.find(F=>F.id===B);if(!L)throw new Error(`deck inesistente: ${B}`);return T.current=!1,G.current=!0,W(L)},add:async(B,L=null)=>{const F=L===mi?x.current.find(ue=>ue.local):L?x.current.find(ue=>ue.ownerId===L):null;if(!F||F.available===!1)throw new Error("nodo owner non disponibile");const X=await Ym(e,B,F.ownerRoute),ae=Rr(X,yo(F),F.ownerTopology,F.local,!0);return Z([...x.current,ae],!1),ae},rename:async(B,L)=>{const F=x.current.find(Q=>Q.id===B);if(!F)throw new Error("deck inesistente");if(B===j.current&&T.current&&!await P(B))throw new Error(`salvataggio di "${F.name}" fallito: rinomina annullata`);const X=x.current.find(Q=>Q.id===B);if(!X)throw new Error("deck inesistente");const ae=await s0(e,X.name,L,X.revision,X.ownerRoute),ue=Rr(ae,yo(X),X.ownerTopology,X.local,!0),ge=X.local?mi:X.ownerId;return Hu(dE(tl(),ge,B,ue.id)),Z(x.current.map(Q=>Q.id===B?ue:Q),!1),ue},remove:async B=>{const L=x.current.find(X=>X.id===B);if(!L)throw new Error("deck inesistente");B===j.current&&(T.current=!1),await o0(e,L.name,L.revision,L.ownerRoute);const F=L.local?mi:L.ownerId;Hu(hE(tl(),F,B)),Z(x.current.filter(X=>X.id!==B),!1)},reorder:(B,L)=>{const F=x.current.find(q=>q.id===B),X=x.current.find(q=>q.id===L);if(!F||!X)return!1;const ae=F.local?mi:F.ownerId,ue=X.local?mi:X.ownerId;if(ae!==ue)return!1;const ge=x.current.filter(q=>(q.local?mi:q.ownerId)===ae).map(q=>q.id),Q=Hu(uE(tl(),ae,B,L,ge));return Z(yv(x.current,Q),!1),!0},addTileTo:async(B,L)=>{const F=x.current.find(q=>q.id===B);if(!F)throw new Error("deck inesistente");if(F.available===!1)throw new Error(`nodo owner offline: ${F.ownerLabel}`);const X=Xy(L,k.current,C.current);if(!X)throw new Error("riferimento sessione non valido");const ae=W(F),ue=vv(z_(ae,X),F.ownerId,F.ownerTopology),ge=await Va(e,F.name,ue,F.revision,F.ownerRoute),Q=Rr(ge,yo(F),F.ownerTopology,F.local,!0);return Z(x.current.map(q=>q.id===B?Q:q),!1),Q},localMainId:ys(null,"main"),parseDeckId:Al}}const rN="/sw.js",hh="nc-sw-update";let fh=null,ph=!1,mh=null,xw=Object.freeze({needed:!1,kind:null,version:""}),Cw=!1;function Ew(){typeof window<"u"&&window.dispatchEvent(new Event(hh))}function Nw(){const e=mh||(ph?{kind:"reload",version:""}:null);xw=Object.freeze({needed:!!e,kind:(e==null?void 0:e.kind)||null,version:(e==null?void 0:e.version)||""})}function a_(){return xw}function sN(e){return typeof window>"u"?()=>{}:(window.addEventListener(hh,e),()=>window.removeEventListener(hh,e))}function ef(e){ph!==e&&(ph=e,Nw(),Ew())}function oN(e,t,n){let s=null;e&&t&&e!==t?s={kind:"install",version:e}:t&&n&&t!==n&&(s={kind:"reload",version:t});const o=JSON.stringify(s)===JSON.stringify(mh);mh=s,Nw(),o||Ew()}function aN(e){e.addEventListener("statechange",()=>{e.state==="installed"&&navigator.serviceWorker.controller&&ef(!0)})}function lN(){typeof navigator>"u"||!("serviceWorker"in navigator)||(navigator.serviceWorker.addEventListener("controllerchange",()=>{Cw&&location.reload()}),navigator.serviceWorker.register(rN).then(e=>{fh=e,e.waiting&&ef(!0),e.addEventListener("updatefound",()=>{const t=e.installing;t&&aN(t)}),setInterval(()=>{e.update().catch(()=>{})},3600*1e3)}).catch(()=>{}))}function cN(){Cw=!0,ef(!1);const e=fh&&fh.waiting;e?(e.postMessage({type:"SKIP_WAITING"}),setTimeout(()=>location.reload(),4e3)):location.reload()}function uN({hash:e="",origin:t="",pathname:n="",search:s=""}={}){const o=String(e||"").replace(/^#/,""),a=`${n||""}${s||""}`;if(!o)return{token:"",pair:"",nextUrl:a};const u=new URLSearchParams(o),h=u.get("token")||"",p=u.get("pair")||"",f=p?`${t||""}${n||""}#pair=${p}`:"";return{token:h,pair:f,nextUrl:a}}const Rw=9,Pw=24,jw="nc_side_w",l_="nc_side_min",dN=240,c_="(min-width:1024px) and (pointer:fine)";function hN(){const e=Number(localStorage.getItem(jw));return e>=180&&e<=480?e:dN}function fN(){const e=Number(localStorage.getItem("nc_fontsize"));return e>=Rw&&e<=Pw?e:13}function pN(){const e={token:"",pair:""};try{const{token:t,pair:n,nextUrl:s}=uN({hash:location.hash,origin:location.origin,pathname:location.pathname,search:location.search});if(t){e.token=t;try{localStorage.setItem("nc_token",t)}catch{}}if(n){e.pair=n;try{sessionStorage.setItem("nc_pair",n)}catch{}}if(location.hash)try{history.replaceState(null,"",s)}catch{}}catch{}if(e.token||(e.token=sessionStorage.getItem("nc_token")||localStorage.getItem("nc_token")||""),!e.pair){const t=sessionStorage.getItem("nc_pair");t&&(e.pair=t)}return e}function mN(e){try{return Ar(uh(e))}catch{return _s()}}function u_(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 gN(){const[e,t]=N.useState(()=>window.matchMedia(c_).matches);return N.useEffect(()=>{const n=window.matchMedia(c_),s=o=>t(o.matches);return n.addEventListener("change",s),()=>n.removeEventListener("change",s)},[]),e}function d_({session:e,node:t,ownerId:n,cellName:s,token:o,readonly:a=!1,onBack:u}){zn();const[h]=Hh(),[p,f]=N.useState(!1),[_,b]=N.useState(()=>window.matchMedia("(pointer: coarse)").matches),[w,y]=N.useState(null),[S,x]=N.useState(fN),[C,k]=N.useState(s||e),[j,I]=N.useState(""),T=R=>x(M=>{const $=Math.max(Rw,Math.min(Pw,M+R));return localStorage.setItem("nc_fontsize",String($)),$}),G=N.useRef(()=>{}),U=N.useRef(()=>!1),A=N.useRef(()=>{}),ee=N.useRef(!1),[W,Z]=N.useState(!1),[K,O]=N.useState(!1),P=()=>{ee.current=!ee.current,Z(ee.current)};return N.useEffect(()=>{k(s||e)},[s,e]),N.useEffect(()=>{let R=!0;const M=t?t.split("/"):[],$=t?`/api/route/${t.split("/").map(encodeURIComponent).join("/")}/_`:"/api";async function te(){let ie=null,B=null;try{const X=await(await tn(`${$}/sessions`,o)).json();Array.isArray(X.sessions)&&(ie=X.sessions.find(ae=>ae.name===e))}catch{}try{const F=await zr(o,M);F.available&&Array.isArray(F.cells)&&(B=F.cells.find(X=>X.tmuxSession===e))}catch{}if(!R)return;k(Wh({session:e,cell:B||(s?{cell:s}:null)}));let L="";B?L=`${B.engine}${B.key?`·${B.key}`:""}`:ie&&(L=ie.attached?`attached · ${u_(ie.activity)}`:ie.activity?u_(ie.activity):""),I(L)}te();const D=setInterval(te,4e3);return()=>{R=!1,clearInterval(D)}},[e,t,o]),c.jsxs("div",{className:"nc-app",children:[c.jsxs("header",{className:"nc-bar nc-bar-single",children:[c.jsxs("button",{onClick:u,title:g("sessions"),children:[c.jsx(nt,{name:"chevronLeft",size:18}),c.jsx("span",{className:"nc-bar-label",children:g("sessions")})]}),c.jsxs("span",{className:"nc-bar-center",children:[c.jsx("b",{title:t?`${C} · ${t}`:C,children:C}),j&&c.jsx("small",{className:"nc-bar-sub",children:j})]}),c.jsxs("span",{className:"nc-bar-right",children:[c.jsx("button",{onClick:()=>T(-1),title:g("zoom-out"),children:c.jsx(nt,{name:"zoomOut",size:18})}),c.jsx("button",{onClick:()=>T(1),title:g("zoom-in"),children:c.jsx(nt,{name:"zoomIn",size:18})}),c.jsx("button",{onClick:()=>b(R=>!R),title:g("composer"),children:c.jsx(nt,{name:"keyboard",size:20})}),c.jsx("button",{onClick:()=>f(R=>!R),title:g("files"),children:c.jsx(nt,{name:"folder",size:20})})]})]}),c.jsx("div",{className:"nc-termwrap",children:c.jsx(Ay,{session:e,node:t,token:o,readonly:a,takeSize:!0,sendRef:G,composerRef:U,actionRef:A,ctrlRef:ee,setCtrlArmed:Z,onFiles:y,fontSize:S,selectionMode:K,onSelectionModeChange:O,keyboardGesture:h.terminalKeyboardGesture})}),c.jsx(DC,{onKeyboard:()=>b(R=>!R),send:R=>G.current(R),action:R=>A.current(R),ctrlArmed:W,onCtrl:P,selectionMode:K,onSelectionMode:O,keepKeyboardClosed:h.keybarKeepsKeyboardClosed,showEnter:h.showKeybarEnter,keybarLayout:h.keybarLayout}),_&&c.jsx(Ky,{submitText:R=>U.current(R),token:o,session:e,node:t,ownerId:n,keepKeyboardClosedOnVoice:h.voiceKeepsKeyboardClosed}),p&&c.jsx(Iy,{session:e,node:t,token:o,filesEvent:w,onClose:()=>f(!1)})]})}function vN(){zn();const[e]=N.useState(pN),[t,n]=N.useState(e.token),[s,o]=N.useState(e.pair||""),a=N.useCallback(()=>{o("");try{sessionStorage.removeItem("nc_pair")}catch{}},[]),[u,h]=N.useState(!1),p=gN(),[f]=N.useState(()=>aE(typeof location<"u"?location.pathname:"/")),[_,b]=N.useState(f.id),w=_===ys(null,On),[y,S]=N.useState(null),x=de=>{const Ee=fr(de);S(Ee?{...Ee,...typeof(de==null?void 0:de.cellName)=="string"&&de.cellName?{cellName:de.cellName}:{}}:null)},[C,k]=N.useState([]),[j,I]=N.useState([]),[T,G]=N.useState([]),[U,A]=N.useState(()=>f.ownerId?_s():mN(f.name)),[ee,W]=N.useState(null),[Z,K]=N.useState(null),O=de=>K(fr(de)),P=Ch(t,p),R=N.useMemo(()=>(P||[]).filter(de=>de.instanceId).map(de=>({instanceId:de.instanceId,route:de.route,label:de.label,status:de.status})),[P]),M=iN(t,_,U,A,R),$=M.decks;N.useEffect(()=>{const de=new Map;for(const Ee of P||[]){const Ue=(Ee.route||[Ee.name]).join("/"),_t=new Set((Ee.sessions||[]).map(dt=>dt.name));for(const dt of Ee.cells||[])!dt.cell||!dt.tmuxSession||dt.cell===dt.tmuxSession||_t.has(dt.cell)||de.set(`${Ue}:${dt.cell}`,`${Ue}:${dt.tmuxSession}`)}A(Ee=>T0(Ee,de))},[P]),N.useEffect(()=>{M.localNodeId&&A(de=>{const Ee=ch(de,M.localNodeId,R);return JSON.stringify(Ee)===JSON.stringify(de)?de:Ee})},[R,M.localNodeId]);const[te,D]=N.useState(null),[ie,B]=N.useState(null),L=N.useRef(0),[F,X]=N.useState(!1),[ae,ue]=N.useState(hN),[ge,Q]=N.useState(!w),[q,fe]=N.useState(()=>w?localStorage.getItem(l_)==="1":!0),[Ie,et]=N.useState(!1),[ze,Fe]=N.useState("nodes"),[Ct,Ge]=N.useState(!1),[rt,ut]=N.useState(""),ct=(de="nodes",Ee=!1,Ue="")=>{Fe(de),Ge(Ee),ut(Ue),et(!0)},[vt,it]=N.useState(!1),[lt,mt]=N.useState({deviceDefault:"",localNodeId:"",localNameDefault:""}),[le,be]=N.useState(!1);N.useEffect(()=>{try{localStorage.setItem(jw,String(ae))}catch{}},[ae]),N.useEffect(()=>{try{localStorage.setItem(l_,q?"1":"")}catch{}},[q]),N.useEffect(()=>{if(!t)return;let de=!1;return Promise.all([w_(t),tn("/api/config",t).then(Ee=>Ee.json())]).then(([Ee,Ue])=>{de||(mt({deviceDefault:Ee.deviceName||"",localNodeId:Ee.nodeId||"",localNameDefault:Ee.localName||""}),be(!!Ue.readonlyDefault),(Ee.firstRun===!0&&!Ue.readonlyDefault||s)&&it(!0))}).catch(()=>{}),()=>{de=!0}},[t,s]);const Be=N.useCallback(async()=>{try{const Ee=await(await tn("/api/sessions",t)).json();Ee.error||k(Ee.sessions||[])}catch{}try{const de=await zr(t);I(de.available?de.cells||[]:[]),G(de.available?de.capabilities||[]:[])}catch{I([]),G([])}},[t]);N.useEffect(()=>{if(!p)return;Be();const de=setInterval(Be,4e3);return()=>clearInterval(de)},[p,Be]),N.useEffect(()=>{let de=!1;return tn("/api/config",t).then(Ee=>Ee.json()).then(Ee=>{de||oN(Ee.version,Ee.uiVersion,"0.8.34")}).catch(()=>{}),()=>{de=!0}},[t]);const $e=new Set([...C.map(de=>de.name),...P.flatMap(de=>de.sessions.map(Ee=>Ee.key))]),he=vi(U),V=de=>A(Ee=>{const Ue=Xy(de,M.localNodeId,R)||de,_t=z_(Ee,Ue);return _t===Ee&&vi(Ee).length>=9&&M.setError(g("grid-full")),_t}),ne=async(de,Ee=[])=>{try{await wh(t,de,Ee)}catch{return}const Ue=Ee.length?`${Ee.join("/")}:${de}`:de;A(_t=>yl(_t,Ue)),Be()},z=async(de,Ee,Ue=[])=>{try{await y_(t,de,Ee,Ue)}catch{return}Be()},se=async(de,Ee,Ue=[])=>{await g_(t,{cell:de,enabled:!!Ee},Ue),Be()},me=async de=>{if(!te)return;const{cell:Ee}=te,Ue=Array.isArray(te.route)?te.route:[];de.action==="up"?await _h(t,{cell:Ee,boot:!!de.boot,...de.engine?{engine:de.engine}:{},...de.model!==void 0?{model:de.model}:{},...de.permissionPolicy?{permissionPolicy:de.permissionPolicy}:{}},Ue):await fl(t,{cell:Ee,boot:!!de.boot},Ue);const _t=de.action==="up"?!!de.boot:de.boot?!1:!!te.boot;B({id:++L.current,cell:Ee,route:Ue,enabled:_t}),Be()},ke=N.useCallback(de=>{B(Ee=>(Ee==null?void 0:Ee.id)===de?null:Ee)},[]),Ne=async de=>{if(!(!(de!=null&&de.direct)||F)){X(!0);try{await kh(t,de.name,de.tunnelStatus==="up"?"down":"up")}finally{X(!1)}}},we=async(de,Ee)=>{const Ue=String(Ee||"").trim();return!(de!=null&&de.direct)||!bs(Ue)?!1:(await k_(t,de.name,Ue),!0)},Re=de=>{const Ee=$.find(Ue=>Ue.id===de);if(!Ee)return!1;try{const Ue=window.open($u(Ee,t),"_blank");return Ue&&(Ue.opener=null),!!Ue}catch{return!1}},Je=async de=>{if(!de||de===_)return;const Ee=await M.select(de),Ue=M.records.find(_t=>_t.id===de);b(de),A(Ee),W(null),K(null);try{history.replaceState(null,"",$u(Ue||de,null))}catch{}},Mt=async(de,Ee)=>{const Ue=await M.add(de,Ee);await Je(Ue.id)},Te=async(de,Ee)=>{const Ue=await M.rename(de,Ee);if(de===_){b(Ue.id),A(ch(Ue.layout,M.localNodeId,R)),W(null),K(null);try{history.replaceState(null,"",$u(Ue,null))}catch{}}},pn=async de=>{await M.remove(de),de===_&&await Je(M.localMainId)},bt=async(de,Ee)=>{!Ee||Ee===_||(await M.addTileTo(Ee,de),A(Ue=>yl(Ue,de)))};if(!t)return c.jsxs("div",{className:"nc-auth",children:[c.jsx("p",{children:g("auth-prompt")}),c.jsx("input",{onChange:de=>n(de.target.value.trim()),placeholder:"token"}),c.jsxs("label",{children:[c.jsx("input",{type:"checkbox",checked:u,onChange:de=>h(de.target.checked)})," ",g("remember-device")]}),c.jsx("button",{onClick:()=>{(u?localStorage:sessionStorage).setItem("nc_token",t)},children:"ok"})]});const Ke=c.jsxs(c.Fragment,{children:[Ie&&c.jsx(Y2,{token:t,initialTab:ze,initialLocation:rt,startNewCell:Ct,onClose:()=>{et(!1),Ge(!1),ut("")}}),vt&&c.jsx(G2,{token:t,initialPair:s,...lt,onPairDone:a,onDone:()=>it(!1)}),c.jsx(tN,{token:t})]});if(!p)return y?c.jsxs(c.Fragment,{children:[c.jsx(d_,{session:y.session,node:y.node,ownerId:y.ownerId,cellName:y.cellName,token:t,readonly:le,onBack:()=>S(null)}),Ke]}):c.jsxs(c.Fragment,{children:[c.jsx(G0,{onPick:x,token:t,onSettings:ct}),Ke]});const $t=w||!ge;return c.jsxs("div",{className:"nc-workspace",children:[$t&&c.jsx(GC,{sessions:C,cells:j,activeSessions:he,nodeGroups:P,fleetCapabilities:T,bootSettlement:ie,onBootSettlementApplied:ke,localNodeId:M.localNodeId,onPick:O,onAddTile:V,onPower:D,onBoot:se,onBootError:de=>M.setError(String((de==null?void 0:de.message)||de)),onNodePower:Ne,onNodeRename:we,onKill:ne,onVisibility:z,onNew:()=>ct("fleet",!0),onSettings:ct,width:ae,collapsed:q,onResize:ue,onToggleCollapse:()=>fe(de=>!de)}),c.jsxs("div",{className:"nc-workspace-main",children:[c.jsx(jE,{decks:$,currentDeck:_,onCreate:Mt,onRename:Te,onDelete:pn,onReorder:M.reorder,onOpenWindow:Re,onNavigate:Je,saveState:M.saveState,error:M.error,sidebarVisible:$t,onToggleSidebar:w?null:()=>Q(de=>!de)}),c.jsx(nE,{layout:U,onLayoutChange:A,token:t,readonly:le,sessionsAlive:$e,focusSession:ee,onFocus:W,onOpenSingle:O,decks:$,currentDeck:_,onSendToDeck:bt,cells:j,nodeGroups:P})]}),Z&&c.jsx("div",{className:"nc-single-overlay",children:c.jsx(d_,{session:Z.session,node:Z.node,ownerId:Z.ownerId,cellName:Wh({session:Z.session,node:Z.node,ownerId:Z.ownerId,cells:j,nodeGroups:P}),token:t,readonly:le,onBack:()=>K(null)})}),te&&c.jsx(pl,{cell:te,token:t,route:Array.isArray(te.route)?te.route:[],onConfirm:me,onClose:()=>D(null)}),Ke]})}function _N(){zn();const e=N.useSyncExternalStore(sN,a_,a_);if(!e.needed)return null;const t=e.kind==="install"?g("install-version-mismatch").replace("{v}",e.version):g("update-available").replace("{v}",e.version).replace(" "," ").trim();return c.jsxs("div",{className:"nc-update",role:"status","aria-live":"polite",children:[c.jsx("span",{className:"nc-update-msg",children:t}),e.kind==="reload"&&c.jsx("button",{className:"nc-update-btn",onClick:cN,children:g("reload")})]})}lN();Lb.createRoot(document.getElementById("root")).render(c.jsxs(xb.StrictMode,{children:[c.jsx(vN,{}),c.jsx(_N,{})]}));
|
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-cNTOIj7e.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-CrggFFKZ.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.8.
|
|
1
|
+
{"version":"0.8.34"}
|