glimmervoid 0.36.0 → 0.37.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 +86 -84
- package/dist/bin/glimmervoid.js +54 -115
- package/dist/chunks/{adapters-BdnLyJjf.js → adapters-BmfVPxlH.js} +22 -15
- package/dist/chunks/adapters-CqFFmM2R.js +1 -0
- package/dist/chunks/{agent-api-cli-DIqRLvu3.js → agent-api-cli-BTHEyJKS.js} +1 -1
- package/dist/chunks/{agent-api-cli-d-Bgsshx.js → agent-api-cli-CBgOKXoO.js} +41 -12
- package/dist/chunks/{agent-api-core-BUC9WND7.js → agent-api-core-DzKhakCv.js} +94 -3
- package/dist/chunks/{agent-setup-cli-CH7EHsAX.js → agent-setup-cli-C8DGxlA-.js} +1 -1
- package/dist/chunks/{config-CuZ16F9W.js → config-BYc2z1Oc.js} +15 -10
- package/dist/chunks/{config-store-LqQ7MuyV.js → config-store-B7MpiYxG.js} +7 -7
- package/dist/chunks/config-store-CsUoy88Q.js +1 -0
- package/dist/chunks/{contracts-BLZAOP57.js → contracts-C6ipN_zw.js} +48 -9
- package/dist/chunks/git-version-6QFnKbWa.js +139 -0
- package/dist/chunks/{grok-Ch4-35_r.js → grok-CZ6kP_vD.js} +5 -1
- package/dist/chunks/guardrails-deps-Cb-ki4EU.js +70 -0
- package/dist/chunks/{sandbox-proxy-core-hnQgKR0T.js → guardrails-proxy-core-BI3rdY-m.js} +4 -4
- package/dist/chunks/{knowledge-graph-cli-DD7QvcFd.js → knowledge-graph-cli-DApeMzA8.js} +1 -1
- package/dist/chunks/{loopback-post-DepQfdcM.js → loopback-post-DJ6iYWuL.js} +9 -9
- package/dist/chunks/{main-Rmo83Ow-.js → main-BUfjroXE.js} +12976 -11532
- package/dist/chunks/{pair-cli-DzFeAmq5.js → pair-cli-GZ9t1mIO.js} +2 -2
- package/dist/chunks/{remote-config-CPdSF2Xi.js → remote-config-D2Is89Kj.js} +1 -1
- package/dist/chunks/{session-B8RhqQJe.js → session-BZbariv5.js} +54 -8
- package/dist/chunks/{session-routes-DB5aDKsx.js → session-routes-C4t8O8yg.js} +9 -9
- package/dist/chunks/{settings-cli-C2TY6hl-.js → settings-cli-DQOjmjev.js} +3 -3
- package/dist/chunks/{ssh-sign-cli-C2nUwJC-.js → ssh-sign-cli-LIBP8ppO.js} +2 -2
- package/dist/client/assets/index-B_Qt7fbZ.css +1 -0
- package/dist/client/assets/index-D-upBor0.js +265 -0
- package/dist/client/index.html +2 -2
- package/dist/scripts/postinstall-path-check.js +4 -4
- package/dist/server/index.js +1 -1
- package/dist/session/command-hook-relay.js +1 -1
- package/dist/session/{sandbox-open-launcher.js → guardrails-open-launcher.js} +10 -10
- package/dist/session/hook-relay.js +1 -1
- package/dist/session/statusline-relay.js +1 -1
- package/package.json +4 -3
- package/dist/chunks/adapters-BVozg-Lg.js +0 -1
- package/dist/chunks/config-store-tVmt_nfm.js +0 -1
- package/dist/chunks/git-version-CUcDtF5k.js +0 -58
- package/dist/chunks/sandbox-deps-CWiH1d1w.js +0 -70
- package/dist/client/assets/index-BIaSyXwH.css +0 -1
- package/dist/client/assets/index-Ddu7M4Ad.js +0 -259
package/README.md
CHANGED
|
@@ -1,44 +1,45 @@
|
|
|
1
1
|
# Glimmervoid
|
|
2
2
|
|
|
3
3
|
[](https://github.com/johncwaters/glimmervoid/actions/workflows/test.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/glimmervoid)
|
|
4
5
|
[](https://opensource.org/licenses/MIT)
|
|
5
6
|
[](https://nodejs.org)
|
|
6
7
|
[](https://github.com/johncwaters/glimmervoid)
|
|
7
8
|
|
|
8
9
|
**Mission control for your coding agents. Run dozens of Claude Code, Codex and Grok sessions at once. See every one. Miss nothing.**
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
[glimmervoid.dev](https://glimmervoid.dev)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
Glimmervoid is one browser dashboard for all your coding agents: a live terminal for every session, exact status instead of a guess, a notification the moment an agent needs you, and a git worktree per agent that you review and merge from the same page.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Glimmervoid is developed inside Glimmervoid.
|
|
15
|
+
It is an orchestrator, not a harness. Claude Code, Codex and Grok run unmodified, as real processes in real terminals on your own machine. No cloud service, no account. Glimmervoid is developed inside Glimmervoid.
|
|
17
16
|
|
|
18
17
|

|
|
19
18
|
|
|
20
19
|
## Quickstart
|
|
21
20
|
|
|
22
|
-
Windows and macOS:
|
|
21
|
+
**1. Start it.** Windows and macOS:
|
|
23
22
|
|
|
24
23
|
```bash
|
|
25
24
|
npx glimmervoid@latest
|
|
26
25
|
```
|
|
27
26
|
|
|
28
|
-
Linux
|
|
27
|
+
Linux, where node-pty compiles at install time:
|
|
29
28
|
|
|
30
29
|
```bash
|
|
31
30
|
sudo apt install build-essential python3 git
|
|
32
31
|
npx --allow-scripts=node-pty glimmervoid@latest
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
**2. Open http://localhost:3000.** The first start from a terminal opens it in your browser for you; pass `--no-open` to skip that.
|
|
35
|
+
|
|
36
|
+
**3. Follow the checklist.** The empty dashboard walks you through three steps: an agent CLI it can find, the folder that holds your git checkouts, and your first session. It suggests folders such as `~/Projects` that already hold git repos, or you can type a path. Each subfolder becomes a project. For a folder outside your repository roots, open **Run in a folder outside your repository roots** in the **+ Session** dialog.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
That is the whole setup. Allow notifications when the dashboard asks, and keep a dashboard tab open: a notification appears whenever that tab is in the background, unless Streamer mode is on.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
Keep `@latest` so npx picks up new releases. To install globally instead, run `npm install -g glimmervoid` (add `--allow-scripts=node-pty` on Linux), then `glimmervoid`. Never use `sudo npm`; see [EACCES on a global install](docs/troubleshooting.md#eacces-on-a-global-install).
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
If something fails, run `glimmervoid doctor` and see [troubleshooting](docs/troubleshooting.md).
|
|
42
43
|
|
|
43
44
|
### From source
|
|
44
45
|
|
|
@@ -50,92 +51,96 @@ npm run build
|
|
|
50
51
|
npm start
|
|
51
52
|
```
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
To update, run `git pull --ff-only && npm ci && npm run build` and restart. The dashboard's update banner shows the right command for however you installed.
|
|
54
55
|
|
|
55
56
|
## Requirements
|
|
56
57
|
|
|
57
|
-
- **Node.js
|
|
58
|
+
- **Node.js 22.18 or newer.** Distro-packaged Node is usually older; use nvm, NodeSource or the official installer.
|
|
58
59
|
- **Windows 11, Linux or macOS.** CI runs on Linux; Windows and macOS are tested by hand.
|
|
59
|
-
- **
|
|
60
|
+
- **An agent CLI** on PATH: Claude Code, Codex, Grok or another terminal agent.
|
|
60
61
|
- **git** on PATH, for per-session worktrees.
|
|
61
|
-
- **Linux only:** `build-essential` and `python3`
|
|
62
|
-
|
|
62
|
+
- **Linux only:** `build-essential` and `python3` to compile node-pty, plus `bubblewrap`, `socat` and `ripgrep` for session guardrails (`sudo apt install bubblewrap socat ripgrep`).
|
|
63
|
+
|
|
64
|
+
`glimmervoid doctor` checks all of these.
|
|
63
65
|
|
|
64
66
|
## Supported agents
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
| Agent | Command | Status from |
|
|
69
|
+
|-------|---------|-------------|
|
|
70
|
+
| Claude Code (default, most complete) | `claude` | Hooks, with background agent gating and auto-resume |
|
|
71
|
+
| Codex CLI | `codex` | Hooks. Codex has no notification event, so a question asked in prose looks like a finished turn |
|
|
72
|
+
| Grok Build | `grok` | Hooks, after running `glimmervoid agent setup grok` once |
|
|
73
|
+
| Any other terminal agent | your own | Terminal title only |
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
Declare another agent under `customAgents` in `config.json` with an `id`, `label` and `command`, plus optional `args`, `idleTitle` and `busyTitle`. The **+ Session** dialog offers only the agents it finds on PATH, and a project can default to one with `projects[].agent`.
|
|
76
|
+
|
|
77
|
+
## Features
|
|
72
78
|
|
|
73
|
-
|
|
79
|
+
- **Focus view.** A rail of every session on the left, the selected session's live terminal in the middle, its changes on the right.
|
|
80
|
+
- **A worktree per agent.** Every session in a git repo gets its own worktree, so agents never step on your checkout or each other. Review the diff and merge with one click while the agent keeps working. A conflicted merge parks, and **Resolve in session** hands it back to the agent that wrote it.
|
|
81
|
+
- **Exact status.** Hooks report every state change and the terminal title fills the gaps. The screen is never scraped ([how](#how-status-detection-works)).
|
|
82
|
+
- **Notifications.** Browser notifications when a session needs input, finishes or fails, with your own alert sounds if you like (drop `.ogg`, `.mp3`, `.wav`, `.m4a` or `.webm` files into `~/.glimmervoid/sounds/`). Telegram covers you when no dashboard is open.
|
|
83
|
+
- **Phone layout.** Board, terminal and review screens, attention first, with a terminal that resizes around the keyboard.
|
|
84
|
+
- **Plan review.** Read, edit and approve Claude Code plans from the dashboard or your phone.
|
|
85
|
+
- **Guardrails.** Guarded Claude Code sessions on macOS and Linux (on unless a project or folder opts out) cannot touch credentials or Glimmervoid's own state, and Sane YOLO, once its hooks are installed and trusted, refuses commands like `git reset --hard` and `git push --force` in sessions that skip permission prompts.
|
|
86
|
+
- **Usage.** Tokens and estimated cost from local Claude Code, Codex and Grok transcripts, with optional budgets.
|
|
87
|
+
- **Auto-resume.** Sessions live when Glimmervoid stopped come back with their conversation resumed.
|
|
88
|
+
- **Remote access** (opt-in). Reach the dashboard from another device through single-use pairing links.
|
|
89
|
+
- **Radar** (opt-in). Watches PostHog error tracking, pings Telegram on a spike, regression or new issue, and sends an agent to investigate. Optional auto-fix opens a pull request the agent can never push or merge itself.
|
|
90
|
+
- **Team PR review** (opt-in). Drafts a review of each open pull request from a GitHub team in a guarded agent, optionally with your own review skill. Nothing posts until you choose Approve or Comment.
|
|
91
|
+
|
|
92
|
+
### Keyboard
|
|
93
|
+
|
|
94
|
+
Cmd on a Mac, Alt elsewhere:
|
|
95
|
+
|
|
96
|
+
| Keys | Action |
|
|
97
|
+
|------|--------|
|
|
98
|
+
| `Cmd/Alt+J` | Next session that needs you |
|
|
99
|
+
| `Cmd/Alt+1` to `9` | Jump to a session |
|
|
100
|
+
| `Cmd/Alt+Up` / `Down` | Move through the rail |
|
|
101
|
+
| `Cmd/Alt+I` | Merge the selected session |
|
|
102
|
+
| `Cmd/Alt+U` | Resolve a parked merge, or resync the base branch |
|
|
103
|
+
| `Cmd/Alt+0` | Add a session |
|
|
104
|
+
|
|
105
|
+
## Commands
|
|
74
106
|
|
|
75
107
|
```
|
|
76
|
-
glimmervoid
|
|
77
|
-
glimmervoid --port 3001
|
|
78
|
-
glimmervoid doctor
|
|
79
|
-
glimmervoid
|
|
80
|
-
glimmervoid --
|
|
108
|
+
glimmervoid # start the server on port 3000
|
|
109
|
+
glimmervoid --port 3001 # start on another port
|
|
110
|
+
glimmervoid doctor # check the install, PATH, agents and native module
|
|
111
|
+
glimmervoid settings list # read and change settings from the terminal
|
|
112
|
+
glimmervoid pair --name phone # pair a remote device
|
|
81
113
|
```
|
|
82
114
|
|
|
83
115
|
`glimmervoid --help` lists every command.
|
|
84
116
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
- Focus view: a roster rail of every session, the selected session's live terminal in the center, and a worktree review sidebar
|
|
88
|
-
- Per-session git worktree isolation: review and merge each agent's committed work from the dashboard while it keeps running
|
|
89
|
-
- Real-time terminal output via xterm.js with WebGL rendering
|
|
90
|
-
- Structural status detection: hooks as the authoritative signal, an OSC-0 title fallback, never screen scraping (see below)
|
|
91
|
-
- Background sub-agent completion gate: a session with live background agents or tasks stays out of Complete until they finish
|
|
92
|
-
- Browser notifications when a session needs input, finishes or fails
|
|
93
|
-
- Custom alert sounds: drop your own `.ogg`, `.mp3`, `.wav`, `.m4a` or `.webm` files into `~/.glimmervoid/sounds/` (or `$GLIMMERVOID_HOME/sounds/`) and pick one in Settings
|
|
94
|
-
- Phone layout as a first-class second layout: board, terminal and review screens, attention-first ordering, and a terminal that resizes around the soft keyboard instead of hiding behind it
|
|
95
|
-
- Remote access (opt-in): a separate listener with single-use device pairing and cookie auth
|
|
96
|
-
- Telegram notifications (opt-in), sent only when no dashboard tab is open anywhere
|
|
97
|
-
- Plan review: read, edit and approve a Claude Code plan from the dashboard or phone
|
|
98
|
-
- Usage tracking: token use and estimated cost from local Claude Code, Codex and Grok transcripts, with optional budgets
|
|
99
|
-
- Radar (opt-in): polls PostHog error tracking, pings Telegram when an issue spikes, regresses or first appears, and sends an agent to investigate; optional auto-fix opens a pull request the agent itself can never push or merge
|
|
100
|
-
- Team PR review (opt-in): drafts a review of each open pull request from a GitHub team in a sandboxed agent, optionally with your own Claude Code review skill (`teamReview.skill`); nothing posts until you choose Approve or Comment
|
|
101
|
-
- Auto-resume: sessions that were live when Glimmervoid stopped come back with their conversation resumed
|
|
102
|
-
- Hot-reloaded configuration and a Settings view for most of it
|
|
103
|
-
|
|
104
|
-
## Why the status detection is hard (and how Glimmervoid does it)
|
|
105
|
-
|
|
106
|
-
The obvious way to know if a Claude Code session finished, is waiting on you, or is still working is to scrape the terminal: watch for a prompt string, a spinner glyph, some text pattern. It breaks constantly. Every TUI redraw, every theme change, every Claude Code release that adjusts spacing invalidates the scrape. Glimmervoid never does this.
|
|
107
|
-
|
|
108
|
-
Instead, at spawn Glimmervoid injects Claude Code hooks scoped to that one session, no changes to the target repo, that POST to a local HTTP endpoint on every lifecycle event: prompt submitted, turn stopped, notification raised, sub-agent started or finished. These hooks are the authoritative signal. An OSC-0 terminal title fallback (spinner glyph = working, idle glyph = ready) covers the gap for anything that predates or bypasses the hooks. The two are merged with explicit precedence (hook beats title) and a short conflict window so a racing signal can still win before the UI settles.
|
|
109
|
-
|
|
110
|
-
That design didn't arrive whole. Three incidents shaped it:
|
|
111
|
-
|
|
112
|
-
`/clear` and `/compact` fire no `UserPromptSubmit` and no `Stop`, but the terminal redraw briefly flashes a spinner then an idle glyph in the title. Early on, that flash looked exactly like a finished work cycle, so Glimmervoid fired a "session complete" notification on every `/clear`. (The bug report was, more or less, "why did my terminal congratulate me for clearing the screen.") The fix: on a detected clear/compact, reset both signal sources and mute title-only signals until the next real prompt.
|
|
117
|
+
## Configuration
|
|
113
118
|
|
|
114
|
-
|
|
119
|
+
Glimmervoid creates `~/.glimmervoid/config.json` on first run. Most settings live in the dashboard's **Settings** view; the rest you can change with `glimmervoid settings` or by editing the file, which the server reloads on save. Every key, default and environment variable is in the [configuration reference](docs/configuration.md), generated from the code.
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
Worktrees branch from each repo's default branch (`origin/HEAD`, then `main`, then `master`) unless you set `integrationBranch`.
|
|
117
122
|
|
|
118
|
-
|
|
123
|
+
Glimmervoid sends anonymous usage and error data by default. [Telemetry](docs/configuration.md#telemetry) lists exactly what is sent and how to turn it off.
|
|
119
124
|
|
|
120
|
-
##
|
|
125
|
+
## How status detection works
|
|
121
126
|
|
|
122
|
-
|
|
127
|
+
The obvious way to tell whether an agent is working, waiting or done is to scrape the terminal for a prompt or a spinner. That breaks on every redraw, theme change and CLI release, so Glimmervoid never does it.
|
|
123
128
|
|
|
124
|
-
|
|
129
|
+
Instead, each session gets Claude Code hooks scoped to that one session, with no change to your repo. They POST every lifecycle event to a local endpoint: prompt submitted, turn stopped, notification raised, sub-agent started or finished. Hooks are the authoritative signal. The terminal title (spinner glyph means working, idle glyph means ready) covers anything the hooks miss, and a hook wins any conflict.
|
|
125
130
|
|
|
126
|
-
|
|
131
|
+
Three bugs shaped the details:
|
|
127
132
|
|
|
128
|
-
|
|
133
|
+
- **`/clear` looked like a finished turn.** It fires no hooks but flashes a spinner, then an idle glyph, in the title, so early builds sent "session complete" on every clear. Now a clear or compact resets both signals and mutes the title until the next real prompt.
|
|
134
|
+
- **A background sub-agent outlived its parent's turn.** The main agent's `Stop` fired while a sub-agent was still working. Now a session stays out of Complete until the live sub-agent count, reconciled against Claude Code's own `background_tasks` field, reaches zero.
|
|
135
|
+
- **Auto-resume silently never ran.** It waited for `SessionStart`, which Claude Code does not reliably fire on interactive startup. Now the conversation id comes from whichever hook arrives first.
|
|
129
136
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
Glimmervoid sends anonymous usage and error data by default; what it sends and how to turn it off are in [Telemetry](docs/configuration.md#telemetry).
|
|
137
|
+
Every session writes a JSONL recording of hook payloads and state transitions (never terminal bytes), and a replay harness runs recorded sessions back through the detection code as regression tests.
|
|
133
138
|
|
|
134
139
|
## Remote access
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
Off by default. Remote access is a second listener on `127.0.0.1` that you put behind an HTTPS reverse proxy; Glimmervoid never binds a wider address unless `GLIMMERVOID_INSECURE_BIND=1` is set.
|
|
137
142
|
|
|
138
|
-
1. Add a `remote` block to `~/.glimmervoid/config.json`. `
|
|
143
|
+
1. Add a `remote` block to `~/.glimmervoid/config.json`. `port` must differ from the dashboard port, and `publicHost` is the hostname your proxy serves:
|
|
139
144
|
|
|
140
145
|
```json
|
|
141
146
|
{
|
|
@@ -143,16 +148,14 @@ Remote access is off by default. It is a second loopback listener meant to sit b
|
|
|
143
148
|
}
|
|
144
149
|
```
|
|
145
150
|
|
|
146
|
-
2. Restart Glimmervoid
|
|
147
|
-
3. Point an HTTPS reverse proxy at that port. With Tailscale, `tailscale serve --bg 3456` serves `https://<machine>.<tailnet>.ts.net`, which is
|
|
148
|
-
4.
|
|
149
|
-
5. On the host, run `glimmervoid pair --name phone
|
|
150
|
-
|
|
151
|
-
`glimmervoid pair --list` and `glimmervoid pair --revoke <id>` manage devices, and a revoke applies without a restart. A pairing grants full control of the machine as the server account, so treat pairing URLs as passwords.
|
|
151
|
+
2. Restart Glimmervoid. The log shows `Glimmervoid remote listener on http://127.0.0.1:3456 (paired devices only)`.
|
|
152
|
+
3. Point an HTTPS reverse proxy at that port. With Tailscale, `tailscale serve --bg 3456` serves `https://<machine>.<tailnet>.ts.net`, which is what `publicHost` should hold.
|
|
153
|
+
4. From the device, open `https://<publicHost>/`. You should see Glimmervoid's "Pairing required" page.
|
|
154
|
+
5. On the host, run `glimmervoid pair --name phone` and open the printed link on the device. It is single-use and expires in 10 minutes.
|
|
152
155
|
|
|
153
|
-
|
|
156
|
+
`glimmervoid pair --list` shows paired devices, and `glimmervoid pair --revoke <id>` removes one without a restart.
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
A paired device has full control of the machine as your account, so treat pairing links like passwords. Behind HTTP-mode `tailscale serve`, you can also set `"ownerLogin": "you@example.com"` in the `remote` block so only that Tailscale login gets through, on top of pairing. Only HTTP-mode `tailscale serve` strips a forged `Tailscale-User-Login` header; behind any other proxy, or `tailscale serve --tcp` or `--tls-terminated-tcp`, `ownerLogin` proves nothing.
|
|
156
159
|
|
|
157
160
|
## Running as a service (Linux)
|
|
158
161
|
|
|
@@ -178,20 +181,19 @@ systemctl --user enable --now glimmervoid
|
|
|
178
181
|
loginctl enable-linger "$USER"
|
|
179
182
|
```
|
|
180
183
|
|
|
181
|
-
|
|
184
|
+
The unit's `PATH` must reach `node`, `glimmervoid` and your agent CLIs. With nvm, set `Environment=PATH=...` or use absolute paths in `ExecStart`. For a source checkout, use `ExecStart=/usr/bin/env node dist/server/index.js` with `WorkingDirectory=` set to the checkout.
|
|
182
185
|
|
|
183
|
-
|
|
186
|
+
`secrets.env` is optional. Put `GLIMMERVOID_POSTHOG_API_KEY` and `GLIMMERVOID_TELEGRAM_BOT_TOKEN` there to keep them out of `config.json`.
|
|
184
187
|
|
|
185
|
-
|
|
188
|
+
## Security
|
|
186
189
|
|
|
187
|
-
|
|
190
|
+
Glimmervoid runs agents with your account's privileges. The local dashboard has no login and binds only `127.0.0.1` unless `GLIMMERVOID_INSECURE_BIND=1` is set, so any local process can drive it and the port must never be exposed to the network. Use [remote access](#remote-access) to reach it from elsewhere.
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
- **Requires an agent CLI.** Glimmervoid spawns and supervises Claude Code, Codex, Grok or another terminal agent; it does not replace them.
|
|
192
|
+
[SECURITY.md](SECURITY.md) has the full trust model and how to report a vulnerability privately.
|
|
191
193
|
|
|
192
|
-
##
|
|
194
|
+
## Contributing
|
|
193
195
|
|
|
194
|
-
See [
|
|
196
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Release history is in [CHANGELOG.md](CHANGELOG.md).
|
|
195
197
|
|
|
196
198
|
## License
|
|
197
199
|
|
package/dist/bin/glimmervoid.js
CHANGED
|
@@ -2,124 +2,59 @@
|
|
|
2
2
|
import { o as nodePtyRebuildHint, r as firstLine, t as probeNodePty } from "../chunks/node-pty-preflight-C8QIlrtU.js";
|
|
3
3
|
import { i as errorText } from "../chunks/text-DoirglWV.js";
|
|
4
4
|
import { a as packageRoot } from "../chunks/runtime-paths-BkeftE60.js";
|
|
5
|
-
import { t as AGENT_API_VERBS } from "../chunks/session-
|
|
5
|
+
import { t as AGENT_API_VERBS } from "../chunks/session-BZbariv5.js";
|
|
6
6
|
import { i as execSync } from "../chunks/child-process-safe-CbQnyh7D.js";
|
|
7
7
|
import { t as renderTable } from "../chunks/ascii-figure-core-pDAwtlKO.js";
|
|
8
8
|
import { n as glimmervoidHomeDir, t as decideConfigPath } from "../chunks/config-path-core-DFt-TemS.js";
|
|
9
|
-
import { i as
|
|
10
|
-
import {
|
|
9
|
+
import { i as parseCommandLine, r as gitDoctorRows, t as probeGitVersion } from "../chunks/git-version-6QFnKbWa.js";
|
|
10
|
+
import { i as guardrailsDoctorRows, r as probeGuardrailsDependencies } from "../chunks/guardrails-deps-Cb-ki4EU.js";
|
|
11
11
|
import { i as pnpmGlobalBinDir, n as npmGlobalBinDir, r as onPath, t as formatPathNotice } from "../chunks/path-doctor-C23VAqeu.js";
|
|
12
12
|
import fs from "node:fs";
|
|
13
13
|
import path from "node:path";
|
|
14
14
|
import os from "node:os";
|
|
15
|
-
//#region server/core/command-line-core.ts
|
|
16
|
-
var SUBCOMMANDS_HONORING_TRAILING_GLOBAL_OPTIONS = /* @__PURE__ */ new Set([
|
|
17
|
-
"doctor",
|
|
18
|
-
"pair",
|
|
19
|
-
"visions"
|
|
20
|
-
]);
|
|
21
|
-
var SUBCOMMANDS_HONORING_A_LEADING_HELP_OR_VERSION = new Set(AGENT_API_VERBS);
|
|
22
|
-
var OPTIONS_TAKING_A_VALUE = {
|
|
23
|
-
"--config": "configPath",
|
|
24
|
-
"--port": "port"
|
|
25
|
-
};
|
|
26
|
-
function scanGlobalOption(argv, index, state) {
|
|
27
|
-
const token = argv[index];
|
|
28
|
-
const valueKey = OPTIONS_TAKING_A_VALUE[token];
|
|
29
|
-
if (valueKey) {
|
|
30
|
-
const value = argv[index + 1];
|
|
31
|
-
if (value !== void 0) state[valueKey] = value;
|
|
32
|
-
return index + 2;
|
|
33
|
-
}
|
|
34
|
-
if (token === "--help" || token === "-h") state.wantsHelp = true;
|
|
35
|
-
if (token === "--version") state.wantsVersion = true;
|
|
36
|
-
if (token === "--doctor") state.wantsDoctor = true;
|
|
37
|
-
return index + 1;
|
|
38
|
-
}
|
|
39
|
-
function scanTrailingGlobalOptions(subcommandArgs, state) {
|
|
40
|
-
let index = 0;
|
|
41
|
-
while (index < subcommandArgs.length) {
|
|
42
|
-
if (!subcommandArgs[index].startsWith("-")) {
|
|
43
|
-
index += 1;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
index = scanGlobalOption(subcommandArgs, index, state);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function scanLeadingHelpOrVersion(subcommandArgs, state) {
|
|
50
|
-
const firstArgument = subcommandArgs[0];
|
|
51
|
-
if (firstArgument === "--help" || firstArgument === "-h") state.wantsHelp = true;
|
|
52
|
-
if (firstArgument === "--version") state.wantsVersion = true;
|
|
53
|
-
}
|
|
54
|
-
function parseCommandLine(argv) {
|
|
55
|
-
const state = {
|
|
56
|
-
configPath: null,
|
|
57
|
-
port: null,
|
|
58
|
-
wantsHelp: false,
|
|
59
|
-
wantsVersion: false,
|
|
60
|
-
wantsDoctor: false
|
|
61
|
-
};
|
|
62
|
-
let index = 0;
|
|
63
|
-
while (index < argv.length && argv[index].startsWith("-")) index = scanGlobalOption(argv, index, state);
|
|
64
|
-
const name = argv[index];
|
|
65
|
-
const subcommandArgs = argv.slice(index + 1);
|
|
66
|
-
if (name !== void 0 && SUBCOMMANDS_HONORING_TRAILING_GLOBAL_OPTIONS.has(name)) scanTrailingGlobalOptions(subcommandArgs, state);
|
|
67
|
-
if (name !== void 0 && SUBCOMMANDS_HONORING_A_LEADING_HELP_OR_VERSION.has(name)) scanLeadingHelpOrVersion(subcommandArgs, state);
|
|
68
|
-
if (state.wantsHelp) return { kind: "help" };
|
|
69
|
-
if (state.wantsVersion) return { kind: "version" };
|
|
70
|
-
const globalOptions = {
|
|
71
|
-
configPath: state.configPath,
|
|
72
|
-
port: state.port
|
|
73
|
-
};
|
|
74
|
-
if (state.wantsDoctor) return {
|
|
75
|
-
kind: "subcommand",
|
|
76
|
-
name: "doctor",
|
|
77
|
-
subcommandArgs: [],
|
|
78
|
-
globalOptions
|
|
79
|
-
};
|
|
80
|
-
if (name === void 0) return {
|
|
81
|
-
kind: "server",
|
|
82
|
-
globalOptions
|
|
83
|
-
};
|
|
84
|
-
return {
|
|
85
|
-
kind: "subcommand",
|
|
86
|
-
name,
|
|
87
|
-
subcommandArgs,
|
|
88
|
-
globalOptions
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
//#endregion
|
|
92
15
|
//#region bin/glimmervoid.ts
|
|
93
16
|
var args = process.argv.slice(2);
|
|
94
17
|
var pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
|
|
95
18
|
var USAGE = `Usage: glimmervoid [command] [options]
|
|
96
19
|
|
|
20
|
+
With no command, starts the server.
|
|
21
|
+
|
|
97
22
|
Commands:
|
|
98
|
-
doctor
|
|
99
|
-
|
|
100
|
-
pair
|
|
101
|
-
pair --list
|
|
23
|
+
doctor Check the install, PATH, agent CLIs and native module, then exit
|
|
24
|
+
settings <cmd> Read and change config.json: list, get, set, unset, search, schema (each takes --json)
|
|
25
|
+
pair Mint a single-use pairing link for a remote device
|
|
26
|
+
pair --list List paired devices
|
|
102
27
|
pair --revoke <id> Revoke a paired device
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
visions
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
28
|
+
agent setup grok Install the Grok hook relay, which does nothing outside Glimmervoid
|
|
29
|
+
|
|
30
|
+
Editor integration (Visions):
|
|
31
|
+
visions install Install the Visions extension into every VS Code family editor on PATH
|
|
32
|
+
visions uninstall Remove the Visions extension and the editor config it wrote
|
|
33
|
+
visions setup Print LSP client config for Neovim, Helix, Emacs, Kate, Sublime, JetBrains
|
|
34
|
+
visions status Show the relay path and which editors have the extension
|
|
35
|
+
visions relay Run the Visions LSP relay on stdio (an editor's LSP client starts this)
|
|
36
|
+
|
|
37
|
+
Inside a Glimmervoid session:
|
|
38
|
+
spawn <prompt> Start a sibling session on that prompt
|
|
39
|
+
attention <note> Flag this session as needing the operator
|
|
40
|
+
board List the live sessions
|
|
41
|
+
push Land this session's commits on the integration branch and push it
|
|
42
|
+
edit <path> Open a text file for the operator to edit in the dashboard
|
|
43
|
+
limits List which of these commands answer here and what the guardrails block
|
|
44
|
+
factory status Print each factory intent's tickets, state, last reason and next action
|
|
45
|
+
factory retry <id> Reopen the parked tickets of a factory intent
|
|
46
|
+
ssh-sign Sign a commit with the operator's forwarded ssh agent (git runs this)
|
|
47
|
+
|
|
48
|
+
Experimental:
|
|
49
|
+
kg [command] Typed knowledge and task graph (Settings > Lanes > Knowledge graph)
|
|
116
50
|
|
|
117
51
|
Options:
|
|
118
|
-
--
|
|
119
|
-
--
|
|
120
|
-
--
|
|
121
|
-
--
|
|
122
|
-
--
|
|
52
|
+
--port <number> Server port (default: 3000)
|
|
53
|
+
--config <path> Config file (default: ~/.glimmervoid/config.json)
|
|
54
|
+
--name <label> Name for the device being paired (with pair)
|
|
55
|
+
--no-open Do not open the dashboard in a browser on first start
|
|
56
|
+
--version Print the version
|
|
57
|
+
--help, -h Print this help`;
|
|
123
58
|
var commandLine = parseCommandLine(args);
|
|
124
59
|
if (commandLine.kind === "help") {
|
|
125
60
|
console.log(USAGE);
|
|
@@ -139,19 +74,19 @@ var SUBCOMMAND_RUNNERS = new Map([
|
|
|
139
74
|
return 0;
|
|
140
75
|
}],
|
|
141
76
|
["pair", async (subcommandArgs) => {
|
|
142
|
-
const { runPairCli } = await import("../chunks/pair-cli-
|
|
77
|
+
const { runPairCli } = await import("../chunks/pair-cli-GZ9t1mIO.js");
|
|
143
78
|
return runPairCli(subcommandArgs);
|
|
144
79
|
}],
|
|
145
80
|
["agent", async (subcommandArgs) => {
|
|
146
|
-
const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-
|
|
81
|
+
const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-C8DGxlA-.js");
|
|
147
82
|
return runAgentSetupCli(subcommandArgs);
|
|
148
83
|
}],
|
|
149
84
|
["kg", async (subcommandArgs) => {
|
|
150
|
-
const { runKnowledgeGraphCommand } = await import("../chunks/knowledge-graph-cli-
|
|
85
|
+
const { runKnowledgeGraphCommand } = await import("../chunks/knowledge-graph-cli-DApeMzA8.js");
|
|
151
86
|
return runKnowledgeGraphCommand(subcommandArgs);
|
|
152
87
|
}],
|
|
153
88
|
["settings", async (subcommandArgs) => {
|
|
154
|
-
const { runSettingsCommand } = await import("../chunks/settings-cli-
|
|
89
|
+
const { runSettingsCommand } = await import("../chunks/settings-cli-DQOjmjev.js");
|
|
155
90
|
return runSettingsCommand(subcommandArgs);
|
|
156
91
|
}],
|
|
157
92
|
["visions", async (subcommandArgs) => {
|
|
@@ -159,17 +94,21 @@ var SUBCOMMAND_RUNNERS = new Map([
|
|
|
159
94
|
return runVisionsCli(subcommandArgs);
|
|
160
95
|
}],
|
|
161
96
|
["ssh-sign", async (subcommandArgs) => {
|
|
162
|
-
const { runSshSignCli } = await import("../chunks/ssh-sign-cli-
|
|
97
|
+
const { runSshSignCli } = await import("../chunks/ssh-sign-cli-LIBP8ppO.js");
|
|
163
98
|
return runSshSignCli(subcommandArgs);
|
|
164
99
|
}],
|
|
100
|
+
["factory", async (subcommandArgs) => {
|
|
101
|
+
const { runAgentApiCli } = await import("../chunks/agent-api-cli-BTHEyJKS.js");
|
|
102
|
+
return runAgentApiCli(["factory", ...subcommandArgs]);
|
|
103
|
+
}],
|
|
165
104
|
...AGENT_API_VERBS.map((verb) => [verb, async (subcommandArgs, subcommandName) => {
|
|
166
|
-
const { runAgentApiCli } = await import("../chunks/agent-api-cli-
|
|
105
|
+
const { runAgentApiCli } = await import("../chunks/agent-api-cli-BTHEyJKS.js");
|
|
167
106
|
return runAgentApiCli([subcommandName, ...subcommandArgs]);
|
|
168
107
|
}])
|
|
169
108
|
]);
|
|
170
109
|
async function printSessionLimits() {
|
|
171
110
|
if (!process.env["GLIMMERVOID_AGENT_URL"]) return;
|
|
172
|
-
const { sessionLimitsHelpLines } = await import("../chunks/agent-api-cli-
|
|
111
|
+
const { sessionLimitsHelpLines } = await import("../chunks/agent-api-cli-BTHEyJKS.js");
|
|
173
112
|
const limitsLines = await sessionLimitsHelpLines();
|
|
174
113
|
console.log(`\n${limitsLines.join("\n")}`);
|
|
175
114
|
}
|
|
@@ -226,7 +165,7 @@ async function readDeclaredCustomAgents() {
|
|
|
226
165
|
declared: [],
|
|
227
166
|
error: null
|
|
228
167
|
};
|
|
229
|
-
const { loadConfigFile } = await import("../chunks/config-store-
|
|
168
|
+
const { loadConfigFile } = await import("../chunks/config-store-CsUoy88Q.js");
|
|
230
169
|
try {
|
|
231
170
|
const loaded = loadConfigFile(decided.path, { exitOnError: false });
|
|
232
171
|
if (!loaded.config) return {
|
|
@@ -302,7 +241,7 @@ async function runDoctor() {
|
|
|
302
241
|
}
|
|
303
242
|
switchSection("Agents");
|
|
304
243
|
try {
|
|
305
|
-
const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-
|
|
244
|
+
const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-CqFFmM2R.js");
|
|
306
245
|
const declaredCustomAgents = await readDeclaredCustomAgents();
|
|
307
246
|
if (declaredCustomAgents.error) line("config", declaredCustomAgents.error);
|
|
308
247
|
setCustomAgents(declaredCustomAgents.declared);
|
|
@@ -310,7 +249,7 @@ async function runDoctor() {
|
|
|
310
249
|
const { label, path: resolvedPath } = describeAgentResolvability(id);
|
|
311
250
|
line(`${id} (${label})`, resolvedPath ?? "not found on PATH");
|
|
312
251
|
}
|
|
313
|
-
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-
|
|
252
|
+
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-C8DGxlA-.js");
|
|
314
253
|
const grokSetup = inspectGrokAgentSetup();
|
|
315
254
|
line("grok hook setup", `${grokSetup.classification}: ${grokSetup.filePath}`);
|
|
316
255
|
} catch (err) {
|
|
@@ -327,7 +266,7 @@ async function runDoctor() {
|
|
|
327
266
|
try {
|
|
328
267
|
const { resolvePackageBin } = await import("../chunks/runtime-paths-DSmormp7.js");
|
|
329
268
|
const { execFileSync } = await import("../chunks/child-process-safe-ChRmWAJP.js");
|
|
330
|
-
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-
|
|
269
|
+
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-C8DGxlA-.js");
|
|
331
270
|
const binPath = resolvePackageBin("cc-safety-net", "cc-safety-net");
|
|
332
271
|
line("Sane YOLO", binPath || "not installed");
|
|
333
272
|
if (binPath) line("cc-safety-net version", execFileSync(process.execPath, [binPath, "--version"], {
|
|
@@ -347,8 +286,8 @@ async function runDoctor() {
|
|
|
347
286
|
line("reason", nodePty.reason);
|
|
348
287
|
line("hint", nodePtyRebuildHint(platform));
|
|
349
288
|
}
|
|
350
|
-
switchSection("
|
|
351
|
-
for (const [label, value] of
|
|
289
|
+
switchSection("Guardrails");
|
|
290
|
+
for (const [label, value] of guardrailsDoctorRows(probeGuardrailsDependencies({
|
|
352
291
|
platform,
|
|
353
292
|
exec: execSync
|
|
354
293
|
}))) line(label, value);
|