@serkanalgur/opencode-nexus 2.6.0 → 2.8.0
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/README.md +148 -40
- package/dist/index.js +4628 -218
- package/dist/tui.js +266 -52
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@serkanalgur/opencode-nexus)
|
|
6
6
|
[](https://www.npmjs.com/package/@serkanalgur/opencode-nexus)
|
|
7
|
+
[](https://github.com/serkanalgur/opencode-nexus/stargazers)
|
|
7
8
|
[](https://github.com/serkanalgur/opencode-nexus/blob/main/LICENSE)
|
|
8
9
|
[](https://opencode.ai)
|
|
9
10
|
[](https://www.typescriptlang.org/)
|
|
@@ -30,17 +31,17 @@ OpenCode Nexus is an agent orchestration plugin for [OpenCode V2](https://openco
|
|
|
30
31
|
| **DAG Execution** | Tasks are parallelized based on dependency graphs with priority queuing |
|
|
31
32
|
| **Cost-Aware Routing** | Scores models by quality/cost/speed, selects optimal per task complexity |
|
|
32
33
|
| **Self-Healing** | Retries with exponential backoff, context transfer, escalation policies |
|
|
33
|
-
| **Web Dashboard** |
|
|
34
|
+
| **Web Dashboard** | A live view of sessions, agents, tasks, costs and config, served over HTTP + WebSocket (default port 4747) — started on request from `/nexus-dashboard` or the agent, never automatically |
|
|
34
35
|
| **TUI Dashboard** | Monitor agents, budget, and config from the terminal |
|
|
35
36
|
| **Team Mode** | Lead agent orchestrates specialist agents in parallel |
|
|
36
37
|
| **Todo & Goal Tracking** | Enforce task completion, persist objectives across sessions |
|
|
37
38
|
| **Persistent Memory** | SQLite-backed memory store with TTL and search |
|
|
38
39
|
| **Learning Module** | Pattern recognition from failures, confidence scoring |
|
|
39
40
|
| **JSONC Config** | Read/write project and global config files with comments |
|
|
40
|
-
| **LSP
|
|
41
|
+
| **OpenCode LSP opt-in** | On startup, inserts `"lsp": true` into your global `opencode.jsonc` if it isn't already there. That is the whole of it — Nexus does not read LSP state, manage servers, or report anything about them |
|
|
41
42
|
| **AST-Grep** | Pattern-aware code search and rewriting |
|
|
42
43
|
| **Security Scanning** | Automated secrets and vulnerability detection |
|
|
43
|
-
| **Slash Commands** | `/nexus`, `/nexus web`, `/nexus
|
|
44
|
+
| **Slash Commands** | `/nexus`, `/nexus-dashboard`, `/nexus-web`, `/nexus-overview`, `/nexus-config`, `/nexus-model`, `/nexus-status`, `/nexus-reset` |
|
|
44
45
|
|
|
45
46
|
---
|
|
46
47
|
|
|
@@ -105,11 +106,11 @@ Use nexus.goal.set with description="Build complete auth system"
|
|
|
105
106
|
### 3. Use Slash Commands
|
|
106
107
|
|
|
107
108
|
```
|
|
108
|
-
/nexus # Open full configuration
|
|
109
|
-
/nexus
|
|
110
|
-
/nexus
|
|
111
|
-
/nexus
|
|
112
|
-
/nexus
|
|
109
|
+
/nexus # Open full configuration dialog
|
|
110
|
+
/nexus dashboard # Start the web dashboard and open it in your browser
|
|
111
|
+
/nexus status # Show the config summary
|
|
112
|
+
/nexus model coder # Pick the model for a role
|
|
113
|
+
/nexus reset # Reset configuration to defaults
|
|
113
114
|
```
|
|
114
115
|
|
|
115
116
|
---
|
|
@@ -146,35 +147,109 @@ Failed tasks follow a 4-step escalation chain:
|
|
|
146
147
|
|
|
147
148
|
### Web Dashboard
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
A live view of the orchestrator, served by an HTTP + WebSocket server on port 4747 (`127.0.0.1`).
|
|
150
151
|
|
|
151
|
-
**
|
|
152
|
+
**Nothing is listening until you ask for it.** The server is not started at
|
|
153
|
+
startup, and no command starts it implicitly. The start has to happen in the
|
|
154
|
+
OpenCode *server* process, next to the orchestrator that feeds it, and there are
|
|
155
|
+
two ways to reach it:
|
|
152
156
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
```
|
|
158
|
+
/nexus dashboard [port] [host] # from the TUI — starts it and opens it
|
|
159
|
+
Ask the agent: "start the nexus dashboard"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The TUI command submits `/nexus dashboard [port] [host]` to that server process,
|
|
163
|
+
whose prompt hook routes it to the orchestrator; the agent's route calls
|
|
164
|
+
`nexus.dashboard.start(port=4747, host="127.0.0.1")` directly. Both end at the
|
|
165
|
+
same start, and both print the URL it bound. **If the start fails, nothing is
|
|
166
|
+
listening and no browser is opened** — the reason is reported, and no URL is
|
|
167
|
+
offered for a server that is not there. The ways it fails are a port already in
|
|
168
|
+
use (the bind is refused; pass a different `port`) and `dashboard.enabled: false`
|
|
169
|
+
in `nexus.jsonc`, which is refused by name.
|
|
157
170
|
|
|
158
|
-
|
|
159
|
-
```
|
|
160
|
-
/nexus web
|
|
161
|
-
```
|
|
162
|
-
This shows instructions and tries to open your browser.
|
|
171
|
+
Once it is serving, the same command opens it:
|
|
163
172
|
|
|
164
|
-
|
|
173
|
+
```
|
|
174
|
+
/nexus dashboard [port] [host] # /nexus web is an alias of this
|
|
175
|
+
/nexus web [port] [host]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The TUI cannot start the server itself — its process has no orchestrator, no
|
|
179
|
+
module registry and no way to invoke a tool — but it can reach the process that
|
|
180
|
+
has all three, and it can ask whether anything is listening. So the command asks
|
|
181
|
+
`http://host:port/api/health` first, and then does one of four things:
|
|
182
|
+
|
|
183
|
+
| What it found | What it does |
|
|
184
|
+
|---|---|
|
|
185
|
+
| A nexus dashboard | Opens your browser at that URL. Nothing is started a second time |
|
|
186
|
+
| A different process on that port | Says so, opens nothing, suggests another port |
|
|
187
|
+
| Nothing there | Submits `/nexus dashboard [port] [host]` to the server, waits for the port to answer, then opens the browser |
|
|
188
|
+
| Still nothing after ~3s | Says the start did not confirm, opens nothing, and points at the command's own reply for the reason |
|
|
189
|
+
|
|
190
|
+
The browser opens **only** after a confirmed listen. That is the whole point of
|
|
191
|
+
the wait: a browser pointed at a dead address gives a connection-refused page,
|
|
192
|
+
which looks like the dashboard failing rather than the dashboard not running.
|
|
193
|
+
|
|
194
|
+
**How it stays current.** A WebSocket to `/ws/events` carries a throttled
|
|
195
|
+
`orchestrator:state` push — every state change schedules a full snapshot, at
|
|
196
|
+
most once a second — plus the thirteen orchestrator events as they happen. On
|
|
197
|
+
top of that, an **Auto-refresh** checkbox (on by default) has the page ask the
|
|
198
|
+
server for a fresh state every 5 seconds and poll `/api/health` and `/api/costs`,
|
|
199
|
+
the two things the socket does not carry. The push is what keeps the page fresh;
|
|
200
|
+
the interval is a belt-and-braces refresh you can switch off. The page also
|
|
201
|
+
shows how old the last snapshot is, and labels it stale past 15 seconds.
|
|
165
202
|
|
|
166
203
|
**What it shows:**
|
|
167
|
-
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
204
|
+
- **Sessions** — one row per session nexus owns, is still collecting cost from,
|
|
205
|
+
or has abandoned, with each one's state (`running` / `idle` / `abandoned` /
|
|
206
|
+
`settled`), age, last read token count, and unbilled spend. Rows with **no
|
|
207
|
+
owning agent** are called out in a banner above the table, because a session
|
|
208
|
+
that is still generating after its agent was terminated keeps spending and
|
|
209
|
+
nothing is collecting that spend — a case that was invisible on every layer
|
|
210
|
+
before. The **Age** column is elapsed time, not a wall clock, and reads `—`
|
|
211
|
+
for those orphan rows: `spawnedAt` comes from the owning agent, so an unowned
|
|
212
|
+
session has no start time to show. The cell says so in its tooltip, and the
|
|
213
|
+
`—` is shown rather than the column dropped, so the gap is visible.
|
|
214
|
+
This list is nexus's own bookkeeping, not an enumeration of every open session
|
|
215
|
+
on the server, and the page says so on the section itself.
|
|
216
|
+
- **Agents** — role, status, model, session id, and metrics
|
|
217
|
+
- **Budget** — spend against the configured cap, with the alert threshold marked
|
|
218
|
+
- **Tasks and DAG** — the task list, and a graph drawn from the dependency edges
|
|
219
|
+
the state actually reports. Edges pointing at tasks that are not in the
|
|
220
|
+
snapshot, self-edges, and cycles are counted and reported in the section note
|
|
221
|
+
rather than silently not drawn.
|
|
222
|
+
- **Cost breakdown** — by agent and by model, read from `/api/costs`, which
|
|
223
|
+
covers the full history rather than only the live agents
|
|
224
|
+
- **Configuration (read-only)** — the resolved config the orchestrator reports
|
|
225
|
+
as in force, plus a `read-only` JSON viewer. The write path was deliberately
|
|
226
|
+
removed rather than left broken: it used to post a `config:update` message
|
|
227
|
+
that the server does not handle, so the Apply button reported success and
|
|
228
|
+
nothing happened. There is no auth story for writes and the socket is a
|
|
229
|
+
localhost server answering with `CORS: *`, so no write path was added to
|
|
230
|
+
replace it — edit `nexus.jsonc` instead.
|
|
231
|
+
- **Activity log** — every event the broadcaster forwards, each delivered once.
|
|
232
|
+
Two of them carry a fact the line used to leave out:
|
|
233
|
+
- `cost:delta` names **where the price came from** (`settledTier.pricing`) and
|
|
234
|
+
which token tier the amount was priced at. That is deliberately not the same
|
|
235
|
+
as the measured/estimated split elsewhere on the page: `settledTier.pricing`
|
|
236
|
+
is about the *price* — the model's published list, a fallback table because
|
|
237
|
+
this model is not in it, or an unknown-model fallback — while
|
|
238
|
+
measured/estimated is about the *token counts*, which the orchestrator reads
|
|
239
|
+
off a real session. A line whose `settledTier` is missing says so instead of
|
|
240
|
+
implying a price source.
|
|
241
|
+
- `config:reloaded` names the **cause** (`trigger`) alongside the load number,
|
|
242
|
+
the raw ISO load time, and both config files' state, so a reload that
|
|
243
|
+
happened for a reason you did not ask for is visible as one.
|
|
173
244
|
|
|
174
245
|
**Stop the dashboard:**
|
|
175
246
|
```
|
|
176
|
-
nexus
|
|
247
|
+
/nexus dashboard stop
|
|
248
|
+
Ask the agent to call nexus.dashboard.stop
|
|
177
249
|
```
|
|
250
|
+
This stops the HTTP/WebSocket server only. The orchestrator, its agents and its
|
|
251
|
+
sessions keep running. Both routes say so plainly when there was nothing
|
|
252
|
+
running, rather than reporting a stop that did not happen.
|
|
178
253
|
|
|
179
254
|
### Team Mode
|
|
180
255
|
|
|
@@ -297,8 +372,8 @@ nexus.clarify(question="Should I use JWT or OAuth?", options="JWT, OAuth", assum
|
|
|
297
372
|
| `nexus.preset` | Apply preset config | `{ name }` |
|
|
298
373
|
| `nexus.config.save` | Save config to disk | `{ level: 'project' \| 'global' }` |
|
|
299
374
|
| `nexus.config.init` | Initialize config files | `{ level }` |
|
|
300
|
-
| `nexus.dashboard.start` | Start web dashboard | `{ port?, host? }` |
|
|
301
|
-
| `nexus.dashboard.stop` | Stop web dashboard | `{}` |
|
|
375
|
+
| `nexus.dashboard.start` | Start the web dashboard server (port must be free) | `{ port?, host? }` |
|
|
376
|
+
| `nexus.dashboard.stop` | Stop the web dashboard server | `{}` |
|
|
302
377
|
| `nexus.todo.add` | Add a todo item | `{ description, assignedTo? }` |
|
|
303
378
|
| `nexus.todo.list` | List all todos | `{}` |
|
|
304
379
|
| `nexus.todo.complete` | Complete a todo | `{ id }` |
|
|
@@ -327,17 +402,31 @@ nexus.clarify(question="Should I use JWT or OAuth?", options="JWT, OAuth", assum
|
|
|
327
402
|
|
|
328
403
|
## TUI Commands
|
|
329
404
|
|
|
405
|
+
The TUI plugin registers exactly these slash commands. With no argument,
|
|
406
|
+
`/nexus` opens the full configuration dialog; with one, it dispatches to a
|
|
407
|
+
subcommand (`config`/`c`, `status`/`s`, `dashboard`/`d`, `web`/`w`,
|
|
408
|
+
`overview`, `model`/`m`, `reset`).
|
|
409
|
+
|
|
330
410
|
| Command | Alias | Description |
|
|
331
411
|
|---------|-------|-------------|
|
|
332
|
-
| `/nexus` | `Ctrl+N` |
|
|
333
|
-
| `/nexus
|
|
334
|
-
| `/nexus
|
|
335
|
-
| `/nexus
|
|
336
|
-
| `/nexus
|
|
337
|
-
| `/nexus
|
|
338
|
-
| `/nexus
|
|
339
|
-
| `/nexus
|
|
340
|
-
|
|
412
|
+
| `/nexus` | `Ctrl+N` | Full configuration dialog, or a subcommand |
|
|
413
|
+
| `/nexus-dashboard` | `/nd` | Start the web dashboard and open it. If one is already serving, opens that and starts nothing — see [Web Dashboard](#web-dashboard) |
|
|
414
|
+
| `/nexus-web` | `/nw` | Alias of `/nexus-dashboard` |
|
|
415
|
+
| `/nexus-overview` | `/no` | Config, budget and dashboard-status overview. Prints text; starts nothing |
|
|
416
|
+
| `/nexus-config` | `/nc` | Configure models & budget |
|
|
417
|
+
| `/nexus-model` | `/nm` | Select a model for a role |
|
|
418
|
+
| `/nexus-status` | `/ns` | Show the config summary |
|
|
419
|
+
| `/nexus-reset` | — | Reset all settings to defaults |
|
|
420
|
+
|
|
421
|
+
A prompt beginning `/nexus …` typed into the composer is a *different* thing: it
|
|
422
|
+
is intercepted by a prompt hook and routed to `orchestrator.handleCommand()`,
|
|
423
|
+
which understands `status`, `agents`, `costs`, `pause`, `resume`, and
|
|
424
|
+
`dashboard [port] [host]` (which starts the server, or says why it did not),
|
|
425
|
+
`dashboard stop`, and `dashboard state` (the state as JSON). Anything else
|
|
426
|
+
answers `Unknown command`. This hook cannot cancel the prompt — the plugin API
|
|
427
|
+
gives it no way to — so it replaces the command text with the command's result
|
|
428
|
+
rather than leaving the model holding a bare `/nexus dashboard` next to an
|
|
429
|
+
answer it has no reason to read. The table above is the TUI palette.
|
|
341
430
|
|
|
342
431
|
---
|
|
343
432
|
|
|
@@ -356,6 +445,24 @@ Configure via `/nexus` or `Ctrl+N`:
|
|
|
356
445
|
📝 Documenter: opencode/big-pickle
|
|
357
446
|
```
|
|
358
447
|
|
|
448
|
+
### Web Dashboard
|
|
449
|
+
|
|
450
|
+
`.opencode/nexus.jsonc` (project) and `~/.config/opencode/nexus.jsonc` (global)
|
|
451
|
+
both accept:
|
|
452
|
+
|
|
453
|
+
```jsonc
|
|
454
|
+
{
|
|
455
|
+
"dashboard": {
|
|
456
|
+
"enabled": true, // false makes every start attempt refuse, and say so
|
|
457
|
+
"port": 4747, // default port; startDashboard({port}) still wins
|
|
458
|
+
"host": "127.0.0.1"
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
The server has no authentication, which is why `host` defaults to loopback.
|
|
464
|
+
Leave it there unless you have put your own authentication in front of it.
|
|
465
|
+
|
|
359
466
|
### Custom Roles
|
|
360
467
|
|
|
361
468
|
Define your own agent roles:
|
|
@@ -395,7 +502,7 @@ nexus.template(name="documentation") — Documentation update
|
|
|
395
502
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
396
503
|
│ │ SERVER PLUGIN (index.ts) │ │
|
|
397
504
|
│ │ • 40+ tool registrations │ │
|
|
398
|
-
│ │ • Auto-creates
|
|
505
|
+
│ │ • Auto-creates agent files; opts OpenCode into LSP │ │
|
|
399
506
|
│ │ • Config file loading and creation │ │
|
|
400
507
|
│ └────────────────────────────────────────────────────┘ │
|
|
401
508
|
│ │
|
|
@@ -422,7 +529,8 @@ nexus.template(name="documentation") — Documentation update
|
|
|
422
529
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
423
530
|
│ │ WEB DASHBOARD │ │
|
|
424
531
|
│ │ • Bun.serve() HTTP + WebSocket (port 4747) │ │
|
|
425
|
-
│ │ • DAG viz, cost chart, config
|
|
532
|
+
│ │ • DAG viz, cost chart, read-only config, sessions │ │
|
|
533
|
+
│ │ • Started on request; throttled push + poll │ │
|
|
426
534
|
│ └────────────────────────────────────────────────────┘ │
|
|
427
535
|
│ │
|
|
428
536
|
│ ┌────────────────────────────────────────────────────┐ │
|