@mmmbuto/nexuscrew 0.9.16 → 0.9.18
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 +157 -3
- package/docs/ALIBABA_TOKEN_PLAN.md +70 -0
- package/docs/AUDIO_SHARE.md +119 -0
- package/docs/CELL_PANEL.md +156 -0
- package/docs/CONFIGURATION.md +122 -0
- package/docs/FLEET.md +222 -0
- package/docs/INSTALLATION.md +128 -0
- package/docs/LIVE_PROMPT.md +110 -0
- package/docs/MCP.md +151 -0
- package/docs/NODES.md +214 -0
- package/docs/NOTIFICATIONS.md +101 -0
- package/docs/OPERATIONS.md +114 -0
- package/docs/README.md +55 -0
- package/docs/SECURITY.md +185 -0
- package/docs/VL_MICRO_NODES.md +107 -0
- package/docs/img/fleet-deck-desktop.png +0 -0
- package/docs/img/session-mobile.png +0 -0
- package/docs/live-prompt-templates/LIVE_PROMPT.en.md +67 -0
- package/docs/live-prompt-templates/LIVE_PROMPT.es.md +70 -0
- package/docs/live-prompt-templates/LIVE_PROMPT.it.md +67 -0
- package/frontend/dist/assets/{index-CwsGpQwK.js → index-DmpQoCsc.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/auth/middleware.js +1 -1
- package/lib/cells/scope-guard.js +2 -2
- package/lib/cli/commands.js +7 -7
- package/lib/cli/fleet-service.js +2 -2
- package/lib/cli/pidfile.js +15 -15
- package/lib/cli/service.js +2 -2
- package/lib/cli/stable-alias.js +1 -1
- package/lib/files/routes.js +2 -2
- package/lib/fleet/builtin.js +6 -6
- package/lib/fleet/catalogs/zai-responses.json +82 -0
- package/lib/fleet/cell-exec.js +4 -4
- package/lib/fleet/cell-lease-server.js +17 -17
- package/lib/fleet/definitions.js +28 -17
- package/lib/fleet/launch-broker.js +3 -3
- package/lib/fleet/lease-client.js +1 -1
- package/lib/fleet/managed.js +86 -26
- package/lib/fleet/prompt-delivery.js +1 -1
- package/lib/fleet/runtime.js +3 -3
- package/lib/live-host/bridge.js +3 -3
- package/lib/live-host/routes.js +1 -1
- package/lib/mcp/server.js +7 -8
- package/lib/mcp/tools.js +1 -1
- package/lib/nodes/commands.js +2 -2
- package/lib/nodes/health.js +1 -1
- package/lib/nodes/identity.js +10 -13
- package/lib/nodes/store.js +2 -2
- package/lib/nodes/tunnel-supervisor.js +7 -7
- package/lib/nodes/tunnel.js +10 -11
- package/lib/notify/asks.js +4 -4
- package/lib/notify/persist.js +1 -1
- package/lib/notify/push.js +4 -4
- package/lib/notify/routes.js +12 -12
- package/lib/proxy/node-proxy.js +4 -4
- package/lib/proxy/panel-proxy.js +2 -2
- package/lib/pty/attach.js +1 -1
- package/lib/server.js +3 -3
- package/lib/settings/pairing-coordinator.js +1 -1
- package/lib/settings/routes.js +4 -4
- package/lib/tmux/lifecycle.js +4 -4
- package/lib/tmux/preview.js +1 -1
- package/lib/update/runner.js +9 -9
- package/lib/vl-nodes/broker.js +1 -1
- package/lib/ws/bridge.js +1 -1
- package/package.json +20 -2
- package/frontend/index.html +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,133 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NexusCrew are tracked here.
|
|
4
4
|
|
|
5
|
+
## 0.9.18 — 2026-09-04
|
|
6
|
+
|
|
7
|
+
### Upstream Rust 0.153.2
|
|
8
|
+
|
|
9
|
+
- **New upstream capabilities.** The GPT-6-Astra model catalogue is included,
|
|
10
|
+
hook activity is visible in the TUI, and app-server supports asynchronous user
|
|
11
|
+
input with thread metadata.
|
|
12
|
+
- **Smoother daily use.** Vim undo/redo and app-server reconnect improve the
|
|
13
|
+
interactive experience, while `[tui].disable_paste_burst` is documented in
|
|
14
|
+
its canonical configuration location.
|
|
15
|
+
- **More context control.** Experimental context management is available for
|
|
16
|
+
evaluation alongside the upstream release changes.
|
|
17
|
+
|
|
18
|
+
### Codex VL fixes
|
|
19
|
+
|
|
20
|
+
- **Loops stay responsive.** Failed `/loop` owner and delegate preconditions
|
|
21
|
+
report an error without terminating the TUI; `/loop` usage also lists the
|
|
22
|
+
delegate, strategy, delegation and owner commands.
|
|
23
|
+
- **Vivling and fleet metadata recover cleanly.** `manage_loops` sees its jobs
|
|
24
|
+
after resume, and Vivling accepts fenced or prefixed JSON replies from any
|
|
25
|
+
provider while model metadata is resolved by engine identity.
|
|
26
|
+
- **Quieter, clearer operation.** The expression planner no longer floods the
|
|
27
|
+
log database, terminal state reconciles after every terminal event, and Esc
|
|
28
|
+
interrupts a running task even during a long tool call.
|
|
29
|
+
|
|
30
|
+
## 0.9.17 — 2026-09-01 — "The Package Reads Like The Product"
|
|
31
|
+
|
|
32
|
+
- **The installed files no longer carry the workshop's notes.** In the payload
|
|
33
|
+
of 0.9.16 and earlier, a reader of the installed package could find traces of
|
|
34
|
+
the environment that builds NexusCrew: device and node names, working paths
|
|
35
|
+
and hostnames, and the markers of the internal review process, sitting in
|
|
36
|
+
comments and metadata next to the product's own code. Nothing secret and
|
|
37
|
+
nothing functional — but writing addressed to the builders has no business
|
|
38
|
+
shipping to the users, and a version is never republished, so it stayed.
|
|
39
|
+
The payload has been swept: the installed tree now reads as the product
|
|
40
|
+
only. The sweep covers the classes above, and says so rather than claiming
|
|
41
|
+
every sentence of every comment was rewritten.
|
|
42
|
+
|
|
43
|
+
- **The promise is checked by tests that read the artifact, not by a
|
|
44
|
+
checklist.** The npm payload and the published git surface are both
|
|
45
|
+
inspected by the suite: every forbidden pattern is listed with its reason,
|
|
46
|
+
a file that cannot be read fails the check instead of being skipped, and a
|
|
47
|
+
pattern list emptied to make the suite pass fails with it. What the package
|
|
48
|
+
ships is now a tested property, not a step someone remembers to do.
|
|
49
|
+
|
|
50
|
+
- **The guides it points to travel with the package.** The README and three
|
|
51
|
+
built-in skills cite thirteen documentation files that an npm install never
|
|
52
|
+
contained: all twenty-six relative links were dead out of the box, sending
|
|
53
|
+
the reader to files that were not there. The thirteen guides now ship with
|
|
54
|
+
the package — those and no others.
|
|
55
|
+
|
|
56
|
+
## 0.9.16 — 2026-08-28 — "The Cap Becomes One Hundred"
|
|
57
|
+
|
|
58
|
+
- **The fleet engine cap is 100, and the backup reads its own exports.** The
|
|
59
|
+
server accepted up to 100 engines and refused the 101st with an explicit
|
|
60
|
+
reason, but the fleet-backup parser on the frontend still enforced the old
|
|
61
|
+
cap of 24: `createFleetBackup` happily produced documents with 25–100
|
|
62
|
+
engines that `parseFleetBackup` then rejected as `invalid-format`. The
|
|
63
|
+
frontend copy is now aligned with the backend cap, and the parser names the
|
|
64
|
+
cap in its refusal instead of reporting a generic format error — a fleet of
|
|
65
|
+
any valid size round-trips.
|
|
66
|
+
|
|
67
|
+
- **WebSocket closures are classified, counted, and visible.** Inter-cell
|
|
68
|
+
sends and drop counters land in the observability surface, so a connection
|
|
69
|
+
that keeps falling can be told apart from one that was never stable.
|
|
70
|
+
|
|
71
|
+
- **The first health sample that says down means down.** Hysteresis only
|
|
72
|
+
protects a previously authoritative `transport: up`; without one, the first
|
|
73
|
+
failed probe is a real failure and is reported as such, instead of leaving
|
|
74
|
+
a dead node labelled unknown.
|
|
75
|
+
|
|
76
|
+
- **Catalog and skills surface sanitized**: internal cell names are gone from
|
|
77
|
+
skills, UI placeholders and comments.
|
|
78
|
+
|
|
79
|
+
## 0.9.15 — 2026-08-27 — "The Terminals Survive A Flaky Connection"
|
|
80
|
+
|
|
81
|
+
- **Terminal panes no longer rebuild because of one late health probe.** A
|
|
82
|
+
single slow peer-health check declared a live node dead; the roster
|
|
83
|
+
emptied, and when the node came back every terminal window was rebuilt from
|
|
84
|
+
scratch — new socket, new attach, full repaint — whose traffic then caused
|
|
85
|
+
the next drop on an already queued line. Transient peer-health failures are
|
|
86
|
+
tolerated instead of tearing the node down.
|
|
87
|
+
|
|
88
|
+
- **PTYs survive transient terminal disconnects**, and resumes are authorized
|
|
89
|
+
before scope checks, so a reconnecting pane picks its session back up
|
|
90
|
+
instead of starting over.
|
|
91
|
+
|
|
92
|
+
- **The Live star distinguishes a started thread from an active one**, the
|
|
93
|
+
Live bridge declares its Fleet identity on initialize, and thread status
|
|
94
|
+
labels are guarded against Live claims.
|
|
95
|
+
|
|
96
|
+
- **MCP identity failures are no longer cached for life**: a blocked tool
|
|
97
|
+
names the cause and gets a fresh chance instead of being failed forever.
|
|
98
|
+
|
|
99
|
+
## 0.9.14 — 2026-08-27 — "The Bundle Builds After The Bump"
|
|
100
|
+
|
|
101
|
+
- **The package installs.** 0.9.13 shipped with `frontend/dist/version.json`
|
|
102
|
+
still saying `0.9.12` while the server reported `0.9.13`, so nodes refused
|
|
103
|
+
to start with «incomplete installation: frontend and server do not match».
|
|
104
|
+
The bundle had been rebuilt before the version was raised and carried the
|
|
105
|
+
old number. No functional change against 0.9.13 — the same two fixes ship
|
|
106
|
+
again, with both sides agreeing on the version.
|
|
107
|
+
|
|
108
|
+
## 0.9.13 — 2026-08-27 — "The Cells Stay When The Connection Drops"
|
|
109
|
+
|
|
110
|
+
- **One deadline per lease.** The supervisor's lease read the clock three
|
|
111
|
+
times for a single transaction: the persisted bound and the proof expiry
|
|
112
|
+
were taken at different instants, and a one-millisecond gap invalidated the
|
|
113
|
+
proof at the next check, dropping the lease and letting cells detach and
|
|
114
|
+
reattach on their own. The deadline is now computed once and the proof
|
|
115
|
+
expiry derives from it.
|
|
116
|
+
|
|
117
|
+
- **The roster distinguishes "could not ask" from "nothing is there".** A
|
|
118
|
+
skipped poll or a fallen node no longer empties the cell list; the last
|
|
119
|
+
known roster is kept until a real state replaces it.
|
|
120
|
+
|
|
121
|
+
- **A cell's identity is refused, not borrowed.** Identity resolution fails
|
|
122
|
+
closed instead of attributing a cell to another one, and a remote `vl`
|
|
123
|
+
variant that is missing its Ollama pair refuses naming the offending field
|
|
124
|
+
rather than silently falling back to local Ollama. A `vl` cell can live
|
|
125
|
+
outside the local Ollama too, and `vl.native` no longer overrides the
|
|
126
|
+
operator's choices.
|
|
127
|
+
|
|
128
|
+
- **New skill `cellforge`** (create, modify and audit a cell) and
|
|
129
|
+
**opencode-go**: `deepseek-v4-flash-vision-exp` added with real
|
|
130
|
+
measurements; the git publish surface now has the same gate the npm one had.
|
|
131
|
+
|
|
5
132
|
## 0.9.12 — 2026-08-22 — "Build After The Bump"
|
|
6
133
|
|
|
7
134
|
- **Fixes 0.9.11, which would not start.** `frontend/dist/version.json` said
|
|
@@ -2199,7 +2326,7 @@ nothing anywhere said so.
|
|
|
2199
2326
|
injection. Persistent offline queues, attachments and delegated capability workers remain
|
|
2200
2327
|
explicitly deferred rather than being represented as implemented.
|
|
2201
2328
|
- Tests: **770 total** (769 pass / 1 platform-dependent skip) in the isolated Node harness plus
|
|
2202
|
-
5 passing frontend component tests; production build PASS. Real
|
|
2329
|
+
5 passing frontend component tests; production build PASS. Real nodo-A–hub–nodo-B end-to-end
|
|
2203
2330
|
pairing and delivery remain an operator field test and are not represented as automated.
|
|
2204
2331
|
|
|
2205
2332
|
## 0.8.12 — 2026-07-13 — "Mobile Roster"
|
|
@@ -2223,7 +2350,7 @@ nothing anywhere said so.
|
|
|
2223
2350
|
is disabled and explains that a Termux boot script still requires the Termux:Boot app to be
|
|
2224
2351
|
installed and launched once.
|
|
2225
2352
|
- Tests: **749 total** (748 pass / 1 platform-dependent skip), production build PASS, root and
|
|
2226
|
-
frontend dependency audits clean.
|
|
2353
|
+
frontend dependency audits clean. nodo-A–nodo-B–hub end-to-end pairing was not executed in this
|
|
2227
2354
|
release gate.
|
|
2228
2355
|
|
|
2229
2356
|
## 0.8.11 — 2026-07-13 — "Tmux Survival"
|
|
@@ -2281,7 +2408,7 @@ nothing anywhere said so.
|
|
|
2281
2408
|
HTTP-port move when paired peers depend on the configured endpoint.
|
|
2282
2409
|
- Tests: **726 total** (725 pass / 1 platform-dependent skip), frontend production build PASS,
|
|
2283
2410
|
dependency audit clean, isolated HOME clean, and package/public-tree verification required
|
|
2284
|
-
before publication. Real
|
|
2411
|
+
before publication. Real nodo-A–nodo-B–hub interoperability remains an external follow-up and is
|
|
2285
2412
|
not represented as an automated test.
|
|
2286
2413
|
|
|
2287
2414
|
## 0.8.9 — 2026-07-12 — "Hydra Workspaces"
|
|
@@ -2309,6 +2436,11 @@ nothing anywhere said so.
|
|
|
2309
2436
|
|
|
2310
2437
|
## 0.8.8 — 2026-07-12 — "Reliable Composer"
|
|
2311
2438
|
|
|
2439
|
+
> **Never published.** Only a preparation commit exists for this version and
|
|
2440
|
+
> npm has no 0.8.8 — the next published release is 0.8.9. The entry stays
|
|
2441
|
+
> because the changes it describes are real; a reader looking for the version
|
|
2442
|
+
> on the registry should know why it is not there.
|
|
2443
|
+
|
|
2312
2444
|
- The PWA composer now sends long and multiline drafts through xterm's explicit paste path,
|
|
2313
2445
|
preserving the terminal application's bracketed-paste mode. Enter travels as a separate
|
|
2314
2446
|
input only after the complete paste was accepted, so agent TUIs no longer absorb submission
|
|
@@ -2625,6 +2757,19 @@ Core rewrite from screenshot-and-poll to a faithful tmux client.
|
|
|
2625
2757
|
backpressure cutoff, JSON errors with codes
|
|
2626
2758
|
- token delivered via URL fragment (never logged), 0600 file, constant-time compare
|
|
2627
2759
|
|
|
2760
|
+
## 0.2.5 — 2026-04-09 — "Discovery, Routes, Selector"
|
|
2761
|
+
|
|
2762
|
+
Reconstructed from the release commit and its tree: this entry was missing
|
|
2763
|
+
from the file at the time, and the history before 0.7.1 survives only in the
|
|
2764
|
+
`v0.2.5` tag and on npm.
|
|
2765
|
+
|
|
2766
|
+
- Engine discovery and explicit send routes in the runtime — the release
|
|
2767
|
+
commit's own summary — with the chat flow and the status bar wired to them.
|
|
2768
|
+
- A dedicated model selector interface: new `ModelSelector` component and
|
|
2769
|
+
styles wired into the chat.
|
|
2770
|
+
- A prebuilt SQLite library travels with the package instead of requiring a
|
|
2771
|
+
local build.
|
|
2772
|
+
|
|
2628
2773
|
## 0.2.4
|
|
2629
2774
|
|
|
2630
2775
|
- added host-scoped tmux/session discovery so active session truth comes from the selected host
|
|
@@ -2649,3 +2794,12 @@ Core rewrite from screenshot-and-poll to a faithful tmux client.
|
|
|
2649
2794
|
## 0.2.1
|
|
2650
2795
|
|
|
2651
2796
|
- older release line, now deprecated in favor of the current stable line
|
|
2797
|
+
|
|
2798
|
+
## 0.2.0 — 2026-04-01 — "First Published"
|
|
2799
|
+
|
|
2800
|
+
- The first version of the 0.2 line to reach npm: a tmux-based cockpit with
|
|
2801
|
+
persistent sessions for Claude Code, Codex CLI, Gemini CLI and Qwen Code,
|
|
2802
|
+
with remote SSH host support.
|
|
2803
|
+
- Reconstructed from the published package's own description — no release
|
|
2804
|
+
notes from that date survive in this history. The line was later superseded
|
|
2805
|
+
by the 0.4 rewrite and deprecated.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Alibaba Token Plan Personal engines
|
|
2
|
+
|
|
3
|
+
NexusCrew exposes three first-class managed profiles backed by one fixed local
|
|
4
|
+
credential name, `ALIBABA_CODE_API_KEY`:
|
|
5
|
+
|
|
6
|
+
- `claude.alibaba-token-plan`
|
|
7
|
+
- `codex-vl.alibaba-token-plan`
|
|
8
|
+
- `pi.alibaba-token-plan`
|
|
9
|
+
|
|
10
|
+
The credential value is resolved by the NexusCrew credential layer and is
|
|
11
|
+
passed only in the selected child process environment. Engine definitions,
|
|
12
|
+
generated extensions, argv, status responses and logs contain only the
|
|
13
|
+
environment-variable name, never its value. The default model is
|
|
14
|
+
`qwen3.8-max` for all three profiles.
|
|
15
|
+
|
|
16
|
+
## Claude Code
|
|
17
|
+
|
|
18
|
+
- Anthropic-compatible base URL:
|
|
19
|
+
`https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic`
|
|
20
|
+
(Claude appends `/v1/messages`; the configured base must not include it).
|
|
21
|
+
- Authentication: `ANTHROPIC_AUTH_TOKEN` receives the locally resolved value;
|
|
22
|
+
`ANTHROPIC_API_KEY` is deliberately empty.
|
|
23
|
+
- Aliases: MODEL/SONNET/OPUS/FABLE = `qwen3.8-max`, HAIKU =
|
|
24
|
+
`qwen3.6-flash`, SUBAGENT = `qwen3.7-max`.
|
|
25
|
+
- Context: `983616`; effort: `xhigh`. Qwen Cloud documents qwen3.8 thinking
|
|
26
|
+
as always enabled and not user-disableable.
|
|
27
|
+
- State is isolated under
|
|
28
|
+
`~/.nexuscrew/claude-profiles/alibaba-token-plan/` with private modes; the
|
|
29
|
+
native Claude configuration is not modified and no credential is written
|
|
30
|
+
there.
|
|
31
|
+
|
|
32
|
+
## Codex-VL
|
|
33
|
+
|
|
34
|
+
- Compatible base URL:
|
|
35
|
+
`https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1`.
|
|
36
|
+
- Provider wire API: `responses`; provider `env_key`:
|
|
37
|
+
`ALIBABA_CODE_API_KEY`. `OPENAI_API_KEY` is neither read nor forwarded by
|
|
38
|
+
this profile, and there is no OpenAI/PAYG fallback.
|
|
39
|
+
- Latest-Codex allowlist: `qwen3.8-max`, `qwen3.7-max`,
|
|
40
|
+
`qwen3.7-plus`, `qwen3.6-flash`. GLM and DeepSeek are intentionally absent.
|
|
41
|
+
- The bundled qwen3.8 catalog pins context `983616`, effective context `95%`,
|
|
42
|
+
reasoning levels `low/high/xhigh` with `xhigh` default, parallel tools off,
|
|
43
|
+
text+image input and original image detail.
|
|
44
|
+
- Offline compatibility target: `codex-vl 0.144.7`.
|
|
45
|
+
|
|
46
|
+
## Pi
|
|
47
|
+
|
|
48
|
+
- Dedicated provider ID `alibaba-token-plan`, standard permissions only.
|
|
49
|
+
- A generated private extension refers to `$ALIBABA_CODE_API_KEY`; it contains
|
|
50
|
+
no credential value.
|
|
51
|
+
- Qwen response-capable models use Pi's `openai-responses` adapter. `glm-5.2`
|
|
52
|
+
and `deepseek-v4-pro` use Pi's `openai-completions` adapter, whose compatibility
|
|
53
|
+
path preserves `reasoning_content` in replayed assistant/tool turns.
|
|
54
|
+
- `qwen3.8-max` is reasoning-enabled, accepts text+image, uses context
|
|
55
|
+
`983616`, and launches with `--thinking xhigh`.
|
|
56
|
+
- Offline compatibility target: `pi 0.80.10`.
|
|
57
|
+
|
|
58
|
+
## Verification boundary
|
|
59
|
+
|
|
60
|
+
Catalog, normalization, credential resolution, generated config/extension,
|
|
61
|
+
argv/env separation and local runtime loading are tested without network
|
|
62
|
+
access. No provider request is part of this candidate. Endpoint acceptance,
|
|
63
|
+
stream completion, vision input, tool round-trip and Token Plan quota behavior
|
|
64
|
+
remain post-reboot interactive smoke gates.
|
|
65
|
+
|
|
66
|
+
Primary references:
|
|
67
|
+
|
|
68
|
+
- https://docs.qwencloud.com/developer-guides/clients-and-developer-tools/claude-code
|
|
69
|
+
- https://docs.qwencloud.com/developer-guides/clients-and-developer-tools/codex
|
|
70
|
+
- https://docs.qwencloud.com/developer-guides/clients-and-developer-tools/openclaw
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Audio Share and native TTS
|
|
2
|
+
|
|
3
|
+
[← Documentation index](README.md)
|
|
4
|
+
|
|
5
|
+
Audio Share lets an authorized Fleet cell ask a **specific node with a real
|
|
6
|
+
speaker** to synthesize a short utterance. It is separate from browser spoken
|
|
7
|
+
notifications: browser speech remains a visible-and-focused PWA convenience,
|
|
8
|
+
while Audio Share is a node-local backend capability.
|
|
9
|
+
|
|
10
|
+
There is no cloud speech provider, central voice service, browser relay, or
|
|
11
|
+
`play arbitrary audio file` command in this feature.
|
|
12
|
+
|
|
13
|
+
## Consent and routing are separate
|
|
14
|
+
|
|
15
|
+
Three independent controls must all permit delivery:
|
|
16
|
+
|
|
17
|
+
1. **Share** publishes a node to the authorized transport topology.
|
|
18
|
+
2. **Visibility** controls which peers may route through it.
|
|
19
|
+
3. **Audio consent** is local to the node that would make sound, defaults to
|
|
20
|
+
off, and is never mutable through federation.
|
|
21
|
+
|
|
22
|
+
Turning on Share never grants permission to make a device speak. A target node
|
|
23
|
+
also applies its own ACL, `READONLY` state, rate limit, native-adapter
|
|
24
|
+
availability and local consent immediately before synthesis.
|
|
25
|
+
|
|
26
|
+
Configure consent under **Settings → Audio** on the device that owns the
|
|
27
|
+
speaker. The panel exposes only redacted capability metadata, an explicit
|
|
28
|
+
fixed-text local test, and a local Stop button. Stop remains available in
|
|
29
|
+
`READONLY`; speaking does not.
|
|
30
|
+
|
|
31
|
+
## Exact targets and groups
|
|
32
|
+
|
|
33
|
+
`nc_speak` accepts one exact 32-character node instance ID. There is no
|
|
34
|
+
wildcard, `all`, label or route-name target.
|
|
35
|
+
|
|
36
|
+
Settings can also save local named groups of up to eight exact node IDs:
|
|
37
|
+
|
|
38
|
+
- **Primary + failover** is the default. It tries the ordered primary, waits up
|
|
39
|
+
to five seconds for a start acknowledgement, then tries the next endpoint on
|
|
40
|
+
`refused`, `unreachable`, or `unknown`.
|
|
41
|
+
- **Fan-out** is explicit. It tries all configured endpoints in parallel.
|
|
42
|
+
|
|
43
|
+
A group is only an origin-side delivery preference. It cannot override consent,
|
|
44
|
+
ACL, liveness, `READONLY`, or a local Stop on any member. A temporarily absent
|
|
45
|
+
member remains visible in group editing but is not treated as ready.
|
|
46
|
+
|
|
47
|
+
## MCP commands
|
|
48
|
+
|
|
49
|
+
The caller must be an active local Fleet cell. NexusCrew signs the bridge
|
|
50
|
+
request with a node-local HMAC and resolves the real tmux/Fleet identity; a UI
|
|
51
|
+
bearer token, a request header, or a body field cannot declare a cell identity.
|
|
52
|
+
|
|
53
|
+
| Tool | Purpose |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `nc_speak` | Speak to one exact node ID. |
|
|
56
|
+
| `nc_speak_status` | Read the caller-scoped receipt for one exact-target utterance. |
|
|
57
|
+
| `nc_speak_stop` | Stop an exact-target utterance, or all caller-owned utterances on that target. |
|
|
58
|
+
| `nc_speak_group` | Start a local named primary/failover or explicit fan-out group. |
|
|
59
|
+
| `nc_speak_group_status` | Read the caller-scoped per-endpoint group receipt. |
|
|
60
|
+
| `nc_speak_group_stop` | Prevent later failover candidates and send Stop to endpoints already admitted. |
|
|
61
|
+
|
|
62
|
+
Text is limited to 320 characters. Receipts intentionally never retain text,
|
|
63
|
+
language, voice, binary path, route, secret, or an aggregate success boolean.
|
|
64
|
+
Each endpoint is reported independently as:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
refused | unreachable | accepted | spoken | unknown
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`spoken` means only that the native adapter confirmed it started synthesis. It
|
|
71
|
+
does **not** prove that a person heard anything: an output sink can be muted,
|
|
72
|
+
null, disconnected, or unavailable. `accepted` is transitional; the caller
|
|
73
|
+
queries status by immutable utterance ID for later state changes.
|
|
74
|
+
|
|
75
|
+
## Native platform adapters
|
|
76
|
+
|
|
77
|
+
| Platform | Preferred native command | Important limitation |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| Android / Termux | `termux-tts-speak` | Requires Termux:API and its permissions; Android Doze can suspend the process. |
|
|
80
|
+
| macOS | `say` | Requires an active GUI/CoreAudio output path; command success is not an audibility test. |
|
|
81
|
+
| Linux | `espeak-ng`, then `spd-say` | Requires a real output sink; a null or headless sink can return exit 0 without sound. |
|
|
82
|
+
|
|
83
|
+
The implementation probes executable availability without playing sound. It
|
|
84
|
+
sends text on stdin whenever the command supports it; `spd-say` is a declared
|
|
85
|
+
fallback whose text must be placed in argv. Automatic tests use fakes and do
|
|
86
|
+
not claim physical audibility on any platform.
|
|
87
|
+
|
|
88
|
+
## Safety behavior
|
|
89
|
+
|
|
90
|
+
- One node serializes its own utterances; normal pending work is bounded.
|
|
91
|
+
- A high-urgency utterance may preempt queue order but never bypasses rate
|
|
92
|
+
limits or consent.
|
|
93
|
+
- An adapter-start watchdog reports `unknown`, not a fabricated success.
|
|
94
|
+
- Remote Stop is scoped by origin and utterance ID. Local Stop is sovereign and
|
|
95
|
+
works without network, hub, credentials, or a running caller.
|
|
96
|
+
- Audio receipt and group receipt storage are bounded to 512 records with a
|
|
97
|
+
24-hour TTL.
|
|
98
|
+
- An utterance ID cannot be reused between group and single-target commands by
|
|
99
|
+
the same caller while its receipt remains live.
|
|
100
|
+
|
|
101
|
+
## Local state
|
|
102
|
+
|
|
103
|
+
All Audio Share state remains local to the node:
|
|
104
|
+
|
|
105
|
+
| File | Contents |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `~/.nexuscrew/audio.json` | Closed-schema local audio consent. |
|
|
108
|
+
| `~/.nexuscrew/audio-groups.json` | Local named target groups, user-only and atomic. |
|
|
109
|
+
| `~/.nexuscrew/audio-bridge.key` | User-only HMAC secret for MCP-to-server origin proof. |
|
|
110
|
+
|
|
111
|
+
These files are not Fleet credentials, are not federated, and must not be
|
|
112
|
+
copied into logs, tickets, or a shared repository.
|
|
113
|
+
|
|
114
|
+
## Related guides
|
|
115
|
+
|
|
116
|
+
- [MCP bridge](MCP.md)
|
|
117
|
+
- [Notifications](NOTIFICATIONS.md)
|
|
118
|
+
- [Connect nodes](NODES.md)
|
|
119
|
+
- [Security](SECURITY.md)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# The cell panel
|
|
2
|
+
|
|
3
|
+
A cell can carry a **panel**: a web interface of its own that NexusCrew serves
|
|
4
|
+
inside the app, next to the terminal. A remote desktop, a notebook, a dashboard
|
|
5
|
+
— anything that speaks HTTP.
|
|
6
|
+
|
|
7
|
+
The panel is opt-in and off by default. A cell without `panelUrl` simply has no
|
|
8
|
+
panel button.
|
|
9
|
+
|
|
10
|
+
## Configuring it
|
|
11
|
+
|
|
12
|
+
`panelUrl` is a **property of the cell**, a sibling of `engine` and `cwd`. It is
|
|
13
|
+
not a command, not an argument, and not something the cell runs:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"cells": [
|
|
18
|
+
{
|
|
19
|
+
"id": "Design",
|
|
20
|
+
"engine": "shell.local",
|
|
21
|
+
"commands": { "shell.local": "zsh" },
|
|
22
|
+
"panelUrl": "http://127.0.0.1:6080/vnc.html"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Put it on an **engine** instead, and it becomes the default for every cell using
|
|
29
|
+
that engine.
|
|
30
|
+
|
|
31
|
+
The value must be an `http:` or `https:` URL pointing at **loopback** —
|
|
32
|
+
`127.0.0.1`, `[::1]` or `localhost`. Anything else is rejected when the
|
|
33
|
+
definition is read, and the cell keeps working without a panel. A container's
|
|
34
|
+
own address does not count as loopback: publish the port to `127.0.0.1` on the
|
|
35
|
+
host and point the panel there.
|
|
36
|
+
|
|
37
|
+
### The mistake to avoid
|
|
38
|
+
|
|
39
|
+
Do not put the URL in the cell's **command**. The command is what the cell
|
|
40
|
+
executes, so this:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
"commands": { "shell.local": "panelUrl: http://127.0.0.1:6900/" }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
makes the cell try to run `panelUrl:` as a program. It fails to start, and
|
|
47
|
+
because no `panelUrl` property was ever set, no panel button appears either —
|
|
48
|
+
two symptoms, one cause, and neither of them says "wrong field".
|
|
49
|
+
|
|
50
|
+
### Setting it from the app
|
|
51
|
+
|
|
52
|
+
The cell and engine editors carry a `panelUrl` field. It anticipates the same
|
|
53
|
+
loopback rule the backend enforces, so a non-loopback value is flagged before
|
|
54
|
+
you save — but that check is not authoritative: the definition is validated
|
|
55
|
+
again, the same way, the moment it is actually read.
|
|
56
|
+
|
|
57
|
+
You can still set it by editing the fleet definition directly, or by exporting
|
|
58
|
+
a fleet backup, adding the property, and importing it back.
|
|
59
|
+
|
|
60
|
+
## A worked example
|
|
61
|
+
|
|
62
|
+
A container publishing a remote desktop on the host's loopback:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
127.0.0.1:6900 -> the desktop's web interface
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Then, on the cell you want to watch it from:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
"panelUrl": "http://127.0.0.1:6900/"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Start the cell and open it: the panel button sits next to the terminal.
|
|
75
|
+
|
|
76
|
+
If the desktop asks for a password, you will be asked for it inside the panel —
|
|
77
|
+
the forwarder carries no credentials of its own, in either direction.
|
|
78
|
+
|
|
79
|
+
## Checking it worked
|
|
80
|
+
|
|
81
|
+
| What you see | What it means |
|
|
82
|
+
|---|---|
|
|
83
|
+
| No panel button at all | The cell has no valid `panelUrl`. Either it was never set, or it was rejected — a non-loopback host, or a value that is not a URL. |
|
|
84
|
+
| Button present, frame says the panel is unavailable | The cell was found but the destination did not answer. Check the service is listening **on loopback of the machine running the node**. |
|
|
85
|
+
| Button present, frame shows the page | Working. Sub-resources and WebSocket travel on the viewing cookie described below. |
|
|
86
|
+
| "This node does not grant the panel to the requester" | The panel lives on a paired node that has not granted access. It is granted there, not here — see the last section. |
|
|
87
|
+
|
|
88
|
+
Note the difference between the first two rows: a missing button is a
|
|
89
|
+
**configuration** problem, an unavailable frame is a **reachability** one. They
|
|
90
|
+
look similar and have nothing to do with each other.
|
|
91
|
+
|
|
92
|
+
## Why loopback only
|
|
93
|
+
|
|
94
|
+
This is the constraint that shapes everything else, so it is worth stating
|
|
95
|
+
plainly: **the panel forwarder is not a port-forward.**
|
|
96
|
+
|
|
97
|
+
The caller picks *which cell* to open, never *where to connect*. The
|
|
98
|
+
destination is resolved from that cell's own `panelUrl`, on the machine that
|
|
99
|
+
runs the node. Without this rule, a route that opens "a panel" would be a way
|
|
100
|
+
to reach any address the node can reach — including services that trust the
|
|
101
|
+
network they sit on.
|
|
102
|
+
|
|
103
|
+
The panel is also a service that already holds sessions: behind a remote
|
|
104
|
+
desktop there is usually a browser with accounts logged in. That kind of access
|
|
105
|
+
is not revoked by rotating a key, which is why access is granted per peer and
|
|
106
|
+
denied by default.
|
|
107
|
+
|
|
108
|
+
## Where it runs
|
|
109
|
+
|
|
110
|
+
| Platform | State |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Linux server / VPS | **In use.** Any loopback service works; the common case is a container exposing noVNC on `127.0.0.1`. |
|
|
113
|
+
| Termux / Android | **Expected to work, not verified.** The forwarder needs nothing but a loopback HTTP service — no container runtime, no specific desktop. It has not been tried, and until it is, treat it as untested rather than supported. |
|
|
114
|
+
|
|
115
|
+
Nothing in the forwarder is platform-specific: it proxies HTTP and WebSocket to
|
|
116
|
+
a URL. If a platform can run a web service on loopback, it can host a panel.
|
|
117
|
+
|
|
118
|
+
## How the browser gets in
|
|
119
|
+
|
|
120
|
+
Worth knowing, because it explains a design that would otherwise look odd.
|
|
121
|
+
|
|
122
|
+
An `<iframe src>` is a browser navigation: it carries no application headers.
|
|
123
|
+
So the panel cannot sit behind the usual bearer-token gate — the one consumer
|
|
124
|
+
it exists for would be locked out. Putting the token in the query string does
|
|
125
|
+
not help either: the panel's own pages request their sub-resources with
|
|
126
|
+
relative URLs and no query, so everything after the first request would fail
|
|
127
|
+
and the frame would stay blank.
|
|
128
|
+
|
|
129
|
+
Instead:
|
|
130
|
+
|
|
131
|
+
1. the authenticated app asks for a **ticket** — opaque, one-use, valid for
|
|
132
|
+
seconds, bound to one cell;
|
|
133
|
+
2. the frame's first request spends it, and the answer sets a **viewing
|
|
134
|
+
cookie**: `HttpOnly`, `SameSite=Strict`, scoped by `Path` to that cell's
|
|
135
|
+
panel subtree;
|
|
136
|
+
3. the relative sub-resources travel on that cookie, and nothing else does.
|
|
137
|
+
|
|
138
|
+
The node's token never reaches the browser, a log, or a `Referer` header, and
|
|
139
|
+
no credential of ours is forwarded to the panel itself.
|
|
140
|
+
|
|
141
|
+
## Panels on another machine
|
|
142
|
+
|
|
143
|
+
A panel can be opened toward a node you have paired, but **only if that node
|
|
144
|
+
granted it**:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
nexuscrew nodes panel <node> on
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The default is off, and it is off deliberately: in the pairing model a peer is
|
|
151
|
+
otherwise treated much like the operator, and that is a coherent choice for
|
|
152
|
+
everything except a panel, for the reason given above.
|
|
153
|
+
|
|
154
|
+
The decision is always made by the node that **owns** the panel, never by the
|
|
155
|
+
one asking. When it says no, the interface says so by name — you get "panel not
|
|
156
|
+
granted", not a blank rectangle you have to guess about.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
[← Documentation index](README.md)
|
|
4
|
+
|
|
5
|
+
Most setup lives in the PWA. Runtime state is local to the current user and is
|
|
6
|
+
never synchronized to a hosted control service.
|
|
7
|
+
|
|
8
|
+
## Local paths
|
|
9
|
+
|
|
10
|
+
| Path | Contents |
|
|
11
|
+
|---|---|
|
|
12
|
+
| `~/.nexuscrew/config.json` | Port, Fleet mode and runtime options |
|
|
13
|
+
| `~/.nexuscrew/token` | Local PWA bearer token |
|
|
14
|
+
| `~/.nexuscrew/credentials.json` | Optional node-local write-only provider store |
|
|
15
|
+
| `~/.nexuscrew/audio.json` | Local Audio Share consent (default off) |
|
|
16
|
+
| `~/.nexuscrew/audio-groups.json` | Local named Audio Share target groups |
|
|
17
|
+
| `~/.nexuscrew/audio-bridge.key` | Local HMAC proof for MCP Audio Share calls |
|
|
18
|
+
| `~/.nexuscrew/tunnels/` | Managed SSH supervisor state and owner-only logs |
|
|
19
|
+
| `~/NexusFiles/<session>/` | Per-session inbox and outbox |
|
|
20
|
+
|
|
21
|
+
Sensitive files are created with user-only permissions. The credential store is
|
|
22
|
+
optional; NexusCrew can also resolve compatible provider variables from its
|
|
23
|
+
service environment.
|
|
24
|
+
|
|
25
|
+
## Precedence
|
|
26
|
+
|
|
27
|
+
Runtime values follow:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
defaults < config file < environment
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Common overrides:
|
|
34
|
+
|
|
35
|
+
| Variable | Purpose |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `NEXUSCREW_PORT` | Loopback HTTP port |
|
|
38
|
+
| `NEXUSCREW_CONFIG_FILE` | Alternate config file, useful for isolated tests |
|
|
39
|
+
| `NEXUSCREW_TOKEN_FILE` | Alternate bearer-token file |
|
|
40
|
+
| `NEXUSCREW_FILES_ROOT` | Alternate per-session file root |
|
|
41
|
+
| `NEXUSCREW_TMUX` | tmux executable |
|
|
42
|
+
| `NEXUSCREW_FLEET=0` | Disable Fleet management |
|
|
43
|
+
| `NEXUSCREW_ALTERNATE_SCREEN=1` | Keep tmux's standard alternate-screen behavior for newly created NexusCrew sessions |
|
|
44
|
+
| `NEXUSCREW_READONLY=1` | Disable server-side mutations |
|
|
45
|
+
| `NEXUSCREW_AUTO_UPDATE=0` | Disable the stable update scheduler |
|
|
46
|
+
| `NEXUSCREW_DEBUG=1` | Enable bounded verbose diagnostics |
|
|
47
|
+
| `NEXUSCREW_VOICE_URL` | Optional server-side speech-to-text endpoint |
|
|
48
|
+
| `NEXUSCREW_VOICE_TOKEN_FILE` | Token file for the optional STT endpoint |
|
|
49
|
+
|
|
50
|
+
Use `NEXUSCREW_CONFIG_FILE`, `NEXUSCREW_TOKEN_FILE`, `NEXUSCREW_FILES_ROOT`
|
|
51
|
+
and a separate `HOME`/XDG root when creating an isolated test runtime.
|
|
52
|
+
|
|
53
|
+
## Session and Fleet settings
|
|
54
|
+
|
|
55
|
+
| `config.json` key | Default | Effect |
|
|
56
|
+
|---|---:|---|
|
|
57
|
+
| `alternateScreen` | `false` | New sessions created through Fleet or the PWA keep full-screen TUI output on the normal screen, where it enters tmux history and remains scrollable. Set `true` to restore the standard tmux alternate screen. |
|
|
58
|
+
|
|
59
|
+
The setting applies only when NexusCrew creates a session through Fleet or the
|
|
60
|
+
PWA; active and unmanaged sessions are not changed. `NEXUSCREW_ALTERNATE_SCREEN=1` has the usual
|
|
61
|
+
environment precedence. For the normal-screen mode, keep the user-owned tmux
|
|
62
|
+
`history-limit` at least 10000 (100000 is a practical value). `nexuscrew doctor`
|
|
63
|
+
warns when it observes a lower value but never changes `~/.tmux.conf`.
|
|
64
|
+
In the PWA, the same local setting is available at **Settings → System →
|
|
65
|
+
Diagnostics**.
|
|
66
|
+
|
|
67
|
+
Fleet cells and engines are managed in **Settings → Fleet**. A cell records:
|
|
68
|
+
|
|
69
|
+
- logical cell name and tmux session
|
|
70
|
+
- working directory
|
|
71
|
+
- engine, provider and model
|
|
72
|
+
- permission policy
|
|
73
|
+
- optional system prompt or trusted Shell command
|
|
74
|
+
- boot state
|
|
75
|
+
|
|
76
|
+
Provider key values are not stored in cell or engine definitions. The PWA
|
|
77
|
+
reports whether a required variable is configured, but never returns its value.
|
|
78
|
+
|
|
79
|
+
## Browser-local settings
|
|
80
|
+
|
|
81
|
+
The following are local to the browser origin:
|
|
82
|
+
|
|
83
|
+
- deck layout and node collapse state
|
|
84
|
+
- session ordering and pins
|
|
85
|
+
- mobile key-bar and keyboard preferences
|
|
86
|
+
- per-cell draft, composer size and bounded prompt history
|
|
87
|
+
- spoken-notification opt-in and successful per-page voice priming
|
|
88
|
+
|
|
89
|
+
Browser-local state is not included in Fleet backups and can be cleared from
|
|
90
|
+
**Settings → System**.
|
|
91
|
+
|
|
92
|
+
## Speech input
|
|
93
|
+
|
|
94
|
+
Speech-to-text has two independent paths:
|
|
95
|
+
|
|
96
|
+
- Browser Web Speech where the browser exposes it.
|
|
97
|
+
- Optional server STT through `NEXUSCREW_VOICE_URL`.
|
|
98
|
+
|
|
99
|
+
If neither path is available, the microphone control is hidden. Server STT is
|
|
100
|
+
separate from optional spoken notifications, which use the device's browser
|
|
101
|
+
speech engine and send no text to a speech service.
|
|
102
|
+
|
|
103
|
+
## Token rotation
|
|
104
|
+
|
|
105
|
+
The browser token travels in the URL fragment (`#token=...`), not in the
|
|
106
|
+
initial HTTP request. To rotate it:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
nexuscrew stop
|
|
110
|
+
rm ~/.nexuscrew/token
|
|
111
|
+
nexuscrew
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Review the exact target before removing any alternate token file.
|
|
115
|
+
|
|
116
|
+
## Related guides
|
|
117
|
+
|
|
118
|
+
- [Fleet and terminals](FLEET.md)
|
|
119
|
+
- [Notifications](NOTIFICATIONS.md)
|
|
120
|
+
- [Audio Share and native TTS](AUDIO_SHARE.md)
|
|
121
|
+
- [Security](SECURITY.md)
|
|
122
|
+
- [Operations](OPERATIONS.md)
|