@cordfuse/llmux 0.11.0 → 0.12.1
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 +155 -75
- package/dist/index.js +3268 -78
- package/package.json +17 -4
- package/src/cli.ts +100 -0
- package/src/{client.ts → client/client.ts} +3 -3
- package/src/daemon/agents.ts +193 -0
- package/src/daemon/auth-store.ts +85 -0
- package/src/daemon/config.ts +77 -0
- package/src/daemon/handlers.ts +414 -0
- package/src/daemon/net.ts +113 -0
- package/src/daemon/state.ts +78 -0
- package/src/daemon/tmux.ts +117 -0
- package/src/daemon/token.ts +13 -0
- package/src/daemon/web/server.ts +2277 -0
- package/src/index.ts +386 -37
package/README.md
CHANGED
|
@@ -1,119 +1,199 @@
|
|
|
1
1
|
# llmux
|
|
2
2
|
|
|
3
|
-
You have Claude Code in one terminal, Codex in another,
|
|
4
|
-
|
|
3
|
+
You have Claude Code in one terminal, Codex in another, Aider in a third, and
|
|
4
|
+
OpenCode in a fourth. They're all live, all mid-conversation, all costing
|
|
5
5
|
nothing to keep open. But to fire a prompt at one of them you have to find the
|
|
6
|
-
right window, click in, and type. To
|
|
7
|
-
just don't. And if you're on the couch with your phone? Forget it.
|
|
6
|
+
right window, click in, and type. To do anything from your phone? Forget it.
|
|
8
7
|
|
|
9
8
|
llmux turns every agent CLI into a named tmux session you can drive from
|
|
10
|
-
anywhere. Spawn `claude`, `agy`, `
|
|
11
|
-
`
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
anywhere. Spawn `claude`, `codex`, `agy`, `gemini`, `qwen`, `opencode`, `amp`,
|
|
10
|
+
`grok`, `aider`, `continue`, `kiro`, `cursor`, `plandex`, `goose`, or
|
|
11
|
+
`gh copilot` once. Then fire prompts at any of them — by name, from a CLI, from
|
|
12
|
+
a REST call, or from a browser on your phone over Tailscale. Past
|
|
13
|
+
conversations are browsable and resumable. The agents keep running.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
>
|
|
18
|
-
> See [CHANGELOG.md](./CHANGELOG.md).
|
|
15
|
+
> **Status:** v0.12.0 — daemon + CLI client consolidated into one binary
|
|
16
|
+
> (`llmux`). Auth, tokens, mobile picker, conversation resume, Claude Code
|
|
17
|
+
> history adapter shipped. See [CHANGELOG.md](./CHANGELOG.md).
|
|
19
18
|
|
|
20
19
|
## Install
|
|
21
20
|
|
|
22
21
|
```bash
|
|
23
|
-
#
|
|
24
|
-
npm install -g @cordfuse/llmuxd
|
|
25
|
-
|
|
26
|
-
# Anywhere you want to send prompts from (laptop, phone, CI)
|
|
22
|
+
# One package, one binary — installs on the daemon host AND any client machine
|
|
27
23
|
npm install -g @cordfuse/llmux
|
|
28
24
|
```
|
|
29
25
|
|
|
26
|
+
If you used the now-deprecated `@cordfuse/llmuxd` package: uninstall it and
|
|
27
|
+
install `@cordfuse/llmux` instead. The `llmuxd` binary is gone; the `llmux`
|
|
28
|
+
binary covers both daemon and client roles.
|
|
29
|
+
|
|
30
30
|
## 30-second quickstart
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
#
|
|
34
|
-
|
|
33
|
+
# 1. Start the daemon (binds REST + WebSocket + browser picker)
|
|
34
|
+
llmux server start --port 3030
|
|
35
|
+
|
|
36
|
+
# 2. Spawn an agent into a named tmux session
|
|
37
|
+
llmux session start claude --name main --cwd ~/projects/myapp
|
|
35
38
|
|
|
36
|
-
# Fire a prompt
|
|
37
|
-
|
|
39
|
+
# 3. Fire a prompt — fire-and-forget
|
|
40
|
+
llmux session prompt main "what does src/index.ts do?"
|
|
38
41
|
|
|
39
|
-
# Or attach interactively (
|
|
40
|
-
|
|
42
|
+
# 4. Or attach interactively (raw TTY pass-through)
|
|
43
|
+
llmux session attach main
|
|
41
44
|
|
|
42
|
-
# Or
|
|
43
|
-
|
|
45
|
+
# 5. Or open the browser picker (URL is in the server start banner)
|
|
46
|
+
# Pick a session, get a full-screen xterm.js terminal wired over WebSocket.
|
|
44
47
|
```
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
On mobile the picker is a real PWA-style surface — spawn / restart / kill /
|
|
50
|
+
edit / resume past conversations, with a confirmation modal on destructive
|
|
51
|
+
actions. The chat page is a phone-friendly xterm with a custom soft-keyboard
|
|
52
|
+
toolbar that surfaces Esc / Tab / Ctrl / Alt / arrows / shell chars that
|
|
53
|
+
gboard hides.
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
> Until then, bind to `127.0.0.1` (default) or expose only over Tailscale.
|
|
55
|
+
## Remote operation
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
The same binary is the client. Set `--server` (or `LLMUX_SERVER` env) on any
|
|
58
|
+
session/agent verb and it routes over HTTP instead of operating locally:
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
```bash
|
|
61
|
+
export LLMUX_SERVER=http://100.105.221.46:3030
|
|
62
|
+
export LLMUX_TOKEN=sas_… # mint with `llmux token create`
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
llmux session list
|
|
65
|
+
llmux session prompt main "tomorrow's plan?"
|
|
66
|
+
llmux session attach main # raw TTY pass-through over WS
|
|
67
|
+
llmux session resume main --latest # rebind to the most recent claude convo
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Localhost requests bypass auth; remote requests require a Bearer token.
|
|
71
|
+
`--token <sas>` per-command works too.
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
input via `tmux send-keys` and reads output by attaching xterm.js over a
|
|
66
|
-
WebSocket bridge. That keeps the agent CLIs unmodified — Claude Code is still
|
|
67
|
-
running Claude Code; llmuxd just coordinates input and exposes the surface.
|
|
73
|
+
## Noun-prefix surface
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
```
|
|
76
|
+
session list / start / stop / restart / attach / prompt / broadcast
|
|
77
|
+
/ resume / history
|
|
78
|
+
server start
|
|
79
|
+
token create / list / revoke
|
|
80
|
+
agent list [--all] [--installed] [--json]
|
|
81
|
+
```
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
Global flags: `--server <url>`, `--token <sas>`, `--help`, `--version`.
|
|
84
|
+
|
|
85
|
+
Backward-compat shims (kept one release): `llmux serve`, `llmux ls`,
|
|
86
|
+
`llmux status`, and the legacy flat verbs (`llmux send`, `llmux spawn`,
|
|
87
|
+
`llmux kill`, etc.) still work; they fall through to the noun-prefix
|
|
88
|
+
dispatcher.
|
|
89
|
+
|
|
90
|
+
## How it works
|
|
91
|
+
|
|
92
|
+
Each spawned agent is a real tmux session, not a wrapped PTY. The daemon
|
|
93
|
+
dispatches input via `tmux send-keys` and exposes the surface over a REST API
|
|
94
|
+
plus a WebSocket bridge to xterm.js (via node-pty attached to
|
|
95
|
+
`tmux attach -t <name>`). That keeps the agent CLIs unmodified — Claude Code
|
|
96
|
+
is still running Claude Code; llmux just coordinates input and exposes the
|
|
97
|
+
surface.
|
|
98
|
+
|
|
99
|
+
State lives at `~/.local/state/llmuxd/sessions.json` (or
|
|
100
|
+
`$XDG_STATE_HOME/llmuxd/sessions.json`) with `0600` perms and a versioned
|
|
101
|
+
schema. Auth tokens live in the sibling `auth.json`. The state directory keeps
|
|
102
|
+
its `llmuxd/` name across the v0.12.0 package consolidation so existing
|
|
103
|
+
operators don't need to migrate anything.
|
|
104
|
+
|
|
105
|
+
The daemon runs on Node (not Bun) — `node-pty`'s native prebuilds target
|
|
106
|
+
Node, and attaching to tmux through node-pty under Bun caused immediate SIGHUP.
|
|
76
107
|
|
|
77
108
|
## Supported agents
|
|
78
109
|
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
| `claude` | [Claude Code](https://
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`
|
|
110
|
+
| Key | CLI | Danger-mode default |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `claude` | [Claude Code](https://docs.claude.com/en/docs/claude-code/overview) | `--dangerously-skip-permissions` |
|
|
113
|
+
| `codex` | [OpenAI Codex CLI](https://github.com/openai/codex) | `--dangerously-bypass-approvals-and-sandbox` |
|
|
114
|
+
| `agy` | [Antigravity CLI](https://antigravity.google) | `--dangerously-skip-permissions` |
|
|
115
|
+
| `gemini` | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | `--yolo` |
|
|
116
|
+
| `qwen` | [Qwen Code](https://github.com/QwenLM/qwen-code) | `--yolo` |
|
|
117
|
+
| `opencode` | [OpenCode](https://opencode.ai) | env: `OPENCODE_YOLO=1` (TUI lacks a flag) |
|
|
118
|
+
| `amp` | [Sourcegraph Amp](https://ampcode.com) | `--dangerously-allow-all` |
|
|
119
|
+
| `grok` | [Grok Build CLI](https://x.ai/cli) | `--always-approve` |
|
|
120
|
+
| `aider` | [Aider](https://aider.chat) | `--yes-always` |
|
|
121
|
+
| `continue` | [Continue CLI](https://docs.continue.dev/guides/cli) (`cn`) | `--auto` |
|
|
122
|
+
| `kiro` | [Kiro CLI](https://kiro.dev/cli/) | `--trust-all-tools` |
|
|
123
|
+
| `cursor` | [Cursor CLI](https://cursor.com/docs/cli/installation) (`cursor-agent`) | (config-based) |
|
|
124
|
+
| `plandex` | [Plandex](https://plandex.ai) | (interactive `set-auto`) |
|
|
125
|
+
| `goose` | [Goose](https://block.github.io/goose) | env: `GOOSE_MODE=auto` |
|
|
126
|
+
| `copilot` | [GitHub Copilot CLI](https://docs.github.com/en/copilot/how-tos/use-copilot-in-the-cli) (`gh copilot`) | n/a |
|
|
127
|
+
|
|
128
|
+
Only installed agents appear in `llmux agent list` and the picker dropdown.
|
|
129
|
+
Detection uses a pure-Node PATH walk for most; `copilot` checks the gh-managed
|
|
130
|
+
binary directory.
|
|
131
|
+
|
|
132
|
+
Per-session overrides via `llmux session start <agent>`:
|
|
133
|
+
- `--name <X>` — tmux session name (defaults to the agent key)
|
|
134
|
+
- `--cwd <path>` — working directory (accepts `~/…` shorthand)
|
|
135
|
+
- `--flags "<f>"` — replace the agent's default flags entirely
|
|
136
|
+
- `--env "KEY=VAL"` — extra env vars (newline-separated for multiple)
|
|
137
|
+
|
|
138
|
+
Editing any of these on a running session via the web picker auto-respawns
|
|
139
|
+
the tmux session so changes take effect immediately.
|
|
140
|
+
|
|
141
|
+
## Conversation resume
|
|
142
|
+
|
|
143
|
+
For agents with a history adapter (Claude Code today; codex/gemini/etc.
|
|
144
|
+
coming), the row gets a `☰ N` button. Tap it to see past conversations in the
|
|
145
|
+
session's cwd; pick one to relaunch the agent with its `--resume <id>` flag.
|
|
146
|
+
State preserves the binding across restarts so respawn keeps you on the
|
|
147
|
+
same conversation. Use `llmux session resume <name> --latest` from the CLI
|
|
148
|
+
for the same flow.
|
|
149
|
+
|
|
150
|
+
## Auth
|
|
151
|
+
|
|
152
|
+
`llmux server start` runs without auth until you create a token:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
llmux token create --name phone
|
|
156
|
+
# prints sas_…<43-char-base64url> once; copy it.
|
|
157
|
+
# pass --qr-endpoint tailscale-https for a QR-code deep-link that logs you
|
|
158
|
+
# in on first scan from a phone.
|
|
159
|
+
|
|
160
|
+
llmux token list
|
|
161
|
+
llmux token revoke <8-char-id>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
After the first token exists, all non-localhost HTTP/WS requests require
|
|
165
|
+
either `Authorization: Bearer <sas>` (CLI / curl) or the `llmuxd_token`
|
|
166
|
+
cookie set by the browser gate. Localhost stays open so local CLI use needs
|
|
167
|
+
no token.
|
|
168
|
+
|
|
169
|
+
If `tailscale serve --https=443 http://localhost:<port>` is configured on the
|
|
170
|
+
host, the server-start banner surfaces the HTTPS hostname URL above the
|
|
171
|
+
http endpoints. The browser picker is a clean TLS surface; CLI `attach`
|
|
172
|
+
currently speaks ws:// only.
|
|
93
173
|
|
|
94
174
|
## Config (`.llmux.yaml`)
|
|
95
175
|
|
|
96
|
-
|
|
176
|
+
A YAML config (project-local or global) can override per-agent defaults.
|
|
177
|
+
Discovery order:
|
|
97
178
|
|
|
98
179
|
1. `--config <path>` flag
|
|
99
|
-
2. `./.llmux.yaml` (project-
|
|
180
|
+
2. `./.llmux.yaml` (project-local, auto-discovered in cwd)
|
|
100
181
|
3. `~/.config/llmux/config.yaml` (global default)
|
|
101
182
|
4. `LLMUX_CONFIG=<path>` env var
|
|
102
183
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
184
|
+
llmux runs without any YAML file — all defaults are baked into
|
|
185
|
+
`agents.ts`. The `init` command to generate a starter YAML is not yet
|
|
186
|
+
shipped; create one by hand if you want to override defaults today.
|
|
106
187
|
|
|
107
|
-
##
|
|
188
|
+
## Environment
|
|
108
189
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
- [ ] **Phase 7** — polish + npm publish
|
|
190
|
+
| Variable | Purpose |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `LLMUX_SERVER` | Default `--server` URL for session/agent verbs |
|
|
193
|
+
| `LLMUX_TOKEN` | Default `--token` SAS auth |
|
|
194
|
+
| `LLMUX_PORT` | Default port resolution for QR-endpoint helpers |
|
|
195
|
+
| `XDG_STATE_HOME` | Override for the state directory parent |
|
|
196
|
+
| `OPENCODE_YOLO`, `GOOSE_MODE`, … | Forwarded by `envDefaults` per-agent |
|
|
117
197
|
|
|
118
198
|
## License
|
|
119
199
|
|