mulmoterminal 0.1.4 → 0.3.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 +135 -5
- package/dist/assets/index-BKmWMFww.js +296 -0
- package/dist/assets/index-NQD6EPeb.css +1 -0
- package/dist/assets/{marp-BhLpsKq8.js → marp-Bux3kHcu.js} +1 -1
- package/dist/assets/material-symbols-outlined-gxDE5xnH.woff2 +0 -0
- package/dist/index.html +2 -2
- package/package.json +14 -12
- package/plugins/plugins.json +8 -1
- package/server/app-config.spec.ts +64 -0
- package/server/app-config.ts +45 -0
- package/server/backends/collections.spec.ts +221 -0
- package/server/backends/collections.ts +410 -0
- package/server/backends/files.spec.ts +67 -0
- package/server/backends/files.ts +126 -0
- package/server/backends/html.spec.ts +85 -0
- package/server/backends/html.ts +119 -0
- package/server/backends/shortcuts.spec.ts +124 -0
- package/server/backends/shortcuts.ts +139 -0
- package/server/backends/viewToken.spec.ts +111 -0
- package/server/backends/viewToken.ts +101 -0
- package/server/claude-args.spec.ts +59 -0
- package/server/claude-args.ts +37 -0
- package/server/config-routes.ts +48 -0
- package/server/cwd-presets.spec.ts +54 -0
- package/server/cwd-presets.ts +44 -0
- package/server/gitRemote.spec.ts +157 -0
- package/server/gitRemote.ts +79 -0
- package/server/host-tools.ts +4 -2
- package/server/index.ts +423 -87
- package/server/open-dir.spec.ts +14 -0
- package/server/open-dir.ts +46 -0
- package/server/pick-file.spec.ts +32 -0
- package/server/pick-file.ts +73 -0
- package/server/plugins-registry.ts +10 -1
- package/server/scripts.spec.ts +95 -0
- package/server/scripts.ts +63 -0
- package/server/transcript.spec.ts +137 -0
- package/server/transcript.ts +164 -0
- package/dist/assets/index-BCNbHKHg.js +0 -225
- package/dist/assets/index-C8IoNzUa.css +0 -1
- package/dist/assets/material-symbols-outlined-CogVgFVS.woff2 +0 -0
package/README.md
CHANGED
|
@@ -10,6 +10,13 @@ WebSocket. A sidebar lists every Claude session for the project and reflects, in
|
|
|
10
10
|
real time, which sessions are **working** (Claude is thinking) and which **need
|
|
11
11
|
attention** (waiting for input, or finished with output you haven't seen).
|
|
12
12
|
|
|
13
|
+
**Inserting a file path** — like a native terminal, you can put a file's absolute
|
|
14
|
+
path into the prompt: **drag a file** onto the terminal (works where the browser
|
|
15
|
+
exposes the path via `file://` — Firefox/Safari), or click the **📎 file button**
|
|
16
|
+
in the terminal header, which asks the local server to open the OS file dialog and
|
|
17
|
+
inserts the chosen path (works in every browser, including Chrome). The path is
|
|
18
|
+
inserted at the cursor — it is not submitted, so you can review it first.
|
|
19
|
+
|
|
13
20
|
---
|
|
14
21
|
|
|
15
22
|
## Install & run
|
|
@@ -50,10 +57,13 @@ server, and opens the browser. For local development from a clone, see
|
|
|
50
57
|
- [Tech stack](#tech-stack)
|
|
51
58
|
- [Configuration](#configuration)
|
|
52
59
|
- [Running](#running)
|
|
60
|
+
- [Scripts (Run menu)](#scripts-run-menu)
|
|
53
61
|
- [Server API specification](#server-api-specification)
|
|
54
62
|
- [HTTP: `GET /api/sessions`](#http-get-apisessions)
|
|
63
|
+
- [HTTP: `GET /api/scripts`](#http-get-apiscripts)
|
|
55
64
|
- [HTTP: `POST /api/hook`](#http-post-apihook)
|
|
56
65
|
- [WebSocket: `/ws` (terminal)](#websocket-ws-terminal)
|
|
66
|
+
- [WebSocket: `/ws/run` (command terminal)](#websocket-wsrun-command-terminal)
|
|
57
67
|
- [Socket.IO: `/ws/pubsub` (activity pub/sub)](#socketio-wspubsub-activity-pubsub)
|
|
58
68
|
- [Session model](#session-model)
|
|
59
69
|
- [Session lifecycle](#session-lifecycle)
|
|
@@ -83,8 +93,11 @@ server, and opens the browser. For local development from a clone, see
|
|
|
83
93
|
- **Session list** is fetched over HTTP (`/api/sessions`).
|
|
84
94
|
- **Live activity** is pushed over a Socket.IO pub/sub channel (`/ws/pubsub`);
|
|
85
95
|
the server learns of activity from **Claude hooks** that POST to `/api/hook`.
|
|
86
|
-
-
|
|
87
|
-
in
|
|
96
|
+
- **Script commands** (`yarn dev`, tests, …), launched from a cell's directory
|
|
97
|
+
picker, run in their own ephemeral PTY over a separate WebSocket (`/ws/run`);
|
|
98
|
+
they are not Claude sessions. See [Scripts (Run menu)](#scripts-run-menu).
|
|
99
|
+
- The Vite dev server proxies `/ws` (covers `/ws/run`), `/ws/pubsub`, and `/api` to
|
|
100
|
+
the backend; in production the backend serves the built client from `dist/`.
|
|
88
101
|
|
|
89
102
|
---
|
|
90
103
|
|
|
@@ -136,6 +149,23 @@ Example `.env` (gitignored):
|
|
|
136
149
|
CLAUDE_CWD=/Users/you/my-project
|
|
137
150
|
```
|
|
138
151
|
|
|
152
|
+
### UI settings (`~/.mulmoterminal/config.json`)
|
|
153
|
+
|
|
154
|
+
The Settings modal (⚙) persists per-user UI choices to `~/.mulmoterminal/config.json`
|
|
155
|
+
(read/written via `GET`/`POST /api/config`):
|
|
156
|
+
|
|
157
|
+
| Field | Meaning |
|
|
158
|
+
| ------------ | ------- |
|
|
159
|
+
| `cwdPresets` | Quick-pick directories offered when launching a terminal. |
|
|
160
|
+
| `soundFile` | Absolute path to a custom **attention sound** (played when a session needs attention). Empty/unset uses the built-in synthesized chime. |
|
|
161
|
+
|
|
162
|
+
**Attention sound.** The default chime is generated with the Web Audio API — **no
|
|
163
|
+
audio file is bundled**, so the npm package stays light and has no media-licensing
|
|
164
|
+
concerns. To use your own sound, set `soundFile` in Settings (Browse / Test / Use
|
|
165
|
+
chime) or in the config file; the server streams that file at `GET /api/sound` and
|
|
166
|
+
the client decodes it (falling back to the chime if it's missing or not audio). It's
|
|
167
|
+
your own local file referenced by absolute path — nothing is added to the package.
|
|
168
|
+
|
|
139
169
|
---
|
|
140
170
|
|
|
141
171
|
## Running
|
|
@@ -164,6 +194,55 @@ In dev, open the Vite URL; its proxy forwards `/ws`, `/ws/pubsub`, and `/api` to
|
|
|
164
194
|
|
|
165
195
|
---
|
|
166
196
|
|
|
197
|
+
## Scripts (Run menu)
|
|
198
|
+
|
|
199
|
+
An empty grid cell's launcher (the directory picker) offers a **run a script** row
|
|
200
|
+
that launches project scripts (a dev server, tests, a build, …) **in that cell, in
|
|
201
|
+
the directory the cell is pointed at** — so a whole workflow lives in one window
|
|
202
|
+
alongside the Claude sessions. Scripts are **per-directory**: the cell reads the
|
|
203
|
+
`script.json` of whatever directory you select, so different cells can offer
|
|
204
|
+
different projects' scripts.
|
|
205
|
+
|
|
206
|
+
Every running terminal's header also has a **▶ Run ▾** dropdown (next to the
|
|
207
|
+
connection status), in both the single view and each grid cell — but **only when the
|
|
208
|
+
open project has scripts** (no `script.json`, no button). It lists the **open
|
|
209
|
+
project's** `script.json` — the directory that terminal runs in — and launches the
|
|
210
|
+
picked script in a **spare grid cell** (reusing an open launcher, else a new one),
|
|
211
|
+
switching to the grid from the single view so you can watch it. So you can start a
|
|
212
|
+
dev server or tests for the project you're working in without disturbing the
|
|
213
|
+
session that's running.
|
|
214
|
+
|
|
215
|
+
The list is populated from a **`script.json`** at the chosen directory's root. It's
|
|
216
|
+
optional; a directory without one simply shows no scripts.
|
|
217
|
+
|
|
218
|
+
```jsonc
|
|
219
|
+
// <dir>/script.json
|
|
220
|
+
{
|
|
221
|
+
"scripts": [
|
|
222
|
+
{ "label": "Dev server", "command": "yarn dev" },
|
|
223
|
+
{ "label": "Unit tests", "command": "yarn test" },
|
|
224
|
+
{ "label": "Build", "command": "yarn build" },
|
|
225
|
+
// optional per-script working dir (relative to this file, or absolute):
|
|
226
|
+
{ "label": "Sub server", "command": "yarn serve", "cwd": "packages/server" }
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
| Field | Required | Meaning |
|
|
232
|
+
| --------- | -------- | ------- |
|
|
233
|
+
| `label` | yes | What the launcher shows. |
|
|
234
|
+
| `command` | yes | Shell command, run via the login shell (`$SHELL -lc "<command>"`). |
|
|
235
|
+
| `cwd` | no | Working dir, relative to `script.json` or absolute. Defaults to the cell's directory. |
|
|
236
|
+
|
|
237
|
+
A command terminal is **not** a Claude session: it has no session id, no hooks, no
|
|
238
|
+
transcript, and **isn't persisted** — it's ephemeral, so a page reload drops it and
|
|
239
|
+
closing the cell (or reloading) kills the process. When the command exits, the cell
|
|
240
|
+
offers a **↻ re-run**. The browser only ever sends the script's **index** + its
|
|
241
|
+
directory; the server reads that directory's `script.json` and resolves the
|
|
242
|
+
command, so the file is the allowlist of what can run.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
167
246
|
## Server API specification
|
|
168
247
|
|
|
169
248
|
Base URL: `http://localhost:$PORT` (default `http://localhost:3456`).
|
|
@@ -200,6 +279,28 @@ newest first, including freshly-created sessions that aren't yet written to disk
|
|
|
200
279
|
- `500 { "error": string }` on an unexpected filesystem error. A missing project
|
|
201
280
|
directory is **not** an error — it yields an empty `sessions` array.
|
|
202
281
|
|
|
282
|
+
### HTTP: `GET /api/scripts`
|
|
283
|
+
|
|
284
|
+
The runnable entries from `<cwd>/script.json` for a cell's chosen directory
|
|
285
|
+
(`?cwd=<dir>`, falling back to `CLAUDE_CWD`); see
|
|
286
|
+
[Scripts (Run menu)](#scripts-run-menu). The resolved `cwd` is echoed back (the
|
|
287
|
+
server may fall back from a bad path), and each entry carries its `index` (the
|
|
288
|
+
position the client sends back to `/ws/run`).
|
|
289
|
+
|
|
290
|
+
```jsonc
|
|
291
|
+
// GET /api/scripts?cwd=/Users/me/proj
|
|
292
|
+
{
|
|
293
|
+
"cwd": "/Users/me/proj",
|
|
294
|
+
"scripts": [
|
|
295
|
+
{ "index": 0, "label": "Dev server", "command": "yarn dev" },
|
|
296
|
+
{ "index": 1, "label": "Sub server", "command": "yarn serve", "cwd": "packages/server" }
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
A missing or invalid `script.json` is **not** an error — it yields an empty
|
|
302
|
+
`scripts` array.
|
|
303
|
+
|
|
203
304
|
### HTTP: `POST /api/hook`
|
|
204
305
|
|
|
205
306
|
**Internal endpoint.** Claude hooks (injected per session — see
|
|
@@ -262,6 +363,27 @@ A non-JSON frame is written to the PTY verbatim (fallback).
|
|
|
262
363
|
**kept alive** in the background; otherwise it's killed. See
|
|
263
364
|
[Session lifecycle](#session-lifecycle).
|
|
264
365
|
|
|
366
|
+
### WebSocket: `/ws/run` (command terminal)
|
|
367
|
+
|
|
368
|
+
A raw WebSocket carrying a one-off **Run-menu command** (see
|
|
369
|
+
[Scripts (Run menu)](#scripts-run-menu)) — a plain shell PTY, **not** a Claude
|
|
370
|
+
session, so there's no `session` message, no hooks, and no reattach.
|
|
371
|
+
|
|
372
|
+
**Connect**
|
|
373
|
+
|
|
374
|
+
- `ws://host/ws/run?index=<n>&cwd=<dir>` — run the script at position `<n>` in
|
|
375
|
+
`<dir>/script.json` (cwd falls back to `CLAUDE_CWD`). The server reads that
|
|
376
|
+
file and spawns `$SHELL -lc "<command>"` in the script's `cwd`. An out-of-range
|
|
377
|
+
index (or a missing/invalid `script.json`) yields
|
|
378
|
+
`{ "type": "error", "message": string }` and the socket closes.
|
|
379
|
+
|
|
380
|
+
The **output / input / resize / exit** frames are identical to `/ws`. There is no
|
|
381
|
+
`session` frame.
|
|
382
|
+
|
|
383
|
+
**Disconnect** — the terminal is **ephemeral**: when the socket closes (cell
|
|
384
|
+
closed, or page reloaded) the process is **killed**. There is no background
|
|
385
|
+
survival and no resume.
|
|
386
|
+
|
|
265
387
|
### Socket.IO: `/ws/pubsub` (activity pub/sub)
|
|
266
388
|
|
|
267
389
|
A minimal Socket.IO pub/sub for live session-activity updates. Channel names are
|
|
@@ -401,7 +523,9 @@ appears, at which point the on-disk title takes over.
|
|
|
401
523
|
```
|
|
402
524
|
server/
|
|
403
525
|
index.js Express app, /api routes, terminal WebSocket, PTY lifecycle,
|
|
404
|
-
session state, hook injection, session discovery
|
|
526
|
+
session state, hook injection, session discovery; also
|
|
527
|
+
/api/scripts + the /ws/run command-terminal relay
|
|
528
|
+
scripts.ts Loads/validates script.json; resolves a Run-menu command by index
|
|
405
529
|
pubsub.js createPubSub(server) — socket.io pub/sub at /ws/pubsub
|
|
406
530
|
fix-pty-perms.js postinstall: fixes node-pty prebuilt binary permissions
|
|
407
531
|
src/
|
|
@@ -409,10 +533,16 @@ src/
|
|
|
409
533
|
components/
|
|
410
534
|
Sidebar.vue Session list; working dot + waiting bold; pub/sub driven
|
|
411
535
|
Sidebar.spec.ts Vitest component tests
|
|
412
|
-
Terminal.vue xterm.js terminal; /ws
|
|
536
|
+
Terminal.vue xterm.js terminal; /ws (or /ws/run); single-view ▶ Run menu
|
|
537
|
+
GridView.vue Grid toolbar (auto-layout, + Terminal); runs handed-off scripts
|
|
538
|
+
RunMenu.vue ▶ Run dropdown: lists a dir's script.json, emits the pick
|
|
539
|
+
TerminalCell.vue A cell: Claude launcher (dir picker + resume + run-a-script)
|
|
540
|
+
TerminalGrid.vue Grid of cells; auto-sizes by count; zoom lines up every tab
|
|
541
|
+
CommandCell.vue A grid cell that runs a script.json command (ephemeral)
|
|
413
542
|
composables/
|
|
414
543
|
usePubSub.ts socket.io-client pub/sub composable (subscribe/unsubscribe)
|
|
415
|
-
|
|
544
|
+
usePendingScript.ts Hands a header-picked script to the grid to run
|
|
545
|
+
vite.config.ts Dev proxy for /ws (covers /ws/run), /ws/pubsub, /api
|
|
416
546
|
vitest.config.ts jsdom test environment
|
|
417
547
|
```
|
|
418
548
|
|