@mmmbuto/nexuscrew 0.8.32 → 0.8.33
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 +24 -0
- package/MCP_COMPANIONS.md +67 -0
- package/README.md +23 -5
- package/frontend/dist/assets/{index-Dw90VJRY.js → index-CP5MeWCH.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/fleet/launch.js +16 -2
- package/lib/mcp/server.js +13 -0
- 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/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,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.8.33 — 2026-07-23 — "Cold Boot"
|
|
6
|
+
|
|
7
|
+
- Restores Fleet on a true cold boot where no tmux server or default socket
|
|
8
|
+
exists yet. The expected `error connecting to … (No such file or directory)`
|
|
9
|
+
response is now treated as an empty tmux inventory, allowing the boot
|
|
10
|
+
companion to create the shared server and start every `boot:true` cell. The
|
|
11
|
+
migration inventory command uses the POSIX `C` message locale while
|
|
12
|
+
preserving UTF-8 character handling, so this classification remains
|
|
13
|
+
deterministic on localized Linux, macOS and Termux hosts.
|
|
14
|
+
- Keeps migration fail-closed for unexpected tmux failures such as permission
|
|
15
|
+
errors, malformed output, ambiguous legacy sessions and rename collisions.
|
|
16
|
+
The regression reproduces the exact stderr observed after reboot and verifies
|
|
17
|
+
both provider availability and safe persistence of dotted cell identities.
|
|
18
|
+
- Adds an optional MCP companion guide and machine-readable catalog for
|
|
19
|
+
structured Memory, searchable MSA knowledge, bounded Crew delegation and
|
|
20
|
+
Mail. Agents discover existing tools first, recommend a companion only for a
|
|
21
|
+
requested missing capability and never install or configure it without
|
|
22
|
+
consent.
|
|
23
|
+
- Ships generic public `memory`, `vl-msa`, `crew` and `mail-assistant` skills.
|
|
24
|
+
Their canonical instructions are in English and user-facing output follows
|
|
25
|
+
the user's explicit preference or request language; Mail reply drafts follow
|
|
26
|
+
the thread language unless overridden. No account, identity, folder,
|
|
27
|
+
credential or machine-specific path is bundled.
|
|
28
|
+
|
|
5
29
|
## 0.8.32 — 2026-07-22 — "Touch History"
|
|
6
30
|
|
|
7
31
|
- 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
|
@@ -454,6 +454,24 @@ query remote nodes or add commands to the federated directory. The returned comm
|
|
|
454
454
|
and credential-redacted; the failure is a closed `{status, code, phase}` cause rather than raw
|
|
455
455
|
stderr, paths, environment values, prompts or tokens.
|
|
456
456
|
|
|
457
|
+
## Optional MCP companions
|
|
458
|
+
|
|
459
|
+
NexusCrew can work alongside separate local-first MCP servers for durable
|
|
460
|
+
structured memory, searchable document memory, bounded worker delegation and
|
|
461
|
+
mail access. They are optional projects rather than hidden NexusCrew
|
|
462
|
+
dependencies: the AI should discover its existing tools first, recommend a
|
|
463
|
+
companion only for a capability the user actually requested, and ask before
|
|
464
|
+
installing or changing client configuration.
|
|
465
|
+
|
|
466
|
+
See [MCP_COMPANIONS.md](MCP_COMPANIONS.md) for the capability guide and
|
|
467
|
+
[`mcp-companions.json`](mcp-companions.json) for the machine-readable catalog.
|
|
468
|
+
The npm package also includes generic [`memory`](skills/memory/SKILL.md),
|
|
469
|
+
[`vl-msa`](skills/vl-msa/SKILL.md), [`crew`](skills/crew/SKILL.md) and
|
|
470
|
+
[`mail-assistant`](skills/mail-assistant/SKILL.md) skills. Their instructions
|
|
471
|
+
are written in English, while user-facing output follows the user's explicit
|
|
472
|
+
preference or request language; Mail reply drafts follow the email thread by
|
|
473
|
+
default.
|
|
474
|
+
|
|
457
475
|
## Configuration
|
|
458
476
|
|
|
459
477
|
Runtime state is local to the current user:
|
|
@@ -499,16 +517,16 @@ operator's tmux server.
|
|
|
499
517
|
|
|
500
518
|
## Roadmap
|
|
501
519
|
|
|
502
|
-
The
|
|
503
|
-
|
|
504
|
-
Credentials and execution would remain on the owner node, with owner-qualified tool
|
|
505
|
-
and read/mutate ACLs.
|
|
520
|
+
The current companion catalog is documentation and agent guidance only. A future optional MCP
|
|
521
|
+
gateway could expose one NexusCrew endpoint with explicit, per-tool federation through shared
|
|
522
|
+
nodes. Credentials and execution would remain on the owner node, with owner-qualified tool
|
|
523
|
+
identities and read/mutate ACLs. The gateway is planned work and is **not part of this release**.
|
|
506
524
|
|
|
507
525
|
See [CHANGELOG.md](CHANGELOG.md) for released changes.
|
|
508
526
|
|
|
509
527
|
## Status
|
|
510
528
|
|
|
511
|
-
The current stable release is **v0.8.
|
|
529
|
+
The current stable release is **v0.8.33** on npm and GitHub.
|
|
512
530
|
|
|
513
531
|
## License
|
|
514
532
|
|
|
@@ -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.33")}).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-CP5MeWCH.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.33"}
|
package/lib/fleet/launch.js
CHANGED
|
@@ -36,6 +36,20 @@ function minimalEnv() {
|
|
|
36
36
|
return minimalRuntimeEnv(process.env, { home: os.homedir() });
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// tmux reports client-side connection failures through localized stderr. The
|
|
40
|
+
// migration classifier must see the stable POSIX wording on every platform,
|
|
41
|
+
// otherwise a missing first-boot socket could disable Fleet under a non-English
|
|
42
|
+
// locale. Restrict only the message locale: LC_ALL=C would also force ASCII
|
|
43
|
+
// character handling and tmux would sanitize the tab in our format string.
|
|
44
|
+
// Panes, the shared server and the inventory protocol keep a UTF-8 LC_CTYPE.
|
|
45
|
+
function tmuxInventoryEnv() {
|
|
46
|
+
const env = minimalEnv();
|
|
47
|
+
delete env.LC_ALL; // LC_ALL would override LC_MESSAGES and LC_CTYPE.
|
|
48
|
+
env.LANGUAGE = 'C';
|
|
49
|
+
env.LC_MESSAGES = 'C';
|
|
50
|
+
return env;
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
// httpError(status, msg, data?, cause?) — structured HTTP error. `data` carries
|
|
40
54
|
// arbitrary API detail for the response body; `cause` (T4) is the OPTIONAL
|
|
41
55
|
// bounded failure triple {phase, code} of the up() boundary that failed. The
|
|
@@ -170,10 +184,10 @@ async function migrateLegacyTmuxSessions(tmuxBin, defs, { readonly = false } = {
|
|
|
170
184
|
};
|
|
171
185
|
}
|
|
172
186
|
const listing = await tmuxExec(tmuxBin,
|
|
173
|
-
['list-sessions', '-F', '#{session_id}\t#{session_name}'], { env:
|
|
187
|
+
['list-sessions', '-F', '#{session_id}\t#{session_name}'], { env: tmuxInventoryEnv() });
|
|
174
188
|
if (listing.err) {
|
|
175
189
|
const detail = boundedTmuxFailure(listing);
|
|
176
|
-
if (/no server running|failed to connect|connection refused|no such file.*tmux/i.test(detail)) {
|
|
190
|
+
if (/no server running|failed to connect|connection refused|no such file.*tmux|error connecting to .*\(no such file or directory\)/i.test(detail)) {
|
|
177
191
|
return { migrated: [], reason: 'no-tmux-server', needsPersistence: legacyMap.size > 0 };
|
|
178
192
|
}
|
|
179
193
|
throw tmuxMigrationError('TMUX_MIGRATION_LIST_FAILED',
|
package/lib/mcp/server.js
CHANGED
|
@@ -25,6 +25,7 @@ const { loadConfig } = require('../config.js');
|
|
|
25
25
|
const { readTokenSafe } = require('../auth/token.js');
|
|
26
26
|
const { isValidSession } = require('../files/store.js');
|
|
27
27
|
const VERSION = require('../../package.json').version;
|
|
28
|
+
const MCP_COMPANIONS = require('../../mcp-companions.json');
|
|
28
29
|
const { TOOLS, IDENTITY_CODE, IDENTITY_REMEDIATION } = require('./tools.js');
|
|
29
30
|
const cells = require('./cells.js');
|
|
30
31
|
|
|
@@ -38,6 +39,17 @@ const INVALID_REQUEST = -32600;
|
|
|
38
39
|
const METHOD_NOT_FOUND = -32601;
|
|
39
40
|
const INVALID_PARAMS = -32602;
|
|
40
41
|
|
|
42
|
+
function companionInstructions() {
|
|
43
|
+
const catalog = MCP_COMPANIONS.companions
|
|
44
|
+
.map((item) => `${item.id}: ${item.name} (${item.repository})`)
|
|
45
|
+
.join('; ');
|
|
46
|
+
return 'Discover the current client tools before recommending another MCP server. '
|
|
47
|
+
+ 'If a requested capability is missing, these optional NexusCrew companions may cover it: '
|
|
48
|
+
+ `${catalog}. Recommend only the capability actually needed and ask before installing `
|
|
49
|
+
+ 'software, changing MCP configuration, starting services or requesting credentials. '
|
|
50
|
+
+ 'NexusCrew does not install or configure companions automatically.';
|
|
51
|
+
}
|
|
52
|
+
|
|
41
53
|
// --- identita' cella mittente ------------------------------------------------
|
|
42
54
|
// Ordine (design §1, INVARIATO): $TMUX presente -> `tmux display-message -p '#S'`
|
|
43
55
|
// (nome sessione reale); se fallisce/invalida -> fallback env NEXUSCREW_MCP_SESSION;
|
|
@@ -246,6 +258,7 @@ function createMcpServer(opts = {}) {
|
|
|
246
258
|
protocolVersion: pv,
|
|
247
259
|
capabilities: { tools: {} },
|
|
248
260
|
serverInfo: { name: 'nexuscrew', version: VERSION },
|
|
261
|
+
instructions: companionInstructions(),
|
|
249
262
|
});
|
|
250
263
|
}
|
|
251
264
|
if (method === 'ping') return reply(id, {});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"kind": "nexuscrew-mcp-companions",
|
|
4
|
+
"policy": {
|
|
5
|
+
"optional": true,
|
|
6
|
+
"discoverToolsFirst": true,
|
|
7
|
+
"recommendOnlyForRequestedCapability": true,
|
|
8
|
+
"automaticInstall": false,
|
|
9
|
+
"automaticConfiguration": false
|
|
10
|
+
},
|
|
11
|
+
"companions": [
|
|
12
|
+
{
|
|
13
|
+
"id": "memory",
|
|
14
|
+
"name": "mcp-memory-rs",
|
|
15
|
+
"repository": "https://github.com/DioNanos/mcp-memory-rs",
|
|
16
|
+
"installation": "https://github.com/DioNanos/mcp-memory-rs#install",
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"bundledSkill": "skills/memory/SKILL.md",
|
|
19
|
+
"capabilities": [
|
|
20
|
+
"durable structured agent state",
|
|
21
|
+
"versioned categories",
|
|
22
|
+
"bounded append-only journals",
|
|
23
|
+
"full-text memory search"
|
|
24
|
+
],
|
|
25
|
+
"primaryTools": [
|
|
26
|
+
"memory_read",
|
|
27
|
+
"memory_write",
|
|
28
|
+
"memory_append",
|
|
29
|
+
"memory_search",
|
|
30
|
+
"memory_status"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "msa",
|
|
35
|
+
"name": "mcp-vl-msa-rs",
|
|
36
|
+
"repository": "https://github.com/DioNanos/mcp-vl-msa-rs",
|
|
37
|
+
"installation": "https://github.com/DioNanos/mcp-vl-msa-rs#install",
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
|
+
"bundledSkill": "skills/vl-msa/SKILL.md",
|
|
40
|
+
"capabilities": [
|
|
41
|
+
"persistent document collections",
|
|
42
|
+
"BM25 and optional hybrid retrieval",
|
|
43
|
+
"full-source grounding",
|
|
44
|
+
"bounded multi-hop retrieval"
|
|
45
|
+
],
|
|
46
|
+
"primaryTools": [
|
|
47
|
+
"msa_index",
|
|
48
|
+
"msa_index_batch",
|
|
49
|
+
"msa_search",
|
|
50
|
+
"msa_fetch_doc",
|
|
51
|
+
"msa_remember",
|
|
52
|
+
"msa_forget",
|
|
53
|
+
"msa_interleave_round"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "crew",
|
|
58
|
+
"name": "mcp-crewd-rs",
|
|
59
|
+
"repository": "https://github.com/DioNanos/mcp-crewd-rs",
|
|
60
|
+
"installation": "https://github.com/DioNanos/mcp-crewd-rs#install",
|
|
61
|
+
"license": "Apache-2.0",
|
|
62
|
+
"bundledSkill": "skills/crew/SKILL.md",
|
|
63
|
+
"capabilities": [
|
|
64
|
+
"bounded AI worker spawning",
|
|
65
|
+
"task status and structured results",
|
|
66
|
+
"controlled inter-cell messaging"
|
|
67
|
+
],
|
|
68
|
+
"primaryTools": [
|
|
69
|
+
"cell_spawn",
|
|
70
|
+
"cell_send_task",
|
|
71
|
+
"cell_status",
|
|
72
|
+
"cell_result",
|
|
73
|
+
"cell_list",
|
|
74
|
+
"cell_send",
|
|
75
|
+
"cell_ask"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "mail",
|
|
80
|
+
"name": "mcp-email-rs",
|
|
81
|
+
"repository": "https://github.com/DioNanos/mcp-email-rs",
|
|
82
|
+
"installation": "https://github.com/DioNanos/mcp-email-rs#install",
|
|
83
|
+
"license": "Apache-2.0",
|
|
84
|
+
"bundledSkill": "skills/mail-assistant/SKILL.md",
|
|
85
|
+
"capabilities": [
|
|
86
|
+
"IMAP mailbox discovery",
|
|
87
|
+
"mail search and reading",
|
|
88
|
+
"attachments and folders",
|
|
89
|
+
"drafts and optional SMTP sending"
|
|
90
|
+
],
|
|
91
|
+
"primaryTools": [
|
|
92
|
+
"list_folders",
|
|
93
|
+
"list_emails",
|
|
94
|
+
"search_emails",
|
|
95
|
+
"get_email",
|
|
96
|
+
"download_attachment",
|
|
97
|
+
"create_draft",
|
|
98
|
+
"send_email",
|
|
99
|
+
"email_doctor"
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmmbuto/nexuscrew",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.33",
|
|
4
4
|
"description": "Faithful browser tmux client — attach to live sessions over a real PTY, localhost-only, mobile-easy",
|
|
5
5
|
"main": "lib/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"frontend/dist/",
|
|
14
14
|
"frontend/index.html",
|
|
15
15
|
"CHANGELOG.md",
|
|
16
|
+
"MCP_COMPANIONS.md",
|
|
17
|
+
"mcp-companions.json",
|
|
16
18
|
"LICENSE",
|
|
17
19
|
"README.md",
|
|
18
20
|
"NOTICE"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: crew
|
|
3
|
+
description: Use when spawning or coordinating bounded AI worker cells through a Crew MCP fabric, including cell discovery, idempotent spawn, status monitoring, structured results, follow-up tasks, cancellation and the inter-cell message bus. Covers safe worktree isolation, durable audit reports and user-language selection.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Crew
|
|
7
|
+
|
|
8
|
+
Use Crew for bounded delegated work. A coordinator submits tasks to worker
|
|
9
|
+
cells through an MCP server; spawning is asynchronous and returns a thread ID.
|
|
10
|
+
|
|
11
|
+
## Select response language
|
|
12
|
+
|
|
13
|
+
Choose the language for user-facing explanations and summaries in this order:
|
|
14
|
+
|
|
15
|
+
1. the user's explicit language preference;
|
|
16
|
+
2. the language of the current request;
|
|
17
|
+
3. a reliable client or system locale;
|
|
18
|
+
4. English.
|
|
19
|
+
|
|
20
|
+
Keep cell names, engine/profile IDs, thread IDs, tool names, paths and quoted
|
|
21
|
+
worker output unchanged unless the user explicitly asks to translate them.
|
|
22
|
+
Tell a worker which output language is required when its deliverable is
|
|
23
|
+
user-facing.
|
|
24
|
+
|
|
25
|
+
## Discover before spawning
|
|
26
|
+
|
|
27
|
+
1. Use `cell_list` to inspect the cells and engines visible to the caller.
|
|
28
|
+
2. Select an engine/profile appropriate to the bounded task.
|
|
29
|
+
3. Verify that the worker's `cwd` contains the instructions and project context
|
|
30
|
+
it needs.
|
|
31
|
+
4. Do not assume an embedded worker inherits the coordinator's MCP servers.
|
|
32
|
+
Required servers must be configured explicitly in the Crew daemon and
|
|
33
|
+
verified inside a fresh worker.
|
|
34
|
+
|
|
35
|
+
If no Crew tools are exposed and this skill is packaged with NexusCrew, the
|
|
36
|
+
optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
|
|
37
|
+
missing capability and ask before installing, configuring or starting it.
|
|
38
|
+
|
|
39
|
+
## Spawn, monitor and collect
|
|
40
|
+
|
|
41
|
+
1. Call `cell_spawn` with a bounded task, explicit `cwd`, background mode and a
|
|
42
|
+
caller-stable `idempotency_key`.
|
|
43
|
+
2. Keep the returned `crewd_thread_id`; a replayed idempotency key must resolve
|
|
44
|
+
to the existing thread rather than duplicate work.
|
|
45
|
+
3. Monitor with `cell_status`. Use the host's loop or scheduler for waits rather
|
|
46
|
+
than a tight manual poll.
|
|
47
|
+
4. Treat `idle` as normal turn completion. Also handle `timeout`, `failed`,
|
|
48
|
+
`interrupted` and `failed_unknown`; do not wait for a fictional `finished`
|
|
49
|
+
state.
|
|
50
|
+
5. Read `cell_result` and inspect `exit_status`, `final_answer` and the bounded
|
|
51
|
+
event tail.
|
|
52
|
+
6. Verify the worker's files and tests yourself before accepting code changes.
|
|
53
|
+
|
|
54
|
+
A timeout may leave partial edits. Never report success from a thread state
|
|
55
|
+
alone.
|
|
56
|
+
|
|
57
|
+
## Isolate code work
|
|
58
|
+
|
|
59
|
+
- Do not let two mutating workers edit the same worktree concurrently.
|
|
60
|
+
- Give parallel code tasks separate git worktrees.
|
|
61
|
+
- Preserve unrelated user changes and review every worker diff.
|
|
62
|
+
- Use follow-up tasks only when the original thread and scope remain valid.
|
|
63
|
+
- Cancel only the exact thread the user authorized.
|
|
64
|
+
|
|
65
|
+
## Require durable audit evidence
|
|
66
|
+
|
|
67
|
+
For audits, reviews and release gates:
|
|
68
|
+
|
|
69
|
+
1. Choose an authorized absolute report path before spawning, preferably
|
|
70
|
+
outside the repository being audited.
|
|
71
|
+
2. Require scope, evidence, findings by severity, residual risks and a terminal
|
|
72
|
+
verdict in the report.
|
|
73
|
+
3. Require the worker callback to identify the report path and verdict.
|
|
74
|
+
4. Verify that the report is a non-symlink regular file, read it completely and
|
|
75
|
+
confirm its verdict matches the callback.
|
|
76
|
+
5. Recalculate important hashes and gates independently.
|
|
77
|
+
|
|
78
|
+
Chat output or `cell_result.final_answer` is not a substitute for the verified
|
|
79
|
+
report.
|
|
80
|
+
|
|
81
|
+
## Use the message bus deliberately
|
|
82
|
+
|
|
83
|
+
- `cell_send` is fire-and-forget.
|
|
84
|
+
- `cell_ask` plus `cell_await` is appropriate when a reply is required.
|
|
85
|
+
- `cell_send_task` starts a follow-up turn on an existing thread.
|
|
86
|
+
- `cell_inbox` reads queued bus messages.
|
|
87
|
+
|
|
88
|
+
Transport receipts do not prove task acceptance or completion. Report only
|
|
89
|
+
results that have been verified.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mail-assistant
|
|
3
|
+
description: Use for mailbox discovery, email search and reading, priority triage, attachment review, folder organisation, reply drafting, explicit sending, or recurring mail checks through an available Gmail or IMAP/SMTP MCP connector. Select the user-facing language from the request and the draft language from the thread, while preserving read-before-write discipline and explicit confirmation for consequential mail actions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Mail Assistant
|
|
7
|
+
|
|
8
|
+
Use the mail tools already exposed by the current client. Discover folders,
|
|
9
|
+
sender identities and supported operations live; never assume provider-specific
|
|
10
|
+
folder names or account configuration.
|
|
11
|
+
|
|
12
|
+
## Select languages
|
|
13
|
+
|
|
14
|
+
Choose the language for digests, questions and explanations in this order:
|
|
15
|
+
|
|
16
|
+
1. the user's explicit language preference;
|
|
17
|
+
2. the language of the current request;
|
|
18
|
+
3. a reliable client or system locale;
|
|
19
|
+
4. English.
|
|
20
|
+
|
|
21
|
+
Write a reply draft in the language of the email thread unless the user asks
|
|
22
|
+
for another language. Preserve names, quoted text and required legal or
|
|
23
|
+
technical terms; do not translate them merely to match the interface language.
|
|
24
|
+
|
|
25
|
+
## Choose tools
|
|
26
|
+
|
|
27
|
+
1. Prefer an exposed Gmail connector for Gmail-hosted workflows.
|
|
28
|
+
2. Otherwise use an exposed IMAP/SMTP mail MCP such as `mcp-email-rs`.
|
|
29
|
+
3. Search narrowly before listing large folders, and fetch complete message
|
|
30
|
+
bodies only when needed for classification, attachments or drafting.
|
|
31
|
+
4. If no mail tool is available, explain the missing capability. When this
|
|
32
|
+
skill is packaged with NexusCrew, the optional companion is documented in
|
|
33
|
+
`../../MCP_COMPANIONS.md`. Do not install or configure it without consent.
|
|
34
|
+
|
|
35
|
+
## Apply the safety boundary
|
|
36
|
+
|
|
37
|
+
- Reading and searching are allowed when the user asks to inspect mail.
|
|
38
|
+
- Treat moves, flags, drafts, folder changes and calendar/reminder creation as
|
|
39
|
+
explicit mutations; perform only the mutation the user requested.
|
|
40
|
+
- Treat deletion and sending as consequential external actions. Confirm the
|
|
41
|
+
exact targets and intended content immediately before acting unless the
|
|
42
|
+
user's current request already gives unambiguous final authorization.
|
|
43
|
+
- Prefer recoverable archive or Trash moves over permanent deletion.
|
|
44
|
+
- Re-fetch message identifiers after moves; IMAP UIDs are folder-scoped and
|
|
45
|
+
can change.
|
|
46
|
+
- Never expose credentials, authentication links or sensitive message content
|
|
47
|
+
in logs, push notifications, TTS or unrelated summaries.
|
|
48
|
+
- Never infer an authorised sender identity. Use identities returned by the
|
|
49
|
+
connector or explicitly supplied by the user.
|
|
50
|
+
|
|
51
|
+
## Triage and draft
|
|
52
|
+
|
|
53
|
+
1. Fetch the smallest useful set of messages.
|
|
54
|
+
2. Classify each item as urgent/action required, reply soon, waiting, or
|
|
55
|
+
informational, using the user's own priorities when available.
|
|
56
|
+
3. Read attachments only when necessary and identify their type before
|
|
57
|
+
downloading.
|
|
58
|
+
4. Present a compact digest with evidence from the messages and a proposed
|
|
59
|
+
next action.
|
|
60
|
+
5. Draft only when requested. Preserve the user's tone, distinguish facts from
|
|
61
|
+
assumptions and leave unresolved details visible.
|
|
62
|
+
6. Before sending, show or re-confirm recipients, subject, body and attachments.
|
|
63
|
+
7. Report only actions that the tool actually completed.
|
|
64
|
+
|
|
65
|
+
## Recurring checks
|
|
66
|
+
|
|
67
|
+
Create recurring monitoring only when explicitly requested. Use the
|
|
68
|
+
client-native scheduler or loop mechanism instead of manual polling, avoid
|
|
69
|
+
duplicate jobs, respect quiet hours, and remain silent on unchanged ticks when
|
|
70
|
+
the host workflow supports silent monitoring. A recurring authorization does
|
|
71
|
+
not automatically authorize sending or permanent deletion.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory
|
|
3
|
+
description: Use when reading or writing persistent AI-agent state through a Memory MCP server, including category discovery, versioned reads and writes, merge patches, optimistic concurrency, bounded append-only journals, search, context loading and history. Keep durable state separate from bounded logs and choose the user-facing language from the request.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory
|
|
7
|
+
|
|
8
|
+
Use the exposed Memory MCP tools rather than reading or editing the server's
|
|
9
|
+
database, cache or state files directly.
|
|
10
|
+
|
|
11
|
+
## Select response language
|
|
12
|
+
|
|
13
|
+
Choose the language for explanations and summaries in this order:
|
|
14
|
+
|
|
15
|
+
1. the user's explicit language preference;
|
|
16
|
+
2. the language of the current request;
|
|
17
|
+
3. a reliable client or system locale;
|
|
18
|
+
4. English.
|
|
19
|
+
|
|
20
|
+
Keep category names, tool names, JSON fields, paths and quoted source text
|
|
21
|
+
unchanged unless the user explicitly asks to translate them.
|
|
22
|
+
|
|
23
|
+
## Understand the model
|
|
24
|
+
|
|
25
|
+
- A category is a named JSON document. `memory_read` takes a `category`, not a
|
|
26
|
+
nested key.
|
|
27
|
+
- Reads may be broadly available while writes are scoped by the server's
|
|
28
|
+
device or actor ACL.
|
|
29
|
+
- Every write is versioned; inspect `memory_history` when provenance matters.
|
|
30
|
+
- Treat recalled state as a snapshot. Verify live files, flags and services
|
|
31
|
+
before acting on drift-prone facts.
|
|
32
|
+
|
|
33
|
+
## Warm up narrowly
|
|
34
|
+
|
|
35
|
+
1. Call `memory_list` to discover available categories.
|
|
36
|
+
2. Read only relevant categories with `memory_read`, or load a bounded set with
|
|
37
|
+
`memory_context`.
|
|
38
|
+
3. Use `memory_search` when the category is unknown.
|
|
39
|
+
|
|
40
|
+
Do not load every category by default.
|
|
41
|
+
|
|
42
|
+
## Write safely
|
|
43
|
+
|
|
44
|
+
`memory_write {category, content}` replaces the category by default.
|
|
45
|
+
|
|
46
|
+
- Set `merge:true` for a top-level patch: supplied keys are inserted or
|
|
47
|
+
overwritten, JSON `null` deletes a key, and untouched keys remain.
|
|
48
|
+
- Pass `expected_hash` from the prior read/list result for read-modify-write
|
|
49
|
+
workflows so concurrent updates fail instead of being overwritten.
|
|
50
|
+
- Never write another device's namespace unless the server ACL and the user's
|
|
51
|
+
request explicitly authorize it.
|
|
52
|
+
|
|
53
|
+
## Separate state from journals
|
|
54
|
+
|
|
55
|
+
Use `memory_append` for a bounded append-only log category. The server stamps
|
|
56
|
+
entries and prunes them according to retention.
|
|
57
|
+
|
|
58
|
+
- Do not simulate a journal by adding ever-growing dated keys to a normal
|
|
59
|
+
memory category.
|
|
60
|
+
- Do not use `memory_write` on a log category.
|
|
61
|
+
- Do not use `memory_append` on a normal memory category.
|
|
62
|
+
|
|
63
|
+
A useful convention is:
|
|
64
|
+
|
|
65
|
+
- `<name>_state`: lean declarative state, updated with `memory_write`;
|
|
66
|
+
- `<name>_log`: bounded event journal, updated with `memory_append`.
|
|
67
|
+
|
|
68
|
+
Facts that must never be pruned belong in normal durable state or an external
|
|
69
|
+
document store, not in a bounded log.
|
|
70
|
+
|
|
71
|
+
## Search and inspect
|
|
72
|
+
|
|
73
|
+
- Use `memory_search` for BM25 full-text retrieval across categories.
|
|
74
|
+
- Use `memory_search_semantic` only when the server exposes and supports it.
|
|
75
|
+
- Use `memory_history` to inspect versions before restoring or explaining a
|
|
76
|
+
change.
|
|
77
|
+
- Report writes only after the tool confirms them.
|
|
78
|
+
|
|
79
|
+
If no Memory MCP tool is available and this skill is packaged with NexusCrew,
|
|
80
|
+
the optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
|
|
81
|
+
missing capability and ask before installing or configuring anything.
|
|
@@ -36,6 +36,26 @@ Apply these rules:
|
|
|
36
36
|
|
|
37
37
|
The MCP server is the stdio command `nexuscrew mcp` and must be registered in the host AI client. If the `nc_*` tools are not exposed, report that the bridge is not configured in that session and use the fallback flows below where applicable.
|
|
38
38
|
|
|
39
|
+
## Optional capability companions
|
|
40
|
+
|
|
41
|
+
NexusCrew does not make unrelated MCP servers hidden dependencies. When the
|
|
42
|
+
user requests durable structured memory, document retrieval, bounded worker
|
|
43
|
+
delegation or mailbox access:
|
|
44
|
+
|
|
45
|
+
1. Discover the tools already exposed by the current AI client.
|
|
46
|
+
2. Use an available tool that covers the request.
|
|
47
|
+
3. If the capability is absent, consult the packaged
|
|
48
|
+
`../../mcp-companions.json` catalog and mention the matching optional
|
|
49
|
+
companion once.
|
|
50
|
+
4. Explain the capability it adds and link its public repository.
|
|
51
|
+
5. Ask before installing software, changing MCP configuration, starting a
|
|
52
|
+
service or requesting credentials.
|
|
53
|
+
|
|
54
|
+
Never recommend a companion during unrelated work, repeatedly advertise it, or
|
|
55
|
+
claim it is required by NexusCrew. The catalog is discovery metadata only; it
|
|
56
|
+
does not authorize installation or external actions. See
|
|
57
|
+
`../../MCP_COMPANIONS.md` for the human-readable guide.
|
|
58
|
+
|
|
39
59
|
## File exchange (inbox / outbox)
|
|
40
60
|
|
|
41
61
|
Per session, NexusCrew watches `<root>/<session>/{inbox,outbox}` (root = `$NEXUSCREW_FILES_ROOT`, default `~/NexusFiles`):
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vl-msa
|
|
3
|
+
description: Use when indexing or retrieving documents, notes, research or past conversations through a VL-MSA long-term-memory MCP server, including collections, batch indexing, BM25 or hybrid search, full-document grounding, remember/forget and bounded multi-hop interleaving. Enforce locate-then-ground retrieval and choose the user-facing language from the request.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# VL MSA
|
|
7
|
+
|
|
8
|
+
Use VL-MSA for durable searchable source material. Collections persist across
|
|
9
|
+
sessions; BM25 works without embeddings, while hybrid reranking is optional.
|
|
10
|
+
|
|
11
|
+
## Select response language
|
|
12
|
+
|
|
13
|
+
Choose the language for explanations and answers in this order:
|
|
14
|
+
|
|
15
|
+
1. the user's explicit language preference;
|
|
16
|
+
2. the language of the current request;
|
|
17
|
+
3. a reliable client or system locale;
|
|
18
|
+
4. English.
|
|
19
|
+
|
|
20
|
+
Keep collection IDs, document IDs, tool names, metadata fields and quoted
|
|
21
|
+
source text unchanged unless the user explicitly asks to translate them.
|
|
22
|
+
|
|
23
|
+
## Locate, then ground
|
|
24
|
+
|
|
25
|
+
Retrieval has two required steps:
|
|
26
|
+
|
|
27
|
+
1. Call `msa_search {collection, query, k}` to locate relevant chunks.
|
|
28
|
+
2. Call `msa_fetch_doc` for the selected hit before using it as evidence.
|
|
29
|
+
|
|
30
|
+
Search chunks are locators, not complete sources. Do not answer a
|
|
31
|
+
context-dependent question from truncated snippets alone.
|
|
32
|
+
|
|
33
|
+
## Index material
|
|
34
|
+
|
|
35
|
+
- Use `msa_index` for one document or note.
|
|
36
|
+
- Use `msa_index_batch` for bulk ingest.
|
|
37
|
+
- Group related material into a stable named collection.
|
|
38
|
+
- Inspect `msa_list_collections`, `msa_stats` or `msa_manifest` before assuming
|
|
39
|
+
a collection exists or is populated.
|
|
40
|
+
|
|
41
|
+
Index only material the user is authorized to store. Preserve provenance and
|
|
42
|
+
do not place credentials or unrelated personal data in collection metadata.
|
|
43
|
+
|
|
44
|
+
## Manage standalone memories
|
|
45
|
+
|
|
46
|
+
- Use `msa_remember` for a standalone agent memory. Its deduplication and
|
|
47
|
+
low-signal gate may decline a write.
|
|
48
|
+
- Use `msa_forget` for an explicitly requested removal.
|
|
49
|
+
|
|
50
|
+
Do not claim a memory was stored or removed until the tool confirms the
|
|
51
|
+
outcome.
|
|
52
|
+
|
|
53
|
+
## Handle multi-hop questions
|
|
54
|
+
|
|
55
|
+
Use `msa_interleave_round` for one bounded route, deduplicate and read step.
|
|
56
|
+
Repeat only as needed, carrying forward the grounded facts from the prior
|
|
57
|
+
round. Avoid a single unbounded search.
|
|
58
|
+
|
|
59
|
+
## Choose ranking
|
|
60
|
+
|
|
61
|
+
- Use BM25 by default.
|
|
62
|
+
- Pass `dense_alpha` only when the server is built and configured for hybrid
|
|
63
|
+
retrieval.
|
|
64
|
+
- Do not describe lexical-only results as semantic retrieval.
|
|
65
|
+
|
|
66
|
+
If no VL-MSA tool is available and this skill is packaged with NexusCrew, the
|
|
67
|
+
optional companion is documented in `../../MCP_COMPANIONS.md`. Explain the
|
|
68
|
+
missing capability and ask before installing or configuring anything.
|